diff --git a/ITShowPlatform/.gitignore b/.gitignore similarity index 100% rename from ITShowPlatform/.gitignore rename to .gitignore diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59_[Default_Changelist]/shelved.patch new file mode 100644 index 0000000..bd074d2 --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59_[Default_Changelist]/shelved.patch @@ -0,0 +1,106 @@ +Index: .idea/workspace.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n 1650187837343\r\n \r\n \r\n 1650187914913\r\n \r\n \r\n 1650785005055\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .idea/workspace.xml (revision b6a66a1457215e0813938690788ef7e1783a9c22) ++++ .idea/workspace.xml (date 1651329189014) +@@ -31,11 +31,8 @@ + + + +- ++ + +- +- +- + + + + ++ ++ + + + + ++ + + + +@@ -114,7 +119,8 @@ + + + +- ++ ++ + + + 1650187914913 +@@ -130,7 +136,14 @@ + +- + +@@ -163,7 +176,8 @@ + + + +- + + +@@ -178,10 +192,10 @@ + + + +- ++ + + +- ++ + + + +@@ -218,10 +232,10 @@ + + + +- ++ + + +- ++ + + + diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59__Default_Changelist_.xml new file mode 100644 index 0000000..3d884c0 --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_4_13_59__Default_Changelist_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44_[Default_Changelist]/shelved.patch new file mode 100644 index 0000000..e87091a --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44_[Default_Changelist]/shelved.patch @@ -0,0 +1,261 @@ +Index: ITShowPlatform/settings.py +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\"\"\"\r\nDjango settings for ITShowPlatform project.\r\n\r\nGenerated by 'django-admin startproject' using Django 4.0.4.\r\n\r\nFor more information on this file, see\r\nhttps://docs.djangoproject.com/en/4.0/topics/settings/\r\n\r\nFor the full list of settings and their values, see\r\nhttps://docs.djangoproject.com/en/4.0/ref/settings/\r\n\"\"\"\r\n\r\nfrom pathlib import Path\r\nimport os\r\nimport configparser\r\n\r\n# Build paths inside the project like this: BASE_DIR / 'subdir'.\r\nBASE_DIR = Path(__file__).resolve().parent.parent\r\n\r\n# Quick-start development settings - unsuitable for production\r\n# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/\r\n\r\n# SECURITY WARNING: keep the secret key used in production secret!\r\nSECRET_KEY = 'django-insecure-0cn#v4ei2(^n+txyh4%3d5sllz6mknz#7t$!cq-d!ly*_rwvh2'\r\n\r\n# SECURITY WARNING: don't run with debug turned on in production!\r\nDEBUG = True\r\n\r\nALLOWED_HOSTS = [\"*\"]\r\n\r\n# Application definition\r\n\r\nconf = configparser.ConfigParser()\r\n\r\nconf.read(str(BASE_DIR)+r\"\\ITShowPlatform\\config.ini\",encoding=\"utf-8\")\r\n\r\n\r\n\r\nINSTALLED_APPS = [\r\n 'django.contrib.admin',\r\n 'django.contrib.auth',\r\n 'django.contrib.contenttypes',\r\n 'django.contrib.sessions',\r\n 'django.contrib.messages',\r\n 'django.contrib.staticfiles',\r\n 'rest_framework',\r\n 'Apps.enroll',\r\n 'Apps.history',\r\n 'Apps.comments',\r\n\r\n]\r\n\r\nMIDDLEWARE = [\r\n 'django.middleware.security.SecurityMiddleware',\r\n 'django.contrib.sessions.middleware.SessionMiddleware',\r\n 'django.middleware.common.CommonMiddleware',\r\n #'django.middleware.csrf.CsrfViewMiddleware',\r\n 'django.contrib.auth.middleware.AuthenticationMiddleware',\r\n 'django.contrib.messages.middleware.MessageMiddleware',\r\n 'django.middleware.clickjacking.XFrameOptionsMiddleware',\r\n]\r\n\r\nROOT_URLCONF = 'ITShowPlatform.urls'\r\n\r\nTEMPLATES = [\r\n {\r\n 'BACKEND': 'django.template.backends.django.DjangoTemplates',\r\n 'DIRS': [os.path.join(BASE_DIR, 'templates')]\r\n ,\r\n 'APP_DIRS': True,\r\n 'OPTIONS': {\r\n 'context_processors': [\r\n 'django.template.context_processors.debug',\r\n 'django.template.context_processors.request',\r\n 'django.contrib.auth.context_processors.auth',\r\n 'django.contrib.messages.context_processors.messages',\r\n ],\r\n },\r\n },\r\n]\r\n\r\nWSGI_APPLICATION = 'ITShowPlatform.wsgi.application'\r\n\r\n# Database\r\n# https://docs.djangoproject.com/en/4.0/ref/settings/#databases\r\n\r\nDATABASES = {\r\n 'default': {\r\n 'ENGINE': conf.get(\"database\",\"ENGINE\"),\r\n 'NAME': conf.get(\"database\",\"NAME\"),\r\n 'USER': conf.get(\"database\",\"USER\"),\r\n 'PASSWORD': conf.get(\"database\",\"PASSWORD\"),\r\n }\r\n}\r\n\r\n\r\n# Password validation\r\n# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators\r\n\r\nAUTH_PASSWORD_VALIDATORS = [\r\n {\r\n 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\r\n },\r\n {\r\n 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\r\n },\r\n {\r\n 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\r\n },\r\n {\r\n 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\r\n },\r\n]\r\n\r\n# Internationalization\r\n# https://docs.djangoproject.com/en/4.0/topics/i18n/\r\n\r\nLANGUAGE_CODE = 'zh-Hans'\r\n\r\nTIME_ZONE = 'Asia/Shanghai'\r\n\r\nUSE_I18N = True\r\n\r\nUSE_L10N = True\r\n\r\nUSE_TZ = False\r\n\r\n# Static files (CSS, JavaScript, Images)\r\n# https://docs.djangoproject.com/en/4.0/howto/static-files/\r\n\r\nSTATIC_URL = '/static/'\r\nSTATIC_ROOT = os.path.join(BASE_DIR, 'static')\r\nMEDIA_URL = '/media/'\r\nMEDIA_ROOT = os.path.join(BASE_DIR, 'media')\r\n\r\n# Default primary key field type\r\n# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field\r\n\r\nDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'\r\n\r\nREST_FRAMEWORK = {\r\n 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类\r\n 'rest_framework.renderers.JSONRenderer', # json渲染器\r\n 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器\r\n )\r\n}\r\n\r\nEMAIL_HOST = conf.get('email',\"EMAIL_HOST\") # 服务器\r\nEMAIL_PORT = conf.get(\"email\",\"EMAIL_PORT\")\r\nEMAIL_HOST_USER = conf.get(\"email\",\"EMAIL_HOST_USER\") # 账号\r\nEMAIL_HOST_PASSWORD = conf.get(\"email\",\"EMAIL_HOST_PASSWORD\") # 密码 (注意:这里的密码指的是授权码)\r\nEMAIL_USE_SSL = conf.get(\"email\",\"EMAIL_USE_SSL\") # 一般都为False\r\nEMAIL_FROM = conf.get(\"email\",\"EMAIL_FROM\") # 邮箱来自\r\n\r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- ITShowPlatform/settings.py (revision 83c29b0ebfaa2dc6fd723c743e9ef08cd850940a) ++++ ITShowPlatform/settings.py (date 1651756900301) +@@ -17,11 +17,16 @@ + # Build paths inside the project like this: BASE_DIR / 'subdir'. + BASE_DIR = Path(__file__).resolve().parent.parent + ++conf = configparser.RawConfigParser() ++ ++conf.read(str(BASE_DIR)+r"\ITShowPlatform\config.ini",encoding="utf-8") ++ ++ + # Quick-start development settings - unsuitable for production + # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + + # SECURITY WARNING: keep the secret key used in production secret! +-SECRET_KEY = 'django-insecure-0cn#v4ei2(^n+txyh4%3d5sllz6mknz#7t$!cq-d!ly*_rwvh2' ++SECRET_KEY = conf.get("Django","SECRET_KEY") + + # SECURITY WARNING: don't run with debug turned on in production! + DEBUG = True +@@ -30,10 +35,6 @@ + + # Application definition + +-conf = configparser.ConfigParser() +- +-conf.read(str(BASE_DIR)+r"\ITShowPlatform\config.ini",encoding="utf-8") +- + + + INSTALLED_APPS = [ +Index: ITShowPlatform/config.ini +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>[email]\r\nEMAIL_HOST = smtp.qq.com # 服务器\r\n\r\nEMAIL_PORT = 465\r\n\r\nEMAIL_HOST_USER = 2302253692@qq.com # 账号\r\n\r\nEMAIL_HOST_PASSWORD = idujbpdlpgbmdhjg # 密码 (注意:这里的密码指的是授权码)\r\n\r\nEMAIL_USE_SSL = True # 一般都为False\r\n\r\nEMAIL_FROM = 2302253692@qq.com # 邮箱来自\r\n\r\n[database]\r\n\r\nENGINE = django.db.backends.mysql\r\n\r\nNAME = ITShowPlatform\r\n\r\nUSER = root\r\n\r\nPASSWORD = HNXhnx123\r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- ITShowPlatform/config.ini (revision 83c29b0ebfaa2dc6fd723c743e9ef08cd850940a) ++++ ITShowPlatform/config.ini (date 1651756724343) +@@ -1,3 +1,7 @@ ++[Django] ++ ++SECRET_KEY = django-insecure-0cn#v4ei2(^n+txyh4%3d5sllz6mknz#7t$!cq-d!ly*_rwvh2 ++ + [email] + EMAIL_HOST = smtp.qq.com # 服务器 + +Index: .idea/misc.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .idea/misc.xml (revision 83c29b0ebfaa2dc6fd723c743e9ef08cd850940a) ++++ .idea/misc.xml (date 1651757009657) +@@ -3,5 +3,5 @@ + + +- ++ + +\ No newline at end of file +Index: .idea/ITShowPlatform.iml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .idea/ITShowPlatform.iml (revision 83c29b0ebfaa2dc6fd723c743e9ef08cd850940a) ++++ .idea/ITShowPlatform.iml (date 1651757009497) +@@ -16,7 +16,7 @@ + + + +- ++ + + + +Index: .idea/workspace.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n 1650187837343\r\n \r\n \r\n 1650187914913\r\n \r\n \r\n 1650785005055\r\n \r\n \r\n 1651746739401\r\n \r\n \r\n 1651748255265\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .idea/workspace.xml (revision 83c29b0ebfaa2dc6fd723c743e9ef08cd850940a) ++++ .idea/workspace.xml (date 1651761845564) +@@ -26,13 +26,17 @@ + + + +- ++ + + + + +- +- ++ ++ ++ ++ ++ ++ + + + +@@ -228,7 +240,7 @@ + + + +- + + +@@ -247,13 +259,14 @@ + + + +- + +- ++ + + +- ++ + + + +@@ -266,6 +279,10 @@ + + + ++ ++ ++ ++ + + + +@@ -282,22 +299,22 @@ + + + +- ++ + + +- +- ++ ++ + + +- +- ++ ++ + + +- +- ++ ++ + + +- ++ + + + +@@ -326,10 +343,10 @@ + + + +- ++ + + +- ++ + + + +@@ -348,6 +365,6 @@ + + + +- ++ + + +\ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44__Default_Changelist_.xml new file mode 100644 index 0000000..0d326ea --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_2022_5_5_22_44__Default_Changelist_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 8199c04..f1b1875 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -32,8 +32,2471 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -319,22 +2790,22 @@ - + - - + + - - + + - - + + - + @@ -351,18 +2822,18 @@ - + - - + + - - + + - + @@ -387,10 +2858,10 @@ - + - + diff --git a/ITShowPlatform/config.ini b/ITShowPlatform/config.ini deleted file mode 100644 index bf798a8..0000000 --- a/ITShowPlatform/config.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Django] - -SECRET_KEY = django-insecure-0cn#v4ei2(^n+txyh4%3d5sllz6mknz#7t$!cq-d!ly*_rwvh2 - -[email] -EMAIL_HOST = smtp.qq.com # 服务器 - -EMAIL_PORT = 465 - -EMAIL_HOST_USER = 2302253692@qq.com # 账号 - -EMAIL_HOST_PASSWORD = idujbpdlpgbmdhjg # 密码 (注意:这里的密码指的是授权码) - -EMAIL_USE_SSL = True # 一般都为False - -EMAIL_FROM = 2302253692@qq.com # 邮箱来自 - -[database] - -ENGINE = django.db.backends.mysql - -NAME = It_show - -USER = root - -PASSWORD = Qq2442402635* diff --git a/ITShowPlatform/settings.py b/ITShowPlatform/settings.py index 671b114..79f7738 100644 --- a/ITShowPlatform/settings.py +++ b/ITShowPlatform/settings.py @@ -19,7 +19,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent conf = configparser.RawConfigParser() -conf.read(str(BASE_DIR)+r"\ITShowPlatform\config.ini",encoding="utf-8") +conf.read(str(BASE_DIR)+r"\config.ini",encoding="utf-8") # Quick-start development settings - unsuitable for production diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/AUTHORS b/venv/Lib/site-packages/Django-3.2.5.dist-info/AUTHORS similarity index 96% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/AUTHORS rename to venv/Lib/site-packages/Django-3.2.5.dist-info/AUTHORS index dc2e77a..256118a 100644 --- a/venv/Lib/site-packages/Django-4.0.4.dist-info/AUTHORS +++ b/venv/Lib/site-packages/Django-3.2.5.dist-info/AUTHORS @@ -1,4 +1,4 @@ -Django was originally created in late 2003 at World Online, the web division +Django was originally created in late 2003 at World Online, the Web division of the Lawrence Journal-World newspaper in Lawrence, Kansas. Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- @@ -40,7 +40,7 @@ answer newbie questions, and generally made Django that much better: Aldian Fazrihady Aleksandra Sendecka Aleksi Häkli - Alex Dutton + Alexander Dutton Alexander Myodov Alexandr Tatarinov Alex Aktsipetrov @@ -54,7 +54,6 @@ answer newbie questions, and generally made Django that much better: Alexey Boriskin Alexey Tsivunin Ali Vakilzade - Aljaž Košir Aljosa Mohorovic Amit Chakradeo Amit Ramon @@ -75,7 +74,6 @@ answer newbie questions, and generally made Django that much better: Andrew Godwin Andrew Pinkham Andrews Medina - Andrew Northall Andriy Sokolovskiy Andy Chosak Andy Dustman @@ -84,7 +82,6 @@ answer newbie questions, and generally made Django that much better: Anssi Kääriäinen ant9000@netwise.it Anthony Briggs - Anthony Wright Anton Samarchyan Antoni Aloy Antonio Cavedoni @@ -102,7 +99,6 @@ answer newbie questions, and generally made Django that much better: Arthur Koziel Arthur Rio Arvis Bickovskis - Arya Khaligh Aryeh Leib Taurog A S Alam Asif Saif Uddin @@ -254,7 +250,6 @@ answer newbie questions, and generally made Django that much better: David Sanders David Schein David Tulig - David Winterbottom David Wobrock Davide Ceretti Deep L. Sukhwani @@ -304,7 +299,6 @@ answer newbie questions, and generally made Django that much better: Étienne Beaulé Eugene Lazutkin Evan Grim - Fabian Büchler Fabrice Aneche Farhaan Bukhsh favo@exoweb.net @@ -326,7 +320,7 @@ answer newbie questions, and generally made Django that much better: Frank Tegtmeyer Frank Wierzbicki Frank Wiles - František Malina + František Malina Fraser Nevett Gabriel Grant Gabriel Hurley @@ -349,7 +343,6 @@ answer newbie questions, and generally made Django that much better: Gerardo Orozco Gil Gonçalves Girish Kumar - Girish Sontakke Gisle Aas Glenn Maynard glin@seznam.cz @@ -359,7 +352,6 @@ answer newbie questions, and generally made Django that much better: Graham Carlyle Grant Jenks Greg Chapple - Greg Twohig Gregor Allensworth Gregor Müllegger Grigory Fateyev @@ -427,7 +419,6 @@ answer newbie questions, and generally made Django that much better: Jan Rademaker Jarek Głowacki Jarek Zgoda - Jarosław Wygoda Jason Davies (Esaj) Jason Huggins Jason McBrayer @@ -455,7 +446,6 @@ answer newbie questions, and generally made Django that much better: Jeremy Carbaugh Jeremy Dunck Jeremy Lainé - Jerin Peter George Jesse Young Jezeniel Zapanta jhenry @@ -483,14 +473,11 @@ answer newbie questions, and generally made Django that much better: Jökull Sólberg Auðunsson Jon Dufresne Jonas Haag - Jonathan Davis Jonatas C. D. Jonathan Buchanan Jonathan Daugherty (cygnus) Jonathan Feignberg Jonathan Slenders - Jonny Park - Jordan Bae Jordan Dimov Jordi J. Tablada Jorge Bastida @@ -530,7 +517,6 @@ answer newbie questions, and generally made Django that much better: Keith Bussell Kenneth Love Kent Hauser - Keryn Knight Kevin Grinberg Kevin Kubasik Kevin McConnell @@ -571,7 +557,6 @@ answer newbie questions, and generally made Django that much better: Luan Pablo Lucas Connors Luciano Ramalho - Lucidiot Ludvig Ericson Luis C. Berrocal Łukasz Langa @@ -589,7 +574,6 @@ answer newbie questions, and generally made Django that much better: Marc Aymerich Gubern Marc Egli Marcel Telka - Marcelo Galigniana Marc Fargas Marc Garcia Marcin Wróbel @@ -615,7 +599,7 @@ answer newbie questions, and generally made Django that much better: Martin Mahner Martin Maney Martin von Gagern - Mart Sõmermaa + Mart Sõmermaa Marty Alchin Masashi Shibata masonsimon+django@gmail.com @@ -655,7 +639,6 @@ answer newbie questions, and generally made Django that much better: Michael S. Brown Michael Hall Michael Josephson - Michael Lissner Michael Manfre michael.mcewan@gmail.com Michael Placentra II @@ -713,7 +696,6 @@ answer newbie questions, and generally made Django that much better: Nicola Larosa Nicolas Lara Nicolas Noé - Nikita Marchant Niran Babalola Nis Jørgensen Nowell Strite @@ -814,11 +796,9 @@ answer newbie questions, and generally made Django that much better: Rob Nguyen Robin Munn Rodrigo Pinheiro Marques de Araújo - Rohith P R Romain Garrigues Ronny Haryanto Ross Poulton - Roxane Bellot Rozza Rudolph Froger Rudy Mutter @@ -839,7 +819,6 @@ answer newbie questions, and generally made Django that much better: Sander Dijkhuis Sanket Saurav Sanyam Khurana - Sarah Boyce Sarthak Mehrish schwank@gmail.com Scot Hacker @@ -919,12 +898,10 @@ answer newbie questions, and generally made Django that much better: Thomas Stromberg Thomas Tanner tibimicu@gmx.net - Ties Jan Hefting Tim Allen Tim Givois Tim Graham Tim Heap - Tim McCurrach Tim Saylor Tobias Kunze Tobias McNulty @@ -935,7 +912,6 @@ answer newbie questions, and generally made Django that much better: Tom Forbes Tom Insam Tom Tobin - Tom Wojcik Tomáš Ehrlich Tomáš Kopeček Tome Cvitan @@ -989,7 +965,6 @@ answer newbie questions, and generally made Django that much better: Wilson Miner Wim Glenn wojtek - Wu Haotian Xavier Francisco Xia Kai Yann Fouillat @@ -999,7 +974,6 @@ answer newbie questions, and generally made Django that much better: ymasuda@ethercube.com Yoong Kang Lim Yusuke Miyazaki - yyyyyyyan Zac Hatfield-Dodds Zachary Voase Zach Liu diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/INSTALLER b/venv/Lib/site-packages/Django-3.2.5.dist-info/INSTALLER similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/INSTALLER rename to venv/Lib/site-packages/Django-3.2.5.dist-info/INSTALLER diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/LICENSE b/venv/Lib/site-packages/Django-3.2.5.dist-info/LICENSE similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/LICENSE rename to venv/Lib/site-packages/Django-3.2.5.dist-info/LICENSE diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/LICENSE.python b/venv/Lib/site-packages/Django-3.2.5.dist-info/LICENSE.python similarity index 90% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/LICENSE.python rename to venv/Lib/site-packages/Django-3.2.5.dist-info/LICENSE.python index 031ce49..8e1c618 100644 --- a/venv/Lib/site-packages/Django-4.0.4.dist-info/LICENSE.python +++ b/venv/Lib/site-packages/Django-3.2.5.dist-info/LICENSE.python @@ -70,17 +70,6 @@ direction to make these releases possible. B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON =============================================================== -Python software and documentation are licensed under the -Python Software Foundation License Version 2. - -Starting with Python 3.8.6, examples, recipes, and other code in -the documentation are dual licensed under the PSF License Version 2 -and the Zero-Clause BSD license. - -Some software incorporated into Python is under different licenses. -The licenses are listed with code falling under that license. - - PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- @@ -95,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. @@ -202,9 +191,9 @@ version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the internet using the following +Python 1.6.1 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the internet +Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013". 3. In the event Licensee prepares a derivative work that is based on @@ -274,17 +263,3 @@ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION ----------------------------------------------------------------------- - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/METADATA b/venv/Lib/site-packages/Django-3.2.5.dist-info/METADATA similarity index 90% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/METADATA rename to venv/Lib/site-packages/Django-3.2.5.dist-info/METADATA index ad9c573..90c809d 100644 --- a/venv/Lib/site-packages/Django-4.0.4.dist-info/METADATA +++ b/venv/Lib/site-packages/Django-3.2.5.dist-info/METADATA @@ -1,7 +1,7 @@ Metadata-Version: 2.1 Name: Django -Version: 4.0.4 -Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design. +Version: 3.2.5 +Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design. Home-page: https://www.djangoproject.com/ Author: Django Software Foundation Author-email: foundation@djangoproject.com @@ -21,19 +21,19 @@ Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Software Development :: Libraries :: Application Frameworks Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.8 -Requires-Dist: asgiref (<4,>=3.4.1) +Requires-Python: >=3.6 +Requires-Dist: asgiref (<4,>=3.3.2) +Requires-Dist: pytz Requires-Dist: sqlparse (>=0.2.2) -Requires-Dist: backports.zoneinfo ; python_version < "3.9" -Requires-Dist: tzdata ; sys_platform == "win32" Provides-Extra: argon2 Requires-Dist: argon2-cffi (>=19.1.0) ; extra == 'argon2' Provides-Extra: bcrypt @@ -43,7 +43,7 @@ Requires-Dist: bcrypt ; extra == 'bcrypt' Django ====== -Django is a high-level Python web framework that encourages rapid development +Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. All documentation is in the "``docs``" directory and online at diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/RECORD b/venv/Lib/site-packages/Django-3.2.5.dist-info/RECORD similarity index 74% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/RECORD rename to venv/Lib/site-packages/Django-3.2.5.dist-info/RECORD index cb88392..02adfff 100644 --- a/venv/Lib/site-packages/Django-4.0.4.dist-info/RECORD +++ b/venv/Lib/site-packages/Django-3.2.5.dist-info/RECORD @@ -1,26 +1,30 @@ -../../Scripts/django-admin.exe,sha256=86UvmOsNvJjmA_qifNSQOjSlLEXm-bGDqsSsbE4W0cQ,106422 -Django-4.0.4.dist-info/AUTHORS,sha256=b2cbyCGZaQdEiQ7SzcNN-0VZ3WNdVN1mw36OUi_mlaY,39793 -Django-4.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Django-4.0.4.dist-info/LICENSE,sha256=uEZBXRtRTpwd_xSiLeuQbXlLxUbKYSn5UKGM0JHipmk,1552 -Django-4.0.4.dist-info/LICENSE.python,sha256=HqxYFuXzG1c2lJcnZYYD5mMSM9rNmrEZJCiFkfjybfs,14377 -Django-4.0.4.dist-info/METADATA,sha256=C1UXUZ5AdFJwU4AAjJWCLi0dOPtboVrhUFRSLH6f7ZQ,3977 -Django-4.0.4.dist-info/RECORD,, -Django-4.0.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -Django-4.0.4.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 -Django-4.0.4.dist-info/entry_points.txt,sha256=daYW_s0r8Z5eiRi_bNU6vodHqVUXQWzm-DHFOQHTV2Q,83 -Django-4.0.4.dist-info/top_level.txt,sha256=V_goijg9tfO20ox_7os6CcnPvmBavbxu46LpJiNLwjA,7 -django/__init__.py,sha256=ttgErs-0XjFR2CfrA3FVlmBT-EQR9dH2m4gso3NqbEo,799 +../../Scripts/__pycache__/django-admin.cpython-39.pyc,, +../../Scripts/django-admin.exe,sha256=6nVGJkrIDeFXm5ZsnrjvkTHRIsGVpVVDj4HqbJc5QQs,106422 +../../Scripts/django-admin.py,sha256=Pn4NyuLiOfbJedriyzMlXOSYwIoYB34w6oxCk80WMqE,643 +Django-3.2.5.dist-info/AUTHORS,sha256=9MnhZ18vr5pbicYVTBz2psQVvl4Vt2R_0AGr7T8gRA4,38678 +Django-3.2.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Django-3.2.5.dist-info/LICENSE,sha256=uEZBXRtRTpwd_xSiLeuQbXlLxUbKYSn5UKGM0JHipmk,1552 +Django-3.2.5.dist-info/LICENSE.python,sha256=KGS1UtMEsSD14oP7_VTQphIi5e4tJ_dmgieAi2_og3A,13227 +Django-3.2.5.dist-info/METADATA,sha256=529R0nDRcPYlyDChfofKvf-526eWiSiPcXuy1mAGMag,3939 +Django-3.2.5.dist-info/RECORD,, +Django-3.2.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Django-3.2.5.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 +Django-3.2.5.dist-info/entry_points.txt,sha256=daYW_s0r8Z5eiRi_bNU6vodHqVUXQWzm-DHFOQHTV2Q,83 +Django-3.2.5.dist-info/top_level.txt,sha256=V_goijg9tfO20ox_7os6CcnPvmBavbxu46LpJiNLwjA,7 +django/__init__.py,sha256=nLARC6yU6StCz-tj4uaazbtasluTnN72WdA_jlFlSEQ,799 django/__main__.py,sha256=9a5To1vQXqf2Jg_eh8nLvIc0GXmDjEXv4jE1QZEqBFk,211 django/__pycache__/__init__.cpython-39.pyc,, django/__pycache__/__main__.cpython-39.pyc,, django/__pycache__/shortcuts.cpython-39.pyc,, -django/apps/__init__.py,sha256=8WZTI_JnNuP4tyfuimH3_pKQYbDAy2haq-xkQT1UXkc,90 +django/apps/__init__.py,sha256=t0F4yceU4SbybMeWBvpuE6RsGaENmQCVbNSdSuXiEMs,90 django/apps/__pycache__/__init__.cpython-39.pyc,, django/apps/__pycache__/config.cpython-39.pyc,, django/apps/__pycache__/registry.cpython-39.pyc,, -django/apps/config.py,sha256=bXN_4OgE8lyeKT7TkreFuRzL4o9yXP2XuIrtTciwS60,13315 -django/apps/registry.py,sha256=0pItLFP0gw_EPbqeGdvVbSkYljeK6UVT2hL0eH7yDdg,17665 -django/conf/__init__.py,sha256=pT6dcpP5xaTBxx0xWokr4V_XxCT62hBhu6G3ZrudHoM,10808 +django/apps/config.py,sha256=jClakIsqtkVqCEBdJLUkviiG9UHUPSqfjxKfc_0wCXI,13302 +django/apps/registry.py,sha256=LY1_wYiHKjGZCKPmAB612fUje69mtrkth5vhBvE5UrM,17513 +django/bin/__pycache__/django-admin.cpython-39.pyc,, +django/bin/django-admin.py,sha256=jrlSFh4UnmMJLqRJNdJKgA_2nm24FYIEUBM0kL2RV8s,656 +django/conf/__init__.py,sha256=ocXk33i1TKVsddQ4cVkYGS2-2ilIZnm6srdxjFXPWb0,10509 django/conf/__pycache__/__init__.cpython-39.pyc,, django/conf/__pycache__/global_settings.cpython-39.pyc,, django/conf/app_template/__init__.py-tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -30,23 +34,23 @@ django/conf/app_template/migrations/__init__.py-tpl,sha256=47DEQpj8HBSa-_TImW-5J django/conf/app_template/models.py-tpl,sha256=Vjc0p2XbAPgE6HyTF6vll98A4eDhA5AvaQqsc4kQ9AQ,57 django/conf/app_template/tests.py-tpl,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60 django/conf/app_template/views.py-tpl,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63 -django/conf/global_settings.py,sha256=6h2SOLdBvFutsAiBiFdE5_xtsnuQKbtInt1FG9MdHIs,22639 -django/conf/locale/__init__.py,sha256=-5CQ68LtJOAC3ulhpP7OGGxyNdGqRG8QiCvgeMZP0YI,13588 +django/conf/global_settings.py,sha256=mH0AcvVy2wX9CqazZhR6YoU50qJ6tYHnABngR2ayCbI,22752 +django/conf/locale/__init__.py,sha256=iIM8lCB1ze6jqCA-mYk1JWn987kTMi_d3oMkhtLf2HQ,13460 django/conf/locale/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/af/LC_MESSAGES/django.mo,sha256=3KYsjZe0UVNs12pbY1C71twF3KuIQAnLD6yyFPxG0CM,21840 django/conf/locale/af/LC_MESSAGES/django.po,sha256=v1ebpND1kYlrQFEWhYwCq-Zbe1ujvf3xYqcmUBDmvZk,26530 -django/conf/locale/ar/LC_MESSAGES/django.mo,sha256=qBaEPhfJxd2mK1uPH7J06hPI3_leRPsWkVgcKtJSAvQ,35688 -django/conf/locale/ar/LC_MESSAGES/django.po,sha256=MQeB4q0H-uDLurniJP5b2SBOTETAUl9k9NHxtaw0nnU,38892 +django/conf/locale/ar/LC_MESSAGES/django.mo,sha256=QO9CMIzKOV56Mve13lc3KvkbLTQXNDxizLNsYzQWs78,35328 +django/conf/locale/ar/LC_MESSAGES/django.po,sha256=wtuLy6Ty3XdTicasSbv_2TZXM75KUQCFt3oXad_iiD0,38269 django/conf/locale/ar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ar/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ar/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ar/formats.py,sha256=EI9DAiGt1avNY-a6luMnAqKISKGHXHiKE4QLRx7wGHU,696 +django/conf/locale/ar/formats.py,sha256=nm5cnBh1YYjwD4eydBZ5AoknwN54piwrpB25ijpDT-o,696 django/conf/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=1LjYIo3qTIliodHd4Mm7Gmh2tzuZRZzc6s0zohGsiNU,35409 django/conf/locale/ar_DZ/LC_MESSAGES/django.po,sha256=a5lRnz_D4NgsMxCnU_lxE8JsQb3VEfXQ3xIXgH-VQrI,38151 django/conf/locale/ar_DZ/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ar_DZ/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ar_DZ/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ar_DZ/formats.py,sha256=T84q3oMKng-L7_xymPqYwpzs78LvvfHy2drfSRj8XjE,901 +django/conf/locale/ar_DZ/formats.py,sha256=qYoVLwXYkSbP13DGt8xHaNzru9v-7rhl_vUrpdz3Aos,907 django/conf/locale/ast/LC_MESSAGES/django.mo,sha256=XSStt50HP-49AJ8wFcnbn55SLncJCsS2lx_4UwK-h-8,15579 django/conf/locale/ast/LC_MESSAGES/django.po,sha256=7qZUb5JjfrWLqtXPRjpNOMNycbcsEYpNO-oYmazLTk4,23675 django/conf/locale/az/LC_MESSAGES/django.mo,sha256=lsm6IvKmBmUxUCqVW3RowQxKXd6TPzwGKGw7jeO5FX0,27170 @@ -54,159 +58,159 @@ django/conf/locale/az/LC_MESSAGES/django.po,sha256=yO6Qd6eTX9Z9JRVAynpwlZNz8Q8VF django/conf/locale/az/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/az/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/az/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/az/formats.py,sha256=JQoS2AYHKJxiH6TJas1MoeYgTeUv5XcNtYUHF7ulDmw,1087 -django/conf/locale/be/LC_MESSAGES/django.mo,sha256=FZivGaKrh1M4tAxfZGF-H1koGnof248obvfA6Xw57rY,36467 -django/conf/locale/be/LC_MESSAGES/django.po,sha256=N2laJ6pArev1YBKRfqpeez0LbULVaIt7TNNlIX_5nWM,39028 -django/conf/locale/bg/LC_MESSAGES/django.mo,sha256=FqbeKBkf9BqYntobFo98EnL5NeHy6DsSIXuinIWoCHE,33758 -django/conf/locale/bg/LC_MESSAGES/django.po,sha256=3FL505XlkCzIA_peYeeb2O-eKS4hQbOKRW7OSCtAX08,36197 +django/conf/locale/az/formats.py,sha256=Nk4qQqSl5CVtmA2sPA2WQAm12JZowENXH3TjiMxFZuk,1105 +django/conf/locale/be/LC_MESSAGES/django.mo,sha256=IrObGY3L3hf4NuAusYrbwulOK9MCxLJXW4BMHGQGYhE,36239 +django/conf/locale/be/LC_MESSAGES/django.po,sha256=jYUPKFD52M3aYKMJ_05rzMIF-fq_qtnjW0A2412DZEE,38798 +django/conf/locale/bg/LC_MESSAGES/django.mo,sha256=CEeXFNvizpLjS7RcTeiohGJN_s6YqpPn8JUMwjb6lHY,23422 +django/conf/locale/bg/LC_MESSAGES/django.po,sha256=4YwzvFO8IBWjWbMIMBMZBa9HnbJHWoI24zD1cWhl1tI,30133 django/conf/locale/bg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/bg/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/bg/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/bg/formats.py,sha256=LC7P_5yjdGgsxLQ_GDtC8H2bz9NTxUze_CAtzlm37TA,705 +django/conf/locale/bg/formats.py,sha256=iC9zYHKphMaSnluBZfYvH1kV5aDyl3ycsqVjxOoqfOY,705 django/conf/locale/bn/LC_MESSAGES/django.mo,sha256=sB0RIFrGS11Z8dx5829oOFw55vuO4vty3W4oVzIEe8Q,16660 django/conf/locale/bn/LC_MESSAGES/django.po,sha256=rF9vML3LDOqXkmK6R_VF3tQaFEoZI7besJAPx5qHNM0,26877 django/conf/locale/bn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/bn/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/bn/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/bn/formats.py,sha256=jynhZ9XNNuxTXeF7f2FrJYYZuFwlLY58fGfQ6gVs7s8,964 -django/conf/locale/br/LC_MESSAGES/django.mo,sha256=Xow2-sd55CZJsvfF8axtxXNRe27EDwxKixCGelVQ4aU,14009 -django/conf/locale/br/LC_MESSAGES/django.po,sha256=ODCUDdEDAvsOVOAr49YiWT2YQaBZmc-38brdgYWc8Bs,24293 +django/conf/locale/bn/formats.py,sha256=INeNl0xlt9B-YJTkcdC2kSpJLly9d5AKT60GMyS-Bm4,964 +django/conf/locale/br/LC_MESSAGES/django.mo,sha256=_FgUCO-4axk8vTwhhf0JejW8jNjk8lmJEcha8V4vP_U,14291 +django/conf/locale/br/LC_MESSAGES/django.po,sha256=6AExAxT4GQRuzTgv6rtAJkxPEhUXWZeNqFVzNTF9jRs,24356 django/conf/locale/bs/LC_MESSAGES/django.mo,sha256=Xa5QAbsHIdLkyG4nhLCD4UHdCngrw5Oh120abCNdWlA,10824 django/conf/locale/bs/LC_MESSAGES/django.po,sha256=IB-2VvrQKUivAMLMpQo1LGRAxw3kj-7kB6ckPai0fug,22070 django/conf/locale/bs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/bs/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/bs/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/bs/formats.py,sha256=760m-h4OHpij6p_BAD2dr3nsWaTb6oR1Y5culX9Gxqw,705 -django/conf/locale/ca/LC_MESSAGES/django.mo,sha256=ssxRpHM0lFShsLwaRoEVs_vRR4coEVkD3ne8mEaU7PI,27427 -django/conf/locale/ca/LC_MESSAGES/django.po,sha256=DARV_Phd8ugGNdL36iaFgsuUMeV3_rA9vEmK_nuh57E,29996 +django/conf/locale/bs/formats.py,sha256=NltIKZw0-WnZW0QY2D2EqqdctUyNc8FEARZ1RRYKtHo,705 +django/conf/locale/ca/LC_MESSAGES/django.mo,sha256=Jkn81L1cKRUxA1Ea_KwTL2eb93aq45FCUweAkNSKcCk,26414 +django/conf/locale/ca/LC_MESSAGES/django.po,sha256=4A9osLUdzYk2JYpvucf227eeLYn1eU-Q8opHkai-AVg,29390 django/conf/locale/ca/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ca/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ca/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ca/formats.py,sha256=6czxXIeGs7BYpAbZVLSFtHXCRleftoPRRabRhRXuKPM,955 -django/conf/locale/cs/LC_MESSAGES/django.mo,sha256=16hlLpPmncCdlC8_s8QDvzTC_43ZN1vofO7qf2bTR68,29207 -django/conf/locale/cs/LC_MESSAGES/django.po,sha256=zDHi4FqaihdMELv-D0ZP8LPPv7N6GPVNUW9Om5---kk,31971 +django/conf/locale/ca/formats.py,sha256=rQJTIIy-DNSu0mASIoXLHWpS8rVar64zkJ-NTM1VMTM,951 +django/conf/locale/cs/LC_MESSAGES/django.mo,sha256=OYfRm7uMXwPzMeGOk6HnQuwqSz8p-aE5FRJumLGHbRE,29027 +django/conf/locale/cs/LC_MESSAGES/django.po,sha256=19qv8rtJ1wm_C_82bwUAi_NlByONaFpaXzqK9_0PP2w,31791 django/conf/locale/cs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/cs/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/cs/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/cs/formats.py,sha256=3MA70CW0wfr0AIYvYqE0ACmX79tNOx-ZdlR6Aetp9e8,1539 +django/conf/locale/cs/formats.py,sha256=SdYIul8ycV5SOzm1gCYmZLqYfZnlxqPbPFW8KuwevnM,1549 django/conf/locale/cy/LC_MESSAGES/django.mo,sha256=s7mf895rsoiqrPrXpyWg2k85rN8umYB2aTExWMTux7s,18319 django/conf/locale/cy/LC_MESSAGES/django.po,sha256=S-1PVWWVgYmugHoYUlmTFAzKCpI81n9MIAhkETbpUoo,25758 django/conf/locale/cy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/cy/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/cy/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/cy/formats.py,sha256=NY1pYPfpu7XjLMCCuJk5ggdpLcufV1h101ojyxfPUrY,1355 -django/conf/locale/da/LC_MESSAGES/django.mo,sha256=guEjF0Lgf_ZqZisWHqPFXYXZFzWP5SKdxO4taCx7mPU,26957 -django/conf/locale/da/LC_MESSAGES/django.po,sha256=GzODGRHoDZA1paoctBSpE4YMyQ0qebQxb1aNl5_n38M,29332 +django/conf/locale/cy/formats.py,sha256=Rg9qe-bsk7MXrSfQyDOHOsa9m0qey18nqocar93GuF4,1594 +django/conf/locale/da/LC_MESSAGES/django.mo,sha256=0JF9j6U_rL2W-ZWccdacEQe0YWFrEoC_-uiqvTDKIkk,26810 +django/conf/locale/da/LC_MESSAGES/django.po,sha256=gYJmSGK4o56GoVSB4Obihl2iAs6pC1k_QhgyOFlGlZA,29183 django/conf/locale/da/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/da/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/da/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/da/formats.py,sha256=-y3033Fo7COyY0NbxeJVYGFybrnLbgXtRf1yBGlouys,876 -django/conf/locale/de/LC_MESSAGES/django.mo,sha256=WBYBgUO9yULQcu6HgA7lOAcgF-13t_HXhySUUdm1z1g,28373 -django/conf/locale/de/LC_MESSAGES/django.po,sha256=rxizy5S92pZkKFFrd2Zb0sOxpRWDRmCkTWNkVWw9j_0,30798 +django/conf/locale/da/formats.py,sha256=jquE6tLj9nOxcGtH_326w57sH9BKhP4BKtPz6eCi4k8,941 +django/conf/locale/de/LC_MESSAGES/django.mo,sha256=mJi5sIz8AXi5UG37KwF3ZeuqyCM4wAKXZ6qTeDgauTU,28125 +django/conf/locale/de/LC_MESSAGES/django.po,sha256=G_HkvboL5tJNlY3b-B3k0UXvXIPLz_2tJX5zJZCMqko,30312 django/conf/locale/de/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/de/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/de/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/de/formats.py,sha256=fysX8z5TkbPUWAngoy_sMeFGWp2iaNU6ftkBz8cqplg,996 +django/conf/locale/de/formats.py,sha256=cboIdd5DucaqpBdqckaZG6rEhu-OYubCNzrq-qYx0Uo,992 django/conf/locale/de_CH/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/de_CH/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/de_CH/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/de_CH/formats.py,sha256=22UDF62ESuU0Jp_iNUqAj-Bhq4_-frpji0-ynBdHXYk,1377 -django/conf/locale/dsb/LC_MESSAGES/django.mo,sha256=EuJv_1tQQTRMDhy7XwIl69Hv4UISRQq6oLgFI8BHmng,29802 -django/conf/locale/dsb/LC_MESSAGES/django.po,sha256=5QeIB5sHXmPKjsbAMEkTVjYjA4KB6brUSVJjxi2-Z54,32262 -django/conf/locale/el/LC_MESSAGES/django.mo,sha256=P5lTOPFcl9x6_j69ZN3hM_mQbhW7Fbbx02RtTNJwfS0,33648 -django/conf/locale/el/LC_MESSAGES/django.po,sha256=rZCComPQcSSr8ZDLPgtz958uBeBZsmV_gEP-sW88kRA,37123 +django/conf/locale/de_CH/formats.py,sha256=EIMNT_qD3RAyWYNbDh7EeKwDN_B4sij6PCy64Ypts-I,1373 +django/conf/locale/dsb/LC_MESSAGES/django.mo,sha256=WFMXoI8OOsBZIS55Xd3b5XWZyhplgcGtv_t_o9djQMk,29585 +django/conf/locale/dsb/LC_MESSAGES/django.po,sha256=LPUqiRhs64nJnXEZR8Ow8eKD8Tb922jaYcTO3XRNl3o,32041 +django/conf/locale/el/LC_MESSAGES/django.mo,sha256=NHyfX4V00FKgGev0vrjo_2GtXtGdhnCBFcg2n2rJn48,34298 +django/conf/locale/el/LC_MESSAGES/django.po,sha256=z1mRXQQry_1W5x3Jkor2ccOEF4wCTsbhCmUkfGXLZPA,37315 django/conf/locale/el/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/el/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/el/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/el/formats.py,sha256=RON2aqQaQK3DYVF_wGlBQJDHrhANxypcUW_udYKI-ro,1241 +django/conf/locale/el/formats.py,sha256=w_3KgU9IKNr7BUQw81drogSqEyK8Zw8W0O73EGiRxIw,1257 django/conf/locale/en/LC_MESSAGES/django.mo,sha256=mVpSj1AoAdDdW3zPZIg5ZDsDbkSUQUMACg_BbWHGFig,356 -django/conf/locale/en/LC_MESSAGES/django.po,sha256=fR3-Y7427YEPhTTKIx6nN5MpJq0eetglzz9iR46Z49k,29556 +django/conf/locale/en/LC_MESSAGES/django.po,sha256=xBs1yW9APHIOKRxkUYELndKLUB8e78PjEFsFKHEVjcI,29394 django/conf/locale/en/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/en/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/en/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/en/formats.py,sha256=VTQUhaZ_WFhS5rQj0PxbnoMySK0nzUSqrd6Gx-DtXxI,2438 -django/conf/locale/en_AU/LC_MESSAGES/django.mo,sha256=SntsKx21R2zdjj0D73BkOXGTDnoN5unsLMJ3y06nONM,25633 -django/conf/locale/en_AU/LC_MESSAGES/django.po,sha256=6Qh4Z6REzhUdG5KwNPNK9xgLlgq3VbAJuoSXyd_eHdE,28270 +django/conf/locale/en/formats.py,sha256=sr2fzOex-HRdvbYTr_bUiZFSQWyPpN2y5eq_h6zyceQ,1620 +django/conf/locale/en_AU/LC_MESSAGES/django.mo,sha256=js3_n3k5hOtU15__AYZ7pFtpfubIeoXZlav05O27sNg,15223 +django/conf/locale/en_AU/LC_MESSAGES/django.po,sha256=lvkcp457FspF5rNwHKY4RndXCdcjaRVygndWRsdKm4M,23302 django/conf/locale/en_AU/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/en_AU/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/en_AU/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/en_AU/formats.py,sha256=BoI5UviKGZ4TccqLmxpcdMf0Yk1YiEhY_iLQUddjvi0,1650 +django/conf/locale/en_AU/formats.py,sha256=q_a1ONYb130Lb5XIAbkbFRO_qgRk71tDi2grqiClAhw,1889 django/conf/locale/en_GB/LC_MESSAGES/django.mo,sha256=jSIe44HYGfzQlPtUZ8tWK2vCYM9GqCKs-CxLURn4e1o,12108 django/conf/locale/en_GB/LC_MESSAGES/django.po,sha256=PTXvOpkxgZFRoyiqftEAuMrFcYRLfLDd6w0K8crN8j4,22140 django/conf/locale/en_GB/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/en_GB/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/en_GB/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/en_GB/formats.py,sha256=cJN8YNthkIOHCIMnwiTaSZ6RCwgSHkjWYMcfw8VFScE,1650 +django/conf/locale/en_GB/formats.py,sha256=vJUVE_XIGGcwvGiO6tl2oNNzKSz1KjYOc8HnSvqhokg,1889 django/conf/locale/eo/LC_MESSAGES/django.mo,sha256=G5VNi-7AMozkh0vK3UhPJi1tCbqJWSTeFSMoHo4jfvM,20433 django/conf/locale/eo/LC_MESSAGES/django.po,sha256=0UwyG4ncz-dI6tkC2gLWg56EgmU1YLBspAh50Caj7-o,26212 django/conf/locale/eo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/eo/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/eo/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/eo/formats.py,sha256=zIEAk-SiLX0cvQVmRc3LpmV69jwRrejMMdC7vtVsSh0,1715 -django/conf/locale/es/LC_MESSAGES/django.mo,sha256=apr2uf-Xnvai6GkGtYtX7yl3v9HnI27yysJl63e5OA8,27700 -django/conf/locale/es/LC_MESSAGES/django.po,sha256=DL6VVKfPlZ62XEGv1yiexHFu0XW8NKSqNxdq4vgDcHY,31003 +django/conf/locale/eo/formats.py,sha256=MTipqX6SDmgmGbY9gVTMdthz2lvF_caBNgzWDxYVt50,2162 +django/conf/locale/es/LC_MESSAGES/django.mo,sha256=-7dqO1YrC5WPMz-oa90Nb9DT2Q2ZBVHvv77D_6tchnc,27567 +django/conf/locale/es/LC_MESSAGES/django.po,sha256=FHDorWXhlIltTaptu0Lda6hLWV_jX8Bfb_Xw_F9YKSo,30791 django/conf/locale/es/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es/formats.py,sha256=j2k3I4e_4ePYFsJbkREtmknqlhqDPxSrvWN3lIQE0oA,953 -django/conf/locale/es_AR/LC_MESSAGES/django.mo,sha256=E06Np7SoEVVznN1sxSKzceWS15w1NStJJRL0BBqHGBU,28082 -django/conf/locale/es_AR/LC_MESSAGES/django.po,sha256=mxJPl0ibBWPZRLtyujymKf7yUubodsVQrihn4W-FZks,30245 +django/conf/locale/es/formats.py,sha256=Z-aM3Z7h7Fjk2SAWKhnUYiuKbHpc7nZZ3-wnelK0NwI,949 +django/conf/locale/es_AR/LC_MESSAGES/django.mo,sha256=tOu9LkbltfWklTlCRdR_n8_XfqPGblDINQXoGmOiSE8,27925 +django/conf/locale/es_AR/LC_MESSAGES/django.po,sha256=ek_VC-_rVpU9PXJsFPRK0NV9WWSkqF06uCQDd5_jeK0,30086 django/conf/locale/es_AR/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es_AR/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es_AR/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es_AR/formats.py,sha256=4qgOJoR2K5ZE-pA2-aYRwFW7AbK-M9F9u3zVwgebr2w,935 +django/conf/locale/es_AR/formats.py,sha256=wY64-6a2hajRveIgJLpkKES_v-QejkkgExdnnJdYN1E,935 django/conf/locale/es_CO/LC_MESSAGES/django.mo,sha256=ehUwvqz9InObH3fGnOLuBwivRTVMJriZmJzXcJHsfjc,18079 django/conf/locale/es_CO/LC_MESSAGES/django.po,sha256=XRgn56QENxEixlyix3v4ZSTSjo4vn8fze8smkrv_gc4,25107 django/conf/locale/es_CO/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es_CO/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es_CO/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es_CO/formats.py,sha256=0uAbBvOkdJZKjvhrrd0htScdO7sTgbofOkkC8A35_a8,691 +django/conf/locale/es_CO/formats.py,sha256=kvTsKSaK7oDWK6a-SeO3V3e__64SjtDBMWoq0ouVDJ4,700 django/conf/locale/es_MX/LC_MESSAGES/django.mo,sha256=CWpmhzGDdfUZ1k59EqU9MmZI_fvzFHsnDFTwV_qWqj0,17424 django/conf/locale/es_MX/LC_MESSAGES/django.po,sha256=BLUYqbCL9Jw_S5f-7Us_vLbkc8Qjz1Uq83bdX6rg4zU,25029 django/conf/locale/es_MX/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es_MX/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es_MX/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es_MX/formats.py,sha256=fBvyAqBcAXARptSE3hxwzFYNx3lEE8QrhNrCWuuGNlA,768 +django/conf/locale/es_MX/formats.py,sha256=tny9CPrJJV5qRJ_myuiQ8fMfg3fnNtv3q6aOSxLdK0E,799 django/conf/locale/es_NI/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es_NI/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es_NI/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es_NI/formats.py,sha256=UiOadPoMrNt0iTp8jZVq65xR_4LkOwp-fjvFb8MyNVg,711 +django/conf/locale/es_NI/formats.py,sha256=QMfHoEWcpR_8yLaE66w5UjmPjtgTAU7Yli8JHgSxGRI,740 django/conf/locale/es_PR/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/es_PR/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/es_PR/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/es_PR/formats.py,sha256=VVTlwyekX80zCKlg1P4jhaAdKNpN5I64pW_xgrhpyVs,675 +django/conf/locale/es_PR/formats.py,sha256=mYKWumkfGElGDL92G0nO_loBoSOOFKs0ktsI3--nlLQ,671 django/conf/locale/es_VE/LC_MESSAGES/django.mo,sha256=h-h1D_Kr-LI_DyUJuIG4Zbu1HcLWTM1s5X515EYLXO8,18840 django/conf/locale/es_VE/LC_MESSAGES/django.po,sha256=Xj38imu4Yw-Mugwge5CqAqWlcnRWnAKpVBPuL06Twjs,25494 -django/conf/locale/et/LC_MESSAGES/django.mo,sha256=AAtf-jezxKyK4-4vqKpCnbTRmXW2Qij4YA2X8ckt6Jw,26794 -django/conf/locale/et/LC_MESSAGES/django.po,sha256=6DeZWGsqrsoNbsPYa-1d7Q0F0fyWQAzqnXXn6aUcmX0,29326 +django/conf/locale/et/LC_MESSAGES/django.mo,sha256=Ca2VcfkCDlKm64IohDlZ_JPTpk09BZJ1qhttUHz0CXg,26646 +django/conf/locale/et/LC_MESSAGES/django.po,sha256=mf16HWWeQpCnvghI3fHB9vHj8hb_5HDLKms73cUgces,29176 django/conf/locale/et/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/et/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/et/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/et/formats.py,sha256=DyFSZVuGSYGoImrRI2FodeM51OtvIcCkKzkI0KvYTQw,707 +django/conf/locale/et/formats.py,sha256=kD0IrKxW4AlMhS6fUEXUtyPWfsdLuBzdDHiEmdfzadQ,707 django/conf/locale/eu/LC_MESSAGES/django.mo,sha256=EFcIbNvLcmCCW2S2A5icZ7pArQzjEgQhGtXuU3NMEPA,21743 django/conf/locale/eu/LC_MESSAGES/django.po,sha256=aZR0jWdLCcfMfWIc-WErfNpeEa3rz3SkTKFmL97OKs0,27112 django/conf/locale/eu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/eu/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/eu/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/eu/formats.py,sha256=-PuRA6eHeXP8R3YV0aIEQRbk2LveaZk-_kjHlBT-Drg,749 -django/conf/locale/fa/LC_MESSAGES/django.mo,sha256=4wb3eCOGWADBdwvAtqc3xekw1o5oYL5wwSFlB4Rnq8w,32052 -django/conf/locale/fa/LC_MESSAGES/django.po,sha256=TRHzYO6sBTpASuuuiUXtGvEb--o5d-gckCFSRbTxukM,34832 +django/conf/locale/eu/formats.py,sha256=R-Ex1e1CoDDIul2LGuhXH5-ZBsiRpTerqxqRAmB8gFM,749 +django/conf/locale/fa/LC_MESSAGES/django.mo,sha256=9YYslrspOQN7Jd1pr1U9I64h35hpNkvGdTC77DKw1S4,31497 +django/conf/locale/fa/LC_MESSAGES/django.po,sha256=jyKkkTekW10qKAnq1SKiPQJj5oS1SYq4LhRII4f5dZ8,34304 django/conf/locale/fa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/fa/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/fa/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/fa/formats.py,sha256=v0dLaIh6-CWCAQHkmX0PaIlA499gTeRcJEi7lVJzw9o,722 -django/conf/locale/fi/LC_MESSAGES/django.mo,sha256=lw8RBCTTc5Tsl3u7FuCx-6T1wYQ0a4vJmyEEiUwm3Rk,27454 -django/conf/locale/fi/LC_MESSAGES/django.po,sha256=O2uiltiQtNUK4kYSqyyNx66e9FiS7q9N6iyEFMAbDjI,29760 +django/conf/locale/fa/formats.py,sha256=RCDlj-iiAS7MVgWwOFSvQ_-QROhBm-7d8OP6QhkcGZw,722 +django/conf/locale/fi/LC_MESSAGES/django.mo,sha256=nv_oUXyAkazEJOoi7o-KVZa1T5bV240fvjQbJakDLpA,27292 +django/conf/locale/fi/LC_MESSAGES/django.po,sha256=xeT795wRyv6fc4X67rRT644ZWxbcE1y5Zc__8i4vpO8,29547 django/conf/locale/fi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/fi/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/fi/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/fi/formats.py,sha256=CO_wD5ZBHwAVgjxArXktLCD7M-PPhtHbayX_bBKqhlA,1213 -django/conf/locale/fr/LC_MESSAGES/django.mo,sha256=rTOlY_ntQgkkBn_sxrnaMGw1kl1Ym5cTL4HIJkRzGSk,28628 -django/conf/locale/fr/LC_MESSAGES/django.po,sha256=MCFFPl9ZCxzkVlgBjvOMS9HZ7mQYfhOcvtbY6sDH0gc,31027 +django/conf/locale/fi/formats.py,sha256=j0LGmhPEQWf6rpShWVu5Vk-C7PaZrjZNpYzQf0HPAGA,1241 +django/conf/locale/fr/LC_MESSAGES/django.mo,sha256=61txaIVtXAt5qXgPwIOEUwUFuo5uqTpKj2TrnJO7Fvs,28479 +django/conf/locale/fr/LC_MESSAGES/django.po,sha256=FfhJ9CZd-SrEG2STJcu4KAMYsQTT5AKOed4jI8m0vP8,30876 django/conf/locale/fr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/fr/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/fr/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/fr/formats.py,sha256=Idd_fVXKJHJSOuB3jRbo_FgwQ2P6VK2AjJbadv5UxK8,1293 +django/conf/locale/fr/formats.py,sha256=3cj753pnN0YY-EUjyCIkiSAnpHpoE02eNB9UCtd2d88,1286 django/conf/locale/fy/LC_MESSAGES/django.mo,sha256=9P7zoJtaYHfXly8d6zBoqkxLM98dO8uI6nmWtsGu-lM,2286 django/conf/locale/fy/LC_MESSAGES/django.po,sha256=jveK-2MjopbqC9jWcrYbttIb4DUmFyW1_-0tYaD6R0I,19684 django/conf/locale/fy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -218,87 +222,87 @@ django/conf/locale/ga/LC_MESSAGES/django.po,sha256=rppcWQVozZdsbl7Gud6KnJo6yDB8T django/conf/locale/ga/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ga/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ga/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ga/formats.py,sha256=Qh7R3UMfWzt7QIdMZqxY0o4OMpVsqlchHK7Z0QnDWds,682 -django/conf/locale/gd/LC_MESSAGES/django.mo,sha256=2VKzI7Nqd2NjABVQGdcduWHjj0h2b3UBGQub7xaTVPs,30752 -django/conf/locale/gd/LC_MESSAGES/django.po,sha256=3PfuhhmosuarfPjvM2TVf2kHhZaw5_G8oIM2VWTc3gI,33347 +django/conf/locale/ga/formats.py,sha256=Kotsp4o-6XvJ1sQrxIaab3qEW2k4oyPdJhcqvlgbGnU,682 +django/conf/locale/gd/LC_MESSAGES/django.mo,sha256=g8tn9MsfYK0rnPAY6R3vtznqtTRBBJ6mZDhj-42lO4c,29604 +django/conf/locale/gd/LC_MESSAGES/django.po,sha256=VXKJ67LTz1PNHoTcm5qRq2rgxYeRloVEg-iIm-B_YjQ,32574 django/conf/locale/gd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/gd/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/gd/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/gd/formats.py,sha256=7doL7JIoCqA_o-lpCwM3jDHMpptA3BbSgeLRqdZk8Lc,715 +django/conf/locale/gd/formats.py,sha256=tWbR1bTImiH457bq3pEyqdr4H2ONUdhOv2rZ2cYUdC8,715 django/conf/locale/gl/LC_MESSAGES/django.mo,sha256=utB99vnkb5SLff8K0i3gFI8Nu_eirBxDEpFKbZ_voPY,14253 django/conf/locale/gl/LC_MESSAGES/django.po,sha256=rvhCJsURGjM2ekm6NBjY5crVGc5lrQv2qpHj35dM3qc,23336 django/conf/locale/gl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/gl/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/gl/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/gl/formats.py,sha256=ygSFv-YTS8htG_LW0awegkkOarPRTZNPbUck5sxkAwI,757 -django/conf/locale/he/LC_MESSAGES/django.mo,sha256=46lIe8tACJ_ga70yOY5qNNDIZhvGZAqNh25zHRoBo_c,30227 -django/conf/locale/he/LC_MESSAGES/django.po,sha256=NrzjGVZoDiXeg6Uolt8m9emSNHpmOCzzIxnyipggDzo,33362 +django/conf/locale/gl/formats.py,sha256=Tr41ECf7XNn4iekgPGUSKI6-lDkcHj1SaHno5gPa5hw,757 +django/conf/locale/he/LC_MESSAGES/django.mo,sha256=6SM5oV1-EvXtELx77SyH16DCtJ2mgr2v6NliXrYD2Pw,31202 +django/conf/locale/he/LC_MESSAGES/django.po,sha256=pnL2ePAMKbg9JiEHJtTaMEvdVufJMeqBfKNumCKlqKE,33600 django/conf/locale/he/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/he/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/he/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/he/formats.py,sha256=M-tu-LmTZd_oYPNH6CZEsdxJN526RUOfnLHlQxRL0N0,712 +django/conf/locale/he/formats.py,sha256=-3Yt81fQFRo7ZwRpwTdTTDLLtbMdGSyC5n5RWcnqINU,712 django/conf/locale/hi/LC_MESSAGES/django.mo,sha256=Zi72xDA1RVm3S5Y9_tRA52_wke8PlvomklvUJBXwiF0,17619 django/conf/locale/hi/LC_MESSAGES/django.po,sha256=R_DRspzGYZ5XxXS4OvpVD4EEVZ9LY3NzrfzD2LbXqIg,27594 django/conf/locale/hi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/hi/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/hi/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/hi/formats.py,sha256=JArVM9dMluSP-cwpZydSVXHB5Vs9QKyR9c-bftI9hds,684 +django/conf/locale/hi/formats.py,sha256=dBY0JvWinGeNiDy4ZrnrtPaZQdwU7JugkzHE22C-M0A,684 django/conf/locale/hr/LC_MESSAGES/django.mo,sha256=HP4PCb-i1yYsl5eqCamg5s3qBxZpS_aXDDKZ4Hlbbcc,19457 django/conf/locale/hr/LC_MESSAGES/django.po,sha256=qeVJgKiAv5dKR2msD2iokSOApZozB3Gp0xqzC09jnvs,26329 django/conf/locale/hr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/hr/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/hr/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/hr/formats.py,sha256=F4mIdDoaOYJ_lPmsJ_6bQo4Zj8pOSVwuldm92zRy4Fo,1723 -django/conf/locale/hsb/LC_MESSAGES/django.mo,sha256=lD79U1OLYaJKq3Q6XrPMTmkrX3qwLiGbr8c-T-Oe6mg,29491 -django/conf/locale/hsb/LC_MESSAGES/django.po,sha256=MkKgegENDgUK9urEUskxfZ__ETOR6IbXqgxyUJq47q0,31920 +django/conf/locale/hr/formats.py,sha256=raeIndCpFhC146U_RnfVZPgBzqyHSEYRmWytkuYXvsY,1802 +django/conf/locale/hsb/LC_MESSAGES/django.mo,sha256=1bmQrnUPSUob97hyck-IJz-UjjQ2KJiCWvRvSvPbYZE,29253 +django/conf/locale/hsb/LC_MESSAGES/django.po,sha256=KKERxHid9-31LiUQ6s966X23HWS-f_FO1YPeTWNgkqU,31678 django/conf/locale/hu/LC_MESSAGES/django.mo,sha256=zORP8fLsHnlY5RRY7i_mlVlx8f3erqSfPziYNAOIAe8,28217 django/conf/locale/hu/LC_MESSAGES/django.po,sha256=6zz8Tvs_InxZDyuSKyo7f-JbNVM2JjkE_XXYbb0skMk,30680 django/conf/locale/hu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/hu/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/hu/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/hu/formats.py,sha256=xAD7mNsC5wFA2_KGRbBMPKwj884pq0jCKmXhEenGAEk,1001 +django/conf/locale/hu/formats.py,sha256=weO4ndGVlEDNDLKYi2YRtCeyxoUj2kSrYYPO_cV1I7Q,1008 django/conf/locale/hy/LC_MESSAGES/django.mo,sha256=KfmTnB-3ZUKDHeNgLiego2Af0WZoHTuNKss3zE-_XOE,22207 django/conf/locale/hy/LC_MESSAGES/django.po,sha256=kNKlJ5NqZmeTnnxdqhmU3kXiqT9t8MgAFgxM2V09AIc,28833 -django/conf/locale/ia/LC_MESSAGES/django.mo,sha256=JcrpersrDAoJXrD3AnPYBCQyGJ-6kUzH_Q8StbqmMeE,21428 -django/conf/locale/ia/LC_MESSAGES/django.po,sha256=LG0juYDjf3KkscDxwjY3ac6H1u5BBwGHljW3QWvr1nc,26859 -django/conf/locale/id/LC_MESSAGES/django.mo,sha256=Fnu4dhDrt6pGQYDxXX9p1B-LX9fLMhY_qqbfl6blqSo,26822 -django/conf/locale/id/LC_MESSAGES/django.po,sha256=heyxYms8XAlxQFevF_rXIAojtvbqQCy94K6Nz0Ph3P4,29126 +django/conf/locale/ia/LC_MESSAGES/django.mo,sha256=drP4pBfkeaVUGO2tAB6r-IUu2cvDQiOWUJfPqsA0iEo,18430 +django/conf/locale/ia/LC_MESSAGES/django.po,sha256=VKowp9naiGfou8TrMutWPoUob-tDFB6W99yswHInNcw,24900 +django/conf/locale/id/LC_MESSAGES/django.mo,sha256=cy4STOBeJFJ5AqCK2s2kXfKRcN-1vp_T_Sev0L3OlXM,26111 +django/conf/locale/id/LC_MESSAGES/django.po,sha256=OL3bUbqB2muAgoL9AErXFXqYeKvnGZCnnJIZIMUtB2g,28753 django/conf/locale/id/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/id/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/id/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/id/formats.py,sha256=kYyOxWHN3Jyif3rFxLFyBUjTzFUwmuaLrkw5JvGbEz8,1644 +django/conf/locale/id/formats.py,sha256=1nQp6j9Vfn7RQLsGV1bGdrLpgouo_ePnLnRXpOfNjD8,1920 django/conf/locale/ig/LC_MESSAGES/django.mo,sha256=tAZG5GKhEbrUCJtLrUxzmrROe1RxOhep8w-RR7DaDYo,27188 django/conf/locale/ig/LC_MESSAGES/django.po,sha256=DB_I4JXKMY4M7PdAeIsdqnLSFpq6ImkGPCuY82rNBpY,28931 django/conf/locale/ig/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ig/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ig/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ig/formats.py,sha256=P3IsxhF5rNFZ5nCWUSyJfFLb0V1QdX_Xn-tYdrcll5Q,1119 +django/conf/locale/ig/formats.py,sha256=x9zavr9_4PcNhy3CslobP2vYY4eEyjHavNFYIQtMu_Q,1161 django/conf/locale/io/LC_MESSAGES/django.mo,sha256=uI78C7Qkytf3g1A6kVWiri_CbS55jReO2XmRfLTeNs0,14317 django/conf/locale/io/LC_MESSAGES/django.po,sha256=FyN4ZTfNPV5TagM8NEhRts8y_FhehIPPouh_MfslnWY,23124 -django/conf/locale/is/LC_MESSAGES/django.mo,sha256=1pFU-dTPg2zs87L0ZqFFGS9q-f-XrzTOlhKujlyNL2E,24273 -django/conf/locale/is/LC_MESSAGES/django.po,sha256=76cQ_9DLg1jR53hiKSc1tLUMeKn8qTdPwpHwutEK014,28607 +django/conf/locale/is/LC_MESSAGES/django.mo,sha256=maGVBXO0fgqWsPbW76d27KpZ7HWwJc3QYFlMiWp_-uk,25331 +django/conf/locale/is/LC_MESSAGES/django.po,sha256=MSXrykvaFIMMp07dGV656mwl4hvoapq42ltNtvvWlrI,28802 django/conf/locale/is/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/is/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/is/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/is/formats.py,sha256=scsNfP4vVacxWIoN03qc2Fa3R8Uh5Izr1MqBicrAl3A,688 -django/conf/locale/it/LC_MESSAGES/django.mo,sha256=TT9-lL6zoRHuxegdPT5m9EpSxSy5bK9JXr3LFXsnsgI,27374 -django/conf/locale/it/LC_MESSAGES/django.po,sha256=CIHNxNJ45orebW0I6VqVTSHOGiEcmmOnmLGwfYefDKI,30205 +django/conf/locale/is/formats.py,sha256=4BbmtZUfTOsQ818Qi6NEZ54QUwd2I8H2wbnaTe0Df74,688 +django/conf/locale/it/LC_MESSAGES/django.mo,sha256=q8Yb29m2wz6CTcPj_jxau4At0ogMaja88ZwJjxuOMeY,27284 +django/conf/locale/it/LC_MESSAGES/django.po,sha256=vniTmzgNshqTbnCAczDEUopQsw1gAJNFB0Yw_iTSvUM,30040 django/conf/locale/it/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/it/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/it/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/it/formats.py,sha256=KzkSb3KXBwfM3gk2FezyR-W8_RYKpnlFeFuIi5zl-S0,1774 -django/conf/locale/ja/LC_MESSAGES/django.mo,sha256=PO_AhwSIFA2XXRMoDG2hbzxsP8IaQXgAW_vtKQDQMDg,30047 -django/conf/locale/ja/LC_MESSAGES/django.po,sha256=CznWWpAbgmFc7i8uld3mpeKwR39mld--4BoXOCb7Los,32447 +django/conf/locale/it/formats.py,sha256=BZvSU2pEB_5L3onVxeUXmASHn7HihVmNxNgubOTkMF4,1801 +django/conf/locale/ja/LC_MESSAGES/django.mo,sha256=YvoSgafrPT6X3sV2n8pRVI_uLFmFua-STAxI9uVU84M,29240 +django/conf/locale/ja/LC_MESSAGES/django.po,sha256=cM3GnHJgFqCdfPB-Fo12M47enZi_8B92Ez2eVEeeEhM,31939 django/conf/locale/ja/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ja/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ja/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ja/formats.py,sha256=MQ1KA6l1qmW07rXLYplRs-V1hR1Acbx30k2RpXnMhQg,729 +django/conf/locale/ja/formats.py,sha256=V6eTbaEUuWeJr-2NEAdQr08diKzOlFox1DbugC5xHpk,729 django/conf/locale/ka/LC_MESSAGES/django.mo,sha256=4e8at-KNaxYJKIJd8r6iPrYhEdnaJ1qtPw-QHPMh-Sc,24759 django/conf/locale/ka/LC_MESSAGES/django.po,sha256=pIgaLU6hXgVQ2WJp1DTFoubI7zHOUkkKMddwV3PTdt8,32088 django/conf/locale/ka/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ka/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ka/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ka/formats.py,sha256=elTGOjS-mxuoSCAKOm8Wz2aLfh4pWvNyClUFcrYq9ng,1861 +django/conf/locale/ka/formats.py,sha256=5QZHpBvZ91i_hjfv_aoI6tq1EuSNp6Oq_wqmrJlrJjg,1897 django/conf/locale/kab/LC_MESSAGES/django.mo,sha256=x5Kyq2Uf3XNlQP06--4lT8Q1MacA096hZbyMJRrHYIc,7139 django/conf/locale/kab/LC_MESSAGES/django.po,sha256=DsFL3IzidcAnPoAWIfIbGJ6Teop1yKPBRALeLYrdiFA,20221 django/conf/locale/kk/LC_MESSAGES/django.mo,sha256=krjcDvA5bu591zcP76bWp2mD2FL1VUl7wutaZjgD668,13148 @@ -308,65 +312,59 @@ django/conf/locale/km/LC_MESSAGES/django.po,sha256=QgRxEiJMopO14drcmeSG6XEXQpiAy django/conf/locale/km/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/km/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/km/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/km/formats.py,sha256=0UMLrZz1aI2sdRPkJ0YzX99co2IV6tldP7pEvGEPdP0,750 +django/conf/locale/km/formats.py,sha256=o0v-vZQaH-v-7ttAc0H0tSWAQPYQlxHDm0tvLzuPJfw,750 django/conf/locale/kn/LC_MESSAGES/django.mo,sha256=fQ7AD5tUiV_PZFBxUjNPQN79dWBJKqfoYwRdrOaQjU4,17515 django/conf/locale/kn/LC_MESSAGES/django.po,sha256=fS4Z7L4NGVQ6ipZ7lMHAqAopTBP0KkOc-eBK0IYdbBE,28133 django/conf/locale/kn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/kn/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/kn/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/kn/formats.py,sha256=X5j9VHIW2XRdeTzDFEyS8tG05OBFzP2R7sEGUQa_INg,680 -django/conf/locale/ko/LC_MESSAGES/django.mo,sha256=WVGMUYgYPYSH1_PNnMyFMrck0QRc7dnKkM7A4Vd72JA,26696 -django/conf/locale/ko/LC_MESSAGES/django.po,sha256=dxYKncgNz7hPl003QlCOwuGQ2riodHmuZIYtQ3heK9I,30363 +django/conf/locale/kn/formats.py,sha256=FK0SWt0_88-SJkA1xz01sKOkAce5ZEyF-F0HUlO5N4k,680 +django/conf/locale/ko/LC_MESSAGES/django.mo,sha256=VioAHuoN4Lm_GWSwMczgeEKACmLVDfExQpR8VDEEoIE,28133 +django/conf/locale/ko/LC_MESSAGES/django.po,sha256=Jvfg_JyZByK_mVyY96a3BTFTWLXEfoFeBX9dgaPdFTs,30604 django/conf/locale/ko/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ko/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ko/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ko/formats.py,sha256=qn36EjiO4Bu12D_6qitjMDkBfy4M0LgFE-FhK8bPOto,2061 -django/conf/locale/ky/LC_MESSAGES/django.mo,sha256=IBVfwPwaZmaoljMRBGww_wWGMJqbF_IOHHnH2j-yJw8,31395 -django/conf/locale/ky/LC_MESSAGES/django.po,sha256=5ACTPMMbXuPJbU7Rfzs0yZHh3xy483pqo5DwSBQp4s4,33332 +django/conf/locale/ko/formats.py,sha256=XTtpMsB_y_rRjsBoEV3ZXl77MLnlh0tcW0vj1o0I_WM,2125 +django/conf/locale/ky/LC_MESSAGES/django.mo,sha256=te3l2Kzai1NNkqVyfd5q6jf9AIgrBC5-CyQWVL0M4SY,31377 +django/conf/locale/ky/LC_MESSAGES/django.po,sha256=v_PAV5FLgcxiwdP-q8rJRGkJmgsbwjBZRoljdEn3B9U,33259 django/conf/locale/ky/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ky/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ky/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ky/formats.py,sha256=QCq7vxAD5fe9VhcjRhG6C3N28jNvdzKR-c-EvDSJ1Pg,1178 +django/conf/locale/ky/formats.py,sha256=EtnZLr0bi2-yShgyfOIseNVRUtWQEp1-0N5FW5gQv4s,1220 django/conf/locale/lb/LC_MESSAGES/django.mo,sha256=tQSJLQUeD5iUt-eA2EsHuyYqsCSYFtbGdryATxisZsc,8008 django/conf/locale/lb/LC_MESSAGES/django.po,sha256=GkKPLO3zfGTNync-xoYTf0vZ2GUSAotAjfPSP01SDMU,20622 -django/conf/locale/lt/LC_MESSAGES/django.mo,sha256=cdUzK5RYW-61Upf8Sd8ydAg9wXg21pJaIRWFSKPv17c,21421 -django/conf/locale/lt/LC_MESSAGES/django.po,sha256=Lvpe_xlbxSa5vWEossxBCKryDVT7Lwz0EnuL1kSO6OY,28455 +django/conf/locale/lt/LC_MESSAGES/django.mo,sha256=VWrkGGbkN_1UKH9QJcPqKVSRIY2JSJjK23B7oXQqpcA,22750 +django/conf/locale/lt/LC_MESSAGES/django.po,sha256=GDdmsy8FopCM8_VTQKoeGWB918aCT1su4VZktAbnmqA,28534 django/conf/locale/lt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/lt/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/lt/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/lt/formats.py,sha256=C9ScR3gYswT1dQXFedUUnYe6DQPVGAS_nLxs0h2E3dE,1637 -django/conf/locale/lv/LC_MESSAGES/django.mo,sha256=A06pCAw9j4IsUV1l6_aoEZt7yjZfDXIzA5U27VUS5jk,28306 -django/conf/locale/lv/LC_MESSAGES/django.po,sha256=15dXbAHj39Lp-mBqUvvDKkBBju4cwkNAwrmJoIhvluc,30954 +django/conf/locale/lt/formats.py,sha256=tEP5X-VHEikeb7BBkWoW4uuJQw6_OfVh5l_x_hKoGGU,1679 +django/conf/locale/lv/LC_MESSAGES/django.mo,sha256=vlmPk9M8gFLW2x4Rg_HDTpIh-iaedioZADkpL7hGpyE,28193 +django/conf/locale/lv/LC_MESSAGES/django.po,sha256=eqnApvNzJ_KgROWd2GBAVcpvDCApZbNTlf0kZ1VS-e4,30762 django/conf/locale/lv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/lv/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/lv/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/lv/formats.py,sha256=k8owdq0U7-x6yl8ll1W5VjRoKdp8a1G2enH04G5_nvU,1713 +django/conf/locale/lv/formats.py,sha256=72AwivAdil7DNTMeaZTRAHXt04PuN6gkw4uDeODlIpM,1755 django/conf/locale/mk/LC_MESSAGES/django.mo,sha256=uQKmcys0rOsRynEa812XDAaeiNTeBMkqhR4LZ_cfdAk,22737 django/conf/locale/mk/LC_MESSAGES/django.po,sha256=4K11QRb493wD-FM6-ruCxks9_vl_jB59V1c1rx-TdKg,29863 django/conf/locale/mk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/mk/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/mk/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/mk/formats.py,sha256=xwnJsXLXGogOqpP18u6GozjehpWAwwKmXbELolYV_k4,1451 -django/conf/locale/ml/LC_MESSAGES/django.mo,sha256=MGvV0e3LGUFdVIA-h__BuY8Ckom2dAhSFvAtZ8FiAXU,30808 -django/conf/locale/ml/LC_MESSAGES/django.po,sha256=iLllS6vlCpBNZfy9Xd_2Cuwi_1-Vz9fW4G1lUNOuZ6k,37271 +django/conf/locale/mk/formats.py,sha256=8FUX0RYKaUN3_9g9JcGCl-W7q4_U-rUwgSDd5B6F7zE,1493 +django/conf/locale/ml/LC_MESSAGES/django.mo,sha256=NTiGRfaWimmV1bxyqzDeN6fqxxtiobN9MbRVeo1qWYg,32498 +django/conf/locale/ml/LC_MESSAGES/django.po,sha256=gvHg9YKgEp2W6sFKYtdp8eU0ZnHues_rw4LnilkAdmQ,38035 django/conf/locale/ml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ml/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ml/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ml/formats.py,sha256=ZR7tMdJF0U6K1H95cTqrFH4gop6ZuSQ7vD2h0yKq6mo,1597 +django/conf/locale/ml/formats.py,sha256=sr2fzOex-HRdvbYTr_bUiZFSQWyPpN2y5eq_h6zyceQ,1620 django/conf/locale/mn/LC_MESSAGES/django.mo,sha256=sd860BHXfgAjDzU3CiwO3JirA8S83nSr4Vy3QUpXHyU,24783 django/conf/locale/mn/LC_MESSAGES/django.po,sha256=VBgXVee15TTorC7zwYFwmHM4qgpYy11yclv_u7UTNwA,30004 django/conf/locale/mn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/mn/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/mn/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/mn/formats.py,sha256=fsexJU9_UTig2PS_o11hcEmrbPBS8voI4ojuAVPOd_U,676 +django/conf/locale/mn/formats.py,sha256=ET9fum7iEOCGRt9E-tWXjvHHvr9YmAR5UxmEHXjJsTc,676 django/conf/locale/mr/LC_MESSAGES/django.mo,sha256=aERpEBdJtkSwBj6zOtiKDaXuFzepi8_IwvPPHi8QtGU,1591 django/conf/locale/mr/LC_MESSAGES/django.po,sha256=GFtk4tVQVi8b7N7KEhoNubVw_PV08pyRvcGOP270s1Q,19401 -django/conf/locale/ms/LC_MESSAGES/django.mo,sha256=U4_kzfbYF7u78DesFRSReOIeVbOnq8hi_pReFfHfyUQ,27066 -django/conf/locale/ms/LC_MESSAGES/django.po,sha256=49pG3cykGjVfC9N8WPyskz-m7r6KmQiq5i8MR6eOi54,28985 -django/conf/locale/ms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -django/conf/locale/ms/__pycache__/__init__.cpython-39.pyc,, -django/conf/locale/ms/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ms/formats.py,sha256=YtOBs6s4j4SOmfB3cpp2ekcxVFoVGgUN8mThoSueCt0,1522 django/conf/locale/my/LC_MESSAGES/django.mo,sha256=SjYOewwnVim3-GrANk2RNanOjo6Hy2omw0qnpkMzTlM,2589 django/conf/locale/my/LC_MESSAGES/django.po,sha256=b_QSKXc3lS2Xzb45yVYVg307uZNaAnA0eoXX2ZmNiT0,19684 django/conf/locale/nb/LC_MESSAGES/django.mo,sha256=XDCGV0qH1f2V-w_Hp2uqkl5w08--EGoqHKAnrBdViGo,26572 @@ -374,7 +372,7 @@ django/conf/locale/nb/LC_MESSAGES/django.po,sha256=DGM90Bk4YsbVffQ18ECsWV4QsZcjw django/conf/locale/nb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/nb/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/nb/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/nb/formats.py,sha256=y1QLE-SG00eHwje0lkAToHtz4t621Rz_HQRyBWCgK8c,1552 +django/conf/locale/nb/formats.py,sha256=gfpEc1o0dLP11NK8miHV-jDMLxWzGvxYv8eayXbkbwM,1571 django/conf/locale/ne/LC_MESSAGES/django.mo,sha256=BcK8z38SNWDXXWVWUmOyHEzwk2xHEeaW2t7JwrxehKM,27248 django/conf/locale/ne/LC_MESSAGES/django.po,sha256=_Kj_i2zMb7JLU7EN7Z7JcUn89YgonJf6agSFCjXa49w,33369 django/conf/locale/nl/LC_MESSAGES/django.mo,sha256=yIiuxrpS6L0qVxm11jnXphVICeyer7Dp-LwSmfb1omQ,27117 @@ -382,83 +380,83 @@ django/conf/locale/nl/LC_MESSAGES/django.po,sha256=S-T7QOXjAJoJz2Vsb1uWQi0h69y9b django/conf/locale/nl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/nl/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/nl/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/nl/formats.py,sha256=cKaaOvRdeauORjvuZ1xyVcVsl36J3Zk4FSE-lnx2Xwg,3927 -django/conf/locale/nn/LC_MESSAGES/django.mo,sha256=Ccj8kjvjTefC8H6TuDCOdSrTmtkYXkmRR2V42HBMYo4,26850 -django/conf/locale/nn/LC_MESSAGES/django.po,sha256=oaVJTl0NgZ92XJv9DHdsXVaKAc81ky_R3CA6HljTH-8,29100 +django/conf/locale/nl/formats.py,sha256=2z34kqQeiIUg5P4Yme0sHw5r65GkO_iTsxpXhBZBcqM,4095 +django/conf/locale/nn/LC_MESSAGES/django.mo,sha256=8CoLejnImo9TMbt-CR7NK8WAbX3wm89AgZOuPn-werQ,13212 +django/conf/locale/nn/LC_MESSAGES/django.po,sha256=AWPfAtzROtcEjxr0YWGTcNBWF7qnyF3wxhGkLiBIQ5k,22582 django/conf/locale/nn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/nn/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/nn/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/nn/formats.py,sha256=y1QLE-SG00eHwje0lkAToHtz4t621Rz_HQRyBWCgK8c,1552 +django/conf/locale/nn/formats.py,sha256=gfpEc1o0dLP11NK8miHV-jDMLxWzGvxYv8eayXbkbwM,1571 django/conf/locale/os/LC_MESSAGES/django.mo,sha256=LBpf_dyfBnvGOvthpn5-oJuFiSNHrgiVHBzJBR-FxOw,17994 django/conf/locale/os/LC_MESSAGES/django.po,sha256=WYlAnNYwGFnH76Elnnth6YP2TWA-fEtvV5UinnNj7AA,26278 django/conf/locale/pa/LC_MESSAGES/django.mo,sha256=H1hCnQzcq0EiSEaayT6t9H-WgONO5V4Cf7l25H2930M,11253 django/conf/locale/pa/LC_MESSAGES/django.po,sha256=26ifUdCX9fOiXfWvgMkOXlsvS6h6nNskZcIBoASJec4,23013 -django/conf/locale/pl/LC_MESSAGES/django.mo,sha256=XlCZKVB_fXSf3QdFlck0r-Cq5knbJw_zwc5AqEMrAH8,29785 -django/conf/locale/pl/LC_MESSAGES/django.po,sha256=kgqUTMnNLZCRCWwV6_WzTS6DjYMABNNVTLaKJoJ4kKM,33489 +django/conf/locale/pl/LC_MESSAGES/django.mo,sha256=fARx7bIFQGw6NsaHNJ3et8HW7D1IZoND2y8Y2BV8zPQ,29577 +django/conf/locale/pl/LC_MESSAGES/django.po,sha256=jNee5Z8NfSD0ZynlSoOCv0uX6s_pK-KpU0WBSRjS_Ls,33279 django/conf/locale/pl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/pl/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/pl/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/pl/formats.py,sha256=KREhPtHuzKS_ZsAqXs5LqYPGhn6O-jLd4WZQ-39BA8I,1032 +django/conf/locale/pl/formats.py,sha256=6aBumG-WeA7mWnDlfoP0_VadHiBZdYXCvPwT6JG2md8,1038 django/conf/locale/pt/LC_MESSAGES/django.mo,sha256=nlj_L7Z2FkXs1w6wCGGseuZ_U-IecnlfYRtG5jPkGrs,20657 django/conf/locale/pt/LC_MESSAGES/django.po,sha256=ETTedbjU2J4FLi2QDHNN8C7zlAsvLWNUlYzkEV1WB6s,26224 django/conf/locale/pt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/pt/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/pt/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/pt/formats.py,sha256=RQ9MuIwUPhiY2u-1hFU2abs9Wqv1qZE2AUAfYVK-NU8,1520 -django/conf/locale/pt_BR/LC_MESSAGES/django.mo,sha256=XD1D86JAuti97A_tDFNL5JiWBd-csKFSGSduaBFz2ZM,26578 -django/conf/locale/pt_BR/LC_MESSAGES/django.po,sha256=3mtKECf2gbuhMimBjEjq-b-NzOX52HfGHiHNOazEi3w,30713 +django/conf/locale/pt/formats.py,sha256=LeVTwDFRHkY9786T-lZx-iKOHTPiFReAiUPYdbrDcmI,1522 +django/conf/locale/pt_BR/LC_MESSAGES/django.mo,sha256=JqEgQtawdFXNy_RCuZTYxO93S5xXnO0nx-s8KG9KuvI,27452 +django/conf/locale/pt_BR/LC_MESSAGES/django.po,sha256=GTyrk_r-3sFTZGn5zFzQ8uXTnXnzRxWGHrbO5u-_rsQ,30954 django/conf/locale/pt_BR/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/pt_BR/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/pt_BR/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/pt_BR/formats.py,sha256=J1IKV7cS2YMJ5_qlT9h1dDYUX9tLFvqA95l_GpZTLUY,1285 +django/conf/locale/pt_BR/formats.py,sha256=YvB8w7UVjacsAQgbSY76tQTX-W3pucfeAGPFIHwWcBo,1283 django/conf/locale/ro/LC_MESSAGES/django.mo,sha256=IMUybfJat0koxf_jSv6urQQuiHlldUhjrqo3FR303WA,22141 django/conf/locale/ro/LC_MESSAGES/django.po,sha256=mdMWVR6kXJwUSxul2bpu3IoWom6kWDiES6Iw5ziynj0,27499 django/conf/locale/ro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ro/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ro/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ro/formats.py,sha256=e_dp0zyfFfoydrGyn6Kk3DnQIj7RTRuvRc6rQ6tSxzA,928 -django/conf/locale/ru/LC_MESSAGES/django.mo,sha256=rTITYVXoFAoqCIKj21rjgnWXCEMwE41QeyX26PqC05M,36220 -django/conf/locale/ru/LC_MESSAGES/django.po,sha256=A98tjI-zPro3bx0zopl5maKKDedF3f2AE5yU4AAZ-mE,40123 +django/conf/locale/ro/formats.py,sha256=hpxpg6HcFGX5HFpypZ-GA4GkAsXCWuivMHLyyV1U2Rw,928 +django/conf/locale/ru/LC_MESSAGES/django.mo,sha256=6vnvXJpZoxLfNrOM69r-DnGRcxTGluQCrtwG8eLD16Y,37637 +django/conf/locale/ru/LC_MESSAGES/django.po,sha256=2XPGmtNxxlPmCiL76oXmSaysClxbs84cHixxPJ7lSc4,40896 django/conf/locale/ru/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ru/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ru/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ru/formats.py,sha256=lTfYbecdSmHCxebog_2bd0N32iD3nEq_f5buh9il-nI,1098 -django/conf/locale/sk/LC_MESSAGES/django.mo,sha256=LLHZDII9g__AFTHCgyLy05I7DQEjZjk20LO-CkrdhS0,27800 -django/conf/locale/sk/LC_MESSAGES/django.po,sha256=iH6cKWjUfKMqVd4Q6HPEnZwOB-39SpllevZIythjk9M,31062 +django/conf/locale/ru/formats.py,sha256=o9xwvKn2hIshuaZ4nea4Ecx-jBhxTzPDLg2W-gygkLw,1116 +django/conf/locale/sk/LC_MESSAGES/django.mo,sha256=hF4gNKo3rYbtCzRuHOtofA_UUMO2MwovfR5uOqi1F0M,22708 +django/conf/locale/sk/LC_MESSAGES/django.po,sha256=A5o5gBcRE1FToEO7yhnZ05bZHRkYnSZ-SagLfE2YfUs,28524 django/conf/locale/sk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sk/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sk/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sk/formats.py,sha256=bWj0FNpYfOAgi9J-L4VuiN6C_jsgPsKNdLYd9gTnFs0,1051 +django/conf/locale/sk/formats.py,sha256=YXxNfnkRJvAei2la5L-9m1IplCOouo3Jhxn0YKpSZ0w,1064 django/conf/locale/sl/LC_MESSAGES/django.mo,sha256=uaPbjsAAam_SrzenHjeHgTC3Pxn6BEecXgnDY9HOzwg,21921 django/conf/locale/sl/LC_MESSAGES/django.po,sha256=MZ8Lz3dN5JSxw7l8bFRN0ozeW4Sue0jnRURm2zpOcuI,27860 django/conf/locale/sl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sl/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sl/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sl/formats.py,sha256=Nq4IfEUnlGebMZeRvB2l9aps-5G5b4y1kQ_3MiJTfe8,1642 -django/conf/locale/sq/LC_MESSAGES/django.mo,sha256=E31OUoJGXPv0kW3MdZk6TQl8XF2tTRHw8Em4fk2k6MY,27802 -django/conf/locale/sq/LC_MESSAGES/django.po,sha256=AI1j6XLOzeWsk_N_KaOHggR9CE8hQeb4LMk44YGbcjE,30191 +django/conf/locale/sl/formats.py,sha256=iIH0ZrXpUEDQ1NvzND-e-UGAHiM8d4NDha8o9U1YPFY,1798 +django/conf/locale/sq/LC_MESSAGES/django.mo,sha256=otSWTfAkMmoEcphsk7DOoTiZWhP808hSlDagLGK0dBk,27682 +django/conf/locale/sq/LC_MESSAGES/django.po,sha256=3kMx282KvzOtyohbnZXyQ7-YVRV7DWN6yrR9OdlXG40,30046 django/conf/locale/sq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sq/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sq/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sq/formats.py,sha256=SA_jCSNwI8-p79skHoLxrPLZnkyq1PVadwT6gMt7n_M,688 -django/conf/locale/sr/LC_MESSAGES/django.mo,sha256=XVnYuUQmoQy6BZnPmHnSrWVz75J4sTYKxGn4NqdJU4c,34059 -django/conf/locale/sr/LC_MESSAGES/django.po,sha256=jvlDoqR-OhFigYmrjPWm2cXMVqeYvT9qpbT-yAlp7Lg,36513 +django/conf/locale/sq/formats.py,sha256=X7IXRLlVWmlgNSa2TSvshv8Vhtjfv0V1Okg0adqVl3o,688 +django/conf/locale/sr/LC_MESSAGES/django.mo,sha256=GjjkLO-zeZXCzK08Jy-dhNy9HvwtWxKp9TUl9m3nqOI,33844 +django/conf/locale/sr/LC_MESSAGES/django.po,sha256=7jjGgdXiGau2u9bEzfT1_8uC_tqSArH38ajPeotvr6g,36242 django/conf/locale/sr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sr/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sr/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sr/formats.py,sha256=F3_gYopOXINcllaPFzTqZrZ2oZ1ye3xzR0NQtlqXYp0,1729 -django/conf/locale/sr_Latn/LC_MESSAGES/django.mo,sha256=T4ZIcgyBTOXlmY0B94ER98YQsc8CoowLVnLeNYPx6wc,22633 -django/conf/locale/sr_Latn/LC_MESSAGES/django.po,sha256=u5bcaCdCEbNGj7OEe2SB88942VWi6BbvAWpOUbTpNSw,27658 +django/conf/locale/sr/formats.py,sha256=1v-fbUFCpU1mjwQJX8-qZMFYUU0-d-9w_uFJ7NgMweY,1754 +django/conf/locale/sr_Latn/LC_MESSAGES/django.mo,sha256=rmf3rXfKKBcl4kx6qpFqm6hrPFKc4GNWIAbSc7GQKSw,21917 +django/conf/locale/sr_Latn/LC_MESSAGES/django.po,sha256=My5dodRD2ZT1vVx8SmQwUuxsTnlzqsu5IaaVx-A5tC4,27268 django/conf/locale/sr_Latn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sr_Latn/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sr_Latn/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sr_Latn/formats.py,sha256=BDZm-ajQgCIxQ8mCcckEH32IoCN9233TvAOXkg4mc38,1728 -django/conf/locale/sv/LC_MESSAGES/django.mo,sha256=E-c3gYgaN7sadn0-nRWJw3EHT-mBiKIMjNSdC-Ia-mk,24820 -django/conf/locale/sv/LC_MESSAGES/django.po,sha256=cAi7HXk2pB6niiTNTlXkMkEwrLcK_dPQ2U9ipCMSQOg,28635 +django/conf/locale/sr_Latn/formats.py,sha256=1v-fbUFCpU1mjwQJX8-qZMFYUU0-d-9w_uFJ7NgMweY,1754 +django/conf/locale/sv/LC_MESSAGES/django.mo,sha256=0W60cBGm7HAqKWtFyZjVGo511N3XNOm2yqk2xfg065c,21718 +django/conf/locale/sv/LC_MESSAGES/django.po,sha256=o5NYw82Qu6lFCz6nLaphtmN1oEz3pG_bB6AI_ClTv04,27117 django/conf/locale/sv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/sv/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/sv/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/sv/formats.py,sha256=9o8ZtaSq1UOa5y6Du3rQsLAAl5ZOEdVY1OVVMbj02RA,1311 +django/conf/locale/sv/formats.py,sha256=46MJnftY5MjzTqgvNfBTV-nVAkbY--8NbXtiLFhT_Lg,1374 django/conf/locale/sw/LC_MESSAGES/django.mo,sha256=aUmIVLANgSCTK5Lq8QZPEKWjZWnsnBvm_-ZUcih3J6g,13534 django/conf/locale/sw/LC_MESSAGES/django.po,sha256=GOE6greXZoLhpccsfPZjE6lR3G4vpK230EnIOdjsgPk,22698 django/conf/locale/ta/LC_MESSAGES/django.mo,sha256=WeM8tElbcmL11P_D60y5oHKtDxUNWZM9UNgXe1CsRQ4,7094 @@ -466,47 +464,47 @@ django/conf/locale/ta/LC_MESSAGES/django.po,sha256=kgHTFqysEMj1hqktLr-bnL1NRM715 django/conf/locale/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/ta/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/ta/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/ta/formats.py,sha256=vmjfiM54oJJxqcdgZJUNNQN7oMS-XLVBYJ4lWBb5ctY,682 +django/conf/locale/ta/formats.py,sha256=LbLmzaXdmz4UbzNCbINYOJLggyU1ytxWAME3iHVt9NY,682 django/conf/locale/te/LC_MESSAGES/django.mo,sha256=Sk45kPC4capgRdW5ImOKYEVxiBjHXsosNyhVIDtHLBc,13259 django/conf/locale/te/LC_MESSAGES/django.po,sha256=IQxpGTpsKUtBGN1P-KdGwvE7ojNCqKqPXEvYD3qT5A4,25378 django/conf/locale/te/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/te/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/te/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/te/formats.py,sha256=-HOoZgmnME4--4CuXzcnhXqNma0Wh7Ninof3RCCGZkU,680 +django/conf/locale/te/formats.py,sha256=aSddq7fhlOce3zBLdTNDQA5L_gfAhsmKRCuyQ8O5TyY,680 django/conf/locale/tg/LC_MESSAGES/django.mo,sha256=ePzS2pD84CTkHBaiaMyXBxiizxfFBjHdsGH7hCt5p_4,28497 django/conf/locale/tg/LC_MESSAGES/django.po,sha256=oSKu3YT3griCrDLPqptZmHcuviI99wvlfX6I6nLJnDk,33351 django/conf/locale/tg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/tg/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/tg/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/tg/formats.py,sha256=TG5TGfLNy4JSjl-QAWk46gIEb0ijdBpqPrDtwfJzshw,1160 +django/conf/locale/tg/formats.py,sha256=wM47-gl6N2XbknMIUAvNmqxNyso6bNnwU11RzoLK3RM,1202 django/conf/locale/th/LC_MESSAGES/django.mo,sha256=SJeeJWbdF-Lae5BendxlyMKqx5zdDmh3GCQa8ER5FyY,18629 django/conf/locale/th/LC_MESSAGES/django.po,sha256=K4ITjzHLq6DyTxgMAfu3CoGxrTd3aG2J6-ZxQj2KG1U,27507 django/conf/locale/th/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/th/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/th/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/th/formats.py,sha256=SmCUD-zVgI1QE2HwqkFtAO87rJ-FoCjw1s-2-cfl1h0,1072 -django/conf/locale/tk/LC_MESSAGES/django.mo,sha256=B80ko55qX70bXz0b7f5azf1_6Vqyp0ELIiB1T-RJMW8,25628 -django/conf/locale/tk/LC_MESSAGES/django.po,sha256=FaDJ7UkoZzRvLhy25uo2l2IQkBGFVQuW-izXHzKdNCM,28772 +django/conf/locale/th/formats.py,sha256=vBGsPtMZkJZN0gVcX3eCDVE3KHsjJJ94EW2_9tCT0W4,1072 +django/conf/locale/tk/LC_MESSAGES/django.mo,sha256=HM4-efqIOSqXPpG139jZXI2WymL2b5GtnUxCXPlFgr8,27139 +django/conf/locale/tk/LC_MESSAGES/django.po,sha256=p-XA0sclJU0-5T9lRI1lQTxDpH0MGVPwn5rYHtABpCI,29133 django/conf/locale/tk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/tk/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/tk/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/tk/formats.py,sha256=TG5TGfLNy4JSjl-QAWk46gIEb0ijdBpqPrDtwfJzshw,1160 -django/conf/locale/tr/LC_MESSAGES/django.mo,sha256=j9d-WIh0uzs315J9d3aTd-77YQPJGlA625GGraeozb8,27998 -django/conf/locale/tr/LC_MESSAGES/django.po,sha256=FxMqznG_70UXSunb8V5g-7DI3xchaV8v_HEeic5fvpg,30525 +django/conf/locale/tk/formats.py,sha256=wM47-gl6N2XbknMIUAvNmqxNyso6bNnwU11RzoLK3RM,1202 +django/conf/locale/tr/LC_MESSAGES/django.mo,sha256=UsB8cqqfqIy5gHWnbbazrwaOw8Un6Fzao9lKJH3S__M,27846 +django/conf/locale/tr/LC_MESSAGES/django.po,sha256=vd-k43i8LblXeqL9-B-K9K7JEwiw6HrGO2OK2t2l0nM,30371 django/conf/locale/tr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/tr/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/tr/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/tr/formats.py,sha256=yJg-7hmevD1gvj9iBRMCiYGgd5DxKZcL7T_C3K3ztME,1019 +django/conf/locale/tr/formats.py,sha256=bzEkWCwULHmwyMHqN-1ACBn6Lr8VbvoL9TuvO4KInVI,1032 django/conf/locale/tt/LC_MESSAGES/django.mo,sha256=r554DvdPjD_S8hBRjW8ehccEjEk8h7czQsp46FZZ_Do,14500 django/conf/locale/tt/LC_MESSAGES/django.po,sha256=W8QgEAH7yXNmjWoF-UeqyVAu5jEMHZ5MXE60e5sawJc,24793 django/conf/locale/udm/LC_MESSAGES/django.mo,sha256=cIf0i3TjY-yORRAcSev3mIsdGYT49jioTHZtTLYAEyc,12822 django/conf/locale/udm/LC_MESSAGES/django.po,sha256=n9Az_8M8O5y16yE3iWmK20R9F9VoKBh3jR3iKwMgFlY,23113 -django/conf/locale/uk/LC_MESSAGES/django.mo,sha256=bWwHvsJbgauAc-IDyL1sUCzpBjiukxy9MCu5wVQ0v_w,26995 -django/conf/locale/uk/LC_MESSAGES/django.po,sha256=H4z9AsoHiNLdNXUU2KZOiyF2DufQlNb6hmyvyDN8514,33904 +django/conf/locale/uk/LC_MESSAGES/django.mo,sha256=yCf4RAtmTckj2K3jD6quwThbJMlbmrGNousTqkd_VzQ,27319 +django/conf/locale/uk/LC_MESSAGES/django.po,sha256=ZpWZKz3GVaOP3kZRzAYNoXodM4yqkebfnvxmizoI0P8,34032 django/conf/locale/uk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/uk/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/uk/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/uk/formats.py,sha256=ZmeYmL0eooFwQgmE054V36RQ469ZTfAv6k8SUJrDYQ8,1241 +django/conf/locale/uk/formats.py,sha256=R4i56pYlss2Ui6zyDT5OvwFZq0SBIxzf4hsyzmnip5U,1268 django/conf/locale/ur/LC_MESSAGES/django.mo,sha256=M6R2DYFRBvcVRAsgVxVOLvH3e8v14b2mJs650UlUb2I,12291 django/conf/locale/ur/LC_MESSAGES/django.po,sha256=Lr0DXaPqWtCFAxn10BQ0vlvZIMNRvCg_QJQxAC01eWk,23479 django/conf/locale/uz/LC_MESSAGES/django.mo,sha256=c8eHLqubZqScsU8LjGK-j2uAGeWzHCSmCy-tYu9x_FA,27466 @@ -514,40 +512,40 @@ django/conf/locale/uz/LC_MESSAGES/django.po,sha256=TxmmhZCC1zrAgo0xM0JQKywju0XBd django/conf/locale/uz/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/uz/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/uz/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/uz/formats.py,sha256=cdmqOUBVnPSyi2k9AkOGl27s89PymFePG2gtnYzYbiw,1176 +django/conf/locale/uz/formats.py,sha256=VJC2U61827xc8v7XTv3eKfySbZUHL34KpCmQyWMKRQ0,1199 django/conf/locale/vi/LC_MESSAGES/django.mo,sha256=TMsBzDnf9kZndozqVUnEKtKxfH2N1ajLdrm8hJ4HkYI,17396 django/conf/locale/vi/LC_MESSAGES/django.po,sha256=tL2rvgunvaN_yqpPSBYAKImFDaFaeqbnpEw_egI11Lo,25342 django/conf/locale/vi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/vi/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/vi/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/vi/formats.py,sha256=_xIugkqLnjN9dzIhefMpsJXaTPldr4blKSGS-c3swg0,762 -django/conf/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=vDGGCa14szv2Ru9NV_HxxBLktu4Kj0XWEAN7ZTCME4M,26198 -django/conf/locale/zh_Hans/LC_MESSAGES/django.po,sha256=_krsSJiCWnMN6Emt_T0AoK3ZiK-GYOH1Jd2eDhiCMCw,29150 +django/conf/locale/vi/formats.py,sha256=H_lZwBQUKUWjtoN0oZOxXw0SsoNWnXg3pKADPYX3RrI,762 +django/conf/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=-tAzfwPrqHJIV29t6BO5Kk9ZxnHNF5dtDItiR7LXbGg,26047 +django/conf/locale/zh_Hans/LC_MESSAGES/django.po,sha256=Rvr3XFGNg6OTKXF8JEO6sBWyh6V-j-gxj7McuVKR_2E,28957 django/conf/locale/zh_Hans/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/zh_Hans/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/zh_Hans/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/zh_Hans/formats.py,sha256=iMb9Taj6xQQA3l_NWCC7wUlQuh4YfNUgs2mHcQ6XUEo,1598 +django/conf/locale/zh_Hans/formats.py,sha256=U-1yJketLR187TFCBAzgUCt0UlZNvCxoLgBkYhZz2Ts,1745 django/conf/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=1U3cID-BpV09p0sgYryzJCCApQYVlCtb4fJ5IPB8wtc,19560 django/conf/locale/zh_Hant/LC_MESSAGES/django.po,sha256=buHXYy_UKFoGW8xz6PNrSwbMx-p8gwmPRgdWGBYwT2U,24939 django/conf/locale/zh_Hant/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/locale/zh_Hant/__pycache__/__init__.cpython-39.pyc,, django/conf/locale/zh_Hant/__pycache__/formats.cpython-39.pyc,, -django/conf/locale/zh_Hant/formats.py,sha256=iMb9Taj6xQQA3l_NWCC7wUlQuh4YfNUgs2mHcQ6XUEo,1598 +django/conf/locale/zh_Hant/formats.py,sha256=U-1yJketLR187TFCBAzgUCt0UlZNvCxoLgBkYhZz2Ts,1745 django/conf/project_template/manage.py-tpl,sha256=JDuGG02670bELmn3XLUSxHFZ8VFhqZTT_oN9VbT5Acc,674 django/conf/project_template/project_name/__init__.py-tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/conf/project_template/project_name/asgi.py-tpl,sha256=q_6Jo5tLy6ba-S7pLs3YTK7byxSBmU0oYylYJlNvwHI,428 -django/conf/project_template/project_name/settings.py-tpl,sha256=JskIPIEWPSX2p7_rlsPr60JDjmFC0bVEeMChmq--0OY,3342 +django/conf/project_template/project_name/settings.py-tpl,sha256=rd5deHQnwKeAjq6tCKOfuZRmeOBfgdohow0nNdieEfk,3360 django/conf/project_template/project_name/urls.py-tpl,sha256=vrokVPIRgYajr3Osw2_D1gCndrJ-waGU3tkpnzhWync,775 django/conf/project_template/project_name/wsgi.py-tpl,sha256=OCfjjCsdEeXPkJgFIrMml_FURt7msovNUPnjzb401fs,428 -django/conf/urls/__init__.py,sha256=qmpaRi5Gn2uaY9h3g9RNu0z3LDEpEeNL9JlfSLed9s0,292 +django/conf/urls/__init__.py,sha256=kHy9_mgebuUHAbAMFrFJ1badWEJvbeZH_YMZA1FC_zQ,656 django/conf/urls/__pycache__/__init__.cpython-39.pyc,, django/conf/urls/__pycache__/i18n.cpython-39.pyc,, django/conf/urls/__pycache__/static.cpython-39.pyc,, -django/conf/urls/i18n.py,sha256=Xz83EPb1MwylIF1z3NimtAD7TlJwd_0ZpZoxj2HEO1E,1184 -django/conf/urls/static.py,sha256=gZOYaiIf3SxQ75N69GyVm9C0OmQv1r1IDrUJ0E7zMe0,908 +django/conf/urls/i18n.py,sha256=TG_09WedGtcOhijJtDxxcQkcOU15Dikq0NkLGVvwvCI,1184 +django/conf/urls/static.py,sha256=WHZ7JNbBEQVshD0-sdImvAW635uV-msIyP2VYntzrPk,886 django/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/__pycache__/__init__.cpython-39.pyc,, -django/contrib/admin/__init__.py,sha256=s4yCvpvHN4PbCIiNNZKSCaUhN_0NdkrLq-qihnJH4L4,1169 +django/contrib/admin/__init__.py,sha256=XAnhrz85PswJx5qVok6lLvRkNQvdUPE39UEeQWl3tJs,1065 django/contrib/admin/__pycache__/__init__.cpython-39.pyc,, django/contrib/admin/__pycache__/actions.cpython-39.pyc,, django/contrib/admin/__pycache__/apps.cpython-39.pyc,, @@ -563,24 +561,24 @@ django/contrib/admin/__pycache__/sites.cpython-39.pyc,, django/contrib/admin/__pycache__/tests.cpython-39.pyc,, django/contrib/admin/__pycache__/utils.cpython-39.pyc,, django/contrib/admin/__pycache__/widgets.cpython-39.pyc,, -django/contrib/admin/actions.py,sha256=efImpehGXIm5Oy5NnZsIn4JYIbYHWjn0ti2QvkkyaWY,3231 -django/contrib/admin/apps.py,sha256=BOiulA4tsb3wuAUtLGTGjrbywpSXX0dLo2pUCGV8URw,840 -django/contrib/admin/checks.py,sha256=3rKY-7UTJ8-1OXnYBXKGDDhYUb3BNWVMjqAidTM5XZ0,49761 -django/contrib/admin/decorators.py,sha256=dki7GLFKOPT-mB5rxsYX12rox18BywroxmrzjG_VJXM,3481 -django/contrib/admin/exceptions.py,sha256=wpzdKnp6V_aTYui_4tQZ8hFJf7W5xYkEMym0Keg1k0k,333 -django/contrib/admin/filters.py,sha256=SPefxuFISo829mqrvfO_RGrsgwHthxXHeeVdUcS9dk4,20802 -django/contrib/admin/forms.py,sha256=0UCJstmmBfp_c_0AqlALJQYy9bxXo9fqoQQICQONGEo,1023 -django/contrib/admin/helpers.py,sha256=nFwn75Yof_P_SbVOcJqBd2W-F7kUGHfiRs2ZIYZ1rBw,18065 +django/contrib/admin/actions.py,sha256=Pahf6ipI3clajrmKH7nq6aWv3bpTic4NgoL8cPMVrXw,3037 +django/contrib/admin/apps.py,sha256=RgAgnpSJproS0C-3ED5QFuORJ9MCcGxZHt1nFPYvr1w,840 +django/contrib/admin/checks.py,sha256=-7wrSRCOIyzDfztIP06Ce2ojuqH4pOiuzWe7Bj1_R3Y,45659 +django/contrib/admin/decorators.py,sha256=SJVppNNCIrsJUHV7sZjjrVqVqHKvGRSKs4sMm4wb_N4,3428 +django/contrib/admin/exceptions.py,sha256=lWAupa8HTBROgZbDeYS1n_vOl_85dcmPhDwz0-Ke1ug,331 +django/contrib/admin/filters.py,sha256=oOvdarUzouFpTomGC1z8lBhh61XzZr1fDtbD7sGirLU,19661 +django/contrib/admin/forms.py,sha256=uJth9S0kX0yijCg6uvbcV4fil5bAoJsZmoKWmvg31tA,1021 +django/contrib/admin/helpers.py,sha256=jrnJhqmTizO0IvyqIOYbL_CSqqARcxaFH5EC36N4Nbw,16218 django/contrib/admin/locale/af/LC_MESSAGES/django.mo,sha256=3VNfQp5JaJy4XRqxM7Uu9uKHDihJCvKXYhdWPXOofc8,16216 django/contrib/admin/locale/af/LC_MESSAGES/django.po,sha256=R2ix5AnK5X35wnhjT38K85JgwewQkmwrYwyVx4YqikQ,17667 django/contrib/admin/locale/af/LC_MESSAGES/djangojs.mo,sha256=dmctO7tPkPwdbpp-tVmZrR0QLZekrJ1aE3rnm6vvUQM,4477 django/contrib/admin/locale/af/LC_MESSAGES/djangojs.po,sha256=1wwspqp0rsSupVes7zjYLyNT_wY4lFefqhpXH5wBdJM,4955 django/contrib/admin/locale/am/LC_MESSAGES/django.mo,sha256=UOwMxYH1r5AEBpu-P9zxHazk3kwI4CtsPosGIYtl6Hs,8309 django/contrib/admin/locale/am/LC_MESSAGES/django.po,sha256=NmsIZoBEQwyBIqbKjkwCJ2_iMHnMKB87atoT0iuNXrw,14651 -django/contrib/admin/locale/ar/LC_MESSAGES/django.mo,sha256=tzGQ8jSJc406IBBwtAErlXVqaA10glxB8krZtWp1Rq4,19890 -django/contrib/admin/locale/ar/LC_MESSAGES/django.po,sha256=RBJbiYNDy57K592OKghugZFYiHpTvxUoEQ_B26-5i8A,21339 -django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo,sha256=xoI2xNKgspuuJe1UCUB9H6Kyp3AGhj5aeo_WEg5e23A,6545 -django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po,sha256=jwehFDFk3lMIEH43AEU_JyHOm84Seo-OLd5FmGBbaxo,7281 +django/contrib/admin/locale/ar/LC_MESSAGES/django.mo,sha256=e1TPsXhFUpNGLgdsOdF3VJgX4fqozB3jGhDLAY-DiOk,19693 +django/contrib/admin/locale/ar/LC_MESSAGES/django.po,sha256=5Kw2JgaC7H6UNoblB2ZmHrFCsS_KkT0Z8kLooogSk78,21173 +django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo,sha256=5G1bV_2YhASuQqUgYY6mQDoV3zcJlRx70iPqDUxcCbU,5843 +django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po,sha256=BMi2aVzpeJtSIbpB0Ivhbj5WaKgNlrpQquYRqFcWpl8,6502 django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=IJlPu_ROkcvVEyTej2un1WMuCueOYBMYNxAmTCK7NbU,19657 django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django.po,sha256=qEHImGRyP-cOeA66387z9glbIhUEeliq-dI-iLhuweM,21027 django/contrib/admin/locale/ar_DZ/LC_MESSAGES/djangojs.mo,sha256=bNJysHeUsNaSg2BgFh9r4FEnRAee9w6DNN4OvfQfYnc,5721 @@ -593,14 +591,14 @@ django/contrib/admin/locale/az/LC_MESSAGES/django.mo,sha256=pOABf7ef6c4Apn3e0YE0 django/contrib/admin/locale/az/LC_MESSAGES/django.po,sha256=ZQVARobZ9XzSbP9HLDV8DhmQpe08ExhoTj5RBpFu__g,17299 django/contrib/admin/locale/az/LC_MESSAGES/djangojs.mo,sha256=3P3iKDFi9G1iMmxTVHWol1FgczmMl4gYHRoBT5W3fYw,4598 django/contrib/admin/locale/az/LC_MESSAGES/djangojs.po,sha256=BpFkIKu93AVAYKPnCKSPswCIAm8L2909oh6NJSZJLu8,5125 -django/contrib/admin/locale/be/LC_MESSAGES/django.mo,sha256=npxIePwS6kY6UScaQl13xx1MPecYJcFCATuGNZB7a5c,21347 -django/contrib/admin/locale/be/LC_MESSAGES/django.po,sha256=4gG-r4SS9yEc9riM3e0fh46FSQHhB6E4vCw9jx1AsNc,22579 +django/contrib/admin/locale/be/LC_MESSAGES/django.mo,sha256=CBomfJ6N52rJdkbZPuuyODMPPS7AJZnC4vUS8Qt_D5k,21096 +django/contrib/admin/locale/be/LC_MESSAGES/django.po,sha256=9nPIJ5aw1i4cTn_LN706sTnNWNngFt_1HArzCU3pE3M,22364 django/contrib/admin/locale/be/LC_MESSAGES/djangojs.mo,sha256=ujRhIpDAf0W8YQpXiWgVOsbjpmS6QqI9I49WkKfqDpc,6558 django/contrib/admin/locale/be/LC_MESSAGES/djangojs.po,sha256=Gg9mu6NJp4K0Fqs7TSPbGkTyn5YkXICHTAlRqL57rMw,7190 -django/contrib/admin/locale/bg/LC_MESSAGES/django.mo,sha256=YgKt1Ai781xv0Meb0j9cB-S5PMKPopZM_PNH1T4gS-Y,21397 -django/contrib/admin/locale/bg/LC_MESSAGES/django.po,sha256=HWrKUkzmIFScPPo8Mzg-4wNh1i_n1SbIN5jzJKl1W_g,22834 -django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo,sha256=B_Xg-IuoDeYFhqjDoaeI4i6eZueznriHwaGcC-JpKO8,6230 -django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po,sha256=obkM96U9Tu99DlLojktQ2N7PZDbwmONQn0AYogudyVE,6832 +django/contrib/admin/locale/bg/LC_MESSAGES/django.mo,sha256=uDQB5G5S--ff1vGwfe37aQxWVFqLf3fBXgnUKs2SpZw,15320 +django/contrib/admin/locale/bg/LC_MESSAGES/django.po,sha256=SXIW1Wz1Mtl_SqZwsjdIotFPYd9DUwl22Hp1YDxGhhA,19148 +django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo,sha256=TGNzP1smzgZmo5-s4VKD1E-nWTMtCSjp_hco1a0j4BQ,5565 +django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po,sha256=5uiQqnTyz0R-1vJTHqY0opwnQhMfgPoB-PxOkGpxNwk,6016 django/contrib/admin/locale/bn/LC_MESSAGES/django.mo,sha256=fKmzDwzLp0Qlv4bvWscf0evanPRAXwR04B6IeJ7wGSw,15247 django/contrib/admin/locale/bn/LC_MESSAGES/django.po,sha256=-go1WtUozfqbnKlUQr-jNnvEXf98eIZjq-C8KjRJ6NA,19812 django/contrib/admin/locale/bn/LC_MESSAGES/djangojs.mo,sha256=t_OiMyPMsR2IdH65qfD9qvQfpWbwFueNuY72XSed2Io,2313 @@ -613,12 +611,12 @@ django/contrib/admin/locale/bs/LC_MESSAGES/django.mo,sha256=44D550fxiO59Pczu5HZ6 django/contrib/admin/locale/bs/LC_MESSAGES/django.po,sha256=FrieR1JB4ssdWwYitJVpZO-odzPBKrW4ZsGK9LA595I,14317 django/contrib/admin/locale/bs/LC_MESSAGES/djangojs.mo,sha256=SupUK-RLDcqJkpLEsOVjgZOWBRKQMALZLRXGEnA623M,1183 django/contrib/admin/locale/bs/LC_MESSAGES/djangojs.po,sha256=TOtcfw-Spn5Y8Yugv2OlPoaZ5DRwJjRIl-YKiyU092U,3831 -django/contrib/admin/locale/ca/LC_MESSAGES/django.mo,sha256=x8ttMC4FzuTMZn_PYzn0QwOCYqnJldVT2RVti_QhC8s,17411 -django/contrib/admin/locale/ca/LC_MESSAGES/django.po,sha256=hsVktdeEaGacE0lXVbLe94yKmumIH8muBvnkx01Xxs4,18963 -django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo,sha256=waHQl2HV9fXDaQtYlyIEIYRVp4c-a9jNAwaXkmnKdUg,5150 -django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po,sha256=ol9nB369AYYt224-DbJJ_pQ9Y1CDsux2vOD8n8YsZQg,5806 -django/contrib/admin/locale/cs/LC_MESSAGES/django.mo,sha256=TOrsVokH8-5xCQ4Fr8rX7hy1slS1GYi7anlWr1QUPG8,17659 -django/contrib/admin/locale/cs/LC_MESSAGES/django.po,sha256=A9utoXRi5J3ZcHISjZp3ucyuPXu6CHbTQtxTRaIvN-4,19121 +django/contrib/admin/locale/ca/LC_MESSAGES/django.mo,sha256=tu97JMcpnH4wTL9Obz7HJh13QLBbvYJnn_7fdq4P3pg,17235 +django/contrib/admin/locale/ca/LC_MESSAGES/django.po,sha256=CL18wSkaPQgh0zUz5Z8bIR6DwJr47Bd16bSmMHB5PSs,18818 +django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo,sha256=xEkD4j5aPzRddlLC8W3aCZ7ah5RHC-MKTgFXI2uTPTI,4519 +django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po,sha256=CLxLUlg5GUQyWa-SC-8QpKdmdcpuzJl6TXHNRlO2s_E,5098 +django/contrib/admin/locale/cs/LC_MESSAGES/django.mo,sha256=kdfKK6BUnysuDqKyv6REMmzA-_BgYy2BpXmieYVzSQY,17448 +django/contrib/admin/locale/cs/LC_MESSAGES/django.po,sha256=K7ZZGmEP9X8Vq1mir6VZHfaZS_4IcMuk0ZJI0uaX1QM,18941 django/contrib/admin/locale/cs/LC_MESSAGES/djangojs.mo,sha256=7JR9YG_KB_aA8HQpMOON7CdTGYx-q3fjXvdvXBPv354,5679 django/contrib/admin/locale/cs/LC_MESSAGES/djangojs.po,sha256=_isJkLp5vzX3WbEnnve63QjtVRKUCZDOiz8Ng0_reho,6415 django/contrib/admin/locale/cy/LC_MESSAGES/django.mo,sha256=7ifUyqraN1n0hbyTVb_UjRIG1jdn1HcwehugHBiQvHs,12521 @@ -629,26 +627,26 @@ django/contrib/admin/locale/da/LC_MESSAGES/django.mo,sha256=jTtKti7NsWwvMyDA_sD8 django/contrib/admin/locale/da/LC_MESSAGES/django.po,sha256=kBfGE2OfUXd-Q8UALsQDErpiwxeaQX0lP4d9FIvyBTM,18093 django/contrib/admin/locale/da/LC_MESSAGES/djangojs.mo,sha256=TFo-KibDumqVFkHFlIDP87O3pLoXyliuf-K8maH3rl0,5098 django/contrib/admin/locale/da/LC_MESSAGES/djangojs.po,sha256=80URMUS7fIcBM3Qx97Wa9Gg_QV7Zcp4J2R-4FeEHgo8,5873 -django/contrib/admin/locale/de/LC_MESSAGES/django.mo,sha256=AjWysv_nOzNvjYTqcLsXCnNZt_5_2gn6qtCWKfvwAR8,17714 -django/contrib/admin/locale/de/LC_MESSAGES/django.po,sha256=i9uvk6nVIlOv2pO1Q-_BS6gjH5-tHxo9Q52gRxqCHOk,19193 -django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo,sha256=-1xXMjwq7J5p1YsOmwqkPEAaIGRzzdpikFVZTW4eYQQ,5203 -django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po,sha256=6XKG1F-vB0ELJn4zbtgCsXHyGm9Sh3BLVXN_wGpMYBg,5887 +django/contrib/admin/locale/de/LC_MESSAGES/django.mo,sha256=Bf2WUKVyn8BpytW_v41pp4V-GtqvgeJ-12zB5pX5j7k,17517 +django/contrib/admin/locale/de/LC_MESSAGES/django.po,sha256=nXHESFCYsDtz_fwX3zhAGr6xzIlSOB6aDaVv3mloqoA,19021 +django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo,sha256=b_NzGtn_jeOUkPH_BweWuRtsT1Hts2AEDP-byynEB1I,4591 +django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po,sha256=usHJodylqb3QltvaYYfyhUeP9-OpLoAXUE3lRKTQD2w,5198 django/contrib/admin/locale/dsb/LC_MESSAGES/django.mo,sha256=xxvchve6F4k4rgc5N8hlOotmv3-2y9kx-FQn-7506vY,17570 django/contrib/admin/locale/dsb/LC_MESSAGES/django.po,sha256=74YowJk3U5JApK8luxJ32HFoj6RTuVsoi4yg6kf2i_U,18784 django/contrib/admin/locale/dsb/LC_MESSAGES/djangojs.mo,sha256=TNj2M3uToLfTRR2fI47MZoZJdCbYs-tifz8vSz6M4Do,5609 django/contrib/admin/locale/dsb/LC_MESSAGES/djangojs.po,sha256=agDAUc7ktsqx37XhaIXkPB3TIFV5mqdFW23WGdS1SBU,6206 django/contrib/admin/locale/el/LC_MESSAGES/django.mo,sha256=54kG_94nJigDgJpZM8Cy58G_AGLdS5csJFEjTTvJBfM,22968 django/contrib/admin/locale/el/LC_MESSAGES/django.po,sha256=f2gUQtedb0sZCBxAoy3hP2rGXT9ysP5UTOlCBvu2NvI,24555 -django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo,sha256=cix1Bkj2hYO_ofRvtPDhJ9rBnTR6-cnKCFKpZrsxJ34,6509 -django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po,sha256=R05tMMuQEjVQpioy_ayQgFBlLM4WdwXthkMguW6ga24,7339 +django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo,sha256=vfha6S1wDTxgteeprHdCY6j1SnSWDdbC67aoks7TVFw,5888 +django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po,sha256=GJQytMIHNrJeWWnpaoGud4M6aiJCtJ7csyXzmfS6GZs,6560 django/contrib/admin/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 -django/contrib/admin/locale/en/LC_MESSAGES/django.po,sha256=q4HqtLXKeG_A5iu73qNDNdiI-XPcFEOAZ97pvyyAcME,23737 +django/contrib/admin/locale/en/LC_MESSAGES/django.po,sha256=bLNeZc57WHeM0Zgup4LUca6SsOSyJ2mpbuAtNZ_ZW6Q,23545 django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po,sha256=ziLBiK7Fw6IGf-ytCHNMk4NmKifDMv8zwNH9a9KPItI,7480 -django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo,sha256=QEvxPxDqNUmq8NxN-8c_F6KMEcWWum3YzERlc3_S_DM,16191 -django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po,sha256=BoVuGaPoGdQcF3zdgGRxrNKSq2XLHTvKfINCyU8t86Y,17548 -django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo,sha256=s0qPS8TjODtPo4miSznQfS6M8CQK9URDeMKeQsp7DK4,5001 -django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po,sha256=YecPU6VmUDDNNIzZVl2Wgd6lNRp3msJaW8FhdHMtEyc,5553 +django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo,sha256=DVjhYEbArfdAQLuE0YAG99eWxa9_eNEz2o9A6X6MrEY,2894 +django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po,sha256=CO7AV-NmmmwnXyBIybSfNZLdXiavphWsd9LNZQNqDL4,11800 +django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo,sha256=LWNYXUicANYZeiNx4mb6pFpjnsaggPTxTBCbNKxPtFw,1714 +django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po,sha256=UZk0oHToRtHzlviraFzWcZlpVAOk_W2oq4NquxevQoE,3966 django/contrib/admin/locale/en_GB/LC_MESSAGES/django.mo,sha256=pFkTMRDDj76WA91wtGPjUB7Pq2PN7IJEC54Tewobrlc,11159 django/contrib/admin/locale/en_GB/LC_MESSAGES/django.po,sha256=REUJMGLGRyDMkqh4kJdYXO9R0Y6CULFVumJ_P3a0nv0,15313 django/contrib/admin/locale/en_GB/LC_MESSAGES/djangojs.mo,sha256=hW325c2HlYIIdvNE308c935_IaDu7_qeP-NlwPnklhQ,3147 @@ -657,12 +655,12 @@ django/contrib/admin/locale/eo/LC_MESSAGES/django.mo,sha256=rrRYsz82QIaUEuHREYIw django/contrib/admin/locale/eo/LC_MESSAGES/django.po,sha256=2AacIHf1R3030J0Deo-2LR8VZhmPmG4nii6mi8q9kbY,16778 django/contrib/admin/locale/eo/LC_MESSAGES/djangojs.mo,sha256=I1Ue345qSHPmJpX4yiYgomQ8vMgshRt1S1D_ZVJWf7g,4452 django/contrib/admin/locale/eo/LC_MESSAGES/djangojs.po,sha256=BdSRWCYCDxLxtbcPSfRdAMGoTRWOWaxRGpdCIm-3HA0,5040 -django/contrib/admin/locale/es/LC_MESSAGES/django.mo,sha256=lHBMJveopDZkJ2pUiNMNjh3WJN6tBB6VAp1ycUodsTI,17696 -django/contrib/admin/locale/es/LC_MESSAGES/django.po,sha256=9bOk-Ye-JWUdyqZzSkUE11NQgX7xnCOgSd-Uy4YspeQ,19639 +django/contrib/admin/locale/es/LC_MESSAGES/django.mo,sha256=8wTwwjDHa-vq64nEkJJVk-yWobNj7kRHsrI_3Y81SIk,17509 +django/contrib/admin/locale/es/LC_MESSAGES/django.po,sha256=PAXUTRrMzwBDcEwPuljyiqg0cbKRgv5I63Xv5aWsNig,19458 django/contrib/admin/locale/es/LC_MESSAGES/djangojs.mo,sha256=FWXwW5YUTD6HHoYV2Snc2WeNmllz9vJVy5rjSCjQlA4,5203 django/contrib/admin/locale/es/LC_MESSAGES/djangojs.po,sha256=v1xp_eeyFsrW9rvXk5V6pOVyzHP_ZX06_fCbQppdE-4,6035 -django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo,sha256=9OUrQduv7L9y1JkYc24fJkI1VXvvnTfZt-p44PC_yiU,17847 -django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po,sha256=DjWG0FHYhL5Fa6-grLAwUUTLXtmbpnMjSL2pKS_VrJY,19082 +django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo,sha256=8K2frakJyZHI42JAyid08zpmomre2mvH1VPOAa7ns1g,17658 +django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po,sha256=v6vTPdV_MYytF3aQ-2y1mxECtAKyb9kc6U9at5Adpi8,18929 django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.mo,sha256=odNv6CkDux2LhzpJX3Dnh66cHQOFDSlbal5eQGZIyjw,5409 django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.po,sha256=t6bJnFzl5Pid7PqEBNDm4QmlAqLaU4zyeOXXWocazyI,5990 django/contrib/admin/locale/es_CO/LC_MESSAGES/django.mo,sha256=0k8kSiwIawYCa-Lao0uetNPLUzd4m_me3tCAVBvgcSw,15156 @@ -685,16 +683,16 @@ django/contrib/admin/locale/eu/LC_MESSAGES/django.mo,sha256=vA5uxffIq16C1hBztWR- django/contrib/admin/locale/eu/LC_MESSAGES/django.po,sha256=JCEgbT6Y8okN675f3DEOAJwY9xnyEBAlUPOAjV-QWkI,16725 django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.mo,sha256=bZHiuTFj8MNrO3AntBAY5iUhmCa6LSluGLYw504RKWg,4522 django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.po,sha256=eMpM70UTWIiCDigCgYVOZ9JKQ2IidYZxYcUWunvG8js,5051 -django/contrib/admin/locale/fa/LC_MESSAGES/django.mo,sha256=Og9enbwYKrRvYCgnhAxxCLPiR5a3qqi-6k7YMr9pH7E,20345 -django/contrib/admin/locale/fa/LC_MESSAGES/django.po,sha256=729GcmVyQ9tksaff4VJbvaeKdaoly5gQhad06Tn42YY,22018 +django/contrib/admin/locale/fa/LC_MESSAGES/django.mo,sha256=MA_fSEofWoIgJ_Bzf7lN0W5poJDqjKIOwWkT1VKgZL8,20069 +django/contrib/admin/locale/fa/LC_MESSAGES/django.po,sha256=Fhi3d1AjUkA2lhdAChTR8I6L8jcnTDQjQw2GQ1V-rTM,21703 django/contrib/admin/locale/fa/LC_MESSAGES/djangojs.mo,sha256=MAje4ub3vWYhiKrVR_LvxAIqkvOlFpVcXQEBz3ezlPs,6050 django/contrib/admin/locale/fa/LC_MESSAGES/djangojs.po,sha256=1nzEmRuswDmyCCMShGH2CYdjMY7tUuedfN4kDCEnTCM,6859 -django/contrib/admin/locale/fi/LC_MESSAGES/django.mo,sha256=FgQSwJAeyDUdx2MZPFD9RD-6B961SSaUdXGGviF0F9k,16810 -django/contrib/admin/locale/fi/LC_MESSAGES/django.po,sha256=kUjN-OY9KRoLuLTplidIYfUSbZPUbmLs2MGsQdcS2c0,18172 -django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo,sha256=htqAFHouiZKPRSNwEH7fe278pxKskccsmHmybLb8qhY,5200 -django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po,sha256=OJWC0eqFhjmokWRWWV3E7nL-FlFkuUjDt4ts5_hXOIs,5856 -django/contrib/admin/locale/fr/LC_MESSAGES/django.mo,sha256=fTZxxfKSDXuQc_XslYUS32yBsQYZxemrIcWJN1CJlsg,18497 -django/contrib/admin/locale/fr/LC_MESSAGES/django.po,sha256=I5-sNdewcrfmvyUG7ZH_u9atE9qM7AmEEX1nvcZnqYI,19791 +django/contrib/admin/locale/fi/LC_MESSAGES/django.mo,sha256=_VYESi8y2__R9MNNT7eItdzOpKBtO6P0C_LtiKs4J-I,16601 +django/contrib/admin/locale/fi/LC_MESSAGES/django.po,sha256=m1fQI68Pq0FeSu6hZ9P_hJoHFc37FSKlRzyYzoKYbhk,17946 +django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo,sha256=PtdmimFeCuhnPBhURSpbrE3LobqSmHvVovauR705vAo,5174 +django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po,sha256=KIKHcUBhIZodbc9dBc8FOEhz3ZdBi7shR76lPpJ34UM,5784 +django/contrib/admin/locale/fr/LC_MESSAGES/django.mo,sha256=GRJvPVpRudS3lHx_xKdBwRshrEwOZQOSUMEi2cj3FXg,18311 +django/contrib/admin/locale/fr/LC_MESSAGES/django.po,sha256=Cpi0rXkY8RV1_ctWKkW7STXHCam1a1sSbULjFt2ppXQ,19641 django/contrib/admin/locale/fr/LC_MESSAGES/djangojs.mo,sha256=gxMws51GbzrgGA2b0oMFvujuO97g-GdLJDgIb0biiIk,5324 django/contrib/admin/locale/fr/LC_MESSAGES/djangojs.po,sha256=j78nbvunsFPNzE3ebSLaQ21nQxI_vapJf3LmZQclmi0,5940 django/contrib/admin/locale/fy/LC_MESSAGES/django.mo,sha256=mWnHXGJUtiewo1F0bsuJCE_YBh7-Ak9gjTpwjOAv-HI,476 @@ -705,18 +703,18 @@ django/contrib/admin/locale/ga/LC_MESSAGES/django.mo,sha256=cIOjVge5KC37U6g-0MMa django/contrib/admin/locale/ga/LC_MESSAGES/django.po,sha256=Qx1D0cEGIIPnO10I_83IfU3faEYpp0lm-KHg48lJMxE,17687 django/contrib/admin/locale/ga/LC_MESSAGES/djangojs.mo,sha256=G-9VfhiMcooTbAI1IMvbvUwj_h_ttNyxGS89nIgrpw4,5247 django/contrib/admin/locale/ga/LC_MESSAGES/djangojs.po,sha256=DsDMYhm5PEpFBBGepf2iRD0qCkh2r45Y4tIHzFtjJAo,5920 -django/contrib/admin/locale/gd/LC_MESSAGES/django.mo,sha256=HEqiGvjMp0NnfIS0Z-c1i8SicEtMPIg8LvNMh-SXiPg,18871 -django/contrib/admin/locale/gd/LC_MESSAGES/django.po,sha256=cZWnJyEoyGFLbk_M4-eddTJLKJ0dqTIlIj4w6YwcjJg,20139 -django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo,sha256=QA2_hxHGzt_y0U8sAGQaT27IvvyWrehLPKP2X1jAvEs,5904 -django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po,sha256=KyYGpFHq2E55dK005xzH0I2RD-C2kD6BlJi8bcMjtRA,6540 -django/contrib/admin/locale/gl/LC_MESSAGES/django.mo,sha256=rRBlaoBQzzpFHN9ZuuHvXTJnLYciMYHA8IX9K6-4fmw,10785 -django/contrib/admin/locale/gl/LC_MESSAGES/django.po,sha256=OEOpmZuXMGG_SSHX0zdw_bgy3gDzcjWz9zgb9esYHrA,15902 +django/contrib/admin/locale/gd/LC_MESSAGES/django.mo,sha256=kEshne-LdMmLV-kbDMGUkdBPxI4uMRo0O0Xg3vO6e9k,18664 +django/contrib/admin/locale/gd/LC_MESSAGES/django.po,sha256=gw60FvWiiFJ4-RG01FTbfA9nROy3YY2iUn1FGBdN54g,19963 +django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo,sha256=MHKCPa0zUdp76nbzy96fK3DsaWmtTs_HoOD1G0s4TRY,5278 +django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po,sha256=QMMFZMl6zjz9CoZzlzmGej0vPgvrxIy_xeDl6xlymJw,6492 +django/contrib/admin/locale/gl/LC_MESSAGES/django.mo,sha256=_9JW7LdCw2on4M1oz3Iyl_VMrhrw_0oVIQl4h_rCX6g,13246 +django/contrib/admin/locale/gl/LC_MESSAGES/django.po,sha256=xqdcVwIX5zPxq471crW0yxcOYcbZVaRwKiKx-MAGiqk,16436 django/contrib/admin/locale/gl/LC_MESSAGES/djangojs.mo,sha256=YkT7l3U9ffSGqXmu6S41Ex0r7tbK-0BKH5lS6O8PAGs,3279 django/contrib/admin/locale/gl/LC_MESSAGES/djangojs.po,sha256=EDccOpm1mpT8mVRvu5LBsq8nao50oP1V7aKEnuRmtF8,4803 -django/contrib/admin/locale/he/LC_MESSAGES/django.mo,sha256=5Ckbdd-vF0C-W6tHf2_o2SZzMiRyrv9u9W0CLsqt0XM,16297 -django/contrib/admin/locale/he/LC_MESSAGES/django.po,sha256=FoVOVR6iqKlFLhkHMLJMnQJmLLwzkVKe5wQ7IsFPX_c,18924 -django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo,sha256=sdc97pmpMSUAvoMwrWOHyGPYV4j3DDhz4DlqFeRVTT4,5791 -django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po,sha256=ZXy7lexBNYbzAriBG27Jn-mv2DFoGobsV1Ur2lDtRMQ,6573 +django/contrib/admin/locale/he/LC_MESSAGES/django.mo,sha256=0afg9nan7nwEChy6dJtLzexdQK_rPT_k1LhjA_QnIoM,16250 +django/contrib/admin/locale/he/LC_MESSAGES/django.po,sha256=QPOorIn6TOY8UFgK0mcVTZ1gxq2uacdE7JhV8H-qAyw,18811 +django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo,sha256=odvNcABcTGzBw9u3CYoUjG58toB_IVPV4B45NT6Qj8I,5117 +django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po,sha256=Cx6k80Xb4jowscPTwMGjw69kpyeLq3LGmccHoPwk_YM,5781 django/contrib/admin/locale/hi/LC_MESSAGES/django.mo,sha256=EogCHT8iAURSuE34kZ0kwEIoz5VjgUQUG2eAIqDxReU,18457 django/contrib/admin/locale/hi/LC_MESSAGES/django.po,sha256=NcTFbFyHhWOIieUpzIVL7aSDWZ8ZNmfnv5gcxhON1zc,21770 django/contrib/admin/locale/hi/LC_MESSAGES/djangojs.mo,sha256=yCUHDS17dQDKcAbqCg5q8ualaUgaa9qndORgM-tLCIw,4893 @@ -725,8 +723,8 @@ django/contrib/admin/locale/hr/LC_MESSAGES/django.mo,sha256=3TR3uFcd0pnkDi551WaB django/contrib/admin/locale/hr/LC_MESSAGES/django.po,sha256=qcW7tvZoWZIR8l-nMRexGDD8VlrOD7l5Fah6-ecilMk,17378 django/contrib/admin/locale/hr/LC_MESSAGES/djangojs.mo,sha256=KR34lviGYh1esCkPE9xcDE1pQ_q-RxK1R2LPjnG553w,3360 django/contrib/admin/locale/hr/LC_MESSAGES/djangojs.po,sha256=w7AqbYcLtu88R3KIKKKXyRt2gwBBBnr-ulxONWbw01I,4870 -django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo,sha256=-ParKlCmKrzENtnyHqh4a0yJE18J5yi0TP-r9jQfArI,17520 -django/contrib/admin/locale/hsb/LC_MESSAGES/django.po,sha256=9e_sWbpB1UlyORWsbjOMXvA2vtsiff1saZfhOoGhX50,18700 +django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo,sha256=xdFfD6IiKou_-oWJDKZt-L-FoxaYFXcqbh0LJ2tlXhQ,17310 +django/contrib/admin/locale/hsb/LC_MESSAGES/django.po,sha256=sBWUnTFK-d6ZAtmfk_RqyoreuXLZYeteOt75vvDl-bc,18520 django/contrib/admin/locale/hsb/LC_MESSAGES/djangojs.mo,sha256=nx5PHpjnFVOL_64Ui3HGTgUOZWnIUy5HF0DxHi7Dyz0,5681 django/contrib/admin/locale/hsb/LC_MESSAGES/djangojs.po,sha256=XMpmL1mYKGymExY5KXl9FBBZgMrJIGNSBBvc92y8yno,6281 django/contrib/admin/locale/hu/LC_MESSAGES/django.mo,sha256=O_QBDJcYI_rVYvXdI3go3YA2Y1u-NOuKOwshF6Ic7bs,17427 @@ -741,26 +739,26 @@ django/contrib/admin/locale/ia/LC_MESSAGES/django.mo,sha256=SRKlr8RqW8FQhzMsXdA9 django/contrib/admin/locale/ia/LC_MESSAGES/django.po,sha256=pBQLQsMinRNh0UzIHBy3qEW0etUWMhFALu4-h-woFyE,15337 django/contrib/admin/locale/ia/LC_MESSAGES/djangojs.mo,sha256=28MiqUf-0-p3PIaongqgPQp2F3D54MLAujPslVACAls,3177 django/contrib/admin/locale/ia/LC_MESSAGES/djangojs.po,sha256=CauoEc8Fiowa8k6K-f9N8fQDle40qsgtXdNPDHBiudQ,4567 -django/contrib/admin/locale/id/LC_MESSAGES/django.mo,sha256=K4QEDjEXouYrPWgmgGUbOdH895CruqSPsXizLBeZi6g,16681 -django/contrib/admin/locale/id/LC_MESSAGES/django.po,sha256=_xgVOVRsRVendtBnCwOJe2J1CKZqk93RYhN0LbBP3AY,18131 -django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo,sha256=S2kI3uRCVI1vic6VTp4NPQL_CGcEKMe-Q2cpJhyfxtk,5006 -django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po,sha256=_0EkG-U2XN68kTUGry_NPq2YKr2ufk2Xm7Ody4tmHuE,5680 +django/contrib/admin/locale/id/LC_MESSAGES/django.mo,sha256=RsmykBQKAWODi-L74zHjhxv82jb96LpvJ1TXkXNYyc8,16474 +django/contrib/admin/locale/id/LC_MESSAGES/django.po,sha256=6e_BBHUtcnMY7OK7eXQKbGWv-ba-2M8KVB79Q759Jw4,17946 +django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo,sha256=Qxy1U7XeN_YLy19vRrn0pom2FD2iMUTDAtAc7XKZazg,4373 +django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po,sha256=zEz-pQryKAi75nve5849v1nzN-X4GUCHg5U1KZ4gBDo,5620 django/contrib/admin/locale/io/LC_MESSAGES/django.mo,sha256=URiYZQZpROBedC-AkpVo0q3Tz78VfkmwN1W7j6jYpMo,12624 django/contrib/admin/locale/io/LC_MESSAGES/django.po,sha256=y0WXY7v_9ff-ZbFasj33loG-xWlFO8ttvCB6YPyF7FQ,15562 django/contrib/admin/locale/io/LC_MESSAGES/djangojs.mo,sha256=nMu5JhIy8Fjie0g5bT8-h42YElCiS00b4h8ej_Ie-w0,464 django/contrib/admin/locale/io/LC_MESSAGES/djangojs.po,sha256=WLh40q6yDs-8ZG1hpz6kfMQDXuUzOZa7cqtEPDywxG4,2852 django/contrib/admin/locale/is/LC_MESSAGES/django.mo,sha256=csD3bmz3iQgLLdSqCKOmY_d893147TvDumrpRVoRTY0,16804 django/contrib/admin/locale/is/LC_MESSAGES/django.po,sha256=tXgb3ARXP5tPa5iEYwwiHscDGfjS5JgIV2BsUX8OnjE,18222 -django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo,sha256=Z3ujWoenX5yYTAUmHUSCvHcuV65nQmYKPv6Jo9ygx_c,5174 -django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po,sha256=YPf4XqfnpvrS9irAS8O4G0jgU5PCoQ9C-w3MoDipelk,5847 -django/contrib/admin/locale/it/LC_MESSAGES/django.mo,sha256=Z5WsBMKDNX_0t7U0_q0NVMqaqrquzrN2zjFr8gTEO8E,17309 -django/contrib/admin/locale/it/LC_MESSAGES/django.po,sha256=5xuPVKs6xKuxJwPt4X8oek88baE2Fn-cL5VNi5OXZf0,18982 +django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo,sha256=VcJvjwOJ8FgYiGRWVD1sPi-yuhFMR19ejIewhOQyP84,4554 +django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po,sha256=lpbOnRlgNaESvPfojZskcAn4HNnsFfYK9rxV8D6ucQg,5150 +django/contrib/admin/locale/it/LC_MESSAGES/django.mo,sha256=Pk4GFKmpamyy3YpDbij4Z1NR_whRb00tUsrchoqpmwY,17108 +django/contrib/admin/locale/it/LC_MESSAGES/django.po,sha256=BWXpcabtGkczT2JYuZ6KeyNfGQoA_KZUUPq7IrQU3vg,18769 django/contrib/admin/locale/it/LC_MESSAGES/djangojs.mo,sha256=dZ-_EjmtlPHhaOgnz1ISFAYkaSkuEpw_d-sdU7BZeaU,5119 django/contrib/admin/locale/it/LC_MESSAGES/djangojs.po,sha256=ixWfLqf2WwFnkn63WR1DzY4GWXHXSa9eyoxTNPAFDGA,5919 -django/contrib/admin/locale/ja/LC_MESSAGES/django.mo,sha256=XuHILa1vd1pSQAI2u-Z-tAFb2T3_JfgDT215bPiGnM4,18553 -django/contrib/admin/locale/ja/LC_MESSAGES/django.po,sha256=4MtIoWafdzn5UMzlU1pS88933VMAPUCUFn9skh9F08Y,20037 -django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo,sha256=FVs4HxrhvIXVfZMxHq5vPSx55ggb8Mmd0F4Go47JDvA,5284 -django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po,sha256=v2P8YcTDTOWKPWgw0J45V1m5Tzpz752xmaIh_CzRYQQ,5899 +django/contrib/admin/locale/ja/LC_MESSAGES/django.mo,sha256=hm9obimsew7xXJ1TbN61cW_nKp6L_nDxAY4OxtAnk9w,18371 +django/contrib/admin/locale/ja/LC_MESSAGES/django.po,sha256=EFG0PQ9ygHR_CmZvoh0ldJjbs2seKFfGWDaXD5JBvrY,19886 +django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo,sha256=W_m4kF7EVFZzW_BZAsvti79739ijFgAPHiaENk_9IRs,4638 +django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po,sha256=UNzTc5BrNBlUyBaIzgcdIrd4vkIJqsoSGWJVmX3hjYY,5789 django/contrib/admin/locale/ka/LC_MESSAGES/django.mo,sha256=M3FBRrXFFa87DlUi0HDD_n7a_0IYElQAOafJoIH_i60,20101 django/contrib/admin/locale/ka/LC_MESSAGES/django.po,sha256=abkt7pw4Kc-Y74ZCpAk_VpFWIkr7trseCtQdM6IUYpQ,23527 django/contrib/admin/locale/ka/LC_MESSAGES/djangojs.mo,sha256=GlPU3qUavvU0FXPfvCl-8KboYhDOmMsKM-tv14NqOac,5516 @@ -778,15 +776,15 @@ django/contrib/admin/locale/km/LC_MESSAGES/django.po,sha256=RSxy5vY2sgC43h-9sl6e django/contrib/admin/locale/km/LC_MESSAGES/djangojs.mo,sha256=Ja8PIXmw6FMREHZhhBtGrr3nRKQF_rVjgLasGPnU95w,1334 django/contrib/admin/locale/km/LC_MESSAGES/djangojs.po,sha256=LH4h4toEgpVBb9yjw7d9JQ8sdU0WIZD-M025JNlLXAU,3846 django/contrib/admin/locale/kn/LC_MESSAGES/django.mo,sha256=955iPq05ru6tm_iPFVMebxwvZMtEa5_7GaFG1mPt6HU,9203 -django/contrib/admin/locale/kn/LC_MESSAGES/django.po,sha256=-4YAm0MyhS-wp4RQmo0TzWvqYqmzHFNpIBtdQlg_8Dw,16059 +django/contrib/admin/locale/kn/LC_MESSAGES/django.po,sha256=xMGtsVCItMTs18xdFQHELdVZKCwTNNyKfb8n1ARcFws,16053 django/contrib/admin/locale/kn/LC_MESSAGES/djangojs.mo,sha256=dHzxizjDQWiZeRfBqnVFcK1yk1-M5p1KOfQ1ya9TMVU,1872 django/contrib/admin/locale/kn/LC_MESSAGES/djangojs.po,sha256=MqRj6ozyr1e9-qNORUTJXNahe6SL3ee3OveSm3efV4g,4214 -django/contrib/admin/locale/ko/LC_MESSAGES/django.mo,sha256=n5v2HMHEYIaX1_uog-MkbDQ011j79xlMwljjfrpETpg,17829 -django/contrib/admin/locale/ko/LC_MESSAGES/django.po,sha256=d1HDdGcdcp9oNv0lXsl7Bf9Ws0bDy8Kizeff1G_llfo,19671 -django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo,sha256=NQOPmHV4Z1wMqS1WobZrNb6z-f-uPgNFh5WY6W2nLNo,5092 -django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po,sha256=FUqBIciM_sVkeIMdu0_WYtIDhIINpIDp5MElLTPTsug,5824 -django/contrib/admin/locale/ky/LC_MESSAGES/django.mo,sha256=eg-TnIzJO4h3q_FS2a1LnCs7qOf5dpNJwvRD99ZZ0GQ,20129 -django/contrib/admin/locale/ky/LC_MESSAGES/django.po,sha256=dWxU3yUAKHUGKdVJbRLkS6fJEefPBk2XM0i2INcRPms,21335 +django/contrib/admin/locale/ko/LC_MESSAGES/django.mo,sha256=t-VhuZQjQDfGOyrvtvRvIrGvxEZ01cz-9SeYy5OHr80,17852 +django/contrib/admin/locale/ko/LC_MESSAGES/django.po,sha256=nlMbskDxKxE0ywhyEYfOUNc2Jp8WtLh3GFzZdx5UiTA,19587 +django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo,sha256=StaaunOE52Uo9MgCvyTQpgKhicFsHlXktYSZAOn7u_Y,4462 +django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po,sha256=Tv1_SxJW_5y2tXMNVom5MxxTVt-sYiXvf5QMFloiJD4,5080 +django/contrib/admin/locale/ky/LC_MESSAGES/django.mo,sha256=oiXcQY5ZiwbTHE19fK_Ru5SPoR7Ap5vpemHP7awrxNM,19917 +django/contrib/admin/locale/ky/LC_MESSAGES/django.po,sha256=qUPSbpaQe-P40ywplZwbk1GB4vkPefR_jrlKOg9BdT0,21154 django/contrib/admin/locale/ky/LC_MESSAGES/djangojs.mo,sha256=VuBYBwFwIHC27GFZiHY2_4AB0cME2R0Q3juczjOs3G0,5888 django/contrib/admin/locale/ky/LC_MESSAGES/djangojs.po,sha256=uMk9CxL1wP45goq2093lYMza7LRuO4XbVo5RRWlsbaE,6432 django/contrib/admin/locale/lb/LC_MESSAGES/django.mo,sha256=8GGM2sYG6GQTQwQFJ7lbg7w32SvqgSzNRZIUi9dIe6M,913 @@ -797,30 +795,26 @@ django/contrib/admin/locale/lt/LC_MESSAGES/django.mo,sha256=SpaNUiaGtDlX5qngVj0d django/contrib/admin/locale/lt/LC_MESSAGES/django.po,sha256=tHnRrSNG2ENVduP0sOffCIYQUn69O6zIev3Bb7PjKb0,18497 django/contrib/admin/locale/lt/LC_MESSAGES/djangojs.mo,sha256=vZtnYQupzdTjVHnWrtjkC2QKNpsca5yrpb4SDuFx0_0,5183 django/contrib/admin/locale/lt/LC_MESSAGES/djangojs.po,sha256=dMjFClA0mh5g0aNFTyHC8nbYxwmFD0-j-7gCKD8NFnw,5864 -django/contrib/admin/locale/lv/LC_MESSAGES/django.mo,sha256=JasSPrV3rsW1_Axk8bfJILKuAx3jNqIKjQwdfLLb85E,17086 -django/contrib/admin/locale/lv/LC_MESSAGES/django.po,sha256=zpn9vVxFQ60Tu8050Zc_Hh47rzZN0jyyCqXLYB9y8_g,18460 +django/contrib/admin/locale/lv/LC_MESSAGES/django.mo,sha256=X8X5_tms9JliGku_YG-z21TnB6WLhVkxUx4fI3UPfyY,16880 +django/contrib/admin/locale/lv/LC_MESSAGES/django.po,sha256=ky9VntKirOLFY-TG_Mx4VsE691ZEreihW0BlgY2NYzc,18290 django/contrib/admin/locale/lv/LC_MESSAGES/djangojs.mo,sha256=m2v9CtKlzh10brhZelAruNj0var77Mr1N0uoeetqW-4,5491 django/contrib/admin/locale/lv/LC_MESSAGES/djangojs.po,sha256=1hCp5ziBGVDokhDDU0wRCeGeLkWuhTyI291oX3m0boM,6165 -django/contrib/admin/locale/mk/LC_MESSAGES/django.mo,sha256=wy8NuOl_ojwSrY0pWjJ7XXbPl_O0kckp618zIE0a8Hk,15611 -django/contrib/admin/locale/mk/LC_MESSAGES/django.po,sha256=DYsVd2DM6QYWWLcYnOROHN_oFilWmjxIpUMyF9RtmvM,19505 -django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo,sha256=8BkWjadml2f1lDeH-IULdxsogXSK8NpVuu293GvcQc8,4719 -django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po,sha256=u9mVSzbIgA1uRgV_L8ZOZLelyknoKFvXH0HbBurezf8,6312 +django/contrib/admin/locale/mk/LC_MESSAGES/django.mo,sha256=0lnlstwZKEB-RvsQ2VE0QFbxszjLXahoNJgpOsfJH6E,15672 +django/contrib/admin/locale/mk/LC_MESSAGES/django.po,sha256=liRNtoZV26sqc0A35lcdDON-kQzbM_21YLW3O4VF1eg,19452 +django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo,sha256=ZyQQ49zqs8GiS73XBaSd5l3Rh3vOA0glMpX98GH6nhU,5633 +django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po,sha256=bWph0TVgwC-Fmlof8_4SiR21uCFm9rftp59AMZ3WIYA,6188 django/contrib/admin/locale/ml/LC_MESSAGES/django.mo,sha256=4Y1KAip3NNsoRc9Zz3k0YFLzes3DNRFvAXWSTBivXDk,20830 django/contrib/admin/locale/ml/LC_MESSAGES/django.po,sha256=jL9i3kmOnoKYDq2RiF90WCc55KeA8EBN9dmPHjuUfmo,24532 django/contrib/admin/locale/ml/LC_MESSAGES/djangojs.mo,sha256=COohY0mAHAOkv1eNzLkaGZy8mimXzcDK1EgRd3tTB_E,6200 django/contrib/admin/locale/ml/LC_MESSAGES/djangojs.po,sha256=NvN0sF_w5tkc3bND4lBtCHsIDLkwqdEPo-8wi2MTQ14,7128 -django/contrib/admin/locale/mn/LC_MESSAGES/django.mo,sha256=Lu8mM_3lJuByz4xXE7shq4nuBwE71_yh4_HIuy7KK64,14812 -django/contrib/admin/locale/mn/LC_MESSAGES/django.po,sha256=yNbv9cOeXEHPiDOKPXIbq2-cBZvUXSXCfL4TPe74x0s,18851 +django/contrib/admin/locale/mn/LC_MESSAGES/django.mo,sha256=tsi1Lc7qcDD5dTjMQKy-9Hq-V2Akzyi994QY8wVaqNk,20545 +django/contrib/admin/locale/mn/LC_MESSAGES/django.po,sha256=T9WZQ5k0M9_pLCf5A-fDFIXKgN9fRisfsoZNnm4u-jk,21954 django/contrib/admin/locale/mn/LC_MESSAGES/djangojs.mo,sha256=H7fIPdWTK3_iuC0WRBJdfXN8zO77p7-IzTviEUVQJ2U,5228 django/contrib/admin/locale/mn/LC_MESSAGES/djangojs.po,sha256=vJIqqVG34Zd7q8-MhTgZcXTtl6gukOSb6egt70AOyAc,5757 django/contrib/admin/locale/mr/LC_MESSAGES/django.mo,sha256=UAxGnGliid2PTx6SMgIuHVfbCcqVvcwC4FQUWtDuSTc,468 django/contrib/admin/locale/mr/LC_MESSAGES/django.po,sha256=TNARpu8Pfmu9fGOLUP0bRwqqDdyFmlh9rWjFspboTyc,10491 django/contrib/admin/locale/mr/LC_MESSAGES/djangojs.mo,sha256=2Z5jaGJzpiJTCnhCk8ulCDeAdj-WwR99scdHFPRoHoA,468 django/contrib/admin/locale/mr/LC_MESSAGES/djangojs.po,sha256=uGe9kH2mwrab97Ue77oggJBlrpzZNckKGRUMU1vaigs,2856 -django/contrib/admin/locale/ms/LC_MESSAGES/django.mo,sha256=Xj5v1F4_m1ZFUn42Rbep9eInxIV-NE-oA_NyfQkbp00,16840 -django/contrib/admin/locale/ms/LC_MESSAGES/django.po,sha256=ykFH-mPbv2plm2NIvKgaj3WVukJ3SquU8nQIAXuOrWA,17967 -django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.mo,sha256=9VY_MrHK-dGOIkucLCyR9psy4o5p4nHd8kN_5N2E-gY,5018 -django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.po,sha256=P4GvM17rlX1Vl-7EbCyfWVasAJBEv_RvgWEvfJqcErA,5479 django/contrib/admin/locale/my/LC_MESSAGES/django.mo,sha256=xvlgM0vdYxZuA7kPQR7LhrLzgmyVCHAvqaqvFhKX9wY,3677 django/contrib/admin/locale/my/LC_MESSAGES/django.po,sha256=zdUCYcyq2-vKudkYvFcjk95YUtbMDDSKQHCysmQ-Pvc,12522 django/contrib/admin/locale/my/LC_MESSAGES/djangojs.mo,sha256=1fS9FfWi8b9NJKm3DBKETmuffsrTX-_OHo9fkCCXzpg,3268 @@ -829,28 +823,28 @@ django/contrib/admin/locale/nb/LC_MESSAGES/django.mo,sha256=viQKBFH6ospYn2sE-Dok django/contrib/admin/locale/nb/LC_MESSAGES/django.po,sha256=x0ANRpDhe1rxxAH0qjpPxRfccCvR73_4g5TNUdJqmrc,17682 django/contrib/admin/locale/nb/LC_MESSAGES/djangojs.mo,sha256=KwrxBpvwveERK4uKTIgh-DCc9aDLumpHQYh5YroqxhQ,4939 django/contrib/admin/locale/nb/LC_MESSAGES/djangojs.po,sha256=ygn6a5zkHkoIYMC8Hgup8Uw1tMbZcLGgwwDu3x33M-o,5555 -django/contrib/admin/locale/ne/LC_MESSAGES/django.mo,sha256=yrm85YXwXIli7eNaPyBTtV7y3TxQuH4mokKuHdAja2A,15772 -django/contrib/admin/locale/ne/LC_MESSAGES/django.po,sha256=F8vfWKvSNngkLPZUIwik_qDYu0UAnrWepbI9Z9Iz35g,20400 +django/contrib/admin/locale/ne/LC_MESSAGES/django.mo,sha256=r01XjvWuPnnyQ8RXqK4-LsyFKA4WAFl5WNJ1g-UFIvk,15882 +django/contrib/admin/locale/ne/LC_MESSAGES/django.po,sha256=UNTRvBq1FpftJJpveiyC7VHxctbxhnrbC1ybDRYj-MA,20221 django/contrib/admin/locale/ne/LC_MESSAGES/djangojs.mo,sha256=mJdtpLT9k4vDbN9fk2fOeiy4q720B3pLD3OjLbAjmUI,5362 django/contrib/admin/locale/ne/LC_MESSAGES/djangojs.po,sha256=N91RciTV1m7e8-6Ihod5U2xR9K0vrLoFnyXjn2ta098,6458 django/contrib/admin/locale/nl/LC_MESSAGES/django.mo,sha256=ndq_k6QUL6hwc9iuI-rlPbML_-HdcUslCXLRxiV10yw,17070 django/contrib/admin/locale/nl/LC_MESSAGES/django.po,sha256=SaTkp0m6wEbwl79Q3Lj6vICGw61HI5Um4_8Bs2hfhg0,18768 django/contrib/admin/locale/nl/LC_MESSAGES/djangojs.mo,sha256=yHX5iQjKqqrIxl_K-AQkBMFNQ8YmgdUxAJVkOEfWDE4,4592 django/contrib/admin/locale/nl/LC_MESSAGES/djangojs.po,sha256=B9y-TjAFtDgnX7RcPlWWgCqdOUzWY5EWV-buuXtP468,5457 -django/contrib/admin/locale/nn/LC_MESSAGES/django.mo,sha256=rVQYyJ7SYlKH3vfpOWD3MQZgL5AVnxxDiwDBjhlb1dk,16502 -django/contrib/admin/locale/nn/LC_MESSAGES/django.po,sha256=sh1dss7STEXkyLssLgFbvM_1T4xAd68M5mW3x2mUG34,17825 -django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo,sha256=RsDri1DmCwrby8m7mLWkFdCe6HK7MD7GindOarVYPWc,4939 -django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po,sha256=koVTt2mmdku1j7SUDRbnug8EThxXuCIF2XPnGckMi7A,5543 +django/contrib/admin/locale/nn/LC_MESSAGES/django.mo,sha256=zKIlvBLMvoqrXO90TqPJcdTEXkVweUWpz6ynsWeg8mU,10943 +django/contrib/admin/locale/nn/LC_MESSAGES/django.po,sha256=-CFana0-PPFwv1jcdyjYuLK2OYOPva-xxMjlVhvsoCw,14999 +django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo,sha256=A7MT59BoyOSiM7W0phx8LLKQyH4Q8AEu6jUsBjUBOoE,3120 +django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po,sha256=tCXUV4F6FhMa-K0SBw9lQ0U2KY5kcMpGzT7jzKSvceo,4578 django/contrib/admin/locale/os/LC_MESSAGES/django.mo,sha256=c51PwfOeLU2YcVNEEPCK6kG4ZyNc79jUFLuNopmsRR8,14978 django/contrib/admin/locale/os/LC_MESSAGES/django.po,sha256=yugDw7iziHto6s6ATNDK4yuG6FN6yJUvYKhrGxvKmcY,18188 django/contrib/admin/locale/os/LC_MESSAGES/djangojs.mo,sha256=0gMkAyO4Zi85e9qRuMYmxm6JV98WvyRffOKbBVJ_fLQ,3806 django/contrib/admin/locale/os/LC_MESSAGES/djangojs.po,sha256=skiTlhgUEN8uKk7ihl2z-Rxr1ZXqu5qV4wB4q9qXVq0,5208 -django/contrib/admin/locale/pa/LC_MESSAGES/django.mo,sha256=mSBJpzzGEhkKnqAhofa2vC6MPG8t--uJhF5xvRsx_1I,8556 -django/contrib/admin/locale/pa/LC_MESSAGES/django.po,sha256=tYtU9_fGdp83ea1cWiJfa6aVG-DYo_WfvXPFZB0W7Cg,15671 -django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo,sha256=Hub-6v7AfF-tWhw53abpyhnVHo76h_xBgGIhlGIcS70,1148 -django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po,sha256=7L8D4qqhq53XG83NJUZNoM8zCCScwMwzsrzzsyO4lHY,4357 -django/contrib/admin/locale/pl/LC_MESSAGES/django.mo,sha256=-rFoUXC3uUhMfP-DAAD8Qa2U7oUvm-3I_e30IZ7I-J4,18021 -django/contrib/admin/locale/pl/LC_MESSAGES/django.po,sha256=TBpwB1rPLFoxvwIi8G0vPmVUZG6Q6DvTiXfT8tVK-cg,19824 +django/contrib/admin/locale/pa/LC_MESSAGES/django.mo,sha256=n31qIjOVaJRpib4VU4EHZRua3tBnBM6t_ukH9Aj37GM,10185 +django/contrib/admin/locale/pa/LC_MESSAGES/django.po,sha256=MR6ZOTypay-qCvafn0J0rZF06rOsWz771CLDD1qvISE,16446 +django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo,sha256=vdEMaVBuJtK1bnECgbqd_dS06PcmN7cgdv0hKGH5UKA,1207 +django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po,sha256=xU8tchSEH3MCLFSu4-71oVCR8pliKmILqFevM13IQ5M,3717 +django/contrib/admin/locale/pl/LC_MESSAGES/django.mo,sha256=hw_xkSdgLd1AjnjzMUqX_dFR43caBerEBLEuTRL55ZU,17544 +django/contrib/admin/locale/pl/LC_MESSAGES/django.po,sha256=FO8Cguc9_dN0PtlTOKkRnasRnbHh89eFHHw1PH3IcQ4,19371 django/contrib/admin/locale/pl/LC_MESSAGES/djangojs.mo,sha256=6Oi_eVA7sYkM7xHROBdaSnfSOBBGijyW4_EA_z63Bfk,5702 django/contrib/admin/locale/pl/LC_MESSAGES/djangojs.po,sha256=xAEEIhbbqbEHDZa9QCAH7ccgFdO4vMRcSZLFrhwF778,6662 django/contrib/admin/locale/pt/LC_MESSAGES/django.mo,sha256=MTFRTfUKot-0r-h7qtggPe8l_q0JPAzVF9GzdtB9600,16912 @@ -869,10 +863,10 @@ django/contrib/admin/locale/ru/LC_MESSAGES/django.mo,sha256=QJ6L9257dATWvsiBLc9Q django/contrib/admin/locale/ru/LC_MESSAGES/django.po,sha256=GFDQeIY3pDT7CbKCttBkz81AzUE1ztaUUCLd62Il_vg,23779 django/contrib/admin/locale/ru/LC_MESSAGES/djangojs.mo,sha256=RiHcf9X0qZaOjCeaJKnyNpoEV52AQ4NRe3ANgAyQ5u8,7149 django/contrib/admin/locale/ru/LC_MESSAGES/djangojs.po,sha256=RmQxQ4zn3rJgeHYvVBcQ_svbkBklIhExAw4v8jTtYhI,8161 -django/contrib/admin/locale/sk/LC_MESSAGES/django.mo,sha256=hSHmImczSCOq8Fq1zVyZD5Sn5bhqUGBHiqM7WFMIMnw,17090 -django/contrib/admin/locale/sk/LC_MESSAGES/django.po,sha256=u4mxos-LzwOoZ0KqzYlynCFGagw9y2kQhx9nHE8svJg,18791 -django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo,sha256=-9dSuiVIPqZDSkF5arXISKP3TXbHtEveZO3vXy5ZotQ,5291 -django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po,sha256=wHjVgHIHxubOaeAuf8nBmj1vlXcPeWTGf1xMrhdVL2E,6083 +django/contrib/admin/locale/sk/LC_MESSAGES/django.mo,sha256=PU6IoeAF8JFMWKO-_n_wzzW5rLi50OM6tTO1mWfDpCY,13416 +django/contrib/admin/locale/sk/LC_MESSAGES/django.po,sha256=WkgdfaJT94lZ0FAy2RtPwjFLddOJ-4hidirMi5VIGVU,16892 +django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo,sha256=0FifzbnJmubmNNUsePBcbM2MwExXmtnt699xtY2_uzo,4677 +django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po,sha256=F9lWj_7Ir6-VBYosrtbQnkxHR_tOVFO1V3VUnvfWNeI,5382 django/contrib/admin/locale/sl/LC_MESSAGES/django.mo,sha256=iqcg1DYwwDVacRAKJ3QR4fTmKQhRGXU4WkwYco9ASaA,16136 django/contrib/admin/locale/sl/LC_MESSAGES/django.po,sha256=VeIJDh1PojyUy-4AdPcVezbQ-XVWqp04vFE_u3KU2tU,17508 django/contrib/admin/locale/sl/LC_MESSAGES/djangojs.mo,sha256=0jqGv5lgcfyxh9pdnB0Nt7e0bF2G0nO-iVWJjKwyZqI,4724 @@ -925,50 +919,50 @@ django/contrib/admin/locale/udm/LC_MESSAGES/django.mo,sha256=2Q_lfocM7OEjFKebqNR django/contrib/admin/locale/udm/LC_MESSAGES/django.po,sha256=L4TgEk2Fm2mtKqhZroE6k_gfz1VC-_dXe39CiJvaOPE,10496 django/contrib/admin/locale/udm/LC_MESSAGES/djangojs.mo,sha256=CNmoKj9Uc0qEInnV5t0Nt4ZnKSZCRdIG5fyfSsqwky4,462 django/contrib/admin/locale/udm/LC_MESSAGES/djangojs.po,sha256=ZLYr0yHdMYAl7Z7ipNSNjRFIMNYmzIjT7PsKNMT6XVk,2811 -django/contrib/admin/locale/uk/LC_MESSAGES/django.mo,sha256=KDkQFp-PXtWWsO5Yt5_1nvOlykXFiOJ9SolC5jxzfLw,15623 -django/contrib/admin/locale/uk/LC_MESSAGES/django.po,sha256=aI4WE1idRp6zIXJDrRsbA_yj9OJY2oodXd198x7CBK8,20011 -django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo,sha256=_YwTcBttv3DZNYkBq4Rsl6oq30o8nDvUHPI5Yx0GaA4,5787 -django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po,sha256=4lYvm_LDX5xha4Qj1dXE5tGs4BjGPUgjigvG2n6y1S4,6993 +django/contrib/admin/locale/uk/LC_MESSAGES/django.mo,sha256=Wc1E8kLHTeu0GRg1vkj_kataySFcnrVk_oCLYMUpa6M,20988 +django/contrib/admin/locale/uk/LC_MESSAGES/django.po,sha256=n7NqZajp0dDWD9r5o1Aot8pQski1gtp6eZziqHg0gEU,22827 +django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo,sha256=YL-bL4CeoOsvcXKY30FsakS6A8kG0egbvDf2yYdFfU8,5930 +django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po,sha256=lKHsuFkzp8_evIKm8mVyZKIf99EIo8BsLYkIiyN29UY,6654 django/contrib/admin/locale/ur/LC_MESSAGES/django.mo,sha256=HvyjnSeLhUf1JVDy759V_TI7ygZfLaMhLnoCBJxhH_s,13106 django/contrib/admin/locale/ur/LC_MESSAGES/django.po,sha256=BFxxLbHs-UZWEmbvtWJNA7xeuvO9wDc32H2ysKZQvF4,17531 django/contrib/admin/locale/ur/LC_MESSAGES/djangojs.mo,sha256=eYN9Q9KKTV2W0UuqRc-gg7y42yFAvJP8avMeZM-W7mw,2678 django/contrib/admin/locale/ur/LC_MESSAGES/djangojs.po,sha256=Nj-6L6axLrqA0RHUQbidNAT33sXYfVdGcX4egVua-Pk,4646 -django/contrib/admin/locale/uz/LC_MESSAGES/django.mo,sha256=bWJujZSbu9Q4u2hcVJAkHDQCjx8Uo_Bj5gcU3CbkeLw,4610 -django/contrib/admin/locale/uz/LC_MESSAGES/django.po,sha256=3fxRPvC5_1md4LrntCTLUXVINdrHxgHOav04xabwYUg,13107 -django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo,sha256=LpuFvNKqNRCCiV5VyRnJoZ8gY3Xieb05YV9KakNU7o8,3783 -django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po,sha256=joswozR3I1ijRapf50FZMzQQhI_aU2XiiSTLeSxkL64,5235 -django/contrib/admin/locale/vi/LC_MESSAGES/django.mo,sha256=coCDRhju7xVvdSaounXO5cMqCmLWICZPJth6JI3Si2c,18077 -django/contrib/admin/locale/vi/LC_MESSAGES/django.po,sha256=Q1etVmaAb1f79f4uVjbNjPkn-_3m2Spz1buNAV3y9lk,19543 -django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo,sha256=45E-fCQkq-BRLzRzsGkw1-AvWlvjL1rdsRFqfsvAq98,5302 -django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po,sha256=k87QvFnt8psnwMXXrFO6TyH6xCyXIDd_rlnWDfl2FAA,5958 -django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=dS4gJsC_O-Wx46PGYYs1e_W3AbB7EBRowEMl1P03vz4,16014 -django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po,sha256=bTyioHpGSUuekG1nOWEd7bsGHzoIv2beoiQilrYX2W4,17985 +django/contrib/admin/locale/uz/LC_MESSAGES/django.mo,sha256=EY1JuM2_Ulx2vZzWZPSmaFpPO-v9Npr6SvmUQHKvOns,3984 +django/contrib/admin/locale/uz/LC_MESSAGES/django.po,sha256=IGSgnKiE_DImS0LjrUhPXptg1-cNLRCQ_KQ__QrebNs,12803 +django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo,sha256=LhMWp7foVSN65gP4RqFGzkLlSaEfqVQ8kW16X-5kJVs,4517 +django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po,sha256=-YpHNtdwmKeavDSVZZMUsNQ9MirfhNS_Kzox72FatS4,4950 +django/contrib/admin/locale/vi/LC_MESSAGES/django.mo,sha256=nkSrBQaktbMGWr8IMNoPoOVQBAIR1GJF13BvKLu2CeM,14860 +django/contrib/admin/locale/vi/LC_MESSAGES/django.po,sha256=FxcEsnT3-FvPXjnHp9y51jFPILUgSx27egwtwU_wbS0,17847 +django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo,sha256=M_wqHg1NO-I7xfY-mMZ29BqUAqGzlizgJ3_DIGBWOUc,3733 +django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po,sha256=d3YtQhNuCqtfMO3u5-6zoNhhGBNYkoUhTrxz7I3PRkQ,5018 +django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=54flhrIfCICEolonPNRcP1Bfa6Zb2BUKREmP7TQwq7c,15844 +django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po,sha256=-VcDFuL6GQn6SBktuCrdBIDnboFzwVnfxp_X3GZmtT0,17811 django/contrib/admin/locale/zh_Hans/LC_MESSAGES/djangojs.mo,sha256=WbSCAiqttOXlVFh64D4gLTZIX5C_JYAQBCD3TrE842E,4831 django/contrib/admin/locale/zh_Hans/LC_MESSAGES/djangojs.po,sha256=cQKCmWwRnAzL9RHKWENfT_4j-Pu-7xF_ZtQP5i-oOew,5752 django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=kEKX-cQPRFCNkiqNs1BnyzEvJQF-EzA814ASnYPFMsw,15152 django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django.po,sha256=iH3w7Xt_MelkZefKi8F0yAWN6QGdQCJBz8VaFY4maUg,16531 django/contrib/admin/locale/zh_Hant/LC_MESSAGES/djangojs.mo,sha256=yFwS8aTJUAG5lN4tYLCxx-FLfTsiOxXrCEhlIA-9vcs,4230 django/contrib/admin/locale/zh_Hant/LC_MESSAGES/djangojs.po,sha256=C4Yk5yuYcmaovVs_CS8YFYY2iS4RGi0oNaUpTm7akeU,4724 -django/contrib/admin/migrations/0001_initial.py,sha256=zFK34hmWHrMGFVyooWug9TdVRPzbq9YtlpzMUa_-wmY,2508 -django/contrib/admin/migrations/0002_logentry_remove_auto_add.py,sha256=Fjda6T9ZALOk0ApouxbPe5Ph4RCGZigpgYEZipqFtT4,554 -django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py,sha256=OYurX3-py7fLmfuwyzhED9efg74mAut92S-NEqgBINc,539 +django/contrib/admin/migrations/0001_initial.py,sha256=9EuqU1zlIQtP_U2z1orVgxGvIhZ57df9S3GhpDhNWgM,1892 +django/contrib/admin/migrations/0002_logentry_remove_auto_add.py,sha256=_7XFWubtQ7NG0eQ02MqtxXQmjBmYc6Od5rwcAiT1aCs,554 +django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py,sha256=UCS9mPrkhZ5YL_9RMSrgA7uWDTzvLzqSLq_LSXVXimM,539 django/contrib/admin/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/admin/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/admin/migrations/__pycache__/0002_logentry_remove_auto_add.cpython-39.pyc,, django/contrib/admin/migrations/__pycache__/0003_logentry_add_action_flag_choices.cpython-39.pyc,, django/contrib/admin/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/admin/models.py,sha256=2eTsW0WRQbXV1GeOPLX76RMFv6GQqIGuZs10GFT7V48,6501 -django/contrib/admin/options.py,sha256=Wfo1NJ43awhoAGxGURhVWyFVSucn6JFd01KhWM5VmOk,97409 -django/contrib/admin/sites.py,sha256=ynRgsMxZBVflmalSQTOLqGTw1zZeYi-x-NK3iWwwrC0,22588 +django/contrib/admin/models.py,sha256=qqwq3V_KqV4_WJIYqKjIQnVxZZnIPzyHBDhnMg101Ho,5672 +django/contrib/admin/options.py,sha256=Ouy0052pCOkVeukOTd9yNCPac8nq6meRgAiF2MjvNGs,92875 +django/contrib/admin/sites.py,sha256=XeDxjtCJFLZU_CrcWr9meOtd0EbREWlgXAFxMjVI_H8,21926 django/contrib/admin/static/admin/css/autocomplete.css,sha256=6-fcQdqClpGf8EpH1NxgS8YL-diGXc8CFq3Sw2I9K8k,9114 django/contrib/admin/static/admin/css/base.css,sha256=uIqqrMcVS8cUSwazn-SVHKwzrZsB3k-Hhw4MOMBs74c,19513 -django/contrib/admin/static/admin/css/changelists.css,sha256=OCIbdBFGRaKdigNGwvfpBqC6zvSMO426Fa8CfUMVwuQ,6932 +django/contrib/admin/static/admin/css/changelists.css,sha256=iu5vcArMuGfTXzmilFiXTZ-YtdYAQBv2gFG2B1FDnPQ,6874 django/contrib/admin/static/admin/css/dashboard.css,sha256=i2OcDTa1R_bO6aBTZ66-aRlTXl0l4sjeHfasUrfzjd0,380 django/contrib/admin/static/admin/css/fonts.css,sha256=SnBl3KjeUZqRmZw3F0iNm1YpqFhjrNC_fNN0H2TkuYc,423 -django/contrib/admin/static/admin/css/forms.css,sha256=kz4EvcKQHygihbK8ZDZHSpMFHlJwT6slnJuI1PCcByw,8878 -django/contrib/admin/static/admin/css/login.css,sha256=frvfBpUydI9A0hoe_YH04sVn7q6WeNAAFVYO5YlHXEQ,954 -django/contrib/admin/static/admin/css/nav_sidebar.css,sha256=RzKShcJR6SA6MtgDJUS950GcbwFitBXLoYQgTwyLBm8,2616 -django/contrib/admin/static/admin/css/responsive.css,sha256=EYQpfNTd8XXZS7-RxmT0VClzcAgyL84-osPR6HNRLYs,18575 +django/contrib/admin/static/admin/css/forms.css,sha256=9cjBZtfQuzda7S-X_8tpbSFo2xGpXI_ZIPD2V_g8QaM,8804 +django/contrib/admin/static/admin/css/login.css,sha256=grNYganuogydcA6Z4E-wNkB8_n4v7E70ILY0N8TYhNo,939 +django/contrib/admin/static/admin/css/nav_sidebar.css,sha256=_Bck_vuBIoRDeNWj7LHY3bNhQR4nTcdDiSphJYV64Q4,2271 +django/contrib/admin/static/admin/css/responsive.css,sha256=VqiYdkqye_cMzg6ZNfcuDQIdx_AKvolWoHdasXmmpGw,18545 django/contrib/admin/static/admin/css/responsive_rtl.css,sha256=iM8FIfXLuXgurjYK0JwboVuilUg1hnaZw7wa3hx8aI0,1741 django/contrib/admin/static/admin/css/rtl.css,sha256=1omOH5YP-blvLuFKw1fFtPiMaKlC7UDmT2T2JvM7D4o,3234 django/contrib/admin/static/admin/css/vendor/select2/LICENSE-SELECT2.md,sha256=TuDLxRNwr941hlKg-XeXIFNyntV4tqQvXioDfRFPCzk,1124 @@ -1004,25 +998,25 @@ django/contrib/admin/static/admin/img/tooltag-add.svg,sha256=fTZCouGMJC6Qq2xlqw_ django/contrib/admin/static/admin/img/tooltag-arrowright.svg,sha256=GIAqy_4Oor9cDMNC2fSaEGh-3gqScvqREaULnix3wHc,280 django/contrib/admin/static/admin/js/SelectBox.js,sha256=FLFCFiaO4KziwueL83Un_WCR0-Lf66PhVqpvnbat50s,4360 django/contrib/admin/static/admin/js/SelectFilter2.js,sha256=Nkgyinav9IBHIkJf8zCfAwArDZnY2Jbji2847SByUoU,12350 -django/contrib/admin/static/admin/js/actions.js,sha256=90nO6o7754a2w8bNZOrS7EoEoh_MZEnIOJzJji1zTl8,7872 +django/contrib/admin/static/admin/js/actions.js,sha256=alN6-Bf1Jx3qXHvzwG29yZ5Q-SObo9Gu_5Dk_7nQ--Y,7664 django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js,sha256=7tcIrJeL0C8WtUHCohivb_2n9i1XnbUi1-3a4H_5DNc,19634 django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js,sha256=pZamCGsBc4mp14Fbzyaq4agdOhktSby3B9eZXDsa494,5984 -django/contrib/admin/static/admin/js/autocomplete.js,sha256=tzMf-zIpdciVp0jRNP0kv1KcOVMYyeqwdqCULVHiSJ8,1121 +django/contrib/admin/static/admin/js/autocomplete.js,sha256=5XwFfi-ziyvI1FTFi_-lUu8Dur_TMNAL6La8g2WOTyA,1320 django/contrib/admin/static/admin/js/calendar.js,sha256=vsYjQ4Nv6LPpqMVMhko8mnsv6U5EXkk5hOHhmkC5m7g,8466 django/contrib/admin/static/admin/js/cancel.js,sha256=UEZdvvWu5s4ZH16lFfxa8UPgWXJ3i8VseK5Lcw2Kreg,884 django/contrib/admin/static/admin/js/change_form.js,sha256=zOTeORCq1i9XXV_saSBBDOXbou5UtZvxYFpVPqxQ02Q,606 django/contrib/admin/static/admin/js/collapse.js,sha256=UONBUueHwsm5SMlG0Ufp4mlqdgu7UGimU6psKzpxbuE,1803 -django/contrib/admin/static/admin/js/core.js,sha256=AVLCrqYJOnCuLJLo0jqe0pyEKxA_4tzJ_rMcDQE1szw,5698 -django/contrib/admin/static/admin/js/inlines.js,sha256=sPIdb715z-NYiXhjR7q6PmyU0aKNtvpFCyA88HExrAY,14969 +django/contrib/admin/static/admin/js/core.js,sha256=BhyKmeEBYz6lwUZ7q3J3O7f4spPsC-uuhADEYRPOOzs,5698 +django/contrib/admin/static/admin/js/inlines.js,sha256=dSEFix4uxQPiv4U8WDt3JTWuczG4yc9kCdemKxt4V6Q,15225 django/contrib/admin/static/admin/js/jquery.init.js,sha256=uM_Kf7EOBMipcCmuQHbyubQkycleSWDCS8-c3WevFW0,347 -django/contrib/admin/static/admin/js/nav_sidebar.js,sha256=WSQQdfs4k0spbO--3gynsqO_XbZ6tQ76Fbwqqrq0pu4,3401 +django/contrib/admin/static/admin/js/nav_sidebar.js,sha256=Ufbx1cSAoDA8ovlBg6VPSdDArY_-fRzt_YnQ-snuSHk,1360 django/contrib/admin/static/admin/js/popup_response.js,sha256=H4ppG14jfrxB1XF5xZp5SS8PapYuYou5H7uwYjHd7eI,551 django/contrib/admin/static/admin/js/prepopulate.js,sha256=UYkWrHNK1-OWp1a5IWZdg0udfo_dcR-jKSn5AlxxqgU,1531 django/contrib/admin/static/admin/js/prepopulate_init.js,sha256=JdhYQLmheJU2wK3xAelyDN5VVesDXT9XU_xwRnKhlKA,492 django/contrib/admin/static/admin/js/urlify.js,sha256=ksu4cDd9JpFsN5cLT8BpOtg0JkkrIlWR1yr3nLlWXbI,7902 -django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt,sha256=1Nuevm8p9RaOrEWtcT8FViOsXQ3NW6ktoj1lCuASAg0,1097 -django/contrib/admin/static/admin/js/vendor/jquery/jquery.js,sha256=H-K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk,288580 -django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js,sha256=_xUj-3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej_m4,89501 +django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt,sha256=H_YDEY79sxN5lWfLSkCFlJVDhPQIQ8pvKcWW9bH4kH0,1095 +django/contrib/admin/static/admin/js/vendor/jquery/jquery.js,sha256=QWo7LDvxbWT2tbbQ97B53yJnYU3WhH_C8ycbRAkjPDc,287630 +django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js,sha256=9_aliU8dGd2tb6OSsuzixeV4y_faTqgFtohetphbbj0,89476 django/contrib/admin/static/admin/js/vendor/select2/LICENSE.md,sha256=TuDLxRNwr941hlKg-XeXIFNyntV4tqQvXioDfRFPCzk,1124 django/contrib/admin/static/admin/js/vendor/select2/i18n/af.js,sha256=IpI3uo19fo77jMtN5R3peoP0OriN-nQfPY2J4fufd8g,866 django/contrib/admin/static/admin/js/vendor/select2/i18n/ar.js,sha256=zxQ3peSnbVIfrH1Ndjx4DrHDsmbpqu6mfeylVWFM5mY,905 @@ -1092,33 +1086,33 @@ django/contrib/admin/templates/admin/404.html,sha256=zyawWu1I9IxDGBRsks6-DgtLUGD django/contrib/admin/templates/admin/500.html,sha256=rZNmFXr9POnc9TdZwD06qkY8h2W5K05vCyssrIzbZGE,551 django/contrib/admin/templates/admin/actions.html,sha256=pTlhPi66D3Lrm2RQdAWHqUVdyJECbqx1Oj86Fkv-fPk,1245 django/contrib/admin/templates/admin/app_index.html,sha256=X-ISFsSrON8osoS93ywjM11MLGhrcx-U0o6tJfpWRqY,389 -django/contrib/admin/templates/admin/app_list.html,sha256=ihZHIZLWNwtvmeDnsdXAVEo_mHNiM6X4CHA7y0I9YdA,1716 +django/contrib/admin/templates/admin/app_list.html,sha256=Zg5jM2ehz66QsuxYIghQ0OyqDjhDMnvLoNeduulP7Ng,1686 django/contrib/admin/templates/admin/auth/user/add_form.html,sha256=5DL3UbNWW2rTvWrpMsxy5XcVNT6_uYv8DjDZZksiVKQ,320 -django/contrib/admin/templates/admin/auth/user/change_password.html,sha256=T_iluw6yjMbD0kQHXLxyUQR8OhReN1WUis3A1rBlGzk,2274 -django/contrib/admin/templates/admin/base.html,sha256=MRUYs02WD0PoUJntbyBJ1bBv0wKsaUW51Tclb-NKSsU,4359 +django/contrib/admin/templates/admin/auth/user/change_password.html,sha256=RdRA3izwkR7q3r3uiZWMtRtCAfCCNMJLVufCJ5ylrTI,2262 +django/contrib/admin/templates/admin/base.html,sha256=8x0eJfHMDWy0pykkShldGLmt6B_F5fd0otJ-60sDRi8,4341 django/contrib/admin/templates/admin/base_site.html,sha256=3ckWrcAdd7Pw1hk6Zwyknab_Qb-rteV9-mXhMnfo6VI,361 django/contrib/admin/templates/admin/change_form.html,sha256=f58vbrT4Wv_nzYtV7ohffAOEFw8y91mnaGlemtsOGa8,3051 django/contrib/admin/templates/admin/change_form_object_tools.html,sha256=C0l0BJF2HuSjIvtY-Yr-ByZ9dePFRrTc-MR-OVJD-AI,403 django/contrib/admin/templates/admin/change_list.html,sha256=FSuAcw7c_Gb4F2SP8-Wt6oAsFZYgCHnlgQMzL9ux0nk,3258 django/contrib/admin/templates/admin/change_list_object_tools.html,sha256=-AX0bYTxDsdLtEpAEK3RFpY89tdvVChMAWPYBLqPn48,378 -django/contrib/admin/templates/admin/change_list_results.html,sha256=_beAjUTnFJTVIagZuVcf7XNz_fMseMaNyk1fRtQ-AME,1543 +django/contrib/admin/templates/admin/change_list_results.html,sha256=qbyav3RbkwkvN0OCT1XIKQqgsSNUsqo4Kuxi-yVQTPU,1550 django/contrib/admin/templates/admin/date_hierarchy.html,sha256=I9Nj9WJb3JM_9ZBHrg4xIFku_a59U-KoqO5yuSaqVJQ,518 django/contrib/admin/templates/admin/delete_confirmation.html,sha256=GfcMpSIo6Xy4QWX1_oNYilY7c1C8FKSbGWiWfw61VlY,2426 django/contrib/admin/templates/admin/delete_selected_confirmation.html,sha256=i2sUDTPuSlJqOh_JMKx5VsxOpZC9W5zD94R2XpiNPBk,2341 -django/contrib/admin/templates/admin/edit_inline/stacked.html,sha256=3ebT2hvOWtiv4tN4LCWLdgFy7H5SYNZOGd2WHuHcyrY,2554 -django/contrib/admin/templates/admin/edit_inline/tabular.html,sha256=r3nQ-dWGs2tX4mM7_68YNt6aLoYV-ciiGNz5-R5Zc5Q,4060 +django/contrib/admin/templates/admin/edit_inline/stacked.html,sha256=pT9vuDv0vbgsaQlU6tDZAJuY08L-c-5_vPm0w5EskA0,2561 +django/contrib/admin/templates/admin/edit_inline/tabular.html,sha256=w3SBaL5OTZ7I2zwe_ZZ4h6nR_MBhge3iObnzPx8Md7k,4485 django/contrib/admin/templates/admin/filter.html,sha256=V1sWCmJMSvBC_GzTtJkNWn-FfdzPpcBySERTVH5i8HY,338 django/contrib/admin/templates/admin/includes/fieldset.html,sha256=DgcBbVUfkho33IMZGEg42Xr9P5y3ZAefFzqkxf74v1Q,1787 django/contrib/admin/templates/admin/includes/object_delete_summary.html,sha256=OC7VhKQiczmi01Gt_3jyemelerSNrGyDiWghUK6xKEI,192 django/contrib/admin/templates/admin/index.html,sha256=IJV2pH-Xi8rYmR1TzckraJ3A2fSjzejV6Dpk-oPqCEA,1861 django/contrib/admin/templates/admin/invalid_setup.html,sha256=F5FS3o7S3l4idPrX29OKlM_azYmCRKzFdYjV_jpTqhE,447 django/contrib/admin/templates/admin/login.html,sha256=yhk3veXIvM_efQLL4NcjfYWxZKqqAct3hPS6mYaWBJ0,1912 -django/contrib/admin/templates/admin/nav_sidebar.html,sha256=CRALc3xH43rRGk3hkhV_NMsrXGEXQ-XrLvyd3yF8oAs,447 +django/contrib/admin/templates/admin/nav_sidebar.html,sha256=wZjmLWbRHWrCaBH0ZyMhWeYlAeTIS3PGWNmcBmWINVw,276 django/contrib/admin/templates/admin/object_history.html,sha256=hr_yKkciaPU-ljl3XM_87c2q0076YhAQXHy7buayLIc,1472 django/contrib/admin/templates/admin/pagination.html,sha256=OBvC2HWFaH3wIuk6gzKSyCli51NTaW8vnJFyBOpNo_8,549 django/contrib/admin/templates/admin/popup_response.html,sha256=Lj8dfQrg1XWdA-52uNtWJ9hwBI98Wt2spSMkO4YBjEk,327 -django/contrib/admin/templates/admin/prepopulated_fields_js.html,sha256=PShGpqQWBBVwQ86r7b-SimwJS0mxNiz8AObaiDOSfvY,209 -django/contrib/admin/templates/admin/search_form.html,sha256=CxonKc0fIOccSuvpIAeMOYGGAf1MJjlRS7jOosPQdkI,1166 +django/contrib/admin/templates/admin/prepopulated_fields_js.html,sha256=vVRsVT_TxUddTdKI7ADfIbwg5Mog4XVQwoBWlivEjRc,214 +django/contrib/admin/templates/admin/search_form.html,sha256=Ea8OEGFRyiTpkqdeWGyQ0mVWK0tuHXVndnO77xmjBYg,1044 django/contrib/admin/templates/admin/submit_line.html,sha256=DgxKlyJ2b8o5NVWzE47yt_2X-xnbobKjdIVK2Y7jXBU,1052 django/contrib/admin/templates/admin/widgets/clearable_file_input.html,sha256=NWjHNdkTZMAxU5HWXrOQCReeAO5A6PXBDRWO8S9gSGI,618 django/contrib/admin/templates/admin/widgets/foreign_key_raw_id.html,sha256=Sp46OiJ5ViQMXfSaug4UkqIiXbiGdlQ8GNEhA8kVLUo,341 @@ -1127,7 +1121,7 @@ django/contrib/admin/templates/admin/widgets/radio.html,sha256=-ob26uqmvrEUMZPQq django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html,sha256=LN8EMnad8qnyi2HIbOes3DkdbGkEsX4R4szGf_KByGM,1490 django/contrib/admin/templates/admin/widgets/split_datetime.html,sha256=BQ9XNv3eqtvNqZZGW38VBM2Nan-5PBxokbo2Fm_wwCQ,238 django/contrib/admin/templates/admin/widgets/url.html,sha256=Tf7PwdoKAiimfmDTVbWzRVxxUeyfhF0OlsuiOZ1tHgI,218 -django/contrib/admin/templates/registration/logged_out.html,sha256=PuviqzJh7C6SZJl9yKZXDcxxqXNCTDVfRuEpqvwJiPE,425 +django/contrib/admin/templates/registration/logged_out.html,sha256=CUO9snYMIOwRkd0j-Uk75xNyio7s_YezY9hnXZFt4QU,425 django/contrib/admin/templates/registration/password_change_done.html,sha256=Zmw7eNYw8wa8tem8xVBG5C2Oavcz8Fxsst4xfxzqOdo,592 django/contrib/admin/templates/registration/password_change_form.html,sha256=-j_Khtxde2DRGu5G9xXZ3PGPMqDfT9U-soEXY6h0MJM,1980 django/contrib/admin/templates/registration/password_reset_complete.html,sha256=_fc5bDeYBaI5fCUJZ0ZFpmOE2CUqlbk3npGk63uc_Ks,417 @@ -1142,22 +1136,22 @@ django/contrib/admin/templatetags/__pycache__/admin_modify.cpython-39.pyc,, django/contrib/admin/templatetags/__pycache__/admin_urls.cpython-39.pyc,, django/contrib/admin/templatetags/__pycache__/base.cpython-39.pyc,, django/contrib/admin/templatetags/__pycache__/log.cpython-39.pyc,, -django/contrib/admin/templatetags/admin_list.py,sha256=oKnqZgQrUlMIeSDeEKKFVtLyuTzszpFgMfPTV1M2Ggk,18492 -django/contrib/admin/templatetags/admin_modify.py,sha256=3t6rainlP3KHTb0OK1Pkr0TlB0PhZ8IC7O8HnRzijMw,4981 -django/contrib/admin/templatetags/admin_urls.py,sha256=GaDOb10w0kPIPYNvlwEaAIqhKvLKpHQDqYBVpOQhXQU,1926 -django/contrib/admin/templatetags/base.py,sha256=SyI_Dwh5OvtdP0DaPNehpvjgZknlJmrucck5tF3eUHY,1474 -django/contrib/admin/templatetags/log.py,sha256=3MT5WKsac8S5H1J2kkM-gasYc9faF91b95TEt3y8E-k,2167 -django/contrib/admin/tests.py,sha256=GZPtUy9fLH7oladvDtZRpUvntv8bq8lmBOhnZs6HI_k,7705 -django/contrib/admin/utils.py,sha256=l8zacnNDFUcABEyKlcShlzfRd-qP4ue5TYVPAT20m88,20151 +django/contrib/admin/templatetags/admin_list.py,sha256=utue6tYsek6X2KRfBLUlBHRGvAh8rlGPiWlFzvY86Ns,17397 +django/contrib/admin/templatetags/admin_modify.py,sha256=KFbvwVixlzFuXEnZx1MVu77_7ZhV9B94JPR8MnHiVw8,4399 +django/contrib/admin/templatetags/admin_urls.py,sha256=b_RxDLR7yLBTMe-_ylzO-m0R3ITq3ZP_pnddRyM_Nos,1791 +django/contrib/admin/templatetags/base.py,sha256=mCcrwBWbgutR3tpaduRKNG3ShTu5Yl0Tjba5O5Rp5hU,1318 +django/contrib/admin/templatetags/log.py,sha256=mxV6mvfVJo0qRCelkjRBNWfrurLABhZvGQlcp5Bn4IU,2079 +django/contrib/admin/tests.py,sha256=O5yjYbAYCEiextaNL-amFfZkwefypnP6RN0CShtPncQ,7602 +django/contrib/admin/utils.py,sha256=IyzizG6_AVb0SniSmWqXhRS9R6jK84rXP0RkJUZJxyo,19788 django/contrib/admin/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/admin/views/__pycache__/__init__.cpython-39.pyc,, django/contrib/admin/views/__pycache__/autocomplete.cpython-39.pyc,, django/contrib/admin/views/__pycache__/decorators.cpython-39.pyc,, django/contrib/admin/views/__pycache__/main.cpython-39.pyc,, -django/contrib/admin/views/autocomplete.py,sha256=yDp5k-zICP16x-EXY_4ntPX3HewTzcPDLQWQlaHbYEs,4316 -django/contrib/admin/views/decorators.py,sha256=4ndYdYoPLhWsdutME0Lxsmcf6UFP5Z2ou3_pMjgNbw8,639 -django/contrib/admin/views/main.py,sha256=2y45kvfecNj_NEOWtFKs4BSIQkClE65Fb2Tz1PJTsFc,23813 -django/contrib/admin/widgets.py,sha256=MZsb5NtspS_m6dOLQA-GrppJsvOvtVKHWVah7jvWafU,18359 +django/contrib/admin/views/autocomplete.py,sha256=hDgWRwFPgE4k1OUWhwDNrmbkpflBrqCbdO8Kt1CSydc,3888 +django/contrib/admin/views/decorators.py,sha256=J4wYcyaFr_-xY1ANl6QF4cFhOupRvjjmBotN0FshVYg,658 +django/contrib/admin/views/main.py,sha256=T_tq6NVYdxJ6fjaE_3JfY3G_jwED8gcpkkdeuqsJYD8,23030 +django/contrib/admin/widgets.py,sha256=9cS7et45j9fPYVx2jj1oANV6wHXMkgtwdcC06S6pdUw,17388 django/contrib/admindocs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/admindocs/__pycache__/__init__.cpython-39.pyc,, django/contrib/admindocs/__pycache__/apps.cpython-39.pyc,, @@ -1165,11 +1159,11 @@ django/contrib/admindocs/__pycache__/middleware.cpython-39.pyc,, django/contrib/admindocs/__pycache__/urls.cpython-39.pyc,, django/contrib/admindocs/__pycache__/utils.cpython-39.pyc,, django/contrib/admindocs/__pycache__/views.cpython-39.pyc,, -django/contrib/admindocs/apps.py,sha256=bklhU4oaTSmPdr0QzpVeuNT6iG77QM1AgiKKZDX05t4,216 -django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo,sha256=MrncgyILquCzFENxkWfJdzauVt6m3yPnQc1sDR4bCMg,2421 -django/contrib/admindocs/locale/af/LC_MESSAGES/django.po,sha256=yHYO9ZMBSGQLiSxd9PLzzNY7GT518wb7M-JAzTjSbw8,5392 -django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo,sha256=MwAJ0TMsgRN4wrwlhlw3gYCfZK5IKDzNPuvjfJS_Eug,7440 -django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po,sha256=KSmZCjSEizBx5a6yN_u0FPqG5QoXsTV9gdJkqWC8xC8,8052 +django/contrib/admindocs/apps.py,sha256=rV3aWVevgI6o8_9WY0yQ62O5CSMRRZrVwZFt1gpfKk0,216 +django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo,sha256=RnpPLulXkAXe6s5TmlkNbHWyK5R-0nGlOv-3TOFT_JU,702 +django/contrib/admindocs/locale/af/LC_MESSAGES/django.po,sha256=18HnMLlT8NzeujAJRPHGmwkKesl9Uy8Fllt3AP_lYgw,4608 +django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo,sha256=Gt6tFwPvlcMaOYZYGgKOFJBqF-TUoEm4tr4Ff3LYjUQ,7421 +django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po,sha256=eO8WOK-lHFS0YaxjvW3M_gqwaWVrJbCpQm0MMmT_tdI,8025 django/contrib/admindocs/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=JfZf3pQPepUkAqcWj4XEKHGVg59E8U4sHI7wXxZ1F9Q,7445 django/contrib/admindocs/locale/ar_DZ/LC_MESSAGES/django.po,sha256=fQLd1eOQppL7PFnjqDYq1cEJchxzNxi5ALOldU_68XA,7942 django/contrib/admindocs/locale/ast/LC_MESSAGES/django.mo,sha256=d4u-2zZXnnueWm9CLSnt4TRWgZk2NMlrA6gaytJ2gdU,715 @@ -1178,28 +1172,28 @@ django/contrib/admindocs/locale/az/LC_MESSAGES/django.mo,sha256=yWjmqVrGit7XjELY django/contrib/admindocs/locale/az/LC_MESSAGES/django.po,sha256=wGdq-g4u8ssHHvODJB-knjZdrP6noxRW9APn_kmOz7w,4993 django/contrib/admindocs/locale/be/LC_MESSAGES/django.mo,sha256=VZl0yvgbo0jwQpf-s472jagbUj83A3twnxddQGwGW5c,8163 django/contrib/admindocs/locale/be/LC_MESSAGES/django.po,sha256=Z8ZtS_t5Tc7iy1p4TTrsKZqiMJl94f1jiTWuv1sep3A,8728 -django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo,sha256=iLkCZ9SUrxFbVuPBiPUajfPYGL928x9NryBtoiaZqss,8236 -django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po,sha256=1OlWLLvowOMsnrZ24R_yoXWuE1RpcLHSpIFpfmIB304,8983 +django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo,sha256=n9GdBZljKJBmfups8Zt82lpHgEWvonacXztOS6qbAjM,7837 +django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po,sha256=SrmOtJ6nOi3lrgEwr-s76jYzN7lZs05dbEwh9OFxFHU,8692 django/contrib/admindocs/locale/bn/LC_MESSAGES/django.mo,sha256=NOKVcE8id9G1OctSly4C5lm64CgEF8dohX-Pdyt4kCM,3794 django/contrib/admindocs/locale/bn/LC_MESSAGES/django.po,sha256=6M7LjIEjvDTjyraxz70On_TIsgqJPLW7omQ0Fz_zyfQ,6266 django/contrib/admindocs/locale/br/LC_MESSAGES/django.mo,sha256=UsPTado4ZNJM_arSMXyuBGsKN-bCHXQZdFbh0GB3dtg,1571 django/contrib/admindocs/locale/br/LC_MESSAGES/django.po,sha256=SHOxPSgozJbOkm8u5LQJ9VmL58ZSBmlxfOVw1fAGl2s,5139 django/contrib/admindocs/locale/bs/LC_MESSAGES/django.mo,sha256=clvhu0z3IF5Nt0tZ85hOt4M37pnGEWeIYumE20vLpsI,1730 django/contrib/admindocs/locale/bs/LC_MESSAGES/django.po,sha256=1-OrVWFqLpeXQFfh7JNjJtvWjVww7iB2s96dcSgLy90,5042 -django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo,sha256=nI2ctIbZVrsaMbJQGIHQCjwqJNTnH3DKxwI2dWR6G_w,6650 -django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po,sha256=hPjkw0bkoUu-yKU8XYE3ji0NG4z5cE1LGonYPJXeze4,7396 +django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo,sha256=0elCZBJul-zx5ofeQ7vu7hVYb5JEl5jo5vgSiKp2HOY,6661 +django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po,sha256=5owS4x9uNL5ZMbh38DFL9GpVZ3MzUtXEv8o7bJTDy7Q,7402 django/contrib/admindocs/locale/cs/LC_MESSAGES/django.mo,sha256=dJ-3fDenE42f6XZFc-yrfWL1pEAmSGt2j1eWAyy-5OQ,6619 django/contrib/admindocs/locale/cs/LC_MESSAGES/django.po,sha256=uU4n9PsiI96O0UpJzL-inVzB1Kx7OB_SbLkjrFLuyVA,7227 django/contrib/admindocs/locale/cy/LC_MESSAGES/django.mo,sha256=sYeCCq0CMrFWjT6rKtmFrpC09OEFpYLSI3vu9WtpVTY,5401 django/contrib/admindocs/locale/cy/LC_MESSAGES/django.po,sha256=GhdikiXtx8Aea459uifQtBjHuTlyUeiKu0_rR_mDKyg,6512 django/contrib/admindocs/locale/da/LC_MESSAGES/django.mo,sha256=vmsIZeMIVpLkSdJNS0G6alAmBBEtLDBLnOd-P3dSOAs,6446 django/contrib/admindocs/locale/da/LC_MESSAGES/django.po,sha256=bSoTGPcE7MdRfAtBybZT9jsuww2VDH9t5CssaxSs_GU,7148 -django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo,sha256=ReSz0aH1TKT6AtP13lWoONnwNM2OGo4jK9fXJlo75Hc,6567 -django/contrib/admindocs/locale/de/LC_MESSAGES/django.po,sha256=tVkDIPF_wYb_KaJ7PF9cZyBJoYu6RpznoM9JIk3RYN4,7180 +django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo,sha256=tsaEPab2JJpJRq7hYbPK9Ulh_gK9rkbMXrsadyAqK1g,6561 +django/contrib/admindocs/locale/de/LC_MESSAGES/django.po,sha256=6g8iEaTVsrXYctYRM4LUqhUSaQ65ZNvz7pPLERA98x0,7125 django/contrib/admindocs/locale/dsb/LC_MESSAGES/django.mo,sha256=K_QuInKk1HrrzQivwJcs_2lc1HreFj7_R7qQh3qMTPY,6807 django/contrib/admindocs/locale/dsb/LC_MESSAGES/django.po,sha256=flF1D0gfTScuC_RddC9njLe6RrnqnksiRxwODVA9Vqw,7332 -django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo,sha256=1x0sTZwWbGEURyRaSn4ONvTPXHwm7XemNlcun9Nm1QI,8581 -django/contrib/admindocs/locale/el/LC_MESSAGES/django.po,sha256=GebfJfW0QPzAQyBKz1Km9a3saCpAWT7d_Qe2nCBvGn4,9320 +django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo,sha256=dJy15irtJqzPFc_yHS3LTeXYmPu0-bIlyrDPfbE5pSE,8598 +django/contrib/admindocs/locale/el/LC_MESSAGES/django.po,sha256=82wcERwp7_v3l66v3GKdlT-lVGhwGs8DK0184SbV3zk,9259 django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/admindocs/locale/en/LC_MESSAGES/django.po,sha256=pEypE71l-Ude2e3XVf0tkBpGx6BSYNqBagWnSYmEbxI,10688 django/contrib/admindocs/locale/en_AU/LC_MESSAGES/django.mo,sha256=BQ54LF9Tx88m-pG_QVz_nm_vqvoy6pVJzL8urSO4l1Q,486 @@ -1218,8 +1212,8 @@ django/contrib/admindocs/locale/es_MX/LC_MESSAGES/django.mo,sha256=3hZiFFVO8J9cC django/contrib/admindocs/locale/es_MX/LC_MESSAGES/django.po,sha256=gNmx1QTbmyMxP3ftGXGWJH_sVGThiSe_VNKkd7M9jOY,5043 django/contrib/admindocs/locale/es_VE/LC_MESSAGES/django.mo,sha256=sMwJ7t5GqPF496w-PvBYUneZ9uSwmi5jP-sWulhc6BM,6663 django/contrib/admindocs/locale/es_VE/LC_MESSAGES/django.po,sha256=ZOcE0f95Q6uD9SelK6bQlKtS2c3JX9QxNYCihPdlM5o,7201 -django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo,sha256=JQHVKehV0sxNaBQRqbsN-Of22CMV70bQ9TUId3QDudY,6381 -django/contrib/admindocs/locale/et/LC_MESSAGES/django.po,sha256=qrS3cPEy16hEi1857jvqsmr9zHF9_AkkJUw4mKimg98,7096 +django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo,sha256=KwJDXghEgvQTDs7Tp2FM0EUedEtB2hvtd1D7neBFHB0,6380 +django/contrib/admindocs/locale/et/LC_MESSAGES/django.po,sha256=EDiJDtGgj7WwVhu0IlfV4HRrbHVxvElljF2Lt8GpI8Y,7062 django/contrib/admindocs/locale/eu/LC_MESSAGES/django.mo,sha256=WHgK7vGaqjO4MwjBkWz2Y3ABPXCqfnwSGelazRhOiuo,6479 django/contrib/admindocs/locale/eu/LC_MESSAGES/django.po,sha256=718XgJN7UQcHgE9ku0VyFp7Frs-cvmCTO1o-xS5kpqc,7099 django/contrib/admindocs/locale/fa/LC_MESSAGES/django.mo,sha256=Qrkrb_CHPGymnXBoBq5oeTs4W54R6nLz5hLIWH63EHM,7499 @@ -1232,12 +1226,12 @@ django/contrib/admindocs/locale/fy/LC_MESSAGES/django.mo,sha256=_xVO-FkPPoTla_R0 django/contrib/admindocs/locale/fy/LC_MESSAGES/django.po,sha256=b3CRH9bSUl_jjb9s51RlvFXp3bmsmuxTfN_MTmIIVNA,5060 django/contrib/admindocs/locale/ga/LC_MESSAGES/django.mo,sha256=PkY5sLKd7gEIE2IkuuNJXP5RmjC-D4OODRv6KCCUDX8,1940 django/contrib/admindocs/locale/ga/LC_MESSAGES/django.po,sha256=-l6VME96KR1KKNACVu7oHzlhCrnkC1PaJQyskOUqOvk,5211 -django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo,sha256=k5-Ov9BkwYHZ_IvIxQdHKVBdOUN7kWGft1l7w5Scd5o,6941 -django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po,sha256=FyvfRNkSrEZo8x1didB6nFHYD54lZfKSoAGcwJ2wLso,7478 +django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo,sha256=1cfTNUgFPK9zGj6r6y7jGGiHcW9QpCq5XAb5yvAawiU,6939 +django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po,sha256=nUKSAF7cI9pjxV4qLswYMrPWUsD__rNRtD-j-Ir8efg,7476 django/contrib/admindocs/locale/gl/LC_MESSAGES/django.mo,sha256=CYtHrSyH_Lw0YxmmmndEnMPU-cw5TMr-8NHUjz6v7JM,2265 django/contrib/admindocs/locale/gl/LC_MESSAGES/django.po,sha256=0S2CJju3EIiEp6kqJIn0Jl1IyRAg2-5ovYMOW0YRtVA,5188 -django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo,sha256=mJKr2rC_1OWQpRaRCecnz01YDEu5APFhJHqRHgGQxXA,6743 -django/contrib/admindocs/locale/he/LC_MESSAGES/django.po,sha256=sYlIetORzAXaKk7DAhr-6J0TGucV7RsOftT9Zilz6yE,7427 +django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo,sha256=g9HBtvV5UTZg3V9TE4Q-qsF5apyMeLcPIJr1494PGXg,6985 +django/contrib/admindocs/locale/he/LC_MESSAGES/django.po,sha256=GX8X7-aPN9sbgbT_paJCFNYnq00b09fZ0HuZ4Jn3hT0,7522 django/contrib/admindocs/locale/hi/LC_MESSAGES/django.mo,sha256=sZhObIxqrmFu5Y-ZOQC0JGM3ly4IVFr02yqOOOHnDag,2297 django/contrib/admindocs/locale/hi/LC_MESSAGES/django.po,sha256=X6UfEc6q0BeaxVP_C4priFt8irhh-YGOUUzNQyVnEYY,5506 django/contrib/admindocs/locale/hr/LC_MESSAGES/django.mo,sha256=fMsayjODNoCdbpBAk9GHtIUaGJGFz4sD9qYrguj-BQA,2550 @@ -1248,8 +1242,8 @@ django/contrib/admindocs/locale/hu/LC_MESSAGES/django.mo,sha256=ATEt9wE2VNQO_NMc django/contrib/admindocs/locale/hu/LC_MESSAGES/django.po,sha256=3XKQrlonyLXXpU8xeS1OLXcKmmE2hiBoMJN-QZ3k82g,7270 django/contrib/admindocs/locale/ia/LC_MESSAGES/django.mo,sha256=KklX2loobVtA6PqHOZHwF1_A9YeVGlqORinHW09iupI,1860 django/contrib/admindocs/locale/ia/LC_MESSAGES/django.po,sha256=Z7btOCeARREgdH4CIJlVob_f89r2M9j55IDtTLtgWJU,5028 -django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo,sha256=2HZrdwFeJV4Xk2HIKsxp_rDyBrmxCuRb92HtFtW8MxE,6343 -django/contrib/admindocs/locale/id/LC_MESSAGES/django.po,sha256=O01yt7iDXvEwkebUxUlk-vCrLR26ebuqI51x64uqFl4,7041 +django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo,sha256=55ze7c7MwxHf27I9Q6n9h--pczff43TWeUiMPjRw2zY,6337 +django/contrib/admindocs/locale/id/LC_MESSAGES/django.po,sha256=N7NrFJdFTpiIjKDPWMpa1FyOVpxdqZ9QChzOVbws6kE,7027 django/contrib/admindocs/locale/io/LC_MESSAGES/django.mo,sha256=5t9Vurrh6hGqKohwsZIoveGeYCsUvRBRMz9M7k9XYY8,464 django/contrib/admindocs/locale/io/LC_MESSAGES/django.po,sha256=SVZZEmaS1WbXFRlLLGg5bzUe09pXR23TeJtHUbhyl0w,5048 django/contrib/admindocs/locale/is/LC_MESSAGES/django.mo,sha256=pEr-_MJi4D-WpNyFaQe3tVKVLq_9V-a4eIF18B3Qyko,1828 @@ -1267,9 +1261,9 @@ django/contrib/admindocs/locale/kk/LC_MESSAGES/django.po,sha256=72sxLw-QDSFnsH8k django/contrib/admindocs/locale/km/LC_MESSAGES/django.mo,sha256=Fff1K0qzialXE_tLiGM_iO5kh8eAmQhPZ0h-eB9iNOU,1476 django/contrib/admindocs/locale/km/LC_MESSAGES/django.po,sha256=E_CaaYc4GqOPgPh2t7iuo0Uf4HSQQFWAoxSOCG-uEGU,4998 django/contrib/admindocs/locale/kn/LC_MESSAGES/django.mo,sha256=lisxE1zzW-Spdm7hIzXxDAfS7bM-RdrAG_mQVwz9WMU,1656 -django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po,sha256=u6JnB-mYoYWvLl-2pzKNfeNlT1s6A2I3lRi947R_0yA,5184 -django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo,sha256=nVBVLfXUlGQCeF2foSQ2kksBmR3KbweXdbD6Kyq-PrU,6563 -django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po,sha256=y2YjuXM3p0haXrGpxRtm6I84o75TQaMeT4xbHCg7zOM,7342 +django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po,sha256=fbiHUPdw_iXrOvgiIvPTJI3WPLD_T77VBfhqW6gjq1c,5178 +django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo,sha256=SZynW9hR503fzQCXSSeYvwwZChBF7ff3iHGMESh4ayA,6592 +django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po,sha256=E81VE22vrKjgxDthgxOIO3sxgTVmNf-gZMba9Qcr9yY,7352 django/contrib/admindocs/locale/ky/LC_MESSAGES/django.mo,sha256=HEJo4CLoIOWpK-MPcTqLhbNMA8Mt3totYN1YbJ_SNn4,7977 django/contrib/admindocs/locale/ky/LC_MESSAGES/django.po,sha256=VaSXjz8Qlr2EI8f12gtziN7yA7IWsaVoEzL3G6dERXs,8553 django/contrib/admindocs/locale/lb/LC_MESSAGES/django.mo,sha256=N0hKFuAdDIq5clRKZirGh4_YDLsxi1PSX3DVe_CZe4k,474 @@ -1286,8 +1280,6 @@ django/contrib/admindocs/locale/mn/LC_MESSAGES/django.mo,sha256=KqdcvSpqmjRfA8M4 django/contrib/admindocs/locale/mn/LC_MESSAGES/django.po,sha256=PGhlnzDKyAIRzaPCbNujpxSpf_JaOG66LK_NMlnZy6I,8316 django/contrib/admindocs/locale/mr/LC_MESSAGES/django.mo,sha256=LDGC7YRyVBU50W-iH0MuESunlRXrNfNjwjXRCBdfFVg,468 django/contrib/admindocs/locale/mr/LC_MESSAGES/django.po,sha256=5cUgPltXyS2Z0kIKF5ER8f5DuBhwmAINJQyfHj652d0,5052 -django/contrib/admindocs/locale/ms/LC_MESSAGES/django.mo,sha256=vgoSQlIQeFWaVfJv3YK9_0FOywWwxLhWGICKBdxcqJY,6557 -django/contrib/admindocs/locale/ms/LC_MESSAGES/django.po,sha256=Qy_NjgqwEwLGk4oaHB4Np3dVbPeCK2URdI73S73IZLE,7044 django/contrib/admindocs/locale/my/LC_MESSAGES/django.mo,sha256=AsdUmou0FjCiML3QOeXMdbHiaSt2GdGMcEKRJFonLOQ,1721 django/contrib/admindocs/locale/my/LC_MESSAGES/django.po,sha256=c75V-PprKrWzgrHbfrZOpm00U_zZRzxAUr2U_j8MF4w,5189 django/contrib/admindocs/locale/nb/LC_MESSAGES/django.mo,sha256=qlzN0-deW2xekojbHi2w6mYKeBe1Cf1nm8Z5FVrmYtA,6308 @@ -1296,8 +1288,8 @@ django/contrib/admindocs/locale/ne/LC_MESSAGES/django.mo,sha256=fWPAUZOX9qrDIxGh django/contrib/admindocs/locale/ne/LC_MESSAGES/django.po,sha256=wb8pCm141YfGSHVW84FnAvsKt5KnKvzNyzGcPr-Wots,5802 django/contrib/admindocs/locale/nl/LC_MESSAGES/django.mo,sha256=nZwZekyuJi9U8WhJHasdQ05O1Qky8kJzj3i6c4lj3rw,6463 django/contrib/admindocs/locale/nl/LC_MESSAGES/django.po,sha256=aP59hIiCQwGCKyHnoJXYJIChzYMbNFlb2IotTX4WBwU,7188 -django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo,sha256=tIOU1WrHkAfxD6JBpdakiMi6pVzzvIg0jun6gii-D08,6299 -django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po,sha256=oekYY3xjjM2sPnHv_ZXxAti1ySPF-HxLrvLLk7Izibk,6824 +django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo,sha256=Dx-A4dlDEoOKrtvis1mWfvwA2Urj-QAiKNmBy--v0oY,1662 +django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po,sha256=VAHAyol0YEaHd0TaGxaQuVUIR72QB3VUnB1ARtr-AWw,4974 django/contrib/admindocs/locale/os/LC_MESSAGES/django.mo,sha256=zSQBgSj4jSu5Km0itNgDtbkb1SbxzRvQeZ5M9sXHI8k,2044 django/contrib/admindocs/locale/os/LC_MESSAGES/django.po,sha256=hZlMmmqfbGmoiElGbJg7Fp791ZuOpRFrSu09xBXt6z4,5215 django/contrib/admindocs/locale/pa/LC_MESSAGES/django.mo,sha256=yFeO0eZIksXeDhAl3CrnkL1CF7PHz1PII2kIxGA0opQ,1275 @@ -1312,8 +1304,8 @@ django/contrib/admindocs/locale/ro/LC_MESSAGES/django.mo,sha256=9K8Sapn6sOg1wtt2 django/contrib/admindocs/locale/ro/LC_MESSAGES/django.po,sha256=b4AsPjWBYHQeThAtLP_TH4pJitwidtoPNkJ7dowUuRg,7476 django/contrib/admindocs/locale/ru/LC_MESSAGES/django.mo,sha256=9pIPv2D0rq29vrBNWZENM_SOdNpaPidxmgT20hWtBis,8434 django/contrib/admindocs/locale/ru/LC_MESSAGES/django.po,sha256=BTlxkS4C0DdfC9QJCegXwi5ejfG9pMsAdfy6UJzec3s,9175 -django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo,sha256=GtiqSwQxKsrC-HBexRMuV3qQhZa8vJeukTpeJdXxsz4,6639 -django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po,sha256=45J2eddF99_xWbWUoUgQ5NrawMYNreUWpeyXHF6KjsI,7339 +django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo,sha256=Y9vQluxcGX9liYofnZb80iwgrdLs9WneKHX4-JX4evY,6644 +django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po,sha256=X9eNfQfHj-SBIEUq5beCU3l5hpVPgv5ktn7GHT__2Qc,7337 django/contrib/admindocs/locale/sl/LC_MESSAGES/django.mo,sha256=FMg_s9ZpeRD42OsSF9bpe8pRQ7wP7-a9WWnaVliqXpU,6508 django/contrib/admindocs/locale/sl/LC_MESSAGES/django.po,sha256=JWO_WZAwBpXw-4FoB7rkWXGhi9aEVq1tH2fOC69rcgg,7105 django/contrib/admindocs/locale/sq/LC_MESSAGES/django.mo,sha256=XvNDzCc3-Hh5Pz7SHhG8zCT_3dtqGzBLkDqhim4jJpc,6551 @@ -1322,8 +1314,8 @@ django/contrib/admindocs/locale/sr/LC_MESSAGES/django.mo,sha256=PyE8DXRYELzSs4RW django/contrib/admindocs/locale/sr/LC_MESSAGES/django.po,sha256=ri7v9WHXORY-3Dl-YDKGsCFfQzH-a5y8t1vT6yziIyo,6108 django/contrib/admindocs/locale/sr_Latn/LC_MESSAGES/django.mo,sha256=au90IT43VR162L2jEsYqhRpso2dvOjpCPSCFiglokTc,1932 django/contrib/admindocs/locale/sr_Latn/LC_MESSAGES/django.po,sha256=tJ4tHLJj0tDaVZba3WIkI0kg95_jEYWTmqXD0rFb6T8,5140 -django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo,sha256=hoeSXXIKXPNwIqJiNUhFsiK5F_Jd5PzJV8o6mDiVv18,6230 -django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po,sha256=P6Ngt4ta0jvLy8MIFE5yjwM9QVYDvZj2dMbs01GevEM,7036 +django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo,sha256=FsErCRG8EAsZB7DhFxnvU_GeAv9gy5VC0gOYgV7-teA,6417 +django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po,sha256=1sPLsQ6XXpmeIvqtKTFrsYpD39tg1ijy37iaBEmsq5Y,7042 django/contrib/admindocs/locale/sw/LC_MESSAGES/django.mo,sha256=pyJfGL7UdPrJAVlCB3YimXxTjTfEkoZQWX-CSpDkcWc,1808 django/contrib/admindocs/locale/sw/LC_MESSAGES/django.po,sha256=SIywrLX1UGx4OiPxoxUYelmQ1YaY2LMa3dxynGQpHp8,4929 django/contrib/admindocs/locale/ta/LC_MESSAGES/django.mo,sha256=8SjQ9eGGyaZGhkuDoZTdtYKuqcVyEtWrJuSabvNRUVM,1675 @@ -1350,7 +1342,7 @@ django/contrib/admindocs/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=ngPlxN85wGO django/contrib/admindocs/locale/zh_Hans/LC_MESSAGES/django.po,sha256=TNdJGJCAi0OijBN6w23SwKieZqNqkgNt2qdlPfY-r20,6823 django/contrib/admindocs/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=7c2QywaTzF_GX8T2PUknQ_PN5s0Cx37_cO-walIg8mk,4725 django/contrib/admindocs/locale/zh_Hant/LC_MESSAGES/django.po,sha256=uX-3zu8RQdntg__qYBweKtcuBgLsXPUYApf4bQx9eSU,6153 -django/contrib/admindocs/middleware.py,sha256=owqLbigBtxKmhPQmz767KOAkN3nKRIJrwZAUuHRIAQM,1329 +django/contrib/admindocs/middleware.py,sha256=Ua-a8Ylsr47RU8BWnuHsOzZ3g0ZnGdf6bj3H9xAR7_4,1225 django/contrib/admindocs/templates/admin_doc/bookmarklets.html,sha256=PnfojSYh6lJA03UPjWbvxci64CNPQmrhJhycdyqlT5U,1281 django/contrib/admindocs/templates/admin_doc/index.html,sha256=o710lPn-AHBJfKSUS6x1eUjAOZYRO9dbnuq_Cg7HEiY,1369 django/contrib/admindocs/templates/admin_doc/missing_docutils.html,sha256=f8CcVOHCgUmbG_V56rVLV1tttQYPdkcxAHY_IWiMPK4,786 @@ -1361,10 +1353,10 @@ django/contrib/admindocs/templates/admin_doc/template_filter_index.html,sha256=U django/contrib/admindocs/templates/admin_doc/template_tag_index.html,sha256=S4U-G05yi1YIlFEv-HG20bDiq4rhdiZCgebhVBzNzdY,1731 django/contrib/admindocs/templates/admin_doc/view_detail.html,sha256=u2rjpM0cLlHxSY-Na7wxqnv76zaGf0P1FgdnHl9XqdQ,928 django/contrib/admindocs/templates/admin_doc/view_index.html,sha256=ZLfmxMkVlPYETRFnjLmU3bagve4ZvY1Xzsya1Lntgkw,1734 -django/contrib/admindocs/urls.py,sha256=zUZG14KLznM6CVtoxnCsJEa7TRwKRN44XLNAp9EgUy8,1310 -django/contrib/admindocs/utils.py,sha256=Gnbj3YXh0yZzs0vFa-pbxAe0GS4Pkg4tZmQ3djFuPso,8061 -django/contrib/admindocs/views.py,sha256=JkVD_O0Wr2daO_sHrxEeCjJX6OvW4_rfke0FEpieHZk,18559 -django/contrib/auth/__init__.py,sha256=RfV_3QgS1JKo_ooxEC90Jik4QXP7xxkzxkx3CLje6uw,7974 +django/contrib/admindocs/urls.py,sha256=zdHaV60yJMjuLqO9xU0H-j7hz1PmSsepEWZA2GH-eI0,1310 +django/contrib/admindocs/utils.py,sha256=tCEGbV5-NyO6qkLIXjl-MX8kT9BgfWkiJuzgkfO1Mso,7735 +django/contrib/admindocs/views.py,sha256=SyS5eZk5yv-MDtHih96mXPC1muxcsiRWlBC_u0WjgYY,16610 +django/contrib/auth/__init__.py,sha256=rKe8ZkIy6BdifutPxw64Qw2RPNYPrUX7t4491mBKS40,8129 django/contrib/auth/__pycache__/__init__.cpython-39.pyc,, django/contrib/auth/__pycache__/admin.cpython-39.pyc,, django/contrib/auth/__pycache__/apps.cpython-39.pyc,, @@ -1384,66 +1376,66 @@ django/contrib/auth/__pycache__/tokens.cpython-39.pyc,, django/contrib/auth/__pycache__/urls.cpython-39.pyc,, django/contrib/auth/__pycache__/validators.cpython-39.pyc,, django/contrib/auth/__pycache__/views.cpython-39.pyc,, -django/contrib/auth/admin.py,sha256=jOLuaoiasplhn_HWMqqAo9VdQpDAVIfGPBFwpdMopVQ,9020 -django/contrib/auth/apps.py,sha256=JE5zuVw7Tx6NFULN_u8sOxs0OnHczMC9bM0N_m1xsmA,1224 -django/contrib/auth/backends.py,sha256=I66mtqQbHZBTzTfp1jWnFnmWaLDN3GBUoG2Tj492zp4,8578 -django/contrib/auth/base_user.py,sha256=KpR-mxMFOXQwZfaJlVUfjdxCrLAiuajwM0jF0UYEkoQ,4484 -django/contrib/auth/checks.py,sha256=q05m4ylm3r3z8t7BPKeJLlpz5qfv6HOiPNcEl6sgAfw,8442 +django/contrib/auth/admin.py,sha256=YbVtoNYWSkoLWKePeJ0Pl6u6wrhaoxeS8dTd3n7hXws,8607 +django/contrib/auth/apps.py,sha256=DgNZlcp3k1Q0rextjMNEtabiYJovxYLseqamxFcvt_k,1222 +django/contrib/auth/backends.py,sha256=fvm2NFyd90CSCzv66G7RA8x5zszGu2u_0YnHhB_JlpY,8584 +django/contrib/auth/base_user.py,sha256=cfEtOcBOBiIU_WZ3yrXU0RbJEQRg0IxEoLUosf_gsVU,4995 +django/contrib/auth/checks.py,sha256=tq__evaH98gSiOojZkB9tyPxpBZb1YWaV9Ki5xvfSes,8270 django/contrib/auth/common-passwords.txt.gz,sha256=CnCdMuzzpa5EVwTpCqtO7-x3CIPsy47PWWw7GUT9C5M,81355 -django/contrib/auth/context_processors.py,sha256=8BbvdbTVPl8GVgB5-2LTzx6FrGsMzev-E7JMnUgr-rM,1911 -django/contrib/auth/decorators.py,sha256=YWxmna4E6nQSgNzEpcNkW6aoawqVGDmUnRoOJqZAxhs,2901 -django/contrib/auth/forms.py,sha256=YgfDipDFlWLAtuhFeUwPpOoKa_gkSAJXVB9zxFfr8QQ,16433 +django/contrib/auth/context_processors.py,sha256=Vb91feuKV9a3BBgR0hrrGmZvVPw0JyYgeA_mRX9QK1c,1822 +django/contrib/auth/decorators.py,sha256=2iowUAGrkZBzaX_Wf0UkUbd0po00UCxtdFQxXj1HIyo,2892 +django/contrib/auth/forms.py,sha256=EOK1aJnd3aq3HWwCOiqpE5YkD8d3qtCkTVm-wjW4JY0,15860 django/contrib/auth/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/auth/handlers/__pycache__/__init__.cpython-39.pyc,, django/contrib/auth/handlers/__pycache__/modwsgi.cpython-39.pyc,, django/contrib/auth/handlers/modwsgi.py,sha256=bTXKVMezywsn1KA2MVyDWeHvTNa2KrwIxn2olH7o_5I,1248 -django/contrib/auth/hashers.py,sha256=p05t7lpA9HZWgN86L8MtkBoCeDP340sL1WM8V1hKeAs,27821 +django/contrib/auth/hashers.py,sha256=lCOVpC2etwCXkHEnA5shHmseAEQrqbWeDpUsOLWTFIA,24886 django/contrib/auth/locale/af/LC_MESSAGES/django.mo,sha256=UKEGdzrpTwNnuhPcejOS-682hL88yV83xh-55dMZzyg,7392 django/contrib/auth/locale/af/LC_MESSAGES/django.po,sha256=GFM0MbuRB9axSqvFQzZXhyeZF9JTKqoMMdfNEgNQVFY,7618 -django/contrib/auth/locale/ar/LC_MESSAGES/django.mo,sha256=7LhxFfL9y6RAfZ8PU-1lKI2V02LbHxXtB1UAf_vXpuc,10040 -django/contrib/auth/locale/ar/LC_MESSAGES/django.po,sha256=2QIaioY0RedAB0CFKVZLhGoCnhLzgUh84sAR7i6QUnQ,10520 +django/contrib/auth/locale/ar/LC_MESSAGES/django.mo,sha256=qZHGbagURzeNxkWTntpWSgdXUPip-DLEKOcqVaDwEms,9983 +django/contrib/auth/locale/ar/LC_MESSAGES/django.po,sha256=4YFAblOEgpwlFfSNp0ok4RhUNoygfBvYlbkH5SqCwn0,10464 django/contrib/auth/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=s6EoUozLpEw-OT2WllVMl8SwKrkBmIWgGO9qbG80xsQ,10167 django/contrib/auth/locale/ar_DZ/LC_MESSAGES/django.po,sha256=P7GHKRC3hZiyVtbfzVGTcY81FuAGf0LFUgR6TZSEwfY,10494 django/contrib/auth/locale/ast/LC_MESSAGES/django.mo,sha256=Pt3gYY3j8Eroo4lAEmf-LR6u9U56mpE3vqLhjR4Uq-o,2250 django/contrib/auth/locale/ast/LC_MESSAGES/django.po,sha256=Kiq4s8d1HnYpo3DQGlgUl3bOkxmgGW8CvGp9AbryRk8,5440 django/contrib/auth/locale/az/LC_MESSAGES/django.mo,sha256=h1bem16bDuYOFR7NEGt2b3ssLOXMHqeWmnZtlni4e9g,7448 django/contrib/auth/locale/az/LC_MESSAGES/django.po,sha256=euNyhutfYGtuMhUHpGJrLVXnlhPEGkJOV4d_gEJn5no,7735 -django/contrib/auth/locale/be/LC_MESSAGES/django.mo,sha256=PKHL5EabL4jB0bQkw6GGiWhUK56Wa6tJxzZ09ymAF-M,10033 -django/contrib/auth/locale/be/LC_MESSAGES/django.po,sha256=ncUrvZfc57_WF38MxBV_rpLJeMcIg14D6d2M8NvLogk,10355 -django/contrib/auth/locale/bg/LC_MESSAGES/django.mo,sha256=iCuCi9MbjhsOUaUh2-OR_Ox9odgtBFSwyzJhmIOH9iQ,9466 -django/contrib/auth/locale/bg/LC_MESSAGES/django.po,sha256=NGss8TqtnQ_KrEtl433W8y3dbgYoezne1w2ylqGrn7s,9994 +django/contrib/auth/locale/be/LC_MESSAGES/django.mo,sha256=SgSeUlTJuQ4-YZj7h6WltiuUVcYldlBcVdlynQ4bT80,9976 +django/contrib/auth/locale/be/LC_MESSAGES/django.po,sha256=LFiM8UDOCw2AY_GAL3Sbwrah_Umg32Q5phkbvjV8UlE,10299 +django/contrib/auth/locale/bg/LC_MESSAGES/django.mo,sha256=ZwwXfAeWM92GObhxU6zzGu36KJUpkGOuEeprRMu5mZc,8751 +django/contrib/auth/locale/bg/LC_MESSAGES/django.po,sha256=_a2hoIiJRbvW3ymKAkAp-UZNk5AiUy5HqPBBby74Jew,9492 django/contrib/auth/locale/bn/LC_MESSAGES/django.mo,sha256=cJSawQn3rNh2I57zK9vRi0r1xc598Wr26AyHh6D50ZQ,5455 django/contrib/auth/locale/bn/LC_MESSAGES/django.po,sha256=5Vqd4n9ab98IMev4GHLxpO7f4r9nnhC3Nfx27HQNd8s,7671 django/contrib/auth/locale/br/LC_MESSAGES/django.mo,sha256=nxLj88BBhT3Hudev1S_BRC8P6Jv7eoR8b6CHGt5eoPo,1436 django/contrib/auth/locale/br/LC_MESSAGES/django.po,sha256=rFo68wfXMyju633KCAhg0Jcb3GVm3rk4opFQqI89d6Y,5433 -django/contrib/auth/locale/bs/LC_MESSAGES/django.mo,sha256=jDjP1qIs02k6RixY9xy3V7Cr6zi-henR8nDnhqNG18s,3146 -django/contrib/auth/locale/bs/LC_MESSAGES/django.po,sha256=NOICHHU8eFtltH0OBlnasz9TF0uZGZd3hMibRmn158E,5975 -django/contrib/auth/locale/ca/LC_MESSAGES/django.mo,sha256=C-Majs_UV5pW2wET8sRPo3zj3FGaEnkrQzAE2O2ShCM,7649 -django/contrib/auth/locale/ca/LC_MESSAGES/django.po,sha256=9ABSsHnSTgWHmuF3m6PkzMxQfQOrdY7rITH1AgG6qCU,8201 -django/contrib/auth/locale/cs/LC_MESSAGES/django.mo,sha256=7TuyZNQ11j4iLxxr_xch3gBDQ0cSTh0VFUa0FMzH1Uo,7836 -django/contrib/auth/locale/cs/LC_MESSAGES/django.po,sha256=qoA5lHFEwLZZakgYONzA-TxBqpBNhBytGHxS40YCf0s,8292 +django/contrib/auth/locale/bs/LC_MESSAGES/django.mo,sha256=1i1CxyXwfskDZtItZQuEpZFlV3cpIo6Ls7Ocs0X3VTA,2963 +django/contrib/auth/locale/bs/LC_MESSAGES/django.po,sha256=C5CQ5vqjuLscWSKHVu0niGzmhxX0y-pf_eiuEr-ZmGU,5793 +django/contrib/auth/locale/ca/LC_MESSAGES/django.mo,sha256=Jdp-SfCkSmIOlNKB2lkSn-F6tiSHQdeNoa46GMnhCns,7600 +django/contrib/auth/locale/ca/LC_MESSAGES/django.po,sha256=N3MrFenu4UdQa90RW3sg5L1KhUdz99YSi7lHXugqyBY,8153 +django/contrib/auth/locale/cs/LC_MESSAGES/django.mo,sha256=HeVt40NO0DW62ZyRYDvKI4AulvMuQzbwJJvwvhXGWQo,7786 +django/contrib/auth/locale/cs/LC_MESSAGES/django.po,sha256=CXv_geqSBQPoEtRtTHg_PxzbIP35vfOkZ2_F4b1_Hm0,8243 django/contrib/auth/locale/cy/LC_MESSAGES/django.mo,sha256=lSfCwEVteW4PDaiGKPDxnSnlDUcGMkPfsxIluExZar0,4338 django/contrib/auth/locale/cy/LC_MESSAGES/django.po,sha256=-LPAKGXNzB77lVHfCRmFlH3SUaLgOXk_YxfC0BomcEs,6353 -django/contrib/auth/locale/da/LC_MESSAGES/django.mo,sha256=r3_2TSpbpLSK62t8KdD2VQqAxUXzdBaox5fyjgtoS7w,7478 -django/contrib/auth/locale/da/LC_MESSAGES/django.po,sha256=3ZgzujWtZwIsdk7gtvzXOxQusiFgzLTZBhXS3jUESAg,7958 -django/contrib/auth/locale/de/LC_MESSAGES/django.mo,sha256=ewzAnUawN_euTDtXmroxkktzKAHsTkHRjZQOkuYHu7k,7529 -django/contrib/auth/locale/de/LC_MESSAGES/django.po,sha256=J8GJ8N1RvQDRACYUfeZwqFnnEwi3PtYe7QaJ_UDFp3Q,8033 -django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo,sha256=QOnCzO9Nf3_JBX9WP2pGfZXISgyAHv3p62OAF6fxkG8,8135 -django/contrib/auth/locale/dsb/LC_MESSAGES/django.po,sha256=g1o_S2lb_fN7aL_YIpOI0KicRiz72gh5sz2DYuwNYNI,8434 -django/contrib/auth/locale/el/LC_MESSAGES/django.mo,sha256=KaP9RLYThwYWLBx0W90HI0zJZ09iNhZ3tk8UVF63n74,10072 -django/contrib/auth/locale/el/LC_MESSAGES/django.po,sha256=O5JsNCUNr1YcNNqMugoM5epN6nC5pgq3E6nKXDh3OY0,10795 +django/contrib/auth/locale/da/LC_MESSAGES/django.mo,sha256=321FuiFJg-xSrNri8oPSLKLU4OPqQBQBxd_w_tRFUQI,7418 +django/contrib/auth/locale/da/LC_MESSAGES/django.po,sha256=jv5xZta-NXpaJNdwpMapg3QCUy0-KwVrDx2JeMH7Bok,7811 +django/contrib/auth/locale/de/LC_MESSAGES/django.mo,sha256=b7ZXlKTff2vYkY7ldkQVD6SX-36KgWBW8VsuP4m8bSY,7477 +django/contrib/auth/locale/de/LC_MESSAGES/django.po,sha256=W9MRGmYgNk5n-nMd6SKfL3kQ-YjsUh_vOZ818CR10Tw,7938 +django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo,sha256=iYj_y2xE4yetsuFgDAfpr5iQgyVCfJL4x5qPIuVPCO0,8081 +django/contrib/auth/locale/dsb/LC_MESSAGES/django.po,sha256=657tWjp8Wowyib_uQh2tFULEETaavrI9zqgmkKq2TCw,8381 +django/contrib/auth/locale/el/LC_MESSAGES/django.mo,sha256=tfjgL-_ZACj_GjsfR7jw1PTjxovgR51-LSo5ngtRX-U,10150 +django/contrib/auth/locale/el/LC_MESSAGES/django.po,sha256=IXkrUAGvMZrQTUb6DpdgftRkWg4aKy9vwyO6i-ajsjU,10753 django/contrib/auth/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 -django/contrib/auth/locale/en/LC_MESSAGES/django.po,sha256=VLGYW9XtfDKb1uNmH5-6q_8vh7WFfpOLV8dleeQFUkE,8219 -django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo,sha256=7cPKOZX0ZmWCYU2ZwgCp8LwXj7FAdP3lMoI2u4nzgeU,7183 -django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po,sha256=92Q42wfwKhGxDkomv8JlGBHVUdFIc_wvm_LUNBc9Q1k,7467 +django/contrib/auth/locale/en/LC_MESSAGES/django.po,sha256=cPtY1qLoggZk3h9DztguWtUaLkeE4uQr3yVQfBesyh8,8012 +django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo,sha256=74v8gY8VcSrDgsPDaIMET5frCvtzgLE8oHgX1xNWUvw,3650 +django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po,sha256=lg-LFEeZXxGsNNZ656ePDvAAncjuy0LKuQxUFvQCUJk,5921 django/contrib/auth/locale/en_GB/LC_MESSAGES/django.mo,sha256=p57gDaYVvgEk1x80Hq4Pn2SZbsp9ly3XrJ5Ttlt2yOE,3179 django/contrib/auth/locale/en_GB/LC_MESSAGES/django.po,sha256=-yDflw5-81VOlyqkmLJN17FRuwDrhYXItFUJwx2aqpE,5787 django/contrib/auth/locale/eo/LC_MESSAGES/django.mo,sha256=0Z-qGFJc_j9rV2-BN4YoVTeGQd15yoHHpt0fSEMwiwg,7329 django/contrib/auth/locale/eo/LC_MESSAGES/django.po,sha256=cghgKB3ZOFTNdjBuCpWXn1WFXJQLRCQGEzBwiLU5D-c,7731 -django/contrib/auth/locale/es/LC_MESSAGES/django.mo,sha256=9BfzdvlAsARjd4CVzSritsWzQJsP5w7sM7nLO-KXF9A,7782 -django/contrib/auth/locale/es/LC_MESSAGES/django.po,sha256=Km0bEjiIkOoNXvC4hhId3CXKqIqFHWSFEdK6OgEHx04,8571 -django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo,sha256=tPRhIvlvgn5urawLpgF-YIoO4zqc06LtHflK_G_FYFU,7943 -django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po,sha256=XqPd_mBJmPG-YYZrDdfVe7nbC6B5NLcHp2aISkk23xI,8214 +django/contrib/auth/locale/es/LC_MESSAGES/django.mo,sha256=gajANBcmH_eF4cnVZpKyhpnVWXJr8r7SWOzm5FwN0-k,7729 +django/contrib/auth/locale/es/LC_MESSAGES/django.po,sha256=aUVmLldyKIQCnWkLrMMEt1n5kfkqnOTzaxR7YnFjbn4,8469 +django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo,sha256=ow-0zlgfVDS_IAr6OLoPqXdVrFGo02EZCPf3Hw3JGyQ,7890 +django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po,sha256=c0z6f_s47yZ1UyaUY7dTr9S_v5dj6mL2YyuhK0qWBOs,8162 django/contrib/auth/locale/es_CO/LC_MESSAGES/django.mo,sha256=K5VaKTyeV_WoKsLR1x8ZG4VQmk3azj6ZM8Phqjs81So,6529 django/contrib/auth/locale/es_CO/LC_MESSAGES/django.po,sha256=qJywTaYi7TmeMB1sjwsiwG8GXtxAOaOX0voj7lLVZRw,7703 django/contrib/auth/locale/es_MX/LC_MESSAGES/django.mo,sha256=dCav1yN5q3bU4PvXZd_NxHQ8cZ9KqQCiNoe4Xi8seoY,7822 @@ -1454,18 +1446,18 @@ django/contrib/auth/locale/et/LC_MESSAGES/django.mo,sha256=yilio-iPwr09MPHPgrDLQ django/contrib/auth/locale/et/LC_MESSAGES/django.po,sha256=OvUyjbna_KS-bI4PUUHagS-JuwtB7G0J1__MtFGxB-M,7886 django/contrib/auth/locale/eu/LC_MESSAGES/django.mo,sha256=K0AoFJGJJSnD1IzYqCY9qB4HZHwx-F7QaDTAGehyo7w,7396 django/contrib/auth/locale/eu/LC_MESSAGES/django.po,sha256=y9BAASQYTTYfoTKWFVQUYs5-zPlminfJ6C5ZORD6g-s,7749 -django/contrib/auth/locale/fa/LC_MESSAGES/django.mo,sha256=yeA_5LAPu7OyQssunvUNlH07bPVCyGLpnvijNenrtHQ,8979 -django/contrib/auth/locale/fa/LC_MESSAGES/django.po,sha256=NChJSgpkXrwAiTrCJzvwlm9mh-LFSD1rR1ESdRQD43o,9513 -django/contrib/auth/locale/fi/LC_MESSAGES/django.mo,sha256=fH_rcYkl9L2dK1G3MjVETXAHunCPhsXQYMTbDcNe-00,7537 -django/contrib/auth/locale/fi/LC_MESSAGES/django.po,sha256=PVwyNBaToxjyHkxy4t4L-kULjJslTe94coSxWNseyn4,7892 -django/contrib/auth/locale/fr/LC_MESSAGES/django.mo,sha256=nppbd8aA9qHRmmneAz9Ld0PtnnaWranJ0Gil2j1ZqP8,8155 -django/contrib/auth/locale/fr/LC_MESSAGES/django.po,sha256=UfsLNtSLwfWgFgHIigXUSM-atmNRoOKm2U4lCUOU2Qo,8530 +django/contrib/auth/locale/fa/LC_MESSAGES/django.mo,sha256=eYicBjtar7IU6zkldkNtcbewhv4DGhf2xIt2pEnrCss,8944 +django/contrib/auth/locale/fa/LC_MESSAGES/django.po,sha256=JEnOdy86onxyUvfNQ6sVY76SG1XSoB6ePZgIlBXQGdI,9431 +django/contrib/auth/locale/fi/LC_MESSAGES/django.mo,sha256=OPQ9WRAp6F6TERy-r62D0hNDPQcmH2zGFlEqZab3keY,7492 +django/contrib/auth/locale/fi/LC_MESSAGES/django.po,sha256=bhwFsyeQtr_dCu3QU8EuyyVnHegU-78AfXp0ptCWcV0,7848 +django/contrib/auth/locale/fr/LC_MESSAGES/django.mo,sha256=CiCGqwKFoJnWDqi7QgHcLEkayZTA9JZX3SWCsIBxTK8,8105 +django/contrib/auth/locale/fr/LC_MESSAGES/django.po,sha256=Kij98WD0TShBZdMYXmjINji3SuCmKTafmxUL8-JLJt0,8481 django/contrib/auth/locale/fy/LC_MESSAGES/django.mo,sha256=95N-77SHF0AzQEer5LuBKu5n5oWf3pbH6_hQGvDrlP4,476 django/contrib/auth/locale/fy/LC_MESSAGES/django.po,sha256=8XOzOFx-WerF7whzTie03hgO-dkbUFZneyrpZtat5JY,3704 django/contrib/auth/locale/ga/LC_MESSAGES/django.mo,sha256=Nd02Ed9ACCY6JCCSwtiWl3DTODLFFu9Mq6JVlr5YbYk,3572 django/contrib/auth/locale/ga/LC_MESSAGES/django.po,sha256=FQJMR5DosuKqo4vvF0NAQnjfqbH54MSzqL2-4BO4-uM,6127 -django/contrib/auth/locale/gd/LC_MESSAGES/django.mo,sha256=BLBYJV9Adx1BsXZaM0qZ54mNRAF5s4dxB1TBLtIyMHQ,8743 -django/contrib/auth/locale/gd/LC_MESSAGES/django.po,sha256=rqPK26mtE_U-TG2qyjc5xCR-feI3sGXZR5H6ohNzx4s,9099 +django/contrib/auth/locale/gd/LC_MESSAGES/django.mo,sha256=-GSChZnB8t2BR6KoF-ZU2qlvfXNq5fAbomOBdoefEZE,8687 +django/contrib/auth/locale/gd/LC_MESSAGES/django.po,sha256=SN-QSmEG04qXHwoIzBgMjHEkqYqFQeJ7OvFXg496A6c,9044 django/contrib/auth/locale/gl/LC_MESSAGES/django.mo,sha256=ZqVb1YCn_0_HyVtb_rnxmn0BSYAuKTVTFNHf2gftt5c,4022 django/contrib/auth/locale/gl/LC_MESSAGES/django.po,sha256=YN_7iJTGc1Kh5llxHnwqq1kZmdQVMUMv1bkti30fMCI,6371 django/contrib/auth/locale/he/LC_MESSAGES/django.mo,sha256=MeI7B43KSAIZL7_qxceKnnFKnyoUVYeZDRkGWabrclw,8606 @@ -1474,24 +1466,24 @@ django/contrib/auth/locale/hi/LC_MESSAGES/django.mo,sha256=7CxV1H37hMbgKIhnAWx-a django/contrib/auth/locale/hi/LC_MESSAGES/django.po,sha256=DU5YM6r1kd5fo40yqFXzEaNh42ezFQFQ-0dmVqkaKQ0,7769 django/contrib/auth/locale/hr/LC_MESSAGES/django.mo,sha256=GEap3QClwCkuwQZKJE7qOZl93RRxmyyvTTnOTYaAWUo,5894 django/contrib/auth/locale/hr/LC_MESSAGES/django.po,sha256=ALftoYSaI1U90RNDEvnaFATbw1SL0m8fNXAyl6DkSvo,7355 -django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo,sha256=J28d-As2avF34TlqEkhOzy91URZAXF47SY27qCPysPg,7974 -django/contrib/auth/locale/hsb/LC_MESSAGES/django.po,sha256=P3onPBZkAYofZpYnTsJ0ILbRFRhHtZorsHZ4-DVuu8w,8264 +django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo,sha256=EPvlwd_NX7HEYa9exou0QWR501uyNr8_3tRMz-l1_FA,7922 +django/contrib/auth/locale/hsb/LC_MESSAGES/django.po,sha256=oylGjyfqTtyTJGRpBEI3xfN5MFzgklZ5FtNVe54ugKM,8213 django/contrib/auth/locale/hu/LC_MESSAGES/django.mo,sha256=TLGY7EaLD12NHYM1hQlqb4D4BM0T68jv8yhECOHIgcA,7655 django/contrib/auth/locale/hu/LC_MESSAGES/django.po,sha256=E51MM5qqplgrOSrh60bfz-EvyL91Ik3kL3YJOK-dqzk,8040 django/contrib/auth/locale/hy/LC_MESSAGES/django.mo,sha256=zoLe0EqIH8HQYC5XAWd8b8mA2DpbmDSEBsF-WIKX_OQ,8001 django/contrib/auth/locale/hy/LC_MESSAGES/django.po,sha256=wIWLbz6f0n44ZcjEbZZsgoWTpzXRGND15hudr_DQ3l0,8787 -django/contrib/auth/locale/ia/LC_MESSAGES/django.mo,sha256=OTxh6u0QmsytMrp8IKWBwMnhrYCpyS6qVnF7YBCAWe0,7626 -django/contrib/auth/locale/ia/LC_MESSAGES/django.po,sha256=ue4RXEXweO1-9sZOKkLZsyZe8yxnPWB3JZyyh3qzmlA,7895 +django/contrib/auth/locale/ia/LC_MESSAGES/django.mo,sha256=oTzOm7fRjn79_pU9zy6D_Ehex5FK7hjQYe4soeHhRkk,3314 +django/contrib/auth/locale/ia/LC_MESSAGES/django.po,sha256=LzJOXjj1Fa61zk3v2d-aWS48eva2S0b0jJ9r5CqiFDY,5881 django/contrib/auth/locale/id/LC_MESSAGES/django.mo,sha256=gCVLTVK24TVnaaeb3JAqQ9Wzt0Cad0FLcCBr0gD76kU,7170 django/contrib/auth/locale/id/LC_MESSAGES/django.po,sha256=0bxsUqjQMA2qCjBkx1Q62v007ow3S5J3UgcV2ll9sL4,7589 django/contrib/auth/locale/io/LC_MESSAGES/django.mo,sha256=YwAS3aWljAGXWcBhGU_GLVuGJbHJnGY8kUCE89CPdks,464 django/contrib/auth/locale/io/LC_MESSAGES/django.po,sha256=W36JXuA1HQ72LspixRxeuvxogVxtk7ZBbT0VWI38_oM,3692 django/contrib/auth/locale/is/LC_MESSAGES/django.mo,sha256=0PBYGqQKJaAG9m2jmJUzcqRVPc16hCe2euECMCrNGgI,7509 django/contrib/auth/locale/is/LC_MESSAGES/django.po,sha256=o6dQ8WMuPCw4brSzKUU3j8PYhkLBO7XQ3M7RlsIw-VY,7905 -django/contrib/auth/locale/it/LC_MESSAGES/django.mo,sha256=cKZiZxBLLKL3HqbBWCtMxkP_Y5o9tgo5J-dMLPPthk0,7608 -django/contrib/auth/locale/it/LC_MESSAGES/django.po,sha256=VTurpVr2_2HRZpw1bUqjehWaypfGfI6ySGjoYA_MFFM,8178 -django/contrib/auth/locale/ja/LC_MESSAGES/django.mo,sha256=MB36xS89-mlWuDRood1NGARfcWmqj7dp2JMCXuA16fA,8085 -django/contrib/auth/locale/ja/LC_MESSAGES/django.po,sha256=aAVHTduL9eq_BuCvjLjBxOmU7_Oq3MnrsZ4JqutCSFM,8436 +django/contrib/auth/locale/it/LC_MESSAGES/django.mo,sha256=dI8wYt63mrZ02kL3r1XVY-AIussOMwQyvWBfefM4Zw0,7539 +django/contrib/auth/locale/it/LC_MESSAGES/django.po,sha256=wnIrW0RSky6QG7hrmof8Ow3-4YLouN6izMC2kik-PHA,8069 +django/contrib/auth/locale/ja/LC_MESSAGES/django.mo,sha256=qzCIy4-2ZpAPjeiBJWcrvcOCP6YyJl7CwdJtI8kn4P4,8024 +django/contrib/auth/locale/ja/LC_MESSAGES/django.po,sha256=l-txD5McDJSjRIG5t3XFWjaezvy0gmnGl3SUUVwumDg,8376 django/contrib/auth/locale/ka/LC_MESSAGES/django.mo,sha256=0QWYd58Dz5Az3OfZo7wV3o-QCre2oc5dgEPu0rnLVJI,10625 django/contrib/auth/locale/ka/LC_MESSAGES/django.po,sha256=oCtz7gS4--mhv7biS1rIh43I4v1UpZX4DKdrB-xZ2RA,11217 django/contrib/auth/locale/kab/LC_MESSAGES/django.mo,sha256=9qKeQ-gDByoOdSxDpSbLaM4uSP5sIi7qlTn8tJidVDs,2982 @@ -1501,17 +1493,17 @@ django/contrib/auth/locale/kk/LC_MESSAGES/django.po,sha256=OebwPN9iWBvjDu0P2gQyB django/contrib/auth/locale/km/LC_MESSAGES/django.mo,sha256=FahcwnCgzEamtWcDEPOiJ4KpXCIHbnSowfSRdRQ2F9U,2609 django/contrib/auth/locale/km/LC_MESSAGES/django.po,sha256=lvRHHIkClbt_8-9Yn0xY57dMxcS72z4sUkxLb4cohP0,5973 django/contrib/auth/locale/kn/LC_MESSAGES/django.mo,sha256=u0YygqGJYljBZwI9rm0rRk_DdgaBEMA1etL-Lk-7Mls,4024 -django/contrib/auth/locale/kn/LC_MESSAGES/django.po,sha256=J67MIAas5egVq_FJBNsug3Y7rZ8KakhQt6isyF23HAA,6957 -django/contrib/auth/locale/ko/LC_MESSAGES/django.mo,sha256=vwD0-GW2g4uAPCQbvsr2CyZ1Y-9VHcF4xlN3qaJbolU,7607 -django/contrib/auth/locale/ko/LC_MESSAGES/django.po,sha256=6PX6SMXjv_bYolpgHfcFpzaKPdkwJSVg95GU5EpjdeM,8350 -django/contrib/auth/locale/ky/LC_MESSAGES/django.mo,sha256=mnBXtpInYxaSNIURJTmx8uBg_PH-NuPN9r54pkQY3q4,8924 -django/contrib/auth/locale/ky/LC_MESSAGES/django.po,sha256=7FeO_Kb2er0S84KnFeXVHO3TgAmEJ0gTQEDHImoxiZ4,9170 +django/contrib/auth/locale/kn/LC_MESSAGES/django.po,sha256=HKQ1t2yhh9OwsqvMft337VpPmi8KU8PhF2M8gKOdtXw,6951 +django/contrib/auth/locale/ko/LC_MESSAGES/django.mo,sha256=eeIHHuqWmYIKdg4Awtzuiq73nJYGZGL912jDSKdK0tc,7578 +django/contrib/auth/locale/ko/LC_MESSAGES/django.po,sha256=ch1Eoy8J5B7wu35yvYPz5mhuXYzIxDocvVEsuICpv0M,8308 +django/contrib/auth/locale/ky/LC_MESSAGES/django.mo,sha256=BNywf3uEH-pgTQpXBupcIfmlijKSXVOzRpRv9BSW8GM,8867 +django/contrib/auth/locale/ky/LC_MESSAGES/django.po,sha256=Ta2eoSIa3EMAGwMg_ZLMXY4ycd4ncCS9do97kU60sVE,9114 django/contrib/auth/locale/lb/LC_MESSAGES/django.mo,sha256=OFhpMA1ZXhrs5fwZPO5IjubvWDiju4wfwWiV94SFkiA,474 django/contrib/auth/locale/lb/LC_MESSAGES/django.po,sha256=dOfY9HjTfMQ0nkRYumw_3ZaywbUrTgT-oTXAnrRyfxo,3702 django/contrib/auth/locale/lt/LC_MESSAGES/django.mo,sha256=-nlZHl7w__TsFUmBb5pQV_XJtKGsi9kzP6CBZXkfM8M,8146 django/contrib/auth/locale/lt/LC_MESSAGES/django.po,sha256=-rdhB6eroSSemsdZkG1Jl4CruNZc_7dj4m5IVoyRBUQ,8620 -django/contrib/auth/locale/lv/LC_MESSAGES/django.mo,sha256=DlqrlpNQc98t2J90qhKomGxiALksW85e8BsSR94DBgk,7631 -django/contrib/auth/locale/lv/LC_MESSAGES/django.po,sha256=86UCfg0-4IJFYvV9g7__7CuuRYFOccYr3XkFNtRAT8Q,8018 +django/contrib/auth/locale/lv/LC_MESSAGES/django.mo,sha256=MeaR3wk2dhEJl0ib7sfLomLmO14r1dDDf9UCGkzgUtA,7582 +django/contrib/auth/locale/lv/LC_MESSAGES/django.po,sha256=o-lm18LyXAna2tVM4BX2aLYdLKsr59m_VWImsYaSvN8,7970 django/contrib/auth/locale/mk/LC_MESSAGES/django.mo,sha256=XS9dslnD_YBeD07P8WQkss1gT7GIV-qLiCx4i5_Vd_k,9235 django/contrib/auth/locale/mk/LC_MESSAGES/django.po,sha256=QOLgcwHub9Uo318P2z6sp69MI8syIIWCcr4VOom9vfs,9799 django/contrib/auth/locale/ml/LC_MESSAGES/django.mo,sha256=UEaqq7nnGvcZ8vqFicLiuqsuEUhEjd2FpWfyzy2HqdU,12611 @@ -1520,8 +1512,6 @@ django/contrib/auth/locale/mn/LC_MESSAGES/django.mo,sha256=hBYT0p3LcvIKKPtIn2NzP django/contrib/auth/locale/mn/LC_MESSAGES/django.po,sha256=R3wAEwnefEHZsma8J-XOn4XlLtuWYKDPLwJ99DUYmvE,9913 django/contrib/auth/locale/mr/LC_MESSAGES/django.mo,sha256=zGuqUTqcWZZn8lZY56lf5tB0_lELn7Dd0Gj78wwO5T4,468 django/contrib/auth/locale/mr/LC_MESSAGES/django.po,sha256=yLW9WuaBHqdp9PXoDEw7c05Vt0oOtlks5TS8oxYPAO8,3696 -django/contrib/auth/locale/ms/LC_MESSAGES/django.mo,sha256=eCAZrzQxsM_pAxr_XQo2fIOsCbj5LjGKpLNCzob2l-I,7654 -django/contrib/auth/locale/ms/LC_MESSAGES/django.po,sha256=FAtyzSGcD1mIhRIg8O_1SHLdisTPGYZK-QUjzgw-wCY,7847 django/contrib/auth/locale/my/LC_MESSAGES/django.mo,sha256=gYzFJKi15RbphgG1IHbJF3yGz3P2D9vaPoHZpA7LoH8,1026 django/contrib/auth/locale/my/LC_MESSAGES/django.po,sha256=lH5mrq-MyY8gvrNkH2_20rkjFnbviq23wIUqIjPIgFI,5130 django/contrib/auth/locale/nb/LC_MESSAGES/django.mo,sha256=T6aK_x_t3c0uoALxmraqrK4--Ln5vTUMPb2m7iuR9bM,7191 @@ -1530,14 +1520,14 @@ django/contrib/auth/locale/ne/LC_MESSAGES/django.mo,sha256=pq8dEr1ugF5ldwkCDHOq5 django/contrib/auth/locale/ne/LC_MESSAGES/django.po,sha256=bV-uWvT1ViEejrbRbVTtwC2cZVD2yX-KaESxKBnxeRI,8902 django/contrib/auth/locale/nl/LC_MESSAGES/django.mo,sha256=g29u9ZMWBkbkWw6jA0UU74pMCAh9s-Gb9Ft3zi9aNn4,7451 django/contrib/auth/locale/nl/LC_MESSAGES/django.po,sha256=U9JaMXlbuY9Lvu2pUK6x5vSD5m7ROaKt2P2rbBTDZ30,8176 -django/contrib/auth/locale/nn/LC_MESSAGES/django.mo,sha256=83HdNOuNQVgJXBZMytPz1jx3wWDy8-e6t_JNEUu6W8w,7147 -django/contrib/auth/locale/nn/LC_MESSAGES/django.po,sha256=4ciwQsZFYSV6CjFqzxxcESAm16huv9XyXvU-nchD-Fs,7363 +django/contrib/auth/locale/nn/LC_MESSAGES/django.mo,sha256=020nmL8b1yQL0ZyrDAdr0ZOsEGmNxvUpp9ISPBOVI8U,2801 +django/contrib/auth/locale/nn/LC_MESSAGES/django.po,sha256=SKgBiBM1llWFIvVjWRR0r2i3O8VcAdWe-PUhxckqmbE,5590 django/contrib/auth/locale/os/LC_MESSAGES/django.mo,sha256=DVsYGz-31nofEjZla4YhM5L7qoBnQaYnZ4TBki03AI4,4434 django/contrib/auth/locale/os/LC_MESSAGES/django.po,sha256=Akc1qelQWRA1DE6xseoK_zsY7SFI8SpiVflsSTUhQLw,6715 django/contrib/auth/locale/pa/LC_MESSAGES/django.mo,sha256=PeOLukzQ_CZjWBa5FGVyBEysat4Gwv40xGMS29UKRww,3666 django/contrib/auth/locale/pa/LC_MESSAGES/django.po,sha256=7ts9PUSuvfXGRLpfyVirJLDtsQcsVWFXDepVKUVlmtc,6476 -django/contrib/auth/locale/pl/LC_MESSAGES/django.mo,sha256=-Ie-Wmu6bIpQFabfX9apO1uYvjnK-lPj-99jTOhtH44,7950 -django/contrib/auth/locale/pl/LC_MESSAGES/django.po,sha256=9dyBmCqSvGEZ4BALTTzokZNx46NTJ04RHOYnrMfNCnU,8704 +django/contrib/auth/locale/pl/LC_MESSAGES/django.mo,sha256=aFiv3R2tRWOKs2UOBg9s35wbYnOIxgLCEfr8fIJbIEw,7908 +django/contrib/auth/locale/pl/LC_MESSAGES/django.po,sha256=pHr8LAF2bobzBHnteZrNS_NL5pbzn-LW4uhWff5UGwA,8619 django/contrib/auth/locale/pt/LC_MESSAGES/django.mo,sha256=oyKCSXRo55UiO3-JKcodMUnK7fuOuQxQrXcU7XkWidA,7756 django/contrib/auth/locale/pt/LC_MESSAGES/django.po,sha256=tEazw0kctJ3BaP21IblsMhno6qooOGW54zwende522Q,8128 django/contrib/auth/locale/pt_BR/LC_MESSAGES/django.mo,sha256=5oeVsEZTpuSXrh05QhaMDtgh-Lju6HdE8QROe-_uV_0,7546 @@ -1546,16 +1536,16 @@ django/contrib/auth/locale/ro/LC_MESSAGES/django.mo,sha256=GD04tb5R6nEeD6ZMAcZgh django/contrib/auth/locale/ro/LC_MESSAGES/django.po,sha256=YfkFuPrMwAR50k6lfOYeBbMosEbvXGWwMBD8B7p_2ZA,8298 django/contrib/auth/locale/ru/LC_MESSAGES/django.mo,sha256=4MRl7yEBk9G9R_GewxkBxHK5ic4ww_WopNQymluwbYs,10347 django/contrib/auth/locale/ru/LC_MESSAGES/django.po,sha256=y9e6hrsZO4GwIOWedYNuU2h7PX9Sa0uomKTyOlKGPw0,10963 -django/contrib/auth/locale/sk/LC_MESSAGES/django.mo,sha256=1xmFLKSKxwWOoW7MLQ6oLhOi5fRs_YEqYQ6VlQ0f7ag,7853 -django/contrib/auth/locale/sk/LC_MESSAGES/django.po,sha256=sNAtYJYT-QLmTRaYpoyAeC9j3adeQwvQqtxjKuDFkn0,8292 +django/contrib/auth/locale/sk/LC_MESSAGES/django.mo,sha256=hJ_ep7FCbG4DVZawMfx4GjOPcJc4ruFSki8bkYn2l2Y,7838 +django/contrib/auth/locale/sk/LC_MESSAGES/django.po,sha256=NOYdZ3dv3Vtl-5vOwJH26Rthl-5nn4JrXgnm3i-d0bY,8199 django/contrib/auth/locale/sl/LC_MESSAGES/django.mo,sha256=UAzD5UAqHBdiCMIPjZdouGt14xoHuo5EXDctNSDTEJk,7552 django/contrib/auth/locale/sl/LC_MESSAGES/django.po,sha256=tUqZLZJegGLteWOQiDwFRUGayBB2j9qATmL6SMgEhb8,7943 django/contrib/auth/locale/sq/LC_MESSAGES/django.mo,sha256=3bm81rsRuQmV_1mD9JrAwSjRIDUlsb3lPmBxRNHfz8w,7813 django/contrib/auth/locale/sq/LC_MESSAGES/django.po,sha256=BWfyT4qg1jMoDGwmpLq4uPHJ1hJXLHI7gyo4BnzVHZI,8128 -django/contrib/auth/locale/sr/LC_MESSAGES/django.mo,sha256=3dRNH8jjE8I2vQwyTZ5J6tGLeBr3_XhlAjdPqcMea0M,9761 -django/contrib/auth/locale/sr/LC_MESSAGES/django.po,sha256=33D4YxtMpY3s0cDsK0L2-bCvfZHlbfxR4XX9oMjCQXM,10081 -django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo,sha256=SXl_MvkY_idYMT3sF7nIuh8z2qMdMC1lJ69Y6FcJMaA,3191 -django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po,sha256=hlU8JVlqIKv-Wx9urJDnFxvyT_m8mLz0vTl8Tcat4lw,5958 +django/contrib/auth/locale/sr/LC_MESSAGES/django.mo,sha256=yVXEIE4iXPxxwIBp5H6P5tCPUoBaFdHYD5D6gIDAI5I,9698 +django/contrib/auth/locale/sr/LC_MESSAGES/django.po,sha256=-MA4QO64bs3Hk7k4h7hvv2njyib_o6gIvTz0jofLGTo,10019 +django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo,sha256=hwAo5ishpZZ9kb9WHrSMHdxmWV9afdxOHgVEwWqb4VE,3293 +django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po,sha256=qccS0IkO-JT504Y2uVGY5nPYfN8EA_58I9z492iQHKI,5934 django/contrib/auth/locale/sv/LC_MESSAGES/django.mo,sha256=cYfXonEKxA6H7RNn3dOxDKTwujEBfYDLb2b41nWmv2s,7416 django/contrib/auth/locale/sv/LC_MESSAGES/django.po,sha256=xtFMHCNOC1Zwx32zxhTmh3RMfx9fU-Xe944l4-26kdE,8029 django/contrib/auth/locale/sw/LC_MESSAGES/django.mo,sha256=I_lEsKuMGm07X1vM3-ReGDx2j09PGLkWcG0onC8q1uQ,5029 @@ -1570,45 +1560,45 @@ django/contrib/auth/locale/th/LC_MESSAGES/django.mo,sha256=zRpZ2xM5JEQoHtfXm2_XY django/contrib/auth/locale/th/LC_MESSAGES/django.po,sha256=Yhh_AQS_aM_9f_yHNNSu_3THbrU-gOoMpfiDKhkaSHo,7914 django/contrib/auth/locale/tk/LC_MESSAGES/django.mo,sha256=AqCIDe-6QrLMN3CNbMZsfrL0KxnQ3zuZwN8KvFmwRhE,7343 django/contrib/auth/locale/tk/LC_MESSAGES/django.po,sha256=LpVXh4T0ZS3EzbIpJud8Dlms0Bu1vWf6c0JqkpoD8q8,7605 -django/contrib/auth/locale/tr/LC_MESSAGES/django.mo,sha256=jQyJ55Sr_xkHTgcfvl3smdWjum0tGCzp4NY0U1w9aig,7506 -django/contrib/auth/locale/tr/LC_MESSAGES/django.po,sha256=5DXWBmUc3fAwVHwcCTeu7M2y8X9VqrI_Ex3ZFKqzCgA,8090 +django/contrib/auth/locale/tr/LC_MESSAGES/django.mo,sha256=XtrQms_lCzSfOlPaCKO-S_r7ATlqapeNxXaFeBKeR5I,7459 +django/contrib/auth/locale/tr/LC_MESSAGES/django.po,sha256=xLy4T5XqKYwu_WNi0nPeHRr1u9HjSvKl-RRkIUY9DfU,8049 django/contrib/auth/locale/tt/LC_MESSAGES/django.mo,sha256=g4pTk8QLQFCOkU29RZvR1wOd1hkOZe_o5GV9Cg5u8N4,1371 django/contrib/auth/locale/tt/LC_MESSAGES/django.po,sha256=owkJ7iPT-zJYkuKLykfWsw8j7O8hbgzVTOD0DVv956E,5222 django/contrib/auth/locale/udm/LC_MESSAGES/django.mo,sha256=zey19UQmS79AJFxHGrOziExPDDpJ1AbUegbCRm0x0hM,462 django/contrib/auth/locale/udm/LC_MESSAGES/django.po,sha256=gLVgaMGg0GA3Tey1_nWIjV1lnM7czLC0XR9NFBgL2Zk,3690 -django/contrib/auth/locale/uk/LC_MESSAGES/django.mo,sha256=1CE7KRwdtP3D0UmEQcgydqXLGWVNZRUs4hpkoQJGMlo,10077 -django/contrib/auth/locale/uk/LC_MESSAGES/django.po,sha256=7KbG2N4XhLlgjtaO20ZXCPIIotoWO7fr_nFUlWxzv6s,10776 +django/contrib/auth/locale/uk/LC_MESSAGES/django.mo,sha256=4Q872RPjz9d_FkIrV29AebGdKeUkdqkoGwVfzraHC1c,10030 +django/contrib/auth/locale/uk/LC_MESSAGES/django.po,sha256=vwPQoOqriNLj2jXJzixqQwiL7w9DWaNdLrL_bJZkw7Y,10730 django/contrib/auth/locale/ur/LC_MESSAGES/django.mo,sha256=rippTNHoh49W19c4HDUF8G5Yo3SknL3C87Afu8YXxzA,698 django/contrib/auth/locale/ur/LC_MESSAGES/django.po,sha256=gwSd8noEwbcvDE1Q4ZsrftvoWMwhw1J15gvdtK6E9ns,4925 django/contrib/auth/locale/uz/LC_MESSAGES/django.mo,sha256=bDkhpvduocjekq6eZiuEfWJqnIt5hQmxxoIMhLQWzqM,2549 django/contrib/auth/locale/uz/LC_MESSAGES/django.po,sha256=tPp8tRZwSMQCQ9AyAeUDtnRfmOk54UQMwok3HH8VNSQ,5742 -django/contrib/auth/locale/vi/LC_MESSAGES/django.mo,sha256=eBMTwnpRWRj8SZVZ1tN592Re_8CPyJzuF4Vtg9IMmFw,7892 -django/contrib/auth/locale/vi/LC_MESSAGES/django.po,sha256=mOr5WgFpwztdW-pEZ4O80MGlltYQyL2cAMhz6-Esfo0,8246 -django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=xV9wTiaL7hMCKmUOHuEs5XtxEibXWLnywDYTjeXoVCA,6907 -django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po,sha256=CUdR2ch2mOf5v3GTOTIQg2IOj-7M1mS6Dw9yvz891Yw,7638 +django/contrib/auth/locale/vi/LC_MESSAGES/django.mo,sha256=4YOb_ZbCI90UB01DpNsBAe6qqrc3P209Bz22FSVqvog,4703 +django/contrib/auth/locale/vi/LC_MESSAGES/django.po,sha256=1YjTrGYr04j9GtG8w0c7v71pHjHU8mHzT7tChroyfaw,6723 +django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=-lHBBA2wJ_5II1b95faiXUBAJ5-eL7DwpF8husPB8rw,6866 +django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po,sha256=YGpvA-isPZV6Wj0bXDIPGKP6cMY9APNcZ9TjyBJbWl8,7563 django/contrib/auth/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=yQ5Gllu4hXzuBpBNAgtJaBMVivJeXUUlpfDS4CT1wg4,6728 django/contrib/auth/locale/zh_Hant/LC_MESSAGES/django.po,sha256=Rw18_ZEtobUhmj2oF544zdQ6Vrac0T9UI9RJO4plOdc,7145 -django/contrib/auth/management/__init__.py,sha256=uKE77RX6iQSDb0B6ZP-QTt-kjSelmBY6FQSqxXv3zvI,5327 +django/contrib/auth/management/__init__.py,sha256=FZbrn--_vNJ5MA-fXVSUoHTbq_9Io_IAXxT1kMJQXrw,5136 django/contrib/auth/management/__pycache__/__init__.cpython-39.pyc,, django/contrib/auth/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/auth/management/commands/__pycache__/__init__.cpython-39.pyc,, django/contrib/auth/management/commands/__pycache__/changepassword.cpython-39.pyc,, django/contrib/auth/management/commands/__pycache__/createsuperuser.cpython-39.pyc,, -django/contrib/auth/management/commands/changepassword.py,sha256=uMA0bm8Xy2JovP9M4WrVdZF4qxgRLMaebx3sET2BKSY,2633 -django/contrib/auth/management/commands/createsuperuser.py,sha256=5ukgw880M6u7pbvgWjr66uLKdjoEEvatC9lGyT03UU0,12806 -django/contrib/auth/middleware.py,sha256=_Y3pB-F4WhZdAZZMHL4iQ-TSBQrivkz2flALIjodXiM,5431 -django/contrib/auth/migrations/0001_initial.py,sha256=gos3cWdK6busgmjH4dUBR-S9_7m-LIYF7AtTIlGRFq4,7282 -django/contrib/auth/migrations/0002_alter_permission_name_max_length.py,sha256=Ofj3HGISrF35pqmD-VBr1rlbLTszxaePWXrULRDc0gM,347 -django/contrib/auth/migrations/0003_alter_user_email_max_length.py,sha256=7sFie4LLqpe74dZB6gjymc2PJ6Fm8R1i-_L_1Otr204,419 -django/contrib/auth/migrations/0004_alter_user_username_opts.py,sha256=8Yebbje2LQMo8RlvLhiuNxXT_Fjo60zEMSGwWTctD2A,881 -django/contrib/auth/migrations/0005_alter_user_last_login_null.py,sha256=NvFKl9IxcAEz2Oi21u3XSvkK1DI6ZYg0emW_7PQMqe8,411 -django/contrib/auth/migrations/0006_require_contenttypes_0002.py,sha256=EBEPNuSJtXBj58m2x-rr3WWCSc30ssHWI0Ni6_g9gtk,370 -django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py,sha256=2mOgZgr3in1UU8JAROculPKm_8hqSTbPPFV3XgFc_Ko,803 -django/contrib/auth/migrations/0008_alter_user_username_max_length.py,sha256=vKKsmPu5_6EqyIDrSRByKn9jhrd-_OGgSwQ-4-x_ZRQ,815 -django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py,sha256=T5-y4RVYe5R9dhrP2VvFrmruuNs-UJ8bHL45FIfQtjM,416 -django/contrib/auth/migrations/0010_alter_group_name_max_length.py,sha256=j7YCblkuVz50X1pZdMzRWXJSdwBrHEOt1kyVAe3AsqY,379 -django/contrib/auth/migrations/0011_update_proxy_permissions.py,sha256=Do06UrWbcE6d_tmqkhRfU8TspxSXi-TIMPcT1dGU57k,2879 -django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py,sha256=9AvEG8YCXFe4mm_bjxjp6-5IvIeou6b_QP7Kr5FTD-U,412 +django/contrib/auth/management/commands/changepassword.py,sha256=gBOnRnh8rHTbkW3i5A7BHsd7mLMunEYN0fK-VGuSt5U,2541 +django/contrib/auth/management/commands/createsuperuser.py,sha256=cUSI2CBlmIl48clgdbA78NP4OgxS1hqsjinXfJnN-vw,11457 +django/contrib/auth/middleware.py,sha256=uM_M3pXiyfjwWQFJRYdT1tsWm4R8wrq34Oks1FKcWck,5310 +django/contrib/auth/migrations/0001_initial.py,sha256=q5UGhGKIHnJD9gJOfnhHDVp3NWpH-NUMAD1mUIBGZ_U,4960 +django/contrib/auth/migrations/0002_alter_permission_name_max_length.py,sha256=xSlhMiUbrVCPMOwmwVNAUgYjZih3t-ieALNm7rQ1OI0,347 +django/contrib/auth/migrations/0003_alter_user_email_max_length.py,sha256=bPcpCTPAJV2NgrwEa6WFfxkhbPmj5J-EqU1HM3RXtq0,389 +django/contrib/auth/migrations/0004_alter_user_username_opts.py,sha256=aN0oHoA5q2bKpJN8SnI8T9GNtTBKzLRFozL87tNh8_I,785 +django/contrib/auth/migrations/0005_alter_user_last_login_null.py,sha256=0s9ZPGWNP9HT7TmXAuChMLLwL1Ml5SdQwNs9qfy5dN4,381 +django/contrib/auth/migrations/0006_require_contenttypes_0002.py,sha256=_S7o_MhU0lAnPhDEt0kh1sBmpCLXW88VBuATERiMBlk,370 +django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py,sha256=JeJpm_jyu2CbBckw4xJt0DlwQ4SDg2fyHqduRLZ1HFI,740 +django/contrib/auth/migrations/0008_alter_user_username_max_length.py,sha256=KpeVuknt_7WErQO_WLDSCMg1sJkXCXjNQ5I4u_l99kc,752 +django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py,sha256=rwLs5SDzFJsDKtCfyMP6XueUPHiRvRMein3wXMzHeDk,386 +django/contrib/auth/migrations/0010_alter_group_name_max_length.py,sha256=JQ2cqUnTooqDKlZ5LcXQDbQld9xQmC3up5_wCWn1LFg,379 +django/contrib/auth/migrations/0011_update_proxy_permissions.py,sha256=uSc1MAiLarJWy_SuoFAYrgUBoaTALUJ3Qq9Svqv5tZ0,2795 +django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py,sha256=b_Xd1QsaC5Gc4kuJ-fQ5zKdheVkj4Yd6Asmno8iNkKM,382 django/contrib/auth/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/auth/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/auth/migrations/__pycache__/0002_alter_permission_name_max_length.cpython-39.pyc,, @@ -1623,16 +1613,16 @@ django/contrib/auth/migrations/__pycache__/0010_alter_group_name_max_length.cpyt django/contrib/auth/migrations/__pycache__/0011_update_proxy_permissions.cpython-39.pyc,, django/contrib/auth/migrations/__pycache__/0012_alter_user_first_name_max_length.cpython-39.pyc,, django/contrib/auth/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/auth/mixins.py,sha256=rHq9HsX4W8lKtfXsazxM3chhTFLqd3eKI-OVKpbeLjQ,4652 -django/contrib/auth/models.py,sha256=o9nq-sR8W8eIJ2vlYfShx7o6mtj5x5_ZOuy5O42whS4,16158 -django/contrib/auth/password_validation.py,sha256=9-cOqtABxf9zW6uSM46RIAVqlHkNXiXyp9uN5ZjmcCQ,9379 +django/contrib/auth/mixins.py,sha256=YUGeNQFuJZr0L1tp9JovNWyxoPDR7SXO8TdxRXGqbTI,4501 +django/contrib/auth/models.py,sha256=Ahrr6TKvlHzUZRwplH_a4uir5VX40IcK-FqSDZKn3s8,15954 +django/contrib/auth/password_validation.py,sha256=RAMoa_8HHQZkJ_X9H3TTluCNvgGXL7CQbHbSiMu4yL8,7566 django/contrib/auth/signals.py,sha256=BFks70O0Y8s6p1fr8SCD4-yk2kjucv7HwTcdRUzVDFM,118 django/contrib/auth/templates/auth/widgets/read_only_password_hash.html,sha256=cMrG-iMsrVQ6Qd6T_Xz21b6WIWhXxaIwgNDW2NpDpuM,185 django/contrib/auth/templates/registration/password_reset_subject.txt,sha256=-TZcy_r0vArBgdPK7feeUY6mr9EkYwy7esQ62_onbBk,132 -django/contrib/auth/tokens.py,sha256=M5HB9H6D8JASBkN8SpDyuRsGe4ISyVheUC-Y9SRFk-M,3773 -django/contrib/auth/urls.py,sha256=Uh8DrSqpJXDA5a17Br9fMmIbEcgLkxdN9FvCRg-vxyg,1185 -django/contrib/auth/validators.py,sha256=N67vd_FgRCMKk9MXsXulprJPKA4UGPgSLWZcGb13yLw,687 -django/contrib/auth/views.py,sha256=fV8BiF3QnlQOGt0JJwSPs0jurTkjumoR0EFrQKX67yA,14071 +django/contrib/auth/tokens.py,sha256=1yWQXaDJK6PKbBb8-eqSHzreo0jOfcGtlbbFdr4bxNg,4587 +django/contrib/auth/urls.py,sha256=riSsFkaDjHBAi-OXwUSL-eqCkWivK_YvfZWb_pHEE7k,1049 +django/contrib/auth/validators.py,sha256=4SU1JF5Dc4A3WTbdc45PxGusO8r6rgztgG5oEb_JhKw,687 +django/contrib/auth/views.py,sha256=b48oMCGgdg2wg131_uobg_7mqnl_bksLyO3CosbwqrE,13466 django/contrib/contenttypes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/contenttypes/__pycache__/__init__.cpython-39.pyc,, django/contrib/contenttypes/__pycache__/admin.cpython-39.pyc,, @@ -1642,11 +1632,11 @@ django/contrib/contenttypes/__pycache__/fields.cpython-39.pyc,, django/contrib/contenttypes/__pycache__/forms.cpython-39.pyc,, django/contrib/contenttypes/__pycache__/models.cpython-39.pyc,, django/contrib/contenttypes/__pycache__/views.cpython-39.pyc,, -django/contrib/contenttypes/admin.py,sha256=a0KrlT8k2aPIKn54fNwCDTaAVdVr1fLY1BDz_FrE3ts,5200 -django/contrib/contenttypes/apps.py,sha256=1Q1mWjPvfYU7EaO50JvsWuDg_3uK8DoCwpvdIdT7iKY,846 -django/contrib/contenttypes/checks.py,sha256=KKB-4FOfPO60TM-uxqK8m9sIXzB3CRx7Imr-jaauM_U,1268 -django/contrib/contenttypes/fields.py,sha256=izgsYFgSWG43mi5LvXXUY9sz6z_iBMLEuslf_n7QJUI,28262 -django/contrib/contenttypes/forms.py,sha256=T6fZZkJjPrD6R3h5Wos2a9aDM3mZJLerHSh6NXHJp4I,3956 +django/contrib/contenttypes/admin.py,sha256=fKJoxKO6BtKbR_IiMRbU_eJiGWSedDYwyCNghAIDU84,4915 +django/contrib/contenttypes/apps.py,sha256=ueCXt0w5BCo8UYP7cV06kVb_8oD90g3PLp_42M1NbSM,851 +django/contrib/contenttypes/checks.py,sha256=ooW997jE1y5goWgO3dzc7tfJt5Z4tJPWRRSG1P1-AcU,1234 +django/contrib/contenttypes/fields.py,sha256=XNm_52qKE2AqotlCLT0THRxoO4luaUb4DetxruEhr6M,27526 +django/contrib/contenttypes/forms.py,sha256=pr-gnBUkL7WAcXqwpIrYbmm2Km5cdtfq5cKVOL0X9rE,3761 django/contrib/contenttypes/locale/af/LC_MESSAGES/django.mo,sha256=93nlniPFfVcxfBCs_PsLtMKrJ2BqpcofPRNYYTTlels,1070 django/contrib/contenttypes/locale/af/LC_MESSAGES/django.po,sha256=SY04sW55-xpO_qBjv8pHoN7eqB2C5q_9CxQguMz7Q94,1244 django/contrib/contenttypes/locale/ar/LC_MESSAGES/django.mo,sha256=2t3y_6wxi0khsYi6s9ZyJwjRB8bnRT1PKvazWOKhJcQ,1271 @@ -1659,8 +1649,8 @@ django/contrib/contenttypes/locale/az/LC_MESSAGES/django.mo,sha256=VTQ2qQ7aoZYUV django/contrib/contenttypes/locale/az/LC_MESSAGES/django.po,sha256=9NcmP1jMQPfjPraoXui6iqJn3z3f3uG1RYN7K5-_-dU,1359 django/contrib/contenttypes/locale/be/LC_MESSAGES/django.mo,sha256=Kp1TpXX1v0IgGp9HZxleXJ6y5ZvMZ6AqJrSIVcDs7xA,1353 django/contrib/contenttypes/locale/be/LC_MESSAGES/django.po,sha256=Oy5QXZBmBM_OYLT5OeXJQzTBCHXBp8NVMYuKmr_TUm0,1615 -django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo,sha256=IFghXuYj0yxP5j-LfRsNJXlyS2b2dUNJXD01uhUqxLg,1225 -django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po,sha256=y-OpKdDHxHDYATSmi8DAUXuhpIwgujKZUe48G8So8AU,1613 +django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo,sha256=yVH2saAhE3bVtamkCeIBDQuJpn2awfF2M7ISujswiRU,1267 +django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po,sha256=YdzC82ifG-pPY5Iy4mXIBj9Qq583g37OqZir-jpbUpc,1576 django/contrib/contenttypes/locale/bn/LC_MESSAGES/django.mo,sha256=2Z1GL6c1ukKQCMcls7R0_n4eNdH3YOXZSR8nCct7SLI,1201 django/contrib/contenttypes/locale/bn/LC_MESSAGES/django.po,sha256=PLjnppx0FxfGBQMuWVjo0N4sW2QYc2DAEMK6ziGWUc8,1491 django/contrib/contenttypes/locale/br/LC_MESSAGES/django.mo,sha256=kAlOemlwBvCdktgYoV-4NpC7XFDaIue_XN7GJYzDu88,1419 @@ -1742,13 +1732,13 @@ django/contrib/contenttypes/locale/it/LC_MESSAGES/django.po,sha256=njEgvhDwWOc-C django/contrib/contenttypes/locale/ja/LC_MESSAGES/django.mo,sha256=tVH6RvZ5tXz56lEM3aoJtFp5PKsSR-XXpi8ZNCHjiFw,1211 django/contrib/contenttypes/locale/ja/LC_MESSAGES/django.po,sha256=5_-Uo7Ia3X9gAWm2f72ezQnNr_pQzf6Ax4AUutULuZU,1534 django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.mo,sha256=1_yGL68sK0QG_mhwFAVdksiDlB57_1W5QkL7NGGE5L0,1429 -django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po,sha256=6iUBbKjXsIgrq7Dj_xhxzoxItSSSKwQjIZsDayefGr8,1654 +django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po,sha256=fr8rGQDWgUQSv-ZjXhSAR5P_zWLhQ7bq1cHLKIzY4bY,1649 django/contrib/contenttypes/locale/kk/LC_MESSAGES/django.mo,sha256=SNY0vydwLyR2ExofAHjmg1A2ykoLI7vU5Ryq-QFu5Gs,627 django/contrib/contenttypes/locale/kk/LC_MESSAGES/django.po,sha256=PU-NAl6xUEeGV0jvJx9siVBTZIzHywL7oKc4DgUjNkc,1130 django/contrib/contenttypes/locale/km/LC_MESSAGES/django.mo,sha256=BXifukxf48Lr0t0V3Y0GJUMhD1KiHN1wwbueoK0MW1A,678 django/contrib/contenttypes/locale/km/LC_MESSAGES/django.po,sha256=fTPlBbnaNbLZxjzJutGvqe33t6dWsEKiHQYaw27m7KQ,1123 django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.mo,sha256=a4sDGaiyiWn-1jFozYI4vdAvuHXrs8gbZErP_SAUk9Y,714 -django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po,sha256=A6Vss8JruQcPUKQvY-zaubVZDTLEPwHsnd_rXcyzQUs,1168 +django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po,sha256=QDD_q_loZtGRlhmaqgNDtJ_5AjVFQ8fSmypvaWLOwp4,1162 django/contrib/contenttypes/locale/ko/LC_MESSAGES/django.mo,sha256=myRfFxf2oKcbpmCboongTsL72RTM95nEmAC938M-ckE,1089 django/contrib/contenttypes/locale/ko/LC_MESSAGES/django.po,sha256=uui_LhgGTrW0uo4p-oKr4JUzhjvkLbFCqRVLNMrptzY,1383 django/contrib/contenttypes/locale/ky/LC_MESSAGES/django.mo,sha256=ULoIe36zGKPZZs113CenA6J9HviYcBOKagXrPGxyBUI,1182 @@ -1767,8 +1757,6 @@ django/contrib/contenttypes/locale/mn/LC_MESSAGES/django.mo,sha256=J6kKYjUOsQxpt django/contrib/contenttypes/locale/mn/LC_MESSAGES/django.po,sha256=x8aRJH2WQvMBBWlQt3T3vpV4yHeZXLmRTT1U0at4ZIk,1525 django/contrib/contenttypes/locale/mr/LC_MESSAGES/django.mo,sha256=2Z5jaGJzpiJTCnhCk8ulCDeAdj-WwR99scdHFPRoHoA,468 django/contrib/contenttypes/locale/mr/LC_MESSAGES/django.po,sha256=FgZKD9E-By0NztUnBM4llpR59K0MJSIMZIrJYGKDqpc,983 -django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.mo,sha256=EIwbOZ0QahW9AFFWRmRdKGKBtYYY_eTcfU4eqDVSVxw,1035 -django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.po,sha256=t7nKsOMxycn_CsXw2nIfU-owJRge3FAixgbTsDhffvo,1225 django/contrib/contenttypes/locale/my/LC_MESSAGES/django.mo,sha256=YYa2PFe9iJygqL-LZclfpgR6rBmIvx61JRpBkKS6Hrs,1554 django/contrib/contenttypes/locale/my/LC_MESSAGES/django.po,sha256=6F3nXd9mBc-msMchkC8OwAHME1x1O90xrsZp7xmynpU,1732 django/contrib/contenttypes/locale/nb/LC_MESSAGES/django.mo,sha256=EHU9Lm49U7WilR5u-Lq0Fg8ChR_OzOce4UyPlkZ6Zs4,1031 @@ -1777,8 +1765,8 @@ django/contrib/contenttypes/locale/ne/LC_MESSAGES/django.mo,sha256=-zZAn5cex4PkS django/contrib/contenttypes/locale/ne/LC_MESSAGES/django.po,sha256=1ZCUkulQ9Gxb50yMKFKWaTJli2SinBeNj0KpXkKpsNE,1519 django/contrib/contenttypes/locale/nl/LC_MESSAGES/django.mo,sha256=aXDHgg891TyTiMWNcbNaahfZQ2hqtl5yTkx5gNRocMU,1040 django/contrib/contenttypes/locale/nl/LC_MESSAGES/django.po,sha256=zDJ_vyQxhP0mP06U-e4p6Uj6v1g863s8oaxc0JIAMjg,1396 -django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo,sha256=a_X8e2lMieWwUtENJueBr8wMvkw6at0QSaWXd5AM6yQ,1040 -django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po,sha256=xFSirHUAKv78fWUpik6xv-6WQSEoUgN5jjPbTOy58C4,1317 +django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo,sha256=jfxiglKOxjX2xdbLDnJhujJiGcbDJv3NDcUUCWrZmuU,1054 +django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po,sha256=c1sz3ssHULL1c5gpbEOy4Xo2Nh0_2ar_Zg4nECouM4k,1299 django/contrib/contenttypes/locale/os/LC_MESSAGES/django.mo,sha256=QV533Wu-UpjV3XiCe83jlz7XGuwgRviV0ggoeMaIOIY,1116 django/contrib/contenttypes/locale/os/LC_MESSAGES/django.po,sha256=UZahnxo8z6oWJfEz4JNHGng0EAifXYtJupB6lx0JB60,1334 django/contrib/contenttypes/locale/pa/LC_MESSAGES/django.mo,sha256=qacd7eywof8rvJpstNfEmbHgvDiQ9gmkcyG7gfato8s,697 @@ -1793,8 +1781,8 @@ django/contrib/contenttypes/locale/ro/LC_MESSAGES/django.mo,sha256=sCthDD10v7GY2 django/contrib/contenttypes/locale/ro/LC_MESSAGES/django.po,sha256=n-BPEfua0Gd6FN0rsP7qAlTGbQEZ14NnDMA8jI2844Y,1407 django/contrib/contenttypes/locale/ru/LC_MESSAGES/django.mo,sha256=OSf206SFmVLULHmwVhTaRhWTQtyDKsxe03gIzuvAUnY,1345 django/contrib/contenttypes/locale/ru/LC_MESSAGES/django.po,sha256=xHyJYD66r8We3iN5Hqo69syWkjhz4zM7X9BWPIiI6mU,1718 -django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo,sha256=xf95XGPB9Tyz7p8JH1aqiY4BYMkug2cnN5gNNlHV7xU,1082 -django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po,sha256=wqbW-x6NEJU7nIAmYnKw9ncgmrcD3TKW7aPg7rIiX_M,1395 +django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo,sha256=Wkcfu7VTpa6IMqGHUH6Rra42ydbyyaLnMa6wg137E7o,1104 +django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po,sha256=oFmpjsUP8WXXd6TpObHcnM-mstebPAB4wCjsluH5EFc,1398 django/contrib/contenttypes/locale/sl/LC_MESSAGES/django.mo,sha256=sMML-ubI_9YdKptzeri1du8FOdKcEzJbe4Tt0J4ePFI,1147 django/contrib/contenttypes/locale/sl/LC_MESSAGES/django.po,sha256=0zxiyzRWWDNVpNNLlcwl-OLh5sLukma1vm-kYrGHYrE,1392 django/contrib/contenttypes/locale/sq/LC_MESSAGES/django.mo,sha256=jYDQH3OpY4Vx9hp6ISFMI88uxBa2GDQK0BkLGm8Qulk,1066 @@ -1833,20 +1821,20 @@ django/contrib/contenttypes/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=RviK0bqL django/contrib/contenttypes/locale/zh_Hans/LC_MESSAGES/django.po,sha256=vSKJDEQ_ANTj3-W8BFJd9u_QGdTMF12iS15rVgeujOs,1380 django/contrib/contenttypes/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=NMumOJ9dPX-7YjQH5Obm4Yj0-lnGXJmCMN5DGbsLQG4,1046 django/contrib/contenttypes/locale/zh_Hant/LC_MESSAGES/django.po,sha256=7WIqYRpcs986MjUsegqIido5k6HG8d3FVvkrOQCRVCI,1338 -django/contrib/contenttypes/management/__init__.py,sha256=ZVHVJAYi_jCIXxWUZSkxq0IDECe6bvbFsWayrqbutfc,4937 +django/contrib/contenttypes/management/__init__.py,sha256=TXx5LvsBtM-750d_ImI4zpHKrXmsfVVXSgOxwecW11Y,4850 django/contrib/contenttypes/management/__pycache__/__init__.cpython-39.pyc,, django/contrib/contenttypes/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/contenttypes/management/commands/__pycache__/__init__.cpython-39.pyc,, django/contrib/contenttypes/management/commands/__pycache__/remove_stale_contenttypes.cpython-39.pyc,, -django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py,sha256=lbHKtUIqAbOQf5cO02Xp3ZQjgsy6niCn1v9jvYKLgiY,4245 -django/contrib/contenttypes/migrations/0001_initial.py,sha256=6-NKdPKCI-2eJnQkspHtLV4pwFLxt8CC0oRPzCjaa6k,1435 -django/contrib/contenttypes/migrations/0002_remove_content_type_name.py,sha256=L2DGA1x-ZSP4Tr7d9jq1ysJOvR7nWhzMgAk1NFpYxGU,1150 +django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py,sha256=1wDE5cS2qIPc8qq6QeyhxKAPLXWFLIqajCnJuzaLhmY,3838 +django/contrib/contenttypes/migrations/0001_initial.py,sha256=o3bVVr-O_eUNiloAC1z-JIHDoCJQ4ifdA-6DhdVUrp8,1157 +django/contrib/contenttypes/migrations/0002_remove_content_type_name.py,sha256=4h1AUWSWAvwfEMAaopJZce-yNj1AVpCYFAk2E-Ur-wM,1103 django/contrib/contenttypes/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/contenttypes/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/contenttypes/migrations/__pycache__/0002_remove_content_type_name.cpython-39.pyc,, django/contrib/contenttypes/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/contenttypes/models.py,sha256=WNyzQh3HPwvJEdUdbg2uyWnQRH4UeBMmQotc19ZVW0A,6626 -django/contrib/contenttypes/views.py,sha256=HBoIbNpgHTQN5pH8mul77UMEMZHbbkEH_Qdln-XFgd0,3549 +django/contrib/contenttypes/models.py,sha256=kkLMgaQGfqBKEV-d7SKlU8Hik6dvu0uGBARADFmylN0,6662 +django/contrib/contenttypes/views.py,sha256=fnoup7g6T17YpfCkffdWehuaWlo-KPAZj0p7kkk7v1E,3549 django/contrib/flatpages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/flatpages/__pycache__/__init__.cpython-39.pyc,, django/contrib/flatpages/__pycache__/admin.cpython-39.pyc,, @@ -1857,9 +1845,9 @@ django/contrib/flatpages/__pycache__/models.cpython-39.pyc,, django/contrib/flatpages/__pycache__/sitemaps.cpython-39.pyc,, django/contrib/flatpages/__pycache__/urls.cpython-39.pyc,, django/contrib/flatpages/__pycache__/views.cpython-39.pyc,, -django/contrib/flatpages/admin.py,sha256=ynemOSDgvKoCfRFLXZrPwj27U0mPUXmxdrue7SOZeqQ,701 -django/contrib/flatpages/apps.py,sha256=_OlaDxWbMrUmFNCS4u-RnBsg67rCWs8Qzh_c58wvtXA,252 -django/contrib/flatpages/forms.py,sha256=MyuENmsP1Wn01frdVSug7JnabiwoHf8nm-PthAlcoQw,2493 +django/contrib/flatpages/admin.py,sha256=m_TsFRA36bunPrg2dSdxDJpWLfJkiaVmE3kcYAO9trY,654 +django/contrib/flatpages/apps.py,sha256=K07x0pM4R4Hy6GWG7EytmW2SpWONoNhadIORTP13Q_c,252 +django/contrib/flatpages/forms.py,sha256=XOqw37h_Itd4CU4qDk0K03Ql7y6oMkr-sC6Oj52YHZg,2420 django/contrib/flatpages/locale/af/LC_MESSAGES/django.mo,sha256=c0XEKXJYgpy2snfmWFPQqeYeVla1F5s_wXIBaioiyPc,2297 django/contrib/flatpages/locale/af/LC_MESSAGES/django.po,sha256=_psp14JfICDxrKx_mKF0uLnItkJPkCNMvrNOyE35nFw,2428 django/contrib/flatpages/locale/ar/LC_MESSAGES/django.mo,sha256=dBHaqsaKH9QOIZ0h2lIDph8l9Bv2UAcD-Hr9TAxj8Ac,2636 @@ -1872,8 +1860,8 @@ django/contrib/flatpages/locale/az/LC_MESSAGES/django.mo,sha256=6ID6KejChxQzsUT4 django/contrib/flatpages/locale/az/LC_MESSAGES/django.po,sha256=v7tkbuUUqkbUzXoOOWxS75TpvuMESqoZAEXDXisfbiA,2679 django/contrib/flatpages/locale/be/LC_MESSAGES/django.mo,sha256=mOQlbfwwIZiwWCrFStwag2irCwsGYsXIn6wZDsPRvyA,2978 django/contrib/flatpages/locale/be/LC_MESSAGES/django.po,sha256=wlIfhun5Jd6gxbkmmYPSIy_tzPVmSu4CjMwPzBNnvpo,3161 -django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo,sha256=9Un5mKtsAuNeYWFQKFkIyCpQquE6qVD3zIrFoq8sCDI,2802 -django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po,sha256=Vr6d-9XjgK4_eXdWY3FEpdTlCEGgbCv93bLGyMTE9hs,3104 +django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo,sha256=p3RZmS9PAqdlAmbc7UswSoG0t1eeuXYDp1WZ3mWfFow,2569 +django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po,sha256=DqRp9KTLxks9tNEXs2g_jvIp7dI92jXLkKNDNyLhHac,2779 django/contrib/flatpages/locale/bn/LC_MESSAGES/django.mo,sha256=2oK2Rm0UtAI7QFRwpUR5aE3-fOltE6kTilsTbah737Y,2988 django/contrib/flatpages/locale/bn/LC_MESSAGES/django.po,sha256=QrbX69iqXOD6oByLcgPkD1QzAkfthpfTjezIFQ-6kVg,3172 django/contrib/flatpages/locale/br/LC_MESSAGES/django.mo,sha256=SKbykdilX_NcpkVi_lHF8LouB2G49ZAzdF09xw49ERc,2433 @@ -1892,12 +1880,12 @@ django/contrib/flatpages/locale/de/LC_MESSAGES/django.mo,sha256=I4CHFzjYM_Wd-vuI django/contrib/flatpages/locale/de/LC_MESSAGES/django.po,sha256=P6tPVPumP9JwBIv-XXi1QQYJyj1PY3OWoM4yOAmgTRE,2592 django/contrib/flatpages/locale/dsb/LC_MESSAGES/django.mo,sha256=oTILSe5teHa9XTYWoamstpyPu02yb_xo8S0AtkP7WP8,2391 django/contrib/flatpages/locale/dsb/LC_MESSAGES/django.po,sha256=1xD2aH5alerranvee6QLZqgxDVXxHThXCHR4kOJAV48,2576 -django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo,sha256=LQ8qIGwzoKwewtLz_1NhnhEeR4dPx2rrQ_hAN4BF6Og,2864 -django/contrib/flatpages/locale/el/LC_MESSAGES/django.po,sha256=gbLO52fcZK7LoG5Rget2Aq5PTFoz467ackXpSsR81kY,3221 +django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo,sha256=WxBbtlMvLwH2e7KUP7RcrxgEHP4DC9MKiO_KLCuFbmc,2870 +django/contrib/flatpages/locale/el/LC_MESSAGES/django.po,sha256=oIgwZoftZQVOrfsTDdL8iN9CpPN7UdmkCfpFOJoNHt0,3141 django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/flatpages/locale/en/LC_MESSAGES/django.po,sha256=0bNWKiu-1MkHFJ_UWrCLhp9ENr-pHzBz1lkhBkkrhJM,2169 -django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo,sha256=dTt7KtwiEyMEKYVzkPSqs6VS0CiUfK7ISz2c6rV2erA,2210 -django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po,sha256=_V4RTf0JtmyU7DRQv7jIwtPJs05KA2THPid5nKQ0ego,2418 +django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo,sha256=cuifXT2XlF4c_bR6ECRhlraSZyA7q4ZLhUgwvW73miw,486 +django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po,sha256=ZMAJRrjovd_cdWvzkuEiJ-9ZU9rqRTwoA3x8uY2khcs,1533 django/contrib/flatpages/locale/en_GB/LC_MESSAGES/django.mo,sha256=7zyXYOsqFkUGxclW-VPPxrQTZKDuiYQ7MQJy4m8FClo,1989 django/contrib/flatpages/locale/en_GB/LC_MESSAGES/django.po,sha256=oHrBd6lVnO7-SdnO-Taa7iIyiqp_q2mQZjkuuU3Qa_s,2232 django/contrib/flatpages/locale/eo/LC_MESSAGES/django.mo,sha256=QsmIOjVlQGcgeAFTa8ND9Uuuihyl63OIJnFwh4MkvZ0,2032 @@ -1955,13 +1943,13 @@ django/contrib/flatpages/locale/it/LC_MESSAGES/django.po,sha256=ar8i-bTtAKhiXLUL django/contrib/flatpages/locale/ja/LC_MESSAGES/django.mo,sha256=Qax3t7FFRonMrszVEeiyQNMtYyWQB3dmOeeIklEmhAg,2469 django/contrib/flatpages/locale/ja/LC_MESSAGES/django.po,sha256=N6PBvnXLEWELKTx8nHm5KwydDuFFKq5pn6AIHsBSM5M,2848 django/contrib/flatpages/locale/ka/LC_MESSAGES/django.mo,sha256=R4OSbZ-lGxMdeJYsaXVXpo6-KSZWeKPuErKmEsUvEQE,3022 -django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po,sha256=TWKtkRamM6YD-4WMoqfZ7KY-ZPs5ny7G82Wst6vQRko,3306 +django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po,sha256=YCVnkX9uayvAQjYy_2jS7fYb36meoMJTKSc2lfoUbeM,3301 django/contrib/flatpages/locale/kk/LC_MESSAGES/django.mo,sha256=lMPryzUQr21Uy-NAGQhuIZjHz-4LfBHE_zxEc2_UPaw,2438 django/contrib/flatpages/locale/kk/LC_MESSAGES/django.po,sha256=3y9PbPw-Q8wM7tCq6u3KeYUT6pfTqcQwlNlSxpAXMxQ,2763 django/contrib/flatpages/locale/km/LC_MESSAGES/django.mo,sha256=FYRfhNSqBtavYb10sHZNfB-xwLwdZEfVEzX116nBs-k,1942 django/contrib/flatpages/locale/km/LC_MESSAGES/django.po,sha256=d2AfbR78U0rJqbFmJQvwiBl_QvYIeSwsPKEnfYM4JZA,2471 django/contrib/flatpages/locale/kn/LC_MESSAGES/django.mo,sha256=n5HCZEPYN_YIVCXrgA1qhxvfhZtDbhfiannJy5EkHkI,1902 -django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po,sha256=-CHwu13UuE2-Qg6poG949I_dw3YiPI9ZhMh5h2vP4xw,2443 +django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po,sha256=o9xnLjwDw7L49Mkyr8C6aQZ13Yq5MYx1JYXEtcIsiWU,2437 django/contrib/flatpages/locale/ko/LC_MESSAGES/django.mo,sha256=M-IInVdIH24ORarb-KgY60tEorJZgrThDfJQOxW-S0c,2304 django/contrib/flatpages/locale/ko/LC_MESSAGES/django.po,sha256=DjAtWVAN_fwOvZb-7CUSLtO8WN0Sr08z3jQLNqZ98wY,2746 django/contrib/flatpages/locale/ky/LC_MESSAGES/django.mo,sha256=WmdWR6dRgmJ-nqSzFDUETypf373fj62igDVHC4ww7hQ,2667 @@ -1980,8 +1968,6 @@ django/contrib/flatpages/locale/mn/LC_MESSAGES/django.mo,sha256=tqwROY6D-bJ4gbDQ django/contrib/flatpages/locale/mn/LC_MESSAGES/django.po,sha256=jqiBpFLXlptDyU4F8ZWbP61S4APSPh0-nuTpNOejA6c,3003 django/contrib/flatpages/locale/mr/LC_MESSAGES/django.mo,sha256=GvSfsp0Op7st6Ifd8zp8Cj4tTHoFMltQb4p64pebrqI,468 django/contrib/flatpages/locale/mr/LC_MESSAGES/django.po,sha256=sayU0AfVaSFpBj0dT32Ri55LRafQFUHLi03K06kI7gc,1515 -django/contrib/flatpages/locale/ms/LC_MESSAGES/django.mo,sha256=5t_67bMQhux6v6SSWqHfzzCgc6hm3olxgHAsKOMGGZU,2184 -django/contrib/flatpages/locale/ms/LC_MESSAGES/django.po,sha256=-ZzZ8lfAglGkO_BRYz1lRlywxaF1zZ28-Xv74O2nT04,2336 django/contrib/flatpages/locale/my/LC_MESSAGES/django.mo,sha256=OcbiA7tJPkyt_WNrqyvoFjHt7WL7tMGHV06AZSxzkho,507 django/contrib/flatpages/locale/my/LC_MESSAGES/django.po,sha256=EPWE566Vn7tax0PYUKq93vtydvmt-A4ooIau9Cwcdfc,1550 django/contrib/flatpages/locale/nb/LC_MESSAGES/django.mo,sha256=L_XICESZ0nywkk1dn6RqzdUbFTcR92ju-zHCT1g3iEg,2208 @@ -1990,8 +1976,8 @@ django/contrib/flatpages/locale/ne/LC_MESSAGES/django.mo,sha256=gDZKhcku1NVlSs5Z django/contrib/flatpages/locale/ne/LC_MESSAGES/django.po,sha256=GWlzsDaMsJkOvw2TidJOEf1Fvxx9WxGdGAtfZIHkHwk,2178 django/contrib/flatpages/locale/nl/LC_MESSAGES/django.mo,sha256=_yV_-SYYjpbo-rOHp8NlRzVHFPOSrfS-ndHOEJ9JP3Y,2231 django/contrib/flatpages/locale/nl/LC_MESSAGES/django.po,sha256=xUuxx2b4ZTCA-1RIdoMqykLgjLLkmpO4ur1Vh93IITU,2669 -django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo,sha256=sHkuZneEWo1TItSlarlnOUR7ERjc76bJfHUcuFgd9mQ,2256 -django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po,sha256=MpI9qkWqj4rud__xetuqCP-eFHUgMYJpfBhDnWRKPK4,2487 +django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo,sha256=A50zQJ-0YYPjPCeeEa-gwqA2N5eON13YW8SJZvtJBZc,1693 +django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po,sha256=H5hnBsH3sUdlPkMjxiqNnh8izcrTSAs6o-ywlNCTKtw,2119 django/contrib/flatpages/locale/os/LC_MESSAGES/django.mo,sha256=cXGTA5M229UFsgc7hEiI9vI9SEBrNQ8d3A0XrtazO6w,2329 django/contrib/flatpages/locale/os/LC_MESSAGES/django.po,sha256=m-qoTiKePeFviKGH1rJRjZRH-doJ2Fe4DcZ6W52rG8s,2546 django/contrib/flatpages/locale/pa/LC_MESSAGES/django.mo,sha256=69_ZsZ4nWlQ0krS6Mx3oL6c4sP5W9mx-yAmOhZOnjPU,903 @@ -2006,8 +1992,8 @@ django/contrib/flatpages/locale/ro/LC_MESSAGES/django.mo,sha256=oS3MXuRh2USyLOMr django/contrib/flatpages/locale/ro/LC_MESSAGES/django.po,sha256=UNKGNSZKS92pJDjxKDLqVUW87DKCWP4_Q51xS16IZl0,2632 django/contrib/flatpages/locale/ru/LC_MESSAGES/django.mo,sha256=AACtHEQuytEohUZVgk-o33O7rJTFAluq22VJOw5JqII,2934 django/contrib/flatpages/locale/ru/LC_MESSAGES/django.po,sha256=H6JOPAXNxji1oni9kfga_hNZevodStpEl0O6cDnZ148,3312 -django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo,sha256=8_NZkzRd3Bcewp4GiczCAjQshq5rl29TPEj1RbBPipo,2321 -django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po,sha256=qo9Xvr2whYmwtc1n39T_9ADcI3nP-t-jtVh2S51KkFQ,2601 +django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo,sha256=f_qbUdkwYKzg3DQT5x-ab883NUWF80gNMc7yekFctPM,2145 +django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po,sha256=OD_E2Z-nElhfFcsnuK8Y3r341OXjLON2CoWjNJfHIt8,2482 django/contrib/flatpages/locale/sl/LC_MESSAGES/django.mo,sha256=MBjwhw6wppQUl0Lb_rShXZj_Sq-JLSkdYU5Xhi0OtYY,2173 django/contrib/flatpages/locale/sl/LC_MESSAGES/django.po,sha256=6zbOXzkLTsdWRKAhuLzBVBc53n6MQKpvOeHw4cRrAlc,2400 django/contrib/flatpages/locale/sq/LC_MESSAGES/django.mo,sha256=Jv2sebdAM6CfiLzgi1b7rHo5hp-6_BFeeMQ4_BwYpjk,2328 @@ -2047,18 +2033,18 @@ django/contrib/flatpages/locale/zh_Hans/LC_MESSAGES/django.po,sha256=loi9RvOnrgF django/contrib/flatpages/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=Y5nDMQ3prLJ6OHuQEeEqjDLBC9_L-4XHDGJSLNoCgqg,2200 django/contrib/flatpages/locale/zh_Hant/LC_MESSAGES/django.po,sha256=6dKCSJpw_8gnunfTY86_apXdH5Pqe0kKYSVaqRtOIh0,2475 django/contrib/flatpages/middleware.py,sha256=aXeOeOkUmpdkGOyqZnkR-l1VrDQ161RWIWa3WPBhGac,784 -django/contrib/flatpages/migrations/0001_initial.py,sha256=hTnlVa-FRA-H5lNuNkT2CKhxDViGJ3xyeZaPR7qpWR0,2409 +django/contrib/flatpages/migrations/0001_initial.py,sha256=7lhJRTsJCQrf_jyKbg9VXcyjPIWJSqLir-WgKQjJcl8,1719 django/contrib/flatpages/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/flatpages/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/flatpages/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/flatpages/models.py,sha256=3ugRRsDwB5C3GHOWvtOzjJl-y0yqqjYZBSOMt24QYuw,1764 -django/contrib/flatpages/sitemaps.py,sha256=CEhZOsLwv3qIJ1hs4eHlE_0AAtYjicb_yRzsstY19eg,584 +django/contrib/flatpages/models.py,sha256=_CeWgWjhuD_y8FgMKpv9kvgolNz1on3DH0NkvJnwlOM,1742 +django/contrib/flatpages/sitemaps.py,sha256=0WGMLfr61H5aVX1inE4X_BJhx2b_lw4LKMO4OQGiDX4,554 django/contrib/flatpages/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/flatpages/templatetags/__pycache__/__init__.cpython-39.pyc,, django/contrib/flatpages/templatetags/__pycache__/flatpages.cpython-39.pyc,, -django/contrib/flatpages/templatetags/flatpages.py,sha256=QH-suzsoPIMSrgyHR9O8uOdmfIkBv_w3LM-hGfQvnU8,3552 -django/contrib/flatpages/urls.py,sha256=Rs37Ij192SOtSBjd4Lx9YtpINfEMg7XRY01dEOY8Rgg,179 -django/contrib/flatpages/views.py,sha256=H4LG7Janb6Dcn-zINLmp358hR60JigAKGzh4A4PMPaM,2724 +django/contrib/flatpages/templatetags/flatpages.py,sha256=q0wsGQqXHhSCH4_UR-wHkj_pJsxBOo_liODBT_BZcTc,3561 +django/contrib/flatpages/urls.py,sha256=v_bP8Axlf0XLgb2kJVdEPDqW8WY7RkwSwm7_BH_0eWE,179 +django/contrib/flatpages/views.py,sha256=ywkDuZHZwu_kZx6frjAFt7MAB3mo6-mLicyByw13EfY,2723 django/contrib/gis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/__pycache__/apps.cpython-39.pyc,, @@ -2068,13 +2054,13 @@ django/contrib/gis/__pycache__/measure.cpython-39.pyc,, django/contrib/gis/__pycache__/ptr.cpython-39.pyc,, django/contrib/gis/__pycache__/shortcuts.cpython-39.pyc,, django/contrib/gis/__pycache__/views.cpython-39.pyc,, -django/contrib/gis/admin/__init__.py,sha256=4Y8GtbXZ5kmJutSADVitiMByz3Z96wmXleF9bUC5L3w,672 +django/contrib/gis/admin/__init__.py,sha256=fFeTcK_K9Djj7J7aA93DdHqpgEi2xDecTNH5LavSG8Q,524 django/contrib/gis/admin/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/admin/__pycache__/options.cpython-39.pyc,, django/contrib/gis/admin/__pycache__/widgets.cpython-39.pyc,, -django/contrib/gis/admin/options.py,sha256=7dR6t_kD3yma_pcz8gwrudWiKbaIkIh6cFX7T5lqoWU,6390 -django/contrib/gis/admin/widgets.py,sha256=_PR7FeyClESjUabCLAdynewYufTFSw5iJhYcuQNJ4C4,4737 -django/contrib/gis/apps.py,sha256=dbAFKx9jj9_QdhdNfL5KCC47puH_ZTw098jsJFwDO9Y,417 +django/contrib/gis/admin/options.py,sha256=z4UrI7Pzb73FsT2WgIMX9zsMG_Hg6g89vkkvgKPHOz8,5145 +django/contrib/gis/admin/widgets.py,sha256=_X3Li-k9q0m7soBvu0Vu3jwwmODZWTx9A3IswYKeXLM,4720 +django/contrib/gis/apps.py,sha256=bqwbH70dnlB1MBr6USXiIZ3GxYCKuR1aAkezb5dwgKE,395 django/contrib/gis/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/db/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -2086,10 +2072,10 @@ django/contrib/gis/db/backends/base/__pycache__/adapter.cpython-39.pyc,, django/contrib/gis/db/backends/base/__pycache__/features.cpython-39.pyc,, django/contrib/gis/db/backends/base/__pycache__/models.cpython-39.pyc,, django/contrib/gis/db/backends/base/__pycache__/operations.cpython-39.pyc,, -django/contrib/gis/db/backends/base/adapter.py,sha256=qbLG-sLB6EZ_sA6-E_uIClyp5E5hz9UQ-CsR3BWx8W8,592 -django/contrib/gis/db/backends/base/features.py,sha256=fF-AKB6__RjkxVRadNkOP7Av4wMaRGkXKybYV6ES2Gk,3718 -django/contrib/gis/db/backends/base/models.py,sha256=WqpmVLqK21m9J6k_N-SGPXq1VZMuNHafyB9xqxUwR4k,4009 -django/contrib/gis/db/backends/base/operations.py,sha256=SHW9YHjDexxhUN_BMQ85lGND5mLQwNQOJ6UQUXdNDOs,6778 +django/contrib/gis/db/backends/base/adapter.py,sha256=sdZlHGUfz2eSPsTjlO9xBoGSz7BqKD_NhURW8fzz26o,579 +django/contrib/gis/db/backends/base/features.py,sha256=6ZAZlkhWMuK-0Dh15sKopLRCz9v2E282avWR4miw3ks,3718 +django/contrib/gis/db/backends/base/models.py,sha256=vkDweNsExmKWkHNSae9G6P-fT-SMdIgHZ85i31ihXg0,3962 +django/contrib/gis/db/backends/base/operations.py,sha256=gEsPQMT49P6nOZh8bCbbOtdoosLoKvioYBJj1TuLntg,6264 django/contrib/gis/db/backends/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/db/backends/mysql/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/backends/mysql/__pycache__/base.cpython-39.pyc,, @@ -2097,11 +2083,11 @@ django/contrib/gis/db/backends/mysql/__pycache__/features.cpython-39.pyc,, django/contrib/gis/db/backends/mysql/__pycache__/introspection.cpython-39.pyc,, django/contrib/gis/db/backends/mysql/__pycache__/operations.cpython-39.pyc,, django/contrib/gis/db/backends/mysql/__pycache__/schema.cpython-39.pyc,, -django/contrib/gis/db/backends/mysql/base.py,sha256=z75wKhm-e9JfRLCvgDq-iv9OqOjBBAS238JTTrWfHRQ,498 -django/contrib/gis/db/backends/mysql/features.py,sha256=AtlQM3bi4kTxYA-PIg2RGedGFTNeBDvF6_MNhaymWTc,1529 -django/contrib/gis/db/backends/mysql/introspection.py,sha256=zRwqQ3v1-GZ94Y35zdkon0raFduDs6KWquNcOXZQPhk,1807 -django/contrib/gis/db/backends/mysql/operations.py,sha256=2anxu0etylypmJadf6UwrhhKgRuXSwoKem3wneJ4rTs,4330 -django/contrib/gis/db/backends/mysql/schema.py,sha256=4hegMdTi5jTjVP3WtZPZ8grQYsBDnroS4qkKOeYPgYE,3070 +django/contrib/gis/db/backends/mysql/base.py,sha256=rz8tnvXJlY4V6liWxYshuxQE-uTNuKSBogCz_GtXoaY,507 +django/contrib/gis/db/backends/mysql/features.py,sha256=Yv8V7FrMCLLT_Uri7J6iXWIOgGelv4tUzCA26LiAhlc,1481 +django/contrib/gis/db/backends/mysql/introspection.py,sha256=QuoJOaHeTxqr0eju8HWA5AmzGYpC15Kt9U5uCNxJWHA,1834 +django/contrib/gis/db/backends/mysql/operations.py,sha256=Gmeyln2BjxjFbXYAsXANXZUH5A3nOD9JQIERQ6pRvH8,4098 +django/contrib/gis/db/backends/mysql/schema.py,sha256=B86TeF5hvlmLzgY7TFZGTKaIzVbK87ByPmjhNz83JTA,2976 django/contrib/gis/db/backends/oracle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/db/backends/oracle/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/backends/oracle/__pycache__/adapter.cpython-39.pyc,, @@ -2111,13 +2097,13 @@ django/contrib/gis/db/backends/oracle/__pycache__/introspection.cpython-39.pyc,, django/contrib/gis/db/backends/oracle/__pycache__/models.cpython-39.pyc,, django/contrib/gis/db/backends/oracle/__pycache__/operations.cpython-39.pyc,, django/contrib/gis/db/backends/oracle/__pycache__/schema.cpython-39.pyc,, -django/contrib/gis/db/backends/oracle/adapter.py,sha256=IB5C_zBe_yvbZ-w71kuh_A77sGESuJOUbxGTFKEHDw4,2025 -django/contrib/gis/db/backends/oracle/base.py,sha256=_7qhvEdbnrJQEKL51sg8YYu8kRYmQNAlBgNb2OUbBkw,507 -django/contrib/gis/db/backends/oracle/features.py,sha256=HGnfEoauV5O_gNxcbnzDqLM3QAXU1cESg7AF2t4ZPg0,566 -django/contrib/gis/db/backends/oracle/introspection.py,sha256=51_nz8_OKGP1TCw44no20Vt6EV1B9MTKu8irSnkqZBo,1890 -django/contrib/gis/db/backends/oracle/models.py,sha256=7mij7owmmwqAl-4rPJmEU_zW3hZZI0hix7HyFOwJkms,2084 -django/contrib/gis/db/backends/oracle/operations.py,sha256=x6zraAyoMZa8rdcLAc2k35rprJxBAoqzDSdFYMcm52k,8652 -django/contrib/gis/db/backends/oracle/schema.py,sha256=XQybscqUYVRAS9H_5tidC2wz-SAraOrX9VQgdgeYw0w,4282 +django/contrib/gis/db/backends/oracle/adapter.py,sha256=4S7lzuIOxOFm1MTE3TtLGqs3tfRXp34vRb_wI0gg4LQ,2043 +django/contrib/gis/db/backends/oracle/base.py,sha256=NQYlEvE4ioobvMd7u2WC7vMtDiRq_KtilGprD6qfJCo,516 +django/contrib/gis/db/backends/oracle/features.py,sha256=x6IeC2cw3DdB_4abrTQNQyUfWiW403E7R8GLMe8NMas,566 +django/contrib/gis/db/backends/oracle/introspection.py,sha256=EfGUexqpa3yDX3IQ4PVx9AjVX8qY9djZtFLwdiqyNL8,1889 +django/contrib/gis/db/backends/oracle/models.py,sha256=pT32f_A1FRYwO5hWMigX7PU_ojpRmIhdUlhOqdz2R9k,2084 +django/contrib/gis/db/backends/oracle/operations.py,sha256=8zhgMQamyeBXLEGW8a1Sj2x5iqgX1KH4rCyxAgGJD_w,8468 +django/contrib/gis/db/backends/oracle/schema.py,sha256=EJOTAG4rPrnOMAWmwecnVwFSwmJOjZS5R_p48NybDi0,3909 django/contrib/gis/db/backends/postgis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/db/backends/postgis/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/backends/postgis/__pycache__/adapter.cpython-39.pyc,, @@ -2129,15 +2115,15 @@ django/contrib/gis/db/backends/postgis/__pycache__/models.cpython-39.pyc,, django/contrib/gis/db/backends/postgis/__pycache__/operations.cpython-39.pyc,, django/contrib/gis/db/backends/postgis/__pycache__/pgraster.cpython-39.pyc,, django/contrib/gis/db/backends/postgis/__pycache__/schema.cpython-39.pyc,, -django/contrib/gis/db/backends/postgis/adapter.py,sha256=zAesRYPyQ5vQJpHrBJr6YDiKE6oTlZXG1eatfR-EGNo,2219 -django/contrib/gis/db/backends/postgis/base.py,sha256=Ai3kvz6JqH1HqJG_XqjfnZOX26Uo7qmwbUPavudCcTI,937 -django/contrib/gis/db/backends/postgis/const.py,sha256=_ODq71ixhGpojzbO1DAWs5O4REFgzruIpQkNhPw9O-E,2007 +django/contrib/gis/db/backends/postgis/adapter.py,sha256=jhXBRB4TCl48h2bjLDyMKeY2kWXSZB3GcOTcz01JFYE,2188 +django/contrib/gis/db/backends/postgis/base.py,sha256=sFCNoMHRzd-a_MRc9hv-tyVHEODmGveyIopbP6CTPCg,937 +django/contrib/gis/db/backends/postgis/const.py,sha256=CMe_bpzcOcYakC3mu64EKfF2HgRxBT4yhoRX6zg3O_k,1967 django/contrib/gis/db/backends/postgis/features.py,sha256=GuBG7N5_zUuyf49ZIOLMI0AxQciUMav5A1VxAmWlCuk,457 -django/contrib/gis/db/backends/postgis/introspection.py,sha256=ihrNd_qHQ64DRjoaPj9-1a0y3H8Ko4gWbK2N5fDA3_g,3164 -django/contrib/gis/db/backends/postgis/models.py,sha256=nFFshpCS4Az4js853MuZxdsp_SOOIlghjuu2XZEeB-Y,2002 -django/contrib/gis/db/backends/postgis/operations.py,sha256=BCp87C5Sc6xT29xdbi1DljYXQ-jqhNloq-ssHgkKbNU,16209 -django/contrib/gis/db/backends/postgis/pgraster.py,sha256=_cxT4yPT4123YdIs5M8_gV4ela-YdGd-B3fGISSD-5M,4658 -django/contrib/gis/db/backends/postgis/schema.py,sha256=gA5w4fEwKqP-S52OEYDLXSFD8MWCBKnAAXD6-ySMfE8,2879 +django/contrib/gis/db/backends/postgis/introspection.py,sha256=htz45PonMVDsdiSLsQJg4xOlysaXdaXdyjiDNJxm6WI,2977 +django/contrib/gis/db/backends/postgis/models.py,sha256=tKiRZzO6p2YJnPbPXReMlFcAiFij-C_H_6w8FHhLqxk,2000 +django/contrib/gis/db/backends/postgis/operations.py,sha256=_Faugb1A4-cfoH89bDynpH13NPevfruCMXlwRAUYNuQ,15778 +django/contrib/gis/db/backends/postgis/pgraster.py,sha256=nVS1pSMQFKffKcJNNvHMWDX8HxcYRIWG4RvK9fiwbH8,4558 +django/contrib/gis/db/backends/postgis/schema.py,sha256=5z4OlLjQGwVE-cLNHFhLVK8XsL-5Z6ixfmYRoUvwF7A,2921 django/contrib/gis/db/backends/spatialite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/db/backends/spatialite/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/backends/spatialite/__pycache__/adapter.cpython-39.pyc,, @@ -2148,40 +2134,40 @@ django/contrib/gis/db/backends/spatialite/__pycache__/introspection.cpython-39.p django/contrib/gis/db/backends/spatialite/__pycache__/models.cpython-39.pyc,, django/contrib/gis/db/backends/spatialite/__pycache__/operations.cpython-39.pyc,, django/contrib/gis/db/backends/spatialite/__pycache__/schema.cpython-39.pyc,, -django/contrib/gis/db/backends/spatialite/adapter.py,sha256=qTiA5BBGUFND3D7xGK_85oo__HSexTH32XF4uin3ZV0,318 -django/contrib/gis/db/backends/spatialite/base.py,sha256=wU1fgp68CLyKELsMfO6zYM85ox4g_GloWESEK8EPrfM,3218 -django/contrib/gis/db/backends/spatialite/client.py,sha256=dNM7mqDyTzFlgQR1XhqZIftnR9VRH7AfcSvvy4vucEs,138 -django/contrib/gis/db/backends/spatialite/features.py,sha256=zkmJPExFtRqjRj608ZTlsSpxkYaPbV3A3SEfX3PcaFY,876 -django/contrib/gis/db/backends/spatialite/introspection.py,sha256=V_iwkz0zyF1U-AKq-UlxvyDImqQCsitcmvxk2cUw81A,3118 -django/contrib/gis/db/backends/spatialite/models.py,sha256=Of5O1At0W9wQ5PPLVpO0LWth2KDCOJt6Cfz5_OwaYR0,1930 -django/contrib/gis/db/backends/spatialite/operations.py,sha256=me6dv6Ejt5VQ2-lkiQC3TkiT-DABB92YqY9z7AcEra0,8308 -django/contrib/gis/db/backends/spatialite/schema.py,sha256=Uqo4Zp3q_HlmdjTWXvMAVn4_p5piK35iJ7UGXzqQ0Hc,7204 -django/contrib/gis/db/backends/utils.py,sha256=rLwSv79tKJPxvDHACY8rhPDLFZC79mEIlIySTyl_qqc,785 -django/contrib/gis/db/models/__init__.py,sha256=TrCS27JdVa-Q7Hok-YaJxb4eLrPdyvRmasJGIu05fvA,865 +django/contrib/gis/db/backends/spatialite/adapter.py,sha256=y74p_UEgLtoYjNZEi72mwcJOh_b-MzJ7sZd68WJXBiY,317 +django/contrib/gis/db/backends/spatialite/base.py,sha256=pg7m0arvmnwOsDJo-Mj9NudCclRMThEhQzDBjQWQLNI,3011 +django/contrib/gis/db/backends/spatialite/client.py,sha256=NsqD2vAnfjqn_FbQnCQeAqbGyZf9oa6gl7EPsMTPf8c,138 +django/contrib/gis/db/backends/spatialite/features.py,sha256=Wid-w2AJTAtyNgMWUtdCCmWN5Dbj2jEfrgLI497TMVk,836 +django/contrib/gis/db/backends/spatialite/introspection.py,sha256=NQ2T3GsDYBrbTiVzjWPp_RElKMP-qNxUiGEnOFZTSrg,3076 +django/contrib/gis/db/backends/spatialite/models.py,sha256=iiodcKYWAMIz_xrJagr-1nbiiO2YJY_Q0vt_0uyaD54,1928 +django/contrib/gis/db/backends/spatialite/operations.py,sha256=p6VKrtXtOjngk7XZYqwnli7L91VCf1TWqjoSAvT6kMw,7857 +django/contrib/gis/db/backends/spatialite/schema.py,sha256=yGarSHxvb0f7pZ2CP5DnkhB4P2Pt14j6qfZuULZE4Sk,6800 +django/contrib/gis/db/backends/utils.py,sha256=y4q0N0oDplot6dZQIFnjGPqVsTiGyLTmEMt5-xj-2b4,784 +django/contrib/gis/db/models/__init__.py,sha256=BR3kQAefIv4O1NksiVCUShwlSO4OCNoUGan6dCRGIyU,817 django/contrib/gis/db/models/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/models/__pycache__/aggregates.cpython-39.pyc,, django/contrib/gis/db/models/__pycache__/fields.cpython-39.pyc,, django/contrib/gis/db/models/__pycache__/functions.cpython-39.pyc,, django/contrib/gis/db/models/__pycache__/lookups.cpython-39.pyc,, django/contrib/gis/db/models/__pycache__/proxy.cpython-39.pyc,, -django/contrib/gis/db/models/aggregates.py,sha256=kM-GKfjwurd7D3P6sDbkEpZXBaocqobcSarQ89OEJko,2969 -django/contrib/gis/db/models/fields.py,sha256=n40s9HYbqVpFKIW9b4X4IQ8INWUus7QZi5QdiWVPsTI,14312 -django/contrib/gis/db/models/functions.py,sha256=v-DSLE-QfBCghNn-M2ZzXbKEoyMDulS581CIeSBICvg,18259 -django/contrib/gis/db/models/lookups.py,sha256=1raEdKM1m7e2rdMRZ4g30UKzLieJ1QCXcAdeAyuH1LA,11798 -django/contrib/gis/db/models/proxy.py,sha256=o2wXW3sFIWhjhkSrzrwFaCdatvZLF8Z5Zs3s1ugmriA,3173 -django/contrib/gis/db/models/sql/__init__.py,sha256=-rzcC3izMJi2bnvyQUCMzIOrigBnY6N_5EQIim4wCSY,134 +django/contrib/gis/db/models/aggregates.py,sha256=dGTRWMPhKO94XNf8U8VDoiwuYWOtaxQEYXhumCCdHqM,2832 +django/contrib/gis/db/models/fields.py,sha256=DsBUuL8kSx2kZ6b5DuEHCuwqQFzmRJ2_lsKGPRb-MAA,13824 +django/contrib/gis/db/models/functions.py,sha256=IBVa0k51r-uE3HtSqRuoqBuMUhugLLfn0Zu7B4feTSo,17493 +django/contrib/gis/db/models/lookups.py,sha256=3zvAOFS0qy3vr5ZGWk5Vq8so5yPPgrLwTJoJHCDzXfU,11491 +django/contrib/gis/db/models/proxy.py,sha256=BSZoCQ1IG8n_M6dSOdF3wAzIHfMElSVnIGu8ZWj1-_0,3122 +django/contrib/gis/db/models/sql/__init__.py,sha256=oYJYL-5DAO-DIcpIQ7Jmeq_cuKapRB83V1KLVIs_5iU,139 django/contrib/gis/db/models/sql/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/db/models/sql/__pycache__/conversion.cpython-39.pyc,, -django/contrib/gis/db/models/sql/conversion.py,sha256=AZLJCMSw_svSLQPB5LTvA-YRFnMZSXYdHdvPSTFmK4Y,2432 -django/contrib/gis/feeds.py,sha256=0vNVVScIww13bOxvlQfXAOCItIOGWSXroKKl6QXGB58,5995 -django/contrib/gis/forms/__init__.py,sha256=Zyid_YlZzHUcMYkfGX1GewmPPDNc0ni7HyXKDTeIkjo,318 +django/contrib/gis/db/models/sql/conversion.py,sha256=gG1mTUWb33YK_Uf1ZJRg5MRhkCTLtgajD3xxi7thODA,2400 +django/contrib/gis/feeds.py,sha256=43TmSa40LR3LguE4VDeBThJZgO_rbtfrT5Y6DQ7RBiQ,5732 +django/contrib/gis/forms/__init__.py,sha256=fREam1OSkDWr9ugUMNZMFn8Y9TufpRCn3Glj14DTMbQ,298 django/contrib/gis/forms/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/forms/__pycache__/fields.cpython-39.pyc,, django/contrib/gis/forms/__pycache__/widgets.cpython-39.pyc,, -django/contrib/gis/forms/fields.py,sha256=FrZaZWXFUdWK1QEu8wlda3u6EtqaVHjQRYrSKKu66PA,4608 -django/contrib/gis/forms/widgets.py,sha256=JYsXnfglVDttWxUbpd6GcoliXDwYUenVB2NfCfQo6iw,4000 +django/contrib/gis/forms/fields.py,sha256=iFXKmHxs5c74Q_O9cLXlTyPm8aLQyoq_PerOrihvuWQ,4483 +django/contrib/gis/forms/widgets.py,sha256=Ksh8NEaoBO1vsrbJusvxACCuEb9wnrWNISmal3fkbCo,3816 django/contrib/gis/gdal/LICENSE,sha256=VwoEWoNyts1qAOMOuv6OPo38Cn_j1O8sxfFtQZ62Ous,1526 -django/contrib/gis/gdal/__init__.py,sha256=m5cRj_qvD3jbLDjMk0ggDxW_hifeZ-CbtRtHZUIsRiQ,1827 +django/contrib/gis/gdal/__init__.py,sha256=UCuq9p1azua2uui6zycmyhwiRYtvyhX0UzZ0pu5z364,1793 django/contrib/gis/gdal/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/gdal/__pycache__/base.cpython-39.pyc,, django/contrib/gis/gdal/__pycache__/datasource.cpython-39.pyc,, @@ -2196,16 +2182,16 @@ django/contrib/gis/gdal/__pycache__/layer.cpython-39.pyc,, django/contrib/gis/gdal/__pycache__/libgdal.cpython-39.pyc,, django/contrib/gis/gdal/__pycache__/srs.cpython-39.pyc,, django/contrib/gis/gdal/base.py,sha256=yymyL0vZRMBfiFUzrehvaeaunIxMH5ucGjPRfKj-rAo,181 -django/contrib/gis/gdal/datasource.py,sha256=OkvQNRQXlKExw181GqD7JKElGGrSTI3jwhNZf1DkGNU,4606 -django/contrib/gis/gdal/driver.py,sha256=eCzrqEVOwyTlcRItrUirmEdNaSrsAIvw9jP_Z669xds,3351 -django/contrib/gis/gdal/envelope.py,sha256=Aj3Qn33QWjDYrwX1je2AZOmokffzs-s4kD96HL1easQ,7323 -django/contrib/gis/gdal/error.py,sha256=Vt-Uis9z786UGE3tD7fjiH8_0P5HSTO81n4fad4l6kw,1578 -django/contrib/gis/gdal/feature.py,sha256=HPWoCZjwzsUnhc7QmKh-BBMRqJCjj07RcFI6vjbdnp4,4017 -django/contrib/gis/gdal/field.py,sha256=2v1ouT_nxL_OkVsoIl1u79TaJCK3ib0M4gL_Q_uoFeo,6927 -django/contrib/gis/gdal/geometries.py,sha256=tYXqoHD0kY8LWN1SVcabj15kfeXy2WTQW9zKIeR8-iQ,24346 -django/contrib/gis/gdal/geomtype.py,sha256=VD_w5GymdaKJwgBW1cq2Xjtl3EVXCvJh26LIlKgW_PM,3071 -django/contrib/gis/gdal/layer.py,sha256=PygAgsRZzWekp6kq6NEAZ6vhQTSo1Nk4c1Yi_pOdK58,8825 -django/contrib/gis/gdal/libgdal.py,sha256=q8cknqkr32TU6uKCuDD9kWXsLtx3lV0juhqBwc7xWKE,3577 +django/contrib/gis/gdal/datasource.py,sha256=HDGVVh_6S-mQOIKiVGtmjL8djSpwAg3oZTqUIyWIaL0,4523 +django/contrib/gis/gdal/driver.py,sha256=E7Jj4z3z--WC2Idm5GvYtDGGErdtm1tAqzN8Lil-yRg,3264 +django/contrib/gis/gdal/envelope.py,sha256=lL13BYlaEyxDNkCJCPnFZk13eyRb9pOkOOrAdP16Qtw,6970 +django/contrib/gis/gdal/error.py,sha256=yv9yvtBPjLWRqQHlzglF-gLDW-nR7zF_F5xsej_oBx4,1576 +django/contrib/gis/gdal/feature.py,sha256=KYGyQYNWXrEJm2I0eIG1Kcd7WTOZWiC-asIjF5DmO9I,3926 +django/contrib/gis/gdal/field.py,sha256=AerdJ9sLeru9Z39PEtTAXp14vabMcwX_LIZjg0EyDAE,6626 +django/contrib/gis/gdal/geometries.py,sha256=VAcd9Kcy971d6_6ORZpdHhXeUjb3s0v_RmSY6U9WH8Y,23869 +django/contrib/gis/gdal/geomtype.py,sha256=hCHfxQsecBakIZUDZwEkECdH7dg3CdF4Y_kAFYkW9Og,3071 +django/contrib/gis/gdal/layer.py,sha256=2PPP3lpmljIA-KcuN1FI5dNQPkELR3eyPmarP2KYfYk,8527 +django/contrib/gis/gdal/libgdal.py,sha256=qv9Tp0DwbTemfriVe7UzrwV8ikq1yNxb1kNt0nHXtAA,3431 django/contrib/gis/gdal/prototypes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/gdal/prototypes/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/gdal/prototypes/__pycache__/ds.cpython-39.pyc,, @@ -2214,32 +2200,32 @@ django/contrib/gis/gdal/prototypes/__pycache__/generation.cpython-39.pyc,, django/contrib/gis/gdal/prototypes/__pycache__/geom.cpython-39.pyc,, django/contrib/gis/gdal/prototypes/__pycache__/raster.cpython-39.pyc,, django/contrib/gis/gdal/prototypes/__pycache__/srs.cpython-39.pyc,, -django/contrib/gis/gdal/prototypes/ds.py,sha256=7ITRVx-ewxVFzLNV5DwBoqTCNfb74FhrtrnWyAjaTGU,4650 -django/contrib/gis/gdal/prototypes/errcheck.py,sha256=wlRqrVnozMingrYIBH_9oMMzY9DMrX00BYzP_n54iu0,4173 -django/contrib/gis/gdal/prototypes/generation.py,sha256=c4m3x0QkDhDDaYxavGcvMLs3RNNb9EzfKTzHudWF1f8,4889 -django/contrib/gis/gdal/prototypes/geom.py,sha256=LjygKS-WbNMXj4Y8kaYGSn0OU5-UlQpjCmpmj3aPjhY,5046 -django/contrib/gis/gdal/prototypes/raster.py,sha256=HPLc2gAsGRhNwkjTgtZzHdjWG8LKbcSdwRl1A3qjQDk,5994 -django/contrib/gis/gdal/prototypes/srs.py,sha256=o103FkuUlAjk4drsHuT1JpOLp1wfTAIG_aPYj9QnIS0,3785 +django/contrib/gis/gdal/prototypes/ds.py,sha256=GnxQ4229MOZ5NQjJTtmCcstxGPH6HhUd9AsCWsih6_s,4586 +django/contrib/gis/gdal/prototypes/errcheck.py,sha256=ckjyqcZtrVZctrw-HvQb1isDavhUAblLqKuno9U4upw,4137 +django/contrib/gis/gdal/prototypes/generation.py,sha256=9UdPSqWR28AsUG7HDdHMRG2nI1-iKr1ru1V998uifP8,4867 +django/contrib/gis/gdal/prototypes/geom.py,sha256=ELRO7bR8RxO3HIuxtitr06yhsG4DxYTlRsTa6NenTqI,4946 +django/contrib/gis/gdal/prototypes/raster.py,sha256=zPIc-Vahtau1XQTADqxQNtzcAv6LunbhVHkWkMOEWKo,5690 +django/contrib/gis/gdal/prototypes/srs.py,sha256=zu98VgVLNI_lwTpq9YQ1Ht1F9oOkBqInKztmS8V7d5Y,3685 django/contrib/gis/gdal/raster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/gdal/raster/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/gdal/raster/__pycache__/band.cpython-39.pyc,, django/contrib/gis/gdal/raster/__pycache__/base.cpython-39.pyc,, django/contrib/gis/gdal/raster/__pycache__/const.cpython-39.pyc,, django/contrib/gis/gdal/raster/__pycache__/source.cpython-39.pyc,, -django/contrib/gis/gdal/raster/band.py,sha256=RPdut6BeQ9vW71rrPMwb2CnXrbCys8YAt1BA8Aholy0,8343 -django/contrib/gis/gdal/raster/base.py,sha256=2GGlL919lPr7YVGFtdIynLPIH-QKYhzrUpoXwVRlM1k,2882 -django/contrib/gis/gdal/raster/const.py,sha256=xBoMW6PeykWg3_IfVIEaGdrKTahxCMENCtDVzHOB8V8,2981 -django/contrib/gis/gdal/raster/source.py,sha256=yUh6gpu04EyynuDubIhPXAVSCqcb-5g9kk3mmez6z9c,18324 -django/contrib/gis/gdal/srs.py,sha256=uIF9WL5FGThhV_rspN79DATEJ41Xtcco3H-Zg7RyuNc,12675 -django/contrib/gis/geoip2/__init__.py,sha256=xHxacfECnYNrV-6h0uQJvx0QnweBnHG1ZFrIY6NdaQo,823 +django/contrib/gis/gdal/raster/band.py,sha256=dRikGQ6-cKCgOj3bjRSnIKd196FGRGM2Ee9BtPQGVk0,8247 +django/contrib/gis/gdal/raster/base.py,sha256=WLdZNgRlGAT6kyIXz5bBhPbpNY53ImxQkSeVLyv4Ohc,2861 +django/contrib/gis/gdal/raster/const.py,sha256=uPk8859YSREMtiQtXGkVOhISmgsF6gXP7JUfufQDXII,2891 +django/contrib/gis/gdal/raster/source.py,sha256=3CbkxyDalxrByJQXMG7vfL8iFPuSnuAS7zqzw-MPv8k,17965 +django/contrib/gis/gdal/srs.py,sha256=r3AHXDhKuofiYyJP3QENgHpDZq1u2dFZinxADpwiIqo,12611 +django/contrib/gis/geoip2/__init__.py,sha256=uIUWQyMsbSrYL-oVqFsmhqQkYGrh7pHLIVvIM3W_EG4,822 django/contrib/gis/geoip2/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/geoip2/__pycache__/base.cpython-39.pyc,, django/contrib/gis/geoip2/__pycache__/resources.cpython-39.pyc,, -django/contrib/gis/geoip2/base.py,sha256=6Dq6rh_IGlF_fRqQC3Irz9lac2NBzM0QrmJ4mmjWSQs,9245 -django/contrib/gis/geoip2/resources.py,sha256=Lzz-Ok677UBmMZQdHsPv1-qPBeJ8bc4HKTk7_UzmY0I,819 -django/contrib/gis/geometry.py,sha256=yW9GSA4QgKYOuucfmQySCiVfIRJNUWijUk5JPto50Ng,662 +django/contrib/gis/geoip2/base.py,sha256=yx8gZUBCkrVurux06tuJhnXsamzj7hg0iiGFYmfu0yE,8976 +django/contrib/gis/geoip2/resources.py,sha256=u39vbZzNV5bQKS0nKb0VbHsSRm3m69r29bZwpNbNs3Y,819 +django/contrib/gis/geometry.py,sha256=hA1SQGzGfTyV7A5kaBuxCzwkqZNAYz0kqZMaz3E1zIQ,662 django/contrib/gis/geos/LICENSE,sha256=CL8kt1USOK4yUpUkVCWxyuua0PQvni0wPHs1NQJjIEU,1530 -django/contrib/gis/geos/__init__.py,sha256=LCGbpFFWXYm6SunsMzV9LoPLNRtDKEWaQ7P4VUtsk84,660 +django/contrib/gis/geos/__init__.py,sha256=DXFaljVp6gf-E0XAbfO1JnYjPYSDfGZQ2VLtGYBcUZQ,648 django/contrib/gis/geos/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/geos/__pycache__/base.cpython-39.pyc,, django/contrib/gis/geos/__pycache__/collections.cpython-39.pyc,, @@ -2255,19 +2241,19 @@ django/contrib/gis/geos/__pycache__/point.cpython-39.pyc,, django/contrib/gis/geos/__pycache__/polygon.cpython-39.pyc,, django/contrib/gis/geos/__pycache__/prepared.cpython-39.pyc,, django/contrib/gis/geos/base.py,sha256=NdlFg5l9akvDp87aqzh9dk0A3ZH2TI3cOq10mmmuHBk,181 -django/contrib/gis/geos/collections.py,sha256=p3-m7yjqxsKPhLZxvLoQUtNKElM3tQjbs860LTCSnYM,3940 -django/contrib/gis/geos/coordseq.py,sha256=zK2p4lzNHzgw6HgYT1vXwEgQg_ad3BdUIMSDHSS2H-U,7284 +django/contrib/gis/geos/collections.py,sha256=yUMj02Akhu1BN9zpaPMSaoyfpRJWi282kkY_R6MF-kY,3895 +django/contrib/gis/geos/coordseq.py,sha256=kJEdoM6L_TW5SZYAgTivMnZbFRRm1ojf_2ycxjF7Ks0,7232 django/contrib/gis/geos/error.py,sha256=r3SNTnwDBI6HtuyL3mQ_iEEeKlOqqqdkHnhNoUkMohw,104 -django/contrib/gis/geos/factory.py,sha256=KQF6lqAh5KRlFSDgN-BSXWojmWFabbEUFgz2IGYX_vk,961 -django/contrib/gis/geos/geometry.py,sha256=tvuu2HBtFH-sYVNH2H3_wDxk3Aqw9a54TmVA35hHk5Q,25705 -django/contrib/gis/geos/io.py,sha256=P3bfg3AIWv99lrqmzFZyP-i6e5YiCuC32fql_IXPgUo,799 -django/contrib/gis/geos/libgeos.py,sha256=NZPlXWrRsyrq4HFW5zAkFvJ5lq5vX7AHA_fszf1quds,4990 -django/contrib/gis/geos/linestring.py,sha256=BJAoWfHW08EX1UpNFVB09iSKXdTS6pZsTIBc6DcZcfc,6372 -django/contrib/gis/geos/mutable_list.py,sha256=nthCtQ0FsJrDGd29cSERwXb-tJkpK35Vc0T_ywCnXgc,10121 -django/contrib/gis/geos/point.py,sha256=bvatsdXTb1XYy1EaSZvp4Rnr2LwXZU12zILefLu6sRw,4781 -django/contrib/gis/geos/polygon.py,sha256=Ads6NGbbvtrZtAdfHIx_1Dv-XT3EX5qZTbHnmUQek0E,6734 -django/contrib/gis/geos/prepared.py,sha256=J5Dj6e3u3gEfVPNOM1E_rvcmcXR2-CdwtbAcoiDU5a0,1577 -django/contrib/gis/geos/prototypes/__init__.py,sha256=phW_juFbYZncypjG-7KUnjKGGwnXenzjTIG29wCl3lw,1392 +django/contrib/gis/geos/factory.py,sha256=f6u2m1AtmYYHk_KrIC9fxt7VGsJokJVoSWEx-DkPWx0,961 +django/contrib/gis/geos/geometry.py,sha256=S8TMiNBNUPwU8FAKHvRAgJwQ7NUlqRzVSkLyc_qJm_M,25544 +django/contrib/gis/geos/io.py,sha256=Om5DBSlttixUc3WQAGZDhzPdb5JTe82728oImIj_l3k,787 +django/contrib/gis/geos/libgeos.py,sha256=dmktmuklfnViT3m3qQEwssEzOkCqyNDyg5ajuUw9HCM,4999 +django/contrib/gis/geos/linestring.py,sha256=mZnjmJQ3IUtwR8oKZsReTJ5nqZjLBv0cJqqoAlBfSvw,6293 +django/contrib/gis/geos/mutable_list.py,sha256=8uJ_9r48AlIIDzYaUb_qAD0eYslek9yvAX9ICdCmh5A,10131 +django/contrib/gis/geos/point.py,sha256=_5UI0cfAax9Q8_UuQeO25E3XhuS8PEVwkeZ2dgO0yQM,4757 +django/contrib/gis/geos/polygon.py,sha256=nAJFsaBXbIM9ZA_gSxVB_3WNXJHwakmhlxN_VzKs4WQ,6664 +django/contrib/gis/geos/prepared.py,sha256=rJf35HOTxPrrk_yA-YR9bQlL_pPDKecuhwZlcww8lxY,1575 +django/contrib/gis/geos/prototypes/__init__.py,sha256=gJo1iIH3eOITX_p20QqbWqOPAPps6fnhWQ8jPMzGMAY,1236 django/contrib/gis/geos/prototypes/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/geos/prototypes/__pycache__/coordseq.cpython-39.pyc,, django/contrib/gis/geos/prototypes/__pycache__/errcheck.cpython-39.pyc,, @@ -2278,15 +2264,15 @@ django/contrib/gis/geos/prototypes/__pycache__/predicates.cpython-39.pyc,, django/contrib/gis/geos/prototypes/__pycache__/prepared.cpython-39.pyc,, django/contrib/gis/geos/prototypes/__pycache__/threadsafe.cpython-39.pyc,, django/contrib/gis/geos/prototypes/__pycache__/topology.cpython-39.pyc,, -django/contrib/gis/geos/prototypes/coordseq.py,sha256=fIcSIzmyCbazQSR-XdvCwtP2YZItQur1Y27vfAKXNfw,3122 -django/contrib/gis/geos/prototypes/errcheck.py,sha256=aW4kLew3tdXZ4NmJhOF2NFY837ACid6Vm-_a10ET5Q8,2788 -django/contrib/gis/geos/prototypes/geom.py,sha256=wfYJnRL38BJ4m46q43r1M5_tPGb8RHr_zunqGL6WjqI,3332 -django/contrib/gis/geos/prototypes/io.py,sha256=gyiOOrXxY3r7J9DWN2pVvM6-gK2TL997q_koEaBmrWA,11360 -django/contrib/gis/geos/prototypes/misc.py,sha256=3Ek1DTeDo4BBsS7LloseeSHPBz70Vu-4mF-dxSjyXLU,1168 -django/contrib/gis/geos/prototypes/predicates.py,sha256=67HWiwf5NWFWNjiDJ8GvdlS5rCw0BcO7brqcDMwv_5s,1599 -django/contrib/gis/geos/prototypes/prepared.py,sha256=4I9pS75Q5MZ1z8A1v0mKkmdCly33Kj_0sDcrqxOppzM,1175 -django/contrib/gis/geos/prototypes/threadsafe.py,sha256=n1yCYvQCtc7piFrhjeZCWH8Pf0-AiOGBH33VZusTgWI,2302 -django/contrib/gis/geos/prototypes/topology.py,sha256=7TNgvTU8L3cyoU0VMXbox3RA3qmUePDXejJiHMntXlU,2327 +django/contrib/gis/geos/prototypes/coordseq.py,sha256=aBm_yTkis2ZloQeHqimjbMGYDkhEvv0FzeQGH3pVuqc,3103 +django/contrib/gis/geos/prototypes/errcheck.py,sha256=YTUBFoHU5pZOAamBPgogFymDswgnMr1_KL59sZfInYo,2654 +django/contrib/gis/geos/prototypes/geom.py,sha256=zKB1r_-6faLyq8OL4qJdM-lbMMpw8NKYYl8L9tCesBQ,3074 +django/contrib/gis/geos/prototypes/io.py,sha256=V2SlUEniZGfVnj_9r17XneT7w-OoCUpkL_sumKIhLbU,11229 +django/contrib/gis/geos/prototypes/misc.py,sha256=7Xwk0HG__JtPt6wJD-ieMkD-7KxpnofYrHSk6NEUeJo,1161 +django/contrib/gis/geos/prototypes/predicates.py,sha256=Ya06ir7LZQBSUypB05iv9gpvZowOSLIKa4fhCnhZuYY,1587 +django/contrib/gis/geos/prototypes/prepared.py,sha256=SC7g9_vvsW_ty7LKqlMzJfF9v3EvsJX9-j3kpSeCRfY,1184 +django/contrib/gis/geos/prototypes/threadsafe.py,sha256=Ll_TmpfJhRTmWV5dgKJx_Dh67ay1pa-SdlH558NRPw4,2309 +django/contrib/gis/geos/prototypes/topology.py,sha256=wd0OxkUQiMNioDXpJdRc1h9swsZ2CeOgqMvHxqJFY5s,2256 django/contrib/gis/locale/af/LC_MESSAGES/django.mo,sha256=TN3GddZjlqXnhK8UKLlMoMIXNw2szzj7BeRjoKjsR5c,470 django/contrib/gis/locale/af/LC_MESSAGES/django.po,sha256=XPdXaQsZ6yDPxF3jVMEI4bli_5jrEawoO-8DHMk8Q_A,1478 django/contrib/gis/locale/ar/LC_MESSAGES/django.mo,sha256=5LCO903yJTtRVaaujBrmwMx8f8iLa3ihasgmj8te9eg,2301 @@ -2319,8 +2305,8 @@ django/contrib/gis/locale/de/LC_MESSAGES/django.mo,sha256=1PBxHsFHDrbkCslumxKVD_ django/contrib/gis/locale/de/LC_MESSAGES/django.po,sha256=0XnbUsy9yZHhFsGGhcSnXUqJpDlMVqmrRl-0c-kdcYk,2163 django/contrib/gis/locale/dsb/LC_MESSAGES/django.mo,sha256=NzmmexcIC525FHQ5XvsKdzCZtkkb5wnrSd12fdAkZ-0,2071 django/contrib/gis/locale/dsb/LC_MESSAGES/django.po,sha256=aTBfL_NB8uIDt2bWBxKCdKi-EUNo9lQ9JZ0ekWeI4Yk,2234 -django/contrib/gis/locale/el/LC_MESSAGES/django.mo,sha256=OBxHnlLrT4tY0bW5TuaRqBCKtchnz_53RtrEc0fZ3V4,2484 -django/contrib/gis/locale/el/LC_MESSAGES/django.po,sha256=q0YzrFC5seve2ralJJDSmMG2uukAAALhoRflYOPFudg,2937 +django/contrib/gis/locale/el/LC_MESSAGES/django.mo,sha256=8QAS4MCktYLFsCgcIVflPXePYAWwr6iEZ7K8_axi_5U,2519 +django/contrib/gis/locale/el/LC_MESSAGES/django.po,sha256=6JVoYCUCUznxgQYlOCWJw1Ad6SR3Fa9jlorSCYkiwLw,2886 django/contrib/gis/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/gis/locale/en/LC_MESSAGES/django.po,sha256=8yvqHG1Mawkhx9RqD5tDXX8U0-a7RWr-wCQPGHWAqG0,2225 django/contrib/gis/locale/en_AU/LC_MESSAGES/django.mo,sha256=IPn5kRqOvv5S7jpbIUw8PEUkHlyjEL-4GuOANd1iAzI,486 @@ -2382,13 +2368,13 @@ django/contrib/gis/locale/it/LC_MESSAGES/django.po,sha256=Vp1G-GChjjTsODwABsg5Lb django/contrib/gis/locale/ja/LC_MESSAGES/django.mo,sha256=Ro8-P0647LU_963TJT1uOWTohB77YaGGci_2sMLJwEo,2096 django/contrib/gis/locale/ja/LC_MESSAGES/django.po,sha256=shMi1KrURuWbFGc3PpSrpatfEQJlW--QTDH6HwHbtv4,2352 django/contrib/gis/locale/ka/LC_MESSAGES/django.mo,sha256=iqWQ9j8yanPjDhwi9cNSktYgfLVnofIsdICnAg2Y_to,1991 -django/contrib/gis/locale/ka/LC_MESSAGES/django.po,sha256=rkM7RG0zxDN8vqyAudmk5nocajhOYP6CTkdJKu21Pf4,2571 +django/contrib/gis/locale/ka/LC_MESSAGES/django.po,sha256=tWoXkbWfNsZ2A28_JUvc1wtyVT6m7Hl9nJgfxXGqkgY,2566 django/contrib/gis/locale/kk/LC_MESSAGES/django.mo,sha256=NtgQONp0UncUNvrh0W2R7u7Ja8H33R-a-tsQShWq-QI,1349 -django/contrib/gis/locale/kk/LC_MESSAGES/django.po,sha256=78OMHuerBJZJZVo9GjGJ1h5fwdLuSc_X03ZhSRibtf4,1979 +django/contrib/gis/locale/kk/LC_MESSAGES/django.po,sha256=_wNvDk36C_UegH0Ex6ov8P--cKm-J7XtusXYsjVVZno,1974 django/contrib/gis/locale/km/LC_MESSAGES/django.mo,sha256=T0aZIZ_gHqHpQyejnBeX40jdcfhrCOjgKjNm2hLrpNE,459 django/contrib/gis/locale/km/LC_MESSAGES/django.po,sha256=7ARjFcuPQJG0OGLJu9pVfSiAwc2Q-1tT6xcLeKeom1c,1467 django/contrib/gis/locale/kn/LC_MESSAGES/django.mo,sha256=EkJRlJJSHZJvNZJuOLpO4IIUEoyi_fpKwNWe0OGFcy4,461 -django/contrib/gis/locale/kn/LC_MESSAGES/django.po,sha256=MnsSftGvmgJgGfgayQUVDMj755z8ItkM9vBehORfYbk,1475 +django/contrib/gis/locale/kn/LC_MESSAGES/django.po,sha256=NM3FRy48SSVsUIQc8xh0ZKAgTVAP8iK8elp7NQ6-IdE,1469 django/contrib/gis/locale/ko/LC_MESSAGES/django.mo,sha256=3cvrvesJ_JU-XWI5oaYSAANVjwFxn3SLd3UrdRSMAfA,1939 django/contrib/gis/locale/ko/LC_MESSAGES/django.po,sha256=Gg9s__57BxLIYJx5O0c-UJ8cAzsU3TcLuKGE7abn1rE,2349 django/contrib/gis/locale/ky/LC_MESSAGES/django.mo,sha256=1z_LnGCxvS3_6OBr9dBxsyHrDs7mR3Fzm76sdgNGJrU,2221 @@ -2407,8 +2393,6 @@ django/contrib/gis/locale/mn/LC_MESSAGES/django.mo,sha256=-Nn70s2On94C-jmSZwTppW django/contrib/gis/locale/mn/LC_MESSAGES/django.po,sha256=I0ZHocPlRYrogJtzEGVPsWWHpoVEa7e2KYP9Ystlj60,2770 django/contrib/gis/locale/mr/LC_MESSAGES/django.mo,sha256=sO2E__g61S0p5I6aEwnoAsA3epxv7_Jn55TyF0PZCUA,468 django/contrib/gis/locale/mr/LC_MESSAGES/django.po,sha256=McWaLXfWmYTDeeDbIOrV80gwnv07KCtNIt0OXW_v7vw,1476 -django/contrib/gis/locale/ms/LC_MESSAGES/django.mo,sha256=Ws6mtfdx1yajz4NUl1aqrWYc0XNPm2prqAAE8yCNyT0,1887 -django/contrib/gis/locale/ms/LC_MESSAGES/django.po,sha256=wglQEOZ8SF4_d7tZBCoOOSTbRG1U5IM4lIZA1H5MaDg,2017 django/contrib/gis/locale/my/LC_MESSAGES/django.mo,sha256=e6G8VbCCthUjV6tV6PRCy_ZzsXyZ-1OYjbYZIEShbXI,525 django/contrib/gis/locale/my/LC_MESSAGES/django.po,sha256=R3v1S-904f8FWSVGHe822sWrOJI6cNJIk93-K7_E_1c,1580 django/contrib/gis/locale/nb/LC_MESSAGES/django.mo,sha256=a89qhy9BBE_S-MYlOMLaYMdnOvUEJxh8V80jYJqFEj0,1879 @@ -2417,8 +2401,8 @@ django/contrib/gis/locale/ne/LC_MESSAGES/django.mo,sha256=nB-Ta8w57S6hIAhAdWZjDT django/contrib/gis/locale/ne/LC_MESSAGES/django.po,sha256=eMH6uKZZZYn-P3kmHumiO4z9M4923s9tWGhHuJ0eWuI,1825 django/contrib/gis/locale/nl/LC_MESSAGES/django.mo,sha256=d22j68OCI1Bevtl2WgXHSQHFCiDgkPXmrFHca_uUm14,1947 django/contrib/gis/locale/nl/LC_MESSAGES/django.po,sha256=ffytg6K7pTQoIRfxY35i1FpolJeox-fpSsG1JQzvb-0,2381 -django/contrib/gis/locale/nn/LC_MESSAGES/django.mo,sha256=Rp1zi-gbaGBPk9MVR4sw1MS4MhCRs6u9v7Aa8IxrkQQ,1888 -django/contrib/gis/locale/nn/LC_MESSAGES/django.po,sha256=ApoLxcaZ3UzO8owOqfDgDMCJuemnGAfrKH_qJVR47eM,2087 +django/contrib/gis/locale/nn/LC_MESSAGES/django.mo,sha256=32x5_V6o_BQBefFmyajOg3ssClw-DMEdvzXkY90fV3Q,1202 +django/contrib/gis/locale/nn/LC_MESSAGES/django.po,sha256=NWA3nD8ZwAZxG9EkE6TW0POJgB6HTeC4J6GOlTMD7j4,1796 django/contrib/gis/locale/os/LC_MESSAGES/django.mo,sha256=02NpGC8WPjxmPqQkfv9Kj2JbtECdQCtgecf_Tjk1CZc,1594 django/contrib/gis/locale/os/LC_MESSAGES/django.po,sha256=JBIsv5nJg3Wof7Xy7odCI_xKRBLN_Hlbb__kNqNW4Xw,2161 django/contrib/gis/locale/pa/LC_MESSAGES/django.mo,sha256=JR1NxG5_h_dFE_7p6trBWWIx-QqWYIgfGomnjaCsWAA,1265 @@ -2477,21 +2461,21 @@ django/contrib/gis/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC django/contrib/gis/management/commands/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/management/commands/__pycache__/inspectdb.cpython-39.pyc,, django/contrib/gis/management/commands/__pycache__/ogrinspect.cpython-39.pyc,, -django/contrib/gis/management/commands/inspectdb.py,sha256=8WhDOBICFAbLFu7kwAAS4I5pNs_p1BrCv8GJYI3S49k,760 -django/contrib/gis/management/commands/ogrinspect.py,sha256=XnWAbLxRxTSvbKSvjgePN7D1o_Ep4qWkvMwVrG1TpYY,6071 -django/contrib/gis/measure.py,sha256=KieLLeQFsV23gnPzj1WoJvN5unOIK5v8QThgX0Rk4Sg,12557 -django/contrib/gis/ptr.py,sha256=NeIBB-plwO61wGOOxGg7fFyVXI4a5vbAGUdaJ_Fmjqo,1312 +django/contrib/gis/management/commands/inspectdb.py,sha256=tpyZFocjeeRN6hE1yXfp1CANzyaQYqQpI8RLhKtGzBA,717 +django/contrib/gis/management/commands/ogrinspect.py,sha256=huTEev5RAMZU9axT9ZMmrlfd3BVZ_0lNbeuW-9XLwGU,5711 +django/contrib/gis/measure.py,sha256=8RQbkuhUQjg6mvjK17XULYx5LwXGWkydM7EkEj0xsdQ,12070 +django/contrib/gis/ptr.py,sha256=RK-5GCUUaQtBuDD3lAoraS7G05fzYhR5p0acKrzpQVE,1289 django/contrib/gis/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/gis/serializers/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/serializers/__pycache__/geojson.cpython-39.pyc,, -django/contrib/gis/serializers/geojson.py,sha256=j7f8iaEeJttWy3g2TBo9BHXg1W3p2a0bYcue5tbUJpE,2834 -django/contrib/gis/shortcuts.py,sha256=aa9zFjVU38qaEvRc0vAH_j2AgAERlI01rphYLHbc7Tg,1027 -django/contrib/gis/sitemaps/__init__.py,sha256=Tjj057omOVcoC5Fb8ITEYVhLm0HcVjrZ1Mbz_tKoD1A,138 +django/contrib/gis/serializers/geojson.py,sha256=IWR-98IYQXvJSJ4y3d09kh3ZxuFZuEKg-T9eAig5GEA,2710 +django/contrib/gis/shortcuts.py,sha256=fHf3HYP6MP8GeuBW6G3y6d30Mjxa6IL2xtmblDjS8k4,1027 +django/contrib/gis/sitemaps/__init__.py,sha256=eVHUxfzw1VQn6bqH3D8bE471s8bNJSB3phuAI-zg9gA,138 django/contrib/gis/sitemaps/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/sitemaps/__pycache__/kml.cpython-39.pyc,, django/contrib/gis/sitemaps/__pycache__/views.cpython-39.pyc,, -django/contrib/gis/sitemaps/kml.py,sha256=CUn_KKVrwGg2ZmmDcWosBc0QFuJp8hHpeNRCcloVk1U,2573 -django/contrib/gis/sitemaps/views.py,sha256=AFV1ay-oFftFC-IszzeKz3JAGzE0TOCH8pN1cwtg7yI,2353 +django/contrib/gis/sitemaps/kml.py,sha256=yg-soUBEFDRSmf7iIPzdOFEi3lvcQNKp_Jisk-cwiR4,2406 +django/contrib/gis/sitemaps/views.py,sha256=vJt4Oya4IL6BHE7x8Z_FkQn1Do6caVRL8d5hE2XKVCo,2306 django/contrib/gis/static/gis/css/ol3.css,sha256=pJADzfx4_NL2C1onFpU-muconAA5NThN4sEqSNyY_So,657 django/contrib/gis/static/gis/img/draw_line_off.svg,sha256=6XW83xsR5-Guh27UH3y5UFn9y9FB9T_Zc4kSPA-xSOI,918 django/contrib/gis/static/gis/img/draw_line_on.svg,sha256=Hx-pXu4ped11esG6YjXP1GfZC5q84zrFQDPUo1C7FGA,892 @@ -2508,83 +2492,83 @@ django/contrib/gis/templates/gis/kml/base.kml,sha256=VYnJaGgFVHRzDjiFjbcgI-jxlUo django/contrib/gis/templates/gis/kml/placemarks.kml,sha256=TEC81sDL9RK2FVeH0aFJTwIzs6_YWcMeGnHkACJV1Uc,360 django/contrib/gis/templates/gis/openlayers-osm.html,sha256=TeiUqCjt73W8Hgrp_6zAtk_ZMBxskNN6KHSmnJ1-GD4,378 django/contrib/gis/templates/gis/openlayers.html,sha256=gp49iEA82IgDWPHRrAYyCqC0pvInPxTw5674RuxPM_M,1897 -django/contrib/gis/utils/__init__.py,sha256=om0rPPBwSmvN4_BZpEkvpZqT44S0b7RCJpLAS2nI9-o,604 +django/contrib/gis/utils/__init__.py,sha256=F0GOFeUMUtapxuZ306T8d3uNblMhfWftOlpc84HeFVs,596 django/contrib/gis/utils/__pycache__/__init__.cpython-39.pyc,, django/contrib/gis/utils/__pycache__/layermapping.cpython-39.pyc,, django/contrib/gis/utils/__pycache__/ogrinfo.cpython-39.pyc,, django/contrib/gis/utils/__pycache__/ogrinspect.cpython-39.pyc,, django/contrib/gis/utils/__pycache__/srs.cpython-39.pyc,, -django/contrib/gis/utils/layermapping.py,sha256=ASFnX5zbm-T1fFkWbM5DMUeBcUAgWtVveseoef3Lrhc,28895 -django/contrib/gis/utils/ogrinfo.py,sha256=6m3KaRzLoZtQ0OSrpRkaFIQXi9YOXTkQcYeqYb0S0nw,1956 -django/contrib/gis/utils/ogrinspect.py,sha256=nxKd1cufjbP86uJcsaNb1c3n9IA-uy4ltQjLGgPjB1E,9169 -django/contrib/gis/utils/srs.py,sha256=UXsbxW0cQzdnPKO0d9E5K2HPdekdab5NaLZWNOUq-zk,2962 -django/contrib/gis/views.py,sha256=zdCV8QfUVfxEFGxESsUtCicsbSVtZNI_IXybdmsHKiM,714 +django/contrib/gis/utils/layermapping.py,sha256=wHlFJ0hiEmImFW3vOe862g7LJ0Czya16csaaq7knw9A,27663 +django/contrib/gis/utils/ogrinfo.py,sha256=VmbxQ5Ri4zjtTxNymuxJp3t3cAntUC83YBMp9PuMMSU,1934 +django/contrib/gis/utils/ogrinspect.py,sha256=4lZA5_rbdo-IG7DnqddQyT_2JI_AXhuW9nduBwMWrQY,8924 +django/contrib/gis/utils/srs.py,sha256=5D5lPZwFYgZiVaKD7eCkl9vj-pGRB11HEgeNlxUAjfo,2991 +django/contrib/gis/views.py,sha256=zZfnPHc8wxomPp9NcpOfISLhwBKkVG-EtRTm90d2X_Q,700 django/contrib/humanize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/humanize/__pycache__/__init__.cpython-39.pyc,, django/contrib/humanize/__pycache__/apps.cpython-39.pyc,, -django/contrib/humanize/apps.py,sha256=LH3PTbB4V1gbBc8nmCw3BsSuA8La0fNOb4cSISvJAwI,194 +django/contrib/humanize/apps.py,sha256=ODfDrSH8m3y3xYlyIIwm7DZmrNcoYKG2K8l5mU64V7g,194 django/contrib/humanize/locale/af/LC_MESSAGES/django.mo,sha256=bNLjjeZ3H-KD_pm-wa1_5eLCDOmG2FXgDHVOg5vgL7o,5097 django/contrib/humanize/locale/af/LC_MESSAGES/django.po,sha256=p3OduzjtTGkwlgDJhPgSm9aXI2sWzORspsPf7_RnWjs,8923 -django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo,sha256=PokPfBR8w4AbRtNNabl5vO8r5E8_egHvFBjKp4CCvO4,7510 -django/contrib/humanize/locale/ar/LC_MESSAGES/django.po,sha256=QGW-kx-87DlPMGr5l_Eb6Ge-x4tkz2PuwHDe3EIkIQg,12326 +django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo,sha256=-YDFm-RPAWqjWquABE0D-Y4WfELU2RTEjWGiHVFq2Uw,9580 +django/contrib/humanize/locale/ar/LC_MESSAGES/django.po,sha256=_LmxY73PR0hjoK6cqibEdfrczCtnqYGnNo8-v0rZrF4,15386 django/contrib/humanize/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=NwCrL5FX_xdxYdqkW_S8tmU8ktDM8LqimmUvkt8me74,9155 django/contrib/humanize/locale/ar_DZ/LC_MESSAGES/django.po,sha256=tt0AxhohGX79OQ_lX1S5soIo-iSCC07SdAhPpy0O7Q4,15234 django/contrib/humanize/locale/ast/LC_MESSAGES/django.mo,sha256=WvBk8V6g1vgzGqZ_rR-4p7SMh43PFnDnRhIS9HSwdoQ,3468 django/contrib/humanize/locale/ast/LC_MESSAGES/django.po,sha256=S9lcUf2y5wR8Ufa-Rlz-M73Z3bMo7zji_63cXwtDK2I,5762 django/contrib/humanize/locale/az/LC_MESSAGES/django.mo,sha256=G9dyDa8T8wwEJDVw5rrajGLQo2gfs7XqsW6LbURELvA,5286 django/contrib/humanize/locale/az/LC_MESSAGES/django.po,sha256=G0_M87HUGSH280uvUzni0qlCGviv2uwtyr6gne5SszA,9139 -django/contrib/humanize/locale/be/LC_MESSAGES/django.mo,sha256=7KyJKhNqMqv32CPdJi01RPLBefOVCQW-Gx6-Vf9JVrs,6653 -django/contrib/humanize/locale/be/LC_MESSAGES/django.po,sha256=2mbReEHyXhmZysqhSmaT6A2XCHn8mYb2R_O16TMGCAo,10666 -django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo,sha256=jCdDIbqWlhOs-4gML44wSRIXJQxypfak6ByRG_reMsk,4823 -django/contrib/humanize/locale/bg/LC_MESSAGES/django.po,sha256=v2ih4-pL1cdDXaa3uXm9FxRjRKyULLGyz78Q91eKEG8,8267 +django/contrib/humanize/locale/be/LC_MESSAGES/django.mo,sha256=qpbjGVSQnPESRACvTjzc3p5REpxyRGv7qgxQCigrNBY,8409 +django/contrib/humanize/locale/be/LC_MESSAGES/django.po,sha256=pyudF4so8SQG-gfmSNcNdG5BQA27Q0p_nQF1tYMuw88,13148 +django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo,sha256=1mRaFPsm5ITFyfdFdqdeY-_Om2OYKua5YWSEP192WR8,4645 +django/contrib/humanize/locale/bg/LC_MESSAGES/django.po,sha256=kTyRblfWlBUMxd_czXTOe-39CcX68X6e4DTmYm3V2gc,6684 django/contrib/humanize/locale/bn/LC_MESSAGES/django.mo,sha256=jbL4ucZxxtexI10jgldtgnDie3I23XR3u-PrMMMqP6U,4026 django/contrib/humanize/locale/bn/LC_MESSAGES/django.po,sha256=0l4yyy7q3OIWyFk_PW0y883Vw2Pmu48UcnLM9OBxB68,6545 django/contrib/humanize/locale/br/LC_MESSAGES/django.mo,sha256=V_tPVAyQzVdDwWPNlVGWmlVJjmVZfbh35alkwsFlCNU,5850 django/contrib/humanize/locale/br/LC_MESSAGES/django.po,sha256=BcAqEV2JpF0hiCQDttIMblp9xbB7zoHsmj7fJFV632k,12245 django/contrib/humanize/locale/bs/LC_MESSAGES/django.mo,sha256=1-RNRHPgZR_9UyiEn9Djp4mggP3fywKZho45E1nGMjM,1416 django/contrib/humanize/locale/bs/LC_MESSAGES/django.po,sha256=M017Iu3hyXmINZkhCmn2he-FB8rQ7rXN0KRkWgrp7LI,5498 -django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo,sha256=WDvXis2Y1ivSq6NdJgddO_WKbz8w5MpVpkT4sq-pWXI,4270 -django/contrib/humanize/locale/ca/LC_MESSAGES/django.po,sha256=AD3h2guGADdp1f9EcbP1vc1lmfDOL8-1qQfwvXa6I04,7731 -django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo,sha256=VFyZcn19aQUXhVyh2zo2g3PAuzOO38Kx9fMFOCCxzMc,5479 -django/contrib/humanize/locale/cs/LC_MESSAGES/django.po,sha256=mq3LagwA9hyWOGy76M9n_rD4p3wuVk6oQsneB9CF99w,9527 +django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo,sha256=I0A0wyJlSfGw34scNPoj9itqU8iz0twcyxUS15u5nJE,5230 +django/contrib/humanize/locale/ca/LC_MESSAGES/django.po,sha256=t-wxHJ0ZrXrc3bAjavz40eSu5HTJqJjz5wvfdiydJ6k,9153 +django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo,sha256=jhc_EBxxvTREfZ_aaqLTBgdTv0ETP_NfI658jOG5-wQ,5119 +django/contrib/humanize/locale/cs/LC_MESSAGES/django.po,sha256=hxVcvCScc4Sl2wjr4uXNWVDin1Ps1HdVAKOmU9tIluM,9167 django/contrib/humanize/locale/cy/LC_MESSAGES/django.mo,sha256=VjJiaUUhvX9tjOEe6x2Bdp7scvZirVcUsA4-iE2-ElQ,5241 django/contrib/humanize/locale/cy/LC_MESSAGES/django.po,sha256=sylmceSq-NPvtr_FjklQXoBAfueKu7hrjEpMAsVbQC4,7813 -django/contrib/humanize/locale/da/LC_MESSAGES/django.mo,sha256=vfDHopmWFAomwqmmCX3wfmX870-zzVbgUFC6I77n9tE,4316 -django/contrib/humanize/locale/da/LC_MESSAGES/django.po,sha256=v7Al6UOkbYB1p7m8kOe-pPRIAoyWemoyg_Pm9bD5Ldc,7762 -django/contrib/humanize/locale/de/LC_MESSAGES/django.mo,sha256=aOUax9csInbXnjAJc3jq4dcW_9H-6ueVI-TtKz2b9q0,4364 -django/contrib/humanize/locale/de/LC_MESSAGES/django.po,sha256=gW3OfOfoVMvpVudwghKCYztkLrCIPbbcriZjBNnRyGo,7753 -django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo,sha256=OVKcuW9ZXosNvP_3A98WsIIk_Jl6U_kv3zOx4pvwh-g,5588 -django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po,sha256=VimcsmobK3VXTbbTasg6osWDPOIZ555uimbUoUfNco4,9557 +django/contrib/humanize/locale/da/LC_MESSAGES/django.mo,sha256=V8u7uq8GNU7Gk3urruDnM2iR6fiio9RvLB8ou4e3EWY,5298 +django/contrib/humanize/locale/da/LC_MESSAGES/django.po,sha256=AnAvSgks2ph0MS2ZJlYKddKwQTbduEIpHK0kzsNphWM,9151 +django/contrib/humanize/locale/de/LC_MESSAGES/django.mo,sha256=7HZDGVn4FuGS2nNqHLg1RrnmQLB2Ansbri0ysHq-GfM,5418 +django/contrib/humanize/locale/de/LC_MESSAGES/django.po,sha256=wNFP1wO9hDhgyntigfVcHr7ZGao8a2PPgU24j4nl_O8,9184 +django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo,sha256=w2rgnclJnn1QQjqufly0NjUlP6kS6N8dcGwhbeBLq-w,7036 +django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po,sha256=AAbtZ32HrIeB1SDn3xenPU8pFUL0Fy6D9eYlObt6EdU,11690 django/contrib/humanize/locale/el/LC_MESSAGES/django.mo,sha256=o-yjhpzyGRbbdMzwUcG_dBP_FMEMZevm7Wz1p4Wd-pg,6740 django/contrib/humanize/locale/el/LC_MESSAGES/django.po,sha256=UbD5QEw_-JNoNETaOyDfSReirkRsHnlHeSsZF5hOSkI,10658 django/contrib/humanize/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 -django/contrib/humanize/locale/en/LC_MESSAGES/django.po,sha256=7CzW7XKCntUjZon7-mQU_Z2UX9XReoQ8IsjojNowG1w,9050 -django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo,sha256=QFf4EgAsGprbFetnwogmj8vDV7SfGq1E3vhL9D8xTTM,918 -django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po,sha256=Bnfesr1_T9sa31qkKOMunwKKXbnFzZJhzV8rYC_pdSE,6532 +django/contrib/humanize/locale/en/LC_MESSAGES/django.po,sha256=ioDdn19UOz4rbUVzbTbV7T4qBKUD5OhdntfPzkbbTFQ,8930 +django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo,sha256=dTndJxA-F1IE_nMUOtf1sRr7Kq2s_8yjgKk6mkWkVu4,486 +django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po,sha256=dVOlMtk3-d-KrNLM5Rji-Xrk6Y_n801ofjGQvxSu67M,4742 django/contrib/humanize/locale/en_GB/LC_MESSAGES/django.mo,sha256=mkx192XQM3tt1xYG8EOacMfa-BvgzYCbSsJQsWZGeAo,3461 django/contrib/humanize/locale/en_GB/LC_MESSAGES/django.po,sha256=MArKzXxY1104jxaq3kvDZs2WzOGYxicfJxFKsLzFavw,5801 django/contrib/humanize/locale/eo/LC_MESSAGES/django.mo,sha256=b47HphXBi0cax_reCZiD3xIedavRHcH2iRG8pcwqb54,5386 django/contrib/humanize/locale/eo/LC_MESSAGES/django.po,sha256=oN1YqOZgxKY3L1a1liluhM6X5YA5bawg91mHF_Vfqx8,9095 -django/contrib/humanize/locale/es/LC_MESSAGES/django.mo,sha256=F9UaraI_lUDFSv9etjKWY39bA53FZql4n9qlpXCq4pw,4409 -django/contrib/humanize/locale/es/LC_MESSAGES/django.po,sha256=IwonvoL51Wk6UyzqzDlyS3pq3I9aZA31FjfS7alKT9o,8015 -django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo,sha256=-btiXH3B5M1qkAsW9D5I742Gt9GcJs5VC8ZhJ_DKkGY,4425 -django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po,sha256=UsiuRj-eq-Vl41wNZGw9XijCMEmcXhcGrMTPWgZn4LA,7858 +django/contrib/humanize/locale/es/LC_MESSAGES/django.mo,sha256=qBSk64IcMaTrjGtTrlHP3qmNbKpA3rPz7ikNSwvOTKg,5393 +django/contrib/humanize/locale/es/LC_MESSAGES/django.po,sha256=YyTW90cMUAiF-Xec7aH6l-hBFu7mg9HFzYolkjw-wXw,9436 +django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo,sha256=w3GNYZ0Cg9u7QTGWWnTPNI5JNS3PQkk0_XOlReDzLa4,5461 +django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po,sha256=zk18690pQF6URZmvOISW6OsoRQNiiU5lt_q07929Rko,9360 django/contrib/humanize/locale/es_CO/LC_MESSAGES/django.mo,sha256=2GhQNtNOjK5mTov5RvnuJFTYbdoGBkDGLxzvJ8Vsrfs,4203 django/contrib/humanize/locale/es_CO/LC_MESSAGES/django.po,sha256=JBf2fHO8jWi6dFdgZhstKXwyot_qT3iJBixQZc3l330,6326 django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.mo,sha256=82DL2ztdq10X5RIceshK1nO99DW5628ZIjaN8Xzp9ok,3939 django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.po,sha256=-O7AQluA5Kce9-bd04GN4tfQKoCxb8Sa7EZR6TZBCdM,6032 django/contrib/humanize/locale/es_VE/LC_MESSAGES/django.mo,sha256=cJECzKpD99RRIpVFKQW65x0Nvpzrm5Fuhfi-nxOWmkM,942 django/contrib/humanize/locale/es_VE/LC_MESSAGES/django.po,sha256=tDdYtvRILgeDMgZqKHSebe7Z5ZgI1bZhDdvGVtj_anM,4832 -django/contrib/humanize/locale/et/LC_MESSAGES/django.mo,sha256=_vLDxD-e-pBY7vs6gNkhFZNGYu_dAeETVMKGsjjWOHg,4406 -django/contrib/humanize/locale/et/LC_MESSAGES/django.po,sha256=u0tSkVYckwXUv1tVfe1ODdZ8tJ2wUkS0Vv8pakJ8eBM,7915 +django/contrib/humanize/locale/et/LC_MESSAGES/django.mo,sha256=qid7q1XcaF4Yso9EMvjjYHa4GpS2gEABZsjM6K7kvaw,5409 +django/contrib/humanize/locale/et/LC_MESSAGES/django.po,sha256=NwshOQjWccRg8Mc7l6W3am0BxEVM8xHSzRYtCeThWe8,9352 django/contrib/humanize/locale/eu/LC_MESSAGES/django.mo,sha256=w2TlBudWWTI1M7RYCl_n2UY7U1CBzxIuwXl-7DCVl8o,5287 django/contrib/humanize/locale/eu/LC_MESSAGES/django.po,sha256=77QrRqIsMuu-6HxHvaifKsPA9OVZR7686WFp26dQFMg,9146 django/contrib/humanize/locale/fa/LC_MESSAGES/django.mo,sha256=-EfCvMVkX5VqYlXxiX8fLQntzZx8pBjmjtjvIdsaPvU,5808 django/contrib/humanize/locale/fa/LC_MESSAGES/django.po,sha256=Xxv-FVTrSjbx0JB33F6O1wBzodwkHJpmTEiNssNTeYQ,9775 -django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo,sha256=FJfyLFkz-oAz9e15e1aQUct7CJ2EJqSkZKh_ztDxtic,4425 -django/contrib/humanize/locale/fi/LC_MESSAGES/django.po,sha256=j5Z5t9zX1kePdM_Es1hu9AKOpOrijVWTsS2t19CIiaE,7807 -django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo,sha256=dZ3JfZyUIqicd2hsit9QOW_2CA2XgpKQgJQ5G3MbrC4,4484 -django/contrib/humanize/locale/fr/LC_MESSAGES/django.po,sha256=z-rfIbTRVEiYaD_kssNLvMFiINveBAq070-YanuL_hw,7872 +django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo,sha256=JoIeXlbKa9AD06oFeaeA9OQ2OOrLxUcLZOStczKMIGM,4190 +django/contrib/humanize/locale/fi/LC_MESSAGES/django.po,sha256=W9aUxxRF9c8aa9-kbkKmHkdQEkcfTRd8gRzWZwlTChw,7567 +django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo,sha256=M7Qw0-T3752Scd4KXukhQHriG_2hgC8zYnGZGwBo_r8,5461 +django/contrib/humanize/locale/fr/LC_MESSAGES/django.po,sha256=xyn-d8-_ozUhfr25hpuUU5IQhZvtNI0JVDoUYoRzO88,9311 django/contrib/humanize/locale/fy/LC_MESSAGES/django.mo,sha256=YQQy7wpjBORD9Isd-p0lLzYrUgAqv770_56-vXa0EOc,476 django/contrib/humanize/locale/fy/LC_MESSAGES/django.po,sha256=pPvcGgBWiZwQ5yh30OlYs-YZUd_XsFro71T9wErVv0M,4732 django/contrib/humanize/locale/ga/LC_MESSAGES/django.mo,sha256=AOEiBNOak_KQkBeGyUpTNO12zyg3CiK66h4kMoS15_0,5112 @@ -2593,30 +2577,30 @@ django/contrib/humanize/locale/gd/LC_MESSAGES/django.mo,sha256=XNSpJUu4DxtlXryfU django/contrib/humanize/locale/gd/LC_MESSAGES/django.po,sha256=I7s86NJDzeMsCGgXja--fTZNFm9bM7Cd8M1bstxabSY,11874 django/contrib/humanize/locale/gl/LC_MESSAGES/django.mo,sha256=ChoVHsJ_bVIaHtHxhxuUK99Zu1tvRu0iY5vhtB1LDMg,3474 django/contrib/humanize/locale/gl/LC_MESSAGES/django.po,sha256=U5D505aBKEdg80BGWddcwWuzmYdoNHx1WEPzVHQfbTE,5903 -django/contrib/humanize/locale/he/LC_MESSAGES/django.mo,sha256=phFZMDohKT86DUtiAlnZslPFwSmpcpxTgZaXb8pGohc,5875 -django/contrib/humanize/locale/he/LC_MESSAGES/django.po,sha256=xhEZYcK-fg_mHMyGCEZXEwbd6FvutaGvkDyHTET-sic,9970 +django/contrib/humanize/locale/he/LC_MESSAGES/django.mo,sha256=zV7tqLeq2al9nSDKcTGp7cDD2pEuHD-J_34roqIYvZc,7857 +django/contrib/humanize/locale/he/LC_MESSAGES/django.po,sha256=gvUe-8PJc6dn-6lLpEi_PCDgITgJ6UzZls9cUHSA4Ss,12605 django/contrib/humanize/locale/hi/LC_MESSAGES/django.mo,sha256=qrzm-6vXIUsxA7nOxa-210-6iO-3BPBj67vKfhTOPrY,4131 django/contrib/humanize/locale/hi/LC_MESSAGES/django.po,sha256=BrypbKaQGOyY_Gl1-aHXiBVlRqrbSjGfZ2OK8omj_9M,6527 django/contrib/humanize/locale/hr/LC_MESSAGES/django.mo,sha256=29XTvFJHex31hbu2qsOfl5kOusz-zls9eqlxtvw_H0s,1274 django/contrib/humanize/locale/hr/LC_MESSAGES/django.po,sha256=OuEH4fJE6Fk-s0BMqoxxdlUAtndvvKK7N8Iy-9BP3qA,5424 -django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo,sha256=a1DqdiuRfFSfSrD8IvzQmZdzE0dhkxDChFddrmt3fjA,5679 -django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po,sha256=V5aRblcqKii4RXSQO87lyoQwwvxL59T3m4-KOBTx4bc,9648 +django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo,sha256=4ZQDrpkEyLSRtVHEbP31ejNrR6y-LSNDfW1Hhi7VczI,7146 +django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po,sha256=GtSTgK-cKHMYeOYFvHtcUtUnLyWPP05F0ZM3tEYfshs,11800 django/contrib/humanize/locale/hu/LC_MESSAGES/django.mo,sha256=8tEqiZHEc6YmfWjf7hO0Fb3Xd-HSleKaR1gT_XFTQ8g,5307 django/contrib/humanize/locale/hu/LC_MESSAGES/django.po,sha256=KDVYBAGSuMrtwqO98-oGOOAp7Unfm7ode1sv8lfe81c,9124 django/contrib/humanize/locale/hy/LC_MESSAGES/django.mo,sha256=C1yx1DrYTrZ7WkOzZ5hvunphWABvGX-DqXbChNQ5_yg,1488 django/contrib/humanize/locale/hy/LC_MESSAGES/django.po,sha256=MGbuYylBt1C5hvSlktydD4oMLZ1Sjzj7DL_nl7uluTg,7823 django/contrib/humanize/locale/ia/LC_MESSAGES/django.mo,sha256=d0m-FddFnKp08fQYQSC9Wr6M4THVU7ibt3zkIpx_Y_A,4167 django/contrib/humanize/locale/ia/LC_MESSAGES/django.po,sha256=qX6fAZyn54hmtTU62oJcHF8p4QcYnoO2ZNczVjvjOeE,6067 -django/contrib/humanize/locale/id/LC_MESSAGES/django.mo,sha256=AdUmhfkQOV9Le4jXQyQSyd5f2GqwNt-oqnJV-WVELVw,3885 -django/contrib/humanize/locale/id/LC_MESSAGES/django.po,sha256=lMnTtM27j1EWg1i9d7NzAeueo7mRztGVfNOXtXdZVjw,7021 +django/contrib/humanize/locale/id/LC_MESSAGES/django.mo,sha256=Wb_pFDfiAow4QUsbBiqvRYt49T6cBVFTMTB_F2QUbWI,4653 +django/contrib/humanize/locale/id/LC_MESSAGES/django.po,sha256=sNc4OeIE9wvxxOQlFC9xNawJkLxa2gPUVlaKGljovOw,8116 django/contrib/humanize/locale/io/LC_MESSAGES/django.mo,sha256=nMu5JhIy8Fjie0g5bT8-h42YElCiS00b4h8ej_Ie-w0,464 django/contrib/humanize/locale/io/LC_MESSAGES/django.po,sha256=RUs8JkpT0toKOLwdv1oCbcBP298EOk02dkdNSJiC-_A,4720 django/contrib/humanize/locale/is/LC_MESSAGES/django.mo,sha256=D6ElUYj8rODRsZwlJlH0QyBSM44sVmuBCNoEkwPVxko,3805 django/contrib/humanize/locale/is/LC_MESSAGES/django.po,sha256=1VddvtkhsK_5wmpYIqEFqFOo-NxIBnL9wwW74Tw9pbw,8863 -django/contrib/humanize/locale/it/LC_MESSAGES/django.mo,sha256=oFaOtzqH0RFuybF1bPI_IZ23YaPVYAvaW-SbNuS4DPU,4653 -django/contrib/humanize/locale/it/LC_MESSAGES/django.po,sha256=nBU8lS9dAIPJuY3vfKkY_Jgkt7ZawnOtor8IZvEIkYU,8342 -django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo,sha256=x8AvfUPBBJkGtE0jvAP4tLeZEByuyo2H4V_UuLoCEmw,3907 -django/contrib/humanize/locale/ja/LC_MESSAGES/django.po,sha256=G2yTPZq6DxgzPV5uJ6zvMK4o3aiuLWbl4vXPH7ylUhc,6919 +django/contrib/humanize/locale/it/LC_MESSAGES/django.mo,sha256=nOn-bSN3OVnqLwTlUfbb_iHLzwWt9hsR2GVHh4GZJZE,5940 +django/contrib/humanize/locale/it/LC_MESSAGES/django.po,sha256=r7sg7QtNFPzrENz5kj1wdktqdqMluA_RRtM8TKwe7PQ,10046 +django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo,sha256=kYDryScxMRi2u2iOmpXc2dMytZ9_9DQMU3C3xD2REDE,4799 +django/contrib/humanize/locale/ja/LC_MESSAGES/django.po,sha256=6-W89FFg7x_JxJjACQhb4prK2Y7i1vlzm_nnIkgpNGw,8141 django/contrib/humanize/locale/ka/LC_MESSAGES/django.mo,sha256=UeUbonYTkv1d2ljC0Qj8ZHw-59zHu83fuMvnME9Fkmw,4878 django/contrib/humanize/locale/ka/LC_MESSAGES/django.po,sha256=-eAMexwjm8nSB4ARJU3f811UZnuatHKIFf8FevpJEpo,9875 django/contrib/humanize/locale/kk/LC_MESSAGES/django.mo,sha256=jujbUM0jOpt3Mw8zN4LSIIkxCJ0ihk_24vR0bXoux78,2113 @@ -2624,17 +2608,17 @@ django/contrib/humanize/locale/kk/LC_MESSAGES/django.po,sha256=hjZg_NRE9xMA5uEa2 django/contrib/humanize/locale/km/LC_MESSAGES/django.mo,sha256=mfXs9p8VokORs6JqIfaSSnQshZEhS90rRFhOIHjW7CI,459 django/contrib/humanize/locale/km/LC_MESSAGES/django.po,sha256=JQBEHtcy-hrV_GVWIjvUJyOf3dZ5jUzzN8DUTAbHKUg,4351 django/contrib/humanize/locale/kn/LC_MESSAGES/django.mo,sha256=Oq3DIPjgCqkn8VZMb6ael7T8fQ7LnWobPPAZKQSFHl4,461 -django/contrib/humanize/locale/kn/LC_MESSAGES/django.po,sha256=CAJ0etMlQF3voPYrxIRr5ChAwUYO7wI42n5kjpIEVjA,4359 +django/contrib/humanize/locale/kn/LC_MESSAGES/django.po,sha256=yrXx6TInsxjnyJfhl8sXTLmYedd2jaAku9L_38CKR5A,4353 django/contrib/humanize/locale/ko/LC_MESSAGES/django.mo,sha256=hDb7IOB8PRflKkZ81yQbgHtvN4TO35o5kWTK3WpiL4A,4817 django/contrib/humanize/locale/ko/LC_MESSAGES/django.po,sha256=dZpSVF3l5wGTwKOXn0looag7Q23jyLGlzs083kpnqFc,8217 -django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo,sha256=jDu1bVgJMDpaZ0tw9-wdkorvZxDdRzcuzdeC_Ot7rUs,4177 -django/contrib/humanize/locale/ky/LC_MESSAGES/django.po,sha256=MEHbKMLIiFEG7BlxsNVF60viXSnlk5iqlFCH3hgamH0,7157 +django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo,sha256=Az1jPnIXkf3NWnrfHUaptfRChqcgY5IzqO07fjBfswo,5039 +django/contrib/humanize/locale/ky/LC_MESSAGES/django.po,sha256=RZRDS9Fyd7wT9EYkGHdSipsYdXZB3FzbOPgbMrzBPHo,8297 django/contrib/humanize/locale/lb/LC_MESSAGES/django.mo,sha256=xokesKl7h7k9dXFKIJwGETgwx1Ytq6mk2erBSxkgY-o,474 django/contrib/humanize/locale/lb/LC_MESSAGES/django.po,sha256=_y0QFS5Kzx6uhwOnzmoHtCrbufMrhaTLsHD0LfMqtcM,4730 django/contrib/humanize/locale/lt/LC_MESSAGES/django.mo,sha256=O0C-tPhxWNW5J4tCMlB7c7shVjNO6dmTURtIpTVO9uc,7333 django/contrib/humanize/locale/lt/LC_MESSAGES/django.po,sha256=M5LlRxC1KWh1-3fwS93UqTijFuyRENmQJXfpxySSKik,12086 -django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo,sha256=3gEzmKBtYsFz9wvLw0ltiir91CDLxhK3IG2j55-uM7Y,5033 -django/contrib/humanize/locale/lv/LC_MESSAGES/django.po,sha256=yfeBxpH2J49xHDzZUZI3cK5ms4QbWq0gtTmhj8ejAjE,8836 +django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo,sha256=-XzcL0rlKmGkt28ukVIdwQZobR7RMmsOSstKH9eezuQ,6211 +django/contrib/humanize/locale/lv/LC_MESSAGES/django.po,sha256=fJOCQcPLCw1g-q8g4UNWR3MYFtBWSNkeOObjCMdWUp4,10572 django/contrib/humanize/locale/mk/LC_MESSAGES/django.mo,sha256=htUgd6rcaeRPDf6UrEb18onz-Ayltw9LTvWRgEkXm08,4761 django/contrib/humanize/locale/mk/LC_MESSAGES/django.po,sha256=Wl9Rt8j8WA_0jyxKCswIovSiCQD-ZWFYXbhFsCUKIWo,6665 django/contrib/humanize/locale/ml/LC_MESSAGES/django.mo,sha256=5As-FXkEJIYetmV9dMtzLtsRPTOm1oUgyx-oeTH_guY,4655 @@ -2653,14 +2637,14 @@ django/contrib/humanize/locale/ne/LC_MESSAGES/django.mo,sha256=YFT2D-yEkUdJBO2Gf django/contrib/humanize/locale/ne/LC_MESSAGES/django.po,sha256=SN7yH65hthOHohnyEmQUjXusRTDRjxWJG_kuv5g2Enk,9038 django/contrib/humanize/locale/nl/LC_MESSAGES/django.mo,sha256=xSGou2yFmVuiMH3C1IefwHBSys0YI_qW8ZQ9rwLdlPQ,5262 django/contrib/humanize/locale/nl/LC_MESSAGES/django.po,sha256=s7LbdXpSQxkqSr666oTwTNlfdrJpLeYGoCe1xlAkGH8,9217 -django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo,sha256=U8T2xhNfS4eG8olRLsLa0ykJc7lXG1nds8RG7yM_-PU,4327 -django/contrib/humanize/locale/nn/LC_MESSAGES/django.po,sha256=ThvRCloPtKxlz23CiH23JmlkIhgEYP8xCySEpy_X9xY,7648 +django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo,sha256=_Qbyf366ApSCU09Er6CvEf5WrA8s6ZzsyZXs44BoT10,3482 +django/contrib/humanize/locale/nn/LC_MESSAGES/django.po,sha256=qkEeQKQ8XwPKtTv2Y8RscAnE4QarinOze3Y3BTIEMCk,5818 django/contrib/humanize/locale/os/LC_MESSAGES/django.mo,sha256=BwS3Mj7z_Fg5s7Qm-bGLVhzYLZ8nPgXoB0gXLnrMGWc,3902 django/contrib/humanize/locale/os/LC_MESSAGES/django.po,sha256=CGrxyL5l-5HexruOc7QDyRbum7piADf-nY8zjDP9wVM,6212 django/contrib/humanize/locale/pa/LC_MESSAGES/django.mo,sha256=TH1GkAhaVVLk2jrcqAmdxZprWyikAX6qMP0eIlr2tWM,1569 django/contrib/humanize/locale/pa/LC_MESSAGES/django.po,sha256=_7oP0Hn-IU7IPLv_Qxg_wstLEdhgWNBBTCWYwSycMb0,5200 -django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo,sha256=0QheMbF3Y0Q_sxZlN2wAYJRQyK3K_uq6ttVr7wCc33w,5596 -django/contrib/humanize/locale/pl/LC_MESSAGES/django.po,sha256=6wX50O68aIyKiP6CcyLMXZ3xuUnAzasFPIg_8deJQBY,9807 +django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo,sha256=UT-bQF-nGA9XBIuitXuld4JKrJKRct_HAbmHdPOE0eg,6977 +django/contrib/humanize/locale/pl/LC_MESSAGES/django.po,sha256=hgqkd9mPgYmacnv0y2RwMn5svKQO4BCSvh-0zuG2yeQ,11914 django/contrib/humanize/locale/pt/LC_MESSAGES/django.mo,sha256=El9Sdr3kXS-yTol_sCg1dquxf0ThDdWyrWGjjim9Dj4,5408 django/contrib/humanize/locale/pt/LC_MESSAGES/django.po,sha256=XudOc67ybF_fminrTR2XOCKEKwqB5FX14pl3clCNXGE,9281 django/contrib/humanize/locale/pt_BR/LC_MESSAGES/django.mo,sha256=5GqZStkWlU0gGvtk_ufR3ZdLRqLEkSF6KJtbTuJb3pc,5427 @@ -2691,8 +2675,8 @@ django/contrib/humanize/locale/tg/LC_MESSAGES/django.mo,sha256=1Fiqat0CZSyExRXRj django/contrib/humanize/locale/tg/LC_MESSAGES/django.po,sha256=j2iczgQDbqzpthKAAlMt1Jk7gprYLqZ1Ya0ASr2SgD0,7852 django/contrib/humanize/locale/th/LC_MESSAGES/django.mo,sha256=jT7wGhYWP9HHwOvtr2rNPStiOgZW-rGMcO36w1U8Y4c,3709 django/contrib/humanize/locale/th/LC_MESSAGES/django.po,sha256=ZO3_wU7z0VASS5E8RSLEtmTveMDjJ0O8QTynb2-jjt0,8318 -django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo,sha256=D4ChMLE1Uz921NIF_Oe1vNkYAGfRpQuC8xANFwtlygE,4319 -django/contrib/humanize/locale/tr/LC_MESSAGES/django.po,sha256=4PjW65seHF9SsWnLv47JhgYPt0Gvzr-7_Ejech3d3ak,7754 +django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo,sha256=Z7-3YuSHL0_5sVzsUglegY-jD9uQvw3nAzf2LVomTzU,5263 +django/contrib/humanize/locale/tr/LC_MESSAGES/django.po,sha256=aNI_MjfKWeb4UmukfkYWs1ZXj8JabBYG3WKkADGyOK8,9160 django/contrib/humanize/locale/tt/LC_MESSAGES/django.mo,sha256=z8VgtMhlfyDo7bERDfrDmcYV5aqOeBY7LDgqa5DRxDM,3243 django/contrib/humanize/locale/tt/LC_MESSAGES/django.po,sha256=j_tRbg1hzLBFAmPQt0HoN-_WzWFtA07PloCkqhvNkcY,5201 django/contrib/humanize/locale/udm/LC_MESSAGES/django.mo,sha256=CNmoKj9Uc0qEInnV5t0Nt4ZnKSZCRdIG5fyfSsqwky4,462 @@ -2705,14 +2689,14 @@ django/contrib/humanize/locale/uz/LC_MESSAGES/django.mo,sha256=HDah_1qqUz5m_ABBV django/contrib/humanize/locale/uz/LC_MESSAGES/django.po,sha256=Ql3GZOhuoVgS0xHEzxjyYkOWQUyi_jiizfAXBp2Y4uw,7296 django/contrib/humanize/locale/vi/LC_MESSAGES/django.mo,sha256=ZUK_Na0vnfdhjo0MgnBWnGFU34sxcMf_h0MeyuysKG8,3646 django/contrib/humanize/locale/vi/LC_MESSAGES/django.po,sha256=DzRpXObt9yP5RK_slWruaIhnVI0-JXux2hn_uGsVZiE,5235 -django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=YgeAjXHMV1rXNNIrlDu_haxnKB0hxU5twJ86LMR10k8,3844 -django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po,sha256=JGfRVW_5UqwyI2mK_WRK8xDPzwBAO2q_mGsGzf89a88,7122 +django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=JcMWgxYXOPXTCR6t8szkuDHSQ6p0RJX7Tggq84gJhwQ,4709 +django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po,sha256=L7SmGldceykiGHJe42Hxx_qyJa9rBuAnJdYgIY-L-6o,8242 django/contrib/humanize/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=qYO9_rWuIMxnlL9Q8V9HfhUu7Ebv1HGOlvsnh7MvZkE,4520 django/contrib/humanize/locale/zh_Hant/LC_MESSAGES/django.po,sha256=AijEfvIlJK9oVaLJ7lplmbvhGRKIbYcLh8WxoBYoQkA,7929 django/contrib/humanize/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/humanize/templatetags/__pycache__/__init__.cpython-39.pyc,, django/contrib/humanize/templatetags/__pycache__/humanize.cpython-39.pyc,, -django/contrib/humanize/templatetags/humanize.py,sha256=KrYTdiByIhAdenW55earN6pmRZ88EYkBgCVy46Nx3Lc,12071 +django/contrib/humanize/templatetags/humanize.py,sha256=Y6J-BQC4NzosmYWXoRACmEbW-yj5N_TtRhk-GMv4Uns,11203 django/contrib/messages/__init__.py,sha256=6myQIwIFgc3SAyH5P1soIjwELREVgbxgxP85fJcge04,106 django/contrib/messages/__pycache__/__init__.cpython-39.pyc,, django/contrib/messages/__pycache__/api.cpython-39.pyc,, @@ -2722,28 +2706,27 @@ django/contrib/messages/__pycache__/context_processors.cpython-39.pyc,, django/contrib/messages/__pycache__/middleware.cpython-39.pyc,, django/contrib/messages/__pycache__/utils.cpython-39.pyc,, django/contrib/messages/__pycache__/views.cpython-39.pyc,, -django/contrib/messages/api.py,sha256=3DbnVG5oOBdg499clMU8l2hxCXMXB6S03-HCKVuBXjA,3250 -django/contrib/messages/apps.py,sha256=IWh-40L2X_Pzw7hJ_Qd16X0QCiuK3nxd5ryu5TCmpfg,194 -django/contrib/messages/constants.py,sha256=JD4TpaR4C5G0oxIh4BmrWiVmCACv7rnVgZSpJ8Rmzeg,312 -django/contrib/messages/context_processors.py,sha256=xMrgYeX6AcT_WwS9AYKNDDstbvAwE7_u1ssDVLN_bbg,354 -django/contrib/messages/middleware.py,sha256=2mxncCpJVUgLtjouUGSVl39mTF-QskQpWo2jCOOqV8A,986 +django/contrib/messages/api.py,sha256=sWP2DP-n8ZWOTM-BLFDGrH_l-voGwrSxC0OgEyJt1F4,3071 +django/contrib/messages/apps.py,sha256=yGXBKfV5WF_ElcPbX4wJjXq6jzp39ttnO7sp8N_IzOQ,194 +django/contrib/messages/constants.py,sha256=WZxjzvEoKI7mgChSFp_g9e-zUH8r6JLhu9sFsftTGNA,312 +django/contrib/messages/context_processors.py,sha256=0LniZjxZ7Fx2BxYdJ0tcruhG4kkBEEhsc7Urcf31NnE,354 +django/contrib/messages/middleware.py,sha256=4L-bzgSjTw-Kgh8Wg8MOqkJPyilaxyXi_jH1UpP1h-U,986 django/contrib/messages/storage/__init__.py,sha256=gXDHbQ9KgQdfhYOla9Qj59_SlE9WURQiKzIA0cFH0DQ,392 django/contrib/messages/storage/__pycache__/__init__.cpython-39.pyc,, django/contrib/messages/storage/__pycache__/base.cpython-39.pyc,, django/contrib/messages/storage/__pycache__/cookie.cpython-39.pyc,, django/contrib/messages/storage/__pycache__/fallback.cpython-39.pyc,, django/contrib/messages/storage/__pycache__/session.cpython-39.pyc,, -django/contrib/messages/storage/base.py,sha256=sVkSITZRsdYDvyaS5tqjcw8-fylvcbZpR4ctlpWI5bM,5820 -django/contrib/messages/storage/cookie.py,sha256=c03f85FXbVSZ264MmuqphnjmPrLjpK_NOlpDdscsZso,6776 -django/contrib/messages/storage/fallback.py,sha256=K5CrVJfUDakMjIcqSRt1WZd_1Xco1Bc2AQM3O3ld9aA,2093 -django/contrib/messages/storage/session.py,sha256=kvdVosbBAvI3XBA0G4AFKf0vxLleyzlwbGEgl60DfMQ,1764 -django/contrib/messages/utils.py,sha256=_oItQILchdwdXH08SIyZ-DBdYi7q_uobHQajWwmAeUw,256 -django/contrib/messages/views.py,sha256=I_7C4yr-YLkhTEWx3iuhixG7NrKuyuSDG_CVg-EYRD8,524 +django/contrib/messages/storage/base.py,sha256=Yv87oNn-aAFMatjSmwMJDzMw7rs_ip4F0mBkmiaFPY4,5675 +django/contrib/messages/storage/cookie.py,sha256=O0gc4dWBZqIrZCoA3V6qbfnU513hhdxoLsV_rRbnu1o,7868 +django/contrib/messages/storage/fallback.py,sha256=IbyyZg8cTU-19ZeRg6LndLfRK0SoevDwqKtrqzhVp6c,2095 +django/contrib/messages/storage/session.py,sha256=g95KozBe893u5bZXGqyjwxnzqHuH1WoTTIoFGpxwz6Q,1619 +django/contrib/messages/utils.py,sha256=6PzAryJ0e6oOwtSAMrjAIsYGu_nWIpgMG0p8f_rzOrg,256 +django/contrib/messages/views.py,sha256=R5xD2DLmAO0x6EGpE8TX5bku4zioOiYkQnAtf6r-VAE,523 django/contrib/postgres/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/postgres/__pycache__/__init__.cpython-39.pyc,, django/contrib/postgres/__pycache__/apps.cpython-39.pyc,, django/contrib/postgres/__pycache__/constraints.cpython-39.pyc,, -django/contrib/postgres/__pycache__/expressions.cpython-39.pyc,, django/contrib/postgres/__pycache__/functions.cpython-39.pyc,, django/contrib/postgres/__pycache__/indexes.cpython-39.pyc,, django/contrib/postgres/__pycache__/lookups.cpython-39.pyc,, @@ -2758,12 +2741,11 @@ django/contrib/postgres/aggregates/__pycache__/__init__.cpython-39.pyc,, django/contrib/postgres/aggregates/__pycache__/general.cpython-39.pyc,, django/contrib/postgres/aggregates/__pycache__/mixins.cpython-39.pyc,, django/contrib/postgres/aggregates/__pycache__/statistics.cpython-39.pyc,, -django/contrib/postgres/aggregates/general.py,sha256=Iq9o8-_GTPybU-enLoxOlhBIuwuztRCnck_mZlfiIfM,3272 -django/contrib/postgres/aggregates/mixins.py,sha256=0JK-VUxGU4vNFd5FQkl93L9T2Hrd2fby9UpQLszuOGs,2204 -django/contrib/postgres/aggregates/statistics.py,sha256=xSWk5Z5ZVpM2LSaMgP97pxcijOnPHiPATe3X45poXCI,1511 -django/contrib/postgres/apps.py,sha256=rH0nbVQREE1YSORmeRPbcno_tGieC_DajFNiWLDT2lI,3466 -django/contrib/postgres/constraints.py,sha256=aTZgdxI_iCo2Jpcpxw5ssXN86sb-fYR3OBetXOZM4nE,7279 -django/contrib/postgres/expressions.py,sha256=fo5YASHJtIjexadqskuhYYk4WutofxzymYsivWWJS84,405 +django/contrib/postgres/aggregates/general.py,sha256=iv15n2eLinHplH-aN8RshqIuiphElyDhArqqDl6ZMw0,1744 +django/contrib/postgres/aggregates/mixins.py,sha256=kx0asjl1rWyuCc115jGlAAR4B-oIxCNuSBN3YLVs4_o,2064 +django/contrib/postgres/aggregates/statistics.py,sha256=Snn2JTyiri0m9k64ZWl7pr0LtN5D8N8oi2FIu2qoJ0o,1462 +django/contrib/postgres/apps.py,sha256=pbelRLsdlD9OeCRylcxTVUClrqBpEnCOdBa9RKKN9WM,3139 +django/contrib/postgres/constraints.py,sha256=MeG1czYHitrndtuA2BPbKokvGKFU5s3vikaoBD1Y95o,7100 django/contrib/postgres/fields/__init__.py,sha256=Xo8wuWPwVNOkKY-EwV9U1zusQ2DjMXXtL7_8R_xAi5s,148 django/contrib/postgres/fields/__pycache__/__init__.cpython-39.pyc,, django/contrib/postgres/fields/__pycache__/array.cpython-39.pyc,, @@ -2772,22 +2754,24 @@ django/contrib/postgres/fields/__pycache__/hstore.cpython-39.pyc,, django/contrib/postgres/fields/__pycache__/jsonb.cpython-39.pyc,, django/contrib/postgres/fields/__pycache__/ranges.cpython-39.pyc,, django/contrib/postgres/fields/__pycache__/utils.cpython-39.pyc,, -django/contrib/postgres/fields/array.py,sha256=M6oR4upd4OgIaUqge7TJtZbL5eoGxXlVUBUKlOJpZjA,10778 -django/contrib/postgres/fields/citext.py,sha256=_c0QYlKJa5CS2_XFbv1KB0HwG3wJHeQ8PxGVxRrKQrk,438 -django/contrib/postgres/fields/hstore.py,sha256=WWWEoBfMtAjd226vvjFtGqbHMHFCjSly-BEhm9UN1qQ,3276 -django/contrib/postgres/fields/jsonb.py,sha256=ncMGT6WY70lCbcmhwtu2bjRmfDMUIvCr76foUv7tqv0,406 -django/contrib/postgres/fields/ranges.py,sha256=EJ5_adoNlKwAY529T9NQXb3r_tjGwjqYvAc2NvFtrxw,9683 +django/contrib/postgres/fields/array.py,sha256=0-5iXKvWs01MjfVevlaQJR1lMhtDpguVuebhLtb-jyk,10543 +django/contrib/postgres/fields/citext.py,sha256=G40UZv4zop8Zrq2vMhluZ-MT7yPLEc8IEDi3hZ27gGw,439 +django/contrib/postgres/fields/hstore.py,sha256=BfQ3ifm7NGTlKHqYvazgaWoDf6GDRiqDwAcdMgnZ0co,3243 +django/contrib/postgres/fields/jsonb.py,sha256=7OGh-sP4qtQkAZWLZf_2F0UBAOVAK8W5oUW2JcxiukU,1428 +django/contrib/postgres/fields/ranges.py,sha256=sW35EO9kLeEUXowMntEDak4FTEiNQ8k8jRShkDedLyU,9579 django/contrib/postgres/fields/utils.py,sha256=TV-Aj9VpBb13I2iuziSDURttZtz355XakxXnFwvtGio,95 -django/contrib/postgres/forms/__init__.py,sha256=NjENn2-C6BcXH4T8YeC0K2AbDk8MVT8tparL3Q4OF6g,89 +django/contrib/postgres/forms/__init__.py,sha256=GSqucR50I9jrZUYZUFVmb8nV_FSlXu1BcCpFck2pVXI,118 django/contrib/postgres/forms/__pycache__/__init__.cpython-39.pyc,, django/contrib/postgres/forms/__pycache__/array.cpython-39.pyc,, django/contrib/postgres/forms/__pycache__/hstore.cpython-39.pyc,, +django/contrib/postgres/forms/__pycache__/jsonb.cpython-39.pyc,, django/contrib/postgres/forms/__pycache__/ranges.cpython-39.pyc,, -django/contrib/postgres/forms/array.py,sha256=LRUU3fxXePptMh3lolxhX4sbMjNSvnzMvNgcJolKfZc,8401 -django/contrib/postgres/forms/hstore.py,sha256=XN5xOrI-jCeTsWFEjPXf6XMaLzJdXiqA6pTdGSjWdOw,1767 -django/contrib/postgres/forms/ranges.py,sha256=FT6pt98n1xHuWbDeTZJl-0JCc2hepao4YPeI-3DzCRM,3434 -django/contrib/postgres/functions.py,sha256=7v6J01QQvX70KFyg9hDc322PgvT62xZqWlzp_vrl8bA,252 -django/contrib/postgres/indexes.py,sha256=pfwflPdWybvjFe3YS4BOD3gF04ExgnXMMnPlnztfkKM,8108 +django/contrib/postgres/forms/array.py,sha256=qWmxMDlo5UfKTET03kqyhXF1-b3rGCnuuAOhyvbzHL8,8065 +django/contrib/postgres/forms/hstore.py,sha256=f7PJ41fsd8D7cvyJG-_ugslM-hXL7qnZPdx08UZQNXY,1766 +django/contrib/postgres/forms/jsonb.py,sha256=WmDxuxhULUYO8_nKXXsOz26ta4oye0MQwHhDCW5Oe5g,484 +django/contrib/postgres/forms/ranges.py,sha256=GZX5dB4q5G1-FMo54r_gW3Jl89rbnL-EnDetSFNRH_A,3344 +django/contrib/postgres/functions.py,sha256=zHeAyKR5MhnsIGI5qbtmRdxPm8OtycEBE5OmCNyynD8,252 +django/contrib/postgres/indexes.py,sha256=a0q9tl0tTyPVSonmCU2unKCWJQ_AjWeO2OcR6TTs9TA,8222 django/contrib/postgres/jinja2/postgres/widgets/split_array.html,sha256=AzaPLlNLg91qkVQwwtAJxwOqDemrtt_btSkWLpboJDs,54 django/contrib/postgres/locale/af/LC_MESSAGES/django.mo,sha256=kDeL_SZezO8DRNMRh2oXz94YtAK1ZzPiK5dftqAonKI,2841 django/contrib/postgres/locale/af/LC_MESSAGES/django.po,sha256=ALKUHbZ8DE6IH80STMJhGOoyHB8HSSxI4PlX_SfxJWc,3209 @@ -2799,8 +2783,8 @@ django/contrib/postgres/locale/az/LC_MESSAGES/django.mo,sha256=K-2weZNapdDjP5-ec django/contrib/postgres/locale/az/LC_MESSAGES/django.po,sha256=Pn47g_NvMgSBjguFLT_AE1QzxOGXOYjA-g_heXAT_tU,3214 django/contrib/postgres/locale/be/LC_MESSAGES/django.mo,sha256=0Y6S-XR45rgw0zEZgjpRJyNm7szHxr9XOUyolo_5cN0,4134 django/contrib/postgres/locale/be/LC_MESSAGES/django.po,sha256=KIkbhabWDYo4iDaQ8Dt0kxH_VB2wTFsS0rGs9zzKoKU,4635 -django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo,sha256=dkM1WSo5SgBglvJXNVvcIhKHU0ZjUJxmy4cX6_cJgZs,3515 -django/contrib/postgres/locale/bg/LC_MESSAGES/django.po,sha256=jalX0o2VjTVhXJIBKkyEk3aMjqYyNywmSGmyve9cu5M,3974 +django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo,sha256=5YRXtACYtWmAdz7Nmr9Btqypb5Ncu8dswf8gzurOJuo,2969 +django/contrib/postgres/locale/bg/LC_MESSAGES/django.po,sha256=CN_a4ac_1ZLxUHFTbYf5BmYHKBaxuHd7OIBFep558m0,3645 django/contrib/postgres/locale/ca/LC_MESSAGES/django.mo,sha256=XR1UEZV9AXKFz7XrchjRkd-tEdjnlmccW_I7XANyMns,2904 django/contrib/postgres/locale/ca/LC_MESSAGES/django.po,sha256=5wPLvkODU_501cHPZ7v0n89rmFrsuctt7T8dUBMfQ0Q,3430 django/contrib/postgres/locale/cs/LC_MESSAGES/django.mo,sha256=_EmT9NnoX3xeRU-AI5sPlAszjzC0XwryWOmj8d07ox8,3388 @@ -2811,12 +2795,10 @@ django/contrib/postgres/locale/de/LC_MESSAGES/django.mo,sha256=B3HwniAOjSHmhuuqp django/contrib/postgres/locale/de/LC_MESSAGES/django.po,sha256=dZu8_1FIFKw67QnhXsGibfWT2W3d07Ro9CU8Y_HolvE,3468 django/contrib/postgres/locale/dsb/LC_MESSAGES/django.mo,sha256=4Ymt58bCjpZlmNDZbFO8TtI6agusGvTwlDCjip_q8nQ,3573 django/contrib/postgres/locale/dsb/LC_MESSAGES/django.po,sha256=m1PlbIRBIkTnbe2jLzcR0_Oi9MujrsS82apXd8GDkcs,4033 -django/contrib/postgres/locale/el/LC_MESSAGES/django.mo,sha256=NmzROkTfSbioGv8exM3UdMDnRAxR65YMteGv9Nhury4,3583 -django/contrib/postgres/locale/el/LC_MESSAGES/django.po,sha256=4WuswUwrInAh-OPX9k7gDdLb-oMKp1vQFUGvfm0ej00,4144 +django/contrib/postgres/locale/el/LC_MESSAGES/django.mo,sha256=haeVSD4yQq0zxi5mpDItnRv9DpBVOgQ2IOIS6T9OGxQ,3428 +django/contrib/postgres/locale/el/LC_MESSAGES/django.po,sha256=VeB_UwU4IFZCSVum_vTekAaDsYEvanmDywLj3EsPYBo,4013 django/contrib/postgres/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/postgres/locale/en/LC_MESSAGES/django.po,sha256=FtuWLiTQcIvK-kpbZujmawA0yQeRERhzfoJeEiOAyJw,2865 -django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.mo,sha256=WA0RSssD8ljI16g6DynQZQLQhd_0XR8ilrnJnepsIFg,2839 -django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.po,sha256=4JASYUpYlQlSPREPvMxFBqDpDhprlkI1GpAqTJrmb10,3215 django/contrib/postgres/locale/eo/LC_MESSAGES/django.mo,sha256=1wqM_IVO8Dl9AefzvWYuoS4eNTrBg7LDH6XUMovKi9A,2742 django/contrib/postgres/locale/eo/LC_MESSAGES/django.po,sha256=r2tpOblfLAAHMacDWU-OVXTQus_vvAPMjUzVfrV_T7U,3217 django/contrib/postgres/locale/es/LC_MESSAGES/django.mo,sha256=GoDmVupnksF_ypFyzFSjsGYb6EKA--HwvJfByZtSlTA,2917 @@ -2879,16 +2861,12 @@ django/contrib/postgres/locale/ml/LC_MESSAGES/django.mo,sha256=N47idWIsmtghZ_D53 django/contrib/postgres/locale/ml/LC_MESSAGES/django.po,sha256=lt_7fGZV7BCB2XqFWIQQtH4niU4oMBfGzQQuN5sD0fo,2947 django/contrib/postgres/locale/mn/LC_MESSAGES/django.mo,sha256=VWeXaMvdqhW0GHs1Irb1ikTceH7jMKH_xMzKLH0vKZg,3310 django/contrib/postgres/locale/mn/LC_MESSAGES/django.po,sha256=p3141FJiYrkV8rocgqdxnV05FReQYZmosv9LI46FlfE,3867 -django/contrib/postgres/locale/ms/LC_MESSAGES/django.mo,sha256=m3JZm1IIMZwmpvIs3oV0roYCeR_UlswHyCpZjjE6-A8,2712 -django/contrib/postgres/locale/ms/LC_MESSAGES/django.po,sha256=HCMBA1fxKLJct14ywap0PYVBi2bDp2F97Ms5_-G_Pwg,3025 django/contrib/postgres/locale/nb/LC_MESSAGES/django.mo,sha256=3h8DqEFG39i6uHY0vpXuGFmoJnAxTtRFy1RazcYIXfg,2849 django/contrib/postgres/locale/nb/LC_MESSAGES/django.po,sha256=gDUg-HDg3LiYMKzb2QaDrYopqaJmbvnw2Fo-qhUHFuI,3252 django/contrib/postgres/locale/ne/LC_MESSAGES/django.mo,sha256=5XdBLGMkn20qeya3MgTCpsIDxLEa7PV-i2BmK993iRc,875 django/contrib/postgres/locale/ne/LC_MESSAGES/django.po,sha256=1QLLfbrHneJmxM_5UTpNIYalP-qX7Bn7bmj4AfDLIzE,2421 django/contrib/postgres/locale/nl/LC_MESSAGES/django.mo,sha256=ttUzGWvxJYw71fVbcXCwzetyTWERBsURTe_nsf_axq0,2951 django/contrib/postgres/locale/nl/LC_MESSAGES/django.po,sha256=ENw-dI6FHFqxclQKdefthCIVgp41HoIYj0IBmRCz0Vw,3515 -django/contrib/postgres/locale/nn/LC_MESSAGES/django.mo,sha256=RdMFozwxYIckBY40mJhN-jjkghztKn0-ytCtqxFHBMY,2836 -django/contrib/postgres/locale/nn/LC_MESSAGES/django.po,sha256=vl8NkY342eonqbrj89eCR_8PsJpeQuaRjxems-OPIBk,3184 django/contrib/postgres/locale/pl/LC_MESSAGES/django.mo,sha256=HZOPQ8tC_vWEqsCAtDquwnyhEiECyKSmVHuoklAj6hA,3444 django/contrib/postgres/locale/pl/LC_MESSAGES/django.po,sha256=gKrgT2Mpuxhs6ym_D4yJQVC0tVr9KSaZBP7Fc4yW-wY,4150 django/contrib/postgres/locale/pt/LC_MESSAGES/django.mo,sha256=KZvJXjrIdtxbffckcrRV3nJ5GnID6PvqAb7vpOiWpHE,2745 @@ -2925,26 +2903,26 @@ django/contrib/postgres/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=jUqnfwS-XMNK django/contrib/postgres/locale/zh_Hans/LC_MESSAGES/django.po,sha256=7L9pBCN-dScEAfPIe4u-jY14S6NgVe6seZHaqthgms0,3060 django/contrib/postgres/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=Twqt8SVetuVV6UQ8ne48RfXILh2I9_-5De7cIrd5Lvc,2586 django/contrib/postgres/locale/zh_Hant/LC_MESSAGES/django.po,sha256=5qE-q9uXlHM59soKgNSqeCfP-DnFuYI4fXLAbQctJ8c,2962 -django/contrib/postgres/lookups.py,sha256=Y8x4RxGGkVnlqJfNIWcQvBA1Uk5cKtsr4FPUF7hUSuo,1601 -django/contrib/postgres/operations.py,sha256=wBooH3gFy8arxZvMiPhU6w_5fGwwlcWb8OBvm-iiy1s,11808 -django/contrib/postgres/search.py,sha256=ryqdaExbKFOvFcjqEMBib064nQGBujp6qhP-6kaPn2c,11344 -django/contrib/postgres/serializers.py,sha256=wCg0IzTNeuVOiC2cdy1wio6gChjqVvH6Ri4hkCkEeXU,435 -django/contrib/postgres/signals.py,sha256=zpjL9gEF0Pc1peXe-ri9nxGocy3UNB9ORwQDvn9t5ow,2289 +django/contrib/postgres/lookups.py,sha256=PzWopUkxh5JRkqAozJN-RaLs7gwaKhXzHkIE75yQ-g4,1478 +django/contrib/postgres/operations.py,sha256=Nt-prrtntlN1GSGTe6zz4m-Y5J9AR4ysn0HqaJes0MA,9240 +django/contrib/postgres/search.py,sha256=8MtUU6278Rov1qQLubZanx3O1DKT7RhKRrm4bWS6nf0,10427 +django/contrib/postgres/serializers.py,sha256=EPW4-JtgMV_x4_AosG4C-HLX3K4O9ls9Ezw9f07iHd8,435 +django/contrib/postgres/signals.py,sha256=MmUklgaTW1-UBMGQTxNO_1fsO7mZugGs9ScovuCIyJo,2245 django/contrib/postgres/templates/postgres/widgets/split_array.html,sha256=AzaPLlNLg91qkVQwwtAJxwOqDemrtt_btSkWLpboJDs,54 -django/contrib/postgres/utils.py,sha256=32nCnzdMZ7Ra4dDonbIdv1aCppV3tnQnoEX9AhCJe38,1187 -django/contrib/postgres/validators.py,sha256=LT4W70ZC6aJ_uHZzu1VbFTjEY2p0V0hKqnKaTobNV78,2805 +django/contrib/postgres/utils.py,sha256=gBGBmAYMKLkB6nyaRgx5Yz_00bXaOA6BDK9koiE-_co,1187 +django/contrib/postgres/validators.py,sha256=CA_iygE2q3o8tXlQ9JfMYxoO6HDJk3D0PIcmGrahwdI,2675 django/contrib/redirects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/redirects/__pycache__/__init__.cpython-39.pyc,, django/contrib/redirects/__pycache__/admin.cpython-39.pyc,, django/contrib/redirects/__pycache__/apps.cpython-39.pyc,, django/contrib/redirects/__pycache__/middleware.cpython-39.pyc,, django/contrib/redirects/__pycache__/models.cpython-39.pyc,, -django/contrib/redirects/admin.py,sha256=1bPOgeZYRYCHdh7s2SpXnuL2WsfdQjD96U5Y3xhRY8g,314 -django/contrib/redirects/apps.py,sha256=1uS5EBp7WwDnY0WHeaRYo7VW9j-s20h4KDdImodjCNg,251 -django/contrib/redirects/locale/af/LC_MESSAGES/django.mo,sha256=EZpwI7hxr96D4CUt6e-kJHgkE3Q5k9RAmPjn6kXvE8A,1136 -django/contrib/redirects/locale/af/LC_MESSAGES/django.po,sha256=kDPrxqvMg3hn12fGyTaImC1gOtTjSxuJtbKdA7jvl_4,1367 -django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo,sha256=FfPauXNUmQxq0R1-eQ2xw2WY1Oi33sLwVhyKX10_zFw,1336 -django/contrib/redirects/locale/ar/LC_MESSAGES/django.po,sha256=X0xX51asSDWedd56riJ4UrsCGEjH-lZdkcilIg4amgI,1595 +django/contrib/redirects/admin.py,sha256=P9wp8yIvDjJSfIXpWYM2ftDlVhKvte_0AM9Ky_j1JIs,314 +django/contrib/redirects/apps.py,sha256=GtzXJDrUGaZQPHBr8ciXPtb76urejXkuG-A34NvSwJ8,251 +django/contrib/redirects/locale/af/LC_MESSAGES/django.mo,sha256=UqXzx3fQxw4n7RGNgnp4lzLJ93DPRAgIAg6bwPs5GFY,1119 +django/contrib/redirects/locale/af/LC_MESSAGES/django.po,sha256=JvDnHyWH_-IyOTSR36hwSBmd_fXa3trpUAgEThdtDvM,1260 +django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo,sha256=45kuFTs85G4XxI1OrBnkrgQJJfQE0cveTs1GEsf3un4,1311 +django/contrib/redirects/locale/ar/LC_MESSAGES/django.po,sha256=L_mv0nptTvKi3ONK2yJBINoqPkQ0-FIpWu1FWKlzI-s,1565 django/contrib/redirects/locale/ar_DZ/LC_MESSAGES/django.mo,sha256=Nt17Ugj4UVEsyg-y7UYgCnAttSX_pRR5OLS-qRbpZvI,1336 django/contrib/redirects/locale/ar_DZ/LC_MESSAGES/django.po,sha256=ckrjwULi4Sx_mBOxadvywXOy6vyecQYWryACnyg1XGA,1511 django/contrib/redirects/locale/ast/LC_MESSAGES/django.mo,sha256=a1ixBQQIdBZ7o-ADnF2r74CBtPLsuatG7txjc05_GXI,1071 @@ -2953,32 +2931,32 @@ django/contrib/redirects/locale/az/LC_MESSAGES/django.mo,sha256=KzpRUrONOi5Cdr9s django/contrib/redirects/locale/az/LC_MESSAGES/django.po,sha256=RGjd2J_pRdSkin4UlKxg7kc3aA8PCQRjDPXkpGZHdn0,1347 django/contrib/redirects/locale/be/LC_MESSAGES/django.mo,sha256=fVqy28ml508UJf5AA-QVsS5dzKI8Q_ugZZ34WjTpJ-s,1426 django/contrib/redirects/locale/be/LC_MESSAGES/django.po,sha256=zHBVewcpt0KoavV96v3F4wybqtkGb1jUuPz7sbiWWDI,1662 -django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo,sha256=o-ETSDGtAFZRo3SPd_IHe0mJ3R0RHA32KpgfOmUH11M,1279 -django/contrib/redirects/locale/bg/LC_MESSAGES/django.po,sha256=9qm8s6vj-0LStnyEJ8iYVi13_MfugVAAs2RHvIi7kW8,1587 +django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo,sha256=fEXrzyixSGCWaWu5XxVsjRKMlPwYkORpFtAiwNNShvM,1268 +django/contrib/redirects/locale/bg/LC_MESSAGES/django.po,sha256=_Xha-uOePDqOqOVmYgcR8auVgNT3CS-Z_V_vwyTlwfk,1493 django/contrib/redirects/locale/bn/LC_MESSAGES/django.mo,sha256=SbQh_pgxNCogvUFud7xW9T6NTAvpaQb2jngXCtpjICM,1319 django/contrib/redirects/locale/bn/LC_MESSAGES/django.po,sha256=LgUuiPryDLSXxo_4KMCdjM5XC3BiRfINuEk0s5PUQYQ,1511 django/contrib/redirects/locale/br/LC_MESSAGES/django.mo,sha256=Yt8xo5B5LJ9HB8IChCkj5mljFQAAKlaW_gurtF8q8Yw,1429 django/contrib/redirects/locale/br/LC_MESSAGES/django.po,sha256=L2qPx6mZEVUNay1yYEweKBLr_fXVURCnACfsezfP_pI,1623 django/contrib/redirects/locale/bs/LC_MESSAGES/django.mo,sha256=0Yak4rXHjRRXLu3oYYzvS8qxvk2v4IFvUiDPA68a5YI,1115 django/contrib/redirects/locale/bs/LC_MESSAGES/django.po,sha256=s9Nhx3H4074hlSqo1zgQRJbozakdJTwA1aTuMSqEJWw,1316 -django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo,sha256=VHE6qHCEoA7rQk0fMUpoTfwqSfu63-CiOFvhvKp5DMQ,1136 -django/contrib/redirects/locale/ca/LC_MESSAGES/django.po,sha256=PSMb_7iZBuYhtdR8byh9zr9dr50Z_tQ518DUlqoEA_M,1484 +django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo,sha256=sp3kaIhlTGdtYeHjZ8fQypdYKINsea8C0tufuCAlFBY,1106 +django/contrib/redirects/locale/ca/LC_MESSAGES/django.po,sha256=SMB90_SWZQF1cpWYjEzwy9w3Y9w8rtZND6WW-degBCs,1417 django/contrib/redirects/locale/cs/LC_MESSAGES/django.mo,sha256=UwYsoEHsg7FJLVe0JxdOa1cTGypqJFienAbWe7Vldf0,1229 django/contrib/redirects/locale/cs/LC_MESSAGES/django.po,sha256=hnWJLXX7IjwZK7_8L3p-dpj5XpDmEo7lQ7-F4upjn7U,1504 django/contrib/redirects/locale/cy/LC_MESSAGES/django.mo,sha256=NSGoK12A7gbtuAuzQEVFPNSZMqqmhHyRvTEn9PUm9So,1132 django/contrib/redirects/locale/cy/LC_MESSAGES/django.po,sha256=jDmC64z5exPnO9zwRkBmpa9v3DBlaeHRhqZYPoWqiIY,1360 django/contrib/redirects/locale/da/LC_MESSAGES/django.mo,sha256=_UVfTMRG__5j7Ak8Q3HtXSy_DPGpZ1XvKj9MHdmR_xI,1132 django/contrib/redirects/locale/da/LC_MESSAGES/django.po,sha256=RAWWbZXbJciNSdw4skUEoTnOb19iKXAe1KXJLWi0zPQ,1418 -django/contrib/redirects/locale/de/LC_MESSAGES/django.mo,sha256=uh-ldy-QkWS5-ARX6cLyzxzdhbTb_chyEbBPFCvCKuE,1155 -django/contrib/redirects/locale/de/LC_MESSAGES/django.po,sha256=hhGNnVCRV4HNxhCYfmVXTOIkabD7qsVQccwxKa5Tz9g,1424 +django/contrib/redirects/locale/de/LC_MESSAGES/django.mo,sha256=8Zn398kFjKp-I9CLi6wAMw_0PmDrK4cJc1SjnQ_K8bY,1095 +django/contrib/redirects/locale/de/LC_MESSAGES/django.po,sha256=hXoA4dzgP29HekziQtDHeQb_GcRCK9xAhICB7gMeHgE,1315 django/contrib/redirects/locale/dsb/LC_MESSAGES/django.mo,sha256=LXgczA38RzrN7zSWpxKy8_RY4gPg5tZLl30CJGjJ63s,1236 django/contrib/redirects/locale/dsb/LC_MESSAGES/django.po,sha256=rI9dyDp7zuZ6CjvFyo2OkGUDK5XzdvdI0ma8IGVkjp4,1431 -django/contrib/redirects/locale/el/LC_MESSAGES/django.mo,sha256=sD3HT4e53Yd3HmQap_Mqlxkm0xF98A6PFW8Lil0PihI,1395 -django/contrib/redirects/locale/el/LC_MESSAGES/django.po,sha256=puhVCcshg5HaPHsVAOucneVgBYT6swhCCBpVGOZykgA,1716 +django/contrib/redirects/locale/el/LC_MESSAGES/django.mo,sha256=kzCurtbtzdZsJOzqLbTtn3kjltOnBq6Nd8p8EFTllF0,1384 +django/contrib/redirects/locale/el/LC_MESSAGES/django.po,sha256=-lFhtPYSaYaS81Zh1CX9vxx0lvQDpAUsTBRNT48ne94,1611 django/contrib/redirects/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/redirects/locale/en/LC_MESSAGES/django.po,sha256=u4RcMkFmNvlG9Bv6kM0a0scWUMDUbTEDJGR90-G8C0E,1123 -django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo,sha256=wxCpSLGl_zsE47kDwilDkpihazwHkA363PvtGOLWhdk,1127 -django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po,sha256=zujH1WuxoHw_32flptG0x2Ob_BlilLKXuMjQxVbZmgw,1307 +django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo,sha256=dTndJxA-F1IE_nMUOtf1sRr7Kq2s_8yjgKk6mkWkVu4,486 +django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po,sha256=CcP5GVZaImhRgohA5zy5K3rCscOlBtn81DB-V26-Wxg,958 django/contrib/redirects/locale/en_GB/LC_MESSAGES/django.mo,sha256=VscL30uJnV-eiQZITpBCy0xk_FfKdnMh4O9Hk4HGxww,1053 django/contrib/redirects/locale/en_GB/LC_MESSAGES/django.po,sha256=loe8xIVjZ7eyteQNLPoa-QceBZdgky22dR6deK5ubmA,1246 django/contrib/redirects/locale/eo/LC_MESSAGES/django.mo,sha256=pZo0DSbfGGTHi-jgaTGp29kJK-iplaai-WXJoOPluMA,1138 @@ -2993,8 +2971,8 @@ django/contrib/redirects/locale/es_MX/LC_MESSAGES/django.mo,sha256=38fbiReibMAmC django/contrib/redirects/locale/es_MX/LC_MESSAGES/django.po,sha256=t7R6PiQ1bCc7jhfMrjHlZxVQ6BRlWT2Vv4XXhxBD_Oo,1397 django/contrib/redirects/locale/es_VE/LC_MESSAGES/django.mo,sha256=59fZBDut-htCj38ZUoqPjhXJPjZBz-xpU9__QFr3kLs,486 django/contrib/redirects/locale/es_VE/LC_MESSAGES/django.po,sha256=f4XZW8OHjRJoztMJtSDCxd2_Mfy-XK44hLtigjGSsZY,958 -django/contrib/redirects/locale/et/LC_MESSAGES/django.mo,sha256=34-Z1s9msdnj6U7prMctEWCxAR8TNnP44MIoyUuFsls,1131 -django/contrib/redirects/locale/et/LC_MESSAGES/django.po,sha256=1VWcUbM9z_nNmiGnT9Mka3Y3ZLRVHuJdS_j_yNXvmQ0,1479 +django/contrib/redirects/locale/et/LC_MESSAGES/django.mo,sha256=10TVT6ftY7UuZwJsUImwNuqo6mcHGgVG-YVNiyGd9Y4,1097 +django/contrib/redirects/locale/et/LC_MESSAGES/django.po,sha256=fI2Wf7WcAV2n-weyPMrQot-c7VOtciTks6QzGzh_RQE,1404 django/contrib/redirects/locale/eu/LC_MESSAGES/django.mo,sha256=yHlAEz01pWse4ZworAj7JiATUam5Fp20EZd_3PRgSNw,1126 django/contrib/redirects/locale/eu/LC_MESSAGES/django.po,sha256=zAvSdahjvq727hXeGjHJ_R5L5meCrOv98tbH3rwlBcE,1404 django/contrib/redirects/locale/fa/LC_MESSAGES/django.mo,sha256=vZa1KKm2y8duEv9UbJMyiM8WO2EAXcevdR3Lj1ISgLU,1234 @@ -3007,8 +2985,8 @@ django/contrib/redirects/locale/fy/LC_MESSAGES/django.mo,sha256=YQQy7wpjBORD9Isd django/contrib/redirects/locale/fy/LC_MESSAGES/django.po,sha256=D7xverCbf3kTCcFM8h7EKWM5DcxZRqeOSKDB1irbKeE,948 django/contrib/redirects/locale/ga/LC_MESSAGES/django.mo,sha256=blwOMshClFZKvOZXVvqENK_E_OkdS1ydbjQCDXcHXd4,1075 django/contrib/redirects/locale/ga/LC_MESSAGES/django.po,sha256=76rdrG4GVbcKwgUQN4bB-B0t6hpivCA_ehf4uzGM_mY,1341 -django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo,sha256=baZXdulbPZwe4_Q3OwfHFl4GJ4hCYtoZz-lE4wcdJvg,1250 -django/contrib/redirects/locale/gd/LC_MESSAGES/django.po,sha256=M4E2giFgzRowd3OsvhD389MyJmT5osKz1Vs1sEfmUpU,1428 +django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo,sha256=D_gqvGcUh2X9888kwDdFG1tjuAJUtQ2LhK4K4xCyiuI,1219 +django/contrib/redirects/locale/gd/LC_MESSAGES/django.po,sha256=PnKpFPVIzSpflfuobqU6Z5aV3ke5kNWJHWfDl1oCF3w,1397 django/contrib/redirects/locale/gl/LC_MESSAGES/django.mo,sha256=LoMrpBThJSmWzZ1wT66xGndnNCVCOq2eCEyo88qKwkA,1127 django/contrib/redirects/locale/gl/LC_MESSAGES/django.po,sha256=d8qXhC2wI45yXtFJuMBgibzHsCkZSxAD3I6pVdpxlSU,1313 django/contrib/redirects/locale/he/LC_MESSAGES/django.mo,sha256=MnCcK4Vb3Z5ZQ2A52tb0kM60hmoHQJ0UrWcrhuI2RK0,1204 @@ -3025,26 +3003,26 @@ django/contrib/redirects/locale/hy/LC_MESSAGES/django.mo,sha256=gT5x1TZXMNyBwfmQ django/contrib/redirects/locale/hy/LC_MESSAGES/django.po,sha256=40QTpth2AVeoy9P36rMJC2C82YsBh_KYup19WL6zM6w,1359 django/contrib/redirects/locale/ia/LC_MESSAGES/django.mo,sha256=PDB5ZQP6iH31xN6N2YmPZYjt6zzc88TRmh9_gAWH2U0,1152 django/contrib/redirects/locale/ia/LC_MESSAGES/django.po,sha256=GXjbzY-cQz2QLx_iuqgijT7VUMcoNKL7prbP6yIbj8E,1297 -django/contrib/redirects/locale/id/LC_MESSAGES/django.mo,sha256=XEsvVWMR9As9csO_6iXNAcLZrErxz3HfDj5GTe06fJU,1105 -django/contrib/redirects/locale/id/LC_MESSAGES/django.po,sha256=t8FoC1xIB-XHDplyDJByQGFnHggxR0LSfUMGwWoAKWE,1410 +django/contrib/redirects/locale/id/LC_MESSAGES/django.mo,sha256=O7EKMm1GR4o1JXQV5vFP58nFK-__2evesMPJFucOxsc,1067 +django/contrib/redirects/locale/id/LC_MESSAGES/django.po,sha256=4qK_1_82j2RmRm4d6JWMskOCy1QIeuNral9xP1x2s10,1364 django/contrib/redirects/locale/io/LC_MESSAGES/django.mo,sha256=vz7TWRML-DFDFapbEXTByb9-pRQwoeJ0ApSdh6nOzXY,1019 django/contrib/redirects/locale/io/LC_MESSAGES/django.po,sha256=obStuMYYSQ7x2utkGS3gekdPfnsNAwp3DcNwlwdg1sI,1228 django/contrib/redirects/locale/is/LC_MESSAGES/django.mo,sha256=aMjlGilYfP7clGriAp1Za60uCD40rvLt9sKXuYX3ABg,1040 django/contrib/redirects/locale/is/LC_MESSAGES/django.po,sha256=nw5fxVV20eQqsk4WKg6cIiKttG3zsITSVzH4p5xBV8s,1299 django/contrib/redirects/locale/it/LC_MESSAGES/django.mo,sha256=bBj6dvhZSpxojLZ0GiMBamh1xiluxAYMt6RHubi9CxU,1092 django/contrib/redirects/locale/it/LC_MESSAGES/django.po,sha256=NHSVus7ixtrB7JDIrYw22srZcse5i4Z9y8Ply_-Jcts,1390 -django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo,sha256=XSJw3iLK0gYVjZ86MYuV4jfoiN_-WkH--oMK5uW9cs8,1193 -django/contrib/redirects/locale/ja/LC_MESSAGES/django.po,sha256=SlYrmC3arGgS7SL8cCnq7d37P-bQGcmpgUXAwVC2eRw,1510 +django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo,sha256=gE1gwugGwKaDtpGI1PuThkuy8rBhxpoAO8Ecucp1iUY,1133 +django/contrib/redirects/locale/ja/LC_MESSAGES/django.po,sha256=aXGFOdUr825bNhtXi8ZMTLysw6MydtkIoztqPT1qO38,1402 django/contrib/redirects/locale/ka/LC_MESSAGES/django.mo,sha256=0aOLKrhUX6YAIMNyt6KES9q2iFk2GupEr76WeGlJMkk,1511 -django/contrib/redirects/locale/ka/LC_MESSAGES/django.po,sha256=AQWIEdhxp55XnJwwHrUxxQaGbLJPmdo1YLeT86IJqnY,1725 +django/contrib/redirects/locale/ka/LC_MESSAGES/django.po,sha256=bK3ULAIG00Nszoz74r-W3W8CihaoijYkWlc6sUqJXrg,1720 django/contrib/redirects/locale/kab/LC_MESSAGES/django.mo,sha256=Ogx9NXK1Nfw4ctZfp-slIL81ziDX3f4DZ01OkVNY5Tw,699 django/contrib/redirects/locale/kab/LC_MESSAGES/django.po,sha256=gI6aUPkXH-XzKrStDsMCMNfQKDEc-D1ffqE-Z-ItQuI,1001 django/contrib/redirects/locale/kk/LC_MESSAGES/django.mo,sha256=KVLc6PKL1MP_Px0LmpoW2lIvgLiSzlvoJ9062F-s3Zw,1261 -django/contrib/redirects/locale/kk/LC_MESSAGES/django.po,sha256=Xoy4mnOT51F_GS1oIO91EAuwt-ZfePKh-sutedo6D_g,1478 +django/contrib/redirects/locale/kk/LC_MESSAGES/django.po,sha256=k3TtiYJ7x50M19DCu2eLcsCroKusJ3paiC2RvZ-920A,1473 django/contrib/redirects/locale/km/LC_MESSAGES/django.mo,sha256=tcW1s7jvTG0cagtdRNT0jSNkhX-B903LKl7bK31ZvJU,1248 django/contrib/redirects/locale/km/LC_MESSAGES/django.po,sha256=KJ4h1umpfFLdsWZtsfXoeOl6cUPUD97U4ISWt80UZ2U,1437 -django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo,sha256=24GHcQlEoCDri-98eLtqLbGjtJz9cTPAfYdAijsL5ck,788 -django/contrib/redirects/locale/kn/LC_MESSAGES/django.po,sha256=xkH24itr2fpuCQMGQ3xssOqaN_7KzM-GLy0u00ti27I,1245 +django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo,sha256=-gqNBZVFvxqOiPWUb9jH4myXufHHfdyr_yROTfpk2jU,1396 +django/contrib/redirects/locale/kn/LC_MESSAGES/django.po,sha256=qFM2v3ys7E5u-WJE7CR-2IMrDTqFjNq96OQ1syMDWoI,1588 django/contrib/redirects/locale/ko/LC_MESSAGES/django.mo,sha256=RJRxocjiFAeDTEVtAawhpkv99axVeNmLDyBhwmjGCcM,1079 django/contrib/redirects/locale/ko/LC_MESSAGES/django.po,sha256=QNDHQmvOgJnfpv9vMIIZVw--4YXSArJeOJks75m3zKo,1445 django/contrib/redirects/locale/ky/LC_MESSAGES/django.mo,sha256=4jX_g-hledmjWEx0RvY99G5QcBj_mQt_HZzpd000J44,1265 @@ -3052,7 +3030,7 @@ django/contrib/redirects/locale/ky/LC_MESSAGES/django.po,sha256=yvx21nxsqqVzPyyx django/contrib/redirects/locale/lb/LC_MESSAGES/django.mo,sha256=xokesKl7h7k9dXFKIJwGETgwx1Ytq6mk2erBSxkgY-o,474 django/contrib/redirects/locale/lb/LC_MESSAGES/django.po,sha256=Hv1CF9CC78YuVVNpklDtPJDU5-iIUeuXcljewmc9akg,946 django/contrib/redirects/locale/lt/LC_MESSAGES/django.mo,sha256=reiFMXJnvE4XUosbKjyvUFzl4IKjlJoFK1gVJE9Tbnc,1191 -django/contrib/redirects/locale/lt/LC_MESSAGES/django.po,sha256=G56UIYuuVLgwzHCIj_suHNYPe1z76Y_cauWfGEs4nKI,1448 +django/contrib/redirects/locale/lt/LC_MESSAGES/django.po,sha256=3D3sSO1D9XyRpiT57l-0emy7V11uKCWJYqpEzmmpUzE,1377 django/contrib/redirects/locale/lv/LC_MESSAGES/django.mo,sha256=slGK6O2tYD5yciS8m_7h2WA4LOPf05nQ4oTRKB63etE,1175 django/contrib/redirects/locale/lv/LC_MESSAGES/django.po,sha256=GUDn1IYQ5UMOQUBvGfuVOeVb-bpf5FHVigqTt_N0I0M,1442 django/contrib/redirects/locale/mk/LC_MESSAGES/django.mo,sha256=3XGgf2K60LclScPKcgw07TId6x535AW5jtGVJ9lC01A,1353 @@ -3063,8 +3041,6 @@ django/contrib/redirects/locale/mn/LC_MESSAGES/django.mo,sha256=14fdHC_hZrRaA0EA django/contrib/redirects/locale/mn/LC_MESSAGES/django.po,sha256=7_QzUWf5l0P-7gM35p9UW7bOj33NabQq_zSrekUeZsY,1502 django/contrib/redirects/locale/mr/LC_MESSAGES/django.mo,sha256=2Z5jaGJzpiJTCnhCk8ulCDeAdj-WwR99scdHFPRoHoA,468 django/contrib/redirects/locale/mr/LC_MESSAGES/django.po,sha256=0aGKTlriCJoP-Tirl-qCl7tjjpjURhgCjRGmurHVO3c,940 -django/contrib/redirects/locale/ms/LC_MESSAGES/django.mo,sha256=WUk6hvvHPWuylCGiDvy0MstWoQ1mdmwwfqlms1Nv4Ng,1094 -django/contrib/redirects/locale/ms/LC_MESSAGES/django.po,sha256=bsQDwxqtS5FgPCqTrfm9kw2hH_R2y44DnI5nluUgduc,1255 django/contrib/redirects/locale/my/LC_MESSAGES/django.mo,sha256=H5-y9A3_1yIXJzC4sSuHqhURxhOlnYEL8Nvc0IF4zUE,549 django/contrib/redirects/locale/my/LC_MESSAGES/django.po,sha256=MZGNt0jMQA6aHA6OmjvaC_ajvRWfUfDiKkV0j3_E480,1052 django/contrib/redirects/locale/nb/LC_MESSAGES/django.mo,sha256=pxRtj5VFxTQBbi_mDS05iGoQs4BZ4y6LLJZ9pozJezY,1110 @@ -3073,14 +3049,14 @@ django/contrib/redirects/locale/ne/LC_MESSAGES/django.mo,sha256=TxTnBGIi5k0PKAjA django/contrib/redirects/locale/ne/LC_MESSAGES/django.po,sha256=5b5R-6AlSIQrDyTtcmquoW5xrQRGZwlxZpBpZfVo5t4,1607 django/contrib/redirects/locale/nl/LC_MESSAGES/django.mo,sha256=uGVQu5YnzWjf2aBtxY2ZdCHXz7M8T2GKz5EcQ20ODvM,1080 django/contrib/redirects/locale/nl/LC_MESSAGES/django.po,sha256=fnEiqRdM-BOP2_6v4U-FC4cCmcVgXAXloiWKhYu-uOE,1400 -django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo,sha256=8TQXBF2mzENl7lFpcrsKxkJ4nKySTOgXJM5_I2OD7q8,1143 -django/contrib/redirects/locale/nn/LC_MESSAGES/django.po,sha256=pfrKVQd1wLKKpq-b7CBpc-rZnEEgyZFDSjbipsEiwxM,1344 +django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo,sha256=oiw7wSgqGUrHIdec6sIa7OlHXGME5iWA9h1UUlhl6Mw,1072 +django/contrib/redirects/locale/nn/LC_MESSAGES/django.po,sha256=pfu1XKvB-9DS_5dAbvjGzZCKAYxBEtnStJlBJxRSEXk,1267 django/contrib/redirects/locale/os/LC_MESSAGES/django.mo,sha256=joQ-ibV9_6ctGMNPLZQLCx5fUamRQngs6_LDd_s9sMQ,1150 django/contrib/redirects/locale/os/LC_MESSAGES/django.po,sha256=ZwFWiuGS9comy7r2kMnKuqaPOvVehVdAAuFvXM5ldxM,1358 django/contrib/redirects/locale/pa/LC_MESSAGES/django.mo,sha256=MY-OIDNXlZth-ZRoOJ52nlUPg_51_F5k0NBIpc7GZEw,748 django/contrib/redirects/locale/pa/LC_MESSAGES/django.po,sha256=TPDTK2ZvDyvO1ob8Qfr64QDbHVWAREfEeBO5w9jf63E,1199 -django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo,sha256=9Sc_8aDC8-PADnr4hYdat6iRUXj0QxsWR1RGWKIQP3M,1285 -django/contrib/redirects/locale/pl/LC_MESSAGES/django.po,sha256=RLuSAlWQPvxDGSNHL3j5ohMdf4IZL-g21-_QIuTdY4c,1605 +django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo,sha256=SkjPoylTfT7ygiHOT6M5BbhcL1J5lG8MvSwSHfVC5cU,1281 +django/contrib/redirects/locale/pl/LC_MESSAGES/django.po,sha256=9YkvmU8c4jRRUm4JijWq-tKwAqMp3A1BBxVcG0reHAU,1601 django/contrib/redirects/locale/pt/LC_MESSAGES/django.mo,sha256=WocPaVk3fQEz_MLmGVtFBGwsThD-gNU7GDocqEbeaBA,1129 django/contrib/redirects/locale/pt/LC_MESSAGES/django.po,sha256=ptCzoE41c9uFAbgSjb6VHSFYPEUv_51YyBdoThXN3XA,1350 django/contrib/redirects/locale/pt_BR/LC_MESSAGES/django.mo,sha256=LxFEZCH75ucCaB5fEmdsjEJi5aJa3barRLqcd6r-gj0,1171 @@ -3089,8 +3065,8 @@ django/contrib/redirects/locale/ro/LC_MESSAGES/django.mo,sha256=D8FkmV6IxZOn5QAP django/contrib/redirects/locale/ro/LC_MESSAGES/django.po,sha256=Z_-pDi2-A7_KXrEQtFlAJ_KLO0vXFKCbMphsNlqfNJk,1477 django/contrib/redirects/locale/ru/LC_MESSAGES/django.mo,sha256=IvO0IXq1xuX0wpo2hV8po1AMifLS3ElGyQal0vmC_Jw,1457 django/contrib/redirects/locale/ru/LC_MESSAGES/django.po,sha256=FHb4L3RMVV5ajxGj9y6ZymPtO_XjZrhHmvCZBPwwzmQ,1762 -django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo,sha256=oVA89AU0UVErADtesum66Oo3D27RRy04qLHy3n0Y9-w,1189 -django/contrib/redirects/locale/sk/LC_MESSAGES/django.po,sha256=Kjbdc7nrKsMCaEphxUdGb4VbpJbFhF0cs3ReqrY7638,1468 +django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo,sha256=4U3JX_UnnYmBNtKseSUobgTslILeZWfn37Dg7q52svY,1160 +django/contrib/redirects/locale/sk/LC_MESSAGES/django.po,sha256=8tDwfdkGAXo4eAR66nfkIdegbyjc3-qBfrMZgrf_cF4,1376 django/contrib/redirects/locale/sl/LC_MESSAGES/django.mo,sha256=GAZtOFSUxsOHdXs3AzT40D-3JFWIlNDZU_Z-cMvdaHo,1173 django/contrib/redirects/locale/sl/LC_MESSAGES/django.po,sha256=gkZTyxNh8L2gNxyLVzm-M1HTiK8KDvughTa2MK9NzWo,1351 django/contrib/redirects/locale/sq/LC_MESSAGES/django.mo,sha256=f2HyVjWFGnjNXV-EIk0YMFaMH6_ZwYLYgSDwU4fIJfM,1165 @@ -3131,14 +3107,14 @@ django/contrib/redirects/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=iftb_HccNV3 django/contrib/redirects/locale/zh_Hans/LC_MESSAGES/django.po,sha256=xZmfuCEYx7ou_qvtxBcBly5mBmkSBEhnx0xqJj3nvMw,1490 django/contrib/redirects/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=-H2o5p5v8j5RqKZ6vOsWToFWGOn8CaO3KSTiU42Zqjk,1071 django/contrib/redirects/locale/zh_Hant/LC_MESSAGES/django.po,sha256=fQicS5nmJLgloKM83l6NcSJp36-Wjn2Dl9jf03e0pGo,1334 -django/contrib/redirects/middleware.py,sha256=ydqidqi5JTaoguEFQBRzLEkU3HeiohgVsFglHUE-HIU,1921 -django/contrib/redirects/migrations/0001_initial.py,sha256=FmCw6R7-BK2bQIsetCADPKjV_s5bU4_gQLXzs5YySrk,2102 -django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py,sha256=qUWUkoM5ak5ywVulV9dzKNby3iDZgG212c9U659KdQg,636 +django/contrib/redirects/middleware.py,sha256=kJfTIj8G2loRgiEJkqiYEredzt4xhNAfDaTZkk9Coyo,1926 +django/contrib/redirects/migrations/0001_initial.py,sha256=-JUuBA7Ynmpo_RHV-_uQR2x-yT6RbJs9SwTpA_PwuAQ,1498 +django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py,sha256=JwejmL986nbhP88Qa2PZxukFxv1B8g0pTzSc0tBbWpw,636 django/contrib/redirects/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/redirects/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/redirects/migrations/__pycache__/0002_alter_redirect_new_path_help_text.cpython-39.pyc,, django/contrib/redirects/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/redirects/models.py,sha256=KJ6mj0BS243BNPKp26K7OSqcT9j49FPth5m0gNWWxFM,1083 +django/contrib/redirects/models.py,sha256=dN6ZpYv4KgAXR6bhycqa5iE3pLFIC1fSj7PZBbeVED0,1046 django/contrib/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sessions/__pycache__/__init__.cpython-39.pyc,, django/contrib/sessions/__pycache__/apps.cpython-39.pyc,, @@ -3147,7 +3123,7 @@ django/contrib/sessions/__pycache__/exceptions.cpython-39.pyc,, django/contrib/sessions/__pycache__/middleware.cpython-39.pyc,, django/contrib/sessions/__pycache__/models.cpython-39.pyc,, django/contrib/sessions/__pycache__/serializers.cpython-39.pyc,, -django/contrib/sessions/apps.py,sha256=5WIMqa3ymqEvYMnFHe3uWZB8XSijUF_NSgaorRD50Lg,194 +django/contrib/sessions/apps.py,sha256=q_fkp7a7_1GT14XHkHgNIET0sItgfBeFT7B137_KeZM,194 django/contrib/sessions/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sessions/backends/__pycache__/__init__.cpython-39.pyc,, django/contrib/sessions/backends/__pycache__/base.cpython-39.pyc,, @@ -3156,14 +3132,14 @@ django/contrib/sessions/backends/__pycache__/cached_db.cpython-39.pyc,, django/contrib/sessions/backends/__pycache__/db.cpython-39.pyc,, django/contrib/sessions/backends/__pycache__/file.cpython-39.pyc,, django/contrib/sessions/backends/__pycache__/signed_cookies.cpython-39.pyc,, -django/contrib/sessions/backends/base.py,sha256=7e8_8MljyPkIuxmYNweGZakyiYawYInJFpb0U7h3_7o,11492 -django/contrib/sessions/backends/cache.py,sha256=Dz4lOirEI3ZSrvOWnAffQpyA53TuPm3MmV1u8jkT-hI,2741 -django/contrib/sessions/backends/cached_db.py,sha256=pxPlY9klOH0NCht8OZrHQew_UkMrQlKMtIKMLYIv2DI,2098 -django/contrib/sessions/backends/db.py,sha256=qEYZNmyWk1pBbuXGXbTsLtQ2Xt_HgoRALxTQm55ZLy0,3785 -django/contrib/sessions/backends/file.py,sha256=06SgHJOy45CeL8WI4is9gmyfV_CCJwM6iSoFLqaETpE,7779 -django/contrib/sessions/backends/signed_cookies.py,sha256=keRgy5CyvufiEo4A91znOKbX6UOzzH2hzaw51UzK_0Y,2676 -django/contrib/sessions/base_session.py,sha256=1woSGGF4IFWm2apOabxtdQHeVS6OmnivL_fwjUYGJwc,1490 -django/contrib/sessions/exceptions.py,sha256=KhkhXiFwfUflSP_t6wCLOEXz1YjBRTKVNbrLmGhOTLo,359 +django/contrib/sessions/backends/base.py,sha256=LZGbZDSQdvRzOFq1l_Ir6FBBU_UlvDKPywQXo-OTAiM,13900 +django/contrib/sessions/backends/cache.py,sha256=-qeSz07gUidiY_xq7imMJ3SP17J_rLsIO50KxOhq_8E,2713 +django/contrib/sessions/backends/cached_db.py,sha256=c9JtGXxyJYRT7MMVrqwo0jw1v3JCpaBNXeL8d1tAfBE,2011 +django/contrib/sessions/backends/db.py,sha256=zzhv0nQ4OIFeyM2QXrIUG26l_IJosagKaGOI2NcZnz4,3770 +django/contrib/sessions/backends/file.py,sha256=0L3yDX0_eFtP9_GVl79OpRfHRtJ9o5vOUsRCYHFHOEA,7740 +django/contrib/sessions/backends/signed_cookies.py,sha256=L43gDpk-RFbMF_-fluEjzyUO5nKrEiCTX0yZs7cd5eI,2665 +django/contrib/sessions/base_session.py,sha256=5FofwClB_ukwCsXPfJbzUvKoYaMQ78B_lWXU0fqSg1k,1490 +django/contrib/sessions/exceptions.py,sha256=ckmS8rDOBIyTDE9PzBdD1VQKeYsYjFNHob3-n9Q5CPw,356 django/contrib/sessions/locale/af/LC_MESSAGES/django.mo,sha256=0DS0pgVrMN-bUimDfesgHs8Lgr0loz2c6nJdz58RxyQ,717 django/contrib/sessions/locale/af/LC_MESSAGES/django.po,sha256=ZJRLBshQCAiTTAUycdB3MZIadLeHR5LxbSlDvSWLnEo,838 django/contrib/sessions/locale/ar/LC_MESSAGES/django.mo,sha256=yoepqaR68PTGLx--cAOzP94Sqyl5xIYpeQ0IFWgY380,846 @@ -3176,8 +3152,8 @@ django/contrib/sessions/locale/az/LC_MESSAGES/django.mo,sha256=_4XcYdtRasbCjRoaW django/contrib/sessions/locale/az/LC_MESSAGES/django.po,sha256=qYd7vz6A-hHQNwewzI6wEsxRVLdoc2xLGm1RPW0Hxc4,891 django/contrib/sessions/locale/be/LC_MESSAGES/django.mo,sha256=FHZ72QuOd-vAOjOXisLs4CaEk7uZuzjO_EfUSB6754M,854 django/contrib/sessions/locale/be/LC_MESSAGES/django.po,sha256=tHsYVn3XNTcukB0SrHUWP1iV763rrQHCimOyJHRPiek,1023 -django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo,sha256=fFZ8EgRlJ1Z-IP8gPtsUXAnqVHbqQRZpYv6PLWNlNVA,759 -django/contrib/sessions/locale/bg/LC_MESSAGES/django.po,sha256=tXcaDPNmFIv0RU-7sGscRkLCbKEgTBowzVj3AYymarY,997 +django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo,sha256=DGp3j3E0-5bBjFCKx9c6Jcz9ZaXysd2DgVPuxROWDmU,783 +django/contrib/sessions/locale/bg/LC_MESSAGES/django.po,sha256=AEgnW2F8S85JZOh4JVJ6nLynsmHRZOBBoOluVxHosVo,942 django/contrib/sessions/locale/bn/LC_MESSAGES/django.mo,sha256=0BdFN7ou9tmoVG00fCA-frb1Tri3iKz43W7SWal398s,762 django/contrib/sessions/locale/bn/LC_MESSAGES/django.po,sha256=LycmTel6LXV2HGGN6qzlAfID-cVEQCNnW1Nv_hbWXJk,909 django/contrib/sessions/locale/br/LC_MESSAGES/django.mo,sha256=6ubPQUyXX08KUssyVZBMMkTlD94mlA6wzsteAMiZ8C8,1027 @@ -3200,8 +3176,8 @@ django/contrib/sessions/locale/el/LC_MESSAGES/django.mo,sha256=QbTbmcfgc8_4r5hFr django/contrib/sessions/locale/el/LC_MESSAGES/django.po,sha256=HeaEbpVmFhhrZt2NsZteYaYoeo8FYKZF0IoNJwtzZkc,971 django/contrib/sessions/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/sessions/locale/en/LC_MESSAGES/django.po,sha256=afaM-IIUZtcRZduojUTS8tT0w7C4Ya9lXgReOvq_iF0,804 -django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo,sha256=FgY1K6IVyQjMjXqVZxcsyWW_Tu5ckfrbmIfNYq5P-_E,693 -django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po,sha256=cMV15gJq8jNSUzkhn7uyOf2JYMFx7BNH1oFYa1vISnc,853 +django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo,sha256=dTndJxA-F1IE_nMUOtf1sRr7Kq2s_8yjgKk6mkWkVu4,486 +django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po,sha256=gvnvUpim1l7oImnzPXqBww-Uz0TgGjzCLaaszpdkQ10,761 django/contrib/sessions/locale/en_GB/LC_MESSAGES/django.mo,sha256=T5NQCTYkpERfP9yKbUvixT0VdBt1zGmGB8ITlkVc420,707 django/contrib/sessions/locale/en_GB/LC_MESSAGES/django.po,sha256=1ks_VE1qpEfPcyKg0HybkTG0-DTttTHTfUPhQCR53sw,849 django/contrib/sessions/locale/eo/LC_MESSAGES/django.mo,sha256=eBvYQbZS_WxVV3QCSZAOyHNIljC2ZXxVc4mktUuXVjI,727 @@ -3221,7 +3197,7 @@ django/contrib/sessions/locale/et/LC_MESSAGES/django.po,sha256=VNBYohAOs59jYWkjV django/contrib/sessions/locale/eu/LC_MESSAGES/django.mo,sha256=M9piOB_t-ZnfN6pX-jeY0yWh2S_5cCuo1oGiy7X65A4,728 django/contrib/sessions/locale/eu/LC_MESSAGES/django.po,sha256=bHdSoknoH0_dy26e93tWVdO4TT7rnCPXlSLPsYAhwyw,893 django/contrib/sessions/locale/fa/LC_MESSAGES/django.mo,sha256=6DdJcqaYuBnhpFFHR42w-RqML0eQPFMAUEEDY0Redy8,755 -django/contrib/sessions/locale/fa/LC_MESSAGES/django.po,sha256=rklhNf0UFl2bM6mt7x9lWvfzPH4XWGbrW9Gc2w-9rzg,922 +django/contrib/sessions/locale/fa/LC_MESSAGES/django.po,sha256=NgJlLPsS9FXjRzKqGgUTkNG9puYrBRf0KQK-QqXMIxQ,916 django/contrib/sessions/locale/fi/LC_MESSAGES/django.mo,sha256=oAugvlTEvJmG8KsZw09WcfnifYY5oHnGo4lxcxqKeaY,721 django/contrib/sessions/locale/fi/LC_MESSAGES/django.po,sha256=BVVrjbZZtLGAuZ9HK63p769CbjZFZMlS4BewSMfNMKU,889 django/contrib/sessions/locale/fr/LC_MESSAGES/django.mo,sha256=aDGYdzx2eInF6IZ-UzPDEJkuYVPnvwVND3qVuSfJNWw,692 @@ -3235,7 +3211,7 @@ django/contrib/sessions/locale/gd/LC_MESSAGES/django.po,sha256=fEa40AUqA5vh743Zq django/contrib/sessions/locale/gl/LC_MESSAGES/django.mo,sha256=uQ2ZmtUNoVCB2mSlMGSy-j4a_hu9PBfJDo796d8beFA,701 django/contrib/sessions/locale/gl/LC_MESSAGES/django.po,sha256=FovTLHdVK15N9FI9lFFAOP4zt7GsvO0kKdocgeVDkNk,902 django/contrib/sessions/locale/he/LC_MESSAGES/django.mo,sha256=qhgjSWfGAOgl-i7iwzSrJttx88xcj1pB0iLkEK64mJU,809 -django/contrib/sessions/locale/he/LC_MESSAGES/django.po,sha256=KvQG6wOpokM-2JkhWnB2UUQacy5Ie1402K_pH2zUOu0,1066 +django/contrib/sessions/locale/he/LC_MESSAGES/django.po,sha256=gtBgkC2bpVyWm8B5pjV3-9tBo0xqUsJuJz2neN79isg,969 django/contrib/sessions/locale/hi/LC_MESSAGES/django.mo,sha256=naqxOjfAnNKy3qqnUG-4LGf9arLRJpjyWWmSj5tEfao,759 django/contrib/sessions/locale/hi/LC_MESSAGES/django.po,sha256=WnTGvOz9YINMcUJg2BYCaHceZLKaTfsba_0AZtRNP38,951 django/contrib/sessions/locale/hr/LC_MESSAGES/django.mo,sha256=axyJAmXmadpFxIhu8rroVD8NsGGadQemh9-_ZDo7L1U,819 @@ -3259,15 +3235,15 @@ django/contrib/sessions/locale/it/LC_MESSAGES/django.po,sha256=hEv0iTGLuUvEBk-lF django/contrib/sessions/locale/ja/LC_MESSAGES/django.mo,sha256=hbv9FzWzXRIGRh_Kf_FLQB34xfmPU_9RQKn9u1kJqGU,757 django/contrib/sessions/locale/ja/LC_MESSAGES/django.po,sha256=ppGx5ekOWGgDF3vzyrWsqnFUZ-sVZZhiOhvAzl_8v54,920 django/contrib/sessions/locale/ka/LC_MESSAGES/django.mo,sha256=VZ-ysrDbea_-tMV-1xtlTeW62IAy2RWR94V3Y1iSh4U,803 -django/contrib/sessions/locale/ka/LC_MESSAGES/django.po,sha256=hqiWUiATlrc7qISF7ndlelIrFwc61kzhKje9l-DY6V4,955 +django/contrib/sessions/locale/ka/LC_MESSAGES/django.po,sha256=MDOG7BAO8Ez75CfgERCq1zA3syJbvQKpc4wBVlryfqQ,950 django/contrib/sessions/locale/kab/LC_MESSAGES/django.mo,sha256=W_yE0NDPJrVznA2Qb89VuprJNwyxSg59ovvjkQe6mAs,743 django/contrib/sessions/locale/kab/LC_MESSAGES/django.po,sha256=FJeEuv4P3NT_PpWHEUsQVSWXu65nYkJ6Z2AlbSKb0ZA,821 django/contrib/sessions/locale/kk/LC_MESSAGES/django.mo,sha256=FROGz_MuIhsIU5_-EYV38cHnRZrc3-OxxkBeK0ax9Rk,810 -django/contrib/sessions/locale/kk/LC_MESSAGES/django.po,sha256=P-oHO3Oi3V_RjWHjEAHdTrDfTwKP2xh3yJh7BlXL1VQ,1029 +django/contrib/sessions/locale/kk/LC_MESSAGES/django.po,sha256=l5gu1XfvRMNhCHBl-NTGoUHWa0nRSxqSDt0zljpr7Kg,1024 django/contrib/sessions/locale/km/LC_MESSAGES/django.mo,sha256=VOuKsIG2DEeCA5JdheuMIeJlpmAhKrI6lD4KWYqIIPk,929 django/contrib/sessions/locale/km/LC_MESSAGES/django.po,sha256=09i6Nd_rUK7UqFpJ70LMXTR6xS0NuGETRLe0CopMVBk,1073 -django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo,sha256=TMZ71RqNR6zI20BeozyLa9cjYrWlvfIajGDfpnHd3pQ,810 -django/contrib/sessions/locale/kn/LC_MESSAGES/django.po,sha256=whdM8P74jkAAHvjgJN8Q77dYd9sIsf_135ID8KBu-a8,990 +django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo,sha256=X5svX5_r3xZUy4OjUuo2gItc5PIOSjZOvE5IZwnM6Io,814 +django/contrib/sessions/locale/kn/LC_MESSAGES/django.po,sha256=Rq-I2veQe5l7Q7HG9pRY_mKeNcxhSRDgqphKbuNpoNc,961 django/contrib/sessions/locale/ko/LC_MESSAGES/django.mo,sha256=EUyVQYGtiFJg01mP30a0iOqBYHvpzHAcGTZM28Ubs5Q,700 django/contrib/sessions/locale/ko/LC_MESSAGES/django.po,sha256=PjntvSzRz_Aekj9VFhGsP5yO6rAsxTMzwFj58JqToIU,855 django/contrib/sessions/locale/ky/LC_MESSAGES/django.mo,sha256=ME7YUgKOYQz9FF_IdrqHImieEONDrkcn4T3HxTZKSV0,742 @@ -3275,7 +3251,7 @@ django/contrib/sessions/locale/ky/LC_MESSAGES/django.po,sha256=JZHTs9wYmlWzilRMy django/contrib/sessions/locale/lb/LC_MESSAGES/django.mo,sha256=xokesKl7h7k9dXFKIJwGETgwx1Ytq6mk2erBSxkgY-o,474 django/contrib/sessions/locale/lb/LC_MESSAGES/django.po,sha256=3igeAnQjDg6D7ItBkQQhyBoFJOZlBxT7NoZiExwD-Fo,749 django/contrib/sessions/locale/lt/LC_MESSAGES/django.mo,sha256=L9w8-qxlDlCqR_2P0PZegfhok_I61n0mJ1koJxzufy4,786 -django/contrib/sessions/locale/lt/LC_MESSAGES/django.po,sha256=dEefLGtg5flFr_v4vHS5HhK1kxx9WYWTw98cvEn132M,1023 +django/contrib/sessions/locale/lt/LC_MESSAGES/django.po,sha256=7e5BmXuaHHgGX5W1eC6wIH2QyMTNOg4JZjkZM0i-jTc,952 django/contrib/sessions/locale/lv/LC_MESSAGES/django.mo,sha256=exEzDUNwNS0GLsUkKPu_SfqBxU7T6VRA_T2schIQZ88,753 django/contrib/sessions/locale/lv/LC_MESSAGES/django.po,sha256=fBgQEbsGg1ECVm1PFDrS2sfKs2eqmsqrSYzx9ELotNQ,909 django/contrib/sessions/locale/mk/LC_MESSAGES/django.mo,sha256=4oTWp8-qzUQBiqG32hNieABgT3O17q2C4iEhcFtAxLA,816 @@ -3286,8 +3262,6 @@ django/contrib/sessions/locale/mn/LC_MESSAGES/django.mo,sha256=CcCH2ggVYrD29Q11Z django/contrib/sessions/locale/mn/LC_MESSAGES/django.po,sha256=nvcjbJzXiDvWFXrM5CxgOQIq8XucsZEUVdYkY8LnCRE,992 django/contrib/sessions/locale/mr/LC_MESSAGES/django.mo,sha256=2Z5jaGJzpiJTCnhCk8ulCDeAdj-WwR99scdHFPRoHoA,468 django/contrib/sessions/locale/mr/LC_MESSAGES/django.po,sha256=FQRdZ-qIDuvTCrwbnWfxoxNi8rywLSebcNbxGvr-hb0,743 -django/contrib/sessions/locale/ms/LC_MESSAGES/django.mo,sha256=rFi4D_ZURYUPjs5AqJ66bW70yL7AekAKWnrZRBvGPiE,649 -django/contrib/sessions/locale/ms/LC_MESSAGES/django.po,sha256=nZuJ_D0JZUzmGensLa7tSgzbBo05qgQcuHmte2oU6WQ,786 django/contrib/sessions/locale/my/LC_MESSAGES/django.mo,sha256=8zzzyfJYok969YuAwDUaa6YhxaSi3wcXy3HRNXDb_70,872 django/contrib/sessions/locale/my/LC_MESSAGES/django.po,sha256=mfs0zRBI0tugyyEfXBZzZ_FMIohydq6EYPZGra678pw,997 django/contrib/sessions/locale/nb/LC_MESSAGES/django.mo,sha256=hfJ1NCFgcAAtUvNEpaZ9b31PyidHxDGicifUWANIbM8,717 @@ -3296,8 +3270,8 @@ django/contrib/sessions/locale/ne/LC_MESSAGES/django.mo,sha256=slFgMrqGVtLRHdGor django/contrib/sessions/locale/ne/LC_MESSAGES/django.po,sha256=1vyoiGnnaB8f9SFz8PGfzpw6V_NoL78DQwjjnB6fS98,978 django/contrib/sessions/locale/nl/LC_MESSAGES/django.mo,sha256=84BTlTyxa409moKbQMFyJisI65w22p09qjJHBAmQe-g,692 django/contrib/sessions/locale/nl/LC_MESSAGES/django.po,sha256=smRr-QPGm6h6hdXxghggWES8b2NnL7yDQ07coUypa8g,909 -django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo,sha256=cytH72J3yS1PURcgyrD8R2PV5d3SbPE73IAqOMBPPVg,667 -django/contrib/sessions/locale/nn/LC_MESSAGES/django.po,sha256=y9l60yy_W3qjxWzxgJg5VgEH9KAIHIQb5hv7mgnep9w,851 +django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo,sha256=042gOyJuXb51nG7gxI_rYst9QWuB3thtAeevKpDLFVQ,695 +django/contrib/sessions/locale/nn/LC_MESSAGES/django.po,sha256=j2kDL1vDsHoBX_ky6_S0tWxaqFst6v7OLqqlt6N2ECI,842 django/contrib/sessions/locale/os/LC_MESSAGES/django.mo,sha256=xVux1Ag45Jo9HQBbkrRzcWrNjqP09nMQl16jIh0YVlo,732 django/contrib/sessions/locale/os/LC_MESSAGES/django.po,sha256=1hG5Vsz2a2yW05_Z9cTNrBKtK9VRPZuQdx4KJ_0n98o,892 django/contrib/sessions/locale/pa/LC_MESSAGES/django.mo,sha256=qEx4r_ONwXK1-qYD5uxxXEQPqK5I6rf38QZoUSm7UVA,771 @@ -3313,7 +3287,7 @@ django/contrib/sessions/locale/ro/LC_MESSAGES/django.po,sha256=fEgVxL_0Llnjspu9E django/contrib/sessions/locale/ru/LC_MESSAGES/django.mo,sha256=n-8vXR5spEbdfyeWOYWC_6kBbAppNoRrWYgqKFY6gJA,913 django/contrib/sessions/locale/ru/LC_MESSAGES/django.po,sha256=sNqNGdoof6eXzFlh4YIp1O54MdDOAFDjD3GvAFsNP8k,1101 django/contrib/sessions/locale/sk/LC_MESSAGES/django.mo,sha256=Yntm624Wt410RwuNPU1c-WwQoyrRrBs69VlKMlNUHeQ,766 -django/contrib/sessions/locale/sk/LC_MESSAGES/django.po,sha256=wt7BJk6RpFogJ2Wwa9Mh0mJi9YMpNYKTUSDuDuv1Ong,975 +django/contrib/sessions/locale/sk/LC_MESSAGES/django.po,sha256=JIvzoKw_r4jZXWEaHvIYAZDAzrEkfpr0WM9dNfUlzBE,924 django/contrib/sessions/locale/sl/LC_MESSAGES/django.mo,sha256=EE6mB8BiYRyAxK6qzurRWcaYVs96FO_4rERYQdtIt3k,770 django/contrib/sessions/locale/sl/LC_MESSAGES/django.po,sha256=KTjBWyvaNCHbpV9K6vbnavwxxXqf2DlIqVPv7MVFcO8,928 django/contrib/sessions/locale/sq/LC_MESSAGES/django.mo,sha256=eRaTy3WOC76EYLtMSD4xtJj2h8eE4W-TS4VvCVxI5bw,683 @@ -3343,7 +3317,7 @@ django/contrib/sessions/locale/tt/LC_MESSAGES/django.po,sha256=UC85dFs_1836noZTu django/contrib/sessions/locale/udm/LC_MESSAGES/django.mo,sha256=CNmoKj9Uc0qEInnV5t0Nt4ZnKSZCRdIG5fyfSsqwky4,462 django/contrib/sessions/locale/udm/LC_MESSAGES/django.po,sha256=CPml2Fn9Ax_qO5brCFDLPBoTiNdvsvJb1btQ0COwUfY,737 django/contrib/sessions/locale/uk/LC_MESSAGES/django.mo,sha256=jzNrLuFghQMCHNRQ0ihnKMCicgear0yWiTOLnvdPszw,841 -django/contrib/sessions/locale/uk/LC_MESSAGES/django.po,sha256=4K2geuGjRpJCtNfGPMhYWZlGxUy5xzIoDKA2jL2iGos,1171 +django/contrib/sessions/locale/uk/LC_MESSAGES/django.po,sha256=GM9kNL1VoFSRfbHB5KiivIbp-nJl1aZ69wL2xszNqlM,1017 django/contrib/sessions/locale/ur/LC_MESSAGES/django.mo,sha256=FkGIiHegr8HR8zjVyJ9TTW1T9WYtAL5Mg77nRKnKqWk,729 django/contrib/sessions/locale/ur/LC_MESSAGES/django.po,sha256=qR4QEBTP6CH09XFCzsPSPg2Dv0LqzbRV_I67HO2OUwk,879 django/contrib/sessions/locale/uz/LC_MESSAGES/django.mo,sha256=asPu0RhMB_Ui1li-OTVL4qIXnM9XpjsYyx5yJldDYBY,744 @@ -3359,28 +3333,28 @@ django/contrib/sessions/management/__pycache__/__init__.cpython-39.pyc,, django/contrib/sessions/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sessions/management/commands/__pycache__/__init__.cpython-39.pyc,, django/contrib/sessions/management/commands/__pycache__/clearsessions.cpython-39.pyc,, -django/contrib/sessions/management/commands/clearsessions.py,sha256=pAiO5o7zgButVlYAV93bPnmiwzWP7V5N7-xPtxSkjJg,661 -django/contrib/sessions/middleware.py,sha256=ghX32L-B6lQokp8lH6_f0AvQ_9YhV4RJ3g9YkhZt23M,3496 -django/contrib/sessions/migrations/0001_initial.py,sha256=4tczVgNJxmM5aEhrDw_EfqOBePzsxuJmlchwlMFHWrU,1149 +django/contrib/sessions/management/commands/clearsessions.py,sha256=MUZi9sKifcRq9_6WsGD9CbGsdBW6IwSFt-Nba0RaEuw,679 +django/contrib/sessions/middleware.py,sha256=kzP7OIxHKduwDhnME1K-mi9WW7yfERXoF-cn4RPDWgk,3568 +django/contrib/sessions/migrations/0001_initial.py,sha256=F7fzk2d9hDPjUwx2w-lXdZcFG1h4HyHnkfcJ6aK7C-0,955 django/contrib/sessions/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sessions/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/sessions/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/sessions/models.py,sha256=BguwuQSDzpeTNXhteYRAcspg1rop431tjFeZUVWZNYc,1250 -django/contrib/sessions/serializers.py,sha256=IYLeqZgf-JAZkPwl0j4x3VzbQKispJBk6KFASfnNTU8,228 -django/contrib/sitemaps/__init__.py,sha256=9GZlar98YUNm7umG-4VCm1MtCy152z19VhZ-l9QwF_U,8435 +django/contrib/sessions/models.py,sha256=vmROoszsXHnPHoSbFca8k-U9Z8Wg6EAHYeEK87VHHk8,1257 +django/contrib/sessions/serializers.py,sha256=clq2ENNQ3ujEuuc5gHSDvaz30kWWHelnQPY6tzUu0qs,424 +django/contrib/sitemaps/__init__.py,sha256=JigPeY8NHVtVjZcHM2p0kfI0_I0o4LoXlvjpOQ4NWV4,7705 django/contrib/sitemaps/__pycache__/__init__.cpython-39.pyc,, django/contrib/sitemaps/__pycache__/apps.cpython-39.pyc,, django/contrib/sitemaps/__pycache__/views.cpython-39.pyc,, -django/contrib/sitemaps/apps.py,sha256=xYE-mAs37nL8ZAnv052LhUKVUwGYKB3xyPy4t8pwOpw,249 +django/contrib/sitemaps/apps.py,sha256=ZV4hW6wALz7H60KWKpx68lz0sLBdM4uD5nQ2LRjfHRg,249 django/contrib/sitemaps/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sitemaps/management/__pycache__/__init__.cpython-39.pyc,, django/contrib/sitemaps/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sitemaps/management/commands/__pycache__/__init__.cpython-39.pyc,, django/contrib/sitemaps/management/commands/__pycache__/ping_google.cpython-39.pyc,, -django/contrib/sitemaps/management/commands/ping_google.py,sha256=cU6bAGhDARD7ZM2R9cUZufEPiB9ZrM7Nc3EbghQJI5Y,558 +django/contrib/sitemaps/management/commands/ping_google.py,sha256=gqfCpod-Wp3nFBc8mpWhbP2QSWsWE74IJ-hlcm8_7SY,558 django/contrib/sitemaps/templates/sitemap.xml,sha256=L092SHTtwtmNJ_Lj_jLrzHhfI0-OKKIw5fpyOfr4qRs,683 django/contrib/sitemaps/templates/sitemap_index.xml,sha256=VqDmRlWMx9kC6taiBoi1h9JVspV54ou3nFjE8Nfofl8,209 -django/contrib/sitemaps/views.py,sha256=VS0CcdITHiQ_8KuyyZhP7cnbrafPM_1HcNUMLVqOyfI,3690 +django/contrib/sitemaps/views.py,sha256=mW1KQu9EA-wixh4jc01tB27JmuiGLTO0OG7sZmM2-ns,3532 django/contrib/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sites/__pycache__/__init__.cpython-39.pyc,, django/contrib/sites/__pycache__/admin.cpython-39.pyc,, @@ -3392,9 +3366,9 @@ django/contrib/sites/__pycache__/middleware.cpython-39.pyc,, django/contrib/sites/__pycache__/models.cpython-39.pyc,, django/contrib/sites/__pycache__/requests.cpython-39.pyc,, django/contrib/sites/__pycache__/shortcuts.cpython-39.pyc,, -django/contrib/sites/admin.py,sha256=IWvGDQUTDPEUsd-uuxfHxJq4syGtddNKUdkP0nmVUMA,214 -django/contrib/sites/apps.py,sha256=uBLHUyQoSuo1Q7NwLTwlvsTuRU1MXwj4t6lRUnIBdwk,562 -django/contrib/sites/checks.py,sha256=SsFycVVw6JcbMNF1tNgCen9dix-UGrMTWz8Gbb80adQ,340 +django/contrib/sites/admin.py,sha256=ClzCRn4fUPWO1dNlEWEPjSDInnK87XbNRmadvjYs1go,214 +django/contrib/sites/apps.py,sha256=fdo4csb3NT4qt_AhCGaYmbkRLSZFqztKPXHnShGkgjY,562 +django/contrib/sites/checks.py,sha256=hUrMy0WTDkv-WSjnePl4rkdOi36qQLgenqXpu65w8NM,350 django/contrib/sites/locale/af/LC_MESSAGES/django.mo,sha256=A10bZFMs-wUetVfF5UrFwmuiKnN4ZnlrR4Rx8U4Ut1A,786 django/contrib/sites/locale/af/LC_MESSAGES/django.po,sha256=O0-ZRvmXvV_34kONuqakuXV5OmYbQ569K1Puj3qQNac,907 django/contrib/sites/locale/ar/LC_MESSAGES/django.mo,sha256=kLoytp2jvhWn6p1c8kNVua2sYAMnrpS4xnbluHD22Vs,947 @@ -3431,8 +3405,8 @@ django/contrib/sites/locale/el/LC_MESSAGES/django.mo,sha256=G9o1zLGysUePGzZRicQ2 django/contrib/sites/locale/el/LC_MESSAGES/django.po,sha256=RBi_D-_znYuV6LXfTlSOf1Mvuyl96fIyEoiZ-lgeyWs,1133 django/contrib/sites/locale/en/LC_MESSAGES/django.mo,sha256=U0OV81NfbuNL9ctF-gbGUG5al1StqN-daB-F-gFBFC8,356 django/contrib/sites/locale/en/LC_MESSAGES/django.po,sha256=tSjfrNZ_FqLHsXjm5NuTyo5-JpdlPLsPZjFqF2APhy8,817 -django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo,sha256=G--2j_CR99JjRgVIX2Y_5pDfO7IgIkvK4kYHZtGzpxU,753 -django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po,sha256=Giw634r94MJT1Q3qgqM7gZakQCasRM9Dm7MDkb9JOc8,913 +django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo,sha256=dTndJxA-F1IE_nMUOtf1sRr7Kq2s_8yjgKk6mkWkVu4,486 +django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po,sha256=7V9dBdbfHa9aGAfs9nw6ivSxX30CqaYc1ptfplTAPJc,791 django/contrib/sites/locale/en_GB/LC_MESSAGES/django.mo,sha256=FbSh7msJdrHsXr0EtDMuODFzSANG_HJ3iBlW8ePpqFs,639 django/contrib/sites/locale/en_GB/LC_MESSAGES/django.po,sha256=Ib-DIuTWlrN3kg99kLCuqWJVtt1NWaFD4UbDFK6d4KY,862 django/contrib/sites/locale/eo/LC_MESSAGES/django.mo,sha256=N4KkH12OHxic3pp1okeBhpfDx8XxxpULk3UC219vjWU,792 @@ -3452,7 +3426,7 @@ django/contrib/sites/locale/et/LC_MESSAGES/django.po,sha256=mEfD6EyQ15PPivb5FTlk django/contrib/sites/locale/eu/LC_MESSAGES/django.mo,sha256=1HTAFI3DvTAflLJsN7NVtSd4XOTlfoeLGFyYCOX69Ec,807 django/contrib/sites/locale/eu/LC_MESSAGES/django.po,sha256=NWxdE5-mF6Ak4nPRpCFEgAMIsVDe9YBEZl81v9kEuX8,1023 django/contrib/sites/locale/fa/LC_MESSAGES/django.mo,sha256=odtsOpZ6noNqwDb18HDc2e6nz3NMsa-wrTN-9dk7d9w,872 -django/contrib/sites/locale/fa/LC_MESSAGES/django.po,sha256=-DirRvcTqcpIy90QAUiCSoNkCDRifqpWSzLriJ4cwQU,1094 +django/contrib/sites/locale/fa/LC_MESSAGES/django.po,sha256=uL2I9XjqIxqTUKf6buewtm9rwflM23pxspFMs7w4SPM,1088 django/contrib/sites/locale/fi/LC_MESSAGES/django.mo,sha256=I5DUeLk1ChUC32q5uzriABCLLJpJKNbEK4BfqylPQzg,786 django/contrib/sites/locale/fi/LC_MESSAGES/django.po,sha256=LH2sFIKM3YHPoz9zIu10z1DFv1svXphBdOhXNy4a17s,929 django/contrib/sites/locale/fr/LC_MESSAGES/django.mo,sha256=W7Ne5HqgnRcl42njzbUaDSY059jdhwvr0tgZzecVWD8,756 @@ -3466,7 +3440,7 @@ django/contrib/sites/locale/gd/LC_MESSAGES/django.po,sha256=NPKp7A5-y-MR7r8r4Wqt django/contrib/sites/locale/gl/LC_MESSAGES/django.mo,sha256=QUJdJV71VT-4iVQ5mUAeyszTVhD2LlmmPQv0WpPWttU,742 django/contrib/sites/locale/gl/LC_MESSAGES/django.po,sha256=cLcejsFyoFk0fRX9fAcl9owHoxiD593QZZeZTfObBVw,940 django/contrib/sites/locale/he/LC_MESSAGES/django.mo,sha256=L3bganfG4gHqp2WXGh4rfWmmbaIxHaGc7-ypAqjSL_E,820 -django/contrib/sites/locale/he/LC_MESSAGES/django.po,sha256=iO3OZwz2aiuAzugkKp5Hxonwdg3kKjBurxR685J2ZMk,1082 +django/contrib/sites/locale/he/LC_MESSAGES/django.po,sha256=nT0Gu0iWpFV7ZJ6SAdcogZccCz3CV-R5rgqwEl5NA6c,985 django/contrib/sites/locale/hi/LC_MESSAGES/django.mo,sha256=J4oIS1vJnCvdCCUD4tlTUVyTe4Xn0gKcWedfhH4C0t0,665 django/contrib/sites/locale/hi/LC_MESSAGES/django.po,sha256=INBrm37jL3okBHuzX8MSN1vMptj77a-4kwQkAyt8w_8,890 django/contrib/sites/locale/hr/LC_MESSAGES/django.mo,sha256=KjDUhEaOuYSMexcURu2UgfkatN2rrUcAbCUbcpVSInk,876 @@ -3490,15 +3464,15 @@ django/contrib/sites/locale/it/LC_MESSAGES/django.po,sha256=zxavlLMmp1t1rCDsgrw1 django/contrib/sites/locale/ja/LC_MESSAGES/django.mo,sha256=RNuCS6wv8uK5TmXkSH_7SjsbUFkf24spZfTsvfoTKro,814 django/contrib/sites/locale/ja/LC_MESSAGES/django.po,sha256=e-cj92VOVc5ycIY6NwyFh5bO7Q9q5vp5CG4dOzd_eWQ,982 django/contrib/sites/locale/ka/LC_MESSAGES/django.mo,sha256=m8GTqr9j0ijn0YJhvnsYwlk5oYcASKbHg_5hLqZ91TI,993 -django/contrib/sites/locale/ka/LC_MESSAGES/django.po,sha256=1upohcHrQH9T34b6lW09MTtFkk5WswdYOLs2vMAJIuE,1160 +django/contrib/sites/locale/ka/LC_MESSAGES/django.po,sha256=BCsMvNq-3Pi9-VnUvpUQaGx6pbCgI8rCcIHUA8VL4as,1155 django/contrib/sites/locale/kab/LC_MESSAGES/django.mo,sha256=Utdj5gH5YPeaYMjeMzF-vjqYvYTCipre2qCBkEJSc-Y,808 django/contrib/sites/locale/kab/LC_MESSAGES/django.po,sha256=d78Z-YanYZkyP5tpasj8oAa5RimVEmce6dlq5vDSscA,886 django/contrib/sites/locale/kk/LC_MESSAGES/django.mo,sha256=T2dTZ83vBRfQb2dRaKOrhvO00BHQu_2bu0O0k7RsvGA,895 -django/contrib/sites/locale/kk/LC_MESSAGES/django.po,sha256=HvdSFqsumyNurDJ6NKVLjtDdSIg0KZN2v29dM748GtU,1062 +django/contrib/sites/locale/kk/LC_MESSAGES/django.po,sha256=9ixNnoE3BxfBj4Xza0FM5qInd0uiNnAlXgDb_KaICn4,1057 django/contrib/sites/locale/km/LC_MESSAGES/django.mo,sha256=Q7pn5E4qN957j20-iCHgrfI-p8sm3Tc8O2DWeuH0By8,701 django/contrib/sites/locale/km/LC_MESSAGES/django.po,sha256=TOs76vlCMYOZrdHgXPWZhQH1kTBQTpzsDJ8N4kbJQ7E,926 -django/contrib/sites/locale/kn/LC_MESSAGES/django.mo,sha256=_jl_4_39oe940UMyb15NljGOd45kkCeVNpJy6JvGWTE,673 -django/contrib/sites/locale/kn/LC_MESSAGES/django.po,sha256=cMPXF2DeiQuErhyFMe4i7swxMoqoz1sqtBEXf4Ghx1c,921 +django/contrib/sites/locale/kn/LC_MESSAGES/django.mo,sha256=fikclDn-FKU_t9lZeBtQciisS3Kqv4tJHtu923OXLJI,676 +django/contrib/sites/locale/kn/LC_MESSAGES/django.po,sha256=p_P7L0KAUoKNLH8vuHV4_2mTWK1m1tjep5XgRqbWd2k,904 django/contrib/sites/locale/ko/LC_MESSAGES/django.mo,sha256=wlfoWG-vmMSCipUJVVC0Y_W7QbGNNE-oEnVwl_6-AmY,807 django/contrib/sites/locale/ko/LC_MESSAGES/django.po,sha256=TENAk9obGUxFwMnJQj_V9sZxEKJj4DyWMuGpx3Ft_pM,1049 django/contrib/sites/locale/ky/LC_MESSAGES/django.mo,sha256=IYxp8jG5iyN81h7YJqOiSQdOH7DnwOiIvelKZfzP6ZA,811 @@ -3506,7 +3480,7 @@ django/contrib/sites/locale/ky/LC_MESSAGES/django.po,sha256=rxPdgQoBtGQSi5diOy3M django/contrib/sites/locale/lb/LC_MESSAGES/django.mo,sha256=xokesKl7h7k9dXFKIJwGETgwx1Ytq6mk2erBSxkgY-o,474 django/contrib/sites/locale/lb/LC_MESSAGES/django.po,sha256=1yRdK9Zyh7kcWG7wUexuF9-zxEaKLS2gG3ggVOHbRJ8,779 django/contrib/sites/locale/lt/LC_MESSAGES/django.mo,sha256=bK6PJtd7DaOgDukkzuqos5ktgdjSF_ffL9IJTQY839s,869 -django/contrib/sites/locale/lt/LC_MESSAGES/django.po,sha256=T-vdVqs9KCz9vMs9FfushgZN9z7LQOT-C86D85H2X8c,1195 +django/contrib/sites/locale/lt/LC_MESSAGES/django.po,sha256=9q7QfFf_IR2A1Cr_9aLVIWf-McR0LivtRC284w2_bo0,1124 django/contrib/sites/locale/lv/LC_MESSAGES/django.mo,sha256=t9bQiVqpAmXrq8QijN4Lh0n6EGUGQjnuH7hDcu21z4c,823 django/contrib/sites/locale/lv/LC_MESSAGES/django.po,sha256=vMaEtXGosD3AcTomiuctbOpjLes8TRBnumLe8DC4yq4,1023 django/contrib/sites/locale/mk/LC_MESSAGES/django.mo,sha256=_YXasRJRWjYmmiEWCrAoqnrKuHHPBG_v_EYTUe16Nfo,885 @@ -3517,8 +3491,6 @@ django/contrib/sites/locale/mn/LC_MESSAGES/django.mo,sha256=w2sqJRAe0wyz_IuCZ_Oc django/contrib/sites/locale/mn/LC_MESSAGES/django.po,sha256=Zh_Eao0kLZsrQ8wkL1f-pRrsAtNJOspu45uStq5t8Mo,1127 django/contrib/sites/locale/mr/LC_MESSAGES/django.mo,sha256=2Z5jaGJzpiJTCnhCk8ulCDeAdj-WwR99scdHFPRoHoA,468 django/contrib/sites/locale/mr/LC_MESSAGES/django.po,sha256=pqnjF5oxvpMyjijy6JfI8qJbbbowZzE5tZF0DMYiCBs,773 -django/contrib/sites/locale/ms/LC_MESSAGES/django.mo,sha256=GToJlS8yDNEy-D3-p7p8ZlWEZYHlSzZAcVIH5nQEkkI,727 -django/contrib/sites/locale/ms/LC_MESSAGES/django.po,sha256=_4l4DCIqSWZtZZNyfzpBA0V-CbAaHe9Ckz06VLbTjFo,864 django/contrib/sites/locale/my/LC_MESSAGES/django.mo,sha256=jN59e9wRheZYx1A4t_BKc7Hx11J5LJg2wQRd21aQv08,961 django/contrib/sites/locale/my/LC_MESSAGES/django.po,sha256=EhqYIW5-rX33YjsDsBwfiFb3BK6fZKVc3CRYeJpZX1E,1086 django/contrib/sites/locale/nb/LC_MESSAGES/django.mo,sha256=AaiHGcmcciy5IMBPVAShcc1OQOETJvBCv7GYHMcIQMA,793 @@ -3527,8 +3499,8 @@ django/contrib/sites/locale/ne/LC_MESSAGES/django.mo,sha256=n96YovpBax3T5VZSmIfG django/contrib/sites/locale/ne/LC_MESSAGES/django.po,sha256=B14rhDd8GAaIjxd1sYjxO2pZfS8gAwZ1C-kCdVkRXho,1078 django/contrib/sites/locale/nl/LC_MESSAGES/django.mo,sha256=ghu-tNPNZuE4sVRDWDVmmmVNPYZLWYm_UPJRqh8wmec,735 django/contrib/sites/locale/nl/LC_MESSAGES/django.po,sha256=1DCQNzMRhy4vW-KkmlPGy58UR27Np5ilmYhmjaq-8_k,1030 -django/contrib/sites/locale/nn/LC_MESSAGES/django.mo,sha256=eSW8kwbzm2HsE9s9IRCsAo9juimVQjcfdd8rtl3TQJM,731 -django/contrib/sites/locale/nn/LC_MESSAGES/django.po,sha256=OOyvE7iji9hwvz8Z_OxWoKw2e3ptk3dqeqlriXgilSc,915 +django/contrib/sites/locale/nn/LC_MESSAGES/django.mo,sha256=m1SUw5bhDUemD8yMGDxcWdhbUMtzZ9WXWXtV2AHIzBs,633 +django/contrib/sites/locale/nn/LC_MESSAGES/django.po,sha256=i8BQyewiU2ymkAkj12M2MJBVbCJPp8PB8_NcQiScaD4,861 django/contrib/sites/locale/os/LC_MESSAGES/django.mo,sha256=Su06FkWMOPzBxoung3bEju_EnyAEAXROoe33imO65uQ,806 django/contrib/sites/locale/os/LC_MESSAGES/django.po,sha256=4i4rX6aXDUKjq64T02iStqV2V2erUsSVnTivh2XtQeY,963 django/contrib/sites/locale/pa/LC_MESSAGES/django.mo,sha256=tOHiisOtZrTyIFoo4Ipn_XFH9hhu-ubJLMdOML5ZUgk,684 @@ -3544,7 +3516,7 @@ django/contrib/sites/locale/ro/LC_MESSAGES/django.po,sha256=tWbWVbjFFELNzSXX4_5l django/contrib/sites/locale/ru/LC_MESSAGES/django.mo,sha256=bIZJWMpm2O5S6RC_2cfkrp5NXaTU2GWSsMr0wHVEmcw,1016 django/contrib/sites/locale/ru/LC_MESSAGES/django.po,sha256=jHy5GR05ZSjLmAwaVNq3m0WdhO9GYxge3rDBziqesA8,1300 django/contrib/sites/locale/sk/LC_MESSAGES/django.mo,sha256=-EYdm14ZjoR8bd7Rv2b5G7UJVSKmZa1ItLsdATR3-Cg,822 -django/contrib/sites/locale/sk/LC_MESSAGES/django.po,sha256=VSRlsq8uk-hP0JI94iWsGX8Al76vvGK4N1xIoFtoRQM,1070 +django/contrib/sites/locale/sk/LC_MESSAGES/django.po,sha256=L2YRNq26DdT3OUFhw25ncZBgs232v6kSsAUTc0beIC8,1019 django/contrib/sites/locale/sl/LC_MESSAGES/django.mo,sha256=JmkpTKJGWgnBM3CqOUriGvrDnvg2YWabIU2kbYAOM4s,845 django/contrib/sites/locale/sl/LC_MESSAGES/django.po,sha256=qWrWrSz5r3UOVraX08ILt3TTmfyTDGKbJKbTlN9YImU,1059 django/contrib/sites/locale/sq/LC_MESSAGES/django.mo,sha256=DMLN1ZDJeDnslavjcKloXSXn6IvangVliVP3O6U8dAY,769 @@ -3574,7 +3546,7 @@ django/contrib/sites/locale/tt/LC_MESSAGES/django.po,sha256=yj49TjwcZ4YrGqnJrKh3 django/contrib/sites/locale/udm/LC_MESSAGES/django.mo,sha256=CNmoKj9Uc0qEInnV5t0Nt4ZnKSZCRdIG5fyfSsqwky4,462 django/contrib/sites/locale/udm/LC_MESSAGES/django.po,sha256=vrLZ0XJF63CO3IucbQpd12lxuoM9S8tTUv6cpu3g81c,767 django/contrib/sites/locale/uk/LC_MESSAGES/django.mo,sha256=H4806mPqOoHJFm549F7drzsfkvAXWKmn1w_WVwQx9rk,960 -django/contrib/sites/locale/uk/LC_MESSAGES/django.po,sha256=CJZTOaurDXwpgBiwXx3W7juaF0EctEImPhJdDn8j1xU,1341 +django/contrib/sites/locale/uk/LC_MESSAGES/django.po,sha256=jmJKTuGLhfP4rg8M_d86XR4X8qYB-JAtEf6jRKuzi3w,1187 django/contrib/sites/locale/ur/LC_MESSAGES/django.mo,sha256=s6QL8AB_Mp9haXS4n1r9b0YhEUECPxUyPrHTMI3agts,654 django/contrib/sites/locale/ur/LC_MESSAGES/django.po,sha256=R9tv3qtett8CUGackoHrc5XADeygVKAE0Fz8YzK2PZ4,885 django/contrib/sites/locale/uz/LC_MESSAGES/django.mo,sha256=OsuqnLEDl9gUAwsmM2s1KH7VD74ID-k7JXcjGhjFlEY,799 @@ -3585,18 +3557,18 @@ django/contrib/sites/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=7D9_pDY5lBRpo1k django/contrib/sites/locale/zh_Hans/LC_MESSAGES/django.po,sha256=xI_N00xhV8dWDp4fg5Mmj9ivOBBdHP79T3-JYXPyc5M,946 django/contrib/sites/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=0F6Qmh1smIXlOUNDaDwDajyyGecc1azfwh8BhXrpETo,790 django/contrib/sites/locale/zh_Hant/LC_MESSAGES/django.po,sha256=ixbXNBNKNfrpI_B0O_zktTfo63sRFMOk1B1uIh4DGGg,1046 -django/contrib/sites/management.py,sha256=AElGktvFhWXJtlJwOKpUlIeuv2thkNM8F6boliML84U,1646 -django/contrib/sites/managers.py,sha256=uqD_Cu3P4NCp7VVdGn0NvHfhsZB05MLmiPmgot-ygz4,1994 +django/contrib/sites/management.py,sha256=K6cgSOdN4ins_TiWjUIkGFwuibJmshTlFonqYT2QKrw,1597 +django/contrib/sites/managers.py,sha256=OJfKicEOuqcD0B7NuH4scszrknQZ-X1Nf1PL0XgWqLM,1929 django/contrib/sites/middleware.py,sha256=qYcVHsHOg0VxQNS4saoLHkdF503nJR-D7Z01vE0SvUM,309 -django/contrib/sites/migrations/0001_initial.py,sha256=eSu5aiR8FPElTvIbLeuSQZclOa0TIltT7XaDroJejOg,1362 -django/contrib/sites/migrations/0002_alter_domain_unique.py,sha256=OyuSeh6HxcuRRe6dCrJDQ8vGnAlrngO_jec3LL38Kg0,550 +django/contrib/sites/migrations/0001_initial.py,sha256=7plQm1loCP4AuC1wwCpXlX3Fw8q5V0T6Vxi7lNzbyoY,1068 +django/contrib/sites/migrations/0002_alter_domain_unique.py,sha256=HECWqP0R0hp77p_ubI5bI9DqEXIiGOTTszAr4EpgtVE,517 django/contrib/sites/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/sites/migrations/__pycache__/0001_initial.cpython-39.pyc,, django/contrib/sites/migrations/__pycache__/0002_alter_domain_unique.cpython-39.pyc,, django/contrib/sites/migrations/__pycache__/__init__.cpython-39.pyc,, -django/contrib/sites/models.py,sha256=NZkMEqDxrulV-y2yAq_dYg1Y3GxbI7o7Ca4HYOA_98s,3696 -django/contrib/sites/requests.py,sha256=baABc6fmTejNmk8M3fcoQ1cuI2qpJzF8Y47A1xSt8gY,641 -django/contrib/sites/shortcuts.py,sha256=YPCbb_uIYuwPyZ9SfwlyxAm2yuITkx24mbzSMJ2a7TI,583 +django/contrib/sites/models.py,sha256=fChMnUtphdlXyzGPh7uSDzjWBS3xJ0mIpjLRFk1Z54E,3696 +django/contrib/sites/requests.py,sha256=74RhONzbRqEGoNXLu4T7ZjAFKYvCLmY_XQWnGRz6jdw,640 +django/contrib/sites/shortcuts.py,sha256=RZr1iT8zY_z8o52PIWEBFCQL03pE28pp6708LveS240,581 django/contrib/staticfiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/staticfiles/__pycache__/__init__.cpython-39.pyc,, django/contrib/staticfiles/__pycache__/apps.cpython-39.pyc,, @@ -3608,10 +3580,10 @@ django/contrib/staticfiles/__pycache__/testing.cpython-39.pyc,, django/contrib/staticfiles/__pycache__/urls.cpython-39.pyc,, django/contrib/staticfiles/__pycache__/utils.cpython-39.pyc,, django/contrib/staticfiles/__pycache__/views.cpython-39.pyc,, -django/contrib/staticfiles/apps.py,sha256=SbeI6t0nB9pO56qpwyxRYgPvvCfAvbLTwMJDAzFfn6U,423 +django/contrib/staticfiles/apps.py,sha256=4682vA5WgXhJ8DgOFQmGTBBw3b-xsYjkV1n-TVIc25o,423 django/contrib/staticfiles/checks.py,sha256=rH9A8NIYtEkA_PRYXQJxndm243O6Mz6GwyqWSUe3f24,391 -django/contrib/staticfiles/finders.py,sha256=VqUPjNTjHrJZL5pyMPcrRF2lmqKzjZF9nas_mnyIjaM,11008 -django/contrib/staticfiles/handlers.py,sha256=HGzVGgV4nv8v20XxzX9L1dXdNxV7ciwYzWxG1S0GJ6c,3496 +django/contrib/staticfiles/finders.py,sha256=D_M2N12Tvwn8P3vcmh4yCJhjNf4x_wB0uapXDsThmCE,10366 +django/contrib/staticfiles/handlers.py,sha256=dxjjvVISnAlCmFebHdiylRT16Ac2ddPXtu8WL0Dr49w,3462 django/contrib/staticfiles/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/staticfiles/management/__pycache__/__init__.cpython-39.pyc,, django/contrib/staticfiles/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -3619,20 +3591,20 @@ django/contrib/staticfiles/management/commands/__pycache__/__init__.cpython-39.p django/contrib/staticfiles/management/commands/__pycache__/collectstatic.cpython-39.pyc,, django/contrib/staticfiles/management/commands/__pycache__/findstatic.cpython-39.pyc,, django/contrib/staticfiles/management/commands/__pycache__/runserver.cpython-39.pyc,, -django/contrib/staticfiles/management/commands/collectstatic.py,sha256=Zd65dgKD8JlXmoDb3ig6tvZka4gMV_6egbLcoRLJ1SA,15137 -django/contrib/staticfiles/management/commands/findstatic.py,sha256=TMMGlbV-B1aq1b27nA6Otu6hV44pqAzeuEtTV2DPmp0,1638 -django/contrib/staticfiles/management/commands/runserver.py,sha256=U_7oCY8LJX5Jn1xlMv-qF4EQoUvlT0ldB5E_0sJmRtw,1373 -django/contrib/staticfiles/storage.py,sha256=M9r7p8q0ubHX1VJxcBYwrkkT-gNP7fDvDhVOYPu-XCs,19123 +django/contrib/staticfiles/management/commands/collectstatic.py,sha256=8l6nSPW7RZr_spM67tZmUvlGC_43BB_xEr_N9Ku8d34,14932 +django/contrib/staticfiles/management/commands/findstatic.py,sha256=m4EXJJQwzvYGOPrcANJe3ihZPWGAZV5lvky8jAbZdKI,1561 +django/contrib/staticfiles/management/commands/runserver.py,sha256=uv-h6a8AOs0c92ILT_3Mu0UTBoCiQzThpUEmR-blj70,1318 +django/contrib/staticfiles/storage.py,sha256=FglRyoP7UuJdOSHuTud8Cukzwn8Lcb4_XmCJUVAiTsA,17618 django/contrib/staticfiles/testing.py,sha256=4X-EtOfXnwkJAyFT8qe4H4sbVTKgM65klLUtY81KHiE,463 django/contrib/staticfiles/urls.py,sha256=owDM_hdyPeRmxYxZisSMoplwnzWrptI_W8-3K2f7ITA,498 -django/contrib/staticfiles/utils.py,sha256=iPXHA0yMXu37PQwCrq9zjhSzjZf_zEBXJ-dHGsqZoX8,2279 -django/contrib/staticfiles/views.py,sha256=XacxXwbhLlcmxhspeDOYvNF0OhMtSMOHGouxqQf0jlU,1261 +django/contrib/staticfiles/utils.py,sha256=S-x2G7gXp67kjJ8cKLCljXETZt20UqsRdhjPyJTbLcg,2276 +django/contrib/staticfiles/views.py,sha256=43bHYTHVMWjweU_tqzXpBKEp7EtHru_7rwr2w7U-AZk,1261 django/contrib/syndication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/contrib/syndication/__pycache__/__init__.cpython-39.pyc,, django/contrib/syndication/__pycache__/apps.cpython-39.pyc,, django/contrib/syndication/__pycache__/views.cpython-39.pyc,, -django/contrib/syndication/apps.py,sha256=7IpHoihPWtOcA6S4O6VoG0XRlqEp3jsfrNf-D-eluic,203 -django/contrib/syndication/views.py,sha256=gcCR8C3_b3lBRx3XfQhM3h8WkSO09gXbkziOUjNzj78,8716 +django/contrib/syndication/apps.py,sha256=hXquFH_3BL6NNR2cxLU-vHlBJZ3OCjbcl8jkzCNvE64,203 +django/contrib/syndication/views.py,sha256=qjrDJkXOU8aJZosx9aIJcrAkkKCJrhy9kXXLeRUIBJA,8743 django/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/__pycache__/__init__.cpython-39.pyc,, django/core/__pycache__/asgi.cpython-39.pyc,, @@ -3643,7 +3615,7 @@ django/core/__pycache__/signing.cpython-39.pyc,, django/core/__pycache__/validators.cpython-39.pyc,, django/core/__pycache__/wsgi.cpython-39.pyc,, django/core/asgi.py,sha256=N2L3GS6F6oL-yD9Tu2otspCi2UhbRQ90LEx3ExOP1m0,386 -django/core/cache/__init__.py,sha256=MecIA1TacIKkQTGHI1zti-rnoGNiWr0PgviUWFnPO2k,2252 +django/core/cache/__init__.py,sha256=bllGi5g80kdZnxPwEB_GmUlZouCy1Q9Y0UUJX8ZOHpw,2208 django/core/cache/__pycache__/__init__.cpython-39.pyc,, django/core/cache/__pycache__/utils.cpython-39.pyc,, django/core/cache/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -3654,51 +3626,45 @@ django/core/cache/backends/__pycache__/dummy.cpython-39.pyc,, django/core/cache/backends/__pycache__/filebased.cpython-39.pyc,, django/core/cache/backends/__pycache__/locmem.cpython-39.pyc,, django/core/cache/backends/__pycache__/memcached.cpython-39.pyc,, -django/core/cache/backends/__pycache__/redis.cpython-39.pyc,, -django/core/cache/backends/base.py,sha256=fkEigg1NJnT26lrkDuBLm0n9dmhU_rhY_oxIdSZ7vnQ,14227 -django/core/cache/backends/db.py,sha256=MAesuss4Vkobs_EfD2nEOG1raSByXLXEKq5SlvkY7NM,11077 -django/core/cache/backends/dummy.py,sha256=fQbFiL72DnVKP9UU4WDsZYaxYKx7FlMOJhtP8aky2ic,1043 -django/core/cache/backends/filebased.py,sha256=h54aS2msZiDX2MNVn5rSB02Fa9tFOb1UkbdRTOhwqAo,5658 -django/core/cache/backends/locmem.py,sha256=cqdFgPxYrfEKDvKR2IYiFV7-MwhM0CIHPxLTBxJMDTQ,4035 -django/core/cache/backends/memcached.py,sha256=IIKQ2F2NvAW0xSlSmCXbSOxhBwlvl7qv-hDL6Fs0OgI,8573 -django/core/cache/backends/redis.py,sha256=-BK1Cj2A27--LX7zbH0wVNhfR0e6mRzPE5vxo-ON32s,7963 -django/core/cache/utils.py,sha256=qWHXd2UtdttdO5xvQ9pnCZf60fPYmi4Do-lPYcrvpBU,375 -django/core/checks/__init__.py,sha256=gFG0gY0C0L-akCrk1F0Q_WmkptYDLXYdyzr3wNJVIi4,1195 +django/core/cache/backends/base.py,sha256=5FLTKYtLuI-mwMlpb6MAuaV3F9NK_zhYuBGEsYVv25M,10322 +django/core/cache/backends/db.py,sha256=GmfhmwwPGnJd3wH16qxj1m-RTRfGpRNJ-L7GQDZZd_M,11063 +django/core/cache/backends/dummy.py,sha256=DcfckCOdsfrmqarEQYyeLRDyI73PU3beCfltpofAYSc,1137 +django/core/cache/backends/filebased.py,sha256=6DqOisTScyE0F_q5xL6PmcIKqRiCERezNOkLnUPpNBo,5687 +django/core/cache/backends/locmem.py,sha256=BN3AG5c5A97q0nGAQKeaGNeB-xebRV1f40MKMRlmExg,4161 +django/core/cache/backends/memcached.py,sha256=7NsGWhex0mKKvT2OuVOJNg97e9Y_K__pmP1ru94_bfo,9298 +django/core/cache/utils.py,sha256=nf_f2V3ToTSwtFftQ8fNgN0tsGylo_IE8kTL_Vq7OaI,375 +django/core/checks/__init__.py,sha256=BPnStYHYfhBvSIONGxIKP2Xj-01niFcnCjtVGL0PG2A,994 django/core/checks/__pycache__/__init__.cpython-39.pyc,, django/core/checks/__pycache__/async_checks.cpython-39.pyc,, django/core/checks/__pycache__/caches.cpython-39.pyc,, django/core/checks/__pycache__/database.cpython-39.pyc,, -django/core/checks/__pycache__/files.cpython-39.pyc,, django/core/checks/__pycache__/messages.cpython-39.pyc,, django/core/checks/__pycache__/model_checks.cpython-39.pyc,, django/core/checks/__pycache__/registry.cpython-39.pyc,, django/core/checks/__pycache__/templates.cpython-39.pyc,, django/core/checks/__pycache__/translation.cpython-39.pyc,, django/core/checks/__pycache__/urls.cpython-39.pyc,, -django/core/checks/async_checks.py,sha256=A9p_jebELrf4fiD6jJtBM6Gvm8cMb03sSuW9Ncx3-vU,403 -django/core/checks/caches.py,sha256=hbcIFD_grXUQR2lGAzzlCX6qMJfkXj02ZDJElgdT5Yg,2643 +django/core/checks/async_checks.py,sha256=rtYPbvAzZUbB23OTdfJgArNhVGCrepctB82PLaFTZ9k,403 +django/core/checks/caches.py,sha256=zrr_yP3FuSBo8rll_UtL6DrWEpJfQNTLldyMgFXij4w,2547 django/core/checks/compatibility/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/checks/compatibility/__pycache__/__init__.cpython-39.pyc,, -django/core/checks/compatibility/__pycache__/django_4_0.cpython-39.pyc,, -django/core/checks/compatibility/django_4_0.py,sha256=2s7lm9LZ0NrhaYSrw1Y5mMkL5BC68SS-TyD-TKczbEI,657 django/core/checks/database.py,sha256=sBj-8o4DmpG5QPy1KXgXtZ0FZ0T9xdlT4XBIc70wmEQ,341 -django/core/checks/files.py,sha256=W4yYHiWrqi0d_G6tDWTw79pr2dgJY41rOv7mRpbtp2Q,522 -django/core/checks/messages.py,sha256=vIJtvmeafgwFzwcXaoRBWkcL_t2gLTLjstWSw5xCtjQ,2241 -django/core/checks/model_checks.py,sha256=8aK5uit9yP_lDfdXBJPlz_r-46faP_gIOXLszXqLQqY,8830 -django/core/checks/registry.py,sha256=FaixxLUVKtF-wNVKYXVkOVTg06lLdwOty2mfdDcEfb4,3458 +django/core/checks/messages.py,sha256=ZbasGH7L_MeIGIwb_nYiO9Z_MXF0-aXO1ru2xFACj6Y,2161 +django/core/checks/model_checks.py,sha256=8j97FXQ2mTTNhOQcFhlvxFkoMXiAn4ezqqN4t44Y3G4,8597 +django/core/checks/registry.py,sha256=rt9v24JoPrCd1BV1rsAWAJN7FUGOMhIV1-QhEEKu_Fk,3239 django/core/checks/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/checks/security/__pycache__/__init__.cpython-39.pyc,, django/core/checks/security/__pycache__/base.cpython-39.pyc,, django/core/checks/security/__pycache__/csrf.cpython-39.pyc,, django/core/checks/security/__pycache__/sessions.cpython-39.pyc,, -django/core/checks/security/base.py,sha256=MXT5p_QB23rgdOC8bZ-m7j8OYY6u1y7bXpKFLhxmGMc,8530 -django/core/checks/security/csrf.py,sha256=nT4qi-YgISEvotUC4_X_Vd7gWUZ5f2703gKlFFx7s3U,2047 -django/core/checks/security/sessions.py,sha256=rwuWKyPNI1sUNJ2ZIhLIojnEeOoKpE2l4_fCWsITr70,2558 -django/core/checks/templates.py,sha256=9vbFFNMD99nf1QF5dNuisd5hpbVogP5Hgc_fHfm2F6k,1166 -django/core/checks/translation.py,sha256=it7VjXf10-HBdCc3z55_lSxwok9qEncdojRBG74d4FA,1990 -django/core/checks/urls.py,sha256=NIRbMn2r9GzdgOxhIujAICdYWC2M7SAiC5QuamENfU4,3328 -django/core/exceptions.py,sha256=856Rz8frSvYvg37zHXhX2GtZUReqtAybVueOJIx5Hjo,6297 -django/core/files/__init__.py,sha256=Rhz5Jm9BM6gy_nf5yMtswN1VsTIILYUL7Z-5edjh_HI,60 +django/core/checks/security/base.py,sha256=tDSpqh4E-9Rc9sP35V-kupb7vPY9JQc6lNoKYNLFKCo,8149 +django/core/checks/security/csrf.py,sha256=OKfJW-_gkbpY9EGmgHnE-uDunzzsr-JJS6zzUK8Ah50,2044 +django/core/checks/security/sessions.py,sha256=vvsxKEwb3qHgnCG0R5KUkfUpMHuZMfxjo9-X-2BTp-4,2558 +django/core/checks/templates.py,sha256=9_qZn_MWX94i209MVu2uS66NPRgbKWtk_XxetKczyfU,1092 +django/core/checks/translation.py,sha256=CkywI7a5HvzyWeJxKGaj54AKIynfxSMswGgg6NVV2LM,1974 +django/core/checks/urls.py,sha256=lA8wbw2WDC-e4ZAr-9ooEWtGvrNyMh1G-MZbojGq9W8,3246 +django/core/exceptions.py,sha256=tv-SBj73Sr-UJhkZIpCP0ZZVptXWuhVf-gih6DRMlg8,6231 +django/core/files/__init__.py,sha256=OjalFLvs-vPaTE3vP0eYZWyNwMj9pLJZNgG4AcGn2_Y,60 django/core/files/__pycache__/__init__.cpython-39.pyc,, django/core/files/__pycache__/base.cpython-39.pyc,, django/core/files/__pycache__/images.cpython-39.pyc,, @@ -3709,26 +3675,26 @@ django/core/files/__pycache__/temp.cpython-39.pyc,, django/core/files/__pycache__/uploadedfile.cpython-39.pyc,, django/core/files/__pycache__/uploadhandler.cpython-39.pyc,, django/core/files/__pycache__/utils.cpython-39.pyc,, -django/core/files/base.py,sha256=UeErNSLdQMR2McOUNfgjHBadSlmVP_DDHsAwVrn1gYk,4811 -django/core/files/images.py,sha256=nn_GxARZobyRZr15MtCjbcgax8L4JhNQmfBK3-TvB78,2643 -django/core/files/locks.py,sha256=XWB-AECioVzp4BFO_YImcpqPxJR95pfF8bOfogGIeds,3611 -django/core/files/move.py,sha256=3XS3kX7KerwZy0eYALnzXu2yeWkf-3pVE90uoyk2AK0,3101 -django/core/files/storage.py,sha256=ZeIJb1-SHOpIttd7HRxyeYKYKakrzxWlPKklOA0t3Bo,15668 -django/core/files/temp.py,sha256=iUegEgQ3UyUrDN10SgvKIrHfBPSej1lk-LAgJqMZBcU,2503 -django/core/files/uploadedfile.py,sha256=6hBjxmx8P0fxmZQbtj4OTsXtUk9GdIA7IUcv_KwSI08,4189 -django/core/files/uploadhandler.py,sha256=riobj6SKikjiacrhObFsW9NFRfjG5qPklsaS1pzpFvE,7179 -django/core/files/utils.py,sha256=f0naLw9ovd9z1DzQHLKXPJxHmBogsg4MEFZH4K9nxvg,2659 +django/core/files/base.py,sha256=jsYsE3bNpAgaQcUvTE8m1UTj6HVXkHd4bh-Y38JmF84,4812 +django/core/files/images.py,sha256=jmF29FQ1RHZ1Sio6hNjJ6FYVAiz5JQTkAyqX7qWSAFA,2569 +django/core/files/locks.py,sha256=pOsArksRTvk1wn8Sm1dribFKmvcsZZcfc6ew4wcyuXQ,3583 +django/core/files/move.py,sha256=_4xGm6hCV05X54VY0AkEjYFaNcN85x3hablD2J9jyS4,2973 +django/core/files/storage.py,sha256=oCM3Ro-Dl_WMgJPg6q5K2pKdhX972VNote_gijJNrsU,14927 +django/core/files/temp.py,sha256=yy1ye2buKU2PB884jKmzp8jBGIPbPhCa3nflXulVafQ,2491 +django/core/files/uploadedfile.py,sha256=_FZW5J9ewwE2oZmT0qhfT9bQmuryzvSkXMPWFr0CU8A,3990 +django/core/files/uploadhandler.py,sha256=R6Eyg7K2H-oOaer-OjFSWAV7H_4Z0jRbK6UAF759p3Y,6958 +django/core/files/utils.py,sha256=kfeJJWwFgGtwBmQhupLA-ufvEv_6PJBLcBdHafLPHuE,2659 django/core/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/handlers/__pycache__/__init__.cpython-39.pyc,, django/core/handlers/__pycache__/asgi.cpython-39.pyc,, django/core/handlers/__pycache__/base.cpython-39.pyc,, django/core/handlers/__pycache__/exception.cpython-39.pyc,, django/core/handlers/__pycache__/wsgi.cpython-39.pyc,, -django/core/handlers/asgi.py,sha256=-3Qw_J87Fbp7y9cpTUBwHQE7SrSFxFYJ1zW2RVMZLmE,11647 -django/core/handlers/base.py,sha256=K5SIqBB4VTro0H57XEaVZbDoF2gD3xVBk61x4PhN7-g,14836 -django/core/handlers/exception.py,sha256=aoHIjYBYMTvnm5KxQCkWXUVBpI2ABcBJ-3rmW0GJum4,5898 -django/core/handlers/wsgi.py,sha256=UONg1fxy5L6FqgGln4bUJVck-055ln3zWnJFz8YvUzc,7850 -django/core/mail/__init__.py,sha256=HJSPyTBz34PsIyv4jTFJvhswauZr51NpsB-gpYR73-A,4958 +django/core/handlers/asgi.py,sha256=S7STi-d4_-2np_jqjdZZpYuU_2enrfPnGCTZvsTuYdo,11170 +django/core/handlers/base.py,sha256=jtGOg6E_whcOF6nmSfqx4iqke0mPYO1KD0Dc6a4ZNoY,14427 +django/core/handlers/exception.py,sha256=dJzaaOP67wPexKjofiqH68_sUVaBW_uJpXi0J26BkCM,5646 +django/core/handlers/wsgi.py,sha256=qIfieIyZapfpIR1GmuIaBejuI9brrv_Po3SezAd-glQ,7829 +django/core/mail/__init__.py,sha256=LS59oJ0C1vGsNtVcAoEyLgYlDIAHVnHMLfqiMDauQfE,4875 django/core/mail/__pycache__/__init__.cpython-39.pyc,, django/core/mail/__pycache__/message.cpython-39.pyc,, django/core/mail/__pycache__/utils.cpython-39.pyc,, @@ -3740,23 +3706,23 @@ django/core/mail/backends/__pycache__/dummy.cpython-39.pyc,, django/core/mail/backends/__pycache__/filebased.cpython-39.pyc,, django/core/mail/backends/__pycache__/locmem.cpython-39.pyc,, django/core/mail/backends/__pycache__/smtp.cpython-39.pyc,, -django/core/mail/backends/base.py,sha256=Cljbb7nil40Dfpob2R8iLmlO0Yv_wlOCBA9hF2Z6W54,1683 -django/core/mail/backends/console.py,sha256=Z9damLP7VPLswrNDX9kLjL3MdWf9yAM6ZCeUv-3tRgU,1426 +django/core/mail/backends/base.py,sha256=f9Oeaw1RAiPHmsTdQakeYzEabfOtULz0UvldP4Cydpk,1660 +django/core/mail/backends/console.py,sha256=l1XFESBbk1Ney5bUgjCYVPoSDzjobzIK3GMQyxQX1Qk,1402 django/core/mail/backends/dummy.py,sha256=sI7tAa3MfG43UHARduttBvEAYYfiLasgF39jzaZPu9E,234 -django/core/mail/backends/filebased.py,sha256=AbEBL9tXr6WIhuSQvm3dHoCpuMoDTSIkx6qFb4GMUe4,2353 -django/core/mail/backends/locmem.py,sha256=AT8ilBy4m5OWaiyqm_k82HdkQIemn4gciIYILGZag2o,885 -django/core/mail/backends/smtp.py,sha256=ek6Jp3X5AKYM_LwKAYT4pZ4YVe495uwIerfe60l0ack,5538 -django/core/mail/message.py,sha256=Mdi8_UbFQD0k-WzM43cqTJcwqxpqwM5v5OM3Hzdm9Vk,17709 -django/core/mail/utils.py,sha256=Wf-pdSdv0WLREYzI7EVWr59K6o7tfb3d2HSbAyE3SOE,506 -django/core/management/__init__.py,sha256=ERXugED2C041pzlujq2WB8upfZeAe-OMhWAg26i8gRU,17617 +django/core/mail/backends/filebased.py,sha256=yriBReURf6y1c9fT2vnA2f_czy9cRJ9fSMipq9BX7tE,2300 +django/core/mail/backends/locmem.py,sha256=OgTK_4QGhsBdqtDKY6bwYNKw2MXudc0PSF5GNVqS7gk,884 +django/core/mail/backends/smtp.py,sha256=wJ3IsY94ust3PtXDUu-Vf59BuRUZIKb0ivJ7YCocKL0,5262 +django/core/mail/message.py,sha256=k7fyPYk6ecQTHDia6gfOSgv7LKrmR7L3hLku5egVL8Y,17026 +django/core/mail/utils.py,sha256=us5kx4w4lSev93Jjpv9chldLuxh3dskcQ1yDVS09MgM,506 +django/core/management/__init__.py,sha256=VqzDcs6AU0NtaVjRPmdapdMUFsIyV0m9N1hjIFkmr-M,17079 django/core/management/__pycache__/__init__.cpython-39.pyc,, django/core/management/__pycache__/base.cpython-39.pyc,, django/core/management/__pycache__/color.cpython-39.pyc,, django/core/management/__pycache__/sql.cpython-39.pyc,, django/core/management/__pycache__/templates.cpython-39.pyc,, django/core/management/__pycache__/utils.cpython-39.pyc,, -django/core/management/base.py,sha256=xo6x9RnjdY85DHOsZLL6ANx3v0Jx8QtiAoEYM6pnQMY,24338 -django/core/management/color.py,sha256=a9NzfaVrMCcn9xWYu_Yfi1fdAsFqtGa5H0waJcG_E-M,2874 +django/core/management/base.py,sha256=lrUiEhdj5zC3jh97GR3x_gNSVfWPFmQGYTLk3uT9GXU,22671 +django/core/management/color.py,sha256=Hd3YHJqt_mvIZvpECw50C4dNsk1pj3F9vDSXnLS_pg0,2854 django/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/management/commands/__pycache__/__init__.cpython-39.pyc,, django/core/management/commands/__pycache__/check.cpython-39.pyc,, @@ -3783,35 +3749,35 @@ django/core/management/commands/__pycache__/startapp.cpython-39.pyc,, django/core/management/commands/__pycache__/startproject.cpython-39.pyc,, django/core/management/commands/__pycache__/test.cpython-39.pyc,, django/core/management/commands/__pycache__/testserver.cpython-39.pyc,, -django/core/management/commands/check.py,sha256=KPtpSfNkIPPKaBP4od_vh-kp_D439sG8T9MOU41p9DA,2652 -django/core/management/commands/compilemessages.py,sha256=zb5fkLrfXSg5LQgs5m-SUBDFt7OtYmdgEmqiENv1Vrc,6992 -django/core/management/commands/createcachetable.py,sha256=1gXJFZpvuCZPd1I_VlhFlCVOPmxk-LQxFB0Tf2H2eyA,4616 -django/core/management/commands/dbshell.py,sha256=XWBxHQIxXzKd_o81PxmmOCV67VPcqbDr9Und6LEAt9Q,1731 -django/core/management/commands/diffsettings.py,sha256=NNL_J0P3HRzAZd9XcW7Eo_iE_lNliIpKtdcarDbBRpc,3554 -django/core/management/commands/dumpdata.py,sha256=PTJ32bLwSRd-NkZZhpowxNMHAPQpclAoOFESKMsyGQg,10962 -django/core/management/commands/flush.py,sha256=9KhMxzJFqA3cOCw-0VFZ2Utb2xZ-xCnn8ZGeiVGOm8E,3611 -django/core/management/commands/inspectdb.py,sha256=MfNrdVMb09c8bcfSRufGaFgY70bUaL1LxIpQmJzB93c,15142 -django/core/management/commands/loaddata.py,sha256=RJrdi1VWKv53FcBMrXZhLpTYwsjZvhqo76JJdor3aaA,15969 -django/core/management/commands/makemessages.py,sha256=MgK4yC93plBzG9jQ746VJRPjbAJUPuMnfrngYVWVdZc,27834 -django/core/management/commands/makemigrations.py,sha256=PdOgjdH3luE3R3dalmsf66oQFc8kv7NFMe6cuhltnik,16244 -django/core/management/commands/migrate.py,sha256=RXD2zuUCoQ4gMkyV-5kqOOlHXNmPVMlM3zidM0U3fQU,18458 -django/core/management/commands/runserver.py,sha256=AAXjfEFhDysNLQGaH-xpPUp61G2JfvNzcULTaahEHuo,6788 -django/core/management/commands/sendtestemail.py,sha256=sF5TUMbD_tlGBnUsn9t-oFVGNSyeiWRIrgyPbJE88cs,1518 -django/core/management/commands/shell.py,sha256=LKmj6KYv6zpJzQ2mWtR4-u2CDSQL-_Na6TsT4JLYsi4,4613 -django/core/management/commands/showmigrations.py,sha256=dHDyNji_c55LntHanNT7ZF2EOq6pN4nulP-e4WRPMwE,6807 -django/core/management/commands/sqlflush.py,sha256=wivzfu_vA5XeU7fu2x1k7nEBky_vjtJgU4ruPja1pRQ,991 -django/core/management/commands/sqlmigrate.py,sha256=fjC7M5-cFxPV6yiqpSwpBrvo4ygZQeqoGEAVywVhKQY,3308 -django/core/management/commands/sqlsequencereset.py,sha256=Bf6HoGe5WoyAivZv1qYpklFQF9CaG4X2s1sLxT6U0Xw,1061 -django/core/management/commands/squashmigrations.py,sha256=hPS7TG6zgnXKf0hW30iOX6Uue-U-1paE7b_-43KVPzI,10241 -django/core/management/commands/startapp.py,sha256=Dhllhaf1q3EKVnyBLhJ9QsWf6JmjAtYnVLruHsmMlcQ,503 -django/core/management/commands/startproject.py,sha256=Iv7KOco1GkzGqUEME_LCx5vGi4JfY8-lzdkazDqF7k8,789 -django/core/management/commands/test.py,sha256=R0DDsSQ3rYHvA6rL0tFh-Q66JibpP6naPhirF3PeKnY,2554 -django/core/management/commands/testserver.py,sha256=o0MuEiPYKbZ4w7bj3BnwDQawc5CNOp53nl4e_nretF0,2245 -django/core/management/sql.py,sha256=fP6Bvq4NrQB_9Tb6XsYeCg57xs2Ck6uaCXq0ojFOSvA,1851 -django/core/management/templates.py,sha256=NMBG5yNyTvaBD5_HidoaiJ-8XqZLcTLeXVStgMX80ZY,14694 -django/core/management/utils.py,sha256=VmC3Qz0Qxza4ZZf8g5tVjOY3M_P4phA4RztlUShzXGk,4897 -django/core/paginator.py,sha256=RItcuDrLFaa6IeWvaoOeFWSukWvP2FFRft28oQnFc_0,7542 -django/core/serializers/__init__.py,sha256=gaH58ip_2dyUFDlfOPenMkVJftQQOBvXqCcZBjAKwTA,8772 +django/core/management/commands/check.py,sha256=ForUXuDlVDXWrt6xeMo6bC8E3XoC9g2BkzFG8lgqUZg,2460 +django/core/management/commands/compilemessages.py,sha256=hLywDb4YQon5qWmDT0S7yfJUrOMuCgjvo061Z2vQ7SE,6749 +django/core/management/commands/createcachetable.py,sha256=4s1T7DVbgO4qONQqgNSqsd9-vxz7vRfhyXSM3JMl2NE,4292 +django/core/management/commands/dbshell.py,sha256=_xJesr2QTeJVXrFpFoOqDe29Dzz7xdAkB4FbgAjo1UE,1652 +django/core/management/commands/diffsettings.py,sha256=K5HU5H75py6854mm-iJbN_MN9DE7Hjk_TjPnPgkmOT8,3370 +django/core/management/commands/dumpdata.py,sha256=jIeeHp3jXmf8dhoiFCx3AJ4OD2K0gsg21XqZCP-Ahpk,10395 +django/core/management/commands/flush.py,sha256=ALubZDzzOlxRfzsxBt-3YDLkDBEYHlff7DBLiEpN0Go,3527 +django/core/management/commands/inspectdb.py,sha256=uvY5BAlhqEthYOSBthkvqI1JSpAWcYPNflAfjW07FFU,13810 +django/core/management/commands/loaddata.py,sha256=zFEYTesoAK9PB57JlttgCgwhpONfDV2GTNMNBIfOUIE,14632 +django/core/management/commands/makemessages.py,sha256=hP8OfT5QmdYPv0X069GqguOGI5oBAJJJ3BGK_WxXWVo,26517 +django/core/management/commands/makemigrations.py,sha256=I7NYrtBxHC74R4Yd4ajy9BurEXpz5VBq40GQEYOksrw,15241 +django/core/management/commands/migrate.py,sha256=rMLWzdPyiZTm6bf-E06wnfYdSB6QnqfwEWxT4i1irw0,16863 +django/core/management/commands/runserver.py,sha256=ejXjKOJgI1Y41ldHKac34lBYZB4usKa5g8iNjGCYkHE,6267 +django/core/management/commands/sendtestemail.py,sha256=LsiP5Xg4AWLtc0vS0JinaaAAKjBbLUnfCq0pa0r4FFw,1456 +django/core/management/commands/shell.py,sha256=5cd91a465PQ3SUdmQA07IwTj2VyPP-EQIAZiFFZTE_o,4049 +django/core/management/commands/showmigrations.py,sha256=EYtkzVgj_utUogsQ_y7qoCpMQvAXY1u4KchtcVdH7hU,5855 +django/core/management/commands/sqlflush.py,sha256=SKRkk3MXuo7zc8ooWhxBZLdLJShD-VzaTJiGBn5AU6Q,928 +django/core/management/commands/sqlmigrate.py,sha256=iSaF13OoO5jSeW4mK-8WlnUaYkkb2Q1mJO_4dRPoHPc,3102 +django/core/management/commands/sqlsequencereset.py,sha256=whux0yEJxQDbZ-6B_PYOnAVkcrwLUZOrca0ZFynh97w,982 +django/core/management/commands/squashmigrations.py,sha256=TjKfRi5f_oXJJsTS5a0z5S9RP-Peb00Dqf_uaiJdFHg,9728 +django/core/management/commands/startapp.py,sha256=rvXApmLdP3gBinKaOMJtT1g3YrgVTlHteqNqFioNu8Y,503 +django/core/management/commands/startproject.py,sha256=HTxPhSOdteOaqA51UZI5h75ebFIR2x8rvIsMKoYbD9Y,789 +django/core/management/commands/test.py,sha256=-zi3NpOrkYjoHGNmKQXETa8wDiWfyWqWN3r2ShD5jKE,2248 +django/core/management/commands/testserver.py,sha256=8fEHJtw-k4nX8fgLA2cEHEiS2PYT2Qmhm86JnZFsbMM,2114 +django/core/management/sql.py,sha256=uwvdf0YDSGiTAJKLOmD9bKXH3DUD_s95fmy9Bc3noqY,1641 +django/core/management/templates.py,sha256=f5CFqBr2TN1hrDqgg540IFNzN7bZ5zfDUE6vhXKZJlo,13653 +django/core/management/utils.py,sha256=k_YvRKOkaVDUjrRWkZe3MDGg6kB3iaCFymJDs30pJ_A,4873 +django/core/paginator.py,sha256=6aZ3fyTVY2AsIxAoimlrt6K7gnuiqqxB8Xtqc6P1N9s,7521 +django/core/serializers/__init__.py,sha256=EODXMotb1Jg3Rkedjdqi5QUg84w4_jfDlnbaulUOIE8,8628 django/core/serializers/__pycache__/__init__.cpython-39.pyc,, django/core/serializers/__pycache__/base.cpython-39.pyc,, django/core/serializers/__pycache__/json.cpython-39.pyc,, @@ -3819,21 +3785,21 @@ django/core/serializers/__pycache__/jsonl.cpython-39.pyc,, django/core/serializers/__pycache__/python.cpython-39.pyc,, django/core/serializers/__pycache__/pyyaml.cpython-39.pyc,, django/core/serializers/__pycache__/xml_serializer.cpython-39.pyc,, -django/core/serializers/base.py,sha256=x2JpZwHk5zOHPsO0VLGZONRX7Vi1R7OoQXB_IngHxwo,12995 -django/core/serializers/json.py,sha256=GK9Slqj1cCeQVZU-jkagTC_hRsvgf2kBmdEseBcRpn8,3446 -django/core/serializers/jsonl.py,sha256=671JRbWRgOH3-oeD3auK9QCziwtrcdbyCIRDy5s4Evw,1879 -django/core/serializers/python.py,sha256=mfP8mMuaaYCl4cy6sXVAr8YQLOgsTBO-7jelfmWA9oc,6490 -django/core/serializers/pyyaml.py,sha256=77zu6PCfJg_75m36lX9X5018ADcux5qsDGajKNh4pI8,2955 -django/core/serializers/xml_serializer.py,sha256=iN0du1rdtJuo1CI1mk4vCN_kYsFeZ2Lshs0_KIs6kgw,17949 +django/core/serializers/base.py,sha256=G9Ev7aLCMJxZzylZDU7uxd44A7MDI5WJag3X0iEpNes,11740 +django/core/serializers/json.py,sha256=peWRUbvjFzrMKl7wdF5gucyG68en8DvMrKKd9Y-qNB8,3411 +django/core/serializers/jsonl.py,sha256=cWEF6GD3lhDsupZk9uAWm2uXSbAbgfLHYkVjS7xA5sU,1845 +django/core/serializers/python.py,sha256=SEKlPPvYau0FgQPv4wE6WDzKK_AKoGXVFEknfBA1888,6078 +django/core/serializers/pyyaml.py,sha256=4uBpVQt05FCGSBd7j3KklqEQPYVrrEeWSy9-_6noVEM,2896 +django/core/serializers/xml_serializer.py,sha256=Kv7Xj2P0My4DWBD85jegn6Msv5eTMIOqS_PAK3v1xI0,17257 django/core/servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/core/servers/__pycache__/__init__.cpython-39.pyc,, django/core/servers/__pycache__/basehttp.cpython-39.pyc,, -django/core/servers/basehttp.py,sha256=ueq272hWTqH-t6_j312lvL02ro-pzYaMrI22B48JFuU,8815 +django/core/servers/basehttp.py,sha256=amRRVXIaMgSvy767N0H6_z8eC-NX34li_vSXjwKH3Us,7970 django/core/signals.py,sha256=5vh1e7IgPN78WXPo7-hEMPN9tQcqJSZHu0WCibNgd-E,151 -django/core/signing.py,sha256=R7Ucq6mjE7ZP3yWucDUkqrvJUr_kuiPRMs1AXhyF1wU,8232 -django/core/validators.py,sha256=TTlbJUlq_b6FoEcQzRQGODK6330JkSOfJP0mbFscypU,21222 +django/core/signing.py,sha256=JFwxHIBbpAvpmMlqc43Y8TdAXYa735tDfrvYqhntH2U,8163 +django/core/validators.py,sha256=OBoi9MyUXLrwCHPCzHsNz48uruiH-EIXXY049LfFpqk,20667 django/core/wsgi.py,sha256=2sYMSe3IBrENeQT7rys-04CRmf8hW2Q2CjlkBUIyjHk,388 -django/db/__init__.py,sha256=iw69FXBNG7Xwb2OzJP6o3cDVr46IMFSSRTVKivveUv0,1441 +django/db/__init__.py,sha256=w5hqZD3iUCresDu9m4_J6JC4Nv9JIEGtbtw2Ak95qt4,1365 django/db/__pycache__/__init__.cpython-39.pyc,, django/db/__pycache__/transaction.cpython-39.pyc,, django/db/__pycache__/utils.cpython-39.pyc,, @@ -3852,20 +3818,20 @@ django/db/backends/base/__pycache__/introspection.cpython-39.pyc,, django/db/backends/base/__pycache__/operations.cpython-39.pyc,, django/db/backends/base/__pycache__/schema.cpython-39.pyc,, django/db/backends/base/__pycache__/validation.cpython-39.pyc,, -django/db/backends/base/base.py,sha256=w-xQ5ZzxExbbX73Lg9hVol6hWumRvhQwLrHoKoszjic,25340 -django/db/backends/base/client.py,sha256=90Ffs6zZYCli3tJjwsPH8TItZ8tz1Pp-zhQa-EpsNqc,937 -django/db/backends/base/creation.py,sha256=VX38HmJQsrJeLc6x23-D2p5lqO7su-3buEpWeJiEGmg,15084 -django/db/backends/base/features.py,sha256=syGA6jIvEqUpF9zChLGFA6Fo1ImFUNrEyldlhUInBec,13941 -django/db/backends/base/introspection.py,sha256=r6Pk8lvcT_l-BaqYHjM7i5j8IZId2-hk3sD6td1uTIQ,8068 -django/db/backends/base/operations.py,sha256=Lf1RZPx69GZUyVEavDjVwU7XU7seGbbA7BCUCsx16U0,28043 -django/db/backends/base/schema.py,sha256=xDcy1yxyKCYAcsluVPRIcqt57q9qxkrzCR8h3S8-KSU,67434 -django/db/backends/base/validation.py,sha256=2zpI11hyUJr0I0cA1xmvoFwQVdZ-7_1T2F11TpQ0Rkk,1067 -django/db/backends/ddl_references.py,sha256=eBDnxoh7_PY2H8AGuZ5FUoxsEscpnmMuYEMqzfPRFqk,8129 +django/db/backends/base/base.py,sha256=ZtZwAyIBXINSYIPZf-pAhIiolgaL9fmZIg_PuKmCWoQ,24749 +django/db/backends/base/client.py,sha256=I5cq6_iCIfoYkygvWOSMXQNZW8NF-WYBy5MKI9KXlXA,914 +django/db/backends/base/creation.py,sha256=8pzAFoOBZERV57N0s9_4NKLUZqEqwQfOJlfFiKmlYB0,14643 +django/db/backends/base/features.py,sha256=Wfd1qu2jGCfWZ61z-NljRtzkmIOiXTfa7slvxEmr3DQ,13713 +django/db/backends/base/introspection.py,sha256=-q0LR9IHMrSrIkT6lwqOSEMPoqjj9uMzQiclDXfo76M,7745 +django/db/backends/base/operations.py,sha256=N5CJ4_u1C-3UG8fbcbf9cJzbFvaaNZuUj2RQSMBi66Y,26839 +django/db/backends/base/schema.py,sha256=_kEIILMPwgE4xvaMGt2pTIUIqpN5HqKtr1s-rUBu09I,62622 +django/db/backends/base/validation.py,sha256=4zIAVsePyETiRtK7CAw78y4ZiCPISs0Pv17mFWy2Tr4,1040 +django/db/backends/ddl_references.py,sha256=f0zcaGRwhG2W-hbUiXquzRuEBftMo0Ve8OTHKZKuHTQ,8052 django/db/backends/dummy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/db/backends/dummy/__pycache__/__init__.cpython-39.pyc,, django/db/backends/dummy/__pycache__/base.cpython-39.pyc,, django/db/backends/dummy/__pycache__/features.cpython-39.pyc,, -django/db/backends/dummy/base.py,sha256=TYBN_29LPOUA94rombqiQaVlXoScTcOPJdXYmJaDeu0,2212 +django/db/backends/dummy/base.py,sha256=ZsB_hKOW9tuaNbZt64fGY6tk0_FqMiF72rp8TE3NrDA,2244 django/db/backends/dummy/features.py,sha256=Pg8_jND-aoJomTaBBXU3hJEjzpB-rLs6VwpoKkOYuQg,181 django/db/backends/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/db/backends/mysql/__pycache__/__init__.cpython-39.pyc,, @@ -3878,15 +3844,15 @@ django/db/backends/mysql/__pycache__/introspection.cpython-39.pyc,, django/db/backends/mysql/__pycache__/operations.cpython-39.pyc,, django/db/backends/mysql/__pycache__/schema.cpython-39.pyc,, django/db/backends/mysql/__pycache__/validation.cpython-39.pyc,, -django/db/backends/mysql/base.py,sha256=pAnle6fHcVKqOK4zvdyq2GG6TAa2yXcVLm-GvneSS3o,16835 -django/db/backends/mysql/client.py,sha256=kehdRl8BX9KPcfTwYd3iDtHN3cdl-IFWUcV3cYHlIXY,2581 -django/db/backends/mysql/compiler.py,sha256=wju0iZ_KHfEgyNtQjnLizXC_GxW2uc61zgznLIGo_C0,3075 -django/db/backends/mysql/creation.py,sha256=8BV8YHk3qEq555nH3NHukxpZZgxtvXFvkv7XvkRlhKA,3449 -django/db/backends/mysql/features.py,sha256=mn1RgTViBBO80QLUBHHLaClWCHVbUV6i1Dyoivfbmr0,11508 -django/db/backends/mysql/introspection.py,sha256=54X68yV0Q0pNYznhj3ecmJy_EJu7h_zSh23U4LQfMxc,14090 -django/db/backends/mysql/operations.py,sha256=j61c1XrlCOlOY2E-YpYK6xEJ2xzfHfItdkOamEkOF3A,16825 -django/db/backends/mysql/schema.py,sha256=7ho_sQkRzkPRHNzRq-7ZaI3UPZqBFstJP-2HbV9DouU,7294 -django/db/backends/mysql/validation.py,sha256=XERj0lPEihKThPvzoVJmNpWdPOun64cRF3gHv-zmCGk,3093 +django/db/backends/mysql/base.py,sha256=6Qb9anWVdgP4eNeOFeFmlDPDc9yUS0aTV0tySIIzsOU,16208 +django/db/backends/mysql/client.py,sha256=_ckHjGopeVXMWu5vtSA5GQBIrO-wWtOKI4VyYZ4uGMU,2580 +django/db/backends/mysql/compiler.py,sha256=dqo5iPN7qDFCIW-J5qqLkIOonD5RmE-Z2Jx63SibBN8,2997 +django/db/backends/mysql/creation.py,sha256=KGvNrvPt2Qy84S_d_kwv8Zsod4-P1aUQjBn7aLM1OOg,3096 +django/db/backends/mysql/features.py,sha256=O7fkTubc9jFnxxSp4WxZaPZxe5x5spUiuaHGOkAn-v8,9116 +django/db/backends/mysql/introspection.py,sha256=7SwmXKUAtJmxXVXHohw3tu4F6Y2zBKewRHnHbawlEtQ,13737 +django/db/backends/mysql/operations.py,sha256=fiEhJHwgFbgXLHBE1jeYGbGND6wRpd3c-Shlge8OEI0,16104 +django/db/backends/mysql/schema.py,sha256=JJaZkF-ZCrnCi79Klyavu1JsWzRXIUATq4uqluKyWBM,7132 +django/db/backends/mysql/validation.py,sha256=U11SbB91lcWzaZZPxY96Cik9s9wO61cm_fOxnX-Cvzo,2920 django/db/backends/oracle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/db/backends/oracle/__pycache__/__init__.cpython-39.pyc,, django/db/backends/oracle/__pycache__/base.cpython-39.pyc,, @@ -3899,16 +3865,16 @@ django/db/backends/oracle/__pycache__/operations.cpython-39.pyc,, django/db/backends/oracle/__pycache__/schema.cpython-39.pyc,, django/db/backends/oracle/__pycache__/utils.cpython-39.pyc,, django/db/backends/oracle/__pycache__/validation.cpython-39.pyc,, -django/db/backends/oracle/base.py,sha256=qEJQi2sIOte1iS5BKcp40GCCeXsGEjdD8qyvZjLVqeQ,22973 -django/db/backends/oracle/client.py,sha256=DfDURfno8Sek13M8r5S2t2T8VUutx2hBT9DZRfow9VQ,784 -django/db/backends/oracle/creation.py,sha256=KVUU5EqNWeaeRMRj0Q2Z3EQ-F-FRuj25JaXdSTA_Q7I,20834 -django/db/backends/oracle/features.py,sha256=We9YF5bG4TbPEMzJ36bDI4XtcaaJrmsryHCK2WJDBvo,4800 -django/db/backends/oracle/functions.py,sha256=2OoBYyY1Lb4B5hYbkRHjd8YY_artr3QeGu2hlojC-vc,812 -django/db/backends/oracle/introspection.py,sha256=rBgffDO89PipOOCmyyf3AH71nMpj-j2a8Sujvd1L-B0,15416 -django/db/backends/oracle/operations.py,sha256=IHj3v7kxUMF2ry9r6M86u3thi1Q7Nfr5Zq7Z6yFZ7sk,29026 -django/db/backends/oracle/schema.py,sha256=WyS3h3oKwhU6PB0NAfEyFNTcbW3kMaUuEHATxwLnVF8,10299 -django/db/backends/oracle/utils.py,sha256=I4N0L0epD4HLE4StVJZ-SwN8cPl_92ZySZuLkj6d3tQ,2682 -django/db/backends/oracle/validation.py,sha256=cq-Bvy5C0_rmkgng0SSQ4s74FKg2yTM1N782Gfz86nY,860 +django/db/backends/oracle/base.py,sha256=rhJ8S1qq3muH28GlU-xFrx-yXbXrU_GOeAqjpgs-ICA,22773 +django/db/backends/oracle/client.py,sha256=8vh5J141-xep7KCgylRkQkqZb7EeptT1MnOhBO7TKOY,784 +django/db/backends/oracle/creation.py,sha256=PIK2aKSL7ITWPV-HePu0jp0hab34b9iYXZKhQndEJog,19630 +django/db/backends/oracle/features.py,sha256=cVISrqh6crqMReGsR_fxm1Ibe2BQaQ2jv1zDxar7zyw,4867 +django/db/backends/oracle/functions.py,sha256=PHMO9cApG1EhZPD4E0Vd6dzPmE_Dzouf9GIWbF1X7kc,768 +django/db/backends/oracle/introspection.py,sha256=qh2jcooRMM8DW2inwW-qhZisgfmBI1A6vV6U2wVVSSw,14144 +django/db/backends/oracle/operations.py,sha256=MAKOvWff-dVtuNvJO4gb3X0aR2szX8TTvwcx3kN7MP4,27792 +django/db/backends/oracle/schema.py,sha256=RxDpQJKxcJriPJf2s2HuWcOGy8R7yb_9Bkebebs3Ww0,9149 +django/db/backends/oracle/utils.py,sha256=Vx9HvK3GKgqMW9CYVkVXsaF9uVwchVC7IjrrkGo1tfY,2656 +django/db/backends/oracle/validation.py,sha256=O1Vx5ljfyEVo9W-o4OVsu_OTfZ5V5P9HX3kNMtdE75o,860 django/db/backends/postgresql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/db/backends/postgresql/__pycache__/__init__.cpython-39.pyc,, django/db/backends/postgresql/__pycache__/base.cpython-39.pyc,, @@ -3918,13 +3884,13 @@ django/db/backends/postgresql/__pycache__/features.cpython-39.pyc,, django/db/backends/postgresql/__pycache__/introspection.cpython-39.pyc,, django/db/backends/postgresql/__pycache__/operations.cpython-39.pyc,, django/db/backends/postgresql/__pycache__/schema.cpython-39.pyc,, -django/db/backends/postgresql/base.py,sha256=ejKk7ZoCqzNvuzVSoxtoqkOyIXyao-JucRtGW6Lixe0,13825 -django/db/backends/postgresql/client.py,sha256=7-Q-fpIFgeFwO6mbNKosHZYcCQskcA58OqXKBG1kkl4,2052 -django/db/backends/postgresql/creation.py,sha256=ZuyzNToyKmUA6S49a3is6SQg8ELV3_ijvm3Ch3uMf7o,3663 -django/db/backends/postgresql/features.py,sha256=ewVukS_I5N1UuPpveIUk2FxSsjLvn8WOD_yw-C0aZ0g,3443 -django/db/backends/postgresql/introspection.py,sha256=7uOy6gLxdRHKVXmn8tgLtDhqmrNVSUiHFcDMA0rug3g,11149 -django/db/backends/postgresql/operations.py,sha256=f0yjtmKezSnd444Gxqro_WT4v3Ule4wUTA5GrfqLSqM,12445 -django/db/backends/postgresql/schema.py,sha256=OxCeOtxZL8zuZlUabScTlp2y5dmZNgpc4ZCxOtewVQU,12096 +django/db/backends/postgresql/base.py,sha256=OhEp8JRbV1C0MNQ6P3Kv77DbOJZi5Ql1I5Xg6oOsTlA,13221 +django/db/backends/postgresql/client.py,sha256=4Ur2Mr32i0DddxflHt4eXSsqMKITUrSZI_akPJPRouc,1705 +django/db/backends/postgresql/creation.py,sha256=YtzTqKB1406xZhJkPd4auKRoTpxyY74ZvDIhwVGYGW8,3344 +django/db/backends/postgresql/features.py,sha256=OqQ-cIhRxRpTSYXP1e0cSHjCbO1HHwCz933KVeZCbso,3969 +django/db/backends/postgresql/introspection.py,sha256=kFtbD2R1F4K8zHk-wfNcCyZm_VQA6YfVTeTyCxT2TTI,10345 +django/db/backends/postgresql/operations.py,sha256=Z3qLSXIu5xAD5CRO0BVoY33XSmPH5sJfff9WlsRQtMg,11471 +django/db/backends/postgresql/schema.py,sha256=vl9aWYLMIjpmTXTAMT4r-oyD0UqMruMuRXjnFR5NVGg,11091 django/db/backends/signals.py,sha256=Yl14KjYJijTt1ypIZirp90lS7UTJ8UogPFI_DwbcsSc,66 django/db/backends/sqlite3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/db/backends/sqlite3/__pycache__/__init__.cpython-39.pyc,, @@ -3935,14 +3901,14 @@ django/db/backends/sqlite3/__pycache__/features.cpython-39.pyc,, django/db/backends/sqlite3/__pycache__/introspection.cpython-39.pyc,, django/db/backends/sqlite3/__pycache__/operations.cpython-39.pyc,, django/db/backends/sqlite3/__pycache__/schema.cpython-39.pyc,, -django/db/backends/sqlite3/base.py,sha256=ol9gsuu_oaQVQb7Wgn6A6qOwBcrRQYdH4ooCLhhVobk,27350 -django/db/backends/sqlite3/client.py,sha256=Eb_-P1w0aTbZGVNYkv7KA1ku5Il1N2RQov2lc3v0nho,321 -django/db/backends/sqlite3/creation.py,sha256=a64zo_KZ1UTrIoQge7LyhfJiqsKgpmUzBkMFC05kfs8,4528 -django/db/backends/sqlite3/features.py,sha256=h6s4v_ovycrSXYomty2F7ePXlNu3MqAO6T7QmMrSZnc,5973 -django/db/backends/sqlite3/introspection.py,sha256=_HMysyF7tIW51VZgdW0NmXFU2hGU3lXunAPMmbnmnww,21335 -django/db/backends/sqlite3/operations.py,sha256=v4va12h738krQPqvdw1C5igPGLM9x-UypTXpPxBPe60,16177 -django/db/backends/sqlite3/schema.py,sha256=BUbnIm0tGEqmkQ2LpiFs87QNa-HPiuYg4vvIHpoKe_s,22398 -django/db/backends/utils.py,sha256=P4lD-Q6XEqDaLMS_kOMOANq7vuugkgt7TmBtTDkNKn4,9202 +django/db/backends/sqlite3/base.py,sha256=xhSrOTjoMHaMLYT2jenw4-gTcBRCACQvD1-tINLDfdc,26116 +django/db/backends/sqlite3/client.py,sha256=z5Y5RRyT5dwRSaQ0hhS5EYBuSt-CB2g79AKdgGOdzxw,522 +django/db/backends/sqlite3/creation.py,sha256=Z54YcyMPiVqGPwoMsVE4RWd5Bi3G7Yt4RaniLqgLTkw,4370 +django/db/backends/sqlite3/features.py,sha256=_-ods9b47DYwXueye4PpRiqBwvPcZ4oUQTOG9gxN5cI,4815 +django/db/backends/sqlite3/introspection.py,sha256=_3jUocpL34mVHIAI3IlWGB5dMhlaPZLTGBvxcC1699M,20361 +django/db/backends/sqlite3/operations.py,sha256=szVT6ZgY1v-P-fasiklI-OA6rLcA8LJidTCVagni7zA,14934 +django/db/backends/sqlite3/schema.py,sha256=7A_oBdg7ujFfadHpZc94ElEngU7g8Z91JNglOCeFp5E,20748 +django/db/backends/utils.py,sha256=ze_39D7cNv6fVgnVgc5GC85PDuvGE0xdEXluWTtOlPo,8433 django/db/migrations/__init__.py,sha256=Oa4RvfEa6hITCqdcqwXYC66YknFKyluuy7vtNbSc-L4,97 django/db/migrations/__pycache__/__init__.cpython-39.pyc,, django/db/migrations/__pycache__/autodetector.cpython-39.pyc,, @@ -3958,30 +3924,32 @@ django/db/migrations/__pycache__/serializer.cpython-39.pyc,, django/db/migrations/__pycache__/state.cpython-39.pyc,, django/db/migrations/__pycache__/utils.cpython-39.pyc,, django/db/migrations/__pycache__/writer.cpython-39.pyc,, -django/db/migrations/autodetector.py,sha256=uO45iVduLNcrqXLzusc8evQ76hKRb5iPUV9rRefPCP8,70734 -django/db/migrations/exceptions.py,sha256=SotQF7ZKgJpd9KN-gKDL8wCJAKSEgbZToM_vtUAnqHw,1204 -django/db/migrations/executor.py,sha256=hFI8JRGc9vCJCIzEF8TWy4c1XsZDSAMegC8LOgpPCnQ,18698 -django/db/migrations/graph.py,sha256=vt7Pc45LuiXR8aRCrXP5Umm6VDCCTs2LAr5NXh-rxcE,13055 -django/db/migrations/loader.py,sha256=KRHdjq7A0sHqOS0JHVNlR8MtQvbY9smjId7rngwrrOU,16863 -django/db/migrations/migration.py,sha256=gMnKmIgRC8Ixn8fGO4gVvP5EYEmUtxkI_cCmkR5KvZY,9483 -django/db/migrations/operations/__init__.py,sha256=q6REvafmof5Hb6e-PxsfcpCVasydBfIna2Pev_b5c-U,870 +django/db/migrations/autodetector.py,sha256=48V1Fm-CIvFBhKdpyE8A6EJ_fxpBdwRI-YDyUN5Whbc,64174 +django/db/migrations/exceptions.py,sha256=XLTZ_ufpVJX_nL4egDEG5DqvB8eqSGUuVoMNZ1lpXek,1198 +django/db/migrations/executor.py,sha256=DYSnPD6fc-r5QTslLf3yjyNizp9XYQ5V9nDVXeu_5jg,17841 +django/db/migrations/graph.py,sha256=qho3dqkbm8QyaRebGQUBQWFv1TQ-70AS8aWtOmw3Ius,12841 +django/db/migrations/loader.py,sha256=7N0hsdU5HEBxWt8A3NyBlQ9plVvImJ7GQK2YIy3oUfM,16330 +django/db/migrations/migration.py,sha256=nqKwXxVJjSMvsebByz4Up1Js1yC1FgSU-4Z9fmHzdPY,9108 +django/db/migrations/operations/__init__.py,sha256=48VoWNmXeVdSqnMql-wdWVGmv8BWpfFLz2pH3I5RDCY,778 django/db/migrations/operations/__pycache__/__init__.cpython-39.pyc,, django/db/migrations/operations/__pycache__/base.cpython-39.pyc,, django/db/migrations/operations/__pycache__/fields.cpython-39.pyc,, django/db/migrations/operations/__pycache__/models.cpython-39.pyc,, django/db/migrations/operations/__pycache__/special.cpython-39.pyc,, -django/db/migrations/operations/base.py,sha256=-wdWlbVLtUGeOeWKyuQ67R3HCx_jd0ausstbJcBT4QQ,5082 -django/db/migrations/operations/fields.py,sha256=d48jg79ZfPGwHBBJnKXEmKQXcbx-XpOOSKIFW4rMLWE,12633 -django/db/migrations/operations/models.py,sha256=MNc0bHWIwN0J7AZzhucpCWSrGhVMtnCVGStvBcTtwXs,33452 -django/db/migrations/operations/special.py,sha256=3Zbya6B1nEjvIwhQLoFR8kGBZUlc26kgBxX7XS3aeFQ,7831 -django/db/migrations/optimizer.py,sha256=c0JZ5FGltD_gmh20e5SR6A21q_De6rUKfkAJKwmX4Ks,3255 -django/db/migrations/questioner.py,sha256=QRw_8XrD3RmPf25ZUisqE7TXIBoBoP7p5Y4r0K9nGOE,10158 -django/db/migrations/recorder.py,sha256=36vtix99DAFnWgKQYnj4G8VQwNfOQUP2OTsC_afAPNM,3535 -django/db/migrations/serializer.py,sha256=2Ffq8uKPxRDK_qHW_Skm8CMj1_O1oUilqpE0nhLFjDo,13303 -django/db/migrations/state.py,sha256=4QJtorTt9nYVRuplTkSO85yL3XjDC219mrxd4iZG7ZM,39723 -django/db/migrations/utils.py,sha256=uas_tahI8e6HgNL4NdqLK1OdCXgqcwrxs-85_jdezbU,4320 -django/db/migrations/writer.py,sha256=KqsYN3bDTjGWnuvVvkAj06qk2lhFQLkaWsr9cW-oVYI,11458 -django/db/models/__init__.py,sha256=CB0CfDP1McdMRNfGuDs1OaJ7Xw-br2tC_EIjTcH51X4,2774 +django/db/migrations/operations/__pycache__/utils.cpython-39.pyc,, +django/db/migrations/operations/base.py,sha256=-r6c4Q7CICBT9574H73dR5pv94EqsozS5P2aNX8VyTk,5016 +django/db/migrations/operations/fields.py,sha256=UVNS04FHYbj8-e2AK7Rt--lnmZzr1OsHhNsW8yHNVsg,15452 +django/db/migrations/operations/models.py,sha256=ugrht02R5f4DUvXYVKARgt1hfWQNFgl_kU47LtHhgHI,34907 +django/db/migrations/operations/special.py,sha256=6vO2RRgaUPnxEjbkTX3QwAN-LaadZFHYpFHouAaMmig,7792 +django/db/migrations/operations/utils.py,sha256=Lxt1i442JmLmvhEY0EEPaO2UgU7mQfrYSpynC9XiKeM,3816 +django/db/migrations/optimizer.py,sha256=9taqZs5iJLXngtpgpN_DLOT8h61bimFGaP46yKjL_9o,3251 +django/db/migrations/questioner.py,sha256=Dvvktl3jWqmQMVRrTp7dNDBEm6an5L5nQFr25RSpMuE,9911 +django/db/migrations/recorder.py,sha256=ZOWNP5bCjsV9QpL54q0jhiKhdy2OfERB5-MWEMRrmkE,3457 +django/db/migrations/serializer.py,sha256=MiQ_RxXGbSpeltZ9V0PKYdE22muP8iS_LCm3SikL3yQ,12935 +django/db/migrations/state.py,sha256=mkXs4ta0quldv0dCu03LopRZOdZl7ptrRYd46oxMujU,25195 +django/db/migrations/utils.py,sha256=ApIIVhNrnnZ79yzrbPeREFsk5kxLCuOd1rwh3dDaNLI,388 +django/db/migrations/writer.py,sha256=6QsSQ6jOSPBjMduPWEsLzibi4_Cr3Rd8wY7TdCWiNRU,11293 +django/db/models/__init__.py,sha256=7WtGjLKaxGsQomDTe1AOpm0qJkteGoDW163y5uc8SwU,2522 django/db/models/__pycache__/__init__.cpython-39.pyc,, django/db/models/__pycache__/aggregates.cpython-39.pyc,, django/db/models/__pycache__/base.cpython-39.pyc,, @@ -3998,14 +3966,14 @@ django/db/models/__pycache__/query.cpython-39.pyc,, django/db/models/__pycache__/query_utils.cpython-39.pyc,, django/db/models/__pycache__/signals.cpython-39.pyc,, django/db/models/__pycache__/utils.cpython-39.pyc,, -django/db/models/aggregates.py,sha256=-9URmsgm6NcNuQ6exTm6LpuN7c_5pPKZQZu27S3_wn8,6941 -django/db/models/base.py,sha256=aOzl5y4Wjwc5No9XRIhNypQOKEbr1BzIMn2_8Dce8SI,94597 -django/db/models/constants.py,sha256=WtejyxsiW2HplCEPgmQt1CuMYty6-43akCksKfr5nIk,117 -django/db/models/constraints.py,sha256=L3G68J_56wzMTZl--68yOLmSSgIdty76bLLIjyPOo8w,10322 -django/db/models/deletion.py,sha256=5VBdEN8zw7vC-balbAxsYE5kZVgM3l5QYrbVmDEXNgg,20475 -django/db/models/enums.py,sha256=Erf-SMu9CD1aZfq4xct3WdoOjjMIZp_vlja6FyJQfyw,2804 -django/db/models/expressions.py,sha256=vE1YOsCniSazX7g9cI69oSgxAIzJjqjOqPxS3GhIKHg,54979 -django/db/models/fields/__init__.py,sha256=FlWwwqaeWMeZK1FkUv1EQOA57poE88UHE7I8fankrdo,93131 +django/db/models/aggregates.py,sha256=c6JnF1FfI1-h90zX0-Ts2lpDwsl5raNoOC9SF_LfvkE,5933 +django/db/models/base.py,sha256=8k3Q4ggSKtVVSJyJpGQcJlFn3whq7AvFzBIfNfQKQP4,89461 +django/db/models/constants.py,sha256=BstFLrG_rKBHL-IZ7iqXY9uSKLL6IOKOjheXBetCan0,117 +django/db/models/constraints.py,sha256=uAEiBQgc3Iyjl4pEE7NT3NKChb9zM3oUqcTb4QutX28,7796 +django/db/models/deletion.py,sha256=q09Z-ZbLWQ-u749ns6-StTr37bOzRWIsiAm_IILY418,19750 +django/db/models/enums.py,sha256=qfJQ6IXCU72CMizCDOj5vH_-f44TwESP750Nc3m7cuY,2756 +django/db/models/expressions.py,sha256=sPdWEXvn_2hB-Q5NU8S6UMJJZN0ARKqQ5qqRXLZ1EbE,52085 +django/db/models/fields/__init__.py,sha256=BBJrciB7s49ICXczAEBVRJJSs0gAbQ0PI9arSHLmcjg,92398 django/db/models/fields/__pycache__/__init__.cpython-39.pyc,, django/db/models/fields/__pycache__/files.cpython-39.pyc,, django/db/models/fields/__pycache__/json.cpython-39.pyc,, @@ -4015,15 +3983,15 @@ django/db/models/fields/__pycache__/related.cpython-39.pyc,, django/db/models/fields/__pycache__/related_descriptors.cpython-39.pyc,, django/db/models/fields/__pycache__/related_lookups.cpython-39.pyc,, django/db/models/fields/__pycache__/reverse_related.cpython-39.pyc,, -django/db/models/fields/files.py,sha256=gNA62GW-O4X3gtAZBLtl97IwyhdMCmlVhAv0ON9s9qc,18764 -django/db/models/fields/json.py,sha256=ORKD8GVQw9TPrKu4o8VuhlTpuH61EyyYFiYxm_1ZI9w,19542 -django/db/models/fields/mixins.py,sha256=AfnqL5l3yXQmYh9sW35MPFy9AvKjA7SarXijXfd68J8,1823 -django/db/models/fields/proxy.py,sha256=eFHyl4gRTqocjgd6nID9UlQuOIppBA57Vcr71UReTAs,515 -django/db/models/fields/related.py,sha256=4JrPOlyNVUN59sxdDwpM__ibA5QoIEg81A8oQrN3JRw,74943 -django/db/models/fields/related_descriptors.py,sha256=N1BclSaQTSmpX9szmvw2hHTrUIDJ4lkpLTfMUA3Pk1o,56866 -django/db/models/fields/related_lookups.py,sha256=Wgsc93Een1791coJ1lRQKL0KjtGv_VuOpSMltP9PNSU,7682 -django/db/models/fields/reverse_related.py,sha256=dUjBABohT76zLs0iXRZmqopD8I1g6I0DNyYLivwdHe8,11571 -django/db/models/functions/__init__.py,sha256=aglCm_JtzDYk2KmxubDN_78CGG3JCfRWnfJ74Oj5YJ4,2658 +django/db/models/fields/files.py,sha256=K64EHJ-wol3CVBd-Mvo5dkIi5P9q41PK7dHK1fBhFcY,18417 +django/db/models/fields/json.py,sha256=YIt9B7RGLDsn_Fa02liFxKOai2p2XF_OO70o-d5e4lQ,19459 +django/db/models/fields/mixins.py,sha256=9KF0Yg0MpeSHYJFu0D4kSOq_hye0TxnofdfaOmG_NsY,1801 +django/db/models/fields/proxy.py,sha256=fcJ2d1ZiY0sEouSq9SV7W1fm5eE3C_nMGky3Ma347dk,515 +django/db/models/fields/related.py,sha256=Qf1lGG317UEb5r1P8vrsjTGP0t9aiKIMsK6ETqG5Teg,70194 +django/db/models/fields/related_descriptors.py,sha256=yAupS1SqCYf90aykRRscEmYUjStI2DzrXDPsgEjUeNc,54061 +django/db/models/fields/related_lookups.py,sha256=aVkqKHxLqFpt5toGqyMUM-zUSuHxNc5e5B4dXUFIAjs,7073 +django/db/models/fields/reverse_related.py,sha256=JxTeGIPuxShD7lzW56Ou3-M-WG7O4a2izcBxkt2aWDI,11195 +django/db/models/functions/__init__.py,sha256=fve5gEF4bL_4n_ywzrrZ33qGRTg_whrzzLQ5RLx_58o,2083 django/db/models/functions/__pycache__/__init__.cpython-39.pyc,, django/db/models/functions/__pycache__/comparison.cpython-39.pyc,, django/db/models/functions/__pycache__/datetime.cpython-39.pyc,, @@ -4031,20 +3999,20 @@ django/db/models/functions/__pycache__/math.cpython-39.pyc,, django/db/models/functions/__pycache__/mixins.cpython-39.pyc,, django/db/models/functions/__pycache__/text.cpython-39.pyc,, django/db/models/functions/__pycache__/window.cpython-39.pyc,, -django/db/models/functions/comparison.py,sha256=1PAS7MopA64-U1yEzk-pFpqsJF8kqCoiROuEeV_RRjA,8159 -django/db/models/functions/datetime.py,sha256=-T1NrXy0Jw4oLXBNHpdef06HNYEP1-jeDKer2A_E4ak,12920 -django/db/models/functions/math.py,sha256=1MMhlAzVYjIbC2kuuG0wzSQ21aXvgp7OlqMnfPG5y7Q,6104 -django/db/models/functions/mixins.py,sha256=04MuLCiXw4DYDx0kRU3g_QZcOOCbttAkFEa4WtwGeao,2229 -django/db/models/functions/text.py,sha256=nukaAx3ZAOPQoBFvZ4a3zVLkwqztLHI0ZSywuLOPth8,10605 -django/db/models/functions/window.py,sha256=g4fryay1tLQCpZRfmPQhrTiuib4RvPqtwFdodlLbi98,2841 -django/db/models/indexes.py,sha256=4kUVubVDGphZA2y5RVkudeOmnZvE4iAcEJT5zaKW-c8,11706 -django/db/models/lookups.py,sha256=cXjBvJl9RBNZdgWzIV-f-Qoeixm9vQ-X4suvoqXd2HA,24941 -django/db/models/manager.py,sha256=bTLdM0ed5kE7T5QUtLLx2lvhg_KFxBzD5I__Ryznzxo,6917 -django/db/models/options.py,sha256=8iSGfRP739wI5mIk8mERbXb0fPhmYVIPtqgcnNJSbUo,38097 -django/db/models/query.py,sha256=gWuS_K81GemQJj9R6_T2filYkMi7Cx6NDW-fFVS61tQ,88558 -django/db/models/query_utils.py,sha256=nVSdyCJULwzYS6LtZr0vOogb3Fjnbl2w_BJIs0vCJts,11690 -django/db/models/signals.py,sha256=mG6hxVWugr_m0ugTU2XAEMiqlu2FJ4CBuGa34dLJvEQ,1622 -django/db/models/sql/__init__.py,sha256=Syx0wsbb0hqxNffPOFITCgRMwZfSECpk9MgY7cS26Jk,229 +django/db/models/functions/comparison.py,sha256=mLUsq1_4cDe8h3tpWJxKZ9IvvqFSKn2kyyz8MSJuE1M,7311 +django/db/models/functions/datetime.py,sha256=44R-UkfpbVigU_r2xl63qNWP8Gljf7B9n8OFI6LtWHk,11827 +django/db/models/functions/math.py,sha256=mJDDUYXvBtiQ9WkraR5vv3ZdOHM1lOqc8bqPh7Plodc,5243 +django/db/models/functions/mixins.py,sha256=BB5sSl-lVaFI5LkxK1BvhRl-2Z5UPBIMLrDc3VHMRwk,2111 +django/db/models/functions/text.py,sha256=NZSWxzN7bcHffSGWXK38vhM7tOIZQTFiDwji9mjqDmY,10302 +django/db/models/functions/window.py,sha256=yL07Blhu1WudxHMbIfRA2bBIXuwZec8wLPbW6N5eOyc,2761 +django/db/models/indexes.py,sha256=bfkWnneFf2gdBvmTeDh876jm7IYSR8vcEAAV2UGz6qk,11281 +django/db/models/lookups.py,sha256=p_k3K-tzTcvlxSPEP6P8-g9C3gUeGzy5PxMuZ853atE,23101 +django/db/models/manager.py,sha256=IJ88ywyCPbepMmZRLdnIZ05E8aomx7RmA2XIvKPi7YA,6778 +django/db/models/options.py,sha256=I7tai20uoV7ssQ_jJVwWwRGjDKhAQqyc7kUwHHwUyfA,36791 +django/db/models/query.py,sha256=2lorBXqrgp2v1iqywVHEUloX3HMl2LTvbOWsns-emkU,84236 +django/db/models/query_utils.py,sha256=Qc9PydA1acqXUJiNn50royeYaTheiko4MS_pgHObYY0,12421 +django/db/models/signals.py,sha256=qCf59m4zcQX6wXrbNSxIQCvWaFhaKagb6IxEkdx_5VY,1573 +django/db/models/sql/__init__.py,sha256=iwBpPl3WxYM7qrQ1qKaFGG-loqKwU5OOJt0SVH0m3RE,229 django/db/models/sql/__pycache__/__init__.cpython-39.pyc,, django/db/models/sql/__pycache__/compiler.cpython-39.pyc,, django/db/models/sql/__pycache__/constants.cpython-39.pyc,, @@ -4052,19 +4020,19 @@ django/db/models/sql/__pycache__/datastructures.cpython-39.pyc,, django/db/models/sql/__pycache__/query.cpython-39.pyc,, django/db/models/sql/__pycache__/subqueries.cpython-39.pyc,, django/db/models/sql/__pycache__/where.cpython-39.pyc,, -django/db/models/sql/compiler.py,sha256=rw0SCRgdhsbl1Ptf0LqJND2BASvfs61k2PJOwBiJAC0,79564 -django/db/models/sql/constants.py,sha256=usb1LSh9WNGPsurWAGppDkV0wYJJg5GEegKibQdS718,533 -django/db/models/sql/datastructures.py,sha256=cg4EexjiRimHEzgAuvMm3QN9qqmklPu37Zf1ONc0jFQ,7180 -django/db/models/sql/query.py,sha256=Qb_i1uhWhI2sRRom_83Cxaw9JDffUlam9gjAfgO4Cfw,113999 -django/db/models/sql/subqueries.py,sha256=XvD-2xEKJFzCxWE53rmA-OI9IT-C9WjRSUJjoFZ6HbA,5792 -django/db/models/sql/where.py,sha256=_W2lYuyC1VjSAu93y_kHI62AMGylrWq2Ro2PqnLwGds,9542 -django/db/models/utils.py,sha256=wRPcaaZbElvqmPWU30paM_KIh-0ycMP7R1occhVgwoU,1612 -django/db/transaction.py,sha256=cBt1AO_kx_hEphb3qP-KV1UxD20octqFJWNFVuydmYM,12278 -django/db/utils.py,sha256=Qf28qzIodJUzr9fmElkRSFnvtoaRzcYCAonF8fKvQy4,9834 +django/db/models/sql/compiler.py,sha256=DBnM7sZHHhstUX6pIEQ0n9I-lDrVcupxQnYMc9ns3DU,75108 +django/db/models/sql/constants.py,sha256=wtO4kqA_ItJS51LrVhtwL-vbKzR8fgAEbnCBMDRIgYI,638 +django/db/models/sql/datastructures.py,sha256=a79Kx2yTbY5jbT-kZn48QybeXhOemDcpWuJO1eEfg64,6911 +django/db/models/sql/query.py,sha256=cWzcLOu1jDI3Or5_340TJ2IMDN2DMsSOiAAjbTRRXXw,111332 +django/db/models/sql/subqueries.py,sha256=DKda9DC1mIpsReUNdfV-9n3TczH9GCkYKVP17yjw8Is,5744 +django/db/models/sql/where.py,sha256=tEeYXit18WhCHiXuglA54G18dF4y4jtO8Yl8a3uUfOo,8746 +django/db/models/utils.py,sha256=vwhObJeHlfFp_nBU3L3cL2PsYIOXH7jOtNVqTf0uO-s,1612 +django/db/transaction.py,sha256=Khrj3g4N2_vq1WjukuV64r7C-VWHzxID4AN8KAZzkRY,12229 +django/db/utils.py,sha256=3sqS6C2Lg4k9uRiiMcB_Krnq9_yjEfE3rJMKzAfF2JE,9854 django/dispatch/__init__.py,sha256=qP203zNwjaolUFnXLNZHnuBn7HNzyw9_JkODECRKZbc,286 django/dispatch/__pycache__/__init__.cpython-39.pyc,, django/dispatch/__pycache__/dispatcher.cpython-39.pyc,, -django/dispatch/dispatcher.py,sha256=hMPMYVDCkQuUfY1D3XVyP2CqSQDhEHMgp25a-RytTMs,10793 +django/dispatch/dispatcher.py,sha256=V_aUtHcvUrIXPgKHXWShPTFFMGrmgVLJDUUx6fWplqM,11142 django/dispatch/license.txt,sha256=VABMS2BpZOvBY68W0EYHwW5Cj4p4oCb-y1P3DAn0qU8,1743 django/forms/__init__.py,sha256=S6ckOMmvUX-vVST6AC-M8BzsfVQwuEUAdHWabMN-OGI,368 django/forms/__pycache__/__init__.cpython-39.pyc,, @@ -4076,26 +4044,10 @@ django/forms/__pycache__/models.cpython-39.pyc,, django/forms/__pycache__/renderers.cpython-39.pyc,, django/forms/__pycache__/utils.cpython-39.pyc,, django/forms/__pycache__/widgets.cpython-39.pyc,, -django/forms/boundfield.py,sha256=IHMcz-omizXY32A_9Pc5w5L6kU8wwpGKTawujf5ew5w,11280 -django/forms/fields.py,sha256=WrV8UteJcY0yo8OoC86019lpLpEzk_7lAomp_wSYBp0,47957 -django/forms/forms.py,sha256=_rIT1KeXiyi7xaxhsSHIiNfaaQs7Fh9vtHWzu3ZNRds,20494 -django/forms/formsets.py,sha256=QtjOhG3M-0lNpUufPg_h5PAbXQ9wYke1saaMw6Ip3DQ,20624 -django/forms/jinja2/django/forms/attrs.html,sha256=TD0lNK-ohDjb_bWg1Kosdn4kU01B_M0_C19dp9kYJqo,165 -django/forms/jinja2/django/forms/default.html,sha256=stPE5cj2dGb6pxqKLtgDHPr14Qr6pcr4i_s2lCZDFF8,40 -django/forms/jinja2/django/forms/errors/dict/default.html,sha256=1DLQf0Czjr5V4cghQOyJr3v34G2ClF0RAOc-H7GwXUE,49 -django/forms/jinja2/django/forms/errors/dict/text.txt,sha256=E7eqEWc6q2_kLyc9k926klRe2mPp4O2VqG-2_MliYaU,113 -django/forms/jinja2/django/forms/errors/dict/ul.html,sha256=65EYJOqDAn7-ca7FtjrcdbXygLE-RA_IJQTltO7qS1Q,137 -django/forms/jinja2/django/forms/errors/list/default.html,sha256=q41d4u6XcxDL06gRAVdU021kM_iFLIt5BuYa-HATOWE,49 -django/forms/jinja2/django/forms/errors/list/text.txt,sha256=VVbLrGMHcbs1hK9-2v2Y6SIoH9qRMtlKzM6qzLVAFyE,52 -django/forms/jinja2/django/forms/errors/list/ul.html,sha256=AwXfGxnos6llX44dhxMChz6Kk6VStAJiNzUpSLN8_y4,119 -django/forms/jinja2/django/forms/formsets/default.html,sha256=VS7142h_1WElYa58vKdd9vfQiwaRxrQLyatBAI22T3U,77 -django/forms/jinja2/django/forms/formsets/p.html,sha256=HzEX7XdSDt9owDkYJvBdFIETeU9RDbXc1e4R2YEt6ec,84 -django/forms/jinja2/django/forms/formsets/table.html,sha256=L9B4E8lR0roTr7dBoMiUlekuMbO-3y4_b4NHm6Oy_Vg,88 -django/forms/jinja2/django/forms/formsets/ul.html,sha256=ANvMWb6EeFAtLPDTr61IeI3-YHtAYZCT_zmm-_y-5Oc,85 -django/forms/jinja2/django/forms/label.html,sha256=t1MriYEm7v7uZ15Oxk2BgU5Rz5ZahHC0yHe7EPx9UU8,139 -django/forms/jinja2/django/forms/p.html,sha256=fQJWWpBV4WgggOA-KULIY6vIIPTHNVlkfj9yOngfOOY,673 -django/forms/jinja2/django/forms/table.html,sha256=B6EEQIJDDpc2SHC5qJzOZylzjmLVA1IWzOQWzzvRZA8,814 -django/forms/jinja2/django/forms/ul.html,sha256=U6aaYi-Wb66KcLhRGJ_GeGc5TQyeUK9LKLTw4a8utoE,712 +django/forms/boundfield.py,sha256=IRad_GjJb8UwQlqxr3o4fs17LmpHEQ7ds7g_2U1BkqY,10246 +django/forms/fields.py,sha256=jMvPsqvHSuWVzCU7n7c-TjY0PgMZN4jy_praBBNkFo8,46707 +django/forms/forms.py,sha256=yzNt1qEEWrm8Rn_jYFSbZl7BwX_Z9VxyGTD9zzs-POw,19804 +django/forms/formsets.py,sha256=L5SE0e6-_qEgdillfEDZ8M_8pdNYTvfCF0zT7wSGU7k,19864 django/forms/jinja2/django/forms/widgets/attrs.html,sha256=_J2P-AOpHFhIwaqCNcrJFxEY4s-KPdy0Wcq0KlarIG0,172 django/forms/jinja2/django/forms/widgets/checkbox.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/jinja2/django/forms/widgets/checkbox_option.html,sha256=U2dFtAXvOn_eK4ok0oO6BwKE-3-jozJboGah_PQFLVM,55 @@ -4109,7 +4061,7 @@ django/forms/jinja2/django/forms/widgets/hidden.html,sha256=fXpbxMzAdbv_avfWC546 django/forms/jinja2/django/forms/widgets/input.html,sha256=u12fZde-ugkEAAkPAtAfSxwGQmYBkXkssWohOUs-xoE,172 django/forms/jinja2/django/forms/widgets/input_option.html,sha256=PyRNn9lmE9Da0-RK37zW4yJZUSiJWgIPCU9ou5oUC28,219 django/forms/jinja2/django/forms/widgets/multiple_hidden.html,sha256=T54-n1ZeUlTd-svM3C4tLF42umKM0R5A7fdfsdthwkA,54 -django/forms/jinja2/django/forms/widgets/multiple_input.html,sha256=voM3dqu69R0Z202TmCgMFM6toJp7FgFPVvbWY9WKEAU,395 +django/forms/jinja2/django/forms/widgets/multiple_input.html,sha256=O9W9tLA_gdxNqN_No2Tesd8_2GhOTyKEkCOnp_rUBn4,431 django/forms/jinja2/django/forms/widgets/multiwidget.html,sha256=pr-MxRyucRxn_HvBGZvbc3JbFyrAolbroxvA4zmPz2Y,86 django/forms/jinja2/django/forms/widgets/number.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/jinja2/django/forms/widgets/password.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 @@ -4124,24 +4076,8 @@ django/forms/jinja2/django/forms/widgets/text.html,sha256=fXpbxMzAdbv_avfWC5464g django/forms/jinja2/django/forms/widgets/textarea.html,sha256=Av1Y-hpXUU2AjrhnUivgZFKNBLdwCSZSeuSmCqmCkDA,145 django/forms/jinja2/django/forms/widgets/time.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/jinja2/django/forms/widgets/url.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 -django/forms/models.py,sha256=eZfIkvhqJNqJlsdbkDGT44QMB8CVKl5qf7RWEoFYL_8,60082 -django/forms/renderers.py,sha256=k8f8m6JqcF25Fbw83kNPcJP_KJowhsVcexXPTXLcsOo,1950 -django/forms/templates/django/forms/attrs.html,sha256=UFPgCXXCAkbumxZE1NM-aJVE4VCe2RjCrHLNseibv3I,165 -django/forms/templates/django/forms/default.html,sha256=stPE5cj2dGb6pxqKLtgDHPr14Qr6pcr4i_s2lCZDFF8,40 -django/forms/templates/django/forms/errors/dict/default.html,sha256=tFtwfHlkOY_XaKjoUPsWshiSWT5olxm3kDElND-GQtQ,48 -django/forms/templates/django/forms/errors/dict/text.txt,sha256=E7eqEWc6q2_kLyc9k926klRe2mPp4O2VqG-2_MliYaU,113 -django/forms/templates/django/forms/errors/dict/ul.html,sha256=65EYJOqDAn7-ca7FtjrcdbXygLE-RA_IJQTltO7qS1Q,137 -django/forms/templates/django/forms/errors/list/default.html,sha256=Kmx1nwrzQ49MaP80Gd17GC5TQH4B7doWa3I3azXvoHA,48 -django/forms/templates/django/forms/errors/list/text.txt,sha256=VVbLrGMHcbs1hK9-2v2Y6SIoH9qRMtlKzM6qzLVAFyE,52 -django/forms/templates/django/forms/errors/list/ul.html,sha256=5kt2ckbr3esK0yoPzco2EB0WzS8MvGzau_rAcomB508,118 -django/forms/templates/django/forms/formsets/default.html,sha256=VS7142h_1WElYa58vKdd9vfQiwaRxrQLyatBAI22T3U,77 -django/forms/templates/django/forms/formsets/p.html,sha256=qkoHKem-gb3iqvTtROBcHNJqI-RoUwLHUvJC6EoHg-I,82 -django/forms/templates/django/forms/formsets/table.html,sha256=N0G9GETzJfV16wUesvdrNMDwc8Fhh6durrmkHUPeDZY,86 -django/forms/templates/django/forms/formsets/ul.html,sha256=bGQpjbpKwMahyiIP4-2p3zg3yJP-pN1A48yCqhHdw7o,83 -django/forms/templates/django/forms/label.html,sha256=Azlf6IUf8tPWRlmhpY4upnVjHYhmJTq3_adlhWxEBro,114 -django/forms/templates/django/forms/p.html,sha256=N3sx-PBlt3Trs6lfjE4oQa3owxhM3rqXTy-AQg9Hr44,684 -django/forms/templates/django/forms/table.html,sha256=zuLIyEOeNzV7aeIjIqIwM4XfZP_SlEc_OZ_x87rbOhY,825 -django/forms/templates/django/forms/ul.html,sha256=K8kCd5q4nD-_ChR47s3q5fkHd8BHrHAa830-5H8aXVI,723 +django/forms/models.py,sha256=KJvlZioaiTFigqwaUHQiyrAfTTQttjjVfbLKhYod3XU,58083 +django/forms/renderers.py,sha256=2jxMO8pIjEKJ_Putd_Y5fv1pElwMKP7O8Y65tLwZnxA,1917 django/forms/templates/django/forms/widgets/attrs.html,sha256=9ylIPv5EZg-rx2qPLgobRkw6Zq_WJSM8kt106PpSYa0,172 django/forms/templates/django/forms/widgets/checkbox.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/templates/django/forms/widgets/checkbox_option.html,sha256=U2dFtAXvOn_eK4ok0oO6BwKE-3-jozJboGah_PQFLVM,55 @@ -4155,7 +4091,7 @@ django/forms/templates/django/forms/widgets/hidden.html,sha256=fXpbxMzAdbv_avfWC django/forms/templates/django/forms/widgets/input.html,sha256=dwzzrLocGLZQIaGe-_X8k7z87jV6AFtn28LilnUnUH0,189 django/forms/templates/django/forms/widgets/input_option.html,sha256=PyRNn9lmE9Da0-RK37zW4yJZUSiJWgIPCU9ou5oUC28,219 django/forms/templates/django/forms/widgets/multiple_hidden.html,sha256=T54-n1ZeUlTd-svM3C4tLF42umKM0R5A7fdfsdthwkA,54 -django/forms/templates/django/forms/widgets/multiple_input.html,sha256=jxEWRqV32a73340eQ0uIn672Xz5jW9qm3V_srByLEd0,426 +django/forms/templates/django/forms/widgets/multiple_input.html,sha256=HwEaZLEiZYdPJ6brC9QWRGaIKzcX5UA2Tj5Rsq_NvOk,462 django/forms/templates/django/forms/widgets/multiwidget.html,sha256=slk4AgCdXnVmFvavhjVcsza0quTOP2LG50D8wna0dw0,117 django/forms/templates/django/forms/widgets/number.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/templates/django/forms/widgets/password.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 @@ -4170,18 +4106,18 @@ django/forms/templates/django/forms/widgets/text.html,sha256=fXpbxMzAdbv_avfWC54 django/forms/templates/django/forms/widgets/textarea.html,sha256=Av1Y-hpXUU2AjrhnUivgZFKNBLdwCSZSeuSmCqmCkDA,145 django/forms/templates/django/forms/widgets/time.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 django/forms/templates/django/forms/widgets/url.html,sha256=fXpbxMzAdbv_avfWC5464gD2jFng931Eq7vzbzy1-yA,48 -django/forms/utils.py,sha256=RHMTeA7rsJqAMx5D52uLtQ24rmzAWnhrP7VYa5xq30k,7178 -django/forms/widgets.py,sha256=NYfQ9xKzUMpNmQDlkhkZhVk-Dvk7-JyEPgpCIbs3-Ds,38406 -django/http/__init__.py,sha256=kaczUV9rEkyRGlun8wzsmdGwdtOoFS-1Rpy7OV-e8Bs,1118 +django/forms/utils.py,sha256=fhEwzt5GMyxJIKIF-WELl-jeIOOJygnAaI9VmD68iKg,5996 +django/forms/widgets.py,sha256=HUfspqbpHNV0rtJpt5Kxj2zFqnG91bfHS0-LKAc1M-4,37564 +django/http/__init__.py,sha256=5JImoB1BZNuZBOt5qyDX7t51McYbkDLX45eKmNN_Fes,1010 django/http/__pycache__/__init__.cpython-39.pyc,, django/http/__pycache__/cookie.cpython-39.pyc,, django/http/__pycache__/multipartparser.cpython-39.pyc,, django/http/__pycache__/request.cpython-39.pyc,, django/http/__pycache__/response.cpython-39.pyc,, -django/http/cookie.py,sha256=t7yGORGClUnCYVKQqyLBlEYsxQLLHn9crsMSWqK_Eic,679 -django/http/multipartparser.py,sha256=lwny_1GnNjyzeKlNduvIxMYLTktOgH0u8pcD_15x_zo,26910 -django/http/request.py,sha256=McMG8tMBx0pO5UAUYFTD4GZFj1RL4OhtnqhsvSzow4A,24724 -django/http/response.py,sha256=jgTJRv3WH9etrftemZGkkmZILaDJQxB4k2mCF-MRJ-c,21855 +django/http/cookie.py,sha256=Zpg6OEW9-dGvr5ByQhlHyGjLJzvNNrnGL1WzolnsM6U,818 +django/http/multipartparser.py,sha256=3JML9I4OYoJjkGvE0tfR5E9j3VWDkrDzHYo3Y_LWLaA,26029 +django/http/request.py,sha256=O_tKtMaDYqeGJnYZaxYXHRlgLstajzOUcbBpNkjLDF4,25416 +django/http/response.py,sha256=OT3u98hQT9AntyfHny0GfR47ngtLpV-M176ha4yQBrc,21227 django/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/middleware/__pycache__/__init__.cpython-39.pyc,, django/middleware/__pycache__/cache.cpython-39.pyc,, @@ -4192,16 +4128,16 @@ django/middleware/__pycache__/gzip.cpython-39.pyc,, django/middleware/__pycache__/http.cpython-39.pyc,, django/middleware/__pycache__/locale.cpython-39.pyc,, django/middleware/__pycache__/security.cpython-39.pyc,, -django/middleware/cache.py,sha256=CL52am97oatuGuOPOVb3MNwJGBxlcfC-d6GhjD36pP8,7941 -django/middleware/clickjacking.py,sha256=KlShjOPFYmouHDC8vwbxV0MuvhLZpECeWmYtEaAJX-U,1697 -django/middleware/common.py,sha256=9G8G40Jd6vNeSVQfu1bKO-y2Rd550Lsm8ohr_fDeAnU,7603 -django/middleware/csrf.py,sha256=iz58TxttDSBxllKrnWfEpjDWZTS2lU7UZwFoU0PP3no,18518 -django/middleware/gzip.py,sha256=P_gEykSWFCwAhcsm4bUB8bvv6oH7B3gl47O3ezOACsQ,2143 -django/middleware/http.py,sha256=RqXN9Kp6GEh8j_ub7YXRi6W2_CKZTZEyAPpFUzeNPBs,1616 -django/middleware/locale.py,sha256=CV8aerSUWmO6cJQ6IrD5BzT3YlOxYNIqFraCqr8DoY4,3442 -django/middleware/security.py,sha256=AjcJ338onziA0HPXsM5WUnIrQkmyW8mpwU0KObaPPUI,2623 -django/shortcuts.py,sha256=UniuxOq4cpBYCN-spLkUCFEYmA2SSXsozeS6xM2Lx8w,5009 -django/template/__init__.py,sha256=-hvAhcRO8ydLdjTJJFr6LYoBVCsJq561ebRqE9kYBJs,1845 +django/middleware/cache.py,sha256=R53yhubcKlNhcS2ujCnqmXcwHm00ShMNAz_qLRIOh2k,8227 +django/middleware/clickjacking.py,sha256=4o-qDAZJTe8HFV_UjHCPkRiWJ8bBJwnDvzHEp28RQoQ,1696 +django/middleware/common.py,sha256=zFTZRKnxLHCBX0Qev90flckgCMNJ86NIeNTucx4bbTM,7457 +django/middleware/csrf.py,sha256=KJgUcXSlb29bjlNF7nX_iOSTFtueSH5BsxXRyd-lw6c,13796 +django/middleware/gzip.py,sha256=Gq1DVIBjW5W3nxn-DA2spZI3BMYUusOUp-EM0q-_lys,2142 +django/middleware/http.py,sha256=JiRGXvtfmXxYTomy7gde5pcG45GX7R0qpXiI5Fk06dE,1624 +django/middleware/locale.py,sha256=60giIJJ_Xnv0LKmO25QUqSfLtIYr9CvDP6hbuE0PVXA,3014 +django/middleware/security.py,sha256=lkL91YPA9TNqDDYjUOxfaW4EEdp6jpDo9sbKz1NLzcc,2524 +django/shortcuts.py,sha256=3sywVfNhusraJcqmqQk-aooxYaP0YINJv-1b0spaDhM,4880 +django/template/__init__.py,sha256=SjShaTKH9sPyLdd7m8FzksxANvGYhtr7XDJZyWgWe9o,2010 django/template/__pycache__/__init__.cpython-39.pyc,, django/template/__pycache__/autoreload.cpython-39.pyc,, django/template/__pycache__/base.cpython-39.pyc,, @@ -4217,7 +4153,7 @@ django/template/__pycache__/loader_tags.cpython-39.pyc,, django/template/__pycache__/response.cpython-39.pyc,, django/template/__pycache__/smartif.cpython-39.pyc,, django/template/__pycache__/utils.cpython-39.pyc,, -django/template/autoreload.py,sha256=SqSite-APbLWmnaYkvz2btuQSQeIlZkyKadjWniYl5U,1756 +django/template/autoreload.py,sha256=X5G6vil-nhJly0vBEFFKfW6gpI_MQRlqu0MO6EbipuM,1744 django/template/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/template/backends/__pycache__/__init__.cpython-39.pyc,, django/template/backends/__pycache__/base.cpython-39.pyc,, @@ -4225,21 +4161,21 @@ django/template/backends/__pycache__/django.cpython-39.pyc,, django/template/backends/__pycache__/dummy.cpython-39.pyc,, django/template/backends/__pycache__/jinja2.cpython-39.pyc,, django/template/backends/__pycache__/utils.cpython-39.pyc,, -django/template/backends/base.py,sha256=leXk6e3XTeD8YYZ4jzGYP9bqKs2HdZupz3C1b-XcImE,2752 -django/template/backends/django.py,sha256=CXaMq1tocKHGFVNnMMzvDYnzmH78NhuublsS1sX2NuE,4198 -django/template/backends/dummy.py,sha256=XAtI1wmVkGJDfVOz-ENr4voPWrfY3h4sW0CjMqoiCK4,1752 -django/template/backends/jinja2.py,sha256=60KCBBNAF5scVuxpU-saKqE8Y7--VgkNnOkcPg3r1Nk,4083 -django/template/backends/utils.py,sha256=z5X_lxKa9qL4KFDVeai-FmsewU3KLgVHO8y-gHLiVts,424 -django/template/base.py,sha256=ddzWYrFyKMhrB_QEd1yeEofaNgk8kbUbJIO2NgYuU7k,40007 -django/template/context.py,sha256=67y6QyhjnwxKx37h4vORKBSNao1tYAf95LhXszZ4O10,9004 -django/template/context_processors.py,sha256=PMIuGUE1iljf5L8oXggIdvvFOhCLJpASdwd39BMdjBE,2480 -django/template/defaultfilters.py,sha256=EYeb9eTizxhERrdpMEeF3iqBVphtvSpxInZBYyk2Mk0,27924 -django/template/defaulttags.py,sha256=mgLeENndoSN-AQ-u5t-rC91C0fV_biVgU7Dm0OPyc00,48461 -django/template/engine.py,sha256=BrFAgOZc5ki4i94DkY_1S7PRNhmy4d5_Mf71VhqfPVw,7765 -django/template/exceptions.py,sha256=rqG3_qZq31tUHbmtZD-MIu0StChqwaFejFFpR4u7th4,1342 -django/template/library.py,sha256=3tel6vxobLwvJPXfdSv_rnm_WVSYsaQaaQel5HoIKgM,13596 -django/template/loader.py,sha256=PVFUUtC5WgiRVVTilhQ6NFZnvjly6sP9s7anFmMoKdo,2054 -django/template/loader_tags.py,sha256=EDqLQHmeo29shyupM8mzKMLwEqHOr1q2Kmf1Fbq3yUw,13153 +django/template/backends/base.py,sha256=P8dvOmQppJ8YMZ5_XyOJGDzspbQMNGV82GxL5IwrMFM,2751 +django/template/backends/django.py,sha256=4RaYN7vAbNbPUY8G3Fkk0oR1z41NmIBzJQXZ_GKamc8,4179 +django/template/backends/dummy.py,sha256=GRerKCIHVU0LjcioT9CmY8NaP0yIeQA4Wrv6lxdY9NM,1720 +django/template/backends/jinja2.py,sha256=nJBIoZ3nb3wq_5zSab9BlXnTyYdUF39fAERaAmaOpok,4075 +django/template/backends/utils.py,sha256=NORFWk_tz1IsX6WNZjP7Iz5Az6X8pUP0dmBfNC4vodk,418 +django/template/base.py,sha256=AcpakRI_AOiVAN7c4rwauxXa2ujf_q8tMSqwEFJegMA,38288 +django/template/context.py,sha256=4Zgmka6B7nfNsoIXD6O-f6FlnCH2IyCQxxXI8qesORU,8940 +django/template/context_processors.py,sha256=l7ZmqrfkR2KY-52TXWQHg-QkfeeoYLlCjL8mZvTrxgs,2408 +django/template/defaultfilters.py,sha256=QifvFRXjWE8ZCapZMpEfNyJ-ujUP5XD5y_1paDE_Dfk,26556 +django/template/defaulttags.py,sha256=sDOiBrALTzbhP7Zw-90WH75vz92atqiu2jZ-GqRA08I,50026 +django/template/engine.py,sha256=HPV4TrvBvq_--wmnJzKhnnUYTj1pR1-ASRgdmxIhOeU,6882 +django/template/exceptions.py,sha256=awd7B80xhFB574Lt2IdIyHCpD6KGGyuKGkIoalr9deo,1340 +django/template/library.py,sha256=ehca-hPsWo00yH07zINB6lA7IeEoZZ9ncoMzUpci9uU,12826 +django/template/loader.py,sha256=-t5cTnWJrxtS2vyg9cguz4rXxlTBni4XoJUuqJNglPI,2054 +django/template/loader_tags.py,sha256=mRr9dm2HkTn9ar3wXAQKUIvFLMA4Rg6zTzcPezU3fKM,12797 django/template/loaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/template/loaders/__pycache__/__init__.cpython-39.pyc,, django/template/loaders/__pycache__/app_directories.cpython-39.pyc,, @@ -4247,14 +4183,14 @@ django/template/loaders/__pycache__/base.cpython-39.pyc,, django/template/loaders/__pycache__/cached.cpython-39.pyc,, django/template/loaders/__pycache__/filesystem.cpython-39.pyc,, django/template/loaders/__pycache__/locmem.cpython-39.pyc,, -django/template/loaders/app_directories.py,sha256=sQpVXKYpnKr9Rl1YStNca-bGIQHcOkSnmm1l2qRGFVE,312 -django/template/loaders/base.py,sha256=Y5V4g0ly9GuNe7BQxaJSMENJnvxzXJm7XhSTxzfFM0s,1636 -django/template/loaders/cached.py,sha256=bDwkWYPgbvprU_u9f9w9oNYpSW_j9b7so_mlKzp9-N4,3716 -django/template/loaders/filesystem.py,sha256=f4silD7WWhv3K9QySMgW7dlGGNwwYAcHCMSTFpwiiXY,1506 -django/template/loaders/locmem.py,sha256=t9p0GYF2VHf4XG6Gggp0KBmHkdIuSKuLdiVXMVb2iHs,672 -django/template/response.py,sha256=UAU-aM7mn6cbGOIJuurn4EE5ITdcAqSFgKD5RXFms4w,5584 -django/template/smartif.py,sha256=eTzcnzPBdbkoiP8j9q_sa_47SoLLMqYdLKC3z0TbjpA,6407 -django/template/utils.py,sha256=C2fRN04KCQR-ZnsvqIouC3_jVjMRncJ-Bg_OS3jeq8I,3628 +django/template/loaders/app_directories.py,sha256=w3a84EAXWX12w7F1CyxIQ_lFiTwxFS7xf3rCEcnUqyc,313 +django/template/loaders/base.py,sha256=UcziL0Vj0jUQl-_vDi3uJUBClJI4lcsmDcZhcv1PlLI,1577 +django/template/loaders/cached.py,sha256=KVJFU9LbTqOp96Yz9CejD9wP-LMtbDU9pMPxVABbeL4,3655 +django/template/loaders/filesystem.py,sha256=OWTnIwWbVj-Td5VrOkKw1G_6pIuz1Vnh5CedZN5glyU,1507 +django/template/loaders/locmem.py,sha256=8cBYI8wPOOnIx_3v7fC5jezA_6pJLqgqObeLwHXQJKo,673 +django/template/response.py,sha256=6InrgDfplDjejgV1NyNsTYPnEKDwHKkm6PqQUxGDtuE,5461 +django/template/smartif.py,sha256=QBvsTtD4YiyGoU4hXrW8vqR0CBAFOZGuDoRP3aGEgOs,6408 +django/template/utils.py,sha256=7bjK3PEM-yEu6LbMVsAh3VQqXEguYBDJSRIPWBII52c,3560 django/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/templatetags/__pycache__/__init__.cpython-39.pyc,, django/templatetags/__pycache__/cache.cpython-39.pyc,, @@ -4262,12 +4198,12 @@ django/templatetags/__pycache__/i18n.cpython-39.pyc,, django/templatetags/__pycache__/l10n.cpython-39.pyc,, django/templatetags/__pycache__/static.cpython-39.pyc,, django/templatetags/__pycache__/tz.cpython-39.pyc,, -django/templatetags/cache.py,sha256=OpiR0FQBsJC9p73aEcXQQamSySR2hwIx2wEiuD925pg,3545 -django/templatetags/i18n.py,sha256=HADC6IaSWefGG-aGN7RSX67tQZ8xP77Wfh9f1KrjI48,19896 -django/templatetags/l10n.py,sha256=F6pnC2_7xNCKfNi0mcfzYQY8pzrQ9enK7_6-ZWzRu3A,1723 -django/templatetags/static.py,sha256=W4Rqt3DN_YtXe6EoqO-GLy7WR7xd7z0JsoX-VT0vvjc,4730 -django/templatetags/tz.py,sha256=O2Tk50xdMHEfmwbm_99LKpdTVGVYK23gA6CmBW2Hpuw,6015 -django/test/__init__.py,sha256=X12C98lKN5JW1-wms7B6OaMTo-Li90waQpjfJE1V3AE,834 +django/templatetags/cache.py,sha256=otY3c4Ti9YLxFfOuIX5TZ7w12aGDPkyGfQNsaPVZ_M0,3401 +django/templatetags/i18n.py,sha256=l4gH7h7WJHfqbzw_iM45DE5IZPTn_wPuse3IYli6DsY,19355 +django/templatetags/l10n.py,sha256=I6jRSBLvL34H-_rwGuHfU22VBhO2IHNRue78KWb8pTc,1723 +django/templatetags/static.py,sha256=om3cu4NVaH4MVUq-XPLxPVNlLUCxTbbp0qAVVSaClj4,4502 +django/templatetags/tz.py,sha256=HFzJsvh-x9yjoju4kiIpKAI0U_4crtoftqiT8llM_u8,5400 +django/test/__init__.py,sha256=QtKYTxK0z6qQQk1M4q_QQ1jztJce7Gfs_bPdNWHhl68,767 django/test/__pycache__/__init__.cpython-39.pyc,, django/test/__pycache__/client.cpython-39.pyc,, django/test/__pycache__/html.cpython-39.pyc,, @@ -4276,14 +4212,14 @@ django/test/__pycache__/selenium.cpython-39.pyc,, django/test/__pycache__/signals.cpython-39.pyc,, django/test/__pycache__/testcases.cpython-39.pyc,, django/test/__pycache__/utils.cpython-39.pyc,, -django/test/client.py,sha256=TzCGZ49OuFxPbG6Xsb9EXhdE67ENLo-rlYMcqaRpJd4,39134 -django/test/html.py,sha256=K8vgdoHMGpy5XRx6R7CAMR3uyfIcA_jpge8QNSPxD30,9000 -django/test/runner.py,sha256=640BlWuMHffdb5LBuZuot9PoKdzI6wgtLsQSlMKh5ug,40223 -django/test/selenium.py,sha256=0JPzph8lyk1i9taDCgsOvLhkxSh-jR-gvM4pPhdTGzc,5129 -django/test/signals.py,sha256=uILgP-mc9okVF9U_-uh0I1qOX2p2FIjUV2wPE4ihrPU,6878 -django/test/testcases.py,sha256=xOZVDZpIRVO9yEONvp6KehXWqUWYfdoPJG1DoACzcC8,68706 -django/test/utils.py,sha256=DEzdS2fKRpEvW5TimfTzwTQObIlqdinNQ9h69UIb8k0,32871 -django/urls/__init__.py,sha256=BHyBIOD3E4_3Ng27SpXnRmqO3IzUqvBLCE4TTfs4wNs,1079 +django/test/client.py,sha256=DFS8cvLRb3hoH-V1EBraykKIiUK9UQGa5seJ-Xfin1M,37255 +django/test/html.py,sha256=St7XLT5y8yHvfQb84_NmNGXzxiqQ-oS6akHOajdjzvM,8524 +django/test/runner.py,sha256=zUccKa23D_iUR5uIQsWnKElSetJnpO_z0ZfdzPKke1w,30148 +django/test/selenium.py,sha256=MN1zXbgesil9CIJ1JmjEJyEXxg8IJVgcbiTcm20BMDg,5129 +django/test/signals.py,sha256=MQSaRkR2YWWQIaZHo1Yq1ZU0Ik_tSuneqPf5Z0SJl_w,6807 +django/test/testcases.py,sha256=0IKRXXSe7kc4hii1DCaGqgK0FJebtcuXgo4FUas4YrY,64617 +django/test/utils.py,sha256=zo984qWWIbcKR99_voh_sFfobgpT2La9ctdfDRI6WQw,30706 +django/urls/__init__.py,sha256=FdHfNv5NwWEIt1EqEpRY7xJ-i4tD-SCLj0tq3qT6X1E,959 django/urls/__pycache__/__init__.cpython-39.pyc,, django/urls/__pycache__/base.cpython-39.pyc,, django/urls/__pycache__/conf.cpython-39.pyc,, @@ -4291,12 +4227,12 @@ django/urls/__pycache__/converters.cpython-39.pyc,, django/urls/__pycache__/exceptions.cpython-39.pyc,, django/urls/__pycache__/resolvers.cpython-39.pyc,, django/urls/__pycache__/utils.cpython-39.pyc,, -django/urls/base.py,sha256=0YT_x7hYBKkYV_1RXetdP6gFQKxQci5_bLHoowWPKQA,5703 -django/urls/conf.py,sha256=UkI6NQ5c6t4I_93g9ufAUN5dt5hPhwBTvQtKJKtJbN4,3246 -django/urls/converters.py,sha256=fVO-I8vTHL0H25GyElAYQWwSZtPMMNa9mJ1W-ZQrHyg,1216 +django/urls/base.py,sha256=sfqdc9ycaHNQSrJwtGRuuR5c0gL58Kn___I0LiGD2Ks,5587 +django/urls/conf.py,sha256=8Xug9NhJXDEysRXWrY2iHf0snfJMUmQkYZAomPltWMY,2946 +django/urls/converters.py,sha256=_eluhZBczkfMwCZJEQtM7s7KJQYbwoO4lygFQvtWSHA,1216 django/urls/exceptions.py,sha256=alLNjkORtAxneC00g4qnRpG5wouOHvJvGbymdpKtG_I,115 -django/urls/resolvers.py,sha256=MpdCwUkGHh964PHewgxwmFCyn7a0CocIygv44j-Xf6k,30684 -django/urls/utils.py,sha256=MSSGo9sAlnsDG3fDt2zayhXwYMCL4qtBzVjQv8BwemA,2197 +django/urls/resolvers.py,sha256=7_qCuQerfppRHVQscmykGp_g3XXWtSyoC8Cmi_j86Lo,27894 +django/urls/utils.py,sha256=VHDcmggNRHSbPJAql5KJhe7wX4pSjrKb64Fu-p14D9Q,2152 django/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/utils/__pycache__/__init__.cpython-39.pyc,, django/utils/__pycache__/_os.cpython-39.pyc,, @@ -4341,67 +4277,67 @@ django/utils/__pycache__/topological_sort.cpython-39.pyc,, django/utils/__pycache__/tree.cpython-39.pyc,, django/utils/__pycache__/version.cpython-39.pyc,, django/utils/__pycache__/xmlutils.cpython-39.pyc,, -django/utils/_os.py,sha256=-_6vh_w0-c2wMUXveE45hj-QHf2HCq5KuWGUkX4_FvI,2310 -django/utils/archive.py,sha256=JExZfmiqSixQ_ujY7UM6sNShVpO5CsF-0hH2qyt44Eo,8086 -django/utils/asyncio.py,sha256=sHogB-77wjWFfKEvkpsPSoC2rb4UnhrQDS1d8GZhUOA,1143 -django/utils/autoreload.py,sha256=sZCFfpy_O8MhLxzNwauntvpmpIR5djovU3vG_aN9mqU,24847 -django/utils/baseconv.py,sha256=mnIn3_P2jqb8ytiFOiaCjrTFFujeNFT0EkympSmt7Ck,3268 -django/utils/cache.py,sha256=A1kU_HT7K7mfTQQbEVcJOiVZKBgy2553LnGok4-Kyrg,16459 -django/utils/connection.py,sha256=XYivlt9CvkX8mQTp68AAfUZoS8gyGEWNj2fx_C9AyYk,2239 -django/utils/crypto.py,sha256=29fvZcWMlx7WwmIAwjf0NB1LaOpUbQQ1BzN-86AEHvk,2661 -django/utils/datastructures.py,sha256=d1l_t4LRjgqR388sgJTZbIhb2-u4kEn4hRdQKoKlFT0,10072 -django/utils/dateformat.py,sha256=AeS8k0SuLOFAT-tGpvhl4P6XF0bsA7bRnGPGBU3Qkj4,10257 -django/utils/dateparse.py,sha256=aWow4S2Owf_Hb4b3ldcoBdhZxaiZGQNKeJHTYu7IoSc,5531 -django/utils/dates.py,sha256=zHUHeOkxuo53rTvHG3dWMLRfVyfaMLBIt5xmA4E_Ids,2179 -django/utils/datetime_safe.py,sha256=KG5hS-S4NCeZ0IAwLd30gvsOe28rHgN0TbXEe9t3jVw,3106 -django/utils/deconstruct.py,sha256=EgXqmTxSn2pzvuJY08zJhlgRkP7bviGT009qBCuyjHk,1996 -django/utils/decorators.py,sha256=xa6p2egupMJYpcXVFrteTAHT9DiatGm-zGFoQ2nxUOs,6939 -django/utils/deprecation.py,sha256=F04-P92PTYhPiSUhsXDnkRsiA4XwVuwer0Cu95I6VP8,5197 -django/utils/duration.py,sha256=8JFvrrlyMGUUcVqN-7_TR_wi388rZYWsBoqeZZtpUbQ,1248 -django/utils/encoding.py,sha256=IFRvnB0-g9mF1H-AS72_LFIZgCHlo4yl11ZDoBTA_gU,8794 -django/utils/feedgenerator.py,sha256=eRqRqtbvrP4P2ACaOnn54IjV6LTvB5Sl1dTNyffyX_Q,15655 -django/utils/formats.py,sha256=SAXsRmNUy4Vcvu3n8QS-q2zlGYzKRvb0TyFiUt7nWIY,10589 -django/utils/functional.py,sha256=OZZDiFloi40-raEZF-Xa-KrI-UiTkqXPwGfRJyPwru4,14267 -django/utils/hashable.py,sha256=kFbHnVOA4g-rTFI_1oHeNGA0ZEzAlY0vOeGTAeqxz7E,740 -django/utils/html.py,sha256=GYbe55OufSL4sPOajYbQoq7RWwvgq3qs-7qVUZlZMlY,13271 -django/utils/http.py,sha256=iuuM_REZzPzLVcn7z5lJ9zrccAxWrlnwYPkK6F0UxiI,12775 -django/utils/inspect.py,sha256=lhDEOtmSLEub5Jj__MIgW3AyWOEVkaA6doJKKwBhZ6A,2235 -django/utils/ipv6.py,sha256=laDOQe_r4W-oVKLOgQ4aAJxs37n8P3LkH-eeIchJqh4,1333 +django/utils/_os.py,sha256=_C_v7KbojT-CD3fn2yJGFbjCbV5HkJr3MBqZrjjxK-s,2295 +django/utils/archive.py,sha256=PnKzyQtuG_m7DXeyaE_m34vH5hzO2uPWm6APos2gP5A,7855 +django/utils/asyncio.py,sha256=sFRUKbrTnXo5uGRNI9RHOZ1bb0dFFOge5UzT7qwGyQ8,1165 +django/utils/autoreload.py,sha256=ZZ1kJEMlnbwU_d_PmwPGEjjFpl8y1hE4WxRBiHo8qMo,24245 +django/utils/baseconv.py,sha256=xYReIqcF2FFD85BqDrl48xo4UijII9D6YyC-FHsUPbw,2989 +django/utils/cache.py,sha256=K72VksIJHEGP3uPsWNJW1HVp764QAwiPDjzqEqGQQ_M,16256 +django/utils/connection.py,sha256=HlxWNMBDM26anSkDHpOkbkiX1qWimrNdi23SypAb4TY,2239 +django/utils/crypto.py,sha256=J1XbnBi-bheoxToEZbSvkEHJoiuD3hsxq88bD2uG12w,3139 +django/utils/datastructures.py,sha256=92cjzQ_w9PhTKXA6OgwOYUnmIpX5vqforHo0JiL2EsA,9891 +django/utils/dateformat.py,sha256=ntU_c6plSP2k8g6uCoDd0wIuVdMIIcfW84TORbhKWY0,10205 +django/utils/dateparse.py,sha256=DeWXc3cZXanT2yuEQlbhG82mheIGEborN1IQpKhxBiI,4894 +django/utils/dates.py,sha256=hl7plurNHC7tj_9Olb7H7-LCtOhOV71oWg-xx5PBFh4,2021 +django/utils/datetime_safe.py,sha256=JsosYYXcRNqnHSCC2VajcW5tC4KnkxUb5gOYmDURRkY,2854 +django/utils/deconstruct.py,sha256=hcO_7qassSI5dTfQ5CPttA8s3f9yaF8UnqKKma3bI6M,1975 +django/utils/decorators.py,sha256=P3Is7I_Xe_evMKH5ho_ssHynuFmTzB7uTysnJwW-XnI,6834 +django/utils/deprecation.py,sha256=OJg5lZbyZk4xzJi8mgnmLlLBLC1Qsye8fB3RJYQ-MAM,5198 +django/utils/duration.py,sha256=VtDUAQKIPFuv6XkwG6gIjLQYtcs8vgGMcS4OQpSFx-E,1234 +django/utils/encoding.py,sha256=g41xTq1TPKSstCG0sD47-GfPLyiB18w8xCF7danfrcI,9336 +django/utils/feedgenerator.py,sha256=rI74OiJ8cWgt9AhA0RnYdKTVi7IXUM6FCLpFUQjDRmc,15109 +django/utils/formats.py,sha256=sORcm7Pr_hBG4kfZYC8Dp7pCP2o-CSjpBKJmluWRjbU,9033 +django/utils/functional.py,sha256=Dkqp_2JZ54pdU-pNG6VM5IWdfW8ZgUzHj28iw7dPWCI,14146 +django/utils/hashable.py,sha256=O8ypHtccXZKg1T7XX8u_H8iTFXnKBKnKmf46OBcbf9k,706 +django/utils/html.py,sha256=9bDmR5GPXrTUGIJjO8pCm2vZrOuB4U37Y6zAOeBl2Is,13151 +django/utils/http.py,sha256=vHOsKd-jfMDawqCxzmn7LCrPo8lsGIQpMmrIFYCTpJI,17700 +django/utils/inspect.py,sha256=O5KdAnq5wd61Hh7VfgsmKycsi13SR7SubmeRDebIuTQ,2279 +django/utils/ipv6.py,sha256=WBkmZXdtbIHgcaWDKm4ElRvzyu_wKLCW2aA18g1RCJo,1350 django/utils/itercompat.py,sha256=lacIDjczhxbwG4ON_KfG1H6VNPOGOpbRhnVhbedo2CY,184 -django/utils/jslex.py,sha256=LwfwKHNFM1FJt_DmgD1G6U_7GBcAVYDyr9f9KcKC2dE,8040 -django/utils/log.py,sha256=zrskZ1PfvWf6opc32bHxwFfwbSQR6JDzGcyGpOMvjUM,7969 -django/utils/lorem_ipsum.py,sha256=yUtBgKhshftIpPg04pc1IrLpOBydZIf7g0isFCIJZqk,5473 -django/utils/module_loading.py,sha256=Ky3472UpPU2b1cRGCedRhFiqYlH1fFOuDDjiyEAnQxc,3847 -django/utils/numberformat.py,sha256=99DahMpI94hgBALt00-VGaN9NMYjJfb4WHcU9tnewSQ,3733 -django/utils/regex_helper.py,sha256=gv0YfkofciCI4iptv_6GEwyLyVZg1_HFaNRwn3DuH4c,12771 -django/utils/safestring.py,sha256=FSD9ZDwX2PFhpf2I9y6LFaAFlSkTKGdHfGz5Kl79nA4,1766 -django/utils/termcolors.py,sha256=vvQbUH7GsFofGRSiKQwx4YvgE4yZMtAGRVz9QPDfisA,7386 -django/utils/text.py,sha256=KzEoBpAt-y6erIblITCfkoPNfiDm7Gm9c32J2OMKi9o,14214 -django/utils/timesince.py,sha256=iHTKSKjPanK3YWtSV5sfZs3cTrDcbnuWYxinIM-6KaQ,3623 -django/utils/timezone.py,sha256=_5qheDG3BUF1QwTLjRrkp_zrwcm_Fb2cEMiaWw6Xk7I,9593 -django/utils/topological_sort.py,sha256=W_xR8enn8cY6W4oM8M2TnoidbbiYZbThfdI6UMI4-gc,1287 -django/utils/translation/__init__.py,sha256=BWLfGwW57kXwWOGpYsp6oIzUvOlPDxr8zk7ho2ZVlno,8889 +django/utils/jslex.py,sha256=FkgHjH5lbd9i0X-ockJlVK6TAa8iq22qR3Y1qrnmLDY,7695 +django/utils/log.py,sha256=EPL1Ns4NX_oUzYZ-yWYOcGP6StU3-eBBVHWE6Uaubgg,7737 +django/utils/lorem_ipsum.py,sha256=P_BSLsITDP2ZW9EJPy6ciFneib0iz9ezBz2LD7CViRE,4775 +django/utils/module_loading.py,sha256=0aH8A5ceSe90pYMpm04JkiUSSivkVqCtyQduDmKlIJM,3592 +django/utils/numberformat.py,sha256=vZy07ugV3tUwTPqDYLyJuuNKxPkIbed2pNcRZT_rUUY,3619 +django/utils/regex_helper.py,sha256=rDwP-EYSHtD_tLLiNG3RCx7rOi5t_FH7COfhDPO1rKg,12739 +django/utils/safestring.py,sha256=zesWIkFq4lAONEDpDVsIxwTDV0wHGq-duKQQGMdzh0w,1764 +django/utils/termcolors.py,sha256=sXUFjND4TFmBqJgoMex1IMhoDGzD5U27iHqNtIMa3rk,7362 +django/utils/text.py,sha256=VS1FOLMJP-nK7zWuMhGPzytEXuZ_Lt1_X7XsFiiYJtc,14309 +django/utils/timesince.py,sha256=k2-fRJCKn45mwqE4l8rgSjbX6a2FWSU9kPXXWstj2e4,3494 +django/utils/timezone.py,sha256=NcsMRbyTY-GndAJ67P0QOa1N7gZ9DRAi-S9Dxiib_hA,8084 +django/utils/topological_sort.py,sha256=JAPUKIset8fuFwQT2FYjyTR8zjJWv3QplaBN0nAVdhQ,1206 +django/utils/translation/__init__.py,sha256=Cx0JVXSGsVtCsM8unby1kadp2lWH4QlqBRdgbqEQlJc,10874 django/utils/translation/__pycache__/__init__.cpython-39.pyc,, django/utils/translation/__pycache__/reloader.cpython-39.pyc,, django/utils/translation/__pycache__/template.cpython-39.pyc,, django/utils/translation/__pycache__/trans_null.cpython-39.pyc,, django/utils/translation/__pycache__/trans_real.cpython-39.pyc,, -django/utils/translation/reloader.py,sha256=oVM0xenn3fraUomMEFucvwlbr5UGYUijWnUn6FL55Zc,1114 -django/utils/translation/template.py,sha256=TOfPNT62RnUbUG64a_6d_VQ7tsDC1_F1TCopw_HwlcA,10549 +django/utils/translation/reloader.py,sha256=C3NrDtG_M_hFjfYNYG2B_D1kdy4XEcJ9FMNnyxjnzgI,1113 +django/utils/translation/template.py,sha256=SVpfKA8df41wf7Q-WqNluORBWhL4pHiAv5FNufWP9Lo,10035 django/utils/translation/trans_null.py,sha256=yp82bHt5oqqL95Z5PFoYCZeENOulxzp-IqMmkWz0l9Y,1257 -django/utils/translation/trans_real.py,sha256=-fwtSc5Aklj9HrstzOihRiDxizv3uJhV2O7T6oAFCus,20219 -django/utils/tree.py,sha256=iST3UIX4Hobmlo3YxQETWMPij7l_wsZqVoeOHnyychs,4558 -django/utils/version.py,sha256=yB8khgSxwUmZVeGzud2KwD5CG1LQuNZHRAHElsca-IU,3607 -django/utils/xmlutils.py,sha256=LsggeI4vhln3An_YXNBk2cCwKLQgMe-O_3L--j3o3GM,1172 -django/views/__init__.py,sha256=GIq6CKUBCbGpQVyK4xIoaAUDPrmRvbBPSX_KSHk0Bb4,63 +django/utils/translation/trans_real.py,sha256=D-3v-8HfqGQJ6nEN9K9EoppBl085NibMdo3VYQu_4Mo,19914 +django/utils/tree.py,sha256=f6GbmuLRerOzV5VxfWpG8Ph85aDd7RdhZCs6eLvBZyE,4918 +django/utils/version.py,sha256=lf4G3gOmEBh8O8mmWl3u6ZoEgQR5bqqfmmh0IvTJT_0,3219 +django/utils/xmlutils.py,sha256=ABVrtMX1Vbv3z8BM8-oc2Bi1FxmwTgvSqafZM0gxVjM,1142 +django/views/__init__.py,sha256=DGdAuGC0t1bMju9i-B9p_gqPgRIFHtLXTdIxNKWFGsw,63 django/views/__pycache__/__init__.cpython-39.pyc,, django/views/__pycache__/csrf.cpython-39.pyc,, django/views/__pycache__/debug.cpython-39.pyc,, django/views/__pycache__/defaults.cpython-39.pyc,, django/views/__pycache__/i18n.cpython-39.pyc,, django/views/__pycache__/static.cpython-39.pyc,, -django/views/csrf.py,sha256=le5ZUjDfQFMYuxHIfkrZm41lbs6_k9YeNpypZHg7nVA,6332 -django/views/debug.py,sha256=s12AU_QjtsmOfc5UYI0-6-W9iH4VJ3vS-88IwQiSkcs,23633 +django/views/csrf.py,sha256=BlyqQhqrVIQFRALKoWnAFkF21AcMAaWo1JxtI84ersw,6282 +django/views/debug.py,sha256=7e9RM6lKTeMw8jjVUQJiwetUBRBOUDmUsFU-7slF01g,22408 django/views/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 django/views/decorators/__pycache__/__init__.cpython-39.pyc,, django/views/decorators/__pycache__/cache.cpython-39.pyc,, @@ -4412,30 +4348,30 @@ django/views/decorators/__pycache__/debug.cpython-39.pyc,, django/views/decorators/__pycache__/gzip.cpython-39.pyc,, django/views/decorators/__pycache__/http.cpython-39.pyc,, django/views/decorators/__pycache__/vary.cpython-39.pyc,, -django/views/decorators/cache.py,sha256=MWG5wGtCAW2Onb-jSNxEiGbAeXlX4j_sX1ILuEOa6HU,2340 -django/views/decorators/clickjacking.py,sha256=RuN4sYDOQyvqVMuhG2VzKLpiYJbLSJrzP4uaUrMLQfw,1571 -django/views/decorators/common.py,sha256=EC0OmNVMZdoKb5nXPWiOfQxOU9lCph_ZDwcWw9bMlmg,489 -django/views/decorators/csrf.py,sha256=sz46dXdnsUdVJbRdoMa7bwaRJUQ0_CSGbgrMdBskH8I,2074 -django/views/decorators/debug.py,sha256=MXGthVNjdh8wzX7BepHN6SEniWhziU2tHMRJs7ijjdU,3150 +django/views/decorators/cache.py,sha256=bBPXOx7_yZogkQwp_82AkjAtn49kgIjJvwcDfmXWX9o,1705 +django/views/decorators/clickjacking.py,sha256=EW-DRe2dR8yg4Rf8HRHl8c4-C8mL3HKGa6PxZRKmFtU,1565 +django/views/decorators/common.py,sha256=2AE4jUJSVf32_EtO_yddqvGLaYQ1dJbdXA-3TUTKD9s,488 +django/views/decorators/csrf.py,sha256=xPWVVNw_DBidvX_ZVYvN7CePt1HpxpUxsb6wMr0Oe4Y,2073 +django/views/decorators/debug.py,sha256=RbK_DO_Vg_120u0-tEqW1BcTYqcgZRccYMuW-X7JjnQ,3090 django/views/decorators/gzip.py,sha256=PtpSGd8BePa1utGqvKMFzpLtZJxpV2_Jej8llw5bCJY,253 -django/views/decorators/http.py,sha256=Loe8PWnfoAZdxhS00G7UchRceM2QAGzNGpZm26_joak,4907 -django/views/decorators/vary.py,sha256=VcBaCDOEjy1CrIy0LnCt2cJdJRnqXgn3B43zmzKuZ80,1089 -django/views/defaults.py,sha256=H742ukDQgQF9mcKKaHZPA6Xk-OZrDkDUw_J_XMSj1z8,4904 -django/views/generic/__init__.py,sha256=VwQKUbBFJktiq5J2fo3qRNzRc0STfcMRPChlLPYAkkE,886 +django/views/decorators/http.py,sha256=SNeC4LhD0S7_NCf2LsQ3mT_5phEup-l4s6si3T8-O1o,4729 +django/views/decorators/vary.py,sha256=6wEXI5yBFZYDVednNPc0bYbXGG-QzkIUQ-50ErDrA_k,1084 +django/views/defaults.py,sha256=cFxfvjxuyvV9d0X5FQEB6Pd52lCRcxk5Y1xmC_NsMx8,4923 +django/views/generic/__init__.py,sha256=WTnzEXnKyJqzHlLu_VsXInYg-GokDNBCUYNV_U6U-ok,822 django/views/generic/__pycache__/__init__.cpython-39.pyc,, django/views/generic/__pycache__/base.cpython-39.pyc,, django/views/generic/__pycache__/dates.cpython-39.pyc,, django/views/generic/__pycache__/detail.cpython-39.pyc,, django/views/generic/__pycache__/edit.cpython-39.pyc,, django/views/generic/__pycache__/list.cpython-39.pyc,, -django/views/generic/base.py,sha256=7LZ5EgMcr-FZu96xG2GedUbe6wQ2rY8cNvFGQcKSaLw,8132 -django/views/generic/dates.py,sha256=xwSEF6zsaSl1jUTePs6NPihnOJEWT-j8SST0RG4bco0,26332 -django/views/generic/detail.py,sha256=zrAuhJxrFvNqJLnlvK-NSiRiiONsKKOYFantD7UztwU,6663 -django/views/generic/edit.py,sha256=Gq0E2HTi9KZuIDJHC24tB4VQVRL0qLswqfyA9gRJ210,9747 -django/views/generic/list.py,sha256=KWsT5UOK5jflxn5JFoJCnyJEQXa0fM4talHswzEjzXU,7941 -django/views/i18n.py,sha256=L54knZenhRK1sLXvjDLxI7jjwqYlW-gC8FSE0FsdXJI,11466 -django/views/static.py,sha256=8KkwEayiB8QKBwP-eUOpsgTpTGRMPBascIZWVXz_iC8,4635 +django/views/generic/base.py,sha256=I3oi9x5_ZkPjtnwfbI0W9B3l5muUGwyzna2wFcg6q-s,7787 +django/views/generic/dates.py,sha256=scFoB5TCuelE4q_IFJXGQZXDZVAx1LFwPFJitH61vYc,25716 +django/views/generic/detail.py,sha256=m8otoffJXPW9ml-vAtXeM4asTT5I4pvuoR4BhjpWB6A,6507 +django/views/generic/edit.py,sha256=zPO3D8rFrSDjJG1OnRYn0frGqVq8VMKAEUihZU2NrIk,8332 +django/views/generic/list.py,sha256=whDapHWQc65L-jspWDMegzGGo6pJ_4pYsDSGQ2vukwg,7676 +django/views/i18n.py,sha256=9YUj0NCIizFirafwxX5D5CltTXILha80IvWDibxO39Y,11410 +django/views/static.py,sha256=CLZ-fOqDw3gdRiFcgwnK_FrJTxhGh4Uyzx3vqapOL8o,4569 django/views/templates/default_urlconf.html,sha256=VuT3cVYagdn5Kb94DGpfJyZhPpNtgC4HjuY8v8eEq5I,11150 django/views/templates/technical_404.html,sha256=dJEOimEguJg6g4IhdRPG5HmdMy8D30U-lNI8wC8wwQs,2706 -django/views/templates/technical_500.html,sha256=OwSy7TU-GW-T_DWjDmVCmgYorzL7YFg5KAq_fZqDH2Q,17343 -django/views/templates/technical_500.txt,sha256=7K_RLYW-WerfBxkwg2PmsXnBWtICjVdxcdnJXWjuKoY,3574 +django/views/templates/technical_500.html,sha256=mhvde7Ad4DbUyBh8HQyYwlmBGahobg6_H1c8h0MPm1I,17606 +django/views/templates/technical_500.txt,sha256=yMJiwQ_H3MkoJgLnlBDM52zVjiYxw_HamQv9eWheiso,3573 diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/REQUESTED b/venv/Lib/site-packages/Django-3.2.5.dist-info/REQUESTED similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/REQUESTED rename to venv/Lib/site-packages/Django-3.2.5.dist-info/REQUESTED diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/WHEEL b/venv/Lib/site-packages/Django-3.2.5.dist-info/WHEEL similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/WHEEL rename to venv/Lib/site-packages/Django-3.2.5.dist-info/WHEEL diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/entry_points.txt b/venv/Lib/site-packages/Django-3.2.5.dist-info/entry_points.txt similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/entry_points.txt rename to venv/Lib/site-packages/Django-3.2.5.dist-info/entry_points.txt diff --git a/venv/Lib/site-packages/Django-4.0.4.dist-info/top_level.txt b/venv/Lib/site-packages/Django-3.2.5.dist-info/top_level.txt similarity index 100% rename from venv/Lib/site-packages/Django-4.0.4.dist-info/top_level.txt rename to venv/Lib/site-packages/Django-3.2.5.dist-info/top_level.txt diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/INSTALLER b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/LICENSE.md b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/LICENSE.md new file mode 100644 index 0000000..2652d97 --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/LICENSE.md @@ -0,0 +1,29 @@ +Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Python Markdown Project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/METADATA b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/METADATA new file mode 100644 index 0000000..0dd5837 --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/METADATA @@ -0,0 +1,110 @@ +Metadata-Version: 2.1 +Name: Markdown +Version: 3.3.6 +Summary: Python implementation of Markdown. +Home-page: https://Python-Markdown.github.io/ +Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg +Author-email: python.markdown@gmail.com +Maintainer: Waylan Limberg +Maintainer-email: python.markdown@gmail.com +License: BSD License +Project-URL: Documentation, https://Python-Markdown.github.io/ +Project-URL: GitHub Project, https://github.com/Python-Markdown/markdown +Project-URL: Issue Tracker, https://github.com/Python-Markdown/markdown/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Communications :: Email :: Filters +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries +Classifier: Topic :: Internet :: WWW/HTTP :: Site Management +Classifier: Topic :: Software Development :: Documentation +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: Markdown +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE.md +Requires-Dist: importlib-metadata (>=4.4) ; python_version < "3.10" +Provides-Extra: testing +Requires-Dist: coverage ; extra == 'testing' +Requires-Dist: pyyaml ; extra == 'testing' + +[Python-Markdown][] +=================== + +[![Build Status][build-button]][build] +[![Coverage Status][codecov-button]][codecov] +[![Latest Version][mdversion-button]][md-pypi] +[![Python Versions][pyversion-button]][md-pypi] +[![BSD License][bsdlicense-button]][bsdlicense] +[![Code of Conduct][codeofconduct-button]][Code of Conduct] + +[build-button]: https://github.com/Python-Markdown/markdown/workflows/CI/badge.svg?event=push +[build]: https://github.com/Python-Markdown/markdown/actions?query=workflow%3ACI+event%3Apush +[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg +[codecov]: https://codecov.io/gh/Python-Markdown/markdown +[mdversion-button]: https://img.shields.io/pypi/v/Markdown.svg +[md-pypi]: https://pypi.org/project/Markdown/ +[pyversion-button]: https://img.shields.io/pypi/pyversions/Markdown.svg +[bsdlicense-button]: https://img.shields.io/badge/license-BSD-yellow.svg +[bsdlicense]: https://opensource.org/licenses/BSD-3-Clause +[codeofconduct-button]: https://img.shields.io/badge/code%20of%20conduct-contributor%20covenant-green.svg?style=flat-square +[Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md + +This is a Python implementation of John Gruber's [Markdown][]. +It is almost completely compliant with the reference implementation, +though there are a few known issues. See [Features][] for information +on what exactly is supported and what is not. Additional features are +supported by the [Available Extensions][]. + +[Python-Markdown]: https://Python-Markdown.github.io/ +[Markdown]: https://daringfireball.net/projects/markdown/ +[Features]: https://Python-Markdown.github.io#Features +[Available Extensions]: https://Python-Markdown.github.io/extensions + +Documentation +------------- + +```bash +pip install markdown +``` +```python +import markdown +html = markdown.markdown(your_text_string) +``` + +For more advanced [installation] and [usage] documentation, see the `docs/` directory +of the distribution or the project website at . + +[installation]: https://python-markdown.github.io/install/ +[usage]: https://python-markdown.github.io/reference/ + +See the change log at . + +Support +------- + +You may report bugs, ask for help, and discuss various other issues on the [bug tracker][]. + +[bug tracker]: https://github.com/Python-Markdown/markdown/issues + +Code of Conduct +--------------- + +Everyone interacting in the Python-Markdown project's codebases, issue trackers, +and mailing lists is expected to follow the [Code of Conduct]. + + + diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/RECORD b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/RECORD new file mode 100644 index 0000000..dd376e7 --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/RECORD @@ -0,0 +1,77 @@ +../../Scripts/markdown_py.exe,sha256=vCw3UluhGhDZ5OHeFwbu_i2uuAow6Ag76pfC21_f61A,106375 +Markdown-3.3.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Markdown-3.3.6.dist-info/LICENSE.md,sha256=bxGTy2NHGOZcOlN9biXr1hSCDsDvaTz8EiSBEmONZNo,1645 +Markdown-3.3.6.dist-info/METADATA,sha256=5gK5efFze8GvYs5GX7G5M597OXkRKG5DtqP8CvscZVQ,4630 +Markdown-3.3.6.dist-info/RECORD,, +Markdown-3.3.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Markdown-3.3.6.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Markdown-3.3.6.dist-info/entry_points.txt,sha256=j4jiKg-iwZGImvi8OzotZePWoFbJJ4GrfzDqH03u3SQ,1103 +Markdown-3.3.6.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9 +markdown/__init__.py,sha256=002-LuHviYzROW2rg_gBGai81nMouUNO9UFj5nSsTSk,2065 +markdown/__main__.py,sha256=JX1057VoovH3NA5uH5nQdQE8b0kXoeT79ZxCzFoL_kg,5803 +markdown/__meta__.py,sha256=IDpR5cdETCEXvY2YxKlRPnIQvdyf5vHJolJNDD4Um9w,1630 +markdown/__pycache__/__init__.cpython-39.pyc,, +markdown/__pycache__/__main__.cpython-39.pyc,, +markdown/__pycache__/__meta__.cpython-39.pyc,, +markdown/__pycache__/blockparser.cpython-39.pyc,, +markdown/__pycache__/blockprocessors.cpython-39.pyc,, +markdown/__pycache__/core.cpython-39.pyc,, +markdown/__pycache__/htmlparser.cpython-39.pyc,, +markdown/__pycache__/inlinepatterns.cpython-39.pyc,, +markdown/__pycache__/pep562.cpython-39.pyc,, +markdown/__pycache__/postprocessors.cpython-39.pyc,, +markdown/__pycache__/preprocessors.cpython-39.pyc,, +markdown/__pycache__/serializers.cpython-39.pyc,, +markdown/__pycache__/test_tools.cpython-39.pyc,, +markdown/__pycache__/treeprocessors.cpython-39.pyc,, +markdown/__pycache__/util.cpython-39.pyc,, +markdown/blockparser.py,sha256=JpBhOokOoBUGCXolftOc5m1hPcR2y9s9hVd9WSuhHzo,4285 +markdown/blockprocessors.py,sha256=LK4mfcgjH8rk3zsyxBzxisxdQjpFj0xkg1LxBtlpLUs,24890 +markdown/core.py,sha256=ZHtqvLdVHOKWIuX_UzdL3rIcxMwji5TC5ZCkV19iM4U,15401 +markdown/extensions/__init__.py,sha256=nw2VtafIf5zHjAcUuykQbaNY6taOmNn7ARn11-Pe080,3661 +markdown/extensions/__pycache__/__init__.cpython-39.pyc,, +markdown/extensions/__pycache__/abbr.cpython-39.pyc,, +markdown/extensions/__pycache__/admonition.cpython-39.pyc,, +markdown/extensions/__pycache__/attr_list.cpython-39.pyc,, +markdown/extensions/__pycache__/codehilite.cpython-39.pyc,, +markdown/extensions/__pycache__/def_list.cpython-39.pyc,, +markdown/extensions/__pycache__/extra.cpython-39.pyc,, +markdown/extensions/__pycache__/fenced_code.cpython-39.pyc,, +markdown/extensions/__pycache__/footnotes.cpython-39.pyc,, +markdown/extensions/__pycache__/legacy_attrs.cpython-39.pyc,, +markdown/extensions/__pycache__/legacy_em.cpython-39.pyc,, +markdown/extensions/__pycache__/md_in_html.cpython-39.pyc,, +markdown/extensions/__pycache__/meta.cpython-39.pyc,, +markdown/extensions/__pycache__/nl2br.cpython-39.pyc,, +markdown/extensions/__pycache__/sane_lists.cpython-39.pyc,, +markdown/extensions/__pycache__/smarty.cpython-39.pyc,, +markdown/extensions/__pycache__/tables.cpython-39.pyc,, +markdown/extensions/__pycache__/toc.cpython-39.pyc,, +markdown/extensions/__pycache__/wikilinks.cpython-39.pyc,, +markdown/extensions/abbr.py,sha256=5TNU5ml6-H1n-fztEkgUphSTvp5yKCXaiPZMrVuRFvo,3186 +markdown/extensions/admonition.py,sha256=INIecvdzQ7RLmgP8M-N6AZJ5uMd6dBfh9Uj6YibgNLk,5847 +markdown/extensions/attr_list.py,sha256=nhKFY_u6BVyKW2oMUeC4wEjqFNGpDSnNXqaohuF6M7I,5988 +markdown/extensions/codehilite.py,sha256=aEorLnWkEA_zwC2gAoqlR5nb8ZwjiUUFe0bA8bVP0Co,11654 +markdown/extensions/def_list.py,sha256=p-JT64hKqMkfxlmhETMVRPxjrdnBIPDW8k3S05S-qNM,3634 +markdown/extensions/extra.py,sha256=udRN8OvSWcq3UwkPygvsFl1RlCVtCJ-ARVg2IwVH6VY,1831 +markdown/extensions/fenced_code.py,sha256=pRZjVaEh8JZdhyLb2Vy7alfqIQNuPtN2Mzt_Imj2Vm0,7346 +markdown/extensions/footnotes.py,sha256=xvT6etWuTWTHLNHXYQWQGV-35RHTCvH9kBp2xJA6Jdg,15481 +markdown/extensions/legacy_attrs.py,sha256=2EaVQkxQoNnP8_lMPvGRBdNda8L4weUQroiyEuVdS-w,2547 +markdown/extensions/legacy_em.py,sha256=18j4L6zdScy9k18y-U2zaIhYsKVTxCaPurjqLFZmWkI,1582 +markdown/extensions/md_in_html.py,sha256=17w2s-YvjzKPWmng9La6J9-1-h1TWNEFBhVd0pF-j9U,15830 +markdown/extensions/meta.py,sha256=EUfkzM7l7UpH__Or9K3pl8ldVddwndlCZWA3d712RAE,2331 +markdown/extensions/nl2br.py,sha256=wAqTNOuf2L1NzlEvEqoID70n9y-aiYaGLkuyQk3CD0w,783 +markdown/extensions/sane_lists.py,sha256=ZQmCf-247KBexVG0fc62nDvokGkV6W1uavYbieNKSG4,1505 +markdown/extensions/smarty.py,sha256=0padzkVCNACainKw-Xj1S5UfT0125VCTfNejmrCZItA,10238 +markdown/extensions/tables.py,sha256=bicFx_wqhnEx6Y_8MJqA56rh71pt5fOe94oiWbvcobY,7685 +markdown/extensions/toc.py,sha256=Q8YP0DIuyl_B0GfUASYbE4q_B7zFsHS93t5fjiZyJWE,14136 +markdown/extensions/wikilinks.py,sha256=GkgT9BY7b1-qW--dIwFAhC9V20RoeF13b7CFdw_V21Q,2812 +markdown/htmlparser.py,sha256=K3OMq-OU2CTWCMNGPbMGqMmiwZAIrG2lQxuDOJxDjYo,13032 +markdown/inlinepatterns.py,sha256=csrxrPIET_nltn-phz80ObXu5i5oibK68h9ZCWT5eAo,29775 +markdown/pep562.py,sha256=5UkqT7sb-cQufgbOl_jF-RYUVVHS7VThzlMzR9vrd3I,8917 +markdown/postprocessors.py,sha256=NeJyWBqPeDuBBJLTGs5Bfm5oTkUBXk9HWBeQy2_OldI,4262 +markdown/preprocessors.py,sha256=-s8QGHGlX7JAIJTfCivuc-CVwTLWs0IyEU94YUT2IvQ,2742 +markdown/serializers.py,sha256=_wQl-iJrPSUEQ4Q1owWYqN9qceVh6TOlAOH_i44BKAQ,6540 +markdown/test_tools.py,sha256=svokrqFAHJ1H_BiYhEY9kilmk4dZIO3jYJTJcOsphCg,8361 +markdown/treeprocessors.py,sha256=S91w6byWeyBF96q9w8SJ_8UQV8p0UuFJ7Brj6Rw0-y4,15433 +markdown/util.py,sha256=1BKofVbYfqmgAK982UJATA22pj-ee9BcwxaHxB_bOZg,16063 diff --git a/venv/Lib/site-packages/django/conf/locale/ms/__init__.py b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/REQUESTED similarity index 100% rename from venv/Lib/site-packages/django/conf/locale/ms/__init__.py rename to venv/Lib/site-packages/Markdown-3.3.6.dist-info/REQUESTED diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/WHEEL b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/entry_points.txt b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/entry_points.txt new file mode 100644 index 0000000..f49693d --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/entry_points.txt @@ -0,0 +1,23 @@ +[console_scripts] +markdown_py = markdown.__main__:run + +[markdown.extensions] +abbr = markdown.extensions.abbr:AbbrExtension +admonition = markdown.extensions.admonition:AdmonitionExtension +attr_list = markdown.extensions.attr_list:AttrListExtension +codehilite = markdown.extensions.codehilite:CodeHiliteExtension +def_list = markdown.extensions.def_list:DefListExtension +extra = markdown.extensions.extra:ExtraExtension +fenced_code = markdown.extensions.fenced_code:FencedCodeExtension +footnotes = markdown.extensions.footnotes:FootnoteExtension +legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension +legacy_em = markdown.extensions.legacy_em:LegacyEmExtension +md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension +meta = markdown.extensions.meta:MetaExtension +nl2br = markdown.extensions.nl2br:Nl2BrExtension +sane_lists = markdown.extensions.sane_lists:SaneListExtension +smarty = markdown.extensions.smarty:SmartyExtension +tables = markdown.extensions.tables:TableExtension +toc = markdown.extensions.toc:TocExtension +wikilinks = markdown.extensions.wikilinks:WikiLinkExtension + diff --git a/venv/Lib/site-packages/Markdown-3.3.6.dist-info/top_level.txt b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/top_level.txt new file mode 100644 index 0000000..0918c97 --- /dev/null +++ b/venv/Lib/site-packages/Markdown-3.3.6.dist-info/top_level.txt @@ -0,0 +1 @@ +markdown diff --git a/venv/Lib/site-packages/MySQLdb/__init__.py b/venv/Lib/site-packages/MySQLdb/__init__.py new file mode 100644 index 0000000..b567363 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/__init__.py @@ -0,0 +1,170 @@ +""" +MySQLdb - A DB API v2.0 compatible interface to MySQL. + +This package is a wrapper around _mysql, which mostly implements the +MySQL C API. + +connect() -- connects to server + +See the C API specification and the MySQL documentation for more info +on other items. + +For information on how MySQLdb handles type conversion, see the +MySQLdb.converters module. +""" + +try: + from MySQLdb.release import version_info + from . import _mysql + + assert version_info == _mysql.version_info +except Exception: + raise ImportError( + "this is MySQLdb version {}, but _mysql is version {!r}\n_mysql: {!r}".format( + version_info, _mysql.version_info, _mysql.__file__ + ) + ) + + +from ._mysql import ( + NotSupportedError, + OperationalError, + get_client_info, + ProgrammingError, + Error, + InterfaceError, + debug, + IntegrityError, + string_literal, + MySQLError, + DataError, + DatabaseError, + InternalError, + Warning, +) +from MySQLdb.constants import FIELD_TYPE +from MySQLdb.times import ( + Date, + Time, + Timestamp, + DateFromTicks, + TimeFromTicks, + TimestampFromTicks, +) + +threadsafety = 1 +apilevel = "2.0" +paramstyle = "format" + + +class DBAPISet(frozenset): + """A special type of set for which A == x is true if A is a + DBAPISet and x is a member of that set.""" + + def __eq__(self, other): + if isinstance(other, DBAPISet): + return not self.difference(other) + return other in self + + +STRING = DBAPISet([FIELD_TYPE.ENUM, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING]) +BINARY = DBAPISet( + [ + FIELD_TYPE.BLOB, + FIELD_TYPE.LONG_BLOB, + FIELD_TYPE.MEDIUM_BLOB, + FIELD_TYPE.TINY_BLOB, + ] +) +NUMBER = DBAPISet( + [ + FIELD_TYPE.DECIMAL, + FIELD_TYPE.DOUBLE, + FIELD_TYPE.FLOAT, + FIELD_TYPE.INT24, + FIELD_TYPE.LONG, + FIELD_TYPE.LONGLONG, + FIELD_TYPE.TINY, + FIELD_TYPE.YEAR, + FIELD_TYPE.NEWDECIMAL, + ] +) +DATE = DBAPISet([FIELD_TYPE.DATE]) +TIME = DBAPISet([FIELD_TYPE.TIME]) +TIMESTAMP = DBAPISet([FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME]) +DATETIME = TIMESTAMP +ROWID = DBAPISet() + + +def test_DBAPISet_set_equality(): + assert STRING == STRING + + +def test_DBAPISet_set_inequality(): + assert STRING != NUMBER + + +def test_DBAPISet_set_equality_membership(): + assert FIELD_TYPE.VAR_STRING == STRING + + +def test_DBAPISet_set_inequality_membership(): + assert FIELD_TYPE.DATE != STRING + + +def Binary(x): + return bytes(x) + + +def Connect(*args, **kwargs): + """Factory function for connections.Connection.""" + from MySQLdb.connections import Connection + + return Connection(*args, **kwargs) + + +connect = Connection = Connect + +__all__ = [ + "BINARY", + "Binary", + "Connect", + "Connection", + "DATE", + "Date", + "Time", + "Timestamp", + "DateFromTicks", + "TimeFromTicks", + "TimestampFromTicks", + "DataError", + "DatabaseError", + "Error", + "FIELD_TYPE", + "IntegrityError", + "InterfaceError", + "InternalError", + "MySQLError", + "NUMBER", + "NotSupportedError", + "DBAPISet", + "OperationalError", + "ProgrammingError", + "ROWID", + "STRING", + "TIME", + "TIMESTAMP", + "Warning", + "apilevel", + "connect", + "connections", + "constants", + "converters", + "cursors", + "debug", + "get_client_info", + "paramstyle", + "string_literal", + "threadsafety", + "version_info", +] diff --git a/venv/Lib/site-packages/MySQLdb/_exceptions.py b/venv/Lib/site-packages/MySQLdb/_exceptions.py new file mode 100644 index 0000000..ba35dea --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/_exceptions.py @@ -0,0 +1,69 @@ +"""Exception classes for _mysql and MySQLdb. + +These classes are dictated by the DB API v2.0: + + https://www.python.org/dev/peps/pep-0249/ +""" + + +class MySQLError(Exception): + """Exception related to operation with MySQL.""" + + +class Warning(Warning, MySQLError): + """Exception raised for important warnings like data truncations + while inserting, etc.""" + + +class Error(MySQLError): + """Exception that is the base class of all other error exceptions + (not Warning).""" + + +class InterfaceError(Error): + """Exception raised for errors that are related to the database + interface rather than the database itself.""" + + +class DatabaseError(Error): + """Exception raised for errors that are related to the + database.""" + + +class DataError(DatabaseError): + """Exception raised for errors that are due to problems with the + processed data like division by zero, numeric value out of range, + etc.""" + + +class OperationalError(DatabaseError): + """Exception raised for errors that are related to the database's + operation and not necessarily under the control of the programmer, + e.g. an unexpected disconnect occurs, the data source name is not + found, a transaction could not be processed, a memory allocation + error occurred during processing, etc.""" + + +class IntegrityError(DatabaseError): + """Exception raised when the relational integrity of the database + is affected, e.g. a foreign key check fails, duplicate key, + etc.""" + + +class InternalError(DatabaseError): + """Exception raised when the database encounters an internal + error, e.g. the cursor is not valid anymore, the transaction is + out of sync, etc.""" + + +class ProgrammingError(DatabaseError): + """Exception raised for programming errors, e.g. table not found + or already exists, syntax error in the SQL statement, wrong number + of parameters specified, etc.""" + + +class NotSupportedError(DatabaseError): + """Exception raised in case a method or database API was used + which is not supported by the database, e.g. requesting a + .rollback() on a connection that does not support transaction or + has transactions turned off.""" diff --git a/venv/Lib/site-packages/MySQLdb/_mysql.cp39-win_amd64.pyd b/venv/Lib/site-packages/MySQLdb/_mysql.cp39-win_amd64.pyd new file mode 100644 index 0000000..c2edd42 Binary files /dev/null and b/venv/Lib/site-packages/MySQLdb/_mysql.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/MySQLdb/connections.py b/venv/Lib/site-packages/MySQLdb/connections.py new file mode 100644 index 0000000..3832466 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/connections.py @@ -0,0 +1,333 @@ +""" +This module implements connections for MySQLdb. Presently there is +only one class: Connection. Others are unlikely. However, you might +want to make your own subclasses. In most cases, you will probably +override Connection.default_cursor with a non-standard Cursor class. +""" +import re + +from . import cursors, _mysql +from ._exceptions import ( + Warning, + Error, + InterfaceError, + DataError, + DatabaseError, + OperationalError, + IntegrityError, + InternalError, + NotSupportedError, + ProgrammingError, +) + +# Mapping from MySQL charset name to Python codec name +_charset_to_encoding = { + "utf8mb4": "utf8", + "utf8mb3": "utf8", + "latin1": "cp1252", + "koi8r": "koi8_r", + "koi8u": "koi8_u", +} + +re_numeric_part = re.compile(r"^(\d+)") + + +def numeric_part(s): + """Returns the leading numeric part of a string. + + >>> numeric_part("20-alpha") + 20 + >>> numeric_part("foo") + >>> numeric_part("16b") + 16 + """ + + m = re_numeric_part.match(s) + if m: + return int(m.group(1)) + return None + + +class Connection(_mysql.connection): + """MySQL Database Connection Object""" + + default_cursor = cursors.Cursor + + def __init__(self, *args, **kwargs): + """ + Create a connection to the database. It is strongly recommended + that you only use keyword parameters. Consult the MySQL C API + documentation for more information. + + :param str host: host to connect + :param str user: user to connect as + :param str password: password to use + :param str passwd: alias of password (deprecated) + :param str database: database to use + :param str db: alias of database (deprecated) + :param int port: TCP/IP port to connect to + :param str unix_socket: location of unix_socket to use + :param dict conv: conversion dictionary, see MySQLdb.converters + :param int connect_timeout: + number of seconds to wait before the connection attempt fails. + + :param bool compress: if set, compression is enabled + :param str named_pipe: if set, a named pipe is used to connect (Windows only) + :param str init_command: + command which is run once the connection is created + + :param str read_default_file: + file from which default client values are read + + :param str read_default_group: + configuration group to use from the default file + + :param type cursorclass: + class object, used to create cursors (keyword only) + + :param bool use_unicode: + If True, text-like columns are returned as unicode objects + using the connection's character set. Otherwise, text-like + columns are returned as bytes. Unicode objects will always + be encoded to the connection's character set regardless of + this setting. + Default to True. + + :param str charset: + If supplied, the connection character set will be changed + to this character set. + + :param str auth_plugin: + If supplied, the connection default authentication plugin will be + changed to this value. Example values: + `mysql_native_password` or `caching_sha2_password` + + :param str sql_mode: + If supplied, the session SQL mode will be changed to this + setting. + For more details and legal values, see the MySQL documentation. + + :param int client_flag: + flags to use or 0 (see MySQL docs or constants/CLIENTS.py) + + :param bool multi_statements: + If True, enable multi statements for clients >= 4.1. + Defaults to True. + + :param str ssl_mode: + specify the security settings for connection to the server; + see the MySQL documentation for more details + (mysql_option(), MYSQL_OPT_SSL_MODE). + Only one of 'DISABLED', 'PREFERRED', 'REQUIRED', + 'VERIFY_CA', 'VERIFY_IDENTITY' can be specified. + + :param dict ssl: + dictionary or mapping contains SSL connection parameters; + see the MySQL documentation for more details + (mysql_ssl_set()). If this is set, and the client does not + support SSL, NotSupportedError will be raised. + + :param bool local_infile: + enables LOAD LOCAL INFILE; zero disables + + :param bool autocommit: + If False (default), autocommit is disabled. + If True, autocommit is enabled. + If None, autocommit isn't set and server default is used. + + :param bool binary_prefix: + If set, the '_binary' prefix will be used for raw byte query + arguments (e.g. Binary). This is disabled by default. + + There are a number of undocumented, non-standard methods. See the + documentation for the MySQL C API for some hints on what they do. + """ + from MySQLdb.constants import CLIENT, FIELD_TYPE + from MySQLdb.converters import conversions, _bytes_or_str + from weakref import proxy + + kwargs2 = kwargs.copy() + + if "db" in kwargs2: + kwargs2["database"] = kwargs2.pop("db") + if "passwd" in kwargs2: + kwargs2["password"] = kwargs2.pop("passwd") + + if "conv" in kwargs: + conv = kwargs["conv"] + else: + conv = conversions + + conv2 = {} + for k, v in conv.items(): + if isinstance(k, int) and isinstance(v, list): + conv2[k] = v[:] + else: + conv2[k] = v + kwargs2["conv"] = conv2 + + cursorclass = kwargs2.pop("cursorclass", self.default_cursor) + charset = kwargs2.get("charset", "") + use_unicode = kwargs2.pop("use_unicode", True) + sql_mode = kwargs2.pop("sql_mode", "") + self._binary_prefix = kwargs2.pop("binary_prefix", False) + + client_flag = kwargs.get("client_flag", 0) + client_flag |= CLIENT.MULTI_RESULTS + multi_statements = kwargs2.pop("multi_statements", True) + if multi_statements: + client_flag |= CLIENT.MULTI_STATEMENTS + kwargs2["client_flag"] = client_flag + + # PEP-249 requires autocommit to be initially off + autocommit = kwargs2.pop("autocommit", False) + + super().__init__(*args, **kwargs2) + self.cursorclass = cursorclass + self.encoders = {k: v for k, v in conv.items() if type(k) is not int} + + self._server_version = tuple( + [numeric_part(n) for n in self.get_server_info().split(".")[:2]] + ) + + self.encoding = "ascii" # overridden in set_character_set() + + if not charset: + charset = self.character_set_name() + self.set_character_set(charset) + + if sql_mode: + self.set_sql_mode(sql_mode) + + if use_unicode: + for t in ( + FIELD_TYPE.STRING, + FIELD_TYPE.VAR_STRING, + FIELD_TYPE.VARCHAR, + FIELD_TYPE.TINY_BLOB, + FIELD_TYPE.MEDIUM_BLOB, + FIELD_TYPE.LONG_BLOB, + FIELD_TYPE.BLOB, + ): + self.converter[t] = _bytes_or_str + # Unlike other string/blob types, JSON is always text. + # MySQL may return JSON with charset==binary. + self.converter[FIELD_TYPE.JSON] = str + + db = proxy(self) + + def unicode_literal(u, dummy=None): + return db.string_literal(u.encode(db.encoding)) + + self.encoders[str] = unicode_literal + + self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS + if self._transactional: + if autocommit is not None: + self.autocommit(autocommit) + self.messages = [] + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def autocommit(self, on): + on = bool(on) + if self.get_autocommit() != on: + _mysql.connection.autocommit(self, on) + + def cursor(self, cursorclass=None): + """ + Create a cursor on which queries may be performed. The + optional cursorclass parameter is used to create the + Cursor. By default, self.cursorclass=cursors.Cursor is + used. + """ + return (cursorclass or self.cursorclass)(self) + + def query(self, query): + # Since _mysql releases GIL while querying, we need immutable buffer. + if isinstance(query, bytearray): + query = bytes(query) + _mysql.connection.query(self, query) + + def _bytes_literal(self, bs): + assert isinstance(bs, (bytes, bytearray)) + x = self.string_literal(bs) # x is escaped and quoted bytes + if self._binary_prefix: + return b"_binary" + x + return x + + def _tuple_literal(self, t): + return b"(%s)" % (b",".join(map(self.literal, t))) + + def literal(self, o): + """If o is a single object, returns an SQL literal as a string. + If o is a non-string sequence, the items of the sequence are + converted and returned as a sequence. + + Non-standard. For internal use; do not use this in your + applications. + """ + if isinstance(o, str): + s = self.string_literal(o.encode(self.encoding)) + elif isinstance(o, bytearray): + s = self._bytes_literal(o) + elif isinstance(o, bytes): + s = self._bytes_literal(o) + elif isinstance(o, (tuple, list)): + s = self._tuple_literal(o) + else: + s = self.escape(o, self.encoders) + if isinstance(s, str): + s = s.encode(self.encoding) + assert isinstance(s, bytes) + return s + + def begin(self): + """Explicitly begin a connection. + + This method is not used when autocommit=False (default). + """ + self.query(b"BEGIN") + + def set_character_set(self, charset): + """Set the connection character set to charset.""" + super().set_character_set(charset) + self.encoding = _charset_to_encoding.get(charset, charset) + + def set_sql_mode(self, sql_mode): + """Set the connection sql_mode. See MySQL documentation for + legal values.""" + if self._server_version < (4, 1): + raise NotSupportedError("server is too old to set sql_mode") + self.query("SET SESSION sql_mode='%s'" % sql_mode) + self.store_result() + + def show_warnings(self): + """Return detailed information about warnings as a + sequence of tuples of (Level, Code, Message). This + is only supported in MySQL-4.1 and up. If your server + is an earlier version, an empty sequence is returned.""" + if self._server_version < (4, 1): + return () + self.query("SHOW WARNINGS") + r = self.store_result() + warnings = r.fetch_row(0) + return warnings + + Warning = Warning + Error = Error + InterfaceError = InterfaceError + DatabaseError = DatabaseError + DataError = DataError + OperationalError = OperationalError + IntegrityError = IntegrityError + InternalError = InternalError + ProgrammingError = ProgrammingError + NotSupportedError = NotSupportedError + + +# vim: colorcolumn=100 diff --git a/venv/Lib/site-packages/MySQLdb/constants/CLIENT.py b/venv/Lib/site-packages/MySQLdb/constants/CLIENT.py new file mode 100644 index 0000000..35f578c --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/CLIENT.py @@ -0,0 +1,27 @@ +"""MySQL CLIENT constants + +These constants are used when creating the connection. Use bitwise-OR +(|) to combine options together, and pass them as the client_flags +parameter to MySQLdb.Connection. For more information on these flags, +see the MySQL C API documentation for mysql_real_connect(). + +""" + +LONG_PASSWORD = 1 +FOUND_ROWS = 2 +LONG_FLAG = 4 +CONNECT_WITH_DB = 8 +NO_SCHEMA = 16 +COMPRESS = 32 +ODBC = 64 +LOCAL_FILES = 128 +IGNORE_SPACE = 256 +CHANGE_USER = 512 +INTERACTIVE = 1024 +SSL = 2048 +IGNORE_SIGPIPE = 4096 +TRANSACTIONS = 8192 # mysql_com.h was WRONG prior to 3.23.35 +RESERVED = 16384 +SECURE_CONNECTION = 32768 +MULTI_STATEMENTS = 65536 +MULTI_RESULTS = 131072 diff --git a/venv/Lib/site-packages/MySQLdb/constants/CR.py b/venv/Lib/site-packages/MySQLdb/constants/CR.py new file mode 100644 index 0000000..9d33cf6 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/CR.py @@ -0,0 +1,105 @@ +"""MySQL Connection Errors + +Nearly all of these raise OperationalError. COMMANDS_OUT_OF_SYNC +raises ProgrammingError. + +""" + +if __name__ == "__main__": + """ + Usage: python CR.py [/path/to/mysql/errmsg.h ...] >> CR.py + """ + import fileinput + import re + + data = {} + error_last = None + for line in fileinput.input(): + line = re.sub(r"/\*.*?\*/", "", line) + m = re.match(r"^\s*#define\s+CR_([A-Z0-9_]+)\s+(\d+)(\s.*|$)", line) + if m: + name = m.group(1) + value = int(m.group(2)) + if name == "ERROR_LAST": + if error_last is None or error_last < value: + error_last = value + continue + if value not in data: + data[value] = set() + data[value].add(name) + for value, names in sorted(data.items()): + for name in sorted(names): + print("{} = {}".format(name, value)) + if error_last is not None: + print("ERROR_LAST = %s" % error_last) + + +ERROR_FIRST = 2000 +MIN_ERROR = 2000 +UNKNOWN_ERROR = 2000 +SOCKET_CREATE_ERROR = 2001 +CONNECTION_ERROR = 2002 +CONN_HOST_ERROR = 2003 +IPSOCK_ERROR = 2004 +UNKNOWN_HOST = 2005 +SERVER_GONE_ERROR = 2006 +VERSION_ERROR = 2007 +OUT_OF_MEMORY = 2008 +WRONG_HOST_INFO = 2009 +LOCALHOST_CONNECTION = 2010 +TCP_CONNECTION = 2011 +SERVER_HANDSHAKE_ERR = 2012 +SERVER_LOST = 2013 +COMMANDS_OUT_OF_SYNC = 2014 +NAMEDPIPE_CONNECTION = 2015 +NAMEDPIPEWAIT_ERROR = 2016 +NAMEDPIPEOPEN_ERROR = 2017 +NAMEDPIPESETSTATE_ERROR = 2018 +CANT_READ_CHARSET = 2019 +NET_PACKET_TOO_LARGE = 2020 +EMBEDDED_CONNECTION = 2021 +PROBE_SLAVE_STATUS = 2022 +PROBE_SLAVE_HOSTS = 2023 +PROBE_SLAVE_CONNECT = 2024 +PROBE_MASTER_CONNECT = 2025 +SSL_CONNECTION_ERROR = 2026 +MALFORMED_PACKET = 2027 +WRONG_LICENSE = 2028 +NULL_POINTER = 2029 +NO_PREPARE_STMT = 2030 +PARAMS_NOT_BOUND = 2031 +DATA_TRUNCATED = 2032 +NO_PARAMETERS_EXISTS = 2033 +INVALID_PARAMETER_NO = 2034 +INVALID_BUFFER_USE = 2035 +UNSUPPORTED_PARAM_TYPE = 2036 +SHARED_MEMORY_CONNECTION = 2037 +SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038 +SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039 +SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040 +SHARED_MEMORY_CONNECT_MAP_ERROR = 2041 +SHARED_MEMORY_FILE_MAP_ERROR = 2042 +SHARED_MEMORY_MAP_ERROR = 2043 +SHARED_MEMORY_EVENT_ERROR = 2044 +SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045 +SHARED_MEMORY_CONNECT_SET_ERROR = 2046 +CONN_UNKNOW_PROTOCOL = 2047 +INVALID_CONN_HANDLE = 2048 +UNUSED_1 = 2049 +FETCH_CANCELED = 2050 +NO_DATA = 2051 +NO_STMT_METADATA = 2052 +NO_RESULT_SET = 2053 +NOT_IMPLEMENTED = 2054 +SERVER_LOST_EXTENDED = 2055 +STMT_CLOSED = 2056 +NEW_STMT_METADATA = 2057 +ALREADY_CONNECTED = 2058 +AUTH_PLUGIN_CANNOT_LOAD = 2059 +DUPLICATE_CONNECTION_ATTR = 2060 +AUTH_PLUGIN_ERR = 2061 +INSECURE_API_ERR = 2062 +FILE_NAME_TOO_LONG = 2063 +SSL_FIPS_MODE_ERR = 2064 +MAX_ERROR = 2999 +ERROR_LAST = 2064 diff --git a/venv/Lib/site-packages/MySQLdb/constants/ER.py b/venv/Lib/site-packages/MySQLdb/constants/ER.py new file mode 100644 index 0000000..fcd5bf2 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/ER.py @@ -0,0 +1,827 @@ +"""MySQL ER Constants + +These constants are error codes for the bulk of the error conditions +that may occur. +""" + +if __name__ == "__main__": + """ + Usage: python ER.py [/path/to/mysql/mysqld_error.h ...] >> ER.py + """ + import fileinput + import re + + data = {} + error_last = None + for line in fileinput.input(): + line = re.sub(r"/\*.*?\*/", "", line) + m = re.match(r"^\s*#define\s+((ER|WARN)_[A-Z0-9_]+)\s+(\d+)\s*", line) + if m: + name = m.group(1) + if name.startswith("ER_"): + name = name[3:] + value = int(m.group(3)) + if name == "ERROR_LAST": + if error_last is None or error_last < value: + error_last = value + continue + if value not in data: + data[value] = set() + data[value].add(name) + for value, names in sorted(data.items()): + for name in sorted(names): + print("{} = {}".format(name, value)) + if error_last is not None: + print("ERROR_LAST = %s" % error_last) + + +ERROR_FIRST = 1000 +NO = 1002 +YES = 1003 +CANT_CREATE_FILE = 1004 +CANT_CREATE_TABLE = 1005 +CANT_CREATE_DB = 1006 +DB_CREATE_EXISTS = 1007 +DB_DROP_EXISTS = 1008 +DB_DROP_RMDIR = 1010 +CANT_FIND_SYSTEM_REC = 1012 +CANT_GET_STAT = 1013 +CANT_LOCK = 1015 +CANT_OPEN_FILE = 1016 +FILE_NOT_FOUND = 1017 +CANT_READ_DIR = 1018 +CHECKREAD = 1020 +DUP_KEY = 1022 +ERROR_ON_READ = 1024 +ERROR_ON_RENAME = 1025 +ERROR_ON_WRITE = 1026 +FILE_USED = 1027 +FILSORT_ABORT = 1028 +GET_ERRNO = 1030 +ILLEGAL_HA = 1031 +KEY_NOT_FOUND = 1032 +NOT_FORM_FILE = 1033 +NOT_KEYFILE = 1034 +OLD_KEYFILE = 1035 +OPEN_AS_READONLY = 1036 +OUTOFMEMORY = 1037 +OUT_OF_SORTMEMORY = 1038 +CON_COUNT_ERROR = 1040 +OUT_OF_RESOURCES = 1041 +BAD_HOST_ERROR = 1042 +HANDSHAKE_ERROR = 1043 +DBACCESS_DENIED_ERROR = 1044 +ACCESS_DENIED_ERROR = 1045 +NO_DB_ERROR = 1046 +UNKNOWN_COM_ERROR = 1047 +BAD_NULL_ERROR = 1048 +BAD_DB_ERROR = 1049 +TABLE_EXISTS_ERROR = 1050 +BAD_TABLE_ERROR = 1051 +NON_UNIQ_ERROR = 1052 +SERVER_SHUTDOWN = 1053 +BAD_FIELD_ERROR = 1054 +WRONG_FIELD_WITH_GROUP = 1055 +WRONG_GROUP_FIELD = 1056 +WRONG_SUM_SELECT = 1057 +WRONG_VALUE_COUNT = 1058 +TOO_LONG_IDENT = 1059 +DUP_FIELDNAME = 1060 +DUP_KEYNAME = 1061 +DUP_ENTRY = 1062 +WRONG_FIELD_SPEC = 1063 +PARSE_ERROR = 1064 +EMPTY_QUERY = 1065 +NONUNIQ_TABLE = 1066 +INVALID_DEFAULT = 1067 +MULTIPLE_PRI_KEY = 1068 +TOO_MANY_KEYS = 1069 +TOO_MANY_KEY_PARTS = 1070 +TOO_LONG_KEY = 1071 +KEY_COLUMN_DOES_NOT_EXITS = 1072 +BLOB_USED_AS_KEY = 1073 +TOO_BIG_FIELDLENGTH = 1074 +WRONG_AUTO_KEY = 1075 +READY = 1076 +SHUTDOWN_COMPLETE = 1079 +FORCING_CLOSE = 1080 +IPSOCK_ERROR = 1081 +NO_SUCH_INDEX = 1082 +WRONG_FIELD_TERMINATORS = 1083 +BLOBS_AND_NO_TERMINATED = 1084 +TEXTFILE_NOT_READABLE = 1085 +FILE_EXISTS_ERROR = 1086 +LOAD_INFO = 1087 +ALTER_INFO = 1088 +WRONG_SUB_KEY = 1089 +CANT_REMOVE_ALL_FIELDS = 1090 +CANT_DROP_FIELD_OR_KEY = 1091 +INSERT_INFO = 1092 +UPDATE_TABLE_USED = 1093 +NO_SUCH_THREAD = 1094 +KILL_DENIED_ERROR = 1095 +NO_TABLES_USED = 1096 +TOO_BIG_SET = 1097 +NO_UNIQUE_LOGFILE = 1098 +TABLE_NOT_LOCKED_FOR_WRITE = 1099 +TABLE_NOT_LOCKED = 1100 +BLOB_CANT_HAVE_DEFAULT = 1101 +WRONG_DB_NAME = 1102 +WRONG_TABLE_NAME = 1103 +TOO_BIG_SELECT = 1104 +UNKNOWN_ERROR = 1105 +UNKNOWN_PROCEDURE = 1106 +WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 +WRONG_PARAMETERS_TO_PROCEDURE = 1108 +UNKNOWN_TABLE = 1109 +FIELD_SPECIFIED_TWICE = 1110 +INVALID_GROUP_FUNC_USE = 1111 +UNSUPPORTED_EXTENSION = 1112 +TABLE_MUST_HAVE_COLUMNS = 1113 +RECORD_FILE_FULL = 1114 +UNKNOWN_CHARACTER_SET = 1115 +TOO_MANY_TABLES = 1116 +TOO_MANY_FIELDS = 1117 +TOO_BIG_ROWSIZE = 1118 +STACK_OVERRUN = 1119 +WRONG_OUTER_JOIN_UNUSED = 1120 +NULL_COLUMN_IN_INDEX = 1121 +CANT_FIND_UDF = 1122 +CANT_INITIALIZE_UDF = 1123 +UDF_NO_PATHS = 1124 +UDF_EXISTS = 1125 +CANT_OPEN_LIBRARY = 1126 +CANT_FIND_DL_ENTRY = 1127 +FUNCTION_NOT_DEFINED = 1128 +HOST_IS_BLOCKED = 1129 +HOST_NOT_PRIVILEGED = 1130 +PASSWORD_ANONYMOUS_USER = 1131 +PASSWORD_NOT_ALLOWED = 1132 +PASSWORD_NO_MATCH = 1133 +UPDATE_INFO = 1134 +CANT_CREATE_THREAD = 1135 +WRONG_VALUE_COUNT_ON_ROW = 1136 +CANT_REOPEN_TABLE = 1137 +INVALID_USE_OF_NULL = 1138 +REGEXP_ERROR = 1139 +MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 +NONEXISTING_GRANT = 1141 +TABLEACCESS_DENIED_ERROR = 1142 +COLUMNACCESS_DENIED_ERROR = 1143 +ILLEGAL_GRANT_FOR_TABLE = 1144 +GRANT_WRONG_HOST_OR_USER = 1145 +NO_SUCH_TABLE = 1146 +NONEXISTING_TABLE_GRANT = 1147 +NOT_ALLOWED_COMMAND = 1148 +SYNTAX_ERROR = 1149 +ABORTING_CONNECTION = 1152 +NET_PACKET_TOO_LARGE = 1153 +NET_READ_ERROR_FROM_PIPE = 1154 +NET_FCNTL_ERROR = 1155 +NET_PACKETS_OUT_OF_ORDER = 1156 +NET_UNCOMPRESS_ERROR = 1157 +NET_READ_ERROR = 1158 +NET_READ_INTERRUPTED = 1159 +NET_ERROR_ON_WRITE = 1160 +NET_WRITE_INTERRUPTED = 1161 +TOO_LONG_STRING = 1162 +TABLE_CANT_HANDLE_BLOB = 1163 +TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 +WRONG_COLUMN_NAME = 1166 +WRONG_KEY_COLUMN = 1167 +WRONG_MRG_TABLE = 1168 +DUP_UNIQUE = 1169 +BLOB_KEY_WITHOUT_LENGTH = 1170 +PRIMARY_CANT_HAVE_NULL = 1171 +TOO_MANY_ROWS = 1172 +REQUIRES_PRIMARY_KEY = 1173 +UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 +KEY_DOES_NOT_EXITS = 1176 +CHECK_NO_SUCH_TABLE = 1177 +CHECK_NOT_IMPLEMENTED = 1178 +CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 +ERROR_DURING_COMMIT = 1180 +ERROR_DURING_ROLLBACK = 1181 +ERROR_DURING_FLUSH_LOGS = 1182 +NEW_ABORTING_CONNECTION = 1184 +MASTER = 1188 +MASTER_NET_READ = 1189 +MASTER_NET_WRITE = 1190 +FT_MATCHING_KEY_NOT_FOUND = 1191 +LOCK_OR_ACTIVE_TRANSACTION = 1192 +UNKNOWN_SYSTEM_VARIABLE = 1193 +CRASHED_ON_USAGE = 1194 +CRASHED_ON_REPAIR = 1195 +WARNING_NOT_COMPLETE_ROLLBACK = 1196 +TRANS_CACHE_FULL = 1197 +SLAVE_NOT_RUNNING = 1199 +BAD_SLAVE = 1200 +MASTER_INFO = 1201 +SLAVE_THREAD = 1202 +TOO_MANY_USER_CONNECTIONS = 1203 +SET_CONSTANTS_ONLY = 1204 +LOCK_WAIT_TIMEOUT = 1205 +LOCK_TABLE_FULL = 1206 +READ_ONLY_TRANSACTION = 1207 +WRONG_ARGUMENTS = 1210 +NO_PERMISSION_TO_CREATE_USER = 1211 +LOCK_DEADLOCK = 1213 +TABLE_CANT_HANDLE_FT = 1214 +CANNOT_ADD_FOREIGN = 1215 +NO_REFERENCED_ROW = 1216 +ROW_IS_REFERENCED = 1217 +CONNECT_TO_MASTER = 1218 +ERROR_WHEN_EXECUTING_COMMAND = 1220 +WRONG_USAGE = 1221 +WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 +CANT_UPDATE_WITH_READLOCK = 1223 +MIXING_NOT_ALLOWED = 1224 +DUP_ARGUMENT = 1225 +USER_LIMIT_REACHED = 1226 +SPECIFIC_ACCESS_DENIED_ERROR = 1227 +LOCAL_VARIABLE = 1228 +GLOBAL_VARIABLE = 1229 +NO_DEFAULT = 1230 +WRONG_VALUE_FOR_VAR = 1231 +WRONG_TYPE_FOR_VAR = 1232 +VAR_CANT_BE_READ = 1233 +CANT_USE_OPTION_HERE = 1234 +NOT_SUPPORTED_YET = 1235 +MASTER_FATAL_ERROR_READING_BINLOG = 1236 +SLAVE_IGNORED_TABLE = 1237 +INCORRECT_GLOBAL_LOCAL_VAR = 1238 +WRONG_FK_DEF = 1239 +KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 +OPERAND_COLUMNS = 1241 +SUBQUERY_NO_1_ROW = 1242 +UNKNOWN_STMT_HANDLER = 1243 +CORRUPT_HELP_DB = 1244 +AUTO_CONVERT = 1246 +ILLEGAL_REFERENCE = 1247 +DERIVED_MUST_HAVE_ALIAS = 1248 +SELECT_REDUCED = 1249 +TABLENAME_NOT_ALLOWED_HERE = 1250 +NOT_SUPPORTED_AUTH_MODE = 1251 +SPATIAL_CANT_HAVE_NULL = 1252 +COLLATION_CHARSET_MISMATCH = 1253 +TOO_BIG_FOR_UNCOMPRESS = 1256 +ZLIB_Z_MEM_ERROR = 1257 +ZLIB_Z_BUF_ERROR = 1258 +ZLIB_Z_DATA_ERROR = 1259 +CUT_VALUE_GROUP_CONCAT = 1260 +WARN_TOO_FEW_RECORDS = 1261 +WARN_TOO_MANY_RECORDS = 1262 +WARN_NULL_TO_NOTNULL = 1263 +WARN_DATA_OUT_OF_RANGE = 1264 +WARN_DATA_TRUNCATED = 1265 +WARN_USING_OTHER_HANDLER = 1266 +CANT_AGGREGATE_2COLLATIONS = 1267 +REVOKE_GRANTS = 1269 +CANT_AGGREGATE_3COLLATIONS = 1270 +CANT_AGGREGATE_NCOLLATIONS = 1271 +VARIABLE_IS_NOT_STRUCT = 1272 +UNKNOWN_COLLATION = 1273 +SLAVE_IGNORED_SSL_PARAMS = 1274 +SERVER_IS_IN_SECURE_AUTH_MODE = 1275 +WARN_FIELD_RESOLVED = 1276 +BAD_SLAVE_UNTIL_COND = 1277 +MISSING_SKIP_SLAVE = 1278 +UNTIL_COND_IGNORED = 1279 +WRONG_NAME_FOR_INDEX = 1280 +WRONG_NAME_FOR_CATALOG = 1281 +BAD_FT_COLUMN = 1283 +UNKNOWN_KEY_CACHE = 1284 +WARN_HOSTNAME_WONT_WORK = 1285 +UNKNOWN_STORAGE_ENGINE = 1286 +WARN_DEPRECATED_SYNTAX = 1287 +NON_UPDATABLE_TABLE = 1288 +FEATURE_DISABLED = 1289 +OPTION_PREVENTS_STATEMENT = 1290 +DUPLICATED_VALUE_IN_TYPE = 1291 +TRUNCATED_WRONG_VALUE = 1292 +INVALID_ON_UPDATE = 1294 +UNSUPPORTED_PS = 1295 +GET_ERRMSG = 1296 +GET_TEMPORARY_ERRMSG = 1297 +UNKNOWN_TIME_ZONE = 1298 +WARN_INVALID_TIMESTAMP = 1299 +INVALID_CHARACTER_STRING = 1300 +WARN_ALLOWED_PACKET_OVERFLOWED = 1301 +CONFLICTING_DECLARATIONS = 1302 +SP_NO_RECURSIVE_CREATE = 1303 +SP_ALREADY_EXISTS = 1304 +SP_DOES_NOT_EXIST = 1305 +SP_DROP_FAILED = 1306 +SP_STORE_FAILED = 1307 +SP_LILABEL_MISMATCH = 1308 +SP_LABEL_REDEFINE = 1309 +SP_LABEL_MISMATCH = 1310 +SP_UNINIT_VAR = 1311 +SP_BADSELECT = 1312 +SP_BADRETURN = 1313 +SP_BADSTATEMENT = 1314 +UPDATE_LOG_DEPRECATED_IGNORED = 1315 +UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 +QUERY_INTERRUPTED = 1317 +SP_WRONG_NO_OF_ARGS = 1318 +SP_COND_MISMATCH = 1319 +SP_NORETURN = 1320 +SP_NORETURNEND = 1321 +SP_BAD_CURSOR_QUERY = 1322 +SP_BAD_CURSOR_SELECT = 1323 +SP_CURSOR_MISMATCH = 1324 +SP_CURSOR_ALREADY_OPEN = 1325 +SP_CURSOR_NOT_OPEN = 1326 +SP_UNDECLARED_VAR = 1327 +SP_WRONG_NO_OF_FETCH_ARGS = 1328 +SP_FETCH_NO_DATA = 1329 +SP_DUP_PARAM = 1330 +SP_DUP_VAR = 1331 +SP_DUP_COND = 1332 +SP_DUP_CURS = 1333 +SP_CANT_ALTER = 1334 +SP_SUBSELECT_NYI = 1335 +STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 +SP_VARCOND_AFTER_CURSHNDLR = 1337 +SP_CURSOR_AFTER_HANDLER = 1338 +SP_CASE_NOT_FOUND = 1339 +FPARSER_TOO_BIG_FILE = 1340 +FPARSER_BAD_HEADER = 1341 +FPARSER_EOF_IN_COMMENT = 1342 +FPARSER_ERROR_IN_PARAMETER = 1343 +FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 +VIEW_NO_EXPLAIN = 1345 +WRONG_OBJECT = 1347 +NONUPDATEABLE_COLUMN = 1348 +VIEW_SELECT_CLAUSE = 1350 +VIEW_SELECT_VARIABLE = 1351 +VIEW_SELECT_TMPTABLE = 1352 +VIEW_WRONG_LIST = 1353 +WARN_VIEW_MERGE = 1354 +WARN_VIEW_WITHOUT_KEY = 1355 +VIEW_INVALID = 1356 +SP_NO_DROP_SP = 1357 +TRG_ALREADY_EXISTS = 1359 +TRG_DOES_NOT_EXIST = 1360 +TRG_ON_VIEW_OR_TEMP_TABLE = 1361 +TRG_CANT_CHANGE_ROW = 1362 +TRG_NO_SUCH_ROW_IN_TRG = 1363 +NO_DEFAULT_FOR_FIELD = 1364 +DIVISION_BY_ZERO = 1365 +TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 +ILLEGAL_VALUE_FOR_TYPE = 1367 +VIEW_NONUPD_CHECK = 1368 +VIEW_CHECK_FAILED = 1369 +PROCACCESS_DENIED_ERROR = 1370 +RELAY_LOG_FAIL = 1371 +UNKNOWN_TARGET_BINLOG = 1373 +IO_ERR_LOG_INDEX_READ = 1374 +BINLOG_PURGE_PROHIBITED = 1375 +FSEEK_FAIL = 1376 +BINLOG_PURGE_FATAL_ERR = 1377 +LOG_IN_USE = 1378 +LOG_PURGE_UNKNOWN_ERR = 1379 +RELAY_LOG_INIT = 1380 +NO_BINARY_LOGGING = 1381 +RESERVED_SYNTAX = 1382 +PS_MANY_PARAM = 1390 +KEY_PART_0 = 1391 +VIEW_CHECKSUM = 1392 +VIEW_MULTIUPDATE = 1393 +VIEW_NO_INSERT_FIELD_LIST = 1394 +VIEW_DELETE_MERGE_VIEW = 1395 +CANNOT_USER = 1396 +XAER_NOTA = 1397 +XAER_INVAL = 1398 +XAER_RMFAIL = 1399 +XAER_OUTSIDE = 1400 +XAER_RMERR = 1401 +XA_RBROLLBACK = 1402 +NONEXISTING_PROC_GRANT = 1403 +PROC_AUTO_GRANT_FAIL = 1404 +PROC_AUTO_REVOKE_FAIL = 1405 +DATA_TOO_LONG = 1406 +SP_BAD_SQLSTATE = 1407 +STARTUP = 1408 +LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 +CANT_CREATE_USER_WITH_GRANT = 1410 +WRONG_VALUE_FOR_TYPE = 1411 +TABLE_DEF_CHANGED = 1412 +SP_DUP_HANDLER = 1413 +SP_NOT_VAR_ARG = 1414 +SP_NO_RETSET = 1415 +CANT_CREATE_GEOMETRY_OBJECT = 1416 +BINLOG_UNSAFE_ROUTINE = 1418 +BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 +STMT_HAS_NO_OPEN_CURSOR = 1421 +COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 +NO_DEFAULT_FOR_VIEW_FIELD = 1423 +SP_NO_RECURSION = 1424 +TOO_BIG_SCALE = 1425 +TOO_BIG_PRECISION = 1426 +M_BIGGER_THAN_D = 1427 +WRONG_LOCK_OF_SYSTEM_TABLE = 1428 +CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 +QUERY_ON_FOREIGN_DATA_SOURCE = 1430 +FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 +FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 +FOREIGN_DATA_STRING_INVALID = 1433 +TRG_IN_WRONG_SCHEMA = 1435 +STACK_OVERRUN_NEED_MORE = 1436 +TOO_LONG_BODY = 1437 +WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 +TOO_BIG_DISPLAYWIDTH = 1439 +XAER_DUPID = 1440 +DATETIME_FUNCTION_OVERFLOW = 1441 +CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 +VIEW_PREVENT_UPDATE = 1443 +PS_NO_RECURSION = 1444 +SP_CANT_SET_AUTOCOMMIT = 1445 +VIEW_FRM_NO_USER = 1447 +VIEW_OTHER_USER = 1448 +NO_SUCH_USER = 1449 +FORBID_SCHEMA_CHANGE = 1450 +ROW_IS_REFERENCED_2 = 1451 +NO_REFERENCED_ROW_2 = 1452 +SP_BAD_VAR_SHADOW = 1453 +TRG_NO_DEFINER = 1454 +OLD_FILE_FORMAT = 1455 +SP_RECURSION_LIMIT = 1456 +SP_WRONG_NAME = 1458 +TABLE_NEEDS_UPGRADE = 1459 +SP_NO_AGGREGATE = 1460 +MAX_PREPARED_STMT_COUNT_REACHED = 1461 +VIEW_RECURSIVE = 1462 +NON_GROUPING_FIELD_USED = 1463 +TABLE_CANT_HANDLE_SPKEYS = 1464 +NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 +REMOVED_SPACES = 1466 +AUTOINC_READ_FAILED = 1467 +USERNAME = 1468 +HOSTNAME = 1469 +WRONG_STRING_LENGTH = 1470 +NON_INSERTABLE_TABLE = 1471 +ADMIN_WRONG_MRG_TABLE = 1472 +TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473 +NAME_BECOMES_EMPTY = 1474 +AMBIGUOUS_FIELD_TERM = 1475 +FOREIGN_SERVER_EXISTS = 1476 +FOREIGN_SERVER_DOESNT_EXIST = 1477 +ILLEGAL_HA_CREATE_OPTION = 1478 +PARTITION_REQUIRES_VALUES_ERROR = 1479 +PARTITION_WRONG_VALUES_ERROR = 1480 +PARTITION_MAXVALUE_ERROR = 1481 +PARTITION_WRONG_NO_PART_ERROR = 1484 +PARTITION_WRONG_NO_SUBPART_ERROR = 1485 +WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486 +FIELD_NOT_FOUND_PART_ERROR = 1488 +INCONSISTENT_PARTITION_INFO_ERROR = 1490 +PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491 +PARTITIONS_MUST_BE_DEFINED_ERROR = 1492 +RANGE_NOT_INCREASING_ERROR = 1493 +INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494 +MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495 +PARTITION_ENTRY_ERROR = 1496 +MIX_HANDLER_ERROR = 1497 +PARTITION_NOT_DEFINED_ERROR = 1498 +TOO_MANY_PARTITIONS_ERROR = 1499 +SUBPARTITION_ERROR = 1500 +CANT_CREATE_HANDLER_FILE = 1501 +BLOB_FIELD_IN_PART_FUNC_ERROR = 1502 +UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503 +NO_PARTS_ERROR = 1504 +PARTITION_MGMT_ON_NONPARTITIONED = 1505 +FOREIGN_KEY_ON_PARTITIONED = 1506 +DROP_PARTITION_NON_EXISTENT = 1507 +DROP_LAST_PARTITION = 1508 +COALESCE_ONLY_ON_HASH_PARTITION = 1509 +REORG_HASH_ONLY_ON_SAME_NO = 1510 +REORG_NO_PARAM_ERROR = 1511 +ONLY_ON_RANGE_LIST_PARTITION = 1512 +ADD_PARTITION_SUBPART_ERROR = 1513 +ADD_PARTITION_NO_NEW_PARTITION = 1514 +COALESCE_PARTITION_NO_PARTITION = 1515 +REORG_PARTITION_NOT_EXIST = 1516 +SAME_NAME_PARTITION = 1517 +NO_BINLOG_ERROR = 1518 +CONSECUTIVE_REORG_PARTITIONS = 1519 +REORG_OUTSIDE_RANGE = 1520 +PARTITION_FUNCTION_FAILURE = 1521 +LIMITED_PART_RANGE = 1523 +PLUGIN_IS_NOT_LOADED = 1524 +WRONG_VALUE = 1525 +NO_PARTITION_FOR_GIVEN_VALUE = 1526 +FILEGROUP_OPTION_ONLY_ONCE = 1527 +CREATE_FILEGROUP_FAILED = 1528 +DROP_FILEGROUP_FAILED = 1529 +TABLESPACE_AUTO_EXTEND_ERROR = 1530 +WRONG_SIZE_NUMBER = 1531 +SIZE_OVERFLOW_ERROR = 1532 +ALTER_FILEGROUP_FAILED = 1533 +BINLOG_ROW_LOGGING_FAILED = 1534 +EVENT_ALREADY_EXISTS = 1537 +EVENT_DOES_NOT_EXIST = 1539 +EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542 +EVENT_ENDS_BEFORE_STARTS = 1543 +EVENT_EXEC_TIME_IN_THE_PAST = 1544 +EVENT_SAME_NAME = 1551 +DROP_INDEX_FK = 1553 +WARN_DEPRECATED_SYNTAX_WITH_VER = 1554 +CANT_LOCK_LOG_TABLE = 1556 +FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557 +COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558 +TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559 +STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560 +PARTITION_NO_TEMPORARY = 1562 +PARTITION_CONST_DOMAIN_ERROR = 1563 +PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564 +NULL_IN_VALUES_LESS_THAN = 1566 +WRONG_PARTITION_NAME = 1567 +CANT_CHANGE_TX_CHARACTERISTICS = 1568 +DUP_ENTRY_AUTOINCREMENT_CASE = 1569 +EVENT_SET_VAR_ERROR = 1571 +PARTITION_MERGE_ERROR = 1572 +BASE64_DECODE_ERROR = 1575 +EVENT_RECURSION_FORBIDDEN = 1576 +ONLY_INTEGERS_ALLOWED = 1578 +UNSUPORTED_LOG_ENGINE = 1579 +BAD_LOG_STATEMENT = 1580 +CANT_RENAME_LOG_TABLE = 1581 +WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582 +WRONG_PARAMETERS_TO_NATIVE_FCT = 1583 +WRONG_PARAMETERS_TO_STORED_FCT = 1584 +NATIVE_FCT_NAME_COLLISION = 1585 +DUP_ENTRY_WITH_KEY_NAME = 1586 +BINLOG_PURGE_EMFILE = 1587 +EVENT_CANNOT_CREATE_IN_THE_PAST = 1588 +EVENT_CANNOT_ALTER_IN_THE_PAST = 1589 +NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591 +BINLOG_UNSAFE_STATEMENT = 1592 +BINLOG_FATAL_ERROR = 1593 +BINLOG_LOGGING_IMPOSSIBLE = 1598 +VIEW_NO_CREATION_CTX = 1599 +VIEW_INVALID_CREATION_CTX = 1600 +TRG_CORRUPTED_FILE = 1602 +TRG_NO_CREATION_CTX = 1603 +TRG_INVALID_CREATION_CTX = 1604 +EVENT_INVALID_CREATION_CTX = 1605 +TRG_CANT_OPEN_TABLE = 1606 +NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609 +SLAVE_CORRUPT_EVENT = 1610 +LOG_PURGE_NO_FILE = 1612 +XA_RBTIMEOUT = 1613 +XA_RBDEADLOCK = 1614 +NEED_REPREPARE = 1615 +WARN_NO_MASTER_INFO = 1617 +WARN_OPTION_IGNORED = 1618 +PLUGIN_DELETE_BUILTIN = 1619 +WARN_PLUGIN_BUSY = 1620 +VARIABLE_IS_READONLY = 1621 +WARN_ENGINE_TRANSACTION_ROLLBACK = 1622 +SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624 +NDB_REPLICATION_SCHEMA_ERROR = 1625 +CONFLICT_FN_PARSE_ERROR = 1626 +EXCEPTIONS_WRITE_ERROR = 1627 +TOO_LONG_TABLE_COMMENT = 1628 +TOO_LONG_FIELD_COMMENT = 1629 +FUNC_INEXISTENT_NAME_COLLISION = 1630 +DATABASE_NAME = 1631 +TABLE_NAME = 1632 +PARTITION_NAME = 1633 +SUBPARTITION_NAME = 1634 +TEMPORARY_NAME = 1635 +RENAMED_NAME = 1636 +TOO_MANY_CONCURRENT_TRXS = 1637 +WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638 +DEBUG_SYNC_TIMEOUT = 1639 +DEBUG_SYNC_HIT_LIMIT = 1640 +DUP_SIGNAL_SET = 1641 +SIGNAL_WARN = 1642 +SIGNAL_NOT_FOUND = 1643 +SIGNAL_EXCEPTION = 1644 +RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645 +SIGNAL_BAD_CONDITION_TYPE = 1646 +WARN_COND_ITEM_TRUNCATED = 1647 +COND_ITEM_TOO_LONG = 1648 +UNKNOWN_LOCALE = 1649 +SLAVE_IGNORE_SERVER_IDS = 1650 +SAME_NAME_PARTITION_FIELD = 1652 +PARTITION_COLUMN_LIST_ERROR = 1653 +WRONG_TYPE_COLUMN_VALUE_ERROR = 1654 +TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655 +MAXVALUE_IN_VALUES_IN = 1656 +TOO_MANY_VALUES_ERROR = 1657 +ROW_SINGLE_PARTITION_FIELD_ERROR = 1658 +FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659 +PARTITION_FIELDS_TOO_LONG = 1660 +BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661 +BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662 +BINLOG_UNSAFE_AND_STMT_ENGINE = 1663 +BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664 +BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665 +BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666 +BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667 +BINLOG_UNSAFE_LIMIT = 1668 +BINLOG_UNSAFE_SYSTEM_TABLE = 1670 +BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671 +BINLOG_UNSAFE_UDF = 1672 +BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673 +BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674 +BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675 +MESSAGE_AND_STATEMENT = 1676 +SLAVE_CANT_CREATE_CONVERSION = 1678 +INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679 +PATH_LENGTH = 1680 +WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681 +WRONG_NATIVE_TABLE_STRUCTURE = 1682 +WRONG_PERFSCHEMA_USAGE = 1683 +WARN_I_S_SKIPPED_TABLE = 1684 +INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685 +STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686 +SPATIAL_MUST_HAVE_GEOM_COL = 1687 +TOO_LONG_INDEX_COMMENT = 1688 +LOCK_ABORTED = 1689 +DATA_OUT_OF_RANGE = 1690 +WRONG_SPVAR_TYPE_IN_LIMIT = 1691 +BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692 +BINLOG_UNSAFE_MIXED_STATEMENT = 1693 +INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694 +STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695 +FAILED_READ_FROM_PAR_FILE = 1696 +VALUES_IS_NOT_INT_TYPE_ERROR = 1697 +ACCESS_DENIED_NO_PASSWORD_ERROR = 1698 +SET_PASSWORD_AUTH_PLUGIN = 1699 +TRUNCATE_ILLEGAL_FK = 1701 +PLUGIN_IS_PERMANENT = 1702 +SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703 +SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704 +STMT_CACHE_FULL = 1705 +MULTI_UPDATE_KEY_CONFLICT = 1706 +TABLE_NEEDS_REBUILD = 1707 +WARN_OPTION_BELOW_LIMIT = 1708 +INDEX_COLUMN_TOO_LONG = 1709 +ERROR_IN_TRIGGER_BODY = 1710 +ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711 +INDEX_CORRUPT = 1712 +UNDO_RECORD_TOO_BIG = 1713 +BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714 +BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715 +BINLOG_UNSAFE_REPLACE_SELECT = 1716 +BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717 +BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718 +BINLOG_UNSAFE_UPDATE_IGNORE = 1719 +PLUGIN_NO_UNINSTALL = 1720 +PLUGIN_NO_INSTALL = 1721 +BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722 +BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723 +BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724 +TABLE_IN_FK_CHECK = 1725 +UNSUPPORTED_ENGINE = 1726 +BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727 +CANNOT_LOAD_FROM_TABLE_V2 = 1728 +MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729 +ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730 +PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731 +PARTITION_EXCHANGE_PART_TABLE = 1732 +PARTITION_EXCHANGE_TEMP_TABLE = 1733 +PARTITION_INSTEAD_OF_SUBPARTITION = 1734 +UNKNOWN_PARTITION = 1735 +TABLES_DIFFERENT_METADATA = 1736 +ROW_DOES_NOT_MATCH_PARTITION = 1737 +BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738 +WARN_INDEX_NOT_APPLICABLE = 1739 +PARTITION_EXCHANGE_FOREIGN_KEY = 1740 +RPL_INFO_DATA_TOO_LONG = 1742 +BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745 +CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746 +PARTITION_CLAUSE_ON_NONPARTITIONED = 1747 +ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748 +CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750 +WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751 +WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752 +MTS_FEATURE_IS_NOT_SUPPORTED = 1753 +MTS_UPDATED_DBS_GREATER_MAX = 1754 +MTS_CANT_PARALLEL = 1755 +MTS_INCONSISTENT_DATA = 1756 +FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757 +DA_INVALID_CONDITION_NUMBER = 1758 +INSECURE_PLAIN_TEXT = 1759 +INSECURE_CHANGE_MASTER = 1760 +FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761 +FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762 +SQLTHREAD_WITH_SECURE_SLAVE = 1763 +TABLE_HAS_NO_FT = 1764 +VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765 +VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766 +SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769 +GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770 +MALFORMED_GTID_SET_SPECIFICATION = 1772 +MALFORMED_GTID_SET_ENCODING = 1773 +MALFORMED_GTID_SPECIFICATION = 1774 +GNO_EXHAUSTED = 1775 +BAD_SLAVE_AUTO_POSITION = 1776 +AUTO_POSITION_REQUIRES_GTID_MODE_NOT_OFF = 1777 +CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778 +GTID_MODE_ON_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779 +CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781 +CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782 +CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783 +GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785 +GTID_UNSAFE_CREATE_SELECT = 1786 +GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787 +GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788 +MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789 +CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790 +UNKNOWN_EXPLAIN_FORMAT = 1791 +CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792 +TOO_LONG_TABLE_PARTITION_COMMENT = 1793 +SLAVE_CONFIGURATION = 1794 +INNODB_FT_LIMIT = 1795 +INNODB_NO_FT_TEMP_TABLE = 1796 +INNODB_FT_WRONG_DOCID_COLUMN = 1797 +INNODB_FT_WRONG_DOCID_INDEX = 1798 +INNODB_ONLINE_LOG_TOO_BIG = 1799 +UNKNOWN_ALTER_ALGORITHM = 1800 +UNKNOWN_ALTER_LOCK = 1801 +MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802 +MTS_RECOVERY_FAILURE = 1803 +MTS_RESET_WORKERS = 1804 +COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805 +SLAVE_SILENT_RETRY_TRANSACTION = 1806 +DISCARD_FK_CHECKS_RUNNING = 1807 +TABLE_SCHEMA_MISMATCH = 1808 +TABLE_IN_SYSTEM_TABLESPACE = 1809 +IO_READ_ERROR = 1810 +IO_WRITE_ERROR = 1811 +TABLESPACE_MISSING = 1812 +TABLESPACE_EXISTS = 1813 +TABLESPACE_DISCARDED = 1814 +INTERNAL_ERROR = 1815 +INNODB_IMPORT_ERROR = 1816 +INNODB_INDEX_CORRUPT = 1817 +INVALID_YEAR_COLUMN_LENGTH = 1818 +NOT_VALID_PASSWORD = 1819 +MUST_CHANGE_PASSWORD = 1820 +FK_NO_INDEX_CHILD = 1821 +FK_NO_INDEX_PARENT = 1822 +FK_FAIL_ADD_SYSTEM = 1823 +FK_CANNOT_OPEN_PARENT = 1824 +FK_INCORRECT_OPTION = 1825 +FK_DUP_NAME = 1826 +PASSWORD_FORMAT = 1827 +FK_COLUMN_CANNOT_DROP = 1828 +FK_COLUMN_CANNOT_DROP_CHILD = 1829 +FK_COLUMN_NOT_NULL = 1830 +DUP_INDEX = 1831 +FK_COLUMN_CANNOT_CHANGE = 1832 +FK_COLUMN_CANNOT_CHANGE_CHILD = 1833 +MALFORMED_PACKET = 1835 +READ_ONLY_MODE = 1836 +GTID_NEXT_TYPE_UNDEFINED_GTID = 1837 +VARIABLE_NOT_SETTABLE_IN_SP = 1838 +CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840 +CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841 +GTID_PURGED_WAS_CHANGED = 1842 +GTID_EXECUTED_WAS_CHANGED = 1843 +BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844 +ALTER_OPERATION_NOT_SUPPORTED = 1845 +ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846 +ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847 +ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848 +ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849 +ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850 +ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851 +ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853 +ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854 +ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855 +ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856 +ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857 +SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858 +DUP_UNKNOWN_IN_INDEX = 1859 +IDENT_CAUSES_TOO_LONG_PATH = 1860 +ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861 +MUST_CHANGE_PASSWORD_LOGIN = 1862 +ROW_IN_WRONG_PARTITION = 1863 +MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864 +BINLOG_LOGICAL_CORRUPTION = 1866 +WARN_PURGE_LOG_IN_USE = 1867 +WARN_PURGE_LOG_IS_ACTIVE = 1868 +AUTO_INCREMENT_CONFLICT = 1869 +WARN_ON_BLOCKHOLE_IN_RBR = 1870 +SLAVE_MI_INIT_REPOSITORY = 1871 +SLAVE_RLI_INIT_REPOSITORY = 1872 +ACCESS_DENIED_CHANGE_USER_ERROR = 1873 +INNODB_READ_ONLY = 1874 +STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875 +STOP_SLAVE_IO_THREAD_TIMEOUT = 1876 +TABLE_CORRUPT = 1877 +TEMP_FILE_WRITE_FAILURE = 1878 +INNODB_FT_AUX_NOT_HEX_ID = 1879 +OLD_TEMPORALS_UPGRADED = 1880 +INNODB_FORCED_RECOVERY = 1881 +AES_INVALID_IV = 1882 +PLUGIN_CANNOT_BE_UNINSTALLED = 1883 +GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID = 1884 +SLAVE_HAS_MORE_GTIDS_THAN_MASTER = 1885 +MISSING_KEY = 1886 +ERROR_LAST = 1973 diff --git a/venv/Lib/site-packages/MySQLdb/constants/FIELD_TYPE.py b/venv/Lib/site-packages/MySQLdb/constants/FIELD_TYPE.py new file mode 100644 index 0000000..3c4eca9 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/FIELD_TYPE.py @@ -0,0 +1,40 @@ +"""MySQL FIELD_TYPE Constants + +These constants represent the various column (field) types that are +supported by MySQL. +""" + +DECIMAL = 0 +TINY = 1 +SHORT = 2 +LONG = 3 +FLOAT = 4 +DOUBLE = 5 +NULL = 6 +TIMESTAMP = 7 +LONGLONG = 8 +INT24 = 9 +DATE = 10 +TIME = 11 +DATETIME = 12 +YEAR = 13 +# NEWDATE = 14 # Internal to MySQL. +VARCHAR = 15 +BIT = 16 +# TIMESTAMP2 = 17 +# DATETIME2 = 18 +# TIME2 = 19 +JSON = 245 +NEWDECIMAL = 246 +ENUM = 247 +SET = 248 +TINY_BLOB = 249 +MEDIUM_BLOB = 250 +LONG_BLOB = 251 +BLOB = 252 +VAR_STRING = 253 +STRING = 254 +GEOMETRY = 255 + +CHAR = TINY +INTERVAL = ENUM diff --git a/venv/Lib/site-packages/MySQLdb/constants/FLAG.py b/venv/Lib/site-packages/MySQLdb/constants/FLAG.py new file mode 100644 index 0000000..00e6c7c --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/FLAG.py @@ -0,0 +1,23 @@ +"""MySQL FLAG Constants + +These flags are used along with the FIELD_TYPE to indicate various +properties of columns in a result set. + +""" + +NOT_NULL = 1 +PRI_KEY = 2 +UNIQUE_KEY = 4 +MULTIPLE_KEY = 8 +BLOB = 16 +UNSIGNED = 32 +ZEROFILL = 64 +BINARY = 128 +ENUM = 256 +AUTO_INCREMENT = 512 +TIMESTAMP = 1024 +SET = 2048 +NUM = 32768 +PART_KEY = 16384 +GROUP = 32768 +UNIQUE = 65536 diff --git a/venv/Lib/site-packages/MySQLdb/constants/__init__.py b/venv/Lib/site-packages/MySQLdb/constants/__init__.py new file mode 100644 index 0000000..0372265 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/constants/__init__.py @@ -0,0 +1 @@ +__all__ = ["CR", "FIELD_TYPE", "CLIENT", "ER", "FLAG"] diff --git a/venv/Lib/site-packages/MySQLdb/converters.py b/venv/Lib/site-packages/MySQLdb/converters.py new file mode 100644 index 0000000..33f22f7 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/converters.py @@ -0,0 +1,139 @@ +"""MySQLdb type conversion module + +This module handles all the type conversions for MySQL. If the default +type conversions aren't what you need, you can make your own. The +dictionary conversions maps some kind of type to a conversion function +which returns the corresponding value: + +Key: FIELD_TYPE.* (from MySQLdb.constants) + +Conversion function: + + Arguments: string + + Returns: Python object + +Key: Python type object (from types) or class + +Conversion function: + + Arguments: Python object of indicated type or class AND + conversion dictionary + + Returns: SQL literal value + + Notes: Most conversion functions can ignore the dictionary, but + it is a required parameter. It is necessary for converting + things like sequences and instances. + +Don't modify conversions if you can avoid it. Instead, make copies +(with the copy() method), modify the copies, and then pass them to +MySQL.connect(). +""" +from decimal import Decimal + +from MySQLdb._mysql import string_literal +from MySQLdb.constants import FIELD_TYPE, FLAG +from MySQLdb.times import ( + Date, + DateTimeType, + DateTime2literal, + DateTimeDeltaType, + DateTimeDelta2literal, + DateTime_or_None, + TimeDelta_or_None, + Date_or_None, +) +from MySQLdb._exceptions import ProgrammingError + +import array + +NoneType = type(None) + +try: + ArrayType = array.ArrayType +except AttributeError: + ArrayType = array.array + + +def Bool2Str(s, d): + return b"1" if s else b"0" + + +def Set2Str(s, d): + # Only support ascii string. Not tested. + return string_literal(",".join(s)) + + +def Thing2Str(s, d): + """Convert something into a string via str().""" + return str(s) + + +def Float2Str(o, d): + s = repr(o) + if s in ("inf", "nan"): + raise ProgrammingError("%s can not be used with MySQL" % s) + if "e" not in s: + s += "e0" + return s + + +def None2NULL(o, d): + """Convert None to NULL.""" + return b"NULL" + + +def Thing2Literal(o, d): + """Convert something into a SQL string literal. If using + MySQL-3.23 or newer, string_literal() is a method of the + _mysql.MYSQL object, and this function will be overridden with + that method when the connection is created.""" + return string_literal(o) + + +def Decimal2Literal(o, d): + return format(o, "f") + + +def array2Str(o, d): + return Thing2Literal(o.tostring(), d) + + +# bytes or str regarding to BINARY_FLAG. +_bytes_or_str = ((FLAG.BINARY, bytes), (None, str)) + +conversions = { + int: Thing2Str, + float: Float2Str, + NoneType: None2NULL, + ArrayType: array2Str, + bool: Bool2Str, + Date: Thing2Literal, + DateTimeType: DateTime2literal, + DateTimeDeltaType: DateTimeDelta2literal, + set: Set2Str, + Decimal: Decimal2Literal, + FIELD_TYPE.TINY: int, + FIELD_TYPE.SHORT: int, + FIELD_TYPE.LONG: int, + FIELD_TYPE.FLOAT: float, + FIELD_TYPE.DOUBLE: float, + FIELD_TYPE.DECIMAL: Decimal, + FIELD_TYPE.NEWDECIMAL: Decimal, + FIELD_TYPE.LONGLONG: int, + FIELD_TYPE.INT24: int, + FIELD_TYPE.YEAR: int, + FIELD_TYPE.TIMESTAMP: DateTime_or_None, + FIELD_TYPE.DATETIME: DateTime_or_None, + FIELD_TYPE.TIME: TimeDelta_or_None, + FIELD_TYPE.DATE: Date_or_None, + FIELD_TYPE.TINY_BLOB: bytes, + FIELD_TYPE.MEDIUM_BLOB: bytes, + FIELD_TYPE.LONG_BLOB: bytes, + FIELD_TYPE.BLOB: bytes, + FIELD_TYPE.STRING: bytes, + FIELD_TYPE.VAR_STRING: bytes, + FIELD_TYPE.VARCHAR: bytes, + FIELD_TYPE.JSON: bytes, +} diff --git a/venv/Lib/site-packages/MySQLdb/cursors.py b/venv/Lib/site-packages/MySQLdb/cursors.py new file mode 100644 index 0000000..f8a4864 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/cursors.py @@ -0,0 +1,489 @@ +"""MySQLdb Cursors + +This module implements Cursors of various types for MySQLdb. By +default, MySQLdb uses the Cursor class. +""" +import re + +from ._exceptions import ProgrammingError + + +#: Regular expression for :meth:`Cursor.executemany`. +#: executemany only supports simple bulk insert. +#: You can use it to load large dataset. +RE_INSERT_VALUES = re.compile( + "".join( + [ + r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)", + r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))", + r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z", + ] + ), + re.IGNORECASE | re.DOTALL, +) + + +class BaseCursor: + """A base for Cursor classes. Useful attributes: + + description + A tuple of DB API 7-tuples describing the columns in + the last executed query; see PEP-249 for details. + + description_flags + Tuple of column flags for last query, one entry per column + in the result set. Values correspond to those in + MySQLdb.constants.FLAG. See MySQL documentation (C API) + for more information. Non-standard extension. + + arraysize + default number of rows fetchmany() will fetch + """ + + #: Max statement size which :meth:`executemany` generates. + #: + #: Max size of allowed statement is max_allowed_packet - packet_header_size. + #: Default value of max_allowed_packet is 1048576. + max_stmt_length = 64 * 1024 + + from ._exceptions import ( + MySQLError, + Warning, + Error, + InterfaceError, + DatabaseError, + DataError, + OperationalError, + IntegrityError, + InternalError, + ProgrammingError, + NotSupportedError, + ) + + connection = None + + def __init__(self, connection): + self.connection = connection + self.description = None + self.description_flags = None + self.rowcount = -1 + self.arraysize = 1 + self._executed = None + + self.lastrowid = None + self._result = None + self.rownumber = None + self._rows = None + + def close(self): + """Close the cursor. No further queries will be possible.""" + try: + if self.connection is None: + return + while self.nextset(): + pass + finally: + self.connection = None + self._result = None + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + del exc_info + self.close() + + def _escape_args(self, args, conn): + encoding = conn.encoding + literal = conn.literal + + def ensure_bytes(x): + if isinstance(x, str): + return x.encode(encoding) + elif isinstance(x, tuple): + return tuple(map(ensure_bytes, x)) + elif isinstance(x, list): + return list(map(ensure_bytes, x)) + return x + + if isinstance(args, (tuple, list)): + ret = tuple(literal(ensure_bytes(arg)) for arg in args) + elif isinstance(args, dict): + ret = { + ensure_bytes(key): literal(ensure_bytes(val)) + for (key, val) in args.items() + } + else: + # If it's not a dictionary let's try escaping it anyways. + # Worst case it will throw a Value error + ret = literal(ensure_bytes(args)) + + ensure_bytes = None # break circular reference + return ret + + def _check_executed(self): + if not self._executed: + raise ProgrammingError("execute() first") + + def nextset(self): + """Advance to the next result set. + + Returns None if there are no more result sets. + """ + if self._executed: + self.fetchall() + + db = self._get_db() + nr = db.next_result() + if nr == -1: + return None + self._do_get_result(db) + self._post_get_result() + return 1 + + def _do_get_result(self, db): + self._result = result = self._get_result() + if result is None: + self.description = self.description_flags = None + else: + self.description = result.describe() + self.description_flags = result.field_flags() + + self.rowcount = db.affected_rows() + self.rownumber = 0 + self.lastrowid = db.insert_id() + + def _post_get_result(self): + pass + + def setinputsizes(self, *args): + """Does nothing, required by DB API.""" + + def setoutputsizes(self, *args): + """Does nothing, required by DB API.""" + + def _get_db(self): + con = self.connection + if con is None: + raise ProgrammingError("cursor closed") + return con + + def execute(self, query, args=None): + """Execute a query. + + query -- string, query to execute on server + args -- optional sequence or mapping, parameters to use with query. + + Note: If args is a sequence, then %s must be used as the + parameter placeholder in the query. If a mapping is used, + %(key)s must be used as the placeholder. + + Returns integer represents rows affected, if any + """ + while self.nextset(): + pass + db = self._get_db() + + if isinstance(query, str): + query = query.encode(db.encoding) + + if args is not None: + if isinstance(args, dict): + nargs = {} + for key, item in args.items(): + if isinstance(key, str): + key = key.encode(db.encoding) + nargs[key] = db.literal(item) + args = nargs + else: + args = tuple(map(db.literal, args)) + try: + query = query % args + except TypeError as m: + raise ProgrammingError(str(m)) + + assert isinstance(query, (bytes, bytearray)) + res = self._query(query) + return res + + def executemany(self, query, args): + # type: (str, list) -> int + """Execute a multi-row query. + + :param query: query to execute on server + :param args: Sequence of sequences or mappings. It is used as parameter. + :return: Number of rows affected, if any. + + This method improves performance on multiple-row INSERT and + REPLACE. Otherwise it is equivalent to looping over args with + execute(). + """ + if not args: + return + + m = RE_INSERT_VALUES.match(query) + if m: + q_prefix = m.group(1) % () + q_values = m.group(2).rstrip() + q_postfix = m.group(3) or "" + assert q_values[0] == "(" and q_values[-1] == ")" + return self._do_execute_many( + q_prefix, + q_values, + q_postfix, + args, + self.max_stmt_length, + self._get_db().encoding, + ) + + self.rowcount = sum(self.execute(query, arg) for arg in args) + return self.rowcount + + def _do_execute_many( + self, prefix, values, postfix, args, max_stmt_length, encoding + ): + conn = self._get_db() + escape = self._escape_args + if isinstance(prefix, str): + prefix = prefix.encode(encoding) + if isinstance(values, str): + values = values.encode(encoding) + if isinstance(postfix, str): + postfix = postfix.encode(encoding) + sql = bytearray(prefix) + args = iter(args) + v = values % escape(next(args), conn) + sql += v + rows = 0 + for arg in args: + v = values % escape(arg, conn) + if len(sql) + len(v) + len(postfix) + 1 > max_stmt_length: + rows += self.execute(sql + postfix) + sql = bytearray(prefix) + else: + sql += b"," + sql += v + rows += self.execute(sql + postfix) + self.rowcount = rows + return rows + + def callproc(self, procname, args=()): + """Execute stored procedure procname with args + + procname -- string, name of procedure to execute on server + + args -- Sequence of parameters to use with procedure + + Returns the original args. + + Compatibility warning: PEP-249 specifies that any modified + parameters must be returned. This is currently impossible + as they are only available by storing them in a server + variable and then retrieved by a query. Since stored + procedures return zero or more result sets, there is no + reliable way to get at OUT or INOUT parameters via callproc. + The server variables are named @_procname_n, where procname + is the parameter above and n is the position of the parameter + (from zero). Once all result sets generated by the procedure + have been fetched, you can issue a SELECT @_procname_0, ... + query using .execute() to get any OUT or INOUT values. + + Compatibility warning: The act of calling a stored procedure + itself creates an empty result set. This appears after any + result sets generated by the procedure. This is non-standard + behavior with respect to the DB-API. Be sure to use nextset() + to advance through all result sets; otherwise you may get + disconnected. + """ + db = self._get_db() + if isinstance(procname, str): + procname = procname.encode(db.encoding) + if args: + fmt = b"@_" + procname + b"_%d=%s" + q = b"SET %s" % b",".join( + fmt % (index, db.literal(arg)) for index, arg in enumerate(args) + ) + self._query(q) + self.nextset() + + q = b"CALL %s(%s)" % ( + procname, + b",".join([b"@_%s_%d" % (procname, i) for i in range(len(args))]), + ) + self._query(q) + return args + + def _query(self, q): + db = self._get_db() + self._result = None + db.query(q) + self._do_get_result(db) + self._post_get_result() + self._executed = q + return self.rowcount + + def _fetch_row(self, size=1): + if not self._result: + return () + return self._result.fetch_row(size, self._fetch_type) + + def __iter__(self): + return iter(self.fetchone, None) + + Warning = Warning + Error = Error + InterfaceError = InterfaceError + DatabaseError = DatabaseError + DataError = DataError + OperationalError = OperationalError + IntegrityError = IntegrityError + InternalError = InternalError + ProgrammingError = ProgrammingError + NotSupportedError = NotSupportedError + + +class CursorStoreResultMixIn: + """This is a MixIn class which causes the entire result set to be + stored on the client side, i.e. it uses mysql_store_result(). If the + result set can be very large, consider adding a LIMIT clause to your + query, or using CursorUseResultMixIn instead.""" + + def _get_result(self): + return self._get_db().store_result() + + def _post_get_result(self): + self._rows = self._fetch_row(0) + self._result = None + + def fetchone(self): + """Fetches a single row from the cursor. None indicates that + no more rows are available.""" + self._check_executed() + if self.rownumber >= len(self._rows): + return None + result = self._rows[self.rownumber] + self.rownumber = self.rownumber + 1 + return result + + def fetchmany(self, size=None): + """Fetch up to size rows from the cursor. Result set may be smaller + than size. If size is not defined, cursor.arraysize is used.""" + self._check_executed() + end = self.rownumber + (size or self.arraysize) + result = self._rows[self.rownumber : end] + self.rownumber = min(end, len(self._rows)) + return result + + def fetchall(self): + """Fetches all available rows from the cursor.""" + self._check_executed() + if self.rownumber: + result = self._rows[self.rownumber :] + else: + result = self._rows + self.rownumber = len(self._rows) + return result + + def scroll(self, value, mode="relative"): + """Scroll the cursor in the result set to a new position according + to mode. + + If mode is 'relative' (default), value is taken as offset to + the current position in the result set, if set to 'absolute', + value states an absolute target position.""" + self._check_executed() + if mode == "relative": + r = self.rownumber + value + elif mode == "absolute": + r = value + else: + raise ProgrammingError("unknown scroll mode %s" % repr(mode)) + if r < 0 or r >= len(self._rows): + raise IndexError("out of range") + self.rownumber = r + + def __iter__(self): + self._check_executed() + result = self.rownumber and self._rows[self.rownumber :] or self._rows + return iter(result) + + +class CursorUseResultMixIn: + + """This is a MixIn class which causes the result set to be stored + in the server and sent row-by-row to client side, i.e. it uses + mysql_use_result(). You MUST retrieve the entire result set and + close() the cursor before additional queries can be performed on + the connection.""" + + def _get_result(self): + return self._get_db().use_result() + + def fetchone(self): + """Fetches a single row from the cursor.""" + self._check_executed() + r = self._fetch_row(1) + if not r: + return None + self.rownumber = self.rownumber + 1 + return r[0] + + def fetchmany(self, size=None): + """Fetch up to size rows from the cursor. Result set may be smaller + than size. If size is not defined, cursor.arraysize is used.""" + self._check_executed() + r = self._fetch_row(size or self.arraysize) + self.rownumber = self.rownumber + len(r) + return r + + def fetchall(self): + """Fetches all available rows from the cursor.""" + self._check_executed() + r = self._fetch_row(0) + self.rownumber = self.rownumber + len(r) + return r + + def __iter__(self): + return self + + def next(self): + row = self.fetchone() + if row is None: + raise StopIteration + return row + + __next__ = next + + +class CursorTupleRowsMixIn: + """This is a MixIn class that causes all rows to be returned as tuples, + which is the standard form required by DB API.""" + + _fetch_type = 0 + + +class CursorDictRowsMixIn: + """This is a MixIn class that causes all rows to be returned as + dictionaries. This is a non-standard feature.""" + + _fetch_type = 1 + + +class Cursor(CursorStoreResultMixIn, CursorTupleRowsMixIn, BaseCursor): + """This is the standard Cursor class that returns rows as tuples + and stores the result set in the client.""" + + +class DictCursor(CursorStoreResultMixIn, CursorDictRowsMixIn, BaseCursor): + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the client.""" + + +class SSCursor(CursorUseResultMixIn, CursorTupleRowsMixIn, BaseCursor): + """This is a Cursor class that returns rows as tuples and stores + the result set in the server.""" + + +class SSDictCursor(CursorUseResultMixIn, CursorDictRowsMixIn, BaseCursor): + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the server.""" diff --git a/venv/Lib/site-packages/MySQLdb/release.py b/venv/Lib/site-packages/MySQLdb/release.py new file mode 100644 index 0000000..38b522c --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/release.py @@ -0,0 +1,4 @@ + +__author__ = "Inada Naoki " +version_info = (2,1,0,'final',0) +__version__ = "2.1.0" diff --git a/venv/Lib/site-packages/MySQLdb/times.py b/venv/Lib/site-packages/MySQLdb/times.py new file mode 100644 index 0000000..915d827 --- /dev/null +++ b/venv/Lib/site-packages/MySQLdb/times.py @@ -0,0 +1,150 @@ +"""times module + +This module provides some Date and Time classes for dealing with MySQL data. + +Use Python datetime module to handle date and time columns. +""" +from time import localtime +from datetime import date, datetime, time, timedelta +from MySQLdb._mysql import string_literal + +Date = date +Time = time +TimeDelta = timedelta +Timestamp = datetime + +DateTimeDeltaType = timedelta +DateTimeType = datetime + + +def DateFromTicks(ticks): + """Convert UNIX ticks into a date instance.""" + return date(*localtime(ticks)[:3]) + + +def TimeFromTicks(ticks): + """Convert UNIX ticks into a time instance.""" + return time(*localtime(ticks)[3:6]) + + +def TimestampFromTicks(ticks): + """Convert UNIX ticks into a datetime instance.""" + return datetime(*localtime(ticks)[:6]) + + +format_TIME = format_DATE = str + + +def format_TIMEDELTA(v): + seconds = int(v.seconds) % 60 + minutes = int(v.seconds // 60) % 60 + hours = int(v.seconds // 3600) % 24 + return "%d %d:%d:%d" % (v.days, hours, minutes, seconds) + + +def format_TIMESTAMP(d): + """ + :type d: datetime.datetime + """ + if d.microsecond: + fmt = " ".join( + [ + "{0.year:04}-{0.month:02}-{0.day:02}", + "{0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}", + ] + ) + else: + fmt = " ".join( + [ + "{0.year:04}-{0.month:02}-{0.day:02}", + "{0.hour:02}:{0.minute:02}:{0.second:02}", + ] + ) + return fmt.format(d) + + +def DateTime_or_None(s): + try: + if len(s) < 11: + return Date_or_None(s) + + micros = s[20:] + + if len(micros) == 0: + # 12:00:00 + micros = 0 + elif len(micros) < 7: + # 12:00:00.123456 + micros = int(micros) * 10 ** (6 - len(micros)) + else: + return None + + return datetime( + int(s[:4]), # year + int(s[5:7]), # month + int(s[8:10]), # day + int(s[11:13] or 0), # hour + int(s[14:16] or 0), # minute + int(s[17:19] or 0), # second + micros, # microsecond + ) + except ValueError: + return None + + +def TimeDelta_or_None(s): + try: + h, m, s = s.split(":") + if "." in s: + s, ms = s.split(".") + ms = ms.ljust(6, "0") + else: + ms = 0 + if h[0] == "-": + negative = True + else: + negative = False + h, m, s, ms = abs(int(h)), int(m), int(s), int(ms) + td = timedelta(hours=h, minutes=m, seconds=s, microseconds=ms) + if negative: + return -td + else: + return td + except ValueError: + # unpacking or int/float conversion failed + return None + + +def Time_or_None(s): + try: + h, m, s = s.split(":") + if "." in s: + s, ms = s.split(".") + ms = ms.ljust(6, "0") + else: + ms = 0 + h, m, s, ms = int(h), int(m), int(s), int(ms) + return time(hour=h, minute=m, second=s, microsecond=ms) + except ValueError: + return None + + +def Date_or_None(s): + try: + return date( + int(s[:4]), + int(s[5:7]), + int(s[8:10]), + ) # year # month # day + except ValueError: + return None + + +def DateTime2literal(d, c): + """Format a DateTime object as an ISO timestamp.""" + return string_literal(format_TIMESTAMP(d)) + + +def DateTimeDelta2literal(d, c): + """Format a DateTimeDelta object as a time.""" + return string_literal(format_TIMEDELTA(d)) diff --git a/venv/Lib/site-packages/PIL/BdfFontFile.py b/venv/Lib/site-packages/PIL/BdfFontFile.py new file mode 100644 index 0000000..102b72e --- /dev/null +++ b/venv/Lib/site-packages/PIL/BdfFontFile.py @@ -0,0 +1,110 @@ +# +# The Python Imaging Library +# $Id$ +# +# bitmap distribution font (bdf) file parser +# +# history: +# 1996-05-16 fl created (as bdf2pil) +# 1997-08-25 fl converted to FontFile driver +# 2001-05-25 fl removed bogus __init__ call +# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) +# 2003-04-22 fl more robustification (from Graham Dumpleton) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +""" +Parse X Bitmap Distribution Format (BDF) +""" + + +from . import FontFile, Image + +bdf_slant = { + "R": "Roman", + "I": "Italic", + "O": "Oblique", + "RI": "Reverse Italic", + "RO": "Reverse Oblique", + "OT": "Other", +} + +bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"} + + +def bdf_char(f): + # skip to STARTCHAR + while True: + s = f.readline() + if not s: + return None + if s[:9] == b"STARTCHAR": + break + id = s[9:].strip().decode("ascii") + + # load symbol properties + props = {} + while True: + s = f.readline() + if not s or s[:6] == b"BITMAP": + break + i = s.find(b" ") + props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") + + # load bitmap + bitmap = [] + while True: + s = f.readline() + if not s or s[:7] == b"ENDCHAR": + break + bitmap.append(s[:-1]) + bitmap = b"".join(bitmap) + + [x, y, l, d] = [int(p) for p in props["BBX"].split()] + [dx, dy] = [int(p) for p in props["DWIDTH"].split()] + + bbox = (dx, dy), (l, -d - y, x + l, -d), (0, 0, x, y) + + try: + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (x, y)) + + return id, int(props["ENCODING"]), bbox, im + + +class BdfFontFile(FontFile.FontFile): + """Font file plugin for the X11 BDF format.""" + + def __init__(self, fp): + super().__init__() + + s = fp.readline() + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") + + props = {} + comments = [] + + while True: + s = fp.readline() + if not s or s[:13] == b"ENDPROPERTIES": + break + i = s.find(b" ") + props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") + if s[:i] in [b"COMMENT", b"COPYRIGHT"]: + if s.find(b"LogicalFontDescription") < 0: + comments.append(s[i + 1 : -1].decode("ascii")) + + while True: + c = bdf_char(fp) + if not c: + break + id, ch, (xy, dst, src), im = c + if 0 <= ch < len(self.glyph): + self.glyph[ch] = xy, dst, src, im diff --git a/venv/Lib/site-packages/PIL/BlpImagePlugin.py b/venv/Lib/site-packages/PIL/BlpImagePlugin.py new file mode 100644 index 0000000..ecd3da5 --- /dev/null +++ b/venv/Lib/site-packages/PIL/BlpImagePlugin.py @@ -0,0 +1,497 @@ +""" +Blizzard Mipmap Format (.blp) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +BLP1 files, used mostly in Warcraft III, are not fully supported. +All types of BLP2 files used in World of Warcraft are supported. + +The BLP file structure consists of a header, up to 16 mipmaps of the +texture + +Texture sizes must be powers of two, though the two dimensions do +not have to be equal; 512x256 is valid, but 512x200 is not. +The first mipmap (mipmap #0) is the full size image; each subsequent +mipmap halves both dimensions. The final mipmap should be 1x1. + +BLP files come in many different flavours: +* JPEG-compressed (type == 0) - only supported for BLP1. +* RAW images (type == 1, encoding == 1). Each mipmap is stored as an + array of 8-bit values, one per pixel, left to right, top to bottom. + Each value is an index to the palette. +* DXT-compressed (type == 1, encoding == 2): +- DXT1 compression is used if alpha_encoding == 0. + - An additional alpha bit is used if alpha_depth == 1. + - DXT3 compression is used if alpha_encoding == 1. + - DXT5 compression is used if alpha_encoding == 7. +""" + +import os +import struct +import warnings +from enum import IntEnum +from io import BytesIO + +from . import Image, ImageFile + + +class Format(IntEnum): + JPEG = 0 + + +class Encoding(IntEnum): + UNCOMPRESSED = 1 + DXT = 2 + UNCOMPRESSED_RAW_BGRA = 3 + + +class AlphaEncoding(IntEnum): + DXT1 = 0 + DXT3 = 1 + DXT5 = 7 + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in { + Format: "BLP_FORMAT_", + Encoding: "BLP_ENCODING_", + AlphaEncoding: "BLP_ALPHA_ENCODING_", + }.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +def unpack_565(i): + return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3) + + +def decode_dxt1(data, alpha=False): + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 8 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + # Decode next 8-byte block. + idx = block * 8 + color0, color1, bits = struct.unpack_from("> 2 + + a = 0xFF + if control == 0: + r, g, b = r0, g0, b0 + elif control == 1: + r, g, b = r1, g1, b1 + elif control == 2: + if color0 > color1: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + else: + r = (r0 + r1) // 2 + g = (g0 + g1) // 2 + b = (b0 + b1) // 2 + elif control == 3: + if color0 > color1: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + else: + r, g, b, a = 0, 0, 0, 0 + + if alpha: + ret[j].extend([r, g, b, a]) + else: + ret[j].extend([r, g, b]) + + return ret + + +def decode_dxt3(data): + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + idx = block * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + bits = struct.unpack_from("<8B", block) + color0, color1 = struct.unpack_from(">= 4 + else: + high = True + a &= 0xF + a *= 17 # We get a value between 0 and 15 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +def decode_dxt5(data): + """ + input: one "row" of data (i.e. will produce 4 * width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block in range(blocks): + idx = block * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 + elif alphacode_index == 15: + alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) + else: # alphacode_index >= 18 and alphacode_index <= 45 + alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 + + if alphacode == 0: + a = a0 + elif alphacode == 1: + a = a1 + elif a0 > a1: + a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 + elif alphacode == 6: + a = 0 + elif alphacode == 7: + a = 255 + else: + a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +class BLPFormatError(NotImplementedError): + pass + + +def _accept(prefix): + return prefix[:4] in (b"BLP1", b"BLP2") + + +class BlpImageFile(ImageFile.ImageFile): + """ + Blizzard Mipmap Format + """ + + format = "BLP" + format_description = "Blizzard Mipmap Format" + + def _open(self): + self.magic = self.fp.read(4) + + self.fp.seek(5, os.SEEK_CUR) + (self._blp_alpha_depth,) = struct.unpack(" mode, rawmode + 1: ("P", "P;1"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX"), +} + + +def _accept(prefix): + return prefix[:2] == b"BM" + + +def _dib_accept(prefix): + return i32(prefix) in [12, 40, 64, 108, 124] + + +# ============================================================================= +# Image plugin for the Windows BMP format. +# ============================================================================= +class BmpImageFile(ImageFile.ImageFile): + """Image plugin for the Windows Bitmap format (BMP)""" + + # ------------------------------------------------------------- Description + format_description = "Windows Bitmap" + format = "BMP" + + # -------------------------------------------------- BMP Compression values + COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} + for k, v in COMPRESSIONS.items(): + vars()[k] = v + + def _bitmap(self, header=0, offset=0): + """Read relevant info about the BMP""" + read, seek = self.fp.read, self.fp.seek + if header: + seek(header) + file_info = {} + # read bmp header size @offset 14 (this is part of the header size) + file_info["header_size"] = i32(read(4)) + file_info["direction"] = -1 + + # -------------------- If requested, read header at a specific position + # read the rest of the bmp header, without its size + header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) + + # -------------------------------------------------- IBM OS/2 Bitmap v1 + # ----- This format has different offsets because of width/height types + if file_info["header_size"] == 12: + file_info["width"] = i16(header_data, 0) + file_info["height"] = i16(header_data, 2) + file_info["planes"] = i16(header_data, 4) + file_info["bits"] = i16(header_data, 6) + file_info["compression"] = self.RAW + file_info["palette_padding"] = 3 + + # --------------------------------------------- Windows Bitmap v2 to v5 + # v3, OS/2 v2, v4, v5 + elif file_info["header_size"] in (40, 64, 108, 124): + file_info["y_flip"] = header_data[7] == 0xFF + file_info["direction"] = 1 if file_info["y_flip"] else -1 + file_info["width"] = i32(header_data, 0) + file_info["height"] = ( + i32(header_data, 4) + if not file_info["y_flip"] + else 2**32 - i32(header_data, 4) + ) + file_info["planes"] = i16(header_data, 8) + file_info["bits"] = i16(header_data, 10) + file_info["compression"] = i32(header_data, 12) + # byte size of pixel data + file_info["data_size"] = i32(header_data, 16) + file_info["pixels_per_meter"] = ( + i32(header_data, 20), + i32(header_data, 24), + ) + file_info["colors"] = i32(header_data, 28) + file_info["palette_padding"] = 4 + self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) + if file_info["compression"] == self.BITFIELDS: + if len(header_data) >= 52: + for idx, mask in enumerate( + ["r_mask", "g_mask", "b_mask", "a_mask"] + ): + file_info[mask] = i32(header_data, 36 + idx * 4) + else: + # 40 byte headers only have the three components in the + # bitfields masks, ref: + # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx + # See also + # https://github.com/python-pillow/Pillow/issues/1293 + # There is a 4th component in the RGBQuad, in the alpha + # location, but it is listed as a reserved component, + # and it is not generally an alpha channel + file_info["a_mask"] = 0x0 + for mask in ["r_mask", "g_mask", "b_mask"]: + file_info[mask] = i32(read(4)) + file_info["rgb_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + ) + file_info["rgba_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + file_info["a_mask"], + ) + else: + raise OSError(f"Unsupported BMP header type ({file_info['header_size']})") + + # ------------------ Special case : header is reported 40, which + # ---------------------- is shorter than real size for bpp >= 16 + self._size = file_info["width"], file_info["height"] + + # ------- If color count was not found in the header, compute from bits + file_info["colors"] = ( + file_info["colors"] + if file_info.get("colors", 0) + else (1 << file_info["bits"]) + ) + if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8: + offset += 4 * file_info["colors"] + + # ---------------------- Check bit depth for unusual unsupported values + self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None)) + if self.mode is None: + raise OSError(f"Unsupported BMP pixel depth ({file_info['bits']})") + + # ---------------- Process BMP with Bitfields compression (not palette) + decoder_name = "raw" + if file_info["compression"] == self.BITFIELDS: + SUPPORTED = { + 32: [ + (0xFF0000, 0xFF00, 0xFF, 0x0), + (0xFF0000, 0xFF00, 0xFF, 0xFF000000), + (0xFF, 0xFF00, 0xFF0000, 0xFF000000), + (0x0, 0x0, 0x0, 0x0), + (0xFF000000, 0xFF0000, 0xFF00, 0x0), + ], + 24: [(0xFF0000, 0xFF00, 0xFF)], + 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], + } + MASK_MODES = { + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", + (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", + (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", + (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", + } + if file_info["bits"] in SUPPORTED: + if ( + file_info["bits"] == 32 + and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]] + ): + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] + self.mode = "RGBA" if "A" in raw_mode else self.mode + elif ( + file_info["bits"] in (24, 16) + and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]] + ): + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] + else: + raise OSError("Unsupported BMP bitfields layout") + else: + raise OSError("Unsupported BMP bitfields layout") + elif file_info["compression"] == self.RAW: + if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset + raw_mode, self.mode = "BGRA", "RGBA" + elif file_info["compression"] == self.RLE8: + decoder_name = "bmp_rle" + else: + raise OSError(f"Unsupported BMP compression ({file_info['compression']})") + + # --------------- Once the header is processed, process the palette/LUT + if self.mode == "P": # Paletted for 1, 4 and 8 bit images + + # ---------------------------------------------------- 1-bit images + if not (0 < file_info["colors"] <= 65536): + raise OSError(f"Unsupported BMP Palette size ({file_info['colors']})") + else: + padding = file_info["palette_padding"] + palette = read(padding * file_info["colors"]) + greyscale = True + indices = ( + (0, 255) + if file_info["colors"] == 2 + else list(range(file_info["colors"])) + ) + + # ----------------- Check if greyscale and ignore palette if so + for ind, val in enumerate(indices): + rgb = palette[ind * padding : ind * padding + 3] + if rgb != o8(val) * 3: + greyscale = False + + # ------- If all colors are grey, white or black, ditch palette + if greyscale: + self.mode = "1" if file_info["colors"] == 2 else "L" + raw_mode = self.mode + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGRX" if padding == 4 else "BGR", palette + ) + + # ---------------------------- Finally set the tile data for the plugin + self.info["compression"] = file_info["compression"] + self.tile = [ + ( + decoder_name, + (0, 0, file_info["width"], file_info["height"]), + offset or self.fp.tell(), + ( + raw_mode, + ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3), + file_info["direction"], + ), + ) + ] + + def _open(self): + """Open file, check magic number and read header""" + # read 14 bytes: magic number, filesize, reserved, header final offset + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if not _accept(head_data): + raise SyntaxError("Not a BMP file") + # read the start position of the BMP image data (u32) + offset = i32(head_data, 10) + # load bitmap information (offset=raster info) + self._bitmap(offset=offset) + + +class BmpRleDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + data = bytearray() + x = 0 + while len(data) < self.state.xsize * self.state.ysize: + pixels = self.fd.read(1) + byte = self.fd.read(1) + if not pixels or not byte: + break + num_pixels = pixels[0] + if num_pixels: + # encoded mode + if x + num_pixels > self.state.xsize: + # Too much data for row + num_pixels = max(0, self.state.xsize - x) + data += byte * num_pixels + x += num_pixels + else: + if byte[0] == 0: + # end of line + while len(data) % self.state.xsize != 0: + data += b"\x00" + x = 0 + elif byte[0] == 1: + # end of bitmap + break + elif byte[0] == 2: + # delta + bytes_read = self.fd.read(2) + if len(bytes_read) < 2: + break + right, up = self.fd.read(2) + data += b"\x00" * (right + up * self.state.xsize) + x = len(data) % self.state.xsize + else: + # absolute mode + bytes_read = self.fd.read(byte[0]) + data += bytes_read + if len(bytes_read) < byte[0]: + break + x += byte[0] + + # align to 16-bit word boundary + if self.fd.tell() % 2 != 0: + self.fd.seek(1, os.SEEK_CUR) + self.set_as_raw(bytes(data), ("P", 0, self.args[-1])) + return -1, 0 + + +# ============================================================================= +# Image plugin for the DIB format (BMP alias) +# ============================================================================= +class DibImageFile(BmpImageFile): + + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self): + self._bitmap() + + +# +# -------------------------------------------------------------------- +# Write BMP file + + +SAVE = { + "1": ("1", 1, 2), + "L": ("L", 8, 256), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), + "RGBA": ("BGRA", 32, 0), +} + + +def _dib_save(im, fp, filename): + _save(im, fp, filename, False) + + +def _save(im, fp, filename, bitmap_header=True): + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as BMP") from e + + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi)) + + stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) + header = 40 # or 64 for OS/2 version 2 + image = stride * im.size[1] + + # bitmap header + if bitmap_header: + offset = 14 + header + colors * 4 + file_size = offset + image + if file_size > 2**32 - 1: + raise ValueError("File size is too large for the BMP format") + fp.write( + b"BM" # file type (magic) + + o32(file_size) # file size + + o32(0) # reserved + + o32(offset) # image data offset + ) + + # bitmap info header + fp.write( + o32(header) # info header size + + o32(im.size[0]) # width + + o32(im.size[1]) # height + + o16(1) # planes + + o16(bits) # depth + + o32(0) # compression (0=uncompressed) + + o32(image) # size of bitmap + + o32(ppm[0]) # resolution + + o32(ppm[1]) # resolution + + o32(colors) # colors used + + o32(colors) # colors important + ) + + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) + + if im.mode == "1": + for i in (0, 255): + fp.write(o8(i) * 4) + elif im.mode == "L": + for i in range(256): + fp.write(o8(i) * 4) + elif im.mode == "P": + fp.write(im.im.getpalette("RGB", "BGRX")) + + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))]) + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(BmpImageFile.format, BmpImageFile, _accept) +Image.register_save(BmpImageFile.format, _save) + +Image.register_extension(BmpImageFile.format, ".bmp") + +Image.register_mime(BmpImageFile.format, "image/bmp") + +Image.register_decoder("bmp_rle", BmpRleDecoder) + +Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) +Image.register_save(DibImageFile.format, _dib_save) + +Image.register_extension(DibImageFile.format, ".dib") + +Image.register_mime(DibImageFile.format, "image/bmp") diff --git a/venv/Lib/site-packages/PIL/BufrStubImagePlugin.py b/venv/Lib/site-packages/PIL/BufrStubImagePlugin.py new file mode 100644 index 0000000..9510f73 --- /dev/null +++ b/venv/Lib/site-packages/PIL/BufrStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# BUFR stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific BUFR image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" + + +class BufrStubImageFile(ImageFile.StubImageFile): + + format = "BUFR" + format_description = "BUFR" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(4)): + raise SyntaxError("Not a BUFR file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr(_handler, "save"): + raise OSError("BUFR save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) +Image.register_save(BufrStubImageFile.format, _save) + +Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/venv/Lib/site-packages/PIL/ContainerIO.py b/venv/Lib/site-packages/PIL/ContainerIO.py new file mode 100644 index 0000000..45e80b3 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ContainerIO.py @@ -0,0 +1,120 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a class to read from a container file +# +# History: +# 1995-06-18 fl Created +# 1995-09-07 fl Added readline(), readlines() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +import io + + +class ContainerIO: + """ + A file object that provides read access to a part of an existing + file (for example a TAR file). + """ + + def __init__(self, file, offset, length): + """ + Create file object. + + :param file: Existing file. + :param offset: Start of region, in bytes. + :param length: Size of region, in bytes. + """ + self.fh = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self): + return False + + def seek(self, offset, mode=io.SEEK_SET): + """ + Move file pointer. + + :param offset: Offset in bytes. + :param mode: Starting position. Use 0 for beginning of region, 1 + for current offset, and 2 for end of region. You cannot move + the pointer outside the defined region. + """ + if mode == 1: + self.pos = self.pos + offset + elif mode == 2: + self.pos = self.length + offset + else: + self.pos = offset + # clamp + self.pos = max(0, min(self.pos, self.length)) + self.fh.seek(self.offset + self.pos) + + def tell(self): + """ + Get current file pointer. + + :returns: Offset from start of region, in bytes. + """ + return self.pos + + def read(self, n=0): + """ + Read data. + + :param n: Number of bytes to read. If omitted or zero, + read until end of region. + :returns: An 8-bit string. + """ + if n: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if not n: # EOF + return b"" if "b" in self.fh.mode else "" + self.pos = self.pos + n + return self.fh.read(n) + + def readline(self): + """ + Read a line of text. + + :returns: An 8-bit string. + """ + s = b"" if "b" in self.fh.mode else "" + newline_character = b"\n" if "b" in self.fh.mode else "\n" + while True: + c = self.read(1) + if not c: + break + s = s + c + if c == newline_character: + break + return s + + def readlines(self): + """ + Read multiple lines of text. + + :returns: A list of 8-bit strings. + """ + lines = [] + while True: + s = self.readline() + if not s: + break + lines.append(s) + return lines diff --git a/venv/Lib/site-packages/PIL/CurImagePlugin.py b/venv/Lib/site-packages/PIL/CurImagePlugin.py new file mode 100644 index 0000000..42af5ca --- /dev/null +++ b/venv/Lib/site-packages/PIL/CurImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Cursor support for PIL +# +# notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# history: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# +from . import BmpImagePlugin, Image +from ._binary import i16le as i16 +from ._binary import i32le as i32 + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:4] == b"\0\0\2\0" + + +## +# Image plugin for Windows Cursor files. + + +class CurImageFile(BmpImagePlugin.BmpImageFile): + + format = "CUR" + format_description = "Windows Cursor" + + def _open(self): + + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError("not a CUR file") + + # pick the largest cursor in the file + m = b"" + for i in range(i16(s, 4)): + s = self.fp.read(16) + if not m: + m = s + elif s[0] > m[0] and s[1] > m[1]: + m = s + if not m: + raise TypeError("No cursors were found") + + # load as bitmap + self._bitmap(i32(m, 12) + offset) + + # patch up the bitmap height + self._size = self.size[0], self.size[1] // 2 + d, e, o, a = self.tile[0] + self.tile[0] = d, (0, 0) + self.size, o, a + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open(CurImageFile.format, CurImageFile, _accept) + +Image.register_extension(CurImageFile.format, ".cur") diff --git a/venv/Lib/site-packages/PIL/DcxImagePlugin.py b/venv/Lib/site-packages/PIL/DcxImagePlugin.py new file mode 100644 index 0000000..de21db8 --- /dev/null +++ b/venv/Lib/site-packages/PIL/DcxImagePlugin.py @@ -0,0 +1,89 @@ +# +# The Python Imaging Library. +# $Id$ +# +# DCX file handling +# +# DCX is a container file format defined by Intel, commonly used +# for fax applications. Each DCX file consists of a directory +# (a list of file offsets) followed by a set of (usually 1-bit) +# PCX files. +# +# History: +# 1995-09-09 fl Created +# 1996-03-20 fl Properly derived from PcxImageFile. +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2002-07-30 fl Fixed file handling +# +# Copyright (c) 1997-98 by Secret Labs AB. +# Copyright (c) 1995-96 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from . import Image +from ._binary import i32le as i32 +from .PcxImagePlugin import PcxImageFile + +MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? + + +def _accept(prefix): + return len(prefix) >= 4 and i32(prefix) == MAGIC + + +## +# Image plugin for the Intel DCX format. + + +class DcxImageFile(PcxImageFile): + + format = "DCX" + format_description = "Intel DCX" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # Header + s = self.fp.read(4) + if not _accept(s): + raise SyntaxError("not a DCX file") + + # Component directory + self._offset = [] + for i in range(1024): + offset = i32(self.fp.read(4)) + if not offset: + break + self._offset.append(offset) + + self.__fp = self.fp + self.frame = None + self.n_frames = len(self._offset) + self.is_animated = self.n_frames > 1 + self.seek(0) + + def seek(self, frame): + if not self._seek_check(frame): + return + self.frame = frame + self.fp = self.__fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +Image.register_open(DcxImageFile.format, DcxImageFile, _accept) + +Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/venv/Lib/site-packages/PIL/DdsImagePlugin.py b/venv/Lib/site-packages/PIL/DdsImagePlugin.py new file mode 100644 index 0000000..3a04bdb --- /dev/null +++ b/venv/Lib/site-packages/PIL/DdsImagePlugin.py @@ -0,0 +1,249 @@ +""" +A Pillow loader for .dds files (S3TC-compressed aka DXTC) +Jerome Leclanche + +Documentation: + https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ +""" + +import struct +from io import BytesIO + +from . import Image, ImageFile +from ._binary import o32le as o32 + +# Magic ("DDS ") +DDS_MAGIC = 0x20534444 + +# DDS flags +DDSD_CAPS = 0x1 +DDSD_HEIGHT = 0x2 +DDSD_WIDTH = 0x4 +DDSD_PITCH = 0x8 +DDSD_PIXELFORMAT = 0x1000 +DDSD_MIPMAPCOUNT = 0x20000 +DDSD_LINEARSIZE = 0x80000 +DDSD_DEPTH = 0x800000 + +# DDS caps +DDSCAPS_COMPLEX = 0x8 +DDSCAPS_TEXTURE = 0x1000 +DDSCAPS_MIPMAP = 0x400000 + +DDSCAPS2_CUBEMAP = 0x200 +DDSCAPS2_CUBEMAP_POSITIVEX = 0x400 +DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800 +DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000 +DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000 +DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000 +DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000 +DDSCAPS2_VOLUME = 0x200000 + +# Pixel Format +DDPF_ALPHAPIXELS = 0x1 +DDPF_ALPHA = 0x2 +DDPF_FOURCC = 0x4 +DDPF_PALETTEINDEXED8 = 0x20 +DDPF_RGB = 0x40 +DDPF_LUMINANCE = 0x20000 + + +# dds.h + +DDS_FOURCC = DDPF_FOURCC +DDS_RGB = DDPF_RGB +DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS +DDS_LUMINANCE = DDPF_LUMINANCE +DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS +DDS_ALPHA = DDPF_ALPHA +DDS_PAL8 = DDPF_PALETTEINDEXED8 + +DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT +DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH +DDS_HEADER_FLAGS_PITCH = DDSD_PITCH +DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE + +DDS_HEIGHT = DDSD_HEIGHT +DDS_WIDTH = DDSD_WIDTH + +DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE +DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX + +DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + + +# DXT1 +DXT1_FOURCC = 0x31545844 + +# DXT3 +DXT3_FOURCC = 0x33545844 + +# DXT5 +DXT5_FOURCC = 0x35545844 + + +# dxgiformat.h + +DXGI_FORMAT_R8G8B8A8_TYPELESS = 27 +DXGI_FORMAT_R8G8B8A8_UNORM = 28 +DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 +DXGI_FORMAT_BC5_TYPELESS = 82 +DXGI_FORMAT_BC5_UNORM = 83 +DXGI_FORMAT_BC5_SNORM = 84 +DXGI_FORMAT_BC7_TYPELESS = 97 +DXGI_FORMAT_BC7_UNORM = 98 +DXGI_FORMAT_BC7_UNORM_SRGB = 99 + + +class DdsImageFile(ImageFile.ImageFile): + format = "DDS" + format_description = "DirectDraw Surface" + + def _open(self): + if not _accept(self.fp.read(4)): + raise SyntaxError("not a DDS file") + (header_size,) = struct.unpack(" 0: + s = fp.read(min(lengthfile, 100 * 1024)) + if not s: + break + lengthfile -= len(s) + f.write(s) + + device = "pngalpha" if transparency else "ppmraw" + + # Build Ghostscript command + command = [ + "gs", + "-q", # quiet mode + "-g%dx%d" % size, # set output geometry (pixels) + "-r%fx%f" % res, # set input DPI (dots per inch) + "-dBATCH", # exit after processing + "-dNOPAUSE", # don't pause between pages + "-dSAFER", # safe mode + f"-sDEVICE={device}", + f"-sOutputFile={outfile}", # output file + # adjust for image origin + "-c", + f"{-bbox[0]} {-bbox[1]} translate", + "-f", + infile, # input file + # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) + "-c", + "showpage", + ] + + if gs_windows_binary is not None: + if not gs_windows_binary: + raise OSError("Unable to locate Ghostscript on paths") + command[0] = gs_windows_binary + + # push data through Ghostscript + try: + startupinfo = None + if sys.platform.startswith("win"): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + subprocess.check_call(command, startupinfo=startupinfo) + out_im = Image.open(outfile) + out_im.load() + finally: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except OSError: + pass + + im = out_im.im.copy() + out_im.close() + return im + + +class PSFile: + """ + Wrapper for bytesio object that treats either CR or LF as end of line. + """ + + def __init__(self, fp): + self.fp = fp + self.char = None + + def seek(self, offset, whence=io.SEEK_SET): + self.char = None + self.fp.seek(offset, whence) + + def readline(self): + s = [self.char or b""] + self.char = None + + c = self.fp.read(1) + while (c not in b"\r\n") and len(c): + s.append(c) + c = self.fp.read(1) + + self.char = self.fp.read(1) + # line endings can be 1 or 2 of \r \n, in either order + if self.char in b"\r\n": + self.char = None + + return b"".join(s).decode("latin-1") + + +def _accept(prefix): + return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) + + +## +# Image plugin for Encapsulated PostScript. This plugin supports only +# a few variants of this format. + + +class EpsImageFile(ImageFile.ImageFile): + """EPS File Parser for the Python Imaging Library""" + + format = "EPS" + format_description = "Encapsulated Postscript" + + mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} + + def _open(self): + (length, offset) = self._find_offset(self.fp) + + # Rewrap the open file pointer in something that will + # convert line endings and decode to latin-1. + fp = PSFile(self.fp) + + # go to offset - start of "%!PS" + fp.seek(offset) + + box = None + + self.mode = "RGB" + self._size = 1, 1 # FIXME: huh? + + # + # Load EPS header + + s_raw = fp.readline() + s = s_raw.strip("\r\n") + + while s_raw: + if s: + if len(s) > 255: + raise SyntaxError("not an EPS file") + + try: + m = split.match(s) + except re.error as e: + raise SyntaxError("not an EPS file") from e + + if m: + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + box = [int(float(i)) for i in v.split()] + self._size = box[2] - box[0], box[3] - box[1] + self.tile = [ + ("eps", (0, 0) + self.size, offset, (length, box)) + ] + except Exception: + pass + + else: + m = field.match(s) + if m: + k = m.group(1) + + if k == "EndComments": + break + if k[:8] == "PS-Adobe": + self.info[k[:8]] = k[9:] + else: + self.info[k] = "" + elif s[0] == "%": + # handle non-DSC PostScript comments that some + # tools mistakenly put in the Comments section + pass + else: + raise OSError("bad EPS header") + + s_raw = fp.readline() + s = s_raw.strip("\r\n") + + if s and s[:1] != "%": + break + + # + # Scan for an "ImageData" descriptor + + while s[:1] == "%": + + if len(s) > 255: + raise SyntaxError("not an EPS file") + + if s[:11] == "%ImageData:": + # Encoded bitmapped image. + x, y, bi, mo = s[11:].split(None, 7)[:4] + + if int(bi) != 8: + break + try: + self.mode = self.mode_map[int(mo)] + except ValueError: + break + + self._size = int(x), int(y) + return + + s = fp.readline().strip("\r\n") + if not s: + break + + if not box: + raise OSError("cannot determine EPS bounding box") + + def _find_offset(self, fp): + + s = fp.read(160) + + if s[:4] == b"%!PS": + # for HEAD without binary preview + fp.seek(0, io.SEEK_END) + length = fp.tell() + offset = 0 + elif i32(s, 0) == 0xC6D3D0C5: + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # more info see: + # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + offset = i32(s, 4) + length = i32(s, 8) + else: + raise SyntaxError("not an EPS file") + + return (length, offset) + + def load(self, scale=1, transparency=False): + # Load EPS via Ghostscript + if self.tile: + self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) + self.mode = self.im.mode + self._size = self.im.size + self.tile = [] + return Image.Image.load(self) + + def load_seek(self, *args, **kwargs): + # we can't incrementally load, so force ImageFile.parser to + # use our custom load method by defining this method. + pass + + +# +# -------------------------------------------------------------------- + + +def _save(im, fp, filename, eps=1): + """EPS Writer for the Python Imaging Library.""" + + # + # make sure image data is available + im.load() + + # + # determine PostScript image mode + if im.mode == "L": + operator = (8, 1, b"image") + elif im.mode == "RGB": + operator = (8, 3, b"false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, b"false 4 colorimage") + else: + raise ValueError("image mode is not supported") + + if eps: + # + # write EPS header + fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") + # fp.write("%%CreationDate: %s"...) + fp.write(b"%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write(b"%%Pages: 1\n") + fp.write(b"%%EndComments\n") + fp.write(b"%%Page: 1 1\n") + fp.write(b"%%ImageData: %d %d " % im.size) + fp.write(b'%d %d 0 1 1 "%s"\n' % operator) + + # + # image header + fp.write(b"gsave\n") + fp.write(b"10 dict begin\n") + fp.write(b"/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write(b"%d %d scale\n" % im.size) + fp.write(b"%d %d 8\n" % im.size) # <= bits + fp.write(b"[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) + fp.write(b"{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + b"\n") + if hasattr(fp, "flush"): + fp.flush() + + ImageFile._save(im, fp, [("eps", (0, 0) + im.size, 0, None)]) + + fp.write(b"\n%%%%EndBinary\n") + fp.write(b"grestore end\n") + if hasattr(fp, "flush"): + fp.flush() + + +# +# -------------------------------------------------------------------- + + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/venv/Lib/site-packages/PIL/ExifTags.py b/venv/Lib/site-packages/PIL/ExifTags.py new file mode 100644 index 0000000..7da2dda --- /dev/null +++ b/venv/Lib/site-packages/PIL/ExifTags.py @@ -0,0 +1,331 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EXIF tags +# +# Copyright (c) 2003 by Secret Labs AB +# +# See the README file for information on usage and redistribution. +# + +""" +This module provides constants and clear-text names for various +well-known EXIF tags. +""" + + +TAGS = { + # possibly incomplete + 0x0001: "InteropIndex", + 0x000B: "ProcessingSoftware", + 0x00FE: "NewSubfileType", + 0x00FF: "SubfileType", + 0x0100: "ImageWidth", + 0x0101: "ImageLength", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0107: "Thresholding", + 0x0108: "CellWidth", + 0x0109: "CellLength", + 0x010A: "FillOrder", + 0x010D: "DocumentName", + 0x010E: "ImageDescription", + 0x010F: "Make", + 0x0110: "Model", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x0118: "MinSampleValue", + 0x0119: "MaxSampleValue", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x011C: "PlanarConfiguration", + 0x011D: "PageName", + 0x0120: "FreeOffsets", + 0x0121: "FreeByteCounts", + 0x0122: "GrayResponseUnit", + 0x0123: "GrayResponseCurve", + 0x0124: "T4Options", + 0x0125: "T6Options", + 0x0128: "ResolutionUnit", + 0x0129: "PageNumber", + 0x012D: "TransferFunction", + 0x0131: "Software", + 0x0132: "DateTime", + 0x013B: "Artist", + 0x013C: "HostComputer", + 0x013D: "Predictor", + 0x013E: "WhitePoint", + 0x013F: "PrimaryChromaticities", + 0x0140: "ColorMap", + 0x0141: "HalftoneHints", + 0x0142: "TileWidth", + 0x0143: "TileLength", + 0x0144: "TileOffsets", + 0x0145: "TileByteCounts", + 0x014A: "SubIFDs", + 0x014C: "InkSet", + 0x014D: "InkNames", + 0x014E: "NumberOfInks", + 0x0150: "DotRange", + 0x0151: "TargetPrinter", + 0x0152: "ExtraSamples", + 0x0153: "SampleFormat", + 0x0154: "SMinSampleValue", + 0x0155: "SMaxSampleValue", + 0x0156: "TransferRange", + 0x0157: "ClipPath", + 0x0158: "XClipPathUnits", + 0x0159: "YClipPathUnits", + 0x015A: "Indexed", + 0x015B: "JPEGTables", + 0x015F: "OPIProxy", + 0x0200: "JPEGProc", + 0x0201: "JpegIFOffset", + 0x0202: "JpegIFByteCount", + 0x0203: "JpegRestartInterval", + 0x0205: "JpegLosslessPredictors", + 0x0206: "JpegPointTransforms", + 0x0207: "JpegQTables", + 0x0208: "JpegDCTables", + 0x0209: "JpegACTables", + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite", + 0x02BC: "XMLPacket", + 0x1000: "RelatedImageFileFormat", + 0x1001: "RelatedImageWidth", + 0x1002: "RelatedImageLength", + 0x4746: "Rating", + 0x4749: "RatingPercent", + 0x800D: "ImageID", + 0x828D: "CFARepeatPatternDim", + 0x828E: "CFAPattern", + 0x828F: "BatteryLevel", + 0x8298: "Copyright", + 0x829A: "ExposureTime", + 0x829D: "FNumber", + 0x83BB: "IPTCNAA", + 0x8649: "ImageResources", + 0x8769: "ExifOffset", + 0x8773: "InterColorProfile", + 0x8822: "ExposureProgram", + 0x8824: "SpectralSensitivity", + 0x8825: "GPSInfo", + 0x8827: "ISOSpeedRatings", + 0x8828: "OECF", + 0x8829: "Interlace", + 0x882A: "TimeZoneOffset", + 0x882B: "SelfTimerMode", + 0x8830: "SensitivityType", + 0x8831: "StandardOutputSensitivity", + 0x8832: "RecommendedExposureIndex", + 0x8833: "ISOSpeed", + 0x8834: "ISOSpeedLatitudeyyy", + 0x8835: "ISOSpeedLatitudezzz", + 0x9000: "ExifVersion", + 0x9003: "DateTimeOriginal", + 0x9004: "DateTimeDigitized", + 0x9010: "OffsetTime", + 0x9011: "OffsetTimeOriginal", + 0x9012: "OffsetTimeDigitized", + 0x9101: "ComponentsConfiguration", + 0x9102: "CompressedBitsPerPixel", + 0x9201: "ShutterSpeedValue", + 0x9202: "ApertureValue", + 0x9203: "BrightnessValue", + 0x9204: "ExposureBiasValue", + 0x9205: "MaxApertureValue", + 0x9206: "SubjectDistance", + 0x9207: "MeteringMode", + 0x9208: "LightSource", + 0x9209: "Flash", + 0x920A: "FocalLength", + 0x920B: "FlashEnergy", + 0x920C: "SpatialFrequencyResponse", + 0x920D: "Noise", + 0x9211: "ImageNumber", + 0x9212: "SecurityClassification", + 0x9213: "ImageHistory", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x9216: "TIFF/EPStandardID", + 0x927C: "MakerNote", + 0x9286: "UserComment", + 0x9290: "SubsecTime", + 0x9291: "SubsecTimeOriginal", + 0x9292: "SubsecTimeDigitized", + 0x9400: "AmbientTemperature", + 0x9401: "Humidity", + 0x9402: "Pressure", + 0x9403: "WaterDepth", + 0x9404: "Acceleration", + 0x9405: "CameraElevationAngle", + 0x9C9B: "XPTitle", + 0x9C9C: "XPComment", + 0x9C9D: "XPAuthor", + 0x9C9E: "XPKeywords", + 0x9C9F: "XPSubject", + 0xA000: "FlashPixVersion", + 0xA001: "ColorSpace", + 0xA002: "ExifImageWidth", + 0xA003: "ExifImageHeight", + 0xA004: "RelatedSoundFile", + 0xA005: "ExifInteroperabilityOffset", + 0xA20B: "FlashEnergy", + 0xA20C: "SpatialFrequencyResponse", + 0xA20E: "FocalPlaneXResolution", + 0xA20F: "FocalPlaneYResolution", + 0xA210: "FocalPlaneResolutionUnit", + 0xA214: "SubjectLocation", + 0xA215: "ExposureIndex", + 0xA217: "SensingMethod", + 0xA300: "FileSource", + 0xA301: "SceneType", + 0xA302: "CFAPattern", + 0xA401: "CustomRendered", + 0xA402: "ExposureMode", + 0xA403: "WhiteBalance", + 0xA404: "DigitalZoomRatio", + 0xA405: "FocalLengthIn35mmFilm", + 0xA406: "SceneCaptureType", + 0xA407: "GainControl", + 0xA408: "Contrast", + 0xA409: "Saturation", + 0xA40A: "Sharpness", + 0xA40B: "DeviceSettingDescription", + 0xA40C: "SubjectDistanceRange", + 0xA420: "ImageUniqueID", + 0xA430: "CameraOwnerName", + 0xA431: "BodySerialNumber", + 0xA432: "LensSpecification", + 0xA433: "LensMake", + 0xA434: "LensModel", + 0xA435: "LensSerialNumber", + 0xA460: "CompositeImage", + 0xA461: "CompositeImageCount", + 0xA462: "CompositeImageExposureTimes", + 0xA500: "Gamma", + 0xC4A5: "PrintImageMatching", + 0xC612: "DNGVersion", + 0xC613: "DNGBackwardVersion", + 0xC614: "UniqueCameraModel", + 0xC615: "LocalizedCameraModel", + 0xC616: "CFAPlaneColor", + 0xC617: "CFALayout", + 0xC618: "LinearizationTable", + 0xC619: "BlackLevelRepeatDim", + 0xC61A: "BlackLevel", + 0xC61B: "BlackLevelDeltaH", + 0xC61C: "BlackLevelDeltaV", + 0xC61D: "WhiteLevel", + 0xC61E: "DefaultScale", + 0xC61F: "DefaultCropOrigin", + 0xC620: "DefaultCropSize", + 0xC621: "ColorMatrix1", + 0xC622: "ColorMatrix2", + 0xC623: "CameraCalibration1", + 0xC624: "CameraCalibration2", + 0xC625: "ReductionMatrix1", + 0xC626: "ReductionMatrix2", + 0xC627: "AnalogBalance", + 0xC628: "AsShotNeutral", + 0xC629: "AsShotWhiteXY", + 0xC62A: "BaselineExposure", + 0xC62B: "BaselineNoise", + 0xC62C: "BaselineSharpness", + 0xC62D: "BayerGreenSplit", + 0xC62E: "LinearResponseLimit", + 0xC62F: "CameraSerialNumber", + 0xC630: "LensInfo", + 0xC631: "ChromaBlurRadius", + 0xC632: "AntiAliasStrength", + 0xC633: "ShadowScale", + 0xC634: "DNGPrivateData", + 0xC635: "MakerNoteSafety", + 0xC65A: "CalibrationIlluminant1", + 0xC65B: "CalibrationIlluminant2", + 0xC65C: "BestQualityScale", + 0xC65D: "RawDataUniqueID", + 0xC68B: "OriginalRawFileName", + 0xC68C: "OriginalRawFileData", + 0xC68D: "ActiveArea", + 0xC68E: "MaskedAreas", + 0xC68F: "AsShotICCProfile", + 0xC690: "AsShotPreProfileMatrix", + 0xC691: "CurrentICCProfile", + 0xC692: "CurrentPreProfileMatrix", + 0xC6BF: "ColorimetricReference", + 0xC6F3: "CameraCalibrationSignature", + 0xC6F4: "ProfileCalibrationSignature", + 0xC6F6: "AsShotProfileName", + 0xC6F7: "NoiseReductionApplied", + 0xC6F8: "ProfileName", + 0xC6F9: "ProfileHueSatMapDims", + 0xC6FA: "ProfileHueSatMapData1", + 0xC6FB: "ProfileHueSatMapData2", + 0xC6FC: "ProfileToneCurve", + 0xC6FD: "ProfileEmbedPolicy", + 0xC6FE: "ProfileCopyright", + 0xC714: "ForwardMatrix1", + 0xC715: "ForwardMatrix2", + 0xC716: "PreviewApplicationName", + 0xC717: "PreviewApplicationVersion", + 0xC718: "PreviewSettingsName", + 0xC719: "PreviewSettingsDigest", + 0xC71A: "PreviewColorSpace", + 0xC71B: "PreviewDateTime", + 0xC71C: "RawImageDigest", + 0xC71D: "OriginalRawFileDigest", + 0xC71E: "SubTileBlockSize", + 0xC71F: "RowInterleaveFactor", + 0xC725: "ProfileLookTableDims", + 0xC726: "ProfileLookTableData", + 0xC740: "OpcodeList1", + 0xC741: "OpcodeList2", + 0xC74E: "OpcodeList3", + 0xC761: "NoiseProfile", +} +"""Maps EXIF tags to tag names.""" + + +GPSTAGS = { + 0: "GPSVersionID", + 1: "GPSLatitudeRef", + 2: "GPSLatitude", + 3: "GPSLongitudeRef", + 4: "GPSLongitude", + 5: "GPSAltitudeRef", + 6: "GPSAltitude", + 7: "GPSTimeStamp", + 8: "GPSSatellites", + 9: "GPSStatus", + 10: "GPSMeasureMode", + 11: "GPSDOP", + 12: "GPSSpeedRef", + 13: "GPSSpeed", + 14: "GPSTrackRef", + 15: "GPSTrack", + 16: "GPSImgDirectionRef", + 17: "GPSImgDirection", + 18: "GPSMapDatum", + 19: "GPSDestLatitudeRef", + 20: "GPSDestLatitude", + 21: "GPSDestLongitudeRef", + 22: "GPSDestLongitude", + 23: "GPSDestBearingRef", + 24: "GPSDestBearing", + 25: "GPSDestDistanceRef", + 26: "GPSDestDistance", + 27: "GPSProcessingMethod", + 28: "GPSAreaInformation", + 29: "GPSDateStamp", + 30: "GPSDifferential", + 31: "GPSHPositioningError", +} +"""Maps EXIF GPS tags to tag names.""" diff --git a/venv/Lib/site-packages/PIL/FitsImagePlugin.py b/venv/Lib/site-packages/PIL/FitsImagePlugin.py new file mode 100644 index 0000000..c16300e --- /dev/null +++ b/venv/Lib/site-packages/PIL/FitsImagePlugin.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS file handling +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import math + +from . import Image, ImageFile + + +def _accept(prefix): + return prefix[:6] == b"SIMPLE" + + +class FitsImageFile(ImageFile.ImageFile): + + format = "FITS" + format_description = "FITS" + + def _open(self): + headers = {} + while True: + header = self.fp.read(80) + if not header: + raise OSError("Truncated FITS file") + keyword = header[:8].strip() + if keyword == b"END": + break + value = header[8:].strip() + if value.startswith(b"="): + value = value[1:].strip() + if not headers and (not _accept(keyword) or value != b"T"): + raise SyntaxError("Not a FITS file") + headers[keyword] = value + + naxis = int(headers[b"NAXIS"]) + if naxis == 0: + raise ValueError("No image data") + elif naxis == 1: + self._size = 1, int(headers[b"NAXIS1"]) + else: + self._size = int(headers[b"NAXIS1"]), int(headers[b"NAXIS2"]) + + number_of_bits = int(headers[b"BITPIX"]) + if number_of_bits == 8: + self.mode = "L" + elif number_of_bits == 16: + self.mode = "I" + # rawmode = "I;16S" + elif number_of_bits == 32: + self.mode = "I" + elif number_of_bits in (-32, -64): + self.mode = "F" + # rawmode = "F" if number_of_bits == -32 else "F;64F" + + offset = math.ceil(self.fp.tell() / 2880) * 2880 + self.tile = [("raw", (0, 0) + self.size, offset, (self.mode, 0, -1))] + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FitsImageFile.format, FitsImageFile, _accept) + +Image.register_extensions(FitsImageFile.format, [".fit", ".fits"]) diff --git a/venv/Lib/site-packages/PIL/FitsStubImagePlugin.py b/venv/Lib/site-packages/PIL/FitsStubImagePlugin.py new file mode 100644 index 0000000..9eed029 --- /dev/null +++ b/venv/Lib/site-packages/PIL/FitsStubImagePlugin.py @@ -0,0 +1,77 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS stub adapter +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import warnings + +from . import FitsImagePlugin, Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific FITS image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + warnings.warn( + "FitsStubImagePlugin is deprecated and will be removed in Pillow " + "10 (2023-07-01). FITS images can now be read without a handler through " + "FitsImagePlugin instead.", + DeprecationWarning, + ) + + # Override FitsImagePlugin with this handler + # for backwards compatibility + try: + Image.ID.remove(FITSStubImageFile.format) + except ValueError: + pass + + Image.register_open( + FITSStubImageFile.format, FITSStubImageFile, FitsImagePlugin._accept + ) + + +class FITSStubImageFile(ImageFile.StubImageFile): + + format = FitsImagePlugin.FitsImageFile.format + format_description = FitsImagePlugin.FitsImageFile.format_description + + def _open(self): + offset = self.fp.tell() + + im = FitsImagePlugin.FitsImageFile(self.fp) + self._size = im.size + self.mode = im.mode + self.tile = [] + + self.fp.seek(offset) + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + raise OSError("FITS save handler not installed") + + +# -------------------------------------------------------------------- +# Registry + +Image.register_save(FITSStubImageFile.format, _save) diff --git a/venv/Lib/site-packages/PIL/FliImagePlugin.py b/venv/Lib/site-packages/PIL/FliImagePlugin.py new file mode 100644 index 0000000..ea95033 --- /dev/null +++ b/venv/Lib/site-packages/PIL/FliImagePlugin.py @@ -0,0 +1,171 @@ +# +# The Python Imaging Library. +# $Id$ +# +# FLI/FLC file handling. +# +# History: +# 95-09-01 fl Created +# 97-01-03 fl Fixed parser, setup decoder tile +# 98-07-15 fl Renamed offset attribute to avoid name clash +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import i32le as i32 +from ._binary import o8 + +# +# decoder + + +def _accept(prefix): + return ( + len(prefix) >= 6 + and i16(prefix, 4) in [0xAF11, 0xAF12] + and i16(prefix, 14) in [0, 3] # flags + ) + + +## +# Image plugin for the FLI/FLC animation format. Use the seek +# method to load individual frames. + + +class FliImageFile(ImageFile.ImageFile): + + format = "FLI" + format_description = "Autodesk FLI/FLC Animation" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # HEAD + s = self.fp.read(128) + if not (_accept(s) and s[20:22] == b"\x00\x00"): + raise SyntaxError("not an FLI/FLC file") + + # frames + self.n_frames = i16(s, 6) + self.is_animated = self.n_frames > 1 + + # image characteristics + self.mode = "P" + self._size = i16(s, 8), i16(s, 10) + + # animation speed + duration = i32(s, 16) + magic = i16(s, 4) + if magic == 0xAF11: + duration = (duration * 1000) // 70 + self.info["duration"] = duration + + # look for palette + palette = [(a, a, a) for a in range(256)] + + s = self.fp.read(16) + + self.__offset = 128 + + if i16(s, 4) == 0xF100: + # prefix chunk; ignore it + self.__offset = self.__offset + i32(s) + s = self.fp.read(16) + + if i16(s, 4) == 0xF1FA: + # look for palette chunk + s = self.fp.read(6) + if i16(s, 4) == 11: + self._palette(palette, 2) + elif i16(s, 4) == 4: + self._palette(palette, 0) + + palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette] + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + # set things up to decode first frame + self.__frame = -1 + self.__fp = self.fp + self.__rewind = self.fp.tell() + self.seek(0) + + def _palette(self, palette, shift): + # load palette + + i = 0 + for e in range(i16(self.fp.read(2))): + s = self.fp.read(2) + i = i + s[0] + n = s[1] + if n == 0: + n = 256 + s = self.fp.read(n * 3) + for n in range(0, len(s), 3): + r = s[n] << shift + g = s[n + 1] << shift + b = s[n + 2] << shift + palette[i] = (r, g, b) + i += 1 + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0) + + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + + def _seek(self, frame): + if frame == 0: + self.__frame = -1 + self.__fp.seek(self.__rewind) + self.__offset = 128 + else: + # ensure that the previous frame was loaded + self.load() + + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + self.__frame = frame + + # move to next frame + self.fp = self.__fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + raise EOFError + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [("fli", (0, 0) + self.size, self.__offset, None)] + + self.__offset += framesize + + def tell(self): + return self.__frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# registry + +Image.register_open(FliImageFile.format, FliImageFile, _accept) + +Image.register_extensions(FliImageFile.format, [".fli", ".flc"]) diff --git a/venv/Lib/site-packages/PIL/FontFile.py b/venv/Lib/site-packages/PIL/FontFile.py new file mode 100644 index 0000000..c5fc80b --- /dev/null +++ b/venv/Lib/site-packages/PIL/FontFile.py @@ -0,0 +1,111 @@ +# +# The Python Imaging Library +# $Id$ +# +# base class for raster font file parsers +# +# history: +# 1997-06-05 fl created +# 1997-08-19 fl restrict image width +# +# Copyright (c) 1997-1998 by Secret Labs AB +# Copyright (c) 1997-1998 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +import os + +from . import Image, _binary + +WIDTH = 800 + + +def puti16(fp, values): + """Write network order (big-endian) 16-bit sequence""" + for v in values: + if v < 0: + v += 65536 + fp.write(_binary.o16be(v)) + + +class FontFile: + """Base class for raster font file handlers.""" + + bitmap = None + + def __init__(self): + + self.info = {} + self.glyph = [None] * 256 + + def __getitem__(self, ix): + return self.glyph[ix] + + def compile(self): + """Create metrics and bitmap""" + + if self.bitmap: + return + + # create bitmap large enough to hold all data + h = w = maxwidth = 0 + lines = 1 + for glyph in self: + if glyph: + d, dst, src, im = glyph + h = max(h, src[3] - src[1]) + w = w + (src[2] - src[0]) + if w > WIDTH: + lines += 1 + w = src[2] - src[0] + maxwidth = max(maxwidth, w) + + xsize = maxwidth + ysize = lines * h + + if xsize == 0 and ysize == 0: + return "" + + self.ysize = h + + # paste glyphs into bitmap + self.bitmap = Image.new("1", (xsize, ysize)) + self.metrics = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx = src[2] - src[0] + # yy = src[3] - src[1] + x0, y0 = x, y + x = x + xx + if x > WIDTH: + x, y = 0, y + h + x0, y0 = x, y + x = xx + s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 + self.bitmap.paste(im.crop(src), s) + self.metrics[i] = d, dst, s + + def save(self, filename): + """Save font""" + + self.compile() + + # font data + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # font metrics + with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp: + fp.write(b"PILfont\n") + fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!! + fp.write(b"DATA\n") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, [0] * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) diff --git a/venv/Lib/site-packages/PIL/FpxImagePlugin.py b/venv/Lib/site-packages/PIL/FpxImagePlugin.py new file mode 100644 index 0000000..5e38546 --- /dev/null +++ b/venv/Lib/site-packages/PIL/FpxImagePlugin.py @@ -0,0 +1,242 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# FlashPix support for PIL +# +# History: +# 97-01-25 fl Created (reads uncompressed RGB images only) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# +import olefile + +from . import Image, ImageFile +from ._binary import i32le as i32 + +# we map from colour field tuples to (mode, rawmode) descriptors +MODES = { + # opacity + (0x00007FFE): ("A", "L"), + # monochrome + (0x00010000,): ("L", "L"), + (0x00018000, 0x00017FFE): ("RGBA", "LA"), + # photo YCC + (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), + (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"), + # standard RGB (NIFRGB) + (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), + (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"), +} + + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == olefile.MAGIC + + +## +# Image plugin for the FlashPix images. + + +class FpxImageFile(ImageFile.ImageFile): + + format = "FPX" + format_description = "FlashPix" + + def _open(self): + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + raise SyntaxError("not an FPX file; invalid OLE file") from e + + if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": + raise SyntaxError("not an FPX file; bad root CLSID") + + self._open_index(1) + + def _open_index(self, index=1): + # + # get the Image Contents Property Set + + prop = self.ole.getproperties( + [f"Data Object Store {index:06d}", "\005Image Contents"] + ) + + # size (highest resolution) + + self._size = prop[0x1000002], prop[0x1000003] + + size = max(self.size) + i = 1 + while size > 64: + size = size / 2 + i += 1 + self.maxid = i - 1 + + # mode. instead of using a single field for this, flashpix + # requires you to specify the mode for each channel in each + # resolution subimage, and leaves it to the decoder to make + # sure that they all match. for now, we'll cheat and assume + # that this is always the case. + + id = self.maxid << 16 + + s = prop[0x2000002 | id] + + colors = [] + bands = i32(s, 4) + if bands > 4: + raise OSError("Invalid number of bands") + for i in range(bands): + # note: for now, we ignore the "uncalibrated" flag + colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF) + + self.mode, self.rawmode = MODES[tuple(colors)] + + # load JPEG tables, if any + self.jpeg = {} + for i in range(256): + id = 0x3000001 | (i << 16) + if id in prop: + self.jpeg[i] = prop[id] + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index=1, subimage=0): + # + # setup tile descriptors for a given subimage + + stream = [ + f"Data Object Store {index:06d}", + f"Resolution {subimage:04d}", + "Subimage 0000 Header", + ] + + fp = self.ole.openstream(stream) + + # skip prefix + fp.read(28) + + # header stream + s = fp.read(36) + + size = i32(s, 4), i32(s, 8) + # tilecount = i32(s, 12) + tilesize = i32(s, 16), i32(s, 20) + # channels = i32(s, 24) + offset = i32(s, 28) + length = i32(s, 32) + + if size != self.size: + raise OSError("subimage mismatch") + + # get tile descriptors + fp.seek(28 + offset) + s = fp.read(i32(s, 12) * length) + + x = y = 0 + xsize, ysize = size + xtile, ytile = tilesize + self.tile = [] + + for i in range(0, len(s), length): + + compression = i32(s, i + 8) + + if compression == 0: + self.tile.append( + ( + "raw", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (self.rawmode), + ) + ) + + elif compression == 1: + + # FIXME: the fill decoder is not implemented + self.tile.append( + ( + "fill", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (self.rawmode, s[12:16]), + ) + ) + + elif compression == 2: + + internal_color_conversion = s[14] + jpeg_tables = s[15] + rawmode = self.rawmode + + if internal_color_conversion: + # The image is stored as usual (usually YCbCr). + if rawmode == "RGBA": + # For "RGBA", data is stored as YCbCrA based on + # negative RGB. The following trick works around + # this problem : + jpegmode, rawmode = "YCbCrK", "CMYK" + else: + jpegmode = None # let the decoder decide + + else: + # The image is stored as defined by rawmode + jpegmode = rawmode + + self.tile.append( + ( + "jpeg", + (x, y, x + xtile, y + ytile), + i32(s, i) + 28, + (rawmode, jpegmode), + ) + ) + + # FIXME: jpeg tables are tile dependent; the prefix + # data must be placed in the tile descriptor itself! + + if jpeg_tables: + self.tile_prefix = self.jpeg[jpeg_tables] + + else: + raise OSError("unknown/invalid compression") + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + self.stream = stream + self.fp = None + + def load(self): + + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) + + return ImageFile.ImageFile.load(self) + + +# +# -------------------------------------------------------------------- + + +Image.register_open(FpxImageFile.format, FpxImageFile, _accept) + +Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/venv/Lib/site-packages/PIL/FtexImagePlugin.py b/venv/Lib/site-packages/PIL/FtexImagePlugin.py new file mode 100644 index 0000000..55d28e1 --- /dev/null +++ b/venv/Lib/site-packages/PIL/FtexImagePlugin.py @@ -0,0 +1,135 @@ +""" +A Pillow loader for .ftc and .ftu files (FTEX) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 + +The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a +packed custom format called FTEX. This file format uses file extensions FTC +and FTU. +* FTC files are compressed textures (using standard texture compression). +* FTU files are not compressed. +Texture File Format +The FTC and FTU texture files both use the same format. This +has the following structure: +{header} +{format_directory} +{data} +Where: +{header} = { + u32:magic, + u32:version, + u32:width, + u32:height, + u32:mipmap_count, + u32:format_count +} + +* The "magic" number is "FTEX". +* "width" and "height" are the dimensions of the texture. +* "mipmap_count" is the number of mipmaps in the texture. +* "format_count" is the number of texture formats (different versions of the +same texture) in this file. + +{format_directory} = format_count * { u32:format, u32:where } + +The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB +uncompressed textures. +The texture data for a format starts at the position "where" in the file. + +Each set of texture data in the file has the following structure: +{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } +* "mipmap_size" is the number of bytes in that mip level. For compressed +textures this is the size of the texture data compressed with DXT1. For 24 bit +uncompressed textures, this is 3 * width * height. Following this are the image +bytes for that mipmap level. + +Note: All data is stored in little-Endian (Intel) byte order. +""" + +import struct +import warnings +from enum import IntEnum +from io import BytesIO + +from . import Image, ImageFile + +MAGIC = b"FTEX" + + +class Format(IntEnum): + DXT1 = 0 + UNCOMPRESSED = 1 + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Format: "FORMAT_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +class FtexImageFile(ImageFile.ImageFile): + format = "FTEX" + format_description = "Texture File Format (IW2:EOC)" + + def _open(self): + if not _accept(self.fp.read(4)): + raise SyntaxError("not an FTEX file") + struct.unpack("= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) + + +## +# Image plugin for the GIMP brush format. + + +class GbrImageFile(ImageFile.ImageFile): + + format = "GBR" + format_description = "GIMP brush file" + + def _open(self): + header_size = i32(self.fp.read(4)) + if header_size < 20: + raise SyntaxError("not a GIMP brush") + version = i32(self.fp.read(4)) + if version not in (1, 2): + raise SyntaxError(f"Unsupported GIMP brush version: {version}") + + width = i32(self.fp.read(4)) + height = i32(self.fp.read(4)) + color_depth = i32(self.fp.read(4)) + if width <= 0 or height <= 0: + raise SyntaxError("not a GIMP brush") + if color_depth not in (1, 4): + raise SyntaxError(f"Unsupported GIMP brush color depth: {color_depth}") + + if version == 1: + comment_length = header_size - 20 + else: + comment_length = header_size - 28 + magic_number = self.fp.read(4) + if magic_number != b"GIMP": + raise SyntaxError("not a GIMP brush, bad magic number") + self.info["spacing"] = i32(self.fp.read(4)) + + comment = self.fp.read(comment_length)[:-1] + + if color_depth == 1: + self.mode = "L" + else: + self.mode = "RGBA" + + self._size = width, height + + self.info["comment"] = comment + + # Image might not be small + Image._decompression_bomb_check(self.size) + + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth + + def load(self): + if not self.im: + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self._data_size)) + return Image.Image.load(self) + + +# +# registry + + +Image.register_open(GbrImageFile.format, GbrImageFile, _accept) +Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/venv/Lib/site-packages/PIL/GdImageFile.py b/venv/Lib/site-packages/PIL/GdImageFile.py new file mode 100644 index 0000000..9c34ada --- /dev/null +++ b/venv/Lib/site-packages/PIL/GdImageFile.py @@ -0,0 +1,90 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GD file handling +# +# History: +# 1996-04-12 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +""" +.. note:: + This format cannot be automatically recognized, so the + class is not registered for use with :py:func:`PIL.Image.open()`. To open a + gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. + +.. warning:: + THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This + implementation is provided for convenience and demonstrational + purposes only. +""" + + +from . import ImageFile, ImagePalette, UnidentifiedImageError +from ._binary import i16be as i16 +from ._binary import i32be as i32 + + +class GdImageFile(ImageFile.ImageFile): + """ + Image plugin for the GD uncompressed format. Note that this format + is not supported by the standard :py:func:`PIL.Image.open()` function. To use + this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and + use the :py:func:`PIL.GdImageFile.open()` function. + """ + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self): + + # Header + s = self.fp.read(1037) + + if not i16(s) in [65534, 65535]: + raise SyntaxError("Not a valid GD 2.x .gd file") + + self.mode = "L" # FIXME: "P" + self._size = i16(s, 2), i16(s, 4) + + trueColor = s[6] + trueColorOffset = 2 if trueColor else 0 + + # transparency index + tindex = i32(s, 7 + trueColorOffset) + if tindex < 256: + self.info["transparency"] = tindex + + self.palette = ImagePalette.raw( + "XBGR", s[7 + trueColorOffset + 4 : 7 + trueColorOffset + 4 + 256 * 4] + ) + + self.tile = [ + ("raw", (0, 0) + self.size, 7 + trueColorOffset + 4 + 256 * 4, ("L", 0, 1)) + ] + + +def open(fp, mode="r"): + """ + Load texture from a GD image file. + + :param filename: GD file name, or an opened file handle. + :param mode: Optional mode. In this version, if the mode argument + is given, it must be "r". + :returns: An image instance. + :raises OSError: If the image could not be read. + """ + if mode != "r": + raise ValueError("bad mode") + + try: + return GdImageFile(fp) + except SyntaxError as e: + raise UnidentifiedImageError("cannot identify this image file") from e diff --git a/venv/Lib/site-packages/PIL/GifImagePlugin.py b/venv/Lib/site-packages/PIL/GifImagePlugin.py new file mode 100644 index 0000000..b798bb9 --- /dev/null +++ b/venv/Lib/site-packages/PIL/GifImagePlugin.py @@ -0,0 +1,1038 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GIF file handling +# +# History: +# 1995-09-01 fl Created +# 1996-12-14 fl Added interlace support +# 1996-12-30 fl Added animation support +# 1997-01-05 fl Added write support, fixed local colour map bug +# 1997-02-23 fl Make sure to load raster data in getdata() +# 1997-07-05 fl Support external decoder (0.4) +# 1998-07-09 fl Handle all modes when saving (0.5) +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) +# 2001-04-17 fl Added palette optimization (0.7) +# 2002-06-06 fl Added transparency support for save (0.8) +# 2004-02-24 fl Disable interlacing for small images +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import itertools +import math +import os +import subprocess +from enum import IntEnum + +from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + + +class LoadingStrategy(IntEnum): + """.. versionadded:: 9.1.0""" + + RGB_AFTER_FIRST = 0 + RGB_AFTER_DIFFERENT_PALETTE_ONLY = 1 + RGB_ALWAYS = 2 + + +#: .. versionadded:: 9.1.0 +LOADING_STRATEGY = LoadingStrategy.RGB_AFTER_FIRST + +# -------------------------------------------------------------------- +# Identify/read GIF files + + +def _accept(prefix): + return prefix[:6] in [b"GIF87a", b"GIF89a"] + + +## +# Image plugin for GIF images. This plugin supports both GIF87 and +# GIF89 images. + + +class GifImageFile(ImageFile.ImageFile): + + format = "GIF" + format_description = "Compuserve GIF" + _close_exclusive_fp_after_loading = False + + global_palette = None + + def data(self): + s = self.fp.read(1) + if s and s[0]: + return self.fp.read(s[0]) + return None + + def _is_palette_needed(self, p): + for i in range(0, len(p), 3): + if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): + return True + return False + + def _open(self): + + # Screen + s = self.fp.read(13) + if not _accept(s): + raise SyntaxError("not a GIF file") + + self.info["version"] = s[:6] + self._size = i16(s, 6), i16(s, 8) + self.tile = [] + flags = s[10] + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = s[11] + # check if palette contains colour indices + p = self.fp.read(3 << bits) + if self._is_palette_needed(p): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p + + self.__fp = self.fp # FIXME: hack + self.__rewind = self.fp.tell() + self._n_frames = None + self._is_animated = None + self._seek(0) # get ready to read first frame + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self._seek(self.tell() + 1, False) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + + @property + def is_animated(self): + if self._is_animated is None: + if self._n_frames is not None: + self._is_animated = self._n_frames != 1 + else: + current = self.tell() + if current: + self._is_animated = True + else: + try: + self._seek(1, False) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + self.im = None + self._seek(0) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + raise EOFError("no more images in GIF file") from e + + def _seek(self, frame, update_image=True): + + if frame == 0: + # rewind + self.__offset = 0 + self.dispose = None + self.__frame = -1 + self.__fp.seek(self.__rewind) + self.disposal_method = 0 + else: + # ensure that the previous frame was loaded + if self.tile and update_image: + self.load() + + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + + self.fp = self.__fp + if self.__offset: + # backup to last frame + self.fp.seek(self.__offset) + while self.data(): + pass + self.__offset = 0 + + s = self.fp.read(1) + if not s or s == b";": + raise EOFError + + self.__frame = frame + + self.tile = [] + + palette = None + + info = {} + frame_transparency = None + interlace = None + frame_dispose_extent = None + while True: + + if not s: + s = self.fp.read(1) + if not s or s == b";": + break + + elif s == b"!": + # + # extensions + # + s = self.fp.read(1) + block = self.data() + if s[0] == 249: + # + # graphic control extension + # + flags = block[0] + if flags & 1: + frame_transparency = block[3] + info["duration"] = i16(block, 1) * 10 + + # disposal method - find the value of bits 4 - 6 + dispose_bits = 0b00011100 & flags + dispose_bits = dispose_bits >> 2 + if dispose_bits: + # only set the dispose if it is not + # unspecified. I'm not sure if this is + # correct, but it seems to prevent the last + # frame from looking odd for some animations + self.disposal_method = dispose_bits + elif s[0] == 254: + # + # comment extension + # + while block: + if "comment" in info: + info["comment"] += block + else: + info["comment"] = block + block = self.data() + s = None + continue + elif s[0] == 255: + # + # application extension + # + info["extension"] = block, self.fp.tell() + if block[:11] == b"NETSCAPE2.0": + block = self.data() + if len(block) >= 3 and block[0] == 1: + info["loop"] = i16(block, 1) + while self.data(): + pass + + elif s == b",": + # + # local image + # + s = self.fp.read(9) + + # extent + x0, y0 = i16(s, 0), i16(s, 2) + x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) + if (x1 > self.size[0] or y1 > self.size[1]) and update_image: + self._size = max(x1, self.size[0]), max(y1, self.size[1]) + frame_dispose_extent = x0, y0, x1, y1 + flags = s[8] + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + p = self.fp.read(3 << bits) + if self._is_palette_needed(p): + palette = ImagePalette.raw("RGB", p) + + # image data + bits = self.fp.read(1)[0] + self.__offset = self.fp.tell() + break + + else: + pass + # raise OSError, "illegal GIF tag `%x`" % s[0] + s = None + + if interlace is None: + # self.__fp = None + raise EOFError + if not update_image: + return + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + self._frame_palette = palette or self.global_palette + if frame == 0: + if self._frame_palette: + self.mode = ( + "RGB" if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS else "P" + ) + else: + self.mode = "L" + + if not palette and self.global_palette: + from copy import copy + + palette = copy(self.global_palette) + self.palette = palette + else: + self._frame_transparency = frame_transparency + if self.mode == "P": + if ( + LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY + or palette + ): + self.pyaccess = None + if "transparency" in self.info: + self.im.putpalettealpha(self.info["transparency"], 0) + self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) + self.mode = "RGBA" + del self.info["transparency"] + else: + self.mode = "RGB" + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + + def _rgb(color): + if self._frame_palette: + color = tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]) + else: + color = (color, color, color) + return color + + self.dispose_extent = frame_dispose_extent + try: + if self.disposal_method < 2: + # do not dispose or none specified + self.dispose = None + elif self.disposal_method == 2: + # replace with background colour + + # only dispose the extent in this frame + x0, y0, x1, y1 = self.dispose_extent + dispose_size = (x1 - x0, y1 - y0) + + Image._decompression_bomb_check(dispose_size) + + # by convention, attempt to use transparency first + dispose_mode = "P" + color = self.info.get("transparency", frame_transparency) + if color is not None: + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(color) + (0,) + else: + color = self.info.get("background", 0) + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGB" + color = _rgb(color) + self.dispose = Image.core.fill(dispose_mode, dispose_size, color) + else: + # replace with previous contents + if self.im is not None: + # only dispose the extent in this frame + self.dispose = self._crop(self.im, self.dispose_extent) + elif frame_transparency is not None: + x0, y0, x1, y1 = self.dispose_extent + dispose_size = (x1 - x0, y1 - y0) + + Image._decompression_bomb_check(dispose_size) + dispose_mode = "P" + color = frame_transparency + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(frame_transparency) + (0,) + self.dispose = Image.core.fill(dispose_mode, dispose_size, color) + except AttributeError: + pass + + if interlace is not None: + transparency = -1 + if frame_transparency is not None: + if frame == 0: + self.info["transparency"] = frame_transparency + elif self.mode not in ("RGB", "RGBA"): + transparency = frame_transparency + self.tile = [ + ( + "gif", + (x0, y0, x1, y1), + self.__offset, + (bits, interlace, transparency), + ) + ] + + for k in ["duration", "comment", "extension", "loop"]: + if k in info: + self.info[k] = info[k] + elif k in self.info: + del self.info[k] + + def load_prepare(self): + temp_mode = "P" if self._frame_palette else "L" + self._prev_im = None + if self.__frame == 0: + if "transparency" in self.info: + self.im = Image.core.fill( + temp_mode, self.size, self.info["transparency"] + ) + elif self.mode in ("RGB", "RGBA"): + self._prev_im = self.im + if self._frame_palette: + self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) + self.im.putpalette(*self._frame_palette.getdata()) + else: + self.im = None + self.mode = temp_mode + self._frame_palette = None + + super().load_prepare() + + def load_end(self): + if self.__frame == 0: + if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: + self.mode = "RGB" + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + return + if self.mode == "P" and self._prev_im: + if self._frame_transparency is not None: + self.im.putpalettealpha(self._frame_transparency, 0) + frame_im = self.im.convert("RGBA") + else: + frame_im = self.im.convert("RGB") + else: + if not self._prev_im: + return + frame_im = self.im + frame_im = self._crop(frame_im, self.dispose_extent) + + self.im = self._prev_im + self.mode = self.im.mode + if frame_im.mode == "RGBA": + self.im.paste(frame_im, self.dispose_extent, frame_im) + else: + self.im.paste(frame_im, self.dispose_extent) + + def tell(self): + return self.__frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# Write GIF files + + +RAWMODE = {"1": "L", "L": "L", "P": "P"} + + +def _normalize_mode(im): + """ + Takes an image (or frame), returns an image in a mode that is appropriate + for saving in a Gif. + + It may return the original image, or it may return an image converted to + palette or 'L' mode. + + :param im: Image object + :returns: Image object + """ + if im.mode in RAWMODE: + im.load() + return im + if Image.getmodebase(im.mode) == "RGB": + im = im.convert("P", palette=Image.Palette.ADAPTIVE) + if im.palette.mode == "RGBA": + for rgba in im.palette.colors.keys(): + if rgba[3] == 0: + im.info["transparency"] = im.palette.colors[rgba] + break + return im + return im.convert("L") + + +def _normalize_palette(im, palette, info): + """ + Normalizes the palette for image. + - Sets the palette to the incoming palette, if provided. + - Ensures that there's a palette for L mode images + - Optimizes the palette if necessary/desired. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: Image object + """ + source_palette = None + if palette: + # a bytes palette + if isinstance(palette, (bytes, bytearray, list)): + source_palette = bytearray(palette[:768]) + if isinstance(palette, ImagePalette.ImagePalette): + source_palette = bytearray(palette.palette) + + if im.mode == "P": + if not source_palette: + source_palette = im.im.getpalette("RGB")[:768] + else: # L-mode + if not source_palette: + source_palette = bytearray(i // 3 for i in range(768)) + im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) + + if palette: + used_palette_colors = [] + for i in range(0, len(source_palette), 3): + source_color = tuple(source_palette[i : i + 3]) + try: + index = im.palette.colors[source_color] + except KeyError: + index = None + used_palette_colors.append(index) + for i, index in enumerate(used_palette_colors): + if index is None: + for j in range(len(used_palette_colors)): + if j not in used_palette_colors: + used_palette_colors[i] = j + break + im = im.remap_palette(used_palette_colors) + else: + used_palette_colors = _get_optimize(im, info) + if used_palette_colors is not None: + return im.remap_palette(used_palette_colors, source_palette) + + im.palette.palette = source_palette + return im + + +def _write_single_frame(im, fp, palette): + im_out = _normalize_mode(im) + for k, v in im_out.info.items(): + im.encoderinfo.setdefault(k, v) + im_out = _normalize_palette(im_out, palette, im.encoderinfo) + + for s in _get_global_header(im_out, im.encoderinfo): + fp.write(s) + + # local image header + flags = 0 + if get_interlace(im): + flags = flags | 64 + _write_local_header(fp, im, (0, 0), flags) + + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save(im_out, fp, [("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])]) + + fp.write(b"\0") # end of image data + + +def _write_multiple_frames(im, fp, palette): + + duration = im.encoderinfo.get("duration", im.info.get("duration")) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) + + im_frames = [] + frame_count = 0 + background_im = None + for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): + for im_frame in ImageSequence.Iterator(imSequence): + # a copy is required here since seek can still mutate the image + im_frame = _normalize_mode(im_frame.copy()) + if frame_count == 0: + for k, v in im_frame.info.items(): + im.encoderinfo.setdefault(k, v) + im_frame = _normalize_palette(im_frame, palette, im.encoderinfo) + + encoderinfo = im.encoderinfo.copy() + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + frame_count += 1 + + if im_frames: + # delta frame + previous = im_frames[-1] + if encoderinfo.get("disposal") == 2: + if background_im is None: + color = im.encoderinfo.get( + "transparency", im.info.get("transparency", (0, 0, 0)) + ) + background = _get_background(im_frame, color) + background_im = Image.new("P", im_frame.size, background) + background_im.putpalette(im_frames[0]["im"].palette) + base_im = background_im + else: + base_im = previous["im"] + if _get_palette_bytes(im_frame) == _get_palette_bytes(base_im): + delta = ImageChops.subtract_modulo(im_frame, base_im) + else: + delta = ImageChops.subtract_modulo( + im_frame.convert("RGB"), base_im.convert("RGB") + ) + bbox = delta.getbbox() + if not bbox: + # This frame is identical to the previous frame + if duration: + previous["encoderinfo"]["duration"] += encoderinfo["duration"] + continue + else: + bbox = None + im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) + + if len(im_frames) > 1: + for frame_data in im_frames: + im_frame = frame_data["im"] + if not frame_data["bbox"]: + # global header + for s in _get_global_header(im_frame, frame_data["encoderinfo"]): + fp.write(s) + offset = (0, 0) + else: + # compress difference + if not palette: + frame_data["encoderinfo"]["include_color_table"] = True + + im_frame = im_frame.crop(frame_data["bbox"]) + offset = frame_data["bbox"][:2] + _write_frame_data(fp, im_frame, offset, frame_data["encoderinfo"]) + return True + elif "duration" in im.encoderinfo and isinstance( + im.encoderinfo["duration"], (list, tuple) + ): + # Since multiple frames will not be written, add together the frame durations + im.encoderinfo["duration"] = sum(im.encoderinfo["duration"]) + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +def _save(im, fp, filename, save_all=False): + # header + if "palette" in im.encoderinfo or "palette" in im.info: + palette = im.encoderinfo.get("palette", im.info.get("palette")) + else: + palette = None + im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) + + if not save_all or not _write_multiple_frames(im, fp, palette): + _write_single_frame(im, fp, palette) + + fp.write(b";") # end of file + + if hasattr(fp, "flush"): + fp.flush() + + +def get_interlace(im): + interlace = im.encoderinfo.get("interlace", 1) + + # workaround for @PIL153 + if min(im.size) < 16: + interlace = 0 + + return interlace + + +def _write_local_header(fp, im, offset, flags): + transparent_color_exists = False + try: + if "transparency" in im.encoderinfo: + transparency = im.encoderinfo["transparency"] + else: + transparency = im.info["transparency"] + transparency = int(transparency) + except (KeyError, ValueError): + pass + else: + # optimize the block away if transparent color is not used + transparent_color_exists = True + + used_palette_colors = _get_optimize(im, im.encoderinfo) + if used_palette_colors is not None: + # adjust the transparency index after optimize + try: + transparency = used_palette_colors.index(transparency) + except ValueError: + transparent_color_exists = False + + if "duration" in im.encoderinfo: + duration = int(im.encoderinfo["duration"] / 10) + else: + duration = 0 + + disposal = int(im.encoderinfo.get("disposal", 0)) + + if transparent_color_exists or duration != 0 or disposal: + packed_flag = 1 if transparent_color_exists else 0 + packed_flag |= disposal << 2 + if not transparent_color_exists: + transparency = 0 + + fp.write( + b"!" + + o8(249) # extension intro + + o8(4) # length + + o8(packed_flag) # packed fields + + o16(duration) # duration + + o8(transparency) # transparency index + + o8(0) + ) + + if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]): + fp.write(b"!" + o8(254)) # extension intro + comment = im.encoderinfo["comment"] + if isinstance(comment, str): + comment = comment.encode() + for i in range(0, len(comment), 255): + subblock = comment[i : i + 255] + fp.write(o8(len(subblock)) + subblock) + fp.write(o8(0)) + if "loop" in im.encoderinfo: + number_of_loops = im.encoderinfo["loop"] + fp.write( + b"!" + + o8(255) # extension intro + + o8(11) + + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(number_of_loops) # number of loops + + o8(0) + ) + include_color_table = im.encoderinfo.get("include_color_table") + if include_color_table: + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + if color_table_size: + flags = flags | 128 # local color table flag + flags = flags | color_table_size + + fp.write( + b"," + + o16(offset[0]) # offset + + o16(offset[1]) + + o16(im.size[0]) # size + + o16(im.size[1]) + + o8(flags) # flags + ) + if include_color_table and color_table_size: + fp.write(_get_header_palette(palette_bytes)) + fp.write(o8(8)) # bits + + +def _save_netpbm(im, fp, filename): + + # Unused by default. + # To use, uncomment the register_save call at the end of the file. + # + # If you need real GIF compression and/or RGB quantization, you + # can use the external NETPBM/PBMPLUS utilities. See comments + # below for information on how to enable this. + tempfile = im._dump() + + try: + with open(filename, "wb") as f: + if im.mode != "RGB": + subprocess.check_call( + ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL + ) + else: + # Pipe ppmquant output into ppmtogif + # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) + quant_cmd = ["ppmquant", "256", tempfile] + togif_cmd = ["ppmtogif"] + quant_proc = subprocess.Popen( + quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + togif_proc = subprocess.Popen( + togif_cmd, + stdin=quant_proc.stdout, + stdout=f, + stderr=subprocess.DEVNULL, + ) + + # Allow ppmquant to receive SIGPIPE if ppmtogif exits + quant_proc.stdout.close() + + retcode = quant_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, quant_cmd) + + retcode = togif_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, togif_cmd) + finally: + try: + os.unlink(tempfile) + except OSError: + pass + + +# Force optimization so that we can test performance against +# cases where it took lots of memory and time previously. +_FORCE_OPTIMIZE = False + + +def _get_optimize(im, info): + """ + Palette optimization is a potentially expensive operation. + + This function determines if the palette should be optimized using + some heuristics, then returns the list of palette entries in use. + + :param im: Image object + :param info: encoderinfo + :returns: list of indexes of palette entries in use, or None + """ + if im.mode in ("P", "L") and info and info.get("optimize", 0): + # Potentially expensive operation. + + # The palette saves 3 bytes per color not used, but palette + # lengths are restricted to 3*(2**N) bytes. Max saving would + # be 768 -> 6 bytes if we went all the way down to 2 colors. + # * If we're over 128 colors, we can't save any space. + # * If there aren't any holes, it's not worth collapsing. + # * If we have a 'large' image, the palette is in the noise. + + # create the new palette if not every color is used + optimise = _FORCE_OPTIMIZE or im.mode == "L" + if optimise or im.width * im.height < 512 * 512: + # check which colors are used + used_palette_colors = [] + for i, count in enumerate(im.histogram()): + if count: + used_palette_colors.append(i) + + if optimise or ( + len(used_palette_colors) <= 128 + and max(used_palette_colors) > len(used_palette_colors) + ): + return used_palette_colors + + +def _get_color_table_size(palette_bytes): + # calculate the palette size for the header + if not palette_bytes: + return 0 + elif len(palette_bytes) < 9: + return 1 + else: + return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 + + +def _get_header_palette(palette_bytes): + """ + Returns the palette, null padded to the next power of 2 (*3) bytes + suitable for direct inclusion in the GIF header + + :param palette_bytes: Unpadded palette bytes, in RGBRGB form + :returns: Null padded palette + """ + color_table_size = _get_color_table_size(palette_bytes) + + # add the missing amount of bytes + # the palette has to be 2< 0: + palette_bytes += o8(0) * 3 * actual_target_size_diff + return palette_bytes + + +def _get_palette_bytes(im): + """ + Gets the palette for inclusion in the gif header + + :param im: Image object + :returns: Bytes, len<=768 suitable for inclusion in gif header + """ + return im.palette.palette + + +def _get_background(im, infoBackground): + background = 0 + if infoBackground: + background = infoBackground + if isinstance(background, tuple): + # WebPImagePlugin stores an RGBA value in info["background"] + # So it must be converted to the same format as GifImagePlugin's + # info["background"] - a global color table index + try: + background = im.palette.getcolor(background, im) + except ValueError as e: + if str(e) == "cannot allocate more than 256 colors": + # If all 256 colors are in use, + # then there is no need for the background color + return 0 + else: + raise + return background + + +def _get_global_header(im, info): + """Return a list of strings representing a GIF header""" + + # Header Block + # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + + version = b"87a" + for extensionKey in ["transparency", "duration", "loop", "comment"]: + if info and extensionKey in info: + if (extensionKey == "duration" and info[extensionKey] == 0) or ( + extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255) + ): + continue + version = b"89a" + break + else: + if im.info.get("version") == b"89a": + version = b"89a" + + background = _get_background(im, info.get("background")) + + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + + return [ + b"GIF" # signature + + version # version + + o16(im.size[0]) # canvas width + + o16(im.size[1]), # canvas height + # Logical Screen Descriptor + # size of global color table + global color table flag + o8(color_table_size + 128), # packed fields + # background + reserved/aspect + o8(background) + o8(0), + # Global Color Table + _get_header_palette(palette_bytes), + ] + + +def _write_frame_data(fp, im_frame, offset, params): + try: + im_frame.encoderinfo = params + + # local image header + _write_local_header(fp, im_frame, offset, 0) + + ImageFile._save( + im_frame, fp, [("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])] + ) + + fp.write(b"\0") # end of image data + finally: + del im_frame.encoderinfo + + +# -------------------------------------------------------------------- +# Legacy GIF utilities + + +def getheader(im, palette=None, info=None): + """ + Legacy Method to get Gif data from image. + + Warning:: May modify image data. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: tuple of(list of header items, optimized palette) + + """ + used_palette_colors = _get_optimize(im, info) + + if info is None: + info = {} + + if "background" not in info and "background" in im.info: + info["background"] = im.info["background"] + + im_mod = _normalize_palette(im, palette, info) + im.palette = im_mod.palette + im.im = im_mod.im + header = _get_global_header(im, info) + + return header, used_palette_colors + + +# To specify duration, add the time in milliseconds to getdata(), +# e.g. getdata(im_frame, duration=1000) +def getdata(im, offset=(0, 0), **params): + """ + Legacy Method + + Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data. + + :param im: Image object + :param offset: Tuple of (x, y) pixels. Defaults to (0,0) + :param \\**params: E.g. duration or other encoder info parameters + :returns: List of Bytes containing gif encoded frame data + + """ + + class Collector: + data = [] + + def write(self, data): + self.data.append(data) + + im.load() # make sure raster data is available + + fp = Collector() + + _write_frame_data(fp, im, offset, params) + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_save_all(GifImageFile.format, _save_all) +Image.register_extension(GifImageFile.format, ".gif") +Image.register_mime(GifImageFile.format, "image/gif") + +# +# Uncomment the following line if you wish to use NETPBM/PBMPLUS +# instead of the built-in "uncompressed" GIF encoder + +# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/venv/Lib/site-packages/PIL/GimpGradientFile.py b/venv/Lib/site-packages/PIL/GimpGradientFile.py new file mode 100644 index 0000000..7ab7f99 --- /dev/null +++ b/venv/Lib/site-packages/PIL/GimpGradientFile.py @@ -0,0 +1,140 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read (and render) GIMP gradient files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +""" +Stuff to translate curve segments to palette values (derived from +the corresponding code in GIMP, written by Federico Mena Quintero. +See the GIMP distribution for more information.) +""" + + +from math import log, pi, sin, sqrt + +from ._binary import o8 + +EPSILON = 1e-10 +"""""" # Enable auto-doc for data member + + +def linear(middle, pos): + if pos <= middle: + if middle < EPSILON: + return 0.0 + else: + return 0.5 * pos / middle + else: + pos = pos - middle + middle = 1.0 - middle + if middle < EPSILON: + return 1.0 + else: + return 0.5 + 0.5 * pos / middle + + +def curved(middle, pos): + return pos ** (log(0.5) / log(max(middle, EPSILON))) + + +def sine(middle, pos): + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + + +def sphere_increasing(middle, pos): + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + + +def sphere_decreasing(middle, pos): + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + + +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] +"""""" # Enable auto-doc for data member + + +class GradientFile: + + gradient = None + + def getpalette(self, entries=256): + + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + + x = i / (entries - 1) + + while x1 < x: + ix += 1 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + w = x1 - x0 + + if w < EPSILON: + scale = segment(0.5, 0.5) + else: + scale = segment((xm - x0) / w, (x - x0) / w) + + # expand to RGBA + r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) + + # add to palette + palette.append(r + g + b + a) + + return b"".join(palette), "RGBA" + + +class GimpGradientFile(GradientFile): + """File handler for GIMP's gradient format.""" + + def __init__(self, fp): + + if fp.readline()[:13] != b"GIMP Gradient": + raise SyntaxError("not a GIMP gradient file") + + line = fp.readline() + + # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do + if line.startswith(b"Name: "): + line = fp.readline().strip() + + count = int(line) + + gradient = [] + + for i in range(count): + + s = fp.readline().split() + w = [float(x) for x in s[:11]] + + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] + + segment = SEGMENTS[int(s[11])] + cspace = int(s[12]) + + if cspace != 0: + raise OSError("cannot handle HSV colour space") + + gradient.append((x0, x1, xm, rgb0, rgb1, segment)) + + self.gradient = gradient diff --git a/venv/Lib/site-packages/PIL/GimpPaletteFile.py b/venv/Lib/site-packages/PIL/GimpPaletteFile.py new file mode 100644 index 0000000..4d7cfba --- /dev/null +++ b/venv/Lib/site-packages/PIL/GimpPaletteFile.py @@ -0,0 +1,56 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read GIMP palette files +# +# History: +# 1997-08-23 fl Created +# 2004-09-07 fl Support GIMP 2.0 palette files. +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1997-2004. +# +# See the README file for information on usage and redistribution. +# + +import re + +from ._binary import o8 + + +class GimpPaletteFile: + """File handler for GIMP's palette format.""" + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [o8(i) * 3 for i in range(256)] + + if fp.readline()[:12] != b"GIMP Palette": + raise SyntaxError("not a GIMP palette file") + + for i in range(256): + + s = fp.readline() + if not s: + break + + # skip fields and comment lines + if re.match(rb"\w+:|#", s): + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = tuple(map(int, s.split()[:3])) + if len(v) != 3: + raise ValueError("bad palette entry") + + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/venv/Lib/site-packages/PIL/GribStubImagePlugin.py b/venv/Lib/site-packages/PIL/GribStubImagePlugin.py new file mode 100644 index 0000000..cc9bc26 --- /dev/null +++ b/venv/Lib/site-packages/PIL/GribStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# GRIB stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific GRIB image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[0:4] == b"GRIB" and prefix[7] == 1 + + +class GribStubImageFile(ImageFile.StubImageFile): + + format = "GRIB" + format_description = "GRIB" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a GRIB file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr(_handler, "save"): + raise OSError("GRIB save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) +Image.register_save(GribStubImageFile.format, _save) + +Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/venv/Lib/site-packages/PIL/Hdf5StubImagePlugin.py b/venv/Lib/site-packages/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 0000000..df11cf2 --- /dev/null +++ b/venv/Lib/site-packages/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# HDF5 stub adapter +# +# Copyright (c) 2000-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler): + """ + Install application-specific HDF5 image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix): + return prefix[:8] == b"\x89HDF\r\n\x1a\n" + + +class HDF5StubImageFile(ImageFile.StubImageFile): + + format = "HDF5" + format_description = "HDF5" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not an HDF file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr(_handler, "save"): + raise OSError("HDF5 save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"]) diff --git a/venv/Lib/site-packages/PIL/IcnsImagePlugin.py b/venv/Lib/site-packages/PIL/IcnsImagePlugin.py new file mode 100644 index 0000000..fa192f0 --- /dev/null +++ b/venv/Lib/site-packages/PIL/IcnsImagePlugin.py @@ -0,0 +1,392 @@ +# +# The Python Imaging Library. +# $Id$ +# +# macOS icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# 2020-04-04 Allow saving on all operating systems. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# Copyright (c) 2014 by Alastair Houghton. +# Copyright (c) 2020 by Pan Jing. +# +# See the README file for information on usage and redistribution. +# + +import io +import os +import struct +import sys + +from PIL import Image, ImageFile, PngImagePlugin, features + +enable_jpeg2k = features.check_codec("jpg_2000") +if enable_jpeg2k: + from PIL import Jpeg2KImagePlugin + +MAGIC = b"icns" +HEADERSIZE = 8 + + +def nextheader(fobj): + return struct.unpack(">4sI", fobj.read(HEADERSIZE)) + + +def read_32t(fobj, start_length, size): + # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(4) + if sig != b"\x00\x00\x00\x00": + raise SyntaxError("Unknown signature, expecting 0x00000000") + return read_32(fobj, (start + 4, length - 4), size) + + +def read_32(fobj, start_length, size): + """ + Read a 32bit RGB icon resource. Seems to be either uncompressed or + an RLE packbits-like scheme. + """ + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + if length == sizesq * 3: + # uncompressed ("RGBRGBGB") + indata = fobj.read(length) + im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) + else: + # decode image + im = Image.new("RGB", pixel_size, None) + for band_ix in range(3): + data = [] + bytesleft = sizesq + while bytesleft > 0: + byte = fobj.read(1) + if not byte: + break + byte = byte[0] + if byte & 0x80: + blocksize = byte - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte + 1 + data.append(fobj.read(blocksize)) + bytesleft -= blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + raise SyntaxError(f"Error reading channel [{repr(bytesleft)} left]") + band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1) + im.im.putband(band.im, band_ix) + return {"RGB": im} + + +def read_mk(fobj, start_length, size): + # Alpha masks seem to be uncompressed + start = start_length[0] + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + band = Image.frombuffer("L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1) + return {"A": band} + + +def read_png_or_jpeg2000(fobj, start_length, size): + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(12) + if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a": + fobj.seek(start) + im = PngImagePlugin.PngImageFile(fobj) + Image._decompression_bomb_check(im.size) + return {"RGBA": im} + elif ( + sig[:4] == b"\xff\x4f\xff\x51" + or sig[:4] == b"\x0d\x0a\x87\x0a" + or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" + ): + if not enable_jpeg2k: + raise ValueError( + "Unsupported icon subimage format (rebuild PIL " + "with JPEG 2000 support to fix this)" + ) + # j2k, jpc or j2c + fobj.seek(start) + jp2kstream = fobj.read(length) + f = io.BytesIO(jp2kstream) + im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) + Image._decompression_bomb_check(im.size) + if im.mode != "RGBA": + im = im.convert("RGBA") + return {"RGBA": im} + else: + raise ValueError("Unsupported icon subimage format") + + +class IcnsFile: + + SIZES = { + (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], + (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], + (256, 256, 2): [(b"ic14", read_png_or_jpeg2000)], + (256, 256, 1): [(b"ic08", read_png_or_jpeg2000)], + (128, 128, 2): [(b"ic13", read_png_or_jpeg2000)], + (128, 128, 1): [ + (b"ic07", read_png_or_jpeg2000), + (b"it32", read_32t), + (b"t8mk", read_mk), + ], + (64, 64, 1): [(b"icp6", read_png_or_jpeg2000)], + (32, 32, 2): [(b"ic12", read_png_or_jpeg2000)], + (48, 48, 1): [(b"ih32", read_32), (b"h8mk", read_mk)], + (32, 32, 1): [ + (b"icp5", read_png_or_jpeg2000), + (b"il32", read_32), + (b"l8mk", read_mk), + ], + (16, 16, 2): [(b"ic11", read_png_or_jpeg2000)], + (16, 16, 1): [ + (b"icp4", read_png_or_jpeg2000), + (b"is32", read_32), + (b"s8mk", read_mk), + ], + } + + def __init__(self, fobj): + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if not _accept(sig): + raise SyntaxError("not an icns file") + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + if blocksize <= 0: + raise SyntaxError("invalid block header") + i += HEADERSIZE + blocksize -= HEADERSIZE + dct[sig] = (i, blocksize) + fobj.seek(blocksize, io.SEEK_CUR) + i += blocksize + + def itersizes(self): + sizes = [] + for size, fmts in self.SIZES.items(): + for (fmt, reader) in fmts: + if fmt in self.dct: + sizes.append(size) + break + return sizes + + def bestsize(self): + sizes = self.itersizes() + if not sizes: + raise SyntaxError("No 32bit icon resources found") + return max(sizes) + + def dataforsize(self, size): + """ + Get an icon resource as {channel: array}. Note that + the arrays are bottom-up like windows bitmaps and will likely + need to be flipped or transposed in some way. + """ + dct = {} + for code, reader in self.SIZES[size]: + desc = self.dct.get(code) + if desc is not None: + dct.update(reader(self.fobj, desc, size)) + return dct + + def getimage(self, size=None): + if size is None: + size = self.bestsize() + if len(size) == 2: + size = (size[0], size[1], 1) + channels = self.dataforsize(size) + + im = channels.get("RGBA", None) + if im: + return im + + im = channels.get("RGB").copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + + +## +# Image plugin for Mac OS icons. + + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL image support for Mac OS .icns files. + Chooses the best resolution, but will possibly load + a different size image if you mutate the size attribute + before calling 'load'. + + The info dictionary has a key 'sizes' that is a list + of sizes that the icns file has. + """ + + format = "ICNS" + format_description = "Mac OS icns resource" + + def _open(self): + self.icns = IcnsFile(self.fp) + self.mode = "RGBA" + self.info["sizes"] = self.icns.itersizes() + self.best_size = self.icns.bestsize() + self.size = ( + self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2], + ) + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + info_size = value + if info_size not in self.info["sizes"] and len(info_size) == 2: + info_size = (info_size[0], info_size[1], 1) + if ( + info_size not in self.info["sizes"] + and len(info_size) == 3 + and info_size[2] == 1 + ): + simple_sizes = [ + (size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"] + ] + if value in simple_sizes: + info_size = self.info["sizes"][simple_sizes.index(value)] + if info_size not in self.info["sizes"]: + raise ValueError("This is not one of the allowed sizes of this image") + self._size = value + + def load(self): + if len(self.size) == 3: + self.best_size = self.size + self.size = ( + self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2], + ) + + px = Image.Image.load(self) + if self.im is not None and self.im.size == self.size: + # Already loaded + return px + self.load_prepare() + # This is likely NOT the best way to do it, but whatever. + im = self.icns.getimage(self.best_size) + + # If this is a PNG or JPEG 2000, it won't be loaded yet + px = im.load() + + self.im = im.im + self.mode = im.mode + self.size = im.size + + return px + + +def _save(im, fp, filename): + """ + Saves the image as a series of PNG files, + that are then combined into a .icns file. + """ + if hasattr(fp, "flush"): + fp.flush() + + sizes = { + b"ic07": 128, + b"ic08": 256, + b"ic09": 512, + b"ic10": 1024, + b"ic11": 32, + b"ic12": 64, + b"ic13": 256, + b"ic14": 512, + } + provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} + size_streams = {} + for size in set(sizes.values()): + image = ( + provided_images[size] + if size in provided_images + else im.resize((size, size)) + ) + + temp = io.BytesIO() + image.save(temp, "png") + size_streams[size] = temp.getvalue() + + entries = [] + for type, size in sizes.items(): + stream = size_streams[size] + entries.append( + {"type": type, "size": HEADERSIZE + len(stream), "stream": stream} + ) + + # Header + fp.write(MAGIC) + file_length = HEADERSIZE # Header + file_length += HEADERSIZE + 8 * len(entries) # TOC + file_length += sum(entry["size"] for entry in entries) + fp.write(struct.pack(">i", file_length)) + + # TOC + fp.write(b"TOC ") + fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) + for entry in entries: + fp.write(entry["type"]) + fp.write(struct.pack(">i", entry["size"])) + + # Data + for entry in entries: + fp.write(entry["type"]) + fp.write(struct.pack(">i", entry["size"])) + fp.write(entry["stream"]) + + if hasattr(fp, "flush"): + fp.flush() + + +def _accept(prefix): + return prefix[:4] == MAGIC + + +Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept) +Image.register_extension(IcnsImageFile.format, ".icns") + +Image.register_save(IcnsImageFile.format, _save) +Image.register_mime(IcnsImageFile.format, "image/icns") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Syntax: python3 IcnsImagePlugin.py [file]") + sys.exit() + + with open(sys.argv[1], "rb") as fp: + imf = IcnsImageFile(fp) + for size in imf.info["sizes"]: + imf.size = size + imf.save("out-%s-%s-%s.png" % size) + with Image.open(sys.argv[1]) as im: + im.save("out.png") + if sys.platform == "windows": + os.startfile("out.png") diff --git a/venv/Lib/site-packages/PIL/IcoImagePlugin.py b/venv/Lib/site-packages/PIL/IcoImagePlugin.py new file mode 100644 index 0000000..17b9855 --- /dev/null +++ b/venv/Lib/site-packages/PIL/IcoImagePlugin.py @@ -0,0 +1,355 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Icon support for PIL +# +# History: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis +# . +# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki +# +# Icon format references: +# * https://en.wikipedia.org/wiki/ICO_(file_format) +# * https://msdn.microsoft.com/en-us/library/ms997538.aspx + + +import warnings +from io import BytesIO +from math import ceil, log + +from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin +from ._binary import i16le as i16 +from ._binary import i32le as i32 +from ._binary import o8 +from ._binary import o16le as o16 +from ._binary import o32le as o32 + +# +# -------------------------------------------------------------------- + +_MAGIC = b"\0\0\1\0" + + +def _save(im, fp, filename): + fp.write(_MAGIC) # (2+2) + bmp = im.encoderinfo.get("bitmap_format") == "bmp" + sizes = im.encoderinfo.get( + "sizes", + [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)], + ) + frames = [] + provided_ims = [im] + im.encoderinfo.get("append_images", []) + width, height = im.size + for size in sorted(set(sizes)): + if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256: + continue + + for provided_im in provided_ims: + if provided_im.size != size: + continue + frames.append(provided_im) + if bmp: + bits = BmpImagePlugin.SAVE[provided_im.mode][1] + bits_used = [bits] + for other_im in provided_ims: + if other_im.size != size: + continue + bits = BmpImagePlugin.SAVE[other_im.mode][1] + if bits not in bits_used: + # Another image has been supplied for this size + # with a different bit depth + frames.append(other_im) + bits_used.append(bits) + break + else: + # TODO: invent a more convenient method for proportional scalings + frame = provided_im.copy() + frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None) + frames.append(frame) + fp.write(o16(len(frames))) # idCount(2) + offset = fp.tell() + len(frames) * 16 + for frame in frames: + width, height = frame.size + # 0 means 256 + fp.write(o8(width if width < 256 else 0)) # bWidth(1) + fp.write(o8(height if height < 256 else 0)) # bHeight(1) + + bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0) + fp.write(o8(colors)) # bColorCount(1) + fp.write(b"\0") # bReserved(1) + fp.write(b"\0\0") # wPlanes(2) + fp.write(o16(bits)) # wBitCount(2) + + image_io = BytesIO() + if bmp: + frame.save(image_io, "dib") + + if bits != 32: + and_mask = Image.new("1", size) + ImageFile._save( + and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))] + ) + else: + frame.save(image_io, "png") + image_io.seek(0) + image_bytes = image_io.read() + if bmp: + image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:] + bytes_len = len(image_bytes) + fp.write(o32(bytes_len)) # dwBytesInRes(4) + fp.write(o32(offset)) # dwImageOffset(4) + current = fp.tell() + fp.seek(offset) + fp.write(image_bytes) + offset = offset + bytes_len + fp.seek(current) + + +def _accept(prefix): + return prefix[:4] == _MAGIC + + +class IcoFile: + def __init__(self, buf): + """ + Parse image from file-like object containing ico file data + """ + + # check magic + s = buf.read(6) + if not _accept(s): + raise SyntaxError("not an ICO file") + + self.buf = buf + self.entry = [] + + # Number of items in file + self.nb_items = i16(s, 4) + + # Get headers for each item + for i in range(self.nb_items): + s = buf.read(16) + + icon_header = { + "width": s[0], + "height": s[1], + "nb_color": s[2], # No. of colors in image (0 if >=8bpp) + "reserved": s[3], + "planes": i16(s, 4), + "bpp": i16(s, 6), + "size": i32(s, 8), + "offset": i32(s, 12), + } + + # See Wikipedia + for j in ("width", "height"): + if not icon_header[j]: + icon_header[j] = 256 + + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + icon_header["color_depth"] = ( + icon_header["bpp"] + or ( + icon_header["nb_color"] != 0 + and ceil(log(icon_header["nb_color"], 2)) + ) + or 256 + ) + + icon_header["dim"] = (icon_header["width"], icon_header["height"]) + icon_header["square"] = icon_header["width"] * icon_header["height"] + + self.entry.append(icon_header) + + self.entry = sorted(self.entry, key=lambda x: x["color_depth"]) + # ICO images are usually squares + # self.entry = sorted(self.entry, key=lambda x: x['width']) + self.entry = sorted(self.entry, key=lambda x: x["square"]) + self.entry.reverse() + + def sizes(self): + """ + Get a list of all available icon sizes and color depths. + """ + return {(h["width"], h["height"]) for h in self.entry} + + def getentryindex(self, size, bpp=False): + for (i, h) in enumerate(self.entry): + if size == h["dim"] and (bpp is False or bpp == h["color_depth"]): + return i + return 0 + + def getimage(self, size, bpp=False): + """ + Get an image from the icon + """ + return self.frame(self.getentryindex(size, bpp)) + + def frame(self, idx): + """ + Get an image from frame idx + """ + + header = self.entry[idx] + + self.buf.seek(header["offset"]) + data = self.buf.read(8) + self.buf.seek(header["offset"]) + + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + Image._decompression_bomb_check(im.size) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + Image._decompression_bomb_check(im.size) + + # change tile dimension to only encompass XOR image + im._size = (im.size[0], int(im.size[1] / 2)) + d, e, o, a = im.tile[0] + im.tile[0] = d, (0, 0) + im.size, o, a + + # figure out where AND mask image starts + bpp = header["bpp"] + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them. + # The DIB is packed in BGRX byte order where X is the alpha + # channel. + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = Image.frombuffer( + "L", # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + "raw", # raw decoder + ("L", 0, -1), # 8bpp inverted, unpadded, reversed + ) + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + + # the total mask data is + # padded row size * height / bits per char + + total_bytes = int((w * im.size[1]) / 8) + and_mask_offset = header["offset"] + header["size"] - total_bytes + + self.buf.seek(and_mask_offset) + mask_data = self.buf.read(total_bytes) + + # convert raw data to image + mask = Image.frombuffer( + "1", # 1 bpp + im.size, # (w, h) + mask_data, # source chars + "raw", # raw decoder + ("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + im = im.convert("RGBA") + im.putalpha(mask) + + return im + + +## +# Image plugin for Windows Icon files. + + +class IcoImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Microsoft Windows .ico files. + + By default the largest resolution image in the file will be loaded. This + can be changed by altering the 'size' attribute before calling 'load'. + + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. + + Handles classic, XP and Vista icon formats. + + When saving, PNG compression is used. Support for this was only added in + Windows Vista. If you are unable to view the icon in Windows, convert the + image to "RGBA" mode before saving. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis + . + https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki + """ + + format = "ICO" + format_description = "Windows Icon" + + def _open(self): + self.ico = IcoFile(self.fp) + self.info["sizes"] = self.ico.sizes() + self.size = self.ico.entry[0]["dim"] + self.load() + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + if value not in self.info["sizes"]: + raise ValueError("This is not one of the allowed sizes of this image") + self._size = value + + def load(self): + if self.im is not None and self.im.size == self.size: + # Already loaded + return Image.Image.load(self) + im = self.ico.getimage(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + if im.size != self.size: + warnings.warn("Image was not the expected size") + + index = self.ico.getentryindex(self.size) + sizes = list(self.info["sizes"]) + sizes[index] = im.size + self.info["sizes"] = set(sizes) + + self.size = im.size + + def load_seek(self): + # Flag the ImageFile.Parser so that it + # just does all the decode at the end. + pass + + +# +# -------------------------------------------------------------------- + + +Image.register_open(IcoImageFile.format, IcoImageFile, _accept) +Image.register_save(IcoImageFile.format, _save) +Image.register_extension(IcoImageFile.format, ".ico") + +Image.register_mime(IcoImageFile.format, "image/x-icon") diff --git a/venv/Lib/site-packages/PIL/ImImagePlugin.py b/venv/Lib/site-packages/PIL/ImImagePlugin.py new file mode 100644 index 0000000..f7e690b --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImImagePlugin.py @@ -0,0 +1,376 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IFUNC IM file handling for PIL +# +# history: +# 1995-09-01 fl Created. +# 1997-01-03 fl Save palette images +# 1997-01-08 fl Added sequence support +# 1997-01-23 fl Added P and RGB save support +# 1997-05-31 fl Read floating point images +# 1997-06-22 fl Save floating point images +# 1997-08-27 fl Read and save 1-bit images +# 1998-06-25 fl Added support for RGB+LUT images +# 1998-07-02 fl Added support for YCC images +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 1998-12-29 fl Added I;16 support +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# 2003-09-26 fl Added LA/PA support +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +import os +import re + +from . import Image, ImageFile, ImagePalette + +# -------------------------------------------------------------------- +# Standard tags + +COMMENT = "Comment" +DATE = "Date" +EQUIPMENT = "Digitalization equipment" +FRAMES = "File size (no of images)" +LUT = "Lut" +NAME = "Name" +SCALE = "Scale (x,y)" +SIZE = "Image size (x*y)" +MODE = "Image type" + +TAGS = { + COMMENT: 0, + DATE: 0, + EQUIPMENT: 0, + FRAMES: 0, + LUT: 0, + NAME: 0, + SCALE: 0, + SIZE: 0, + MODE: 0, +} + +OPEN = { + # ifunc93/p3cfunc formats + "0 1 image": ("1", "1"), + "L 1 image": ("1", "1"), + "Greyscale image": ("L", "L"), + "Grayscale image": ("L", "L"), + "RGB image": ("RGB", "RGB;L"), + "RLB image": ("RGB", "RLB"), + "RYB image": ("RGB", "RLB"), + "B1 image": ("1", "1"), + "B2 image": ("P", "P;2"), + "B4 image": ("P", "P;4"), + "X 24 image": ("RGB", "RGB"), + "L 32 S image": ("I", "I;32"), + "L 32 F image": ("F", "F;32"), + # old p3cfunc formats + "RGB3 image": ("RGB", "RGB;T"), + "RYB3 image": ("RGB", "RYB;T"), + # extensions + "LA image": ("LA", "LA;L"), + "PA image": ("LA", "PA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGBX", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN[f"L {i} image"] = ("F", f"F;{i}") + OPEN[f"L*{i} image"] = ("F", f"F;{i}") +for i in ["16", "16L", "16B"]: + OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") + OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") +for i in ["32S"]: + OPEN[f"L {i} image"] = ("I", f"I;{i}") + OPEN[f"L*{i} image"] = ("I", f"I;{i}") +for i in range(2, 33): + OPEN[f"L*{i} image"] = ("F", f"F;{i}") + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + + +def number(s): + try: + return int(s) + except ValueError: + return float(s) + + +## +# Image plugin for the IFUNC IM file format. + + +class ImImageFile(ImageFile.ImageFile): + + format = "IM" + format_description = "IFUNC Image Memory" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + n = 0 + + # Default values + self.info[MODE] = "L" + self.info[SIZE] = (512, 512) + self.info[FRAMES] = 1 + + self.rawmode = "L" + + while True: + + s = self.fp.read(1) + + # Some versions of IFUNC uses \n\r instead of \r\n... + if s == b"\r": + continue + + if not s or s == b"\0" or s == b"\x1A": + break + + # FIXME: this may read whole file if not a text file + s = s + self.fp.readline() + + if len(s) > 100: + raise SyntaxError("not an IM file") + + if s[-2:] == b"\r\n": + s = s[:-2] + elif s[-1:] == b"\n": + s = s[:-1] + + try: + m = split.match(s) + except re.error as e: + raise SyntaxError("not an IM file") from e + + if m: + + k, v = m.group(1, 2) + + # Don't know if this is the correct encoding, + # but a decent guess (I guess) + k = k.decode("latin-1", "replace") + v = v.decode("latin-1", "replace") + + # Convert value as appropriate + if k in [FRAMES, SCALE, SIZE]: + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) + if len(v) == 1: + v = v[0] + elif k == MODE and v in OPEN: + v, self.rawmode = OPEN[v] + + # Add to dictionary. Note that COMMENT tags are + # combined into a list of strings. + if k == COMMENT: + if k in self.info: + self.info[k].append(v) + else: + self.info[k] = [v] + else: + self.info[k] = v + + if k in TAGS: + n += 1 + + else: + + raise SyntaxError( + "Syntax error in IM header: " + s.decode("ascii", "replace") + ) + + if not n: + raise SyntaxError("Not an IM file") + + # Basic attributes + self._size = self.info[SIZE] + self.mode = self.info[MODE] + + # Skip forward to start of image data + while s and s[0:1] != b"\x1A": + s = self.fp.read(1) + if not s: + raise SyntaxError("File truncated") + + if LUT in self.info: + # convert lookup table to palette or lut attribute + palette = self.fp.read(768) + greyscale = 1 # greyscale palette + linear = 1 # linear greyscale palette + for i in range(256): + if palette[i] == palette[i + 256] == palette[i + 512]: + if palette[i] != i: + linear = 0 + else: + greyscale = 0 + if self.mode in ["L", "LA", "P", "PA"]: + if greyscale: + if not linear: + self.lut = list(palette[:256]) + else: + if self.mode in ["L", "P"]: + self.mode = self.rawmode = "P" + elif self.mode in ["LA", "PA"]: + self.mode = "PA" + self.rawmode = "PA;L" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = list(palette) + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self.__fp = self.fp # FIXME: hack + + if self.rawmode[:2] == "F;": + + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [("bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1))] + return + except ValueError: + pass + + if self.rawmode in ["RGB;T", "RYB;T"]: + # Old LabEye/3PC files. Would be very surprised if anyone + # ever stumbled upon such a file ;-) + size = self.size[0] * self.size[1] + self.tile = [ + ("raw", (0, 0) + self.size, offs, ("G", 0, -1)), + ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), + ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1)), + ] + else: + # LabEye/IFUNC files + self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] + + @property + def n_frames(self): + return self.info[FRAMES] + + @property + def is_animated(self): + return self.info[FRAMES] > 1 + + def seek(self, frame): + if not self._seek_check(frame): + return + + self.frame = frame + + if self.mode == "1": + bits = 1 + else: + bits = 8 * len(self.mode) + + size = ((self.size[0] * bits + 7) // 8) * self.size[1] + offs = self.__offset + frame * size + + self.fp = self.__fp + + self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- +# Save IM files + + +SAVE = { + # mode: (im type, raw mode) + "1": ("0 1", "1"), + "L": ("Greyscale", "L"), + "LA": ("LA", "LA;L"), + "P": ("Greyscale", "P"), + "PA": ("LA", "PA;L"), + "I": ("L 32S", "I;32S"), + "I;16": ("L 16", "I;16"), + "I;16L": ("L 16L", "I;16L"), + "I;16B": ("L 16B", "I;16B"), + "F": ("L 32F", "F;32F"), + "RGB": ("RGB", "RGB;L"), + "RGBA": ("RGBA", "RGBA;L"), + "RGBX": ("RGBX", "RGBX;L"), + "CMYK": ("CMYK", "CMYK;L"), + "YCbCr": ("YCC", "YCbCr;L"), +} + + +def _save(im, fp, filename): + + try: + image_type, rawmode = SAVE[im.mode] + except KeyError as e: + raise ValueError(f"Cannot save {im.mode} images as IM") from e + + frames = im.encoderinfo.get("frames", 1) + + fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) + if filename: + # Each line must be 100 characters or less, + # or: SyntaxError("not an IM file") + # 8 characters are used for "Name: " and "\r\n" + # Keep just the filename, ditch the potentially overlong path + name, ext = os.path.splitext(os.path.basename(filename)) + name = "".join([name[: 92 - len(ext)], ext]) + + fp.write(f"Name: {name}\r\n".encode("ascii")) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii")) + fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) + if im.mode in ["P", "PA"]: + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511 - fp.tell()) + b"\032") + if im.mode in ["P", "PA"]: + fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))]) + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(ImImageFile.format, ImImageFile) +Image.register_save(ImImageFile.format, _save) + +Image.register_extension(ImImageFile.format, ".im") diff --git a/venv/Lib/site-packages/PIL/Image.py b/venv/Lib/site-packages/PIL/Image.py new file mode 100644 index 0000000..813ac52 --- /dev/null +++ b/venv/Lib/site-packages/PIL/Image.py @@ -0,0 +1,3697 @@ +# +# The Python Imaging Library. +# $Id$ +# +# the Image class wrapper +# +# partial release history: +# 1995-09-09 fl Created +# 1996-03-11 fl PIL release 0.0 (proof of concept) +# 1996-04-30 fl PIL release 0.1b1 +# 1999-07-28 fl PIL release 1.0 final +# 2000-06-07 fl PIL release 1.1 +# 2000-10-20 fl PIL release 1.1.1 +# 2001-05-07 fl PIL release 1.1.2 +# 2002-03-15 fl PIL release 1.1.3 +# 2003-05-10 fl PIL release 1.1.4 +# 2005-03-28 fl PIL release 1.1.5 +# 2006-12-02 fl PIL release 1.1.6 +# 2009-11-15 fl PIL release 1.1.7 +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import atexit +import builtins +import io +import logging +import math +import numbers +import os +import re +import struct +import sys +import tempfile +import warnings +from collections.abc import Callable, MutableMapping +from enum import IntEnum +from pathlib import Path + +try: + import defusedxml.ElementTree as ElementTree +except ImportError: + ElementTree = None + +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION was removed in Pillow 9.0.0. +# Use __version__ instead. +from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins +from ._binary import i32le, o32be, o32le +from ._util import deferred_error, isPath + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2} + if name in categories: + warnings.warn( + "Image categories are " + deprecated + "Use is_animated instead.", + DeprecationWarning, + stacklevel=2, + ) + return categories[name] + elif name in ("NEAREST", "NONE"): + warnings.warn( + name + + " is " + + deprecated + + "Use Resampling.NEAREST or Dither.NONE instead.", + DeprecationWarning, + stacklevel=2, + ) + return 0 + old_resampling = { + "LINEAR": "BILINEAR", + "CUBIC": "BICUBIC", + "ANTIALIAS": "LANCZOS", + } + if name in old_resampling: + warnings.warn( + name + + " is " + + deprecated + + "Use Resampling." + + old_resampling[name] + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return Resampling[old_resampling[name]] + for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): + if name in enum.__members__: + warnings.warn( + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +logger = logging.getLogger(__name__) + + +class DecompressionBombWarning(RuntimeWarning): + pass + + +class DecompressionBombError(Exception): + pass + + +# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image +MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3) + + +try: + # If the _imaging C module is not present, Pillow will not load. + # Note that other modules should not refer to _imaging directly; + # import Image and use the Image.core variable instead. + # Also note that Image.core is not a publicly documented interface, + # and should be considered private and subject to change. + from . import _imaging as core + + if __version__ != getattr(core, "PILLOW_VERSION", None): + raise ImportError( + "The _imaging extension was built for another version of Pillow or PIL:\n" + f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" + f"Pillow version: {__version__}" + ) + +except ImportError as v: + core = deferred_error(ImportError("The _imaging C module is not installed.")) + # Explanations for ways that we know we might have an import error + if str(v).startswith("Module use of python"): + # The _imaging C module is present, but not compiled for + # the right version (windows only). Print a warning, if + # possible. + warnings.warn( + "The _imaging extension was built for another version of Python.", + RuntimeWarning, + ) + elif str(v).startswith("The _imaging extension"): + warnings.warn(str(v), RuntimeWarning) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + # see docs/porting.rst + raise + + +# works everywhere, win for pypy, not cpython +USE_CFFI_ACCESS = hasattr(sys, "pypy_version_info") +try: + import cffi +except ImportError: + cffi = None + + +def isImageType(t): + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ + return hasattr(t, "im") + + +# +# Constants + +# transpose +class Transpose(IntEnum): + FLIP_LEFT_RIGHT = 0 + FLIP_TOP_BOTTOM = 1 + ROTATE_90 = 2 + ROTATE_180 = 3 + ROTATE_270 = 4 + TRANSPOSE = 5 + TRANSVERSE = 6 + + +# transforms (also defined in Imaging.h) +class Transform(IntEnum): + AFFINE = 0 + EXTENT = 1 + PERSPECTIVE = 2 + QUAD = 3 + MESH = 4 + + +# resampling filters (also defined in Imaging.h) +class Resampling(IntEnum): + NEAREST = 0 + BOX = 4 + BILINEAR = 2 + HAMMING = 5 + BICUBIC = 3 + LANCZOS = 1 + + +_filters_support = { + Resampling.BOX: 0.5, + Resampling.BILINEAR: 1.0, + Resampling.HAMMING: 1.0, + Resampling.BICUBIC: 2.0, + Resampling.LANCZOS: 3.0, +} + + +# dithers +class Dither(IntEnum): + NONE = 0 + ORDERED = 1 # Not yet implemented + RASTERIZE = 2 # Not yet implemented + FLOYDSTEINBERG = 3 # default + + +# palettes/quantizers +class Palette(IntEnum): + WEB = 0 + ADAPTIVE = 1 + + +class Quantize(IntEnum): + MEDIANCUT = 0 + MAXCOVERAGE = 1 + FASTOCTREE = 2 + LIBIMAGEQUANT = 3 + + +if hasattr(core, "DEFAULT_STRATEGY"): + DEFAULT_STRATEGY = core.DEFAULT_STRATEGY + FILTERED = core.FILTERED + HUFFMAN_ONLY = core.HUFFMAN_ONLY + RLE = core.RLE + FIXED = core.FIXED + + +# -------------------------------------------------------------------- +# Registries + +ID = [] +OPEN = {} +MIME = {} +SAVE = {} +SAVE_ALL = {} +EXTENSION = {} +DECODERS = {} +ENCODERS = {} + +# -------------------------------------------------------------------- +# Modes + +_ENDIAN = "<" if sys.byteorder == "little" else ">" + + +def _conv_type_shape(im): + m = ImageMode.getmode(im.mode) + shape = (im.height, im.width) + extra = len(m.bands) + if extra != 1: + shape += (extra,) + return shape, m.typestr + + +MODES = ["1", "CMYK", "F", "HSV", "I", "L", "LAB", "P", "RGB", "RGBA", "RGBX", "YCbCr"] + +# raw modes that may be memory mapped. NOTE: if you change this, you +# may have to modify the stride calculation in map.c too! +_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") + + +def getmodebase(mode): + """ + Gets the "base" mode for given mode. This function returns "L" for + images that contain grayscale data, and "RGB" for images that + contain color data. + + :param mode: Input mode. + :returns: "L" or "RGB". + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).basemode + + +def getmodetype(mode): + """ + Gets the storage type mode. Given a mode, this function returns a + single-layer mode suitable for storing individual bands. + + :param mode: Input mode. + :returns: "L", "I", or "F". + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).basetype + + +def getmodebandnames(mode): + """ + Gets a list of individual band names. Given a mode, this function returns + a tuple containing the names of individual bands (use + :py:method:`~PIL.Image.getmodetype` to get the mode used to store each + individual band. + + :param mode: Input mode. + :returns: A tuple containing band names. The length of the tuple + gives the number of bands in an image of the given mode. + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).bands + + +def getmodebands(mode): + """ + Gets the number of individual bands for this mode. + + :param mode: Input mode. + :returns: The number of bands in this mode. + :exception KeyError: If the input mode was not a standard mode. + """ + return len(ImageMode.getmode(mode).bands) + + +# -------------------------------------------------------------------- +# Helpers + +_initialized = 0 + + +def preinit(): + """Explicitly load standard file format drivers.""" + + global _initialized + if _initialized >= 1: + return + + try: + from . import BmpImagePlugin + + assert BmpImagePlugin + except ImportError: + pass + try: + from . import GifImagePlugin + + assert GifImagePlugin + except ImportError: + pass + try: + from . import JpegImagePlugin + + assert JpegImagePlugin + except ImportError: + pass + try: + from . import PpmImagePlugin + + assert PpmImagePlugin + except ImportError: + pass + try: + from . import PngImagePlugin + + assert PngImagePlugin + except ImportError: + pass + # try: + # import TiffImagePlugin + # assert TiffImagePlugin + # except ImportError: + # pass + + _initialized = 1 + + +def init(): + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + """ + + global _initialized + if _initialized >= 2: + return 0 + + for plugin in _plugins: + try: + logger.debug("Importing %s", plugin) + __import__(f"PIL.{plugin}", globals(), locals(), []) + except ImportError as e: + logger.debug("Image: failed to import %s: %s", plugin, e) + + if OPEN or SAVE: + _initialized = 2 + return 1 + + +# -------------------------------------------------------------------- +# Codec factories (used by tobytes/frombytes and ImageFile.load) + + +def _getdecoder(mode, decoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + decoder = DECODERS[decoder_name] + except KeyError: + pass + else: + return decoder(mode, *args + extra) + + try: + # get decoder + decoder = getattr(core, decoder_name + "_decoder") + except AttributeError as e: + raise OSError(f"decoder {decoder_name} not available") from e + return decoder(mode, *args + extra) + + +def _getencoder(mode, encoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + encoder = ENCODERS[encoder_name] + except KeyError: + pass + else: + return encoder(mode, *args + extra) + + try: + # get encoder + encoder = getattr(core, encoder_name + "_encoder") + except AttributeError as e: + raise OSError(f"encoder {encoder_name} not available") from e + return encoder(mode, *args + extra) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + + +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + + +class _E: + def __init__(self, data): + self.data = data + + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) + + +def _getscaleoffset(expr): + stub = ["stub"] + data = expr(_E(stub)).data + try: + (a, b, c) = data # simplified syntax + if a is stub and b == "__mul__" and isinstance(c, numbers.Number): + return c, 0.0 + if a is stub and b == "__add__" and isinstance(c, numbers.Number): + return 1.0, c + except TypeError: + pass + try: + ((a, b, c), d, e) = data # full syntax + if ( + a is stub + and b == "__mul__" + and isinstance(c, numbers.Number) + and d == "__add__" + and isinstance(e, numbers.Number) + ): + return c, e + except TypeError: + pass + raise ValueError("illegal expression") + + +# -------------------------------------------------------------------- +# Implementation wrapper + + +class Image: + """ + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. + + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` + """ + + format = None + format_description = None + _close_exclusive_fp_after_loading = True + + def __init__(self): + # FIXME: take "new" parameters / other image? + # FIXME: turn mode and size into delegating properties? + self.im = None + self.mode = "" + self._size = (0, 0) + self.palette = None + self.info = {} + self._category = 0 + self.readonly = 0 + self.pyaccess = None + self._exif = None + + def __getattr__(self, name): + if name == "category": + warnings.warn( + "Image categories are deprecated and will be removed in Pillow 10 " + "(2023-07-01). Use is_animated instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._category + raise AttributeError(name) + + @property + def width(self): + return self.size[0] + + @property + def height(self): + return self.size[1] + + @property + def size(self): + return self._size + + def _new(self, im): + new = Image() + new.im = im + new.mode = im.mode + new._size = im.size + if im.mode in ("P", "PA"): + if self.palette: + new.palette = self.palette.copy() + else: + from . import ImagePalette + + new.palette = ImagePalette.ImagePalette() + new.info = self.info.copy() + return new + + # Context manager support + def __enter__(self): + return self + + def __exit__(self, *args): + if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): + if hasattr(self, "_close__fp"): + self._close__fp() + if self.fp: + self.fp.close() + self.fp = None + + def close(self): + """ + Closes the file pointer, if possible. + + This operation will destroy the image core and release its memory. + The image data will be unusable afterward. + + This function is required to close images that have multiple frames or + have not had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for + more information. + """ + try: + if hasattr(self, "_close__fp"): + self._close__fp() + if self.fp: + self.fp.close() + self.fp = None + except Exception as msg: + logger.debug("Error closing: %s", msg) + + if getattr(self, "map", None): + self.map = None + + # Instead of simply setting to None, we're setting up a + # deferred error that will better explain that the core image + # object is gone. + self.im = deferred_error(ValueError("Operation on closed image")) + + def _copy(self): + self.load() + self.im = self.im.copy() + self.pyaccess = None + self.readonly = 0 + + def _ensure_mutable(self): + if self.readonly: + self._copy() + else: + self.load() + + def _dump(self, file=None, format=None, **options): + suffix = "" + if format: + suffix = "." + format + + if not file: + f, filename = tempfile.mkstemp(suffix) + os.close(f) + else: + filename = file + if not filename.endswith(suffix): + filename = filename + suffix + + self.load() + + if not format or format == "PPM": + self.im.save_ppm(filename) + else: + self.save(filename, format, **options) + + return filename + + def __eq__(self, other): + return ( + self.__class__ is other.__class__ + and self.mode == other.mode + and self.size == other.size + and self.info == other.info + and self._category == other._category + and self.getpalette() == other.getpalette() + and self.tobytes() == other.tobytes() + ) + + def __repr__(self): + return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( + self.__class__.__module__, + self.__class__.__name__, + self.mode, + self.size[0], + self.size[1], + id(self), + ) + + def _repr_pretty_(self, p, cycle): + """IPython plain text display support""" + + # Same as __repr__ but without unpredicatable id(self), + # to keep Jupyter notebook `text/plain` output stable. + p.text( + "<%s.%s image mode=%s size=%dx%d>" + % ( + self.__class__.__module__, + self.__class__.__name__, + self.mode, + self.size[0], + self.size[1], + ) + ) + + def _repr_png_(self): + """iPython display hook support + + :returns: png version of the image as bytes + """ + b = io.BytesIO() + try: + self.save(b, "PNG") + except Exception as e: + raise ValueError("Could not save to PNG for display") from e + return b.getvalue() + + class _ArrayData: + def __init__(self, new): + self.__array_interface__ = new + + def __array__(self, dtype=None): + # numpy array interface support + import numpy as np + + new = {} + shape, typestr = _conv_type_shape(self) + new["shape"] = shape + new["typestr"] = typestr + new["version"] = 3 + if self.mode == "1": + # Binary images need to be extended from bits to bytes + # See: https://github.com/python-pillow/Pillow/issues/350 + new["data"] = self.tobytes("raw", "L") + else: + new["data"] = self.tobytes() + + return np.array(self._ArrayData(new), dtype) + + def __getstate__(self): + return [self.info, self.mode, self.size, self.getpalette(), self.tobytes()] + + def __setstate__(self, state): + Image.__init__(self) + self.tile = [] + info, mode, size, palette, data = state + self.info = info + self.mode = mode + self._size = size + self.im = core.new(mode, size) + if mode in ("L", "LA", "P", "PA") and palette: + self.putpalette(palette) + self.frombytes(data) + + def tobytes(self, encoder_name="raw", *args): + """ + Return image as a bytes object. + + .. warning:: + + This method returns the raw image data from the internal + storage. For compressed image data (e.g. PNG, JPEG) use + :meth:`~.save`, with a BytesIO parameter for in-memory + data. + + :param encoder_name: What encoder to use. The default is to + use the standard "raw" encoder. + :param args: Extra arguments to the encoder. + :returns: A :py:class:`bytes` object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if encoder_name == "raw" and args == (): + args = self.mode + + self.load() + + if self.width == 0 or self.height == 0: + return b"" + + # unpack data + e = _getencoder(self.mode, encoder_name, args) + e.setimage(self.im) + + bufsize = max(65536, self.size[0] * 4) # see RawEncode.c + + data = [] + while True: + l, s, d = e.encode(bufsize) + data.append(d) + if s: + break + if s < 0: + raise RuntimeError(f"encoder error {s} in tobytes") + + return b"".join(data) + + def tobitmap(self, name="image"): + """ + Returns the image converted to an X11 bitmap. + + .. note:: This method only works for mode "1" images. + + :param name: The name prefix to use for the bitmap variables. + :returns: A string containing an X11 bitmap. + :raises ValueError: If the mode is not "1" + """ + + self.load() + if self.mode != "1": + raise ValueError("not a bitmap") + data = self.tobytes("xbm") + return b"".join( + [ + f"#define {name}_width {self.size[0]}\n".encode("ascii"), + f"#define {name}_height {self.size[1]}\n".encode("ascii"), + f"static char {name}_bits[] = {{\n".encode("ascii"), + data, + b"};", + ] + ) + + def frombytes(self, data, decoder_name="raw", *args): + """ + Loads this image with pixel data from a bytes object. + + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + # default format + if decoder_name == "raw" and args == (): + args = self.mode + + # unpack data + d = _getdecoder(self.mode, decoder_name, args) + d.setimage(self.im) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") + + def load(self): + """ + Allocates storage for the image and loads the pixel data. In + normal cases, you don't need to call this method, since the + Image class automatically loads an opened image when it is + accessed for the first time. + + If the file associated with the image was opened by Pillow, then this + method will close it. The exception to this is if the image has + multiple frames, in which case the file will be left open for seek + operations. See :ref:`file-handling` for more information. + + :returns: An image access object. + :rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess` + """ + if self.im is not None and self.palette and self.palette.dirty: + # realize palette + mode, arr = self.palette.getdata() + self.im.putpalette(mode, arr) + self.palette.dirty = 0 + self.palette.rawmode = None + if "transparency" in self.info and mode in ("LA", "PA"): + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) + self.palette.mode = "RGBA" + else: + palette_mode = "RGBA" if mode.startswith("RGBA") else "RGB" + self.palette.mode = palette_mode + self.palette.palette = self.im.getpalette(palette_mode, palette_mode) + + if self.im is not None: + if cffi and USE_CFFI_ACCESS: + if self.pyaccess: + return self.pyaccess + from . import PyAccess + + self.pyaccess = PyAccess.new(self, self.readonly) + if self.pyaccess: + return self.pyaccess + return self.im.pixel_access(self.readonly) + + def verify(self): + """ + Verifies the contents of a file. For data read from a file, this + method attempts to determine if the file is broken, without + actually decoding the image data. If this method finds any + problems, it raises suitable exceptions. If you need to load + the image after using this method, you must reopen the image + file. + """ + pass + + def convert( + self, mode=None, matrix=None, dither=None, palette=Palette.WEB, colors=256 + ): + """ + Returns a converted copy of this image. For the "P" mode, this + method translates pixels through the palette. If mode is + omitted, a mode is chosen so that all information in the image + and the palette can be represented without a palette. + + The current version supports all possible conversions between + "L", "RGB" and "CMYK." The ``matrix`` argument only supports "L" + and "RGB". + + When translating a color image to greyscale (mode "L"), + the library uses the ITU-R 601-2 luma transform:: + + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + + The default method of converting a greyscale ("L") or "RGB" + image into a bilevel (mode "1") image uses Floyd-Steinberg + dither to approximate the original image luminosity levels. If + dither is ``None``, all values larger than 127 are set to 255 (white), + all other values to 0 (black). To use other thresholds, use the + :py:meth:`~PIL.Image.Image.point` method. + + When converting from "RGBA" to "P" without a ``matrix`` argument, + this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, + and ``dither`` and ``palette`` are ignored. + + :param mode: The requested mode. See: :ref:`concept-modes`. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 12-tuple containing floating point values. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` + (default). Note that this is not used when ``matrix`` is supplied. + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are :data:`Palette.WEB` or + :data:`Palette.ADAPTIVE`. + :param colors: Number of colors to use for the :data:`Palette.ADAPTIVE` + palette. Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + has_transparency = self.info.get("transparency") is not None + if not mode and self.mode == "P": + # determine default mode + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + if mode == "RGB" and has_transparency: + mode = "RGBA" + if not mode or (mode == self.mode and not matrix): + return self.copy() + + if matrix: + # matrix conversion + if mode not in ("L", "RGB"): + raise ValueError("illegal conversion") + im = self.im.convert_matrix(mode, matrix) + new = self._new(im) + if has_transparency and self.im.bands == 3: + transparency = new.info["transparency"] + + def convert_transparency(m, v): + v = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 + return max(0, min(255, int(v))) + + if mode == "L": + transparency = convert_transparency(matrix, transparency) + elif len(mode) == 3: + transparency = tuple( + convert_transparency(matrix[i * 4 : i * 4 + 4], transparency) + for i in range(0, len(transparency)) + ) + new.info["transparency"] = transparency + return new + + if mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + trns = None + delete_trns = False + # transparency handling + if has_transparency: + if (self.mode in ("1", "L", "I") and mode in ("LA", "RGBA")) or ( + self.mode == "RGB" and mode == "RGBA" + ): + # Use transparent conversion to promote from transparent + # color to an alpha channel. + new_im = self._new( + self.im.convert_transparent(mode, self.info["transparency"]) + ) + del new_im.info["transparency"] + return new_im + elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"): + t = self.info["transparency"] + if isinstance(t, bytes): + # Dragons. This can't be represented by a single color + warnings.warn( + "Palette images with Transparency expressed in bytes should be " + "converted to RGBA images" + ) + delete_trns = True + else: + # get the new transparency color. + # use existing conversions + trns_im = Image()._new(core.new(self.mode, (1, 1))) + if self.mode == "P": + trns_im.putpalette(self.palette) + if isinstance(t, tuple): + err = "Couldn't allocate a palette color for transparency" + try: + t = trns_im.palette.getcolor(t, self) + except ValueError as e: + if str(e) == "cannot allocate more than 256 colors": + # If all 256 colors are in use, + # then there is no need for transparency + t = None + else: + raise ValueError(err) from e + if t is None: + trns = None + else: + trns_im.putpixel((0, 0), t) + + if mode in ("L", "RGB"): + trns_im = trns_im.convert(mode) + else: + # can't just retrieve the palette number, got to do it + # after quantization. + trns_im = trns_im.convert("RGB") + trns = trns_im.getpixel((0, 0)) + + elif self.mode == "P" and mode in ("LA", "PA", "RGBA"): + t = self.info["transparency"] + delete_trns = True + + if isinstance(t, bytes): + self.im.putpalettealphas(t) + elif isinstance(t, int): + self.im.putpalettealpha(t, 0) + else: + raise ValueError("Transparency for P mode should be bytes or int") + + if mode == "P" and palette == Palette.ADAPTIVE: + im = self.im.quantize(colors) + new = self._new(im) + from . import ImagePalette + + new.palette = ImagePalette.ImagePalette("RGB", new.im.getpalette("RGB")) + if delete_trns: + # This could possibly happen if we requantize to fewer colors. + # The transparency would be totally off in that case. + del new.info["transparency"] + if trns is not None: + try: + new.info["transparency"] = new.palette.getcolor(trns, new) + except Exception: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del new.info["transparency"] + warnings.warn("Couldn't allocate palette entry for transparency") + return new + + # colorspace conversion + if dither is None: + dither = Dither.FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + im = self.im.convert(getmodebase(self.mode)) + im = im.convert(mode, dither) + except KeyError as e: + raise ValueError("illegal conversion") from e + + new_im = self._new(im) + if mode == "P" and palette != Palette.ADAPTIVE: + from . import ImagePalette + + new_im.palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) + if delete_trns: + # crash fail if we leave a bytes transparency in an rgb/l mode. + del new_im.info["transparency"] + if trns is not None: + if new_im.mode == "P": + try: + new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im) + except ValueError as e: + del new_im.info["transparency"] + if str(e) != "cannot allocate more than 256 colors": + # If all 256 colors are in use, + # then there is no need for transparency + warnings.warn( + "Couldn't allocate palette entry for transparency" + ) + else: + new_im.info["transparency"] = trns + return new_im + + def quantize( + self, + colors=256, + method=None, + kmeans=0, + palette=None, + dither=Dither.FLOYDSTEINBERG, + ): + """ + Convert the image to 'P' mode with the specified number + of colors. + + :param colors: The desired number of colors, <= 256 + :param method: :data:`Quantize.MEDIANCUT` (median cut), + :data:`Quantize.MAXCOVERAGE` (maximum coverage), + :data:`Quantize.FASTOCTREE` (fast octree), + :data:`Quantize.LIBIMAGEQUANT` (libimagequant; check support + using :py:func:`PIL.features.check_feature` with + ``feature="libimagequant"``). + + By default, :data:`Quantize.MEDIANCUT` will be used. + + The exception to this is RGBA images. :data:`Quantize.MEDIANCUT` + and :data:`Quantize.MAXCOVERAGE` do not support RGBA images, so + :data:`Quantize.FASTOCTREE` is used by default instead. + :param kmeans: Integer + :param palette: Quantize to the palette of given + :py:class:`PIL.Image.Image`. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` + (default). + :returns: A new image + + """ + + self.load() + + if method is None: + # defaults: + method = Quantize.MEDIANCUT + if self.mode == "RGBA": + method = Quantize.FASTOCTREE + + if self.mode == "RGBA" and method not in ( + Quantize.FASTOCTREE, + Quantize.LIBIMAGEQUANT, + ): + # Caller specified an invalid mode. + raise ValueError( + "Fast Octree (method == 2) and libimagequant (method == 3) " + "are the only valid methods for quantizing RGBA images" + ) + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if self.mode != "RGB" and self.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = self.im.convert("P", dither, palette.im) + new_im = self._new(im) + new_im.palette = palette.palette.copy() + return new_im + + im = self._new(self.im.quantize(colors, method, kmeans)) + + from . import ImagePalette + + mode = im.im.getpalettemode() + palette = im.im.getpalette(mode, mode)[: colors * len(mode)] + im.palette = ImagePalette.ImagePalette(mode, palette) + + return im + + def copy(self): + """ + Copies this image. Use this method if you wish to paste things + into an image, but still retain the original. + + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + self.load() + return self._new(self.im.copy()) + + __copy__ = copy + + def crop(self, box=None): + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. See :ref:`coordinate-system`. + + Note: Prior to Pillow 3.4.0, this was a lazy operation. + + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if box is None: + return self.copy() + + if box[2] < box[0]: + raise ValueError("Coordinate 'right' is less than 'left'") + elif box[3] < box[1]: + raise ValueError("Coordinate 'lower' is less than 'upper'") + + self.load() + return self._new(self._crop(self.im, box)) + + def _crop(self, im, box): + """ + Returns a rectangular region from the core image object im. + + This is equivalent to calling im.crop((x0, y0, x1, y1)), but + includes additional sanity checks. + + :param im: a core image object + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :returns: A core image object. + """ + + x0, y0, x1, y1 = map(int, map(round, box)) + + absolute_values = (abs(x1 - x0), abs(y1 - y0)) + + _decompression_bomb_check(absolute_values) + + return im.crop((x0, y0, x1, y1)) + + def draft(self, mode, size): + """ + Configures the image file loader so it returns a version of the + image that as closely as possible matches the given mode and + size. For example, you can use this method to convert a color + JPEG to greyscale while loading it. + + If any changes are made, returns a tuple with the chosen ``mode`` and + ``box`` with coordinates of the original image within the altered one. + + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. + + Note: This method is not implemented for most images. It is + currently implemented only for JPEG and MPO images. + + :param mode: The requested mode. + :param size: The requested size. + """ + pass + + def _expand(self, xmargin, ymargin=None): + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin, 0)) + + def filter(self, filter): + """ + Filters this image using the given filter. For a list of + available filters, see the :py:mod:`~PIL.ImageFilter` module. + + :param filter: Filter kernel. + :returns: An :py:class:`~PIL.Image.Image` object.""" + + from . import ImageFilter + + self.load() + + if isinstance(filter, Callable): + filter = filter() + if not hasattr(filter, "filter"): + raise TypeError( + "filter argument should be ImageFilter.Filter instance or class" + ) + + multiband = isinstance(filter, ImageFilter.MultibandFilter) + if self.im.bands == 1 or multiband: + return self._new(filter.filter(self.im)) + + ims = [] + for c in range(self.im.bands): + ims.append(self._new(filter.filter(self.im.getband(c)))) + return merge(self.mode, ims) + + def getbands(self): + """ + Returns a tuple containing the name of each band in this image. + For example, ``getbands`` on an RGB image returns ("R", "G", "B"). + + :returns: A tuple containing band names. + :rtype: tuple + """ + return ImageMode.getmode(self.mode).bands + + def getbbox(self): + """ + Calculates the bounding box of the non-zero regions in the + image. + + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. See + :ref:`coordinate-system`. If the image is completely empty, this + method returns None. + + """ + + self.load() + return self.im.getbbox() + + def getcolors(self, maxcolors=256): + """ + Returns a list of colors used in this image. + + The colors will be in the image's mode. For example, an RGB image will + return a tuple of (red, green, blue) color values, and a P image will + return the index of the color in the palette. + + :param maxcolors: Maximum number of colors. If this number is + exceeded, this method returns None. The default limit is + 256 colors. + :returns: An unsorted list of (count, pixel) values. + """ + + self.load() + if self.mode in ("1", "L", "P"): + h = self.im.histogram() + out = [] + for i in range(256): + if h[i]: + out.append((h[i], i)) + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + def getdata(self, band=None): + """ + Returns the contents of this image as a sequence object + containing pixel values. The sequence object is flattened, so + that values for line one follow directly after the values of + line zero, and so on. + + Note that the sequence object returned by this method is an + internal PIL data type, which only supports certain sequence + operations. To convert it to an ordinary sequence (e.g. for + printing), use ``list(im.getdata())``. + + :param band: What band to return. The default is to return + all bands. To return a single band, pass in the index + value (e.g. 0 to get the "R" band from an "RGB" image). + :returns: A sequence-like object. + """ + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + def getextrema(self): + """ + Gets the the minimum and maximum pixel values for each band in + the image. + + :returns: For a single-band image, a 2-tuple containing the + minimum and maximum pixel value. For a multi-band image, + a tuple containing one 2-tuple for each band. + """ + + self.load() + if self.im.bands > 1: + extrema = [] + for i in range(self.im.bands): + extrema.append(self.im.getband(i).getextrema()) + return tuple(extrema) + return self.im.getextrema() + + def _getxmp(self, xmp_tags): + def get_name(tag): + return tag.split("}")[1] + + def get_value(element): + value = {get_name(k): v for k, v in element.attrib.items()} + children = list(element) + if children: + for child in children: + name = get_name(child.tag) + child_value = get_value(child) + if name in value: + if not isinstance(value[name], list): + value[name] = [value[name]] + value[name].append(child_value) + else: + value[name] = child_value + elif value: + if element.text: + value["text"] = element.text + else: + return element.text + return value + + if ElementTree is None: + warnings.warn("XMP data cannot be read without defusedxml dependency") + return {} + else: + root = ElementTree.fromstring(xmp_tags) + return {get_name(root.tag): get_value(root)} + + def getexif(self): + if self._exif is None: + self._exif = Exif() + + exif_info = self.info.get("exif") + if exif_info is None: + if "Raw profile type exif" in self.info: + exif_info = bytes.fromhex( + "".join(self.info["Raw profile type exif"].split("\n")[3:]) + ) + elif hasattr(self, "tag_v2"): + self._exif.bigtiff = self.tag_v2._bigtiff + self._exif.endian = self.tag_v2._endian + self._exif.load_from_fp(self.fp, self.tag_v2._offset) + if exif_info is not None: + self._exif.load(exif_info) + + # XMP tags + if 0x0112 not in self._exif: + xmp_tags = self.info.get("XML:com.adobe.xmp") + if xmp_tags: + match = re.search(r'tiff:Orientation="([0-9])"', xmp_tags) + if match: + self._exif[0x0112] = int(match[1]) + + return self._exif + + def getim(self): + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ + + self.load() + return self.im.ptr + + def getpalette(self, rawmode="RGB"): + """ + Returns the image palette as a list. + + :param rawmode: The mode in which to return the palette. ``None`` will + return the palette in its current mode. + + .. versionadded:: 9.1.0 + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ + + self.load() + try: + mode = self.im.getpalettemode() + except ValueError: + return None # no palette + if rawmode is None: + rawmode = mode + return list(self.im.getpalette(mode, rawmode)) + + def getpixel(self, xy): + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). See + :ref:`coordinate-system`. + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ + + self.load() + if self.pyaccess: + return self.pyaccess.getpixel(xy) + return self.im.getpixel(xy) + + def getprojection(self): + """ + Get projection to x and y axes + + :returns: Two sequences, indicating where there are non-zero + pixels along the X-axis and the Y-axis, respectively. + """ + + self.load() + x, y = self.im.getprojection() + return list(x), list(y) + + def histogram(self, mask=None, extrema=None): + """ + Returns a histogram for the image. The histogram is returned as a + list of pixel counts, one for each pixel value in the source + image. Counts are grouped into 256 bins for each band, even if + the image has more than 8 bits per band. If the image has more + than one band, the histograms for all bands are concatenated (for + example, the histogram for an "RGB" image contains 768 values). + + A bilevel image (mode "1") is treated as a greyscale ("L") image + by this method. + + If a mask is provided, the method returns a histogram for those + parts of the image where the mask image is non-zero. The mask + image must have the same size as the image, and be either a + bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :returns: A list containing pixel counts. + """ + self.load() + if mask: + mask.load() + return self.im.histogram((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.histogram(extrema) + return self.im.histogram() + + def entropy(self, mask=None, extrema=None): + """ + Calculates and returns the entropy for the image. + + A bilevel image (mode "1") is treated as a greyscale ("L") + image by this method. + + If a mask is provided, the method employs the histogram for + those parts of the image where the mask image is non-zero. + The mask image must have the same size as the image, and be + either a bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :returns: A float value representing the image entropy + """ + self.load() + if mask: + mask.load() + return self.im.entropy((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.entropy(extrema) + return self.im.entropy() + + def paste(self, im, box=None, mask=None): + """ + Pastes another image into this image. The box argument is either + a 2-tuple giving the upper left corner, a 4-tuple defining the + left, upper, right, and lower pixel coordinate, or None (same as + (0, 0)). See :ref:`coordinate-system`. If a 4-tuple is given, the size + of the pasted image must match the size of the region. + + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for + details). + + Instead of an image, the source can be a integer or tuple + containing pixel values. The method then fills the region + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L", "LA", "RGBA" + or "RGBa" images (if present, the alpha band is used as mask). + Where the mask is 255, the given image is copied as is. Where + the mask is 0, the current value is preserved. Intermediate + values will mix the two images together, including their alpha + channels if they have them. + + See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to + combine images with respect to their alpha channels. + + :param im: Source image or pixel value (integer or tuple). + :param box: An optional 4-tuple giving the region to paste into. + If a 2-tuple is used instead, it's treated as the upper left + corner. If omitted or None, the source is pasted into the + upper left corner. + + If an image is given as the second argument and there is no + third, the box defaults to (0, 0), and the second argument + is interpreted as a mask image. + :param mask: An optional mask image. + """ + + if isImageType(box) and mask is None: + # abbreviated paste(im, mask) syntax + mask = box + box = None + + if box is None: + box = (0, 0) + + if len(box) == 2: + # upper left corner given; get size from image or mask + if isImageType(im): + size = im.size + elif isImageType(mask): + size = mask.size + else: + # FIXME: use self.size here? + raise ValueError("cannot determine region size; use 4-item box") + box += (box[0] + size[0], box[1] + size[1]) + + if isinstance(im, str): + from . import ImageColor + + im = ImageColor.getcolor(im, self.mode) + + elif isImageType(im): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + im = im.im + + self._ensure_mutable() + + if mask: + mask.load() + self.im.paste(im, box, mask.im) + else: + self.im.paste(im, box) + + def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): + """'In-place' analog of Image.alpha_composite. Composites an image + onto this image. + + :param im: image to composite over this one + :param dest: Optional 2 tuple (left, top) specifying the upper + left corner in this (destination) image. + :param source: Optional 2 (left, top) tuple for the upper left + corner in the overlay source image, or 4 tuple (left, top, right, + bottom) for the bounds of the source rectangle + + Performance Note: Not currently implemented in-place in the core layer. + """ + + if not isinstance(source, (list, tuple)): + raise ValueError("Source must be a tuple") + if not isinstance(dest, (list, tuple)): + raise ValueError("Destination must be a tuple") + if not len(source) in (2, 4): + raise ValueError("Source must be a 2 or 4-tuple") + if not len(dest) == 2: + raise ValueError("Destination must be a 2-tuple") + if min(source) < 0: + raise ValueError("Source must be non-negative") + + if len(source) == 2: + source = source + im.size + + # over image, crop if it's not the whole thing. + if source == (0, 0) + im.size: + overlay = im + else: + overlay = im.crop(source) + + # target for the paste + box = dest + (dest[0] + overlay.width, dest[1] + overlay.height) + + # destination image. don't copy if we're using the whole image. + if box == (0, 0) + self.size: + background = self + else: + background = self.crop(box) + + result = alpha_composite(background, overlay) + self.paste(result, box) + + def point(self, lut, mode=None): + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65536 if + self.mode=="I" and mode == "L") values per band in the + image. A function can be used instead, it should take a + single argument. The function is called once for each + possible pixel value, and the resulting table is applied to + all bands of the image. + + It may also be an :py:class:`~PIL.Image.ImagePointHandler` + object:: + + class Example(Image.ImagePointHandler): + def point(self, data): + # Return result + :param mode: Output mode (default is same as input). In the + current version, this can only be used if the source image + has mode "L" or "P", and the output has mode "1" or the + source image mode is "I" and the output mode is "L". + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + if isinstance(lut, ImagePointHandler): + return lut.point(self) + + if callable(lut): + # if it isn't a list, it should be a function + if self.mode in ("I", "I;16", "F"): + # check if the function can be used with point_transform + # UNDONE wiredfool -- I think this prevents us from ever doing + # a gamma function point transform on > 8bit images. + scale, offset = _getscaleoffset(lut) + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + lut = [lut(i) for i in range(256)] * self.im.bands + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + raise ValueError("point operation not supported for this mode") + + return self._new(self.im.point(lut, mode)) + + def putalpha(self, alpha): + """ + Adds or replaces the alpha layer in this image. If the image + does not have an alpha layer, it's converted to "LA" or "RGBA". + The new layer must be either "L" or "1". + + :param alpha: The new alpha layer. This can either be an "L" or "1" + image having the same size as this image, or an integer or + other color value. + """ + + self._ensure_mutable() + + if self.mode not in ("LA", "PA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + except (AttributeError, ValueError) as e: + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "PA", "RGBA"): + raise ValueError from e # sanity check + self.im = im + self.pyaccess = None + self.mode = self.im.mode + except KeyError as e: + raise ValueError("illegal image mode") from e + + if self.mode in ("LA", "PA"): + band = 1 + else: + band = 3 + + if isImageType(alpha): + # alpha layer + if alpha.mode not in ("1", "L"): + raise ValueError("illegal image mode") + alpha.load() + if alpha.mode == "1": + alpha = alpha.convert("L") + else: + # constant alpha + try: + self.im.fillband(band, alpha) + except (AttributeError, ValueError): + # do things the hard way + alpha = new("L", self.size, alpha) + else: + return + + self.im.putband(alpha.im, band) + + def putdata(self, data, scale=1.0, offset=0.0): + """ + Copies pixel data from a flattened sequence object into the image. The + values should start at the upper left corner (0, 0), continue to the + end of the line, followed directly by the first value of the second + line, and so on. Data will be read until either the image or the + sequence ends. The scale and offset values are used to adjust the + sequence values: **pixel = value*scale + offset**. + + :param data: A flattened sequence object. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ + + self._ensure_mutable() + + self.im.putdata(data, scale, offset) + + def putpalette(self, data, rawmode="RGB"): + """ + Attaches a palette to this image. The image must be a "P", "PA", "L" + or "LA" image. + + The palette sequence must contain at most 256 colors, made up of one + integer value for each channel in the raw mode. + For example, if the raw mode is "RGB", then it can contain at most 768 + values, made up of red, green and blue values for the corresponding pixel + index in the 256 colors. + If the raw mode is "RGBA", then it can contain at most 1024 values, + containing red, green, blue and alpha values. + + Alternatively, an 8-bit string may be used instead of an integer sequence. + + :param data: A palette sequence (either a list or a string). + :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode + that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). + """ + from . import ImagePalette + + if self.mode not in ("L", "LA", "P", "PA"): + raise ValueError("illegal image mode") + if isinstance(data, ImagePalette.ImagePalette): + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + if not isinstance(data, bytes): + data = bytes(data) + palette = ImagePalette.raw(rawmode, data) + self.mode = "PA" if "A" in self.mode else "P" + self.palette = palette + self.palette.mode = "RGB" + self.load() # install new palette + + def putpixel(self, xy, value): + """ + Modifies the pixel at the given position. The color is given as + a single numerical value for single-band images, and a tuple for + multi-band images. In addition to this, RGB and RGBA tuples are + accepted for P images. + + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` + module instead. + + See: + + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :param value: The pixel value. + """ + + if self.readonly: + self._copy() + self.load() + + if self.pyaccess: + return self.pyaccess.putpixel(xy, value) + + if ( + self.mode == "P" + and isinstance(value, (list, tuple)) + and len(value) in [3, 4] + ): + # RGB or RGBA value for a P image + value = self.palette.getcolor(value, self) + return self.im.putpixel(xy, value) + + def remap_palette(self, dest_map, source_palette=None): + """ + Rewrites the image to reorder the palette. + + :param dest_map: A list of indexes into the original palette. + e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` + is the identity transform. + :param source_palette: Bytes or None. + :returns: An :py:class:`~PIL.Image.Image` object. + + """ + from . import ImagePalette + + if self.mode not in ("L", "P"): + raise ValueError("illegal image mode") + + if source_palette is None: + if self.mode == "P": + self.load() + source_palette = self.im.getpalette("RGB")[:768] + else: # L-mode + source_palette = bytearray(i // 3 for i in range(768)) + + palette_bytes = b"" + new_positions = [0] * 256 + + # pick only the used colors from the palette + for i, oldPosition in enumerate(dest_map): + palette_bytes += source_palette[oldPosition * 3 : oldPosition * 3 + 3] + new_positions[oldPosition] = i + + # replace the palette color id of all pixel with the new id + + # Palette images are [0..255], mapped through a 1 or 3 + # byte/color map. We need to remap the whole image + # from palette 1 to palette 2. New_positions is + # an array of indexes into palette 1. Palette 2 is + # palette 1 with any holes removed. + + # We're going to leverage the convert mechanism to use the + # C code to remap the image from palette 1 to palette 2, + # by forcing the source image into 'L' mode and adding a + # mapping 'L' mode palette, then converting back to 'L' + # sans palette thus converting the image bytes, then + # assigning the optimized RGB palette. + + # perf reference, 9500x4000 gif, w/~135 colors + # 14 sec prepatch, 1 sec postpatch with optimization forced. + + mapping_palette = bytearray(new_positions) + + m_im = self.copy() + m_im.mode = "P" + + m_im.palette = ImagePalette.ImagePalette("RGB", palette=mapping_palette * 3) + # possibly set palette dirty, then + # m_im.putpalette(mapping_palette, 'L') # converts to 'P' + # or just force it. + # UNDONE -- this is part of the general issue with palettes + m_im.im.putpalette("RGB;L", m_im.palette.tobytes()) + + m_im = m_im.convert("L") + + # Internally, we require 768 bytes for a palette. + new_palette_bytes = palette_bytes + (768 - len(palette_bytes)) * b"\x00" + m_im.putpalette(new_palette_bytes) + m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) + + return m_im + + def _get_safe_box(self, size, resample, box): + """Expands the box so it includes adjacent pixels + that may be used by resampling with the given resampling filter. + """ + filter_support = _filters_support[resample] - 0.5 + scale_x = (box[2] - box[0]) / size[0] + scale_y = (box[3] - box[1]) / size[1] + support_x = filter_support * scale_x + support_y = filter_support * scale_y + + return ( + max(0, int(box[0] - support_x)), + max(0, int(box[1] - support_y)), + min(self.size[0], math.ceil(box[2] + support_x)), + min(self.size[1], math.ceil(box[3] + support_y)), + ) + + def resize(self, size, resample=None, box=None, reducing_gap=None): + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:data:`PIL.Image.Resampling.NEAREST`, + :py:data:`PIL.Image.Resampling.BOX`, + :py:data:`PIL.Image.Resampling.BILINEAR`, + :py:data:`PIL.Image.Resampling.HAMMING`, + :py:data:`PIL.Image.Resampling.BICUBIC` or + :py:data:`PIL.Image.Resampling.LANCZOS`. + If the image has mode "1" or "P", it is always set to + :py:data:`PIL.Image.Resampling.NEAREST`. + If the image mode specifies a number of bits, such as "I;16", then the + default filter is :py:data:`PIL.Image.Resampling.NEAREST`. + Otherwise, the default filter is + :py:data:`PIL.Image.Resampling.BICUBIC`. See: :ref:`concept-filters`. + :param box: An optional 4-tuple of floats providing + the source image region to be scaled. + The values must be within (0, 0, width, height) rectangle. + If omitted or None, the entire source is used. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce`. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is None (no optimization). + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if resample is None: + type_special = ";" in self.mode + resample = Resampling.NEAREST if type_special else Resampling.BICUBIC + elif resample not in ( + Resampling.NEAREST, + Resampling.BILINEAR, + Resampling.BICUBIC, + Resampling.LANCZOS, + Resampling.BOX, + Resampling.HAMMING, + ): + message = f"Unknown resampling filter ({resample})." + + filters = [ + f"{filter[1]} ({filter[0]})" + for filter in ( + (Resampling.NEAREST, "Image.Resampling.NEAREST"), + (Resampling.LANCZOS, "Image.Resampling.LANCZOS"), + (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), + (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), + (Resampling.BOX, "Image.Resampling.BOX"), + (Resampling.HAMMING, "Image.Resampling.HAMMING"), + ) + ] + raise ValueError( + message + " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + ) + + if reducing_gap is not None and reducing_gap < 1.0: + raise ValueError("reducing_gap must be 1.0 or greater") + + size = tuple(size) + + if box is None: + box = (0, 0) + self.size + else: + box = tuple(box) + + if self.size == size and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ("1", "P"): + resample = Resampling.NEAREST + + if self.mode in ["LA", "RGBA"] and resample != Resampling.NEAREST: + im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + im = im.resize(size, resample, box) + return im.convert(self.mode) + + self.load() + + if reducing_gap is not None and resample != Resampling.NEAREST: + factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 + factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 + if factor_x > 1 or factor_y > 1: + reduce_box = self._get_safe_box(size, resample, box) + factor = (factor_x, factor_y) + if callable(self.reduce): + self = self.reduce(factor, box=reduce_box) + else: + self = Image.reduce(self, factor, box=reduce_box) + box = ( + (box[0] - reduce_box[0]) / factor_x, + (box[1] - reduce_box[1]) / factor_y, + (box[2] - reduce_box[0]) / factor_x, + (box[3] - reduce_box[1]) / factor_y, + ) + + return self._new(self.im.resize(size, resample, box)) + + def reduce(self, factor, box=None): + """ + Returns a copy of the image reduced ``factor`` times. + If the size of the image is not dividable by ``factor``, + the resulting size will be rounded up. + + :param factor: A greater than 0 integer or tuple of two integers + for width and height separately. + :param box: An optional 4-tuple of ints providing + the source image region to be reduced. + The values must be within ``(0, 0, width, height)`` rectangle. + If omitted or ``None``, the entire source is used. + """ + if not isinstance(factor, (list, tuple)): + factor = (factor, factor) + + if box is None: + box = (0, 0) + self.size + else: + box = tuple(box) + + if factor == (1, 1) and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ["LA", "RGBA"]: + im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + im = im.reduce(factor, box) + return im.convert(self.mode) + + self.load() + + return self._new(self.im.reduce(factor, box)) + + def rotate( + self, + angle, + resample=Resampling.NEAREST, + expand=0, + center=None, + translate=None, + fillcolor=None, + ): + """ + Returns a rotated copy of this image. This method returns a + copy of this image, rotated the given number of degrees counter + clockwise around its centre. + + :param angle: In degrees counter clockwise. + :param resample: An optional resampling filter. This can be + one of :py:data:`PIL.Image.Resampling.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`PIL.Image.Resampling.BICUBIC` + (cubic spline interpolation in a 4x4 environment). + If omitted, or if the image has mode "1" or "P", it is + set to :py:data:`PIL.Image.Resampling.NEAREST`. See :ref:`concept-filters`. + :param expand: Optional expansion flag. If true, expands the output + image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the + input image. Note that the expand flag assumes rotation around + the center and no translation. + :param center: Optional center of rotation (a 2-tuple). Origin is + the upper left corner. Default is the center of the image. + :param translate: An optional post-rotate translation (a 2-tuple). + :param fillcolor: An optional color for area outside the rotated image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + angle = angle % 360.0 + + # Fast paths regardless of filter, as long as we're not + # translating or changing the center. + if not (center or translate): + if angle == 0: + return self.copy() + if angle == 180: + return self.transpose(Transpose.ROTATE_180) + if angle in (90, 270) and (expand or self.width == self.height): + return self.transpose( + Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 + ) + + # Calculate the affine matrix. Note that this is the reverse + # transformation (from destination image to source) because we + # want to interpolate the (discrete) destination pixel from + # the local area around the (floating) source pixel. + + # The matrix we actually want (note that it operates from the right): + # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) + # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) + # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) + + # The reverse matrix is thus: + # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) + # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) + # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) + + # In any case, the final translation may be updated at the end to + # compensate for the expand flag. + + w, h = self.size + + if translate is None: + post_trans = (0, 0) + else: + post_trans = translate + if center is None: + # FIXME These should be rounded to ints? + rotn_center = (w / 2.0, h / 2.0) + else: + rotn_center = center + + angle = -math.radians(angle) + matrix = [ + round(math.cos(angle), 15), + round(math.sin(angle), 15), + 0.0, + round(-math.sin(angle), 15), + round(math.cos(angle), 15), + 0.0, + ] + + def transform(x, y, matrix): + (a, b, c, d, e, f) = matrix + return a * x + b * y + c, d * x + e * y + f + + matrix[2], matrix[5] = transform( + -rotn_center[0] - post_trans[0], -rotn_center[1] - post_trans[1], matrix + ) + matrix[2] += rotn_center[0] + matrix[5] += rotn_center[1] + + if expand: + # calculate output size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + x, y = transform(x, y, matrix) + xx.append(x) + yy.append(y) + nw = math.ceil(max(xx)) - math.floor(min(xx)) + nh = math.ceil(max(yy)) - math.floor(min(yy)) + + # We multiply a translation matrix from the right. Because of its + # special form, this is the same as taking the image of the + # translation vector as new translation vector. + matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) + w, h = nw, nh + + return self.transform( + (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor + ) + + def save(self, fp, format=None, **params): + """ + Saves this image under the given filename. If no format is + specified, the format to use is determined from the filename + extension, if possible. + + Keyword options can be used to provide additional instructions + to the writer. If a writer doesn't recognise an option, it is + silently ignored. The available options are described in the + :doc:`image format documentation + <../handbook/image-file-formats>` for each writer. + + You can use a file object instead of a filename. In this case, + you must always specify the format. The file object must + implement the ``seek``, ``tell``, and ``write`` + methods, and be opened in binary mode. + + :param fp: A filename (string), pathlib.Path object or file object. + :param format: Optional format override. If omitted, the + format to use is determined from the filename extension. + If a file object was used instead of a filename, this + parameter should always be used. + :param params: Extra parameters to the image writer. + :returns: None + :exception ValueError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception OSError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + filename = "" + open_fp = False + if isinstance(fp, Path): + filename = str(fp) + open_fp = True + elif isPath(fp): + filename = fp + open_fp = True + elif fp == sys.stdout: + try: + fp = sys.stdout.buffer + except AttributeError: + pass + if not filename and hasattr(fp, "name") and isPath(fp.name): + # only set the name for metadata purposes + filename = fp.name + + # may mutate self! + self._ensure_mutable() + + save_all = params.pop("save_all", False) + self.encoderinfo = params + self.encoderconfig = () + + preinit() + + ext = os.path.splitext(filename)[1].lower() + + if not format: + if ext not in EXTENSION: + init() + try: + format = EXTENSION[ext] + except KeyError as e: + raise ValueError(f"unknown file extension: {ext}") from e + + if format.upper() not in SAVE: + init() + if save_all: + save_handler = SAVE_ALL[format.upper()] + else: + save_handler = SAVE[format.upper()] + + created = False + if open_fp: + created = not os.path.exists(filename) + if params.get("append", False): + # Open also for reading ("+"), because TIFF save_all + # writer needs to go back and edit the written data. + fp = builtins.open(filename, "r+b") + else: + fp = builtins.open(filename, "w+b") + + try: + save_handler(self, fp, filename) + except Exception: + if open_fp: + fp.close() + if created: + try: + os.remove(filename) + except PermissionError: + pass + raise + if open_fp: + fp.close() + + def seek(self, frame): + """ + Seeks to the given frame in this sequence file. If you seek + beyond the end of the sequence, the method raises an + ``EOFError`` exception. When a sequence file is opened, the + library automatically seeks to frame 0. + + See :py:meth:`~PIL.Image.Image.tell`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :param frame: Frame number, starting at 0. + :exception EOFError: If the call attempts to seek beyond the end + of the sequence. + """ + + # overridden by file handlers + if frame != 0: + raise EOFError + + def show(self, title=None): + """ + Displays this image. This method is mainly intended for debugging purposes. + + This method calls :py:func:`PIL.ImageShow.show` internally. You can use + :py:func:`PIL.ImageShow.register` to override its default behaviour. + + The image is first saved to a temporary file. By default, it will be in + PNG format. + + On Unix, the image is then opened using the **display**, **eog** or + **xv** utility, depending on which one can be found. + + On macOS, the image is opened with the native Preview application. + + On Windows, the image is opened with the standard PNG display utility. + + :param title: Optional title to use for the image window, where possible. + """ + + _show(self, title=title) + + def split(self): + """ + Split this image into individual bands. This method returns a + tuple of individual image bands from an image. For example, + splitting an "RGB" image creates three new images each + containing a copy of one of the original bands (red, green, + blue). + + If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` + method can be more convenient and faster. + + :returns: A tuple containing bands. + """ + + self.load() + if self.im.bands == 1: + ims = [self.copy()] + else: + ims = map(self._new, self.im.split()) + return tuple(ims) + + def getchannel(self, channel): + """ + Returns an image containing a single channel of the source image. + + :param channel: What channel to return. Could be index + (0 for "R" channel of "RGB") or channel name + ("A" for alpha channel of "RGBA"). + :returns: An image in "L" mode. + + .. versionadded:: 4.3.0 + """ + self.load() + + if isinstance(channel, str): + try: + channel = self.getbands().index(channel) + except ValueError as e: + raise ValueError(f'The image has no channel "{channel}"') from e + + return self._new(self.im.getband(channel)) + + def tell(self): + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :returns: Frame number, starting with 0. + """ + return 0 + + def thumbnail(self, size, resample=Resampling.BICUBIC, reducing_gap=2.0): + """ + Make this image into a thumbnail. This method modifies the + image to contain a thumbnail version of itself, no larger than + the given size. This method calculates an appropriate thumbnail + size to preserve the aspect of the image, calls the + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader + (where applicable), and finally resizes the image. + + Note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, + apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original + image. + + :param size: Requested size. + :param resample: Optional resampling filter. This can be one + of :py:data:`PIL.Image.Resampling.NEAREST`, + :py:data:`PIL.Image.Resampling.BOX`, + :py:data:`PIL.Image.Resampling.BILINEAR`, + :py:data:`PIL.Image.Resampling.HAMMING`, + :py:data:`PIL.Image.Resampling.BICUBIC` or + :py:data:`PIL.Image.Resampling.LANCZOS`. + If omitted, it defaults to :py:data:`PIL.Image.Resampling.BICUBIC`. + (was :py:data:`PIL.Image.Resampling.NEAREST` prior to version 2.5.0). + See: :ref:`concept-filters`. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce` or + :py:meth:`~PIL.Image.Image.draft` for JPEG images. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is 2.0 (very close to fair resampling + while still being faster in many cases). + :returns: None + """ + + x, y = map(math.floor, size) + if x >= self.width and y >= self.height: + return + + def round_aspect(number, key): + return max(min(math.floor(number), math.ceil(number), key=key), 1) + + # preserve aspect ratio + aspect = self.width / self.height + if x / y >= aspect: + x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) + else: + y = round_aspect( + x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) + ) + size = (x, y) + + box = None + if reducing_gap is not None: + res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap)) + if res is not None: + box = res[1] + + if self.size != size: + im = self.resize(size, resample, box=box, reducing_gap=reducing_gap) + + self.im = im.im + self._size = size + self.mode = self.im.mode + + self.readonly = 0 + self.pyaccess = None + + # FIXME: the different transform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + def transform( + self, + size, + method, + data=None, + resample=Resampling.NEAREST, + fill=1, + fillcolor=None, + ): + """ + Transforms this image. This method creates a new image with the + given size, and the same mode as the original, and copies data + to the new image using the given transform. + + :param size: The output size. + :param method: The transformation method. This is one of + :py:data:`PIL.Image.Transform.EXTENT` (cut out a rectangular subregion), + :py:data:`PIL.Image.Transform.AFFINE` (affine transform), + :py:data:`PIL.Image.Transform.PERSPECTIVE` (perspective transform), + :py:data:`PIL.Image.Transform.QUAD` (map a quadrilateral to a rectangle), or + :py:data:`PIL.Image.Transform.MESH` (map a number of source quadrilaterals + in one operation). + + It may also be an :py:class:`~PIL.Image.ImageTransformHandler` + object:: + + class Example(Image.ImageTransformHandler): + def transform(self, size, data, resample, fill=1): + # Return result + + It may also be an object with a ``method.getdata`` method + that returns a tuple supplying new ``method`` and ``data`` values:: + + class Example: + def getdata(self): + method = Image.Transform.EXTENT + data = (0, 0, 100, 100) + return method, data + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:data:`PIL.Image.Resampling.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.Resampling.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:data:`PIL.Image.Resampling.NEAREST`. + See: :ref:`concept-filters`. + :param fill: If ``method`` is an + :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of + the arguments passed to it. Otherwise, it is unused. + :param fillcolor: Optional fill color for the area outside the + transform in the output image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: + return ( + self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + .transform(size, method, data, resample, fill, fillcolor) + .convert(self.mode) + ) + + if isinstance(method, ImageTransformHandler): + return method.transform(size, self, resample=resample, fill=fill) + + if hasattr(method, "getdata"): + # compatibility w. old-style transform objects + method, data = method.getdata() + + if data is None: + raise ValueError("missing method data") + + im = new(self.mode, size, fillcolor) + if self.mode == "P" and self.palette: + im.palette = self.palette.copy() + im.info = self.info.copy() + if method == Transform.MESH: + # list of quads + for box, quad in data: + im.__transformer( + box, self, Transform.QUAD, quad, resample, fillcolor is None + ) + else: + im.__transformer( + (0, 0) + size, self, method, data, resample, fillcolor is None + ) + + return im + + def __transformer( + self, box, image, method, data, resample=Resampling.NEAREST, fill=1 + ): + w = box[2] - box[0] + h = box[3] - box[1] + + if method == Transform.AFFINE: + data = data[0:6] + + elif method == Transform.EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = (x1 - x0) / w + ys = (y1 - y0) / h + method = Transform.AFFINE + data = (xs, 0, x0, 0, ys, y0) + + elif method == Transform.PERSPECTIVE: + data = data[0:8] + + elif method == Transform.QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h + data = ( + x0, + (ne[0] - x0) * As, + (sw[0] - x0) * At, + (se[0] - sw[0] - ne[0] + x0) * As * At, + y0, + (ne[1] - y0) * As, + (sw[1] - y0) * At, + (se[1] - sw[1] - ne[1] + y0) * As * At, + ) + + else: + raise ValueError("unknown transformation method") + + if resample not in ( + Resampling.NEAREST, + Resampling.BILINEAR, + Resampling.BICUBIC, + ): + if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): + message = { + Resampling.BOX: "Image.Resampling.BOX", + Resampling.HAMMING: "Image.Resampling.HAMMING", + Resampling.LANCZOS: "Image.Resampling.LANCZOS", + }[resample] + f" ({resample}) cannot be used." + else: + message = f"Unknown resampling filter ({resample})." + + filters = [ + f"{filter[1]} ({filter[0]})" + for filter in ( + (Resampling.NEAREST, "Image.Resampling.NEAREST"), + (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), + (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), + ) + ] + raise ValueError( + message + " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + ) + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = Resampling.NEAREST + + self.im.transform2(box, image.im, method, data, resample, fill) + + def transpose(self, method): + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:data:`PIL.Image.Transpose.FLIP_LEFT_RIGHT`, + :py:data:`PIL.Image.Transpose.FLIP_TOP_BOTTOM`, + :py:data:`PIL.Image.Transpose.ROTATE_90`, + :py:data:`PIL.Image.Transpose.ROTATE_180`, + :py:data:`PIL.Image.Transpose.ROTATE_270`, + :py:data:`PIL.Image.Transpose.TRANSPOSE` or + :py:data:`PIL.Image.Transpose.TRANSVERSE`. + :returns: Returns a flipped or rotated copy of this image. + """ + + self.load() + return self._new(self.im.transpose(method)) + + def effect_spread(self, distance): + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + return self._new(self.im.effect_spread(distance)) + + def toqimage(self): + """Returns a QImage copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqimage(self) + + def toqpixmap(self): + """Returns a QPixmap copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.toqpixmap(self) + + +# -------------------------------------------------------------------- +# Abstract handlers. + + +class ImagePointHandler: + """ + Used as a mixin by point transforms + (for use with :py:meth:`~PIL.Image.Image.point`) + """ + + pass + + +class ImageTransformHandler: + """ + Used as a mixin by geometry transforms + (for use with :py:meth:`~PIL.Image.Image.transform`) + """ + + pass + + +# -------------------------------------------------------------------- +# Factories + +# +# Debugging + + +def _wedge(): + """Create greyscale wedge (for debugging only)""" + + return Image()._new(core.wedge("L")) + + +def _check_size(size): + """ + Common check to enforce type and sanity check on size tuples + + :param size: Should be a 2 tuple of (width, height) + :returns: True, or raises a ValueError + """ + + if not isinstance(size, (list, tuple)): + raise ValueError("Size must be a tuple") + if len(size) != 2: + raise ValueError("Size must be a tuple of length 2") + if size[0] < 0 or size[1] < 0: + raise ValueError("Width and height must be >= 0") + + return True + + +def new(mode, size, color=0): + """ + Creates a new image with the given mode and size. + + :param mode: The mode to use for the new image. See: + :ref:`concept-modes`. + :param size: A 2-tuple, containing (width, height) in pixels. + :param color: What color to use for the image. Default is black. + If given, this should be a single integer or floating point value + for single-band modes, and a tuple for multi-band modes (one value + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + _check_size(size) + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isinstance(color, str): + # css3-style specifier + + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + + im = Image() + if mode == "P" and isinstance(color, (list, tuple)) and len(color) in [3, 4]: + # RGB or RGBA value for a P image + from . import ImagePalette + + im.palette = ImagePalette.ImagePalette() + color = im.palette.getcolor(color) + return im._new(core.fill(mode, size, color)) + + +def frombytes(mode, size, data, decoder_name="raw", *args): + """ + Creates a copy of an image memory from pixel data in a buffer. + + In its simplest form, this function takes three arguments + (mode, size, and unpacked pixel data). + + You can also use any pixel decoder supported by PIL. For more + information on available decoders, see the section + :ref:`Writing Your Own File Codec `. + + Note that this function decodes pixel data only, not entire images. + If you have an entire image in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A byte buffer containing raw data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + _check_size(size) + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw" and args == (): + args = mode + + im = new(mode, size) + im.frombytes(data, decoder_name, args) + return im + + +def frombuffer(mode, size, data, decoder_name="raw", *args): + """ + Creates an image memory referencing pixel data in a byte buffer. + + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". + + Note that this function decodes pixel data only, not entire images. + If you have an entire image file in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. + + In the current version, the default parameters used for the "raw" decoder + differs from that used for :py:func:`~PIL.Image.frombytes`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A bytes or other buffer object containing raw + data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. For the + default encoder ("raw"), it's recommended that you provide the + full set of parameters:: + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + :returns: An :py:class:`~PIL.Image.Image` object. + + .. versionadded:: 1.1.4 + """ + + _check_size(size) + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw": + if args == (): + args = mode, 0, 1 + if args[0] in _MAPMODES: + im = new(mode, (1, 1)) + im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) + im.readonly = 1 + return im + + return frombytes(mode, size, data, decoder_name, args) + + +def fromarray(obj, mode=None): + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol). + + If ``obj`` is not contiguous, then the ``tobytes`` method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + If you have an image in NumPy:: + + from PIL import Image + import numpy as np + im = Image.open("hopper.jpg") + a = np.asarray(im) + + Then this can be used to convert it to a Pillow image:: + + im = Image.fromarray(a) + + :param obj: Object with array interface + :param mode: Optional mode to use when reading ``obj``. Will be determined from + type if ``None``. + + This will not be used to convert the data after reading, but will be used to + change how the data is read:: + + from PIL import Image + import numpy as np + a = np.full((1, 1), 300) + im = Image.fromarray(a, mode="L") + im.getpixel((0, 0)) # 44 + im = Image.fromarray(a, mode="RGB") + im.getpixel((0, 0)) # (44, 1, 0) + + See: :ref:`concept-modes` for general information about modes. + :returns: An image object. + + .. versionadded:: 1.1.6 + """ + arr = obj.__array_interface__ + shape = arr["shape"] + ndim = len(shape) + strides = arr.get("strides", None) + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr["typestr"] + except KeyError as e: + raise TypeError("Cannot handle this data type") from e + try: + mode, rawmode = _fromarray_typemap[typekey] + except KeyError as e: + raise TypeError("Cannot handle this data type: %s, %s" % typekey) from e + else: + rawmode = mode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + raise ValueError(f"Too many dimensions: {ndim} > {ndmax}.") + + size = 1 if ndim == 1 else shape[1], shape[0] + if strides is not None: + if hasattr(obj, "tobytes"): + obj = obj.tobytes() + else: + obj = obj.tostring() + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + + +def fromqimage(im): + """Creates an image instance from a QImage image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqimage(im) + + +def fromqpixmap(im): + """Creates an image instance from a QPixmap image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + raise ImportError("Qt bindings are not installed") + return ImageQt.fromqpixmap(im) + + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode + # first two members of shape are set to one + ((1, 1), "|b1"): ("1", "1;8"), + ((1, 1), "|u1"): ("L", "L"), + ((1, 1), "|i1"): ("I", "I;8"), + ((1, 1), "u2"): ("I", "I;16B"), + ((1, 1), "i2"): ("I", "I;16BS"), + ((1, 1), "u4"): ("I", "I;32B"), + ((1, 1), "i4"): ("I", "I;32BS"), + ((1, 1), "f4"): ("F", "F;32BF"), + ((1, 1), "f8"): ("F", "F;64BF"), + ((1, 1, 2), "|u1"): ("LA", "LA"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), +} + +# shortcuts +_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") +_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") + + +def _decompression_bomb_check(size): + if MAX_IMAGE_PIXELS is None: + return + + pixels = size[0] * size[1] + + if pixels > 2 * MAX_IMAGE_PIXELS: + raise DecompressionBombError( + f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " + "pixels, could be decompression bomb DOS attack." + ) + + if pixels > MAX_IMAGE_PIXELS: + warnings.warn( + f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " + "could be decompression bomb DOS attack.", + DecompressionBombWarning, + ) + + +def open(fp, mode="r", formats=None): + """ + Opens and identifies the given image file. + + This is a lazy operation; this function identifies the file, but + the file remains open and the actual image data is not read from + the file until you try to process the data (or call the + :py:meth:`~PIL.Image.Image.load` method). See + :py:func:`~PIL.Image.new`. See :ref:`file-handling`. + + :param fp: A filename (string), pathlib.Path object or a file object. + The file object must implement ``file.read``, + ``file.seek``, and ``file.tell`` methods, + and be opened in binary mode. + :param mode: The mode. If given, this argument must be "r". + :param formats: A list or tuple of formats to attempt to load the file in. + This can be used to restrict the set of formats checked. + Pass ``None`` to try all supported formats. You can print the set of + available formats by running ``python3 -m PIL`` or using + the :py:func:`PIL.features.pilinfo` function. + :returns: An :py:class:`~PIL.Image.Image` object. + :exception FileNotFoundError: If the file cannot be found. + :exception PIL.UnidentifiedImageError: If the image cannot be opened and + identified. + :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` + instance is used for ``fp``. + :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. + """ + + if mode != "r": + raise ValueError(f"bad mode {repr(mode)}") + elif isinstance(fp, io.StringIO): + raise ValueError( + "StringIO cannot be used to open an image. " + "Binary data must be used instead." + ) + + if formats is None: + formats = ID + elif not isinstance(formats, (list, tuple)): + raise TypeError("formats must be a list or tuple") + + exclusive_fp = False + filename = "" + if isinstance(fp, Path): + filename = str(fp.resolve()) + elif isPath(fp): + filename = fp + + if filename: + fp = builtins.open(filename, "rb") + exclusive_fp = True + + try: + fp.seek(0) + except (AttributeError, io.UnsupportedOperation): + fp = io.BytesIO(fp.read()) + exclusive_fp = True + + prefix = fp.read(16) + + preinit() + + accept_warnings = [] + + def _open_core(fp, filename, prefix, formats): + for i in formats: + i = i.upper() + if i not in OPEN: + init() + try: + factory, accept = OPEN[i] + result = not accept or accept(prefix) + if type(result) in [str, bytes]: + accept_warnings.append(result) + elif result: + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError, struct.error): + # Leave disabled by default, spams the logs with image + # opening failures that are entirely expected. + # logger.debug("", exc_info=True) + continue + except BaseException: + if exclusive_fp: + fp.close() + raise + return None + + im = _open_core(fp, filename, prefix, formats) + + if im is None: + if init(): + im = _open_core(fp, filename, prefix, formats) + + if im: + im._exclusive_fp = exclusive_fp + return im + + if exclusive_fp: + fp.close() + for message in accept_warnings: + warnings.warn(message) + raise UnidentifiedImageError( + "cannot identify image file %r" % (filename if filename else fp) + ) + + +# +# Image processing. + + +def alpha_composite(im1, im2): + """ + Alpha composite im2 over im1. + + :param im1: The first image. Must have mode RGBA. + :param im2: The second image. Must have mode RGBA, and the same size as + the first image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.alpha_composite(im1.im, im2.im)) + + +def blend(im1, im2, alpha): + """ + Creates a new image by interpolating between two input images, using + a constant alpha:: + + out = image1 * (1.0 - alpha) + image2 * alpha + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :param alpha: The interpolation alpha factor. If alpha is 0.0, a + copy of the first image is returned. If alpha is 1.0, a copy of + the second image is returned. There are no restrictions on the + alpha value. If necessary, the result is clipped to fit into + the allowed output range. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.blend(im1.im, im2.im, alpha)) + + +def composite(image1, image2, mask): + """ + Create composite image by blending images using a transparency mask. + + :param image1: The first image. + :param image2: The second image. Must have the same mode and + size as the first image. + :param mask: A mask image. This image can have mode + "1", "L", or "RGBA", and must have the same size as the + other two images. + """ + + image = image2.copy() + image.paste(image1, None, mask) + return image + + +def eval(image, *args): + """ + Applies the function (which should take one argument) to each pixel + in the given image. If the image has more than one band, the same + function is applied to each band. Note that the function is + evaluated once for each possible pixel value, so you cannot use + random components or other generators. + + :param image: The input image. + :param function: A function object, taking one integer argument. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + return image.point(args[0]) + + +def merge(mode, bands): + """ + Merge a set of single band images into a new multiband image. + + :param mode: The mode to use for the output image. See: + :ref:`concept-modes`. + :param bands: A sequence containing one single-band image for + each band in the output image. All bands must have the + same size. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if getmodebands(mode) != len(bands) or "*" in mode: + raise ValueError("wrong number of bands") + for band in bands[1:]: + if band.mode != getmodetype(mode): + raise ValueError("mode mismatch") + if band.size != bands[0].size: + raise ValueError("size mismatch") + for band in bands: + band.load() + return bands[0]._new(core.merge(mode, *[b.im for b in bands])) + + +# -------------------------------------------------------------------- +# Plugin registry + + +def register_open(id, factory, accept=None): + """ + Register an image file plugin. This function should not be used + in application code. + + :param id: An image format identifier. + :param factory: An image file factory method. + :param accept: An optional function that can be used to quickly + reject images having another format. + """ + id = id.upper() + ID.append(id) + OPEN[id] = factory, accept + + +def register_mime(id, mimetype): + """ + Registers an image MIME type. This function should not be used + in application code. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype + + +def register_save(id, driver): + """ + Registers an image save function. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE[id.upper()] = driver + + +def register_save_all(id, driver): + """ + Registers an image function to save all the frames + of a multiframe format. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE_ALL[id.upper()] = driver + + +def register_extension(id, extension): + """ + Registers an image extension. This function should not be + used in application code. + + :param id: An image format identifier. + :param extension: An extension used for this format. + """ + EXTENSION[extension.lower()] = id.upper() + + +def register_extensions(id, extensions): + """ + Registers image extensions. This function should not be + used in application code. + + :param id: An image format identifier. + :param extensions: A list of extensions used for this format. + """ + for extension in extensions: + register_extension(id, extension) + + +def registered_extensions(): + """ + Returns a dictionary containing all file extensions belonging + to registered plugins + """ + if not EXTENSION: + init() + return EXTENSION + + +def register_decoder(name, decoder): + """ + Registers an image decoder. This function should not be + used in application code. + + :param name: The name of the decoder + :param decoder: A callable(mode, args) that returns an + ImageFile.PyDecoder object + + .. versionadded:: 4.1.0 + """ + DECODERS[name] = decoder + + +def register_encoder(name, encoder): + """ + Registers an image encoder. This function should not be + used in application code. + + :param name: The name of the encoder + :param encoder: A callable(mode, args) that returns an + ImageFile.PyEncoder object + + .. versionadded:: 4.1.0 + """ + ENCODERS[name] = encoder + + +# -------------------------------------------------------------------- +# Simple display support. + + +def _show(image, **options): + from . import ImageShow + + ImageShow.show(image, **options) + + +# -------------------------------------------------------------------- +# Effects + + +def effect_mandelbrot(size, extent, quality): + """ + Generate a Mandelbrot set covering the given extent. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param extent: The extent to cover, as a 4-tuple: + (x0, y0, x1, y2). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size, sigma): + """ + Generate Gaussian noise centered around 128. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param sigma: Standard deviation of noise. + """ + return Image()._new(core.effect_noise(size, sigma)) + + +def linear_gradient(mode): + """ + Generate 256x256 linear gradient from black to white, top to bottom. + + :param mode: Input mode. + """ + return Image()._new(core.linear_gradient(mode)) + + +def radial_gradient(mode): + """ + Generate 256x256 radial gradient from black to white, centre to edge. + + :param mode: Input mode. + """ + return Image()._new(core.radial_gradient(mode)) + + +# -------------------------------------------------------------------- +# Resources + + +def _apply_env_variables(env=None): + if env is None: + env = os.environ + + for var_name, setter in [ + ("PILLOW_ALIGNMENT", core.set_alignment), + ("PILLOW_BLOCK_SIZE", core.set_block_size), + ("PILLOW_BLOCKS_MAX", core.set_blocks_max), + ]: + if var_name not in env: + continue + + var = env[var_name].lower() + + units = 1 + for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: + if var.endswith(postfix): + units = mul + var = var[: -len(postfix)] + + try: + var = int(var) * units + except ValueError: + warnings.warn(f"{var_name} is not int") + continue + + try: + setter(var) + except ValueError as e: + warnings.warn(f"{var_name}: {e}") + + +_apply_env_variables() +atexit.register(core.clear_cache) + + +class Exif(MutableMapping): + endian = None + bigtiff = False + + def __init__(self): + self._data = {} + self._ifds = {} + self._info = None + self._loaded_exif = None + + def _fixup(self, value): + try: + if len(value) == 1 and isinstance(value, tuple): + return value[0] + except Exception: + pass + return value + + def _fixup_dict(self, src_dict): + # Helper function + # returns a dict with any single item tuples/lists as individual values + return {k: self._fixup(v) for k, v in src_dict.items()} + + def _get_ifd_dict(self, offset): + try: + # an offset pointer to the location of the nested embedded IFD. + # It should be a long, but may be corrupted. + self.fp.seek(offset) + except (KeyError, TypeError): + pass + else: + from . import TiffImagePlugin + + info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + info.load(self.fp) + return self._fixup_dict(info) + + def _get_head(self): + version = b"\x2B" if self.bigtiff else b"\x2A" + if self.endian == "<": + head = b"II" + version + b"\x00" + o32le(8) + else: + head = b"MM\x00" + version + o32be(8) + if self.bigtiff: + head += o32le(8) if self.endian == "<" else o32be(8) + head += b"\x00\x00\x00\x00" + return head + + def load(self, data): + # Extract EXIF information. This is highly experimental, + # and is likely to be replaced with something better in a future + # version. + + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + if data == self._loaded_exif: + return + self._loaded_exif = data + self._data.clear() + self._ifds.clear() + if data and data.startswith(b"Exif\x00\x00"): + data = data[6:] + if not data: + self._info = None + return + + self.fp = io.BytesIO(data) + self.head = self.fp.read(8) + # process dictionary + from . import TiffImagePlugin + + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + self.endian = self._info._endian + self.fp.seek(self._info.next) + self._info.load(self.fp) + + def load_from_fp(self, fp, offset=None): + self._loaded_exif = None + self._data.clear() + self._ifds.clear() + + # process dictionary + from . import TiffImagePlugin + + self.fp = fp + if offset is not None: + self.head = self._get_head() + else: + self.head = self.fp.read(8) + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + if self.endian is None: + self.endian = self._info._endian + if offset is None: + offset = self._info.next + self.fp.seek(offset) + self._info.load(self.fp) + + def _get_merged_dict(self): + merged_dict = dict(self) + + # get EXIF extension + if 0x8769 in self: + ifd = self._get_ifd_dict(self[0x8769]) + if ifd: + merged_dict.update(ifd) + + # GPS + if 0x8825 in self: + merged_dict[0x8825] = self._get_ifd_dict(self[0x8825]) + + return merged_dict + + def tobytes(self, offset=8): + from . import TiffImagePlugin + + head = self._get_head() + ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) + for tag, value in self.items(): + if tag in [0x8769, 0x8225, 0x8825] and not isinstance(value, dict): + value = self.get_ifd(tag) + if ( + tag == 0x8769 + and 0xA005 in value + and not isinstance(value[0xA005], dict) + ): + value = value.copy() + value[0xA005] = self.get_ifd(0xA005) + ifd[tag] = value + return b"Exif\x00\x00" + head + ifd.tobytes(offset) + + def get_ifd(self, tag): + if tag not in self._ifds: + if tag in [0x8769, 0x8825]: + # exif, gpsinfo + if tag in self: + self._ifds[tag] = self._get_ifd_dict(self[tag]) + elif tag in [0xA005, 0x927C]: + # interop, makernote + if 0x8769 not in self._ifds: + self.get_ifd(0x8769) + tag_data = self._ifds[0x8769][tag] + if tag == 0x927C: + # makernote + from .TiffImagePlugin import ImageFileDirectory_v2 + + if tag_data[:8] == b"FUJIFILM": + ifd_offset = i32le(tag_data, 8) + ifd_data = tag_data[ifd_offset:] + + makernote = {} + for i in range(0, struct.unpack(" 4: + (offset,) = struct.unpack("H", tag_data[:2])[0]): + ifd_tag, typ, count, data = struct.unpack( + ">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2] + ) + if ifd_tag == 0x1101: + # CameraInfo + (offset,) = struct.unpack(">L", data) + self.fp.seek(offset) + + camerainfo = {"ModelID": self.fp.read(4)} + + self.fp.read(4) + # Seconds since 2000 + camerainfo["TimeStamp"] = i32le(self.fp.read(12)) + + self.fp.read(4) + camerainfo["InternalSerialNumber"] = self.fp.read(4) + + self.fp.read(12) + parallax = self.fp.read(4) + handler = ImageFileDirectory_v2._load_dispatch[ + TiffTags.FLOAT + ][1] + camerainfo["Parallax"] = handler( + ImageFileDirectory_v2(), parallax, False + ) + + self.fp.read(4) + camerainfo["Category"] = self.fp.read(2) + + makernote = {0x1101: dict(self._fixup_dict(camerainfo))} + self._ifds[tag] = makernote + else: + # interop + self._ifds[tag] = self._get_ifd_dict(tag_data) + return self._ifds.get(tag, {}) + + def __str__(self): + if self._info is not None: + # Load all keys into self._data + for tag in self._info.keys(): + self[tag] + + return str(self._data) + + def __len__(self): + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return len(keys) + + def __getitem__(self, tag): + if self._info is not None and tag not in self._data and tag in self._info: + self._data[tag] = self._fixup(self._info[tag]) + del self._info[tag] + return self._data[tag] + + def __contains__(self, tag): + return tag in self._data or (self._info is not None and tag in self._info) + + def __setitem__(self, tag, value): + if self._info is not None and tag in self._info: + del self._info[tag] + self._data[tag] = value + + def __delitem__(self, tag): + if self._info is not None and tag in self._info: + del self._info[tag] + else: + del self._data[tag] + + def __iter__(self): + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return iter(keys) diff --git a/venv/Lib/site-packages/PIL/ImageChops.py b/venv/Lib/site-packages/PIL/ImageChops.py new file mode 100644 index 0000000..61d3a29 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageChops.py @@ -0,0 +1,328 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard channel operations +# +# History: +# 1996-03-24 fl Created +# 1996-08-13 fl Added logical operations (for "1" images) +# 2000-10-12 fl Added offset method (from Image.py) +# +# Copyright (c) 1997-2000 by Secret Labs AB +# Copyright (c) 1996-2000 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +def constant(image, value): + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.new("L", image.size, value) + + +def duplicate(image): + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return image.copy() + + +def invert(image): + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image.load() + return image._new(image.im.chop_invert()) + + +def lighter(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_lighter(image2.im)) + + +def darker(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_darker(image2.im)) + + +def difference(image1, image2): + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_difference(image2.im)) + + +def multiply(image1, image2): + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_multiply(image2.im)) + + +def screen(image1, image2): + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_screen(image2.im)) + + +def soft_light(image1, image2): + """ + Superimposes two images on top of each other using the Soft Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_soft_light(image2.im)) + + +def hard_light(image1, image2): + """ + Superimposes two images on top of each other using the Hard Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_hard_light(image2.im)) + + +def overlay(image1, image2): + """ + Superimposes two images on top of each other using the Overlay algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_overlay(image2.im)) + + +def add(image1, image2, scale=1.0, offset=0): + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add(image2.im, scale, offset)) + + +def subtract(image1, image2, scale=1.0, offset=0): + """ + Subtracts two images, dividing the result by scale and adding the offset. + If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) + + +def add_modulo(image1, image2): + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add_modulo(image2.im)) + + +def subtract_modulo(image1, image2): + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract_modulo(image2.im)) + + +def logical_and(image1, image2): + """Logical AND between two images. + + Both of the images must have mode "1". If you would like to perform a + logical AND on an image with a mode other than "1", try + :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask + as the second image. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_and(image2.im)) + + +def logical_or(image1, image2): + """Logical OR between two images. + + Both of the images must have mode "1". + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_or(image2.im)) + + +def logical_xor(image1, image2): + """Logical XOR between two images. + + Both of the images must have mode "1". + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_xor(image2.im)) + + +def blend(image1, image2, alpha): + """Blend images using constant transparency weight. Alias for + :py:func:`PIL.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.blend(image1, image2, alpha) + + +def composite(image1, image2, mask): + """Create composite using transparency mask. Alias for + :py:func:`PIL.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.composite(image1, image2, mask) + + +def offset(image, xoffset, yoffset=None): + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If ``yoffset`` is omitted, it + is assumed to be equal to ``xoffset``. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + + if yoffset is None: + yoffset = xoffset + image.load() + return image._new(image.im.offset(xoffset, yoffset)) diff --git a/venv/Lib/site-packages/PIL/ImageCms.py b/venv/Lib/site-packages/PIL/ImageCms.py new file mode 100644 index 0000000..ea328e1 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageCms.py @@ -0,0 +1,1029 @@ +# The Python Imaging Library. +# $Id$ + +# Optional color management support, based on Kevin Cazabon's PyCMS +# library. + +# History: + +# 2009-03-08 fl Added to PIL. + +# Copyright (C) 2002-2003 Kevin Cazabon +# Copyright (c) 2009 by Fredrik Lundh +# Copyright (c) 2013 by Eric Soroos + +# See the README file for information on usage and redistribution. See +# below for the original description. + +import sys +import warnings +from enum import IntEnum + +from PIL import Image + +try: + from PIL import _imagingcms +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + _imagingcms = deferred_error(ex) + +DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + https://www.cazabon.com + + pyCMS home page: https://www.cazabon.com/pyCMS + littleCMS home page: https://www.littlecms.com + (littleCMS is Copyright (C) 1998-2001 Marti Maria) + + Originally released under LGPL. Graciously donated to PIL in + March 2009, for distribution under the standard PIL license + + The pyCMS.py module provides a "clean" interface between Python/PIL and + pyCMSdll, taking care of some of the more complex handling of the direct + pyCMSdll functions, as well as error-checking and making sure that all + relevant data is kept together. + + While it is possible to call pyCMSdll functions directly, it's not highly + recommended. + + Version History: + + 1.0.0 pil Oct 2013 Port to LCMS 2. + + 0.1.0 pil mod March 10, 2009 + + Renamed display profile to proof profile. The proof + profile is the profile of the device that is being + simulated, not the profile of the device which is + actually used to display/print the final simulation + (that'd be the output profile) - also see LCMSAPI.txt + input colorspace -> using 'renderingIntent' -> proof + colorspace -> using 'proofRenderingIntent' -> output + colorspace + + Added LCMS FLAGS support. + Added FLAGS["SOFTPROOFING"] as default flag for + buildProofTransform (otherwise the proof profile/intent + would be ignored). + + 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms + + 0.0.2 alpha Jan 6, 2002 + + Added try/except statements around type() checks of + potential CObjects... Python won't let you use type() + on them, and raises a TypeError (stupid, if you ask + me!) + + Added buildProofTransformFromOpenProfiles() function. + Additional fixes in DLL, see DLL code for details. + + 0.0.1 alpha first public release, Dec. 26, 2002 + + Known to-do list with current version (of Python interface, not pyCMSdll): + + none + +""" + +VERSION = "1.0.0 pil" + +# --------------------------------------------------------------------. + +core = _imagingcms + +# +# intent/direction values + + +class Intent(IntEnum): + PERCEPTUAL = 0 + RELATIVE_COLORIMETRIC = 1 + SATURATION = 2 + ABSOLUTE_COLORIMETRIC = 3 + + +class Direction(IntEnum): + INPUT = 0 + OUTPUT = 1 + PROOF = 2 + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +# +# flags + +FLAGS = { + "MATRIXINPUT": 1, + "MATRIXOUTPUT": 2, + "MATRIXONLY": (1 | 2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + # Don't create prelinearization tables on precalculated transforms + # (internal use): + "NOPRELINEARIZATION": 16, + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache + "NOTPRECALC": 256, + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy + "LOWRESPRECALC": 2048, # Use less memory to minimize resources + "WHITEBLACKCOMPENSATION": 8192, + "BLACKPOINTCOMPENSATION": 8192, + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16, # Gridpoints +} + +_MAX_FLAG = 0 +for flag in FLAGS.values(): + if isinstance(flag, int): + _MAX_FLAG = _MAX_FLAG | flag + + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + + +class ImageCmsProfile: + def __init__(self, profile): + """ + :param profile: Either a string representing a filename, + a file like object containing a profile or a + low-level profile object + + """ + + if isinstance(profile, str): + if sys.platform == "win32": + profile_bytes_path = profile.encode() + try: + profile_bytes_path.decode("ascii") + except UnicodeDecodeError: + with open(profile, "rb") as f: + self._set(core.profile_frombytes(f.read())) + return + self._set(core.profile_open(profile), profile) + elif hasattr(profile, "read"): + self._set(core.profile_frombytes(profile.read())) + elif isinstance(profile, _imagingcms.CmsProfile): + self._set(profile) + else: + raise TypeError("Invalid type for Profile") + + def _set(self, profile, filename=None): + self.profile = profile + self.filename = filename + if profile: + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info + else: + self.product_name = None + self.product_info = None + + def tobytes(self): + """ + Returns the profile in a format suitable for embedding in + saved images. + + :returns: a bytes object containing the ICC profile. + """ + + return core.profile_tobytes(self.profile) + + +class ImageCmsTransform(Image.ImagePointHandler): + + """ + Transform. This can be used with the procedural API, or with the standard + :py:func:`~PIL.Image.Image.point` method. + + Will return the output profile in the ``output.info['icc_profile']``. + """ + + def __init__( + self, + input, + output, + input_mode, + output_mode, + intent=Intent.PERCEPTUAL, + proof=None, + proof_intent=Intent.ABSOLUTE_COLORIMETRIC, + flags=0, + ): + if proof is None: + self.transform = core.buildTransform( + input.profile, output.profile, input_mode, output_mode, intent, flags + ) + else: + self.transform = core.buildProofTransform( + input.profile, + output.profile, + proof.profile, + input_mode, + output_mode, + intent, + proof_intent, + flags, + ) + # Note: inputMode and outputMode are for pyCMS compatibility only + self.input_mode = self.inputMode = input_mode + self.output_mode = self.outputMode = output_mode + + self.output_profile = output + + def point(self, im): + return self.apply(im) + + def apply(self, im, imOut=None): + im.load() + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + self.transform.apply(im.im.id, imOut.im.id) + imOut.info["icc_profile"] = self.output_profile.tobytes() + return imOut + + def apply_in_place(self, im): + im.load() + if im.mode != self.output_mode: + raise ValueError("mode mismatch") # wrong output mode + self.transform.apply(im.im.id, im.im.id) + im.info["icc_profile"] = self.output_profile.tobytes() + return im + + +def get_display_profile(handle=None): + """ + (experimental) Fetches the profile for the current display device. + + :returns: ``None`` if the profile is not known. + """ + + if sys.platform != "win32": + return None + + from PIL import ImageWin + + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(handle, 1) + else: + profile = core.get_display_profile_win32(handle or 0) + if profile is None: + return None + return ImageCmsProfile(profile) + + +# --------------------------------------------------------------------. +# pyCMS compatible layer +# --------------------------------------------------------------------. + + +class PyCMSError(Exception): + + """(pyCMS) Exception class. + This is used for all errors in the pyCMS API.""" + + pass + + +def profileToProfile( + im, + inputProfile, + outputProfile, + renderingIntent=Intent.PERCEPTUAL, + outputMode=None, + inPlace=False, + flags=0, +): + """ + (pyCMS) Applies an ICC transformation to a given image, mapping from + ``inputProfile`` to ``outputProfile``. + + If the input or output profiles specified are not valid filenames, a + :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. + If an error occurs during application of the profiles, + a :exc:`PyCMSError` will be raised. + If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), + a :exc:`PyCMSError` will be raised. + + This function applies an ICC transformation to im from ``inputProfile``'s + color space to ``outputProfile``'s color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + ``outputMode`` can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) + or Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", + "CMYK", etc.). Note: if rendering the image "inPlace", outputMode + MUST be the same mode as the input, or omitted completely. If + omitted, the outputMode will be the same as the mode of the input + image (im.mode) + :param inPlace: Boolean. If ``True``, the original image is modified in-place, + and ``None`` is returned. If ``False`` (default), a new + :py:class:`~PIL.Image.Image` object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on + the value of ``inPlace`` + :exception PyCMSError: + """ + + if outputMode is None: + outputMode = im.mode + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + transform = ImageCmsTransform( + inputProfile, + outputProfile, + im.mode, + outputMode, + renderingIntent, + flags=flags, + ) + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + return imOut + + +def getOpenProfile(profileFilename): + """ + (pyCMS) Opens an ICC profile file. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If ``profileFilename`` is not a valid filename for an ICC profile, + a :exc:`PyCMSError` will be raised. + + :param profileFilename: String, as a valid filename path to the ICC profile + you wish to open, or a file-like object. + :returns: A CmsProfile class object. + :exception PyCMSError: + """ + + try: + return ImageCmsProfile(profileFilename) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildTransform( + inputProfile, + outputProfile, + inMode, + outMode, + renderingIntent=Intent.PERCEPTUAL, + flags=0, +): + """ + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``. Use applyTransform to apply the transform to a given + image. + + If the input or output profiles specified are not valid filenames, a + :exc:`PyCMSError` will be raised. If an error occurs during creation + of the transform, a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`PyCMSError` will be raised. + + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile`` using the ``renderingIntent`` to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the ``inMode`` and ``outMode`` attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags + ) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildProofTransform( + inputProfile, + outputProfile, + proofProfile, + inMode, + outMode, + renderingIntent=Intent.PERCEPTUAL, + proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC, + flags=FLAGS["SOFTPROOFING"], +): + """ + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device. + + If the input, output, or proof profiles specified are not valid + filenames, a :exc:`PyCMSError` will be raised. + + If an error occurs during creation of the transform, + a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`PyCMSError` will be raised. + + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device using ``renderingIntent`` and + ``proofRenderingIntent`` to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in ``inMode`` to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the ``proofProfile`` device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, or a + profile object + :param proofProfile: String, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent + you wish to use for proof->output transform + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + if not isinstance(proofProfile, ImageCmsProfile): + proofProfile = ImageCmsProfile(proofProfile) + return ImageCmsTransform( + inputProfile, + outputProfile, + inMode, + outMode, + renderingIntent, + proofProfile, + proofRenderingIntent, + flags, + ) + except (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + + +def applyTransform(im, transform, inPlace=False): + """ + (pyCMS) Applies a transform to a given image. + + If ``im.mode != transform.inMode``, a :exc:`PyCMSError` is raised. + + If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a + :exc:`PyCMSError` is raised. + + If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not + supported by pyCMSdll or the profiles you used for the transform, a + :exc:`PyCMSError` is raised. + + If an error occurs while the transform is being applied, + a :exc:`PyCMSError` is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) + to an image. The transform can be used for multiple images, saving + considerable calculation time if doing the same conversion multiple times. + + If you want to modify im in-place instead of receiving a new image as + the return value, set ``inPlace`` to ``True``. This can only be done if + ``transform.inMode`` and ``transform.outMode`` are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new :py:class:`~PIL.Image.Image` + object of the same dimensions in mode ``transform.outMode``. + + :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same + as the ``inMode`` supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is + returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the + transform applied is returned (and ``im`` is not changed). The default is + ``False``. + :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, + depending on the value of ``inPlace``. The profile will be returned in + the image's ``info['icc_profile']``. + :exception PyCMSError: + """ + + try: + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (TypeError, ValueError) as v: + raise PyCMSError(v) from v + + return imOut + + +def createProfile(colorSpace, colorTemp=-1): + """ + (pyCMS) Creates a profile. + + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, + a :exc:`PyCMSError` is raised. + + If using LAB and ``colorTemp`` is not a positive integer, + a :exc:`PyCMSError` is raised. + + If an error occurs while creating the profile, + a :exc:`PyCMSError` is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + :param colorSpace: String, the color space of the profile you wish to + create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + :param colorTemp: Positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB + profiles, and is ignored for XYZ and sRGB. + :returns: A CmsProfile class object + :exception PyCMSError: + """ + + if colorSpace not in ["LAB", "XYZ", "sRGB"]: + raise PyCMSError( + f"Color space not supported for on-the-fly profile creation ({colorSpace})" + ) + + if colorSpace == "LAB": + try: + colorTemp = float(colorTemp) + except (TypeError, ValueError) as e: + raise PyCMSError( + f'Color temperature must be numeric, "{colorTemp}" not valid' + ) from e + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileName(profile): + """ + + (pyCMS) Gets the internal product name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised If an error occurs while trying + to obtain the name tag, a :exc:`PyCMSError` is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal name of the profile as stored + in an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.model + manufacturer = profile.profile.manufacturer + + if not (model or manufacturer): + return (profile.profile.profile_description or "") + "\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return f"{model} - {manufacturer}\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileInfo(profile): + """ + (pyCMS) Gets the internal product information for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the info tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # add an extra newline to preserve pyCMS compatibility + # Python, not C. the white point bits weren't working well, + # so skipping. + # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.profile_description + cpright = profile.profile.copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr) + "\r\n\r\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileCopyright(profile): + """ + (pyCMS) Gets the copyright for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the copyright tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + copyright tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.copyright or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileManufacturer(profile): + """ + (pyCMS) Gets the manufacturer for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the manufacturer tag, a + :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + manufacturer tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.manufacturer or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileModel(profile): + """ + (pyCMS) Gets the model for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the model tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + model tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.model or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileDescription(profile): + """ + (pyCMS) Gets the description for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the description tag, + a :exc:`PyCMSError` is raised. + + Use this function to obtain the information stored in the profile's + description tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in an + ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return (profile.profile.profile_description or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getDefaultIntent(profile): + """ + (pyCMS) Gets the default intent name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the default intent, a + :exc:`PyCMSError` is raised. + + Use this function to determine the default (and usually best optimized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this + profile. + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.rendering_intent + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def isIntentSupported(profile, intent, direction): + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + ``intent`` with ``profile``, and that ``profile`` can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents, so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential :exc:`PyCMSError` that will occur if they don't + support the modes you select. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to + use with this profile + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param direction: Integer specifying if the profile is to be used for + input, output, or proof + + INPUT = 0 (or use ImageCms.Direction.INPUT) + OUTPUT = 1 (or use ImageCms.Direction.OUTPUT) + PROOF = 2 (or use ImageCms.Direction.PROOF) + + :returns: 1 if the intent/direction are supported, -1 if they are not. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # FIXME: I get different results for the same data w. different + # compilers. Bug in LittleCMS or in the binding? + if profile.profile.is_intent_supported(intent, direction): + return 1 + else: + return -1 + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def versions(): + """ + (pyCMS) Fetches versions. + """ + + return (VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__) diff --git a/venv/Lib/site-packages/PIL/ImageColor.py b/venv/Lib/site-packages/PIL/ImageColor.py new file mode 100644 index 0000000..25f92f2 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageColor.py @@ -0,0 +1,302 @@ +# +# The Python Imaging Library +# $Id$ +# +# map CSS3-style colour description strings to RGB +# +# History: +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-15 fl Added RGBA support +# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 +# 2004-07-19 fl Fixed gray/grey spelling issues +# 2009-03-05 fl Fixed rounding error in grayscale calculation +# +# Copyright (c) 2002-2004 by Secret Labs AB +# Copyright (c) 2002-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import re + +from . import Image + + +def getrgb(color): + """ + Convert a color string to an RGB or RGBA tuple. If the string cannot be + parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue[, alpha])`` + """ + if len(color) > 100: + raise ValueError("color specifier is too long") + color = color.lower() + + rgb = colormap.get(color, None) + if rgb: + if isinstance(rgb, tuple): + return rgb + colormap[color] = rgb = getrgb(rgb) + return rgb + + # check for known string formats + if re.match("#[a-f0-9]{3}$", color): + return (int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16)) + + if re.match("#[a-f0-9]{4}$", color): + return ( + int(color[1] * 2, 16), + int(color[2] * 2, 16), + int(color[3] * 2, 16), + int(color[4] * 2, 16), + ) + + if re.match("#[a-f0-9]{6}$", color): + return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + + if re.match("#[a-f0-9]{8}$", color): + return ( + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16), + int(color[7:9], 16), + ) + + m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return (int(m.group(1)), int(m.group(2)), int(m.group(3))) + + m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + return ( + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5), + ) + + m = re.match( + r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hls_to_rgb + + rgb = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5), + ) + + m = re.match( + r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hsv_to_rgb + + rgb = hsv_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(2)) / 100.0, + float(m.group(3)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5), + ) + + m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))) + raise ValueError(f"unknown color specifier: {repr(color)}") + + +def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` + """ + # same as getrgb, but converts the result to the given mode + color, alpha = getrgb(color), 255 + if len(color) == 4: + color, alpha = color[0:3], color[3] + + if Image.getmodebase(mode) == "L": + r, g, b = color + # ITU-R Recommendation 601-2 for nonlinear RGB + # scaled to 24 bits to match the convert's implementation. + color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 + if mode[-1] == "A": + return (color, alpha) + else: + if mode[-1] == "A": + return color + (alpha,) + return color + + +colormap = { + # X11 colour table from https://drafts.csswg.org/css-color-4/, with + # gray/grey spelling issues fixed. This is a superset of HTML 4.0 + # colour names used in CSS 1. + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgrey": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "grey": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgreen": "#90ee90", + "lightgray": "#d3d3d3", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "rebeccapurple": "#663399", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", +} diff --git a/venv/Lib/site-packages/PIL/ImageDraw.py b/venv/Lib/site-packages/PIL/ImageDraw.py new file mode 100644 index 0000000..610ccd4 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageDraw.py @@ -0,0 +1,1004 @@ +# +# The Python Imaging Library +# $Id$ +# +# drawing interface operations +# +# History: +# 1996-04-13 fl Created (experimental) +# 1996-08-07 fl Filled polygons, ellipses. +# 1996-08-13 fl Added text support +# 1998-06-28 fl Handle I and F images +# 1998-12-29 fl Added arc; use arc primitive to draw ellipses +# 1999-01-10 fl Added shape stuff (experimental) +# 1999-02-06 fl Added bitmap support +# 1999-02-11 fl Changed all primitives to take options +# 1999-02-20 fl Fixed backwards compatibility +# 2000-10-12 fl Copy on write, when necessary +# 2001-02-18 fl Use default ink for bitmap/text also in fill mode +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing +# 2002-12-11 fl Refactored low-level drawing API (work in progress) +# 2004-08-26 fl Made Draw() a factory function, added getdraw() support +# 2004-09-04 fl Added width support to line primitive +# 2004-09-10 fl Added font mode handling +# 2006-06-19 fl Added font bearing support (getmask2) +# +# Copyright (c) 1997-2006 by Secret Labs AB +# Copyright (c) 1996-2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import math +import numbers + +from . import Image, ImageColor, ImageFont + +""" +A simple 2D drawing interface for PIL images. +

+Application code should use the Draw factory, instead of +directly. +""" + + +class ImageDraw: + def __init__(self, im, mode=None): + """ + Create a drawing instance. + + :param im: The image to draw in. + :param mode: Optional mode to use for color values. For RGB + images, this argument can be RGB or RGBA (to blend the + drawing into the image). For all other modes, this argument + must be the same as the image mode. If omitted, the mode + defaults to the mode of the image. + """ + im.load() + if im.readonly: + im._copy() # make it writeable + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + raise ValueError("mode mismatch") + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self._image = im + self.im = im.im + self.draw = Image.core.draw(self.im, blend) + self.mode = mode + if mode in ("I", "F"): + self.ink = self.draw.draw_ink(1) + else: + self.ink = self.draw.draw_ink(-1) + if mode in ("1", "P", "I", "F"): + # FIXME: fix Fill2 to properly support matte for I+F images + self.fontmode = "1" + else: + self.fontmode = "L" # aliasing is okay for other modes + self.fill = 0 + self.font = None + + def getfont(self): + """ + Get the current default font. + + :returns: An image font.""" + if not self.font: + # FIXME: should add a font repository + from . import ImageFont + + self.font = ImageFont.load_default() + return self.font + + def _getink(self, ink, fill=None): + if ink is None and fill is None: + if self.fill: + fill = self.ink + else: + ink = self.ink + else: + if ink is not None: + if isinstance(ink, str): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink, self._image) + ink = self.draw.draw_ink(ink) + if fill is not None: + if isinstance(fill, str): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and not isinstance(fill, numbers.Number): + fill = self.palette.getcolor(fill, self._image) + fill = self.draw.draw_ink(fill) + return ink, fill + + def arc(self, xy, start, end, fill=None, width=1): + """Draw an arc.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink, width) + + def bitmap(self, xy, bitmap, fill=None): + """Draw a bitmap.""" + bitmap.load() + ink, fill = self._getink(fill) + if ink is None: + ink = fill + if ink is not None: + self.draw.draw_bitmap(xy, bitmap.im, ink) + + def chord(self, xy, start, end, fill=None, outline=None, width=1): + """Draw a chord.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_chord(xy, start, end, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_chord(xy, start, end, ink, 0, width) + + def ellipse(self, xy, fill=None, outline=None, width=1): + """Draw an ellipse.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_ellipse(xy, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_ellipse(xy, ink, 0, width) + + def line(self, xy, fill=None, width=0, joint=None): + """Draw a line, or a connected sequence of line segments.""" + ink = self._getink(fill)[0] + if ink is not None: + self.draw.draw_lines(xy, ink, width) + if joint == "curve" and width > 4: + if not isinstance(xy[0], (list, tuple)): + xy = [tuple(xy[i : i + 2]) for i in range(0, len(xy), 2)] + for i in range(1, len(xy) - 1): + point = xy[i] + angles = [ + math.degrees(math.atan2(end[0] - start[0], start[1] - end[1])) + % 360 + for start, end in ((xy[i - 1], point), (point, xy[i + 1])) + ] + if angles[0] == angles[1]: + # This is a straight line, so no joint is required + continue + + def coord_at_angle(coord, angle): + x, y = coord + angle -= 90 + distance = width / 2 - 1 + return tuple( + p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d)) + for p, p_d in ( + (x, distance * math.cos(math.radians(angle))), + (y, distance * math.sin(math.radians(angle))), + ) + ) + + flipped = ( + angles[1] > angles[0] and angles[1] - 180 > angles[0] + ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0]) + coords = [ + (point[0] - width / 2 + 1, point[1] - width / 2 + 1), + (point[0] + width / 2 - 1, point[1] + width / 2 - 1), + ] + if flipped: + start, end = (angles[1] + 90, angles[0] + 90) + else: + start, end = (angles[0] - 90, angles[1] - 90) + self.pieslice(coords, start - 90, end - 90, fill) + + if width > 8: + # Cover potential gaps between the line and the joint + if flipped: + gapCoords = [ + coord_at_angle(point, angles[0] + 90), + point, + coord_at_angle(point, angles[1] + 90), + ] + else: + gapCoords = [ + coord_at_angle(point, angles[0] - 90), + point, + coord_at_angle(point, angles[1] - 90), + ] + self.line(gapCoords, fill, width=3) + + def shape(self, shape, fill=None, outline=None): + """(Experimental) Draw a shape.""" + shape.close() + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_outline(shape, fill, 1) + if ink is not None and ink != fill: + self.draw.draw_outline(shape, ink, 0) + + def pieslice(self, xy, start, end, fill=None, outline=None, width=1): + """Draw a pieslice.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_pieslice(xy, start, end, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_pieslice(xy, start, end, ink, 0, width) + + def point(self, xy, fill=None): + """Draw one or more individual pixels.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + def polygon(self, xy, fill=None, outline=None, width=1): + """Draw a polygon.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_polygon(xy, fill, 1) + if ink is not None and ink != fill and width != 0: + if width == 1: + self.draw.draw_polygon(xy, ink, 0, width) + else: + # To avoid expanding the polygon outwards, + # use the fill as a mask + mask = Image.new("1", self.im.size) + mask_ink = self._getink(1)[0] + + fill_im = mask.copy() + draw = Draw(fill_im) + draw.draw.draw_polygon(xy, mask_ink, 1) + + ink_im = mask.copy() + draw = Draw(ink_im) + width = width * 2 - 1 + draw.draw.draw_polygon(xy, mask_ink, 0, width) + + mask.paste(ink_im, mask=fill_im) + + im = Image.new(self.mode, self.im.size) + draw = Draw(im) + draw.draw.draw_polygon(xy, ink, 0, width) + self.im.paste(im.im, (0, 0) + im.size, mask.im) + + def regular_polygon( + self, bounding_circle, n_sides, rotation=0, fill=None, outline=None + ): + """Draw a regular polygon.""" + xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) + self.polygon(xy, fill, outline) + + def rectangle(self, xy, fill=None, outline=None, width=1): + """Draw a rectangle.""" + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_rectangle(xy, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_rectangle(xy, ink, 0, width) + + def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1): + """Draw a rounded rectangle.""" + if isinstance(xy[0], (list, tuple)): + (x0, y0), (x1, y1) = xy + else: + x0, y0, x1, y1 = xy + + d = radius * 2 + + full_x = d >= x1 - x0 + if full_x: + # The two left and two right corners are joined + d = x1 - x0 + full_y = d >= y1 - y0 + if full_y: + # The two top and two bottom corners are joined + d = y1 - y0 + if full_x and full_y: + # If all corners are joined, that is a circle + return self.ellipse(xy, fill, outline, width) + + if d == 0: + # If the corners have no curve, that is a rectangle + return self.rectangle(xy, fill, outline, width) + + r = d // 2 + ink, fill = self._getink(outline, fill) + + def draw_corners(pieslice): + if full_x: + # Draw top and bottom halves + parts = ( + ((x0, y0, x0 + d, y0 + d), 180, 360), + ((x0, y1 - d, x0 + d, y1), 0, 180), + ) + elif full_y: + # Draw left and right halves + parts = ( + ((x0, y0, x0 + d, y0 + d), 90, 270), + ((x1 - d, y0, x1, y0 + d), 270, 90), + ) + else: + # Draw four separate corners + parts = ( + ((x1 - d, y0, x1, y0 + d), 270, 360), + ((x1 - d, y1 - d, x1, y1), 0, 90), + ((x0, y1 - d, x0 + d, y1), 90, 180), + ((x0, y0, x0 + d, y0 + d), 180, 270), + ) + for part in parts: + if pieslice: + self.draw.draw_pieslice(*(part + (fill, 1))) + else: + self.draw.draw_arc(*(part + (ink, width))) + + if fill is not None: + draw_corners(True) + + if full_x: + self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill, 1) + else: + self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill, 1) + if not full_x and not full_y: + self.draw.draw_rectangle((x0, y0 + r + 1, x0 + r, y1 - r - 1), fill, 1) + self.draw.draw_rectangle((x1 - r, y0 + r + 1, x1, y1 - r - 1), fill, 1) + if ink is not None and ink != fill and width != 0: + draw_corners(False) + + if not full_x: + self.draw.draw_rectangle( + (x0 + r + 1, y0, x1 - r - 1, y0 + width - 1), ink, 1 + ) + self.draw.draw_rectangle( + (x0 + r + 1, y1 - width + 1, x1 - r - 1, y1), ink, 1 + ) + if not full_y: + self.draw.draw_rectangle( + (x0, y0 + r + 1, x0 + width - 1, y1 - r - 1), ink, 1 + ) + self.draw.draw_rectangle( + (x1 - width + 1, y0 + r + 1, x1, y1 - r - 1), ink, 1 + ) + + def _multiline_check(self, text): + """Draw text.""" + split_character = "\n" if isinstance(text, str) else b"\n" + + return split_character in text + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, str) else b"\n" + + return text.split(split_character) + + def text( + self, + xy, + text, + fill=None, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + stroke_fill=None, + embedded_color=False, + *args, + **kwargs, + ): + if self._multiline_check(text): + return self.multiline_text( + xy, + text, + fill, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + stroke_fill, + embedded_color, + ) + + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if font is None: + font = self.getfont() + + def getink(fill): + ink, fill = self._getink(fill) + if ink is None: + return fill + return ink + + def draw_text(ink, stroke_width=0, stroke_offset=None): + mode = self.fontmode + if stroke_width == 0 and embedded_color: + mode = "RGBA" + coord = xy + try: + mask, offset = font.getmask2( + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + anchor=anchor, + ink=ink, + *args, + **kwargs, + ) + coord = coord[0] + offset[0], coord[1] + offset[1] + except AttributeError: + try: + mask = font.getmask( + text, + mode, + direction, + features, + language, + stroke_width, + anchor, + ink, + *args, + **kwargs, + ) + except TypeError: + mask = font.getmask(text) + if stroke_offset: + coord = coord[0] + stroke_offset[0], coord[1] + stroke_offset[1] + if mode == "RGBA": + # font.getmask2(mode="RGBA") returns color in RGB bands and mask in A + # extract mask and set text alpha + color, mask = mask, mask.getband(3) + color.fillband(3, (ink >> 24) & 0xFF) + coord2 = coord[0] + mask.size[0], coord[1] + mask.size[1] + self.im.paste(color, coord + coord2, mask) + else: + self.draw.draw_bitmap(coord, mask, ink) + + ink = getink(fill) + if ink is not None: + stroke_ink = None + if stroke_width: + stroke_ink = getink(stroke_fill) if stroke_fill is not None else ink + + if stroke_ink is not None: + # Draw stroked text + draw_text(stroke_ink, stroke_width) + + # Draw normal text + draw_text(ink, 0) + else: + # Only draw normal text + draw_text(ink) + + def multiline_text( + self, + xy, + text, + fill=None, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + stroke_fill=None, + embedded_color=False, + ): + if direction == "ttb": + raise ValueError("ttb direction is unsupported for multiline text") + + if anchor is None: + anchor = "la" + elif len(anchor) != 2: + raise ValueError("anchor must be a 2 character string") + elif anchor[1] in "tb": + raise ValueError("anchor not supported for multiline text") + + widths = [] + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width = self.textlength( + line, font, direction=direction, features=features, language=language + ) + widths.append(line_width) + max_width = max(max_width, line_width) + + top = xy[1] + if anchor[1] == "m": + top -= (len(lines) - 1) * line_spacing / 2.0 + elif anchor[1] == "d": + top -= (len(lines) - 1) * line_spacing + + for idx, line in enumerate(lines): + left = xy[0] + width_difference = max_width - widths[idx] + + # first align left by anchor + if anchor[0] == "m": + left -= width_difference / 2.0 + elif anchor[0] == "r": + left -= width_difference + + # then align by align parameter + if align == "left": + pass + elif align == "center": + left += width_difference / 2.0 + elif align == "right": + left += width_difference + else: + raise ValueError('align must be "left", "center" or "right"') + + self.text( + (left, top), + line, + fill, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + stroke_fill=stroke_fill, + embedded_color=embedded_color, + ) + top += line_spacing + + def textsize( + self, + text, + font=None, + spacing=4, + direction=None, + features=None, + language=None, + stroke_width=0, + ): + """Get the size of a given string, in pixels.""" + if self._multiline_check(text): + return self.multiline_textsize( + text, font, spacing, direction, features, language, stroke_width + ) + + if font is None: + font = self.getfont() + return font.getsize(text, direction, features, language, stroke_width) + + def multiline_textsize( + self, + text, + font=None, + spacing=4, + direction=None, + features=None, + language=None, + stroke_width=0, + ): + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width, line_height = self.textsize( + line, font, spacing, direction, features, language, stroke_width + ) + max_width = max(max_width, line_width) + return max_width, len(lines) * line_spacing - spacing + + def textlength( + self, + text, + font=None, + direction=None, + features=None, + language=None, + embedded_color=False, + ): + """Get the length of a given string, in pixels with 1/64 precision.""" + if self._multiline_check(text): + raise ValueError("can't measure length of multiline text") + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if font is None: + font = self.getfont() + mode = "RGBA" if embedded_color else self.fontmode + try: + return font.getlength(text, mode, direction, features, language) + except AttributeError: + size = self.textsize( + text, font, direction=direction, features=features, language=language + ) + if direction == "ttb": + return size[1] + return size[0] + + def textbbox( + self, + xy, + text, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + embedded_color=False, + ): + """Get the bounding box of a given string, in pixels.""" + if embedded_color and self.mode not in ("RGB", "RGBA"): + raise ValueError("Embedded color supported only in RGB and RGBA modes") + + if self._multiline_check(text): + return self.multiline_textbbox( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + ) + + if font is None: + font = self.getfont() + if not isinstance(font, ImageFont.FreeTypeFont): + raise ValueError("Only supported for TrueType fonts") + mode = "RGBA" if embedded_color else self.fontmode + bbox = font.getbbox( + text, mode, direction, features, language, stroke_width, anchor + ) + return bbox[0] + xy[0], bbox[1] + xy[1], bbox[2] + xy[0], bbox[3] + xy[1] + + def multiline_textbbox( + self, + xy, + text, + font=None, + anchor=None, + spacing=4, + align="left", + direction=None, + features=None, + language=None, + stroke_width=0, + embedded_color=False, + ): + if direction == "ttb": + raise ValueError("ttb direction is unsupported for multiline text") + + if anchor is None: + anchor = "la" + elif len(anchor) != 2: + raise ValueError("anchor must be a 2 character string") + elif anchor[1] in "tb": + raise ValueError("anchor not supported for multiline text") + + widths = [] + max_width = 0 + lines = self._multiline_split(text) + line_spacing = ( + self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing + ) + for line in lines: + line_width = self.textlength( + line, + font, + direction=direction, + features=features, + language=language, + embedded_color=embedded_color, + ) + widths.append(line_width) + max_width = max(max_width, line_width) + + top = xy[1] + if anchor[1] == "m": + top -= (len(lines) - 1) * line_spacing / 2.0 + elif anchor[1] == "d": + top -= (len(lines) - 1) * line_spacing + + bbox = None + + for idx, line in enumerate(lines): + left = xy[0] + width_difference = max_width - widths[idx] + + # first align left by anchor + if anchor[0] == "m": + left -= width_difference / 2.0 + elif anchor[0] == "r": + left -= width_difference + + # then align by align parameter + if align == "left": + pass + elif align == "center": + left += width_difference / 2.0 + elif align == "right": + left += width_difference + else: + raise ValueError('align must be "left", "center" or "right"') + + bbox_line = self.textbbox( + (left, top), + line, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + embedded_color=embedded_color, + ) + if bbox is None: + bbox = bbox_line + else: + bbox = ( + min(bbox[0], bbox_line[0]), + min(bbox[1], bbox_line[1]), + max(bbox[2], bbox_line[2]), + max(bbox[3], bbox_line[3]), + ) + + top += line_spacing + + if bbox is None: + return xy[0], xy[1], xy[0], xy[1] + return bbox + + +def Draw(im, mode=None): + """ + A simple 2D drawing interface for PIL images. + + :param im: The image to draw in. + :param mode: Optional mode to use for color values. For RGB + images, this argument can be RGB or RGBA (to blend the + drawing into the image). For all other modes, this argument + must be the same as the image mode. If omitted, the mode + defaults to the mode of the image. + """ + try: + return im.getdraw(mode) + except AttributeError: + return ImageDraw(im, mode) + + +# experimental access to the outline API +try: + Outline = Image.core.outline +except AttributeError: + Outline = None + + +def getdraw(im=None, hints=None): + """ + (Experimental) A more advanced 2D drawing interface for PIL images, + based on the WCK interface. + + :param im: The image to draw in. + :param hints: An optional list of hints. + :returns: A (drawing context, drawing resource factory) tuple. + """ + # FIXME: this needs more work! + # FIXME: come up with a better 'hints' scheme. + handler = None + if not hints or "nicest" in hints: + try: + from . import _imagingagg as handler + except ImportError: + pass + if handler is None: + from . import ImageDraw2 as handler + if im: + im = handler.Draw(im) + return im, handler + + +def floodfill(image, xy, value, border=None, thresh=0): + """ + (experimental) Fills a bounded region with a given color. + + :param image: Target image. + :param xy: Seed position (a 2-item coordinate tuple). See + :ref:`coordinate-system`. + :param value: Fill color. + :param border: Optional border value. If given, the region consists of + pixels with a color different from the border color. If not given, + the region consists of pixels having the same color as the seed + pixel. + :param thresh: Optional threshold value which specifies a maximum + tolerable difference of a pixel value from the 'background' in + order for it to be replaced. Useful for filling regions of + non-homogeneous, but similar, colors. + """ + # based on an implementation by Eric S. Raymond + # amended by yo1995 @20180806 + pixel = image.load() + x, y = xy + try: + background = pixel[x, y] + if _color_diff(value, background) <= thresh: + return # seed point already has fill color + pixel[x, y] = value + except (ValueError, IndexError): + return # seed point outside image + edge = {(x, y)} + # use a set to keep record of current and previous edge pixels + # to reduce memory consumption + full_edge = set() + while edge: + new_edge = set() + for (x, y) in edge: # 4 adjacent method + for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): + # If already processed, or if a coordinate is negative, skip + if (s, t) in full_edge or s < 0 or t < 0: + continue + try: + p = pixel[s, t] + except (ValueError, IndexError): + pass + else: + full_edge.add((s, t)) + if border is None: + fill = _color_diff(p, background) <= thresh + else: + fill = p != value and p != border + if fill: + pixel[s, t] = value + new_edge.add((s, t)) + full_edge = edge # discard pixels processed + edge = new_edge + + +def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation): + """ + Generate a list of vertices for a 2D regular polygon. + + :param bounding_circle: The bounding circle is a tuple defined + by a point and radius. The polygon is inscribed in this circle. + (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) + :param n_sides: Number of sides + (e.g. ``n_sides=3`` for a triangle, ``6`` for a hexagon) + :param rotation: Apply an arbitrary rotation to the polygon + (e.g. ``rotation=90``, applies a 90 degree rotation) + :return: List of regular polygon vertices + (e.g. ``[(25, 50), (50, 50), (50, 25), (25, 25)]``) + + How are the vertices computed? + 1. Compute the following variables + - theta: Angle between the apothem & the nearest polygon vertex + - side_length: Length of each polygon edge + - centroid: Center of bounding circle (1st, 2nd elements of bounding_circle) + - polygon_radius: Polygon radius (last element of bounding_circle) + - angles: Location of each polygon vertex in polar grid + (e.g. A square with 0 degree rotation => [225.0, 315.0, 45.0, 135.0]) + + 2. For each angle in angles, get the polygon vertex at that angle + The vertex is computed using the equation below. + X= xcos(φ) + ysin(φ) + Y= −xsin(φ) + ycos(φ) + + Note: + φ = angle in degrees + x = 0 + y = polygon_radius + + The formula above assumes rotation around the origin. + In our case, we are rotating around the centroid. + To account for this, we use the formula below + X = xcos(φ) + ysin(φ) + centroid_x + Y = −xsin(φ) + ycos(φ) + centroid_y + """ + # 1. Error Handling + # 1.1 Check `n_sides` has an appropriate value + if not isinstance(n_sides, int): + raise TypeError("n_sides should be an int") + if n_sides < 3: + raise ValueError("n_sides should be an int > 2") + + # 1.2 Check `bounding_circle` has an appropriate value + if not isinstance(bounding_circle, (list, tuple)): + raise TypeError("bounding_circle should be a tuple") + + if len(bounding_circle) == 3: + *centroid, polygon_radius = bounding_circle + elif len(bounding_circle) == 2: + centroid, polygon_radius = bounding_circle + else: + raise ValueError( + "bounding_circle should contain 2D coordinates " + "and a radius (e.g. (x, y, r) or ((x, y), r) )" + ) + + if not all(isinstance(i, (int, float)) for i in (*centroid, polygon_radius)): + raise ValueError("bounding_circle should only contain numeric data") + + if not len(centroid) == 2: + raise ValueError( + "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" + ) + + if polygon_radius <= 0: + raise ValueError("bounding_circle radius should be > 0") + + # 1.3 Check `rotation` has an appropriate value + if not isinstance(rotation, (int, float)): + raise ValueError("rotation should be an int or float") + + # 2. Define Helper Functions + def _apply_rotation(point, degrees, centroid): + return ( + round( + point[0] * math.cos(math.radians(360 - degrees)) + - point[1] * math.sin(math.radians(360 - degrees)) + + centroid[0], + 2, + ), + round( + point[1] * math.cos(math.radians(360 - degrees)) + + point[0] * math.sin(math.radians(360 - degrees)) + + centroid[1], + 2, + ), + ) + + def _compute_polygon_vertex(centroid, polygon_radius, angle): + start_point = [polygon_radius, 0] + return _apply_rotation(start_point, angle, centroid) + + def _get_angles(n_sides, rotation): + angles = [] + degrees = 360 / n_sides + # Start with the bottom left polygon vertex + current_angle = (270 - 0.5 * degrees) + rotation + for _ in range(0, n_sides): + angles.append(current_angle) + current_angle += degrees + if current_angle > 360: + current_angle -= 360 + return angles + + # 3. Variable Declarations + angles = _get_angles(n_sides, rotation) + + # 4. Compute Vertices + return [ + _compute_polygon_vertex(centroid, polygon_radius, angle) for angle in angles + ] + + +def _color_diff(color1, color2): + """ + Uses 1-norm distance to calculate difference between two values. + """ + if isinstance(color2, tuple): + return sum(abs(color1[i] - color2[i]) for i in range(0, len(color2))) + else: + return abs(color1 - color2) diff --git a/venv/Lib/site-packages/PIL/ImageDraw2.py b/venv/Lib/site-packages/PIL/ImageDraw2.py new file mode 100644 index 0000000..1f63110 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageDraw2.py @@ -0,0 +1,179 @@ +# +# The Python Imaging Library +# $Id$ +# +# WCK-style drawing interface operations +# +# History: +# 2003-12-07 fl created +# 2005-05-15 fl updated; added to PIL as ImageDraw2 +# 2005-05-15 fl added text support +# 2005-05-20 fl added arc/chord/pieslice support +# +# Copyright (c) 2003-2005 by Secret Labs AB +# Copyright (c) 2003-2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +""" +(Experimental) WCK-style drawing interface operations + +.. seealso:: :py:mod:`PIL.ImageDraw` +""" + + +from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath + + +class Pen: + """Stores an outline color and width.""" + + def __init__(self, color, width=1, opacity=255): + self.color = ImageColor.getrgb(color) + self.width = width + + +class Brush: + """Stores a fill color""" + + def __init__(self, color, opacity=255): + self.color = ImageColor.getrgb(color) + + +class Font: + """Stores a TrueType font and color""" + + def __init__(self, color, file, size=12): + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + + +class Draw: + """ + (Experimental) WCK-style drawing interface + """ + + def __init__(self, image, size=None, color=None): + if not hasattr(image, "im"): + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform = None + + def flush(self): + return self.image + + def render(self, op, xy, pen, brush=None): + # handle color arguments + outline = fill = None + width = 1 + if isinstance(pen, Pen): + outline = pen.color + width = pen.width + elif isinstance(brush, Pen): + outline = brush.color + width = brush.width + if isinstance(brush, Brush): + fill = brush.color + elif isinstance(pen, Brush): + fill = pen.color + # handle transformation + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + # render the item + if op == "line": + self.draw.line(xy, fill=outline, width=width) + else: + getattr(self.draw, op)(xy, fill=fill, outline=outline) + + def settransform(self, offset): + """Sets a transformation offset.""" + (xoffset, yoffset) = offset + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc(self, xy, start, end, *options): + """ + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` + """ + self.render("arc", xy, start, end, *options) + + def chord(self, xy, start, end, *options): + """ + Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points + with a straight line. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` + """ + self.render("chord", xy, start, end, *options) + + def ellipse(self, xy, *options): + """ + Draws an ellipse inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` + """ + self.render("ellipse", xy, *options) + + def line(self, xy, *options): + """ + Draws a line between the coordinates in the ``xy`` list. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` + """ + self.render("line", xy, *options) + + def pieslice(self, xy, start, end, *options): + """ + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` + """ + self.render("pieslice", xy, start, end, *options) + + def polygon(self, xy, *options): + """ + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` + """ + self.render("polygon", xy, *options) + + def rectangle(self, xy, *options): + """ + Draws a rectangle. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` + """ + self.render("rectangle", xy, *options) + + def text(self, xy, text, font): + """ + Draws the string at the given position. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` + """ + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textsize(self, text, font): + """ + Return the size of the given string, in pixels. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` + """ + return self.draw.textsize(text, font=font.font) diff --git a/venv/Lib/site-packages/PIL/ImageEnhance.py b/venv/Lib/site-packages/PIL/ImageEnhance.py new file mode 100644 index 0000000..3b79d5c --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageEnhance.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image enhancement classes +# +# For a background, see "Image Processing By Interpolation and +# Extrapolation", Paul Haeberli and Douglas Voorhies. Available +# at http://www.graficaobscura.com/interp/index.html +# +# History: +# 1996-03-23 fl Created +# 2009-06-16 fl Fixed mean calculation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFilter, ImageStat + + +class _Enhance: + def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ + return Image.blend(self.degenerate, self.image, factor) + + +class Color(_Enhance): + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ + + def __init__(self, image): + self.image = image + self.intermediate_mode = "L" + if "A" in image.getbands(): + self.intermediate_mode = "LA" + + self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + + +class Contrast(_Enhance): + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ + + def __init__(self, image): + self.image = image + mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) + self.degenerate = Image.new("L", image.size, mean).convert(image.mode) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) + + +class Brightness(_Enhance): + """Adjust image brightness. + + This class can be used to control the brightness of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ + + def __init__(self, image): + self.image = image + self.degenerate = Image.new(image.mode, image.size, 0) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) + + +class Sharpness(_Enhance): + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ + + def __init__(self, image): + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) diff --git a/venv/Lib/site-packages/PIL/ImageFile.py b/venv/Lib/site-packages/PIL/ImageFile.py new file mode 100644 index 0000000..34f344f --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageFile.py @@ -0,0 +1,748 @@ +# +# The Python Imaging Library. +# $Id$ +# +# base class for image file handlers +# +# history: +# 1995-09-09 fl Created +# 1996-03-11 fl Fixed load mechanism. +# 1996-04-15 fl Added pcx/xbm decoders. +# 1996-04-30 fl Added encoders. +# 1996-12-14 fl Added load helpers +# 1997-01-11 fl Use encode_to_file where possible +# 1997-08-27 fl Flush output in _save +# 1998-03-05 fl Use memory mapping for some modes +# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" +# 1999-05-31 fl Added image parser +# 2000-10-12 fl Set readonly flag on memory-mapped images +# 2002-03-20 fl Use better messages for common decoder errors +# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available +# 2003-10-30 fl Added StubImageFile class +# 2004-02-25 fl Made incremental parser more robust +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import io +import itertools +import struct +import sys + +from . import Image +from ._util import isPath + +MAXBLOCK = 65536 + +SAFEBLOCK = 1024 * 1024 + +LOAD_TRUNCATED_IMAGES = False +"""Whether or not to load truncated image files. User code may change this.""" + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error", +} +""" +Dict of known error codes returned from :meth:`.PyDecoder.decode`, +:meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and +:meth:`.PyEncoder.encode_to_file`. +""" + + +# +# -------------------------------------------------------------------- +# Helpers + + +def raise_oserror(error): + try: + message = Image.core.getcodecstatus(error) + except AttributeError: + message = ERRORS.get(error) + if not message: + message = f"decoder error {error}" + raise OSError(message + " when reading image file") + + +def _tilesort(t): + # sort on offset + return t[2] + + +# +# -------------------------------------------------------------------- +# ImageFile base class + + +class ImageFile(Image.Image): + """Base class for image file format handlers.""" + + def __init__(self, fp=None, filename=None): + super().__init__() + + self._min_frame = 0 + + self.custom_mimetype = None + + self.tile = None + """ A list of tile descriptors, or ``None`` """ + + self.readonly = 1 # until we know better + + self.decoderconfig = () + self.decodermaxblock = MAXBLOCK + + if isPath(fp): + # filename + self.fp = open(fp, "rb") + self.filename = fp + self._exclusive_fp = True + else: + # stream + self.fp = fp + self.filename = filename + # can be overridden + self._exclusive_fp = None + + try: + try: + self._open() + except ( + IndexError, # end of data + TypeError, # end of data (ord) + KeyError, # unsupported mode + EOFError, # got header but not the first frame + struct.error, + ) as v: + raise SyntaxError(v) from v + + if not self.mode or self.size[0] <= 0: + raise SyntaxError("not identified by this driver") + except BaseException: + # close the file only if we have opened it this constructor + if self._exclusive_fp: + self.fp.close() + raise + + def get_format_mimetype(self): + if self.custom_mimetype: + return self.custom_mimetype + if self.format is not None: + return Image.MIME.get(self.format.upper()) + + def verify(self): + """Check file integrity""" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + if self._exclusive_fp: + self.fp.close() + self.fp = None + + def load(self): + """Load image data based on tile list""" + + if self.tile is None: + raise OSError("cannot load this image") + + pixel = Image.Image.load(self) + if not self.tile: + return pixel + + self.map = None + use_mmap = self.filename and len(self.tile) == 1 + # As of pypy 2.1.0, memory mapping was failing here. + use_mmap = use_mmap and not hasattr(sys, "pypy_version_info") + + readonly = 0 + + # look for read/seek overrides + try: + read = self.load_read + # don't use mmap if there are custom read/seek functions + use_mmap = False + except AttributeError: + read = self.fp.read + + try: + seek = self.load_seek + use_mmap = False + except AttributeError: + seek = self.fp.seek + + if use_mmap: + # try memory mapping + decoder_name, extents, offset, args = self.tile[0] + if ( + decoder_name == "raw" + and len(args) >= 3 + and args[0] == self.mode + and args[0] in Image._MAPMODES + ): + try: + # use mmap, if possible + import mmap + + with open(self.filename) as fp: + self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) + self.im = Image.core.map_buffer( + self.map, self.size, decoder_name, offset, args + ) + readonly = 1 + # After trashing self.im, + # we might need to reload the palette data. + if self.palette: + self.palette.dirty = 1 + except (AttributeError, OSError, ImportError): + self.map = None + + self.load_prepare() + err_code = -3 # initialize to unknown error + if not self.map: + # sort tiles in file order + self.tile.sort(key=_tilesort) + + try: + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = self.tile_prefix + except AttributeError: + prefix = b"" + + # Remove consecutive duplicates that only differ by their offset + self.tile = [ + list(tiles)[-1] + for _, tiles in itertools.groupby( + self.tile, lambda tile: (tile[0], tile[1], tile[3]) + ) + ] + for decoder_name, extents, offset, args in self.tile: + seek(offset) + decoder = Image._getdecoder( + self.mode, decoder_name, args, self.decoderconfig + ) + try: + decoder.setimage(self.im, extents) + if decoder.pulls_fd: + decoder.setfd(self.fp) + err_code = decoder.decode(b"")[1] + else: + b = prefix + while True: + try: + s = read(self.decodermaxblock) + except (IndexError, struct.error) as e: + # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise OSError("image file is truncated") from e + + if not s: # truncated jpeg + if LOAD_TRUNCATED_IMAGES: + break + else: + raise OSError( + "image file is truncated " + f"({len(b)} bytes not processed)" + ) + + b = b + s + n, err_code = decoder.decode(b) + if n < 0: + break + b = b[n:] + finally: + # Need to cleanup here to prevent leaks + decoder.cleanup() + + self.tile = [] + self.readonly = readonly + + self.load_end() + + if self._exclusive_fp and self._close_exclusive_fp_after_loading: + self.fp.close() + self.fp = None + + if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: + # still raised if decoder fails to return anything + raise_oserror(err_code) + + return Image.Image.load(self) + + def load_prepare(self): + # create image memory if necessary + if not self.im or self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self): + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos): + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, bytes): + # pass + + def _seek_check(self, frame): + if ( + frame < self._min_frame + # Only check upper limit on frames if additional seek operations + # are not required to do so + or ( + not (hasattr(self, "_n_frames") and self._n_frames is None) + and frame >= self.n_frames + self._min_frame + ) + ): + raise EOFError("attempt to seek outside sequence") + + return self.tell() != frame + + +class StubImageFile(ImageFile): + """ + Base class for stub image loaders. + + A stub loader is an image loader that can identify files of a + certain format, but relies on external code to load the file. + """ + + def _open(self): + raise NotImplementedError("StubImageFile subclass must implement _open") + + def load(self): + loader = self._load() + if loader is None: + raise OSError(f"cannot find loader for this {self.format} file") + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ + self.__dict__ = image.__dict__ + return image.load() + + def _load(self): + """(Hook) Find actual image loader.""" + raise NotImplementedError("StubImageFile subclass must implement _load") + + +class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + """ + + incremental = None + image = None + data = None + decoder = None + offset = 0 + finished = 0 + + def reset(self): + """ + (Consumer) Reset the parser. Note that you can only call this + method immediately after you've created a parser; parser + instances cannot be reused. + """ + assert self.data is None, "cannot reuse parsers" + + def feed(self, data): + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception OSError: If the parser failed to parse the image file. + """ + # collect data + + if self.finished: + return + + if self.data is None: + self.data = data + else: + self.data = self.data + data + + # parse what we have + if self.decoder: + + if self.offset > 0: + # skip header + skip = min(len(self.data), self.offset) + self.data = self.data[skip:] + self.offset = self.offset - skip + if self.offset > 0 or not self.data: + return + + n, e = self.decoder.decode(self.data) + + if n < 0: + # end of stream + self.data = None + self.finished = 1 + if e < 0: + # decoding error + self.image = None + raise_oserror(e) + else: + # end of image + return + self.data = self.data[n:] + + elif self.image: + + # if we end up here with no decoder, this file cannot + # be incrementally parsed. wait until we've gotten all + # available data + pass + + else: + + # attempt to open this file + try: + with io.BytesIO(self.data) as fp: + im = Image.open(fp) + except OSError: + # traceback.print_exc() + pass # not enough data + else: + flag = hasattr(im, "load_seek") or hasattr(im, "load_read") + if flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # initialize decoder + im.load_prepare() + d, e, o, a = im.tile[0] + im.tile = [] + self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig) + self.decoder.setimage(im.im, e) + + # calculate decoder offset + self.offset = o + if self.offset <= len(self.data): + self.data = self.data[self.offset :] + self.offset = 0 + + self.image = im + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception OSError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. + """ + # finish decoding + if self.decoder: + # get rid of what's left in the buffers + self.feed(b"") + self.data = self.decoder = None + if not self.finished: + raise OSError("image was incomplete") + if not self.image: + raise OSError("cannot parse this image") + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + with io.BytesIO(self.data) as fp: + try: + self.image = Image.open(fp) + finally: + self.image.load() + return self.image + + +# -------------------------------------------------------------------- + + +def _save(im, fp, tile, bufsize=0): + """Helper to save image based on tile list + + :param im: Image object. + :param fp: File object. + :param tile: Tile list. + :param bufsize: Optional buffer size + """ + + im.load() + if not hasattr(im, "encoderconfig"): + im.encoderconfig = () + tile.sort(key=_tilesort) + # FIXME: make MAXBLOCK a configuration parameter + # It would be great if we could have the encoder specify what it needs + # But, it would need at least the image size in most cases. RawEncode is + # a tricky case. + bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c + try: + fh = fp.fileno() + fp.flush() + exc = None + except (AttributeError, io.UnsupportedOperation) as e: + exc = e + for e, b, o, a in tile: + if o > 0: + fp.seek(o) + encoder = Image._getencoder(im.mode, e, a, im.encoderconfig) + try: + encoder.setimage(im.im, b) + if encoder.pushes_fd: + encoder.setfd(fp) + l, s = encoder.encode_to_pyfd() + else: + if exc: + # compress to Python file-compatible object + while True: + l, s, d = encoder.encode(bufsize) + fp.write(d) + if s: + break + else: + # slight speedup: compress to real file object + s = encoder.encode_to_file(fh, bufsize) + if s < 0: + raise OSError(f"encoder error {s} when writing image file") from exc + finally: + encoder.cleanup() + if hasattr(fp, "flush"): + fp.flush() + + +def _safe_read(fp, size): + """ + Reads large blocks in a safe way. Unlike fp.read(n), this function + doesn't trust the user. If the requested size is larger than + SAFEBLOCK, the file is read block by block. + + :param fp: File handle. Must implement a read method. + :param size: Number of bytes to read. + :returns: A string containing size bytes of data. + + Raises an OSError if the file is truncated and the read cannot be completed + + """ + if size <= 0: + return b"" + if size <= SAFEBLOCK: + data = fp.read(size) + if len(data) < size: + raise OSError("Truncated File Read") + return data + data = [] + remaining_size = size + while remaining_size > 0: + block = fp.read(min(remaining_size, SAFEBLOCK)) + if not block: + break + data.append(block) + remaining_size -= len(block) + if sum(len(d) for d in data) < size: + raise OSError("Truncated File Read") + return b"".join(data) + + +class PyCodecState: + def __init__(self): + self.xsize = 0 + self.ysize = 0 + self.xoff = 0 + self.yoff = 0 + + def extents(self): + return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize) + + +class PyCodec: + def __init__(self, mode, *args): + self.im = None + self.state = PyCodecState() + self.fd = None + self.mode = mode + self.init(args) + + def init(self, args): + """ + Override to perform codec specific initialization + + :param args: Array of args items from the tile entry + :returns: None + """ + self.args = args + + def cleanup(self): + """ + Override to perform codec specific cleanup + + :returns: None + """ + pass + + def setfd(self, fd): + """ + Called from ImageFile to set the Python file-like object + + :param fd: A Python file-like object + :returns: None + """ + self.fd = fd + + def setimage(self, im, extents=None): + """ + Called from ImageFile to set the core output image for the codec + + :param im: A core image object + :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle + for this tile + :returns: None + """ + + # following c code + self.im = im + + if extents: + (x0, y0, x1, y1) = extents + else: + (x0, y0, x1, y1) = (0, 0, 0, 0) + + if x0 == 0 and x1 == 0: + self.state.xsize, self.state.ysize = self.im.size + else: + self.state.xoff = x0 + self.state.yoff = y0 + self.state.xsize = x1 - x0 + self.state.ysize = y1 - y0 + + if self.state.xsize <= 0 or self.state.ysize <= 0: + raise ValueError("Size cannot be negative") + + if ( + self.state.xsize + self.state.xoff > self.im.size[0] + or self.state.ysize + self.state.yoff > self.im.size[1] + ): + raise ValueError("Tile cannot extend outside image") + + +class PyDecoder(PyCodec): + """ + Python implementation of a format decoder. Override this class and + add the decoding logic in the :meth:`decode` method. + + See :ref:`Writing Your Own File Codec in Python` + """ + + _pulls_fd = False + + @property + def pulls_fd(self): + return self._pulls_fd + + def decode(self, buffer): + """ + Override to perform the decoding process. + + :param buffer: A bytes object with the data to be decoded. + :returns: A tuple of ``(bytes consumed, errcode)``. + If finished with decoding return -1 for the bytes consumed. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + raise NotImplementedError() + + def set_as_raw(self, data, rawmode=None): + """ + Convenience method to set the internal image from a stream of raw data + + :param data: Bytes to be set + :param rawmode: The rawmode to be used for the decoder. + If not specified, it will default to the mode of the image + :returns: None + """ + + if not rawmode: + rawmode = self.mode + d = Image._getdecoder(self.mode, "raw", (rawmode)) + d.setimage(self.im, self.state.extents()) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") + + +class PyEncoder(PyCodec): + """ + Python implementation of a format encoder. Override this class and + add the decoding logic in the :meth:`encode` method. + + See :ref:`Writing Your Own File Codec in Python` + """ + + _pushes_fd = False + + @property + def pushes_fd(self): + return self._pushes_fd + + def encode(self, bufsize): + """ + Override to perform the encoding process. + + :param bufsize: Buffer size. + :returns: A tuple of ``(bytes encoded, errcode, bytes)``. + If finished with encoding return 1 for the error code. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + raise NotImplementedError() + + def encode_to_pyfd(self): + """ + If ``pushes_fd`` is ``True``, then this method will be used, + and ``encode()`` will only be called once. + + :returns: A tuple of ``(bytes consumed, errcode)``. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + if not self.pushes_fd: + return 0, -8 # bad configuration + bytes_consumed, errcode, data = self.encode(0) + if data: + self.fd.write(data) + return bytes_consumed, errcode + + def encode_to_file(self, fh, bufsize): + """ + :param fh: File handle. + :param bufsize: Buffer size. + + :returns: If finished successfully, return 0. + Otherwise, return an error code. Err codes are from + :data:`.ImageFile.ERRORS`. + """ + errcode = 0 + while errcode == 0: + status, errcode, buf = self.encode(bufsize) + if status > 0: + fh.write(buf[status:]) + return errcode diff --git a/venv/Lib/site-packages/PIL/ImageFilter.py b/venv/Lib/site-packages/PIL/ImageFilter.py new file mode 100644 index 0000000..1320af8 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageFilter.py @@ -0,0 +1,538 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard filters +# +# History: +# 1995-11-27 fl Created +# 2002-06-08 fl Added rank and mode filters +# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2002 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# +import functools + + +class Filter: + pass + + +class MultibandFilter(Filter): + pass + + +class BuiltinFilter(MultibandFilter): + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return image.filter(*self.filterargs) + + +class Kernel(BuiltinFilter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. + + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. The default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ + + name = "Kernel" + + def __init__(self, size, kernel, scale=None, offset=0): + if scale is None: + # default scale is sum of kernel + scale = functools.reduce(lambda a, b: a + b, kernel) + if size[0] * size[1] != len(kernel): + raise ValueError("not enough coefficients in kernel") + self.filterargs = size, scale, offset, kernel + + +class RankFilter(Filter): + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the ``rank``'th value. + + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + + name = "Rank" + + def __init__(self, size, rank): + self.size = size + self.rank = rank + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + image = image.expand(self.size // 2, self.size // 2) + return image.rankfilter(self.size, self.rank) + + +class MedianFilter(RankFilter): + """ + Create a median filter. Picks the median pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Median" + + def __init__(self, size=3): + self.size = size + self.rank = size * size // 2 + + +class MinFilter(RankFilter): + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Min" + + def __init__(self, size=3): + self.size = size + self.rank = 0 + + +class MaxFilter(RankFilter): + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + + name = "Max" + + def __init__(self, size=3): + self.size = size + self.rank = size * size - 1 + + +class ModeFilter(Filter): + """ + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + + name = "Mode" + + def __init__(self, size=3): + self.size = size + + def filter(self, image): + return image.modefilter(self.size) + + +class GaussianBlur(MultibandFilter): + """Blurs the image with a sequence of extended box filters, which + approximates a Gaussian kernel. For details on accuracy see + + + :param radius: Standard deviation of the Gaussian kernel. + """ + + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = radius + + def filter(self, image): + return image.gaussian_blur(self.radius) + + +class BoxBlur(MultibandFilter): + """Blurs the image by setting each pixel to the average value of the pixels + in a square box extending radius pixels in each direction. + Supports float radius of arbitrary size. Uses an optimized implementation + which runs in linear time relative to the size of the image + for any radius value. + + :param radius: Size of the box in one direction. Radius 0 does not blur, + returns an identical image. Radius 1 takes 1 pixel + in each direction, i.e. 9 pixels in total. + """ + + name = "BoxBlur" + + def __init__(self, radius): + self.radius = radius + + def filter(self, image): + return image.box_blur(self.radius) + + +class UnsharpMask(MultibandFilter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + :param radius: Blur Radius + :param percent: Unsharp strength, in percent + :param threshold: Threshold controls the minimum brightness change that + will be sharpened + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + + """ # noqa: E501 + + name = "UnsharpMask" + + def __init__(self, radius=2, percent=150, threshold=3): + self.radius = radius + self.percent = percent + self.threshold = threshold + + def filter(self, image): + return image.unsharp_mask(self.radius, self.percent, self.threshold) + + +class BLUR(BuiltinFilter): + name = "Blur" + # fmt: off + filterargs = (5, 5), 16, 0, ( + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + ) + # fmt: on + + +class CONTOUR(BuiltinFilter): + name = "Contour" + # fmt: off + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class DETAIL(BuiltinFilter): + name = "Detail" + # fmt: off + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0, + ) + # fmt: on + + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + # fmt: off + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1, + ) + # fmt: on + + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1, + ) + # fmt: on + + +class EMBOSS(BuiltinFilter): + name = "Emboss" + # fmt: off + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0, + ) + # fmt: on + + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + # fmt: off + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2, + ) + # fmt: on + + +class SMOOTH(BuiltinFilter): + name = "Smooth" + # fmt: off + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1, + ) + # fmt: on + + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + # fmt: off + filterargs = (5, 5), 100, 0, ( + 1, 1, 1, 1, 1, + 1, 5, 5, 5, 1, + 1, 5, 44, 5, 1, + 1, 5, 5, 5, 1, + 1, 1, 1, 1, 1, + ) + # fmt: on + + +class Color3DLUT(MultibandFilter): + """Three-dimensional color lookup table. + + Transforms 3-channel pixels using the values of the channels as coordinates + in the 3D lookup table and interpolating the nearest elements. + + This method allows you to apply almost any color transformation + in constant time by using pre-calculated decimated tables. + + .. versionadded:: 5.2.0 + + :param size: Size of the table. One int or tuple of (int, int, int). + Minimal size in any dimension is 2, maximum is 65. + :param table: Flat lookup table. A list of ``channels * size**3`` + float elements or a list of ``size**3`` channels-sized + tuples with floats. Channels are changed first, + then first dimension, then second, then third. + Value 0.0 corresponds lowest value of output, 1.0 highest. + :param channels: Number of channels in the table. Could be 3 or 4. + Default is 3. + :param target_mode: A mode for the result image. Should have not less + than ``channels`` channels. Default is ``None``, + which means that mode wouldn't be changed. + """ + + name = "Color 3D LUT" + + def __init__(self, size, table, channels=3, target_mode=None, **kwargs): + if channels not in (3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + self.size = size = self._check_size(size) + self.channels = channels + self.mode = target_mode + + # Hidden flag `_copy_table=False` could be used to avoid extra copying + # of the table if the table is specially made for the constructor. + copy_table = kwargs.get("_copy_table", True) + items = size[0] * size[1] * size[2] + wrong_size = False + + numpy = None + if hasattr(table, "shape"): + try: + import numpy + except ImportError: # pragma: no cover + pass + + if numpy and isinstance(table, numpy.ndarray): + if copy_table: + table = table.copy() + + if table.shape in [ + (items * channels,), + (items, channels), + (size[2], size[1], size[0], channels), + ]: + table = table.reshape(items * channels) + else: + wrong_size = True + + else: + if copy_table: + table = list(table) + + # Convert to a flat list + if table and isinstance(table[0], (list, tuple)): + table, raw_table = [], table + for pixel in raw_table: + if len(pixel) != channels: + raise ValueError( + "The elements of the table should " + "have a length of {}.".format(channels) + ) + table.extend(pixel) + + if wrong_size or len(table) != items * channels: + raise ValueError( + "The table should have either channels * size**3 float items " + "or size**3 items of channels-sized tuples with floats. " + f"Table should be: {channels}x{size[0]}x{size[1]}x{size[2]}. " + f"Actual length: {len(table)}" + ) + self.table = table + + @staticmethod + def _check_size(size): + try: + _, _, _ = size + except ValueError as e: + raise ValueError( + "Size should be either an integer or a tuple of three integers." + ) from e + except TypeError: + size = (size, size, size) + size = [int(x) for x in size] + for size1D in size: + if not 2 <= size1D <= 65: + raise ValueError("Size should be in [2, 65] range.") + return size + + @classmethod + def generate(cls, size, callback, channels=3, target_mode=None): + """Generates new LUT using provided callback. + + :param size: Size of the table. Passed to the constructor. + :param callback: Function with three parameters which correspond + three color channels. Will be called ``size**3`` + times with values from 0.0 to 1.0 and should return + a tuple with ``channels`` elements. + :param channels: The number of channels which should return callback. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + size1D, size2D, size3D = cls._check_size(size) + if channels not in (3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + + table = [0] * (size1D * size2D * size3D * channels) + idx_out = 0 + for b in range(size3D): + for g in range(size2D): + for r in range(size1D): + table[idx_out : idx_out + channels] = callback( + r / (size1D - 1), g / (size2D - 1), b / (size3D - 1) + ) + idx_out += channels + + return cls( + (size1D, size2D, size3D), + table, + channels=channels, + target_mode=target_mode, + _copy_table=False, + ) + + def transform(self, callback, with_normals=False, channels=None, target_mode=None): + """Transforms the table values using provided callback and returns + a new LUT with altered values. + + :param callback: A function which takes old lookup table values + and returns a new set of values. The number + of arguments which function should take is + ``self.channels`` or ``3 + self.channels`` + if ``with_normals`` flag is set. + Should return a tuple of ``self.channels`` or + ``channels`` elements if it is set. + :param with_normals: If true, ``callback`` will be called with + coordinates in the color cube as the first + three arguments. Otherwise, ``callback`` + will be called only with actual color values. + :param channels: The number of channels in the resulting lookup table. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + if channels not in (None, 3, 4): + raise ValueError("Only 3 or 4 output channels are supported") + ch_in = self.channels + ch_out = channels or ch_in + size1D, size2D, size3D = self.size + + table = [0] * (size1D * size2D * size3D * ch_out) + idx_in = 0 + idx_out = 0 + for b in range(size3D): + for g in range(size2D): + for r in range(size1D): + values = self.table[idx_in : idx_in + ch_in] + if with_normals: + values = callback( + r / (size1D - 1), + g / (size2D - 1), + b / (size3D - 1), + *values, + ) + else: + values = callback(*values) + table[idx_out : idx_out + ch_out] = values + idx_in += ch_in + idx_out += ch_out + + return type(self)( + self.size, + table, + channels=ch_out, + target_mode=target_mode or self.mode, + _copy_table=False, + ) + + def __repr__(self): + r = [ + f"{self.__class__.__name__} from {self.table.__class__.__name__}", + "size={:d}x{:d}x{:d}".format(*self.size), + f"channels={self.channels:d}", + ] + if self.mode: + r.append(f"target_mode={self.mode}") + return "<{}>".format(" ".join(r)) + + def filter(self, image): + from . import Image + + return image.color_lut_3d( + self.mode or image.mode, + Image.Resampling.BILINEAR, + self.channels, + self.size[0], + self.size[1], + self.size[2], + self.table, + ) diff --git a/venv/Lib/site-packages/PIL/ImageFont.py b/venv/Lib/site-packages/PIL/ImageFont.py new file mode 100644 index 0000000..f21b6de --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageFont.py @@ -0,0 +1,1083 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIL raster font management +# +# History: +# 1996-08-07 fl created (experimental) +# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 +# 1999-02-06 fl rewrote most font management stuff in C +# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) +# 2001-02-17 fl added freetype support +# 2001-05-09 fl added TransposedFont wrapper class +# 2002-03-04 fl make sure we have a "L" or "1" font +# 2002-12-04 fl skip non-directory entries in the system path +# 2003-04-29 fl add embedded default font +# 2003-09-27 fl added support for truetype charmap encodings +# +# Todo: +# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import base64 +import os +import sys +import warnings +from enum import IntEnum +from io import BytesIO + +from . import Image +from ._util import isDirectory, isPath + + +class Layout(IntEnum): + BASIC = 0 + RAQM = 1 + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Layout: "LAYOUT_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +class _imagingft_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imagingft C module is not installed") + + +try: + from . import _imagingft as core +except ImportError: + core = _imagingft_not_installed() + + +# FIXME: add support for pilfont2 format (see FontFile.py) + +# -------------------------------------------------------------------- +# Font metrics format: +# "PILfont" LF +# fontdescriptor LF +# (optional) key=value... LF +# "DATA" LF +# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) +# +# To place a character, cut out srcbox and paste at dstbox, +# relative to the character position. Then move the character +# position according to dx, dy. +# -------------------------------------------------------------------- + + +class ImageFont: + "PIL font wrapper" + + def _load_pilfont(self, filename): + + with open(filename, "rb") as fp: + image = None + for ext in (".png", ".gif", ".pbm"): + if image: + image.close() + try: + fullname = os.path.splitext(filename)[0] + ext + image = Image.open(fullname) + except Exception: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + if image: + image.close() + raise OSError("cannot find glyph data file") + + self.file = fullname + + self._load_pilfont_data(fp, image) + image.close() + + def _load_pilfont_data(self, file, image): + + # read PILfont header + if file.readline() != b"PILfont\n": + raise SyntaxError("Not a PILfont file") + file.readline().split(b";") + self.info = [] # FIXME: should be a dictionary + while True: + s = file.readline() + if not s or s == b"DATA\n": + break + self.info.append(s) + + # read PILfont metrics + data = file.read(256 * 20) + + # check image + if image.mode not in ("1", "L"): + raise TypeError("invalid font image mode") + + image.load() + + self.font = Image.core.font(image.im, data) + + def getsize(self, text, *args, **kwargs): + """ + Returns width and height (in pixels) of given text. + + :param text: Text to measure. + + :return: (width, height) + """ + return self.font.getsize(text) + + def getmask(self, text, mode="", *args, **kwargs): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + return self.font.getmask(text, mode) + + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + + +class FreeTypeFont: + "FreeType font wrapper (requires _imagingft service)" + + def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): + # FIXME: use service provider instead + + self.path = font + self.size = size + self.index = index + self.encoding = encoding + + if layout_engine not in (Layout.BASIC, Layout.RAQM): + layout_engine = Layout.BASIC + if core.HAVE_RAQM: + layout_engine = Layout.RAQM + elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: + import warnings + + warnings.warn( + "Raqm layout was requested, but Raqm is not available. " + "Falling back to basic layout." + ) + layout_engine = Layout.BASIC + + self.layout_engine = layout_engine + + def load_from_bytes(f): + self.font_bytes = f.read() + self.font = core.getfont( + "", size, index, encoding, self.font_bytes, layout_engine + ) + + if isPath(font): + if sys.platform == "win32": + font_bytes_path = font if isinstance(font, bytes) else font.encode() + try: + font_bytes_path.decode("ascii") + except UnicodeDecodeError: + # FreeType cannot load fonts with non-ASCII characters on Windows + # So load it into memory first + with open(font, "rb") as f: + load_from_bytes(f) + return + self.font = core.getfont( + font, size, index, encoding, layout_engine=layout_engine + ) + else: + load_from_bytes(font) + + def __getstate__(self): + return [self.path, self.size, self.index, self.encoding, self.layout_engine] + + def __setstate__(self, state): + path, size, index, encoding, layout_engine = state + self.__init__(path, size, index, encoding, layout_engine) + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, str) else b"\n" + return text.split(split_character) + + def getname(self): + """ + :return: A tuple of the font family (e.g. Helvetica) and the font style + (e.g. Bold) + """ + return self.font.family, self.font.style + + def getmetrics(self): + """ + :return: A tuple of the font ascent (the distance from the baseline to + the highest outline point) and descent (the distance from the + baseline to the lowest outline point, a negative value) + """ + return self.font.ascent, self.font.descent + + def getlength(self, text, mode="", direction=None, features=None, language=None): + """ + Returns length (in pixels with 1/64 precision) of given text when rendered + in font with provided direction, features, and language. + + This is the amount by which following text should be offset. + Text bounding box may extend past the length in some fonts, + e.g. when using italics or accents. + + The result is returned as a float; it is a whole number if using basic layout. + + Note that the sum of two lengths may not equal the length of a concatenated + string due to kerning. If you need to adjust for kerning, include the following + character and subtract its length. + + For example, instead of + + .. code-block:: python + + hello = font.getlength("Hello") + world = font.getlength("World") + hello_world = hello + world # not adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # may fail + + use + + .. code-block:: python + + hello = font.getlength("HelloW") - font.getlength("W") # adjusted for kerning + world = font.getlength("World") + hello_world = hello + world # adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # True + + or disable kerning with (requires libraqm) + + .. code-block:: python + + hello = draw.textlength("Hello", font, features=["-kern"]) + world = draw.textlength("World", font, features=["-kern"]) + hello_world = hello + world # kerning is disabled, no need to adjust + assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) + + .. versionadded:: 8.0.0 + + :param text: Text to measure. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :return: Width for horizontal, height for vertical text. + """ + return self.font.getlength(text, mode, direction, features, language) / 64 + + def getbbox( + self, + text, + mode="", + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ): + """ + Returns bounding box (in pixels) of given text relative to given anchor + when rendered in font with provided direction, features, and language. + + Use :py:meth:`getlength()` to get the offset of following text with + 1/64 pixel precision. The bounding box includes extra margins for + some fonts, e.g. italics or accents. + + .. versionadded:: 8.0.0 + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :param stroke_width: The width of the text stroke. + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + :return: ``(left, top, right, bottom)`` bounding box + """ + size, offset = self.font.getsize( + text, mode, direction, features, language, anchor + ) + left, top = offset[0] - stroke_width, offset[1] - stroke_width + width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width + return left, top, left + width, top + height + + def getsize( + self, text, direction=None, features=None, language=None, stroke_width=0 + ): + """ + Returns width and height (in pixels) of given text if rendered in font with + provided direction, features, and language. + + Use :py:meth:`getlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. + + .. note:: For historical reasons this function measures text height from + the ascender line instead of the top, see :ref:`text-anchors`. + If you wish to measure text height from the top, it is recommended + to use the bottom value of :meth:`getbbox` with ``anchor='lt'`` instead. + + :param text: Text to measure. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :return: (width, height) + """ + # vertical offset is added for historical reasons + # see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929 + size, offset = self.font.getsize(text, "L", direction, features, language) + return ( + size[0] + stroke_width * 2, + size[1] + stroke_width * 2 + offset[1], + ) + + def getsize_multiline( + self, + text, + direction=None, + spacing=4, + features=None, + language=None, + stroke_width=0, + ): + """ + Returns width and height (in pixels) of given text if rendered in font + with provided direction, features, and language, while respecting + newline characters. + + :param text: Text to measure. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param spacing: The vertical gap between lines, defaulting to 4 pixels. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :return: (width, height) + """ + max_width = 0 + lines = self._multiline_split(text) + line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing + for line in lines: + line_width, line_height = self.getsize( + line, direction, features, language, stroke_width + ) + max_width = max(max_width, line_width) + + return max_width, len(lines) * line_spacing - spacing + + def getoffset(self, text): + """ + Returns the offset of given text. This is the gap between the + starting coordinate and the first marking. Note that this gap is + included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`. + + :param text: Text to measure. + + :return: A tuple of the x and y offset + """ + return self.font.getsize(text)[1] + + def getmask( + self, + text, + mode="", + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ink=0, + ): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + return self.getmask2( + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + anchor=anchor, + ink=ink, + )[0] + + def getmask2( + self, + text, + mode="", + fill=Image.core.fill, + direction=None, + features=None, + language=None, + stroke_width=0, + anchor=None, + ink=0, + *args, + **kwargs, + ): + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left. + See :ref:`text-anchors` for valid values. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :return: A tuple of an internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module, and the text offset, the + gap between the starting coordinate and the first marking + """ + size, offset = self.font.getsize( + text, mode, direction, features, language, anchor + ) + size = size[0] + stroke_width * 2, size[1] + stroke_width * 2 + offset = offset[0] - stroke_width, offset[1] - stroke_width + Image._decompression_bomb_check(size) + im = fill("RGBA" if mode == "RGBA" else "L", size, 0) + self.font.render( + text, im.id, mode, direction, features, language, stroke_width, ink + ) + return im, offset + + def font_variant( + self, font=None, size=None, index=None, encoding=None, layout_engine=None + ): + """ + Create a copy of this FreeTypeFont object, + using any specified arguments to override the settings. + + Parameters are identical to the parameters used to initialize this + object. + + :return: A FreeTypeFont object. + """ + return FreeTypeFont( + font=self.path if font is None else font, + size=self.size if size is None else size, + index=self.index if index is None else index, + encoding=self.encoding if encoding is None else encoding, + layout_engine=layout_engine or self.layout_engine, + ) + + def get_variation_names(self): + """ + :returns: A list of the named styles in a variation font. + :exception OSError: If the font is not a variation font. + """ + try: + names = self.font.getvarnames() + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + return [name.replace(b"\x00", b"") for name in names] + + def set_variation_by_name(self, name): + """ + :param name: The name of the style. + :exception OSError: If the font is not a variation font. + """ + names = self.get_variation_names() + if not isinstance(name, bytes): + name = name.encode() + index = names.index(name) + + if index == getattr(self, "_last_variation_index", None): + # When the same name is set twice in a row, + # there is an 'unknown freetype error' + # https://savannah.nongnu.org/bugs/?56186 + return + self._last_variation_index = index + + self.font.setvarname(index) + + def get_variation_axes(self): + """ + :returns: A list of the axes in a variation font. + :exception OSError: If the font is not a variation font. + """ + try: + axes = self.font.getvaraxes() + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + for axis in axes: + axis["name"] = axis["name"].replace(b"\x00", b"") + return axes + + def set_variation_by_axes(self, axes): + """ + :param axes: A list of values for each axis. + :exception OSError: If the font is not a variation font. + """ + try: + self.font.setvaraxes(axes) + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e + + +class TransposedFont: + "Wrapper for writing rotated or mirrored text" + + def __init__(self, font, orientation=None): + """ + Wrapper that creates a transposed font from any existing font + object. + + :param font: A font object. + :param orientation: An optional orientation. If given, this should + be one of Image.Transpose.FLIP_LEFT_RIGHT, Image.Transpose.FLIP_TOP_BOTTOM, + Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_180, or + Image.Transpose.ROTATE_270. + """ + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getsize(self, text, *args, **kwargs): + w, h = self.font.getsize(text) + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): + return h, w + return w, h + + def getmask(self, text, mode="", *args, **kwargs): + im = self.font.getmask(text, mode, *args, **kwargs) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + + +def load(filename): + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception OSError: If the file could not be read. + """ + f = ImageFont() + f._load_pilfont(filename) + return f + + +def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): + """ + Load a TrueType or OpenType font from a file or file-like object, + and create a font object. + This function loads a font object from the given file or file-like + object, and creates a font object for a font of the given size. + + Pillow uses FreeType to open font files. If you are opening many fonts + simultaneously on Windows, be aware that Windows limits the number of files + that can be open in C at once to 512. If you approach that limit, an + ``OSError`` may be thrown, reporting that FreeType "cannot open resource". + + This function requires the _imagingft service. + + :param font: A filename or file-like object containing a TrueType font. + If the file is not found in this filename, the loader may also + search in other directories, such as the :file:`fonts/` + directory on Windows or :file:`/Library/Fonts/`, + :file:`/System/Library/Fonts/` and :file:`~/Library/Fonts/` on + macOS. + + :param size: The requested size, in pixels. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Possible + encodings include (see the FreeType documentation for more + information): + + * "unic" (Unicode) + * "symb" (Microsoft Symbol) + * "ADOB" (Adobe Standard) + * "ADBE" (Adobe Expert) + * "ADBC" (Adobe Custom) + * "armn" (Apple Roman) + * "sjis" (Shift JIS) + * "gb " (PRC) + * "big5" + * "wans" (Extended Wansung) + * "joha" (Johab) + * "lat1" (Latin-1) + + This specifies the character set to use. It does not alter the + encoding of any text provided in subsequent operations. + :param layout_engine: Which layout engine to use, if available: + :data:`.ImageFont.Layout.BASIC` or :data:`.ImageFont.Layout.RAQM`. + + You can check support for Raqm layout using + :py:func:`PIL.features.check_feature` with ``feature="raqm"``. + + .. versionadded:: 4.2.0 + :return: A font object. + :exception OSError: If the file could not be read. + """ + + def freetype(font): + return FreeTypeFont(font, size, index, encoding, layout_engine) + + try: + return freetype(font) + except OSError: + if not isPath(font): + raise + ttf_filename = os.path.basename(font) + + dirs = [] + if sys.platform == "win32": + # check the windows font repository + # NOTE: must use uppercase WINDIR, to work around bugs in + # 1.5.2's os.environ.get() + windir = os.environ.get("WINDIR") + if windir: + dirs.append(os.path.join(windir, "fonts")) + elif sys.platform in ("linux", "linux2"): + lindirs = os.environ.get("XDG_DATA_DIRS", "") + if not lindirs: + # According to the freedesktop spec, XDG_DATA_DIRS should + # default to /usr/share + lindirs = "/usr/share" + dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] + elif sys.platform == "darwin": + dirs += [ + "/Library/Fonts", + "/System/Library/Fonts", + os.path.expanduser("~/Library/Fonts"), + ] + + ext = os.path.splitext(ttf_filename)[1] + first_font_with_a_different_extension = None + for directory in dirs: + for walkroot, walkdir, walkfilenames in os.walk(directory): + for walkfilename in walkfilenames: + if ext and walkfilename == ttf_filename: + return freetype(os.path.join(walkroot, walkfilename)) + elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: + fontpath = os.path.join(walkroot, walkfilename) + if os.path.splitext(fontpath)[1] == ".ttf": + return freetype(fontpath) + if not ext and first_font_with_a_different_extension is None: + first_font_with_a_different_extension = fontpath + if first_font_with_a_different_extension: + return freetype(first_font_with_a_different_extension) + raise + + +def load_path(filename): + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception OSError: If the file could not be read. + """ + for directory in sys.path: + if isDirectory(directory): + if not isinstance(filename, str): + filename = filename.decode("utf-8") + try: + return load(os.path.join(directory, filename)) + except OSError: + pass + raise OSError("cannot find font file") + + +def load_default(): + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO( + base64.b64decode( + b""" +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +""" + ) + ), + Image.open( + BytesIO( + base64.b64decode( + b""" +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +""" + ) + ) + ), + ) + return f diff --git a/venv/Lib/site-packages/PIL/ImageGrab.py b/venv/Lib/site-packages/PIL/ImageGrab.py new file mode 100644 index 0000000..8bd14d3 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageGrab.py @@ -0,0 +1,124 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber +# +# History: +# 2001-04-26 fl created +# 2001-09-17 fl use builtin driver, if present +# 2002-11-19 fl added grabclipboard support +# +# Copyright (c) 2001-2002 by Secret Labs AB +# Copyright (c) 2001-2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import sys + +from . import Image + +if sys.platform == "darwin": + import os + import subprocess + import tempfile + + +def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None): + if xdisplay is None: + if sys.platform == "darwin": + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + args = ["screencapture"] + if bbox: + left, top, right, bottom = bbox + args += ["-R", f"{left},{right},{right-left},{bottom-top}"] + subprocess.call(args + ["-x", filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + im_resized = im.resize((right - left, bottom - top)) + im.close() + return im_resized + return im + elif sys.platform == "win32": + offset, size, data = Image.core.grabscreen_win32( + include_layered_windows, all_screens + ) + im = Image.frombytes( + "RGB", + size, + data, + # RGB, 32-bit line padding, origin lower left corner + "raw", + "BGR", + (size[0] * 3 + 3) & -4, + -1, + ) + if bbox: + x0, y0 = offset + left, top, right, bottom = bbox + im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) + return im + # use xdisplay=None for default display on non-win32/macOS systems + if not Image.core.HAVE_XCB: + raise OSError("Pillow was built without XCB support") + size, data = Image.core.grabscreen_x11(xdisplay) + im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) + if bbox: + im = im.crop(bbox) + return im + + +def grabclipboard(): + if sys.platform == "darwin": + fh, filepath = tempfile.mkstemp(".jpg") + os.close(fh) + commands = [ + 'set theFile to (open for access POSIX file "' + + filepath + + '" with write permission)', + "try", + " write (the clipboard as JPEG picture) to theFile", + "end try", + "close access theFile", + ] + script = ["osascript"] + for command in commands: + script += ["-e", command] + subprocess.call(script) + + im = None + if os.stat(filepath).st_size != 0: + im = Image.open(filepath) + im.load() + os.unlink(filepath) + return im + elif sys.platform == "win32": + fmt, data = Image.core.grabclipboard_win32() + if fmt == "file": # CF_HDROP + import struct + + o = struct.unpack_from("I", data)[0] + if data[16] != 0: + files = data[o:].decode("utf-16le").split("\0") + else: + files = data[o:].decode("mbcs").split("\0") + return files[: files.index("")] + if isinstance(data, bytes): + import io + + data = io.BytesIO(data) + if fmt == "png": + from . import PngImagePlugin + + return PngImagePlugin.PngImageFile(data) + elif fmt == "DIB": + from . import BmpImagePlugin + + return BmpImagePlugin.DibImageFile(data) + return None + else: + raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only") diff --git a/venv/Lib/site-packages/PIL/ImageMath.py b/venv/Lib/site-packages/PIL/ImageMath.py new file mode 100644 index 0000000..09d9898 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageMath.py @@ -0,0 +1,259 @@ +# +# The Python Imaging Library +# $Id$ +# +# a simple math add-on for the Python Imaging Library +# +# History: +# 1999-02-15 fl Original PIL Plus release +# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 +# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 +# +# Copyright (c) 1999-2005 by Secret Labs AB +# Copyright (c) 2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import builtins + +from . import Image, _imagingmath + + +def _isconstant(v): + return isinstance(v, (int, float)) + + +class _Operand: + """Wraps an image operand, providing standard operators""" + + def __init__(self, im): + self.im = im + + def __fixup(self, im1): + # convert image to suitable mode + if isinstance(im1, _Operand): + # argument was an image. + if im1.im.mode in ("1", "L"): + return im1.im.convert("I") + elif im1.im.mode in ("I", "F"): + return im1.im + else: + raise ValueError(f"unsupported mode: {im1.im.mode}") + else: + # argument was a constant + if _isconstant(im1) and self.im.mode in ("1", "L", "I"): + return Image.new("I", self.im.size, im1) + else: + return Image.new("F", self.im.size, im1) + + def apply(self, op, im1, im2=None, mode=None): + im1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + try: + op = getattr(_imagingmath, op + "_" + im1.mode) + except AttributeError as e: + raise TypeError(f"bad operand type for '{op}'") from e + _imagingmath.unop(op, out.im.id, im1.im.id) + else: + # binary operation + im2 = self.__fixup(im2) + if im1.mode != im2.mode: + # convert both arguments to floating point + if im1.mode != "F": + im1 = im1.convert("F") + if im2.mode != "F": + im2 = im2.convert("F") + if im1.size != im2.size: + # crop both arguments to a common size + size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1])) + if im1.size != size: + im1 = im1.crop((0, 0) + size) + if im2.size != size: + im2 = im2.crop((0, 0) + size) + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + im2.load() + try: + op = getattr(_imagingmath, op + "_" + im1.mode) + except AttributeError as e: + raise TypeError(f"bad operand type for '{op}'") from e + _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) + return _Operand(out) + + # unary operators + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + def __abs__(self): + return self.apply("abs", self) + + def __pos__(self): + return self + + def __neg__(self): + return self.apply("neg", self) + + # binary operators + def __add__(self, other): + return self.apply("add", self, other) + + def __radd__(self, other): + return self.apply("add", other, self) + + def __sub__(self, other): + return self.apply("sub", self, other) + + def __rsub__(self, other): + return self.apply("sub", other, self) + + def __mul__(self, other): + return self.apply("mul", self, other) + + def __rmul__(self, other): + return self.apply("mul", other, self) + + def __truediv__(self, other): + return self.apply("div", self, other) + + def __rtruediv__(self, other): + return self.apply("div", other, self) + + def __mod__(self, other): + return self.apply("mod", self, other) + + def __rmod__(self, other): + return self.apply("mod", other, self) + + def __pow__(self, other): + return self.apply("pow", self, other) + + def __rpow__(self, other): + return self.apply("pow", other, self) + + # bitwise + def __invert__(self): + return self.apply("invert", self) + + def __and__(self, other): + return self.apply("and", self, other) + + def __rand__(self, other): + return self.apply("and", other, self) + + def __or__(self, other): + return self.apply("or", self, other) + + def __ror__(self, other): + return self.apply("or", other, self) + + def __xor__(self, other): + return self.apply("xor", self, other) + + def __rxor__(self, other): + return self.apply("xor", other, self) + + def __lshift__(self, other): + return self.apply("lshift", self, other) + + def __rshift__(self, other): + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other): + return self.apply("eq", self, other) + + def __ne__(self, other): + return self.apply("ne", self, other) + + def __lt__(self, other): + return self.apply("lt", self, other) + + def __le__(self, other): + return self.apply("le", self, other) + + def __gt__(self, other): + return self.apply("gt", self, other) + + def __ge__(self, other): + return self.apply("ge", self, other) + + +# conversions +def imagemath_int(self): + return _Operand(self.im.convert("I")) + + +def imagemath_float(self): + return _Operand(self.im.convert("F")) + + +# logical +def imagemath_equal(self, other): + return self.apply("eq", self, other, mode="I") + + +def imagemath_notequal(self, other): + return self.apply("ne", self, other, mode="I") + + +def imagemath_min(self, other): + return self.apply("min", self, other) + + +def imagemath_max(self, other): + return self.apply("max", self, other) + + +def imagemath_convert(self, mode): + return _Operand(self.im.convert(mode)) + + +ops = {} +for k, v in list(globals().items()): + if k[:10] == "imagemath_": + ops[k[10:]] = v + + +def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ + + # build execution namespace + args = ops.copy() + args.update(_dict) + args.update(kw) + for k, v in list(args.items()): + if hasattr(v, "im"): + args[k] = _Operand(v) + + compiled_code = compile(expression, "", "eval") + + def scan(code): + for const in code.co_consts: + if type(const) == type(compiled_code): + scan(const) + + for name in code.co_names: + if name not in args and name != "abs": + raise ValueError(f"'{name}' not allowed") + + scan(compiled_code) + out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args) + try: + return out.im + except AttributeError: + return out diff --git a/venv/Lib/site-packages/PIL/ImageMode.py b/venv/Lib/site-packages/PIL/ImageMode.py new file mode 100644 index 0000000..0973536 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageMode.py @@ -0,0 +1,91 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard mode descriptors +# +# History: +# 2006-03-20 fl Added +# +# Copyright (c) 2006 by Secret Labs AB. +# Copyright (c) 2006 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import sys + +# mode descriptor cache +_modes = None + + +class ModeDescriptor: + """Wrapper for mode strings.""" + + def __init__(self, mode, bands, basemode, basetype, typestr): + self.mode = mode + self.bands = bands + self.basemode = basemode + self.basetype = basetype + self.typestr = typestr + + def __str__(self): + return self.mode + + +def getmode(mode): + """Gets a mode descriptor for the given mode.""" + global _modes + if not _modes: + # initialize mode cache + modes = {} + endian = "<" if sys.byteorder == "little" else ">" + for m, (basemode, basetype, bands, typestr) in { + # core modes + # Bits need to be extended to bytes + "1": ("L", "L", ("1",), "|b1"), + "L": ("L", "L", ("L",), "|u1"), + "I": ("L", "I", ("I",), endian + "i4"), + "F": ("L", "F", ("F",), endian + "f4"), + "P": ("P", "L", ("P",), "|u1"), + "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), + "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), + "RGBA": ("RGB", "L", ("R", "G", "B", "A"), "|u1"), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K"), "|u1"), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr"), "|u1"), + # UNDONE - unsigned |u1i1i1 + "LAB": ("RGB", "L", ("L", "A", "B"), "|u1"), + "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), + # extra experimental modes + "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), + "BGR;15": ("RGB", "L", ("B", "G", "R"), endian + "u2"), + "BGR;16": ("RGB", "L", ("B", "G", "R"), endian + "u2"), + "BGR;24": ("RGB", "L", ("B", "G", "R"), endian + "u3"), + "BGR;32": ("RGB", "L", ("B", "G", "R"), endian + "u4"), + "LA": ("L", "L", ("L", "A"), "|u1"), + "La": ("L", "L", ("L", "a"), "|u1"), + "PA": ("RGB", "L", ("P", "A"), "|u1"), + }.items(): + modes[m] = ModeDescriptor(m, bands, basemode, basetype, typestr) + # mapping modes + for i16mode, typestr in { + # I;16 == I;16L, and I;32 == I;32L + "I;16": "u2", + "I;16BS": ">i2", + "I;16N": endian + "u2", + "I;16NS": endian + "i2", + "I;32": "u4", + "I;32L": "i4", + "I;32LS": " + +import re + +from . import Image, _imagingmorph + +LUT_SIZE = 1 << 9 + +# fmt: off +ROTATION_MATRIX = [ + 6, 3, 0, + 7, 4, 1, + 8, 5, 2, +] +MIRROR_MATRIX = [ + 2, 1, 0, + 5, 4, 3, + 8, 7, 6, +] +# fmt: on + + +class LutBuilder: + """A class for building a MorphLut from a descriptive language + + The input patterns is a list of a strings sequences like these:: + + 4:(... + .1. + 111)->1 + + (whitespaces including linebreaks are ignored). The option 4 + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: + + - . or X - Ignore + - 1 - Pixel is on + - 0 - Pixel is off + + The result of the operation is described after "->" string. + + The default is to return the current pixel value, which is + returned if no other match is found. + + Operations: + + - 4 - 4 way rotation + - N - Negate + - 1 - Dummy op for no other operation (an op must always be given) + - M - Mirroring + + Example:: + + lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) + lut = lb.build_lut() + + """ + + def __init__(self, patterns=None, op_name=None): + if patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + self.lut = None + if op_name is not None: + known_patterns = { + "corner": ["1:(... ... ...)->0", "4:(00. 01. ...)->1"], + "dilation4": ["4:(... .0. .1.)->1"], + "dilation8": ["4:(... .0. .1.)->1", "4:(... .0. ..1)->1"], + "erosion4": ["4:(... .1. .0.)->0"], + "erosion8": ["4:(... .1. .0.)->0", "4:(... .1. ..0)->0"], + "edge": [ + "1:(... ... ...)->0", + "4:(.0. .1. ...)->1", + "4:(01. .1. ...)->1", + ], + } + if op_name not in known_patterns: + raise Exception("Unknown pattern " + op_name + "!") + + self.patterns = known_patterns[op_name] + + def add_patterns(self, patterns): + self.patterns += patterns + + def build_default_lut(self): + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) + + def get_lut(self): + return self.lut + + def _string_permute(self, pattern, permutation): + """string_permute takes a pattern and a permutation and returns the + string permuted according to the permutation list. + """ + assert len(permutation) == 9 + return "".join(pattern[p] for p in permutation) + + def _pattern_permute(self, basic_pattern, options, basic_result): + """pattern_permute takes a basic pattern and its result and clones + the pattern according to the modifications described in the $options + parameter. It returns a list of all cloned patterns.""" + patterns = [(basic_pattern, basic_result)] + + # rotations + if "4" in options: + res = patterns[-1][1] + for i in range(4): + patterns.append( + (self._string_permute(patterns[-1][0], ROTATION_MATRIX), res) + ) + # mirror + if "M" in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) + + # negate + if "N" in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + # Swap 0 and 1 + pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") + res = 1 - int(res) + patterns.append((pattern, res)) + + return patterns + + def build_lut(self): + """Compile all patterns into a morphology lut. + + TBD :Build based on (file) morphlut:modify_lut + """ + self.build_default_lut() + patterns = [] + + # Parse and create symmetries of the patterns strings + for p in self.patterns: + m = re.search(r"(\w*):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", "")) + if not m: + raise Exception('Syntax error in pattern "' + p + '"') + options = m.group(1) + pattern = m.group(2) + result = int(m.group(3)) + + # Get rid of spaces + pattern = pattern.replace(" ", "").replace("\n", "") + + patterns += self._pattern_permute(pattern, options, result) + + # compile the patterns into regular expressions for speed + for i, pattern in enumerate(patterns): + p = pattern[0].replace(".", "X").replace("X", "[01]") + p = re.compile(p) + patterns[i] = (p, pattern[1]) + + # Step through table and find patterns that match. + # Note that all the patterns are searched. The last one + # caught overrides + for i in range(LUT_SIZE): + # Build the bit pattern + bitpattern = bin(i)[2:] + bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1] + + for p, r in patterns: + if p.match(bitpattern): + self.lut[i] = [0, 1][r] + + return self.lut + + +class MorphOp: + """A class for binary morphological operators""" + + def __init__(self, lut=None, op_name=None, patterns=None): + """Create a binary morphological operator""" + self.lut = lut + if op_name is not None: + self.lut = LutBuilder(op_name=op_name).build_lut() + elif patterns is not None: + self.lut = LutBuilder(patterns=patterns).build_lut() + + def apply(self, image): + """Run a single morphological operation on an image + + Returns a tuple of the number of changed pixels and the + morphed image""" + if self.lut is None: + raise Exception("No operator loaded") + + if image.mode != "L": + raise ValueError("Image mode must be L") + outimage = Image.new(image.mode, image.size, None) + count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id) + return count, outimage + + def match(self, image): + """Get a list of coordinates matching the morphological operation on + an image. + + Returns a list of tuples of (x,y) coordinates + of all matching pixels. See :ref:`coordinate-system`.""" + if self.lut is None: + raise Exception("No operator loaded") + + if image.mode != "L": + raise ValueError("Image mode must be L") + return _imagingmorph.match(bytes(self.lut), image.im.id) + + def get_on_pixels(self, image): + """Get a list of all turned on pixels in a binary image + + Returns a list of tuples of (x,y) coordinates + of all matching pixels. See :ref:`coordinate-system`.""" + + if image.mode != "L": + raise ValueError("Image mode must be L") + return _imagingmorph.get_on_pixels(image.im.id) + + def load_lut(self, filename): + """Load an operator from an mrl file""" + with open(filename, "rb") as f: + self.lut = bytearray(f.read()) + + if len(self.lut) != LUT_SIZE: + self.lut = None + raise Exception("Wrong size operator file!") + + def save_lut(self, filename): + """Save an operator to an mrl file""" + if self.lut is None: + raise Exception("No operator loaded") + with open(filename, "wb") as f: + f.write(self.lut) + + def set_lut(self, lut): + """Set the lut from an external source""" + self.lut = lut diff --git a/venv/Lib/site-packages/PIL/ImageOps.py b/venv/Lib/site-packages/PIL/ImageOps.py new file mode 100644 index 0000000..f0d4545 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageOps.py @@ -0,0 +1,610 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard image operations +# +# History: +# 2001-10-20 fl Created +# 2001-10-23 fl Added autocontrast operator +# 2001-12-18 fl Added Kevin's fit operator +# 2004-03-14 fl Fixed potential division by zero in equalize +# 2005-05-05 fl Fixed equalize for low number of values +# +# Copyright (c) 2001-2004 by Secret Labs AB +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import functools +import operator +import re + +from . import Image + +# +# helpers + + +def _border(border): + if isinstance(border, tuple): + if len(border) == 2: + left, top = right, bottom = border + elif len(border) == 4: + left, top, right, bottom = border + else: + left = top = right = bottom = border + return left, top, right, bottom + + +def _color(color, mode): + if isinstance(color, str): + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + return color + + +def _lut(image, lut): + if image.mode == "P": + # FIXME: apply to lookup table, not image data + raise NotImplementedError("mode P support coming soon") + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + raise OSError("not supported for this image mode") + + +# +# actions + + +def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image (or mask region), removes ``cutoff`` percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: The percent to cut off from the histogram on the low and + high ends. Either a tuple of (low, high), or a single + number for both. + :param ignore: The background pixel value (use None for no background). + :param mask: Histogram used in contrast operation is computed using pixels + within the mask. If no mask is given the entire image is used + for histogram computation. + :param preserve_tone: Preserve image tone in Photoshop-like style autocontrast. + + .. versionadded:: 8.2.0 + + :return: An image. + """ + if preserve_tone: + histogram = image.convert("L").histogram(mask) + else: + histogram = image.histogram(mask) + + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer : layer + 256] + if ignore is not None: + # get rid of outliers + try: + h[ignore] = 0 + except TypeError: + # assume sequence + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + if not isinstance(cutoff, tuple): + cutoff = (cutoff, cutoff) + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = n * cutoff[0] // 100 + for lo in range(256): + if cut > h[lo]: + cut = cut - h[lo] + h[lo] = 0 + else: + h[lo] -= cut + cut = 0 + if cut <= 0: + break + # remove cutoff% samples from the high end + cut = n * cutoff[1] // 100 + for hi in range(255, -1, -1): + if cut > h[hi]: + cut = cut - h[hi] + h[hi] = 0 + else: + h[hi] -= cut + cut = 0 + if cut <= 0: + break + # find lowest/highest samples after preprocessing + for lo in range(256): + if h[lo]: + break + for hi in range(255, -1, -1): + if h[hi]: + break + if hi <= lo: + # don't bother + lut.extend(list(range(256))) + else: + scale = 255.0 / (hi - lo) + offset = -lo * scale + for ix in range(256): + ix = int(ix * scale + offset) + if ix < 0: + ix = 0 + elif ix > 255: + ix = 255 + lut.append(ix) + return _lut(image, lut) + + +def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127): + """ + Colorize grayscale image. + This function calculates a color wedge which maps all black pixels in + the source image to the first color and all white pixels to the + second color. If ``mid`` is specified, it uses three-color mapping. + The ``black`` and ``white`` arguments should be RGB tuples or color names; + optionally you can use three-color mapping by also specifying ``mid``. + Mapping positions for any of the colors can be specified + (e.g. ``blackpoint``), where these parameters are the integer + value corresponding to where the corresponding color should be mapped. + These parameters must have logical order, such that + ``blackpoint <= midpoint <= whitepoint`` (if ``mid`` is specified). + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :param mid: The color to use for midtone input pixels. + :param blackpoint: an int value [0, 255] for the black mapping. + :param whitepoint: an int value [0, 255] for the white mapping. + :param midpoint: an int value [0, 255] for the midtone mapping. + :return: An image. + """ + + # Initial asserts + assert image.mode == "L" + if mid is None: + assert 0 <= blackpoint <= whitepoint <= 255 + else: + assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 + + # Define colors from arguments + black = _color(black, "RGB") + white = _color(white, "RGB") + if mid is not None: + mid = _color(mid, "RGB") + + # Empty lists for the mapping + red = [] + green = [] + blue = [] + + # Create the low-end values + for i in range(0, blackpoint): + red.append(black[0]) + green.append(black[1]) + blue.append(black[2]) + + # Create the mapping (2-color) + if mid is None: + + range_map = range(0, whitepoint - blackpoint) + + for i in range_map: + red.append(black[0] + i * (white[0] - black[0]) // len(range_map)) + green.append(black[1] + i * (white[1] - black[1]) // len(range_map)) + blue.append(black[2] + i * (white[2] - black[2]) // len(range_map)) + + # Create the mapping (3-color) + else: + + range_map1 = range(0, midpoint - blackpoint) + range_map2 = range(0, whitepoint - midpoint) + + for i in range_map1: + red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1)) + green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1)) + blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1)) + for i in range_map2: + red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2)) + green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2)) + blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2)) + + # Create the high-end values + for i in range(0, 256 - whitepoint): + red.append(white[0]) + green.append(white[1]) + blue.append(white[2]) + + # Return converted image + image = image.convert("RGB") + return _lut(image, red + green + blue) + + +def contain(image, size, method=Image.Resampling.BICUBIC): + """ + Returns a resized version of the image, set to the maximum width and height + within the requested size, while maintaining the original aspect ratio. + + :param image: The image to resize and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :return: An image. + """ + + im_ratio = image.width / image.height + dest_ratio = size[0] / size[1] + + if im_ratio != dest_ratio: + if im_ratio > dest_ratio: + new_height = int(image.height / image.width * size[0]) + if new_height != size[1]: + size = (size[0], new_height) + else: + new_width = int(image.width / image.height * size[1]) + if new_width != size[0]: + size = (new_width, size[1]) + return image.resize(size, resample=method) + + +def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5, 0.5)): + """ + Returns a resized and padded version of the image, expanded to fill the + requested aspect ratio and size. + + :param image: The image to resize and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :param color: The background color of the padded image. + :param centering: Control the position of the original image within the + padded version. + + (0.5, 0.5) will keep the image centered + (0, 0) will keep the image aligned to the top left + (1, 1) will keep the image aligned to the bottom + right + :return: An image. + """ + + resized = contain(image, size, method) + if resized.size == size: + out = resized + else: + out = Image.new(image.mode, size, color) + if resized.width != size[0]: + x = int((size[0] - resized.width) * max(0, min(centering[0], 1))) + out.paste(resized, (x, 0)) + else: + y = int((size[1] - resized.height) * max(0, min(centering[1], 1))) + out.paste(resized, (0, y)) + return out + + +def crop(image, border=0): + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ + left, top, right, bottom = _border(border) + return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) + + +def scale(image, factor, resample=Image.Resampling.BICUBIC): + """ + Returns a rescaled image by a specific factor given in parameter. + A factor greater than 1 expands the image, between 0 and 1 contracts the + image. + + :param image: The image to rescale. + :param factor: The expansion factor, as a float. + :param resample: Resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + if factor == 1: + return image.copy() + elif factor <= 0: + raise ValueError("the factor must be greater than 0") + else: + size = (round(factor * image.width), round(factor * image.height)) + return image.resize(size, resample) + + +def deform(image, deformer, resample=Image.Resampling.BILINEAR): + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + ``getmesh`` method can be used. + :param resample: An optional resampling filter. Same values possible as + in the PIL.Image.transform function. + :return: An image. + """ + return image.transform( + image.size, Image.Transform.MESH, deformer.getmesh(image), resample + ) + + +def equalize(image, mask=None): + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ + if image.mode == "P": + image = image.convert("RGB") + h = image.histogram(mask) + lut = [] + for b in range(0, len(h), 256): + histo = [_f for _f in h[b : b + 256] if _f] + if len(histo) <= 1: + lut.extend(list(range(256))) + else: + step = (functools.reduce(operator.add, histo) - histo[-1]) // 255 + if not step: + lut.extend(list(range(256))) + else: + n = step // 2 + for i in range(256): + lut.append(n // step) + n = n + h[i + b] + return _lut(image, lut) + + +def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + color = _color(fill, image.mode) + if image.mode == "P" and image.palette: + image.load() + palette = image.palette.copy() + if isinstance(color, tuple): + color = palette.getcolor(color) + else: + palette = None + out = Image.new(image.mode, (width, height), color) + if palette: + out.putpalette(palette.palette) + out.paste(image, (left, top)) + return out + + +def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, 0.5)): + """ + Returns a resized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param image: The image to resize and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`. + :param bleed: Remove a border around the outside of the image from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + Cannot be greater than or equal to 0.5. + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. + """ + + # by Kevin Cazabon, Feb 17/2000 + # kevin@cazabon.com + # https://www.cazabon.com + + # ensure centering is mutable + centering = list(centering) + + if not 0.0 <= centering[0] <= 1.0: + centering[0] = 0.5 + if not 0.0 <= centering[1] <= 1.0: + centering[1] = 0.5 + + if not 0.0 <= bleed < 0.5: + bleed = 0.0 + + # calculate the area to use for resizing and cropping, subtracting + # the 'bleed' around the edges + + # number of pixels to trim off on Top and Bottom, Left and Right + bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) + + live_size = ( + image.size[0] - bleed_pixels[0] * 2, + image.size[1] - bleed_pixels[1] * 2, + ) + + # calculate the aspect ratio of the live_size + live_size_ratio = live_size[0] / live_size[1] + + # calculate the aspect ratio of the output image + output_ratio = size[0] / size[1] + + # figure out if the sides or top/bottom will be cropped off + if live_size_ratio == output_ratio: + # live_size is already the needed ratio + crop_width = live_size[0] + crop_height = live_size[1] + elif live_size_ratio >= output_ratio: + # live_size is wider than what's needed, crop the sides + crop_width = output_ratio * live_size[1] + crop_height = live_size[1] + else: + # live_size is taller than what's needed, crop the top and bottom + crop_width = live_size[0] + crop_height = live_size[0] / output_ratio + + # make the crop + crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering[0] + crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering[1] + + crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) + + # resize the image and return it + return image.resize(size, method, box=crop) + + +def flip(image): + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ + return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) + + +def grayscale(image): + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ + return image.convert("L") + + +def invert(image): + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ + lut = [] + for i in range(256): + lut.append(255 - i) + return image.point(lut) if image.mode == "1" else _lut(image, lut) + + +def mirror(image): + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ + return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + + +def posterize(image, bits): + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ + lut = [] + mask = ~(2 ** (8 - bits) - 1) + for i in range(256): + lut.append(i & mask) + return _lut(image, lut) + + +def solarize(image, threshold=128): + """ + Invert all pixel values above a threshold. + + :param image: The image to solarize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ + lut = [] + for i in range(256): + if i < threshold: + lut.append(i) + else: + lut.append(255 - i) + return _lut(image, lut) + + +def exif_transpose(image): + """ + If an image has an EXIF Orientation tag, return a new image that is + transposed accordingly. Otherwise, return a copy of the image. + + :param image: The image to transpose. + :return: An image. + """ + exif = image.getexif() + orientation = exif.get(0x0112) + method = { + 2: Image.Transpose.FLIP_LEFT_RIGHT, + 3: Image.Transpose.ROTATE_180, + 4: Image.Transpose.FLIP_TOP_BOTTOM, + 5: Image.Transpose.TRANSPOSE, + 6: Image.Transpose.ROTATE_270, + 7: Image.Transpose.TRANSVERSE, + 8: Image.Transpose.ROTATE_90, + }.get(orientation) + if method is not None: + transposed_image = image.transpose(method) + transposed_exif = transposed_image.getexif() + if 0x0112 in transposed_exif: + del transposed_exif[0x0112] + if "exif" in transposed_image.info: + transposed_image.info["exif"] = transposed_exif.tobytes() + elif "Raw profile type exif" in transposed_image.info: + transposed_image.info[ + "Raw profile type exif" + ] = transposed_exif.tobytes().hex() + elif "XML:com.adobe.xmp" in transposed_image.info: + transposed_image.info["XML:com.adobe.xmp"] = re.sub( + r'tiff:Orientation="([0-9])"', + "", + transposed_image.info["XML:com.adobe.xmp"], + ) + return transposed_image + return image.copy() diff --git a/venv/Lib/site-packages/PIL/ImagePalette.py b/venv/Lib/site-packages/PIL/ImagePalette.py new file mode 100644 index 0000000..1e0d36b --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImagePalette.py @@ -0,0 +1,261 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image palette object +# +# History: +# 1996-03-11 fl Rewritten. +# 1997-01-03 fl Up and running. +# 1997-08-23 fl Added load hack +# 2001-04-16 fl Fixed randint shadow bug in random() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import array +import warnings + +from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile + + +class ImagePalette: + """ + Color palette for palette mapped images + + :param mode: The mode to use for the palette. See: + :ref:`concept-modes`. Defaults to "RGB" + :param palette: An optional palette. If given, it must be a bytearray, + an array or a list of ints between 0-255. The list must consist of + all channels for one color followed by the next color (e.g. RGBRGBRGB). + Defaults to an empty palette. + :param size: An optional palette size. If given, an error is raised + if ``palette`` is not of equal length. + """ + + def __init__(self, mode="RGB", palette=None, size=0): + self.mode = mode + self.rawmode = None # if set, palette contains raw data + self.palette = palette or bytearray() + self.dirty = None + if size != 0: + warnings.warn( + "The size parameter is deprecated and will be removed in Pillow 10 " + "(2023-07-01).", + DeprecationWarning, + ) + if size != len(self.palette): + raise ValueError("wrong palette size") + + @property + def palette(self): + return self._palette + + @palette.setter + def palette(self, palette): + self._palette = palette + + mode_len = len(self.mode) + self.colors = {} + for i in range(0, len(self.palette), mode_len): + color = tuple(self.palette[i : i + mode_len]) + if color in self.colors: + continue + self.colors[color] = i // mode_len + + def copy(self): + new = ImagePalette() + + new.mode = self.mode + new.rawmode = self.rawmode + if self.palette is not None: + new.palette = self.palette[:] + new.dirty = self.dirty + + return new + + def getdata(self): + """ + Get palette contents in format suitable for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ + if self.rawmode: + return self.rawmode, self.palette + return self.mode, self.tobytes() + + def tobytes(self): + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(self.palette, bytes): + return self.palette + arr = array.array("B", self.palette) + return arr.tobytes() + + # Declare tostring as an alias for tobytes + tostring = tobytes + + def getcolor(self, color, image=None): + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(color, tuple): + if self.mode == "RGB": + if len(color) == 4 and color[3] == 255: + color = color[:3] + elif self.mode == "RGBA": + if len(color) == 3: + color += (255,) + try: + return self.colors[color] + except KeyError as e: + # allocate new color slot + if not isinstance(self.palette, bytearray): + self._palette = bytearray(self.palette) + index = len(self.palette) // 3 + special_colors = () + if image: + special_colors = ( + image.info.get("background"), + image.info.get("transparency"), + ) + while index in special_colors: + index += 1 + if index >= 256: + if image: + # Search for an unused index + for i, count in reversed(list(enumerate(image.histogram()))): + if count == 0 and i not in special_colors: + index = i + break + if index >= 256: + raise ValueError("cannot allocate more than 256 colors") from e + self.colors[color] = index + if index * 3 < len(self.palette): + self._palette = ( + self.palette[: index * 3] + + bytes(color) + + self.palette[index * 3 + 3 :] + ) + else: + self._palette += bytes(color) + self.dirty = 1 + return index + else: + raise ValueError(f"unknown color specifier: {repr(color)}") + + def save(self, fp): + """Save palette to text file. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write(f"# Mode: {self.mode}\n") + for i in range(256): + fp.write(f"{i}") + for j in range(i * len(self.mode), (i + 1) * len(self.mode)): + try: + fp.write(f" {self.palette[j]}") + except IndexError: + fp.write(" 0") + fp.write("\n") + fp.close() + + +# -------------------------------------------------------------------- +# Internal + + +def raw(rawmode, data): + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + + +# -------------------------------------------------------------------- +# Factories + + +def make_linear_lut(black, white): + lut = [] + if black == 0: + for i in range(256): + lut.append(white * i // 255) + else: + raise NotImplementedError # FIXME + return lut + + +def make_gamma_lut(exp): + lut = [] + for i in range(256): + lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) + return lut + + +def negative(mode="RGB"): + palette = list(range(256 * len(mode))) + palette.reverse() + return ImagePalette(mode, [i // len(mode) for i in palette]) + + +def random(mode="RGB"): + from random import randint + + palette = [] + for i in range(256 * len(mode)): + palette.append(randint(0, 255)) + return ImagePalette(mode, palette) + + +def sepia(white="#fff0c0"): + bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] + return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) + + +def wedge(mode="RGB"): + palette = list(range(256 * len(mode))) + return ImagePalette(mode, [i // len(mode) for i in palette]) + + +def load(filename): + + # FIXME: supports GIMP gradients only + + with open(filename, "rb") as fp: + + for paletteHandler in [ + GimpPaletteFile.GimpPaletteFile, + GimpGradientFile.GimpGradientFile, + PaletteFile.PaletteFile, + ]: + try: + fp.seek(0) + lut = paletteHandler(fp).getpalette() + if lut: + break + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + else: + raise OSError("cannot load palette") + + return lut # data, rawmode diff --git a/venv/Lib/site-packages/PIL/ImagePath.py b/venv/Lib/site-packages/PIL/ImagePath.py new file mode 100644 index 0000000..3d3538c --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImagePath.py @@ -0,0 +1,19 @@ +# +# The Python Imaging Library +# $Id$ +# +# path interface +# +# History: +# 1996-11-04 fl Created +# 2002-04-14 fl Added documentation stub class +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from . import Image + +Path = Image.core.path diff --git a/venv/Lib/site-packages/PIL/ImageQt.py b/venv/Lib/site-packages/PIL/ImageQt.py new file mode 100644 index 0000000..db8fa0f --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageQt.py @@ -0,0 +1,223 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a simple Qt image interface. +# +# history: +# 2006-06-03 fl: created +# 2006-06-04 fl: inherit from QImage instead of wrapping it +# 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) +# +# Copyright (c) 2006 by Secret Labs AB +# Copyright (c) 2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import sys +from io import BytesIO + +from . import Image +from ._util import isPath + +qt_versions = [ + ["6", "PyQt6"], + ["side6", "PySide6"], + ["5", "PyQt5"], + ["side2", "PySide2"], +] + +# If a version has already been imported, attempt it first +qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True) +for qt_version, qt_module in qt_versions: + try: + if qt_module == "PyQt6": + from PyQt6.QtCore import QBuffer, QIODevice + from PyQt6.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PySide6": + from PySide6.QtCore import QBuffer, QIODevice + from PySide6.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PyQt5": + from PyQt5.QtCore import QBuffer, QIODevice + from PyQt5.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PySide2": + from PySide2.QtCore import QBuffer, QIODevice + from PySide2.QtGui import QImage, QPixmap, qRgba + except (ImportError, RuntimeError): + continue + qt_is_installed = True + break +else: + qt_is_installed = False + qt_version = None + + +def rgb(r, g, b, a=255): + """(Internal) Turns an RGB color into a Qt compatible color integer.""" + # use qRgb to pack the colors, and then turn the resulting long + # into a negative integer with the same bitpattern. + return qRgba(r, g, b, a) & 0xFFFFFFFF + + +def fromqimage(im): + """ + :param im: QImage or PIL ImageQt object + """ + buffer = QBuffer() + if qt_version == "6": + try: + qt_openmode = QIODevice.OpenModeFlag + except AttributeError: + qt_openmode = QIODevice.OpenMode + else: + qt_openmode = QIODevice + buffer.open(qt_openmode.ReadWrite) + # preserve alpha channel with png + # otherwise ppm is more friendly with Image.open + if im.hasAlphaChannel(): + im.save(buffer, "png") + else: + im.save(buffer, "ppm") + + b = BytesIO() + b.write(buffer.data()) + buffer.close() + b.seek(0) + + return Image.open(b) + + +def fromqpixmap(im): + return fromqimage(im) + # buffer = QBuffer() + # buffer.open(QIODevice.ReadWrite) + # # im.save(buffer) + # # What if png doesn't support some image features like animation? + # im.save(buffer, 'ppm') + # bytes_io = BytesIO() + # bytes_io.write(buffer.data()) + # buffer.close() + # bytes_io.seek(0) + # return Image.open(bytes_io) + + +def align8to32(bytes, width, mode): + """ + converts each scanline of data from 8 bit to 32 bit aligned + """ + + bits_per_pixel = {"1": 1, "L": 8, "P": 8, "I;16": 16}[mode] + + # calculate bytes per line and the extra padding if needed + bits_per_line = bits_per_pixel * width + full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) + bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) + + extra_padding = -bytes_per_line % 4 + + # already 32 bit aligned by luck + if not extra_padding: + return bytes + + new_data = [] + for i in range(len(bytes) // bytes_per_line): + new_data.append( + bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + + b"\x00" * extra_padding + ) + + return b"".join(new_data) + + +def _toqclass_helper(im): + data = None + colortable = None + exclusive_fp = False + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = str(im.toUtf8(), "utf-8") + if isPath(im): + im = Image.open(im) + exclusive_fp = True + + qt_format = QImage.Format if qt_version == "6" else QImage + if im.mode == "1": + format = qt_format.Format_Mono + elif im.mode == "L": + format = qt_format.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = qt_format.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i : i + 3])) + elif im.mode == "RGB": + # Populate the 4th channel with 255 + im = im.convert("RGBA") + + data = im.tobytes("raw", "BGRA") + format = qt_format.Format_RGB32 + elif im.mode == "RGBA": + data = im.tobytes("raw", "BGRA") + format = qt_format.Format_ARGB32 + elif im.mode == "I;16" and hasattr(qt_format, "Format_Grayscale16"): # Qt 5.13+ + im = im.point(lambda i: i * 256) + + format = qt_format.Format_Grayscale16 + else: + if exclusive_fp: + im.close() + raise ValueError(f"unsupported image mode {repr(im.mode)}") + + size = im.size + __data = data or align8to32(im.tobytes(), size[0], im.mode) + if exclusive_fp: + im.close() + return {"data": __data, "size": size, "format": format, "colortable": colortable} + + +if qt_is_installed: + + class ImageQt(QImage): + def __init__(self, im): + """ + An PIL image wrapper for Qt. This is a subclass of PyQt's QImage + class. + + :param im: A PIL Image object, or a file name (given either as + Python string or a PyQt string object). + """ + im_data = _toqclass_helper(im) + # must keep a reference, or Qt will crash! + # All QImage constructors that take data operate on an existing + # buffer, so this buffer has to hang on for the life of the image. + # Fixes https://github.com/python-pillow/Pillow/issues/1370 + self.__data = im_data["data"] + super().__init__( + self.__data, + im_data["size"][0], + im_data["size"][1], + im_data["format"], + ) + if im_data["colortable"]: + self.setColorTable(im_data["colortable"]) + + +def toqimage(im): + return ImageQt(im) + + +def toqpixmap(im): + # # This doesn't work. For now using a dumb approach. + # im_data = _toqclass_helper(im) + # result = QPixmap(im_data["size"][0], im_data["size"][1]) + # result.loadFromData(im_data["data"]) + qimage = toqimage(im) + return QPixmap.fromImage(qimage) diff --git a/venv/Lib/site-packages/PIL/ImageSequence.py b/venv/Lib/site-packages/PIL/ImageSequence.py new file mode 100644 index 0000000..9df910a --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageSequence.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# sequence support classes +# +# history: +# 1997-02-20 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## + + +class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. + + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ + + def __init__(self, im): + if not hasattr(im, "seek"): + raise AttributeError("im must have seek method") + self.im = im + self.position = getattr(self.im, "_min_frame", 0) + + def __getitem__(self, ix): + try: + self.im.seek(ix) + return self.im + except EOFError as e: + raise IndexError from e # end of sequence + + def __iter__(self): + return self + + def __next__(self): + try: + self.im.seek(self.position) + self.position += 1 + return self.im + except EOFError as e: + raise StopIteration from e + + +def all_frames(im, func=None): + """ + Applies a given function to all frames in an image or a list of images. + The frames are returned as a list of separate images. + + :param im: An image, or a list of images. + :param func: The function to apply to all of the image frames. + :returns: A list of images. + """ + if not isinstance(im, list): + im = [im] + + ims = [] + for imSequence in im: + current = imSequence.tell() + + ims += [im_frame.copy() for im_frame in Iterator(imSequence)] + + imSequence.seek(current) + return [func(im) for im in ims] if func else ims diff --git a/venv/Lib/site-packages/PIL/ImageShow.py b/venv/Lib/site-packages/PIL/ImageShow.py new file mode 100644 index 0000000..395bb22 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageShow.py @@ -0,0 +1,417 @@ +# +# The Python Imaging Library. +# $Id$ +# +# im.show() drivers +# +# History: +# 2008-04-06 fl Created +# +# Copyright (c) Secret Labs AB 2008. +# +# See the README file for information on usage and redistribution. +# +import os +import shutil +import subprocess +import sys +import warnings +from shlex import quote + +from PIL import Image + +_viewers = [] + + +def register(viewer, order=1): + """ + The :py:func:`register` function is used to register additional viewers:: + + from PIL import ImageShow + ImageShow.register(MyViewer()) # MyViewer will be used as a last resort + ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised + ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised + + :param viewer: The viewer to be registered. + :param order: + Zero or a negative integer to prepend this viewer to the list, + a positive integer to append it. + """ + try: + if issubclass(viewer, Viewer): + viewer = viewer() + except TypeError: + pass # raised if viewer wasn't a class + if order > 0: + _viewers.append(viewer) + else: + _viewers.insert(0, viewer) + + +def show(image, title=None, **options): + r""" + Display a given image. + + :param image: An image object. + :param title: Optional title. Not all viewers can display the title. + :param \**options: Additional viewer options. + :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. + """ + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return True + return False + + +class Viewer: + """Base class for viewers.""" + + # main api + + def show(self, image, **options): + """ + The main function for displaying an image. + Converts the given image to the target format and displays it. + """ + + if not ( + image.mode in ("1", "RGBA") + or (self.format == "PNG" and image.mode in ("I;16", "LA")) + ): + base = Image.getmodebase(image.mode) + if image.mode != base: + image = image.convert(base) + + return self.show_image(image, **options) + + # hook methods + + format = None + """The format to convert the image into.""" + options = {} + """Additional options used to convert the image.""" + + def get_format(self, image): + """Return format name, or ``None`` to save as PGM/PPM.""" + return self.format + + def get_command(self, file, **options): + """ + Returns the command used to display the file. + Not implemented in the base class. + """ + raise NotImplementedError + + def save_image(self, image): + """Save to temporary file and return filename.""" + return image._dump(format=self.get_format(image), **self.options) + + def show_image(self, image, **options): + """Display the given image.""" + return self.show_file(self.save_image(image), **options) + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + os.system(self.get_command(path, **options)) + return 1 + + +# -------------------------------------------------------------------- + + +class WindowsViewer(Viewer): + """The default viewer on Windows is the default system application for PNG files.""" + + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + return ( + f'start "Pillow" /WAIT "{file}" ' + "&& ping -n 2 127.0.0.1 >NUL " + f'&& del /f "{file}"' + ) + + +if sys.platform == "win32": + register(WindowsViewer) + + +class MacViewer(Viewer): + """The default viewer on macOS using ``Preview.app``.""" + + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a Preview.app" + command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" + return command + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.call(["open", "-a", "Preview.app", path]) + subprocess.Popen( + [ + sys.executable, + "-c", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", + path, + ] + ) + return 1 + + +if sys.platform == "darwin": + register(MacViewer) + + +class UnixViewer(Viewer): + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + command = self.get_command_ex(file, **options)[0] + return f"({command} {quote(file)}" + + +class XDGViewer(UnixViewer): + """ + The freedesktop.org ``xdg-open`` command. + """ + + def get_command_ex(self, file, **options): + command = executable = "xdg-open" + return command, executable + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.Popen(["xdg-open", path]) + return 1 + + +class DisplayViewer(UnixViewer): + """ + The ImageMagick ``display`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex(self, file, title=None, **options): + command = executable = "display" + if title: + command += f" -title {quote(title)}" + return command, executable + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + args = ["display"] + title = options.get("title") + if title: + args += ["-title", title] + args.append(path) + + subprocess.Popen(args) + return 1 + + +class GmDisplayViewer(UnixViewer): + """The GraphicsMagick ``gm display`` command.""" + + def get_command_ex(self, file, **options): + executable = "gm" + command = "gm display" + return command, executable + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.Popen(["gm", "display", path]) + return 1 + + +class EogViewer(UnixViewer): + """The GNOME Image Viewer ``eog`` command.""" + + def get_command_ex(self, file, **options): + executable = "eog" + command = "eog -n" + return command, executable + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.Popen(["eog", "-n", path]) + return 1 + + +class XVViewer(UnixViewer): + """ + The X Viewer ``xv`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += f" -name {quote(title)}" + return command, executable + + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + args = ["xv"] + title = options.get("title") + if title: + args += ["-name", title] + args.append(path) + + subprocess.Popen(args) + return 1 + + +if sys.platform not in ("win32", "darwin"): # unixoids + if shutil.which("xdg-open"): + register(XDGViewer) + if shutil.which("display"): + register(DisplayViewer) + if shutil.which("gm"): + register(GmDisplayViewer) + if shutil.which("eog"): + register(EogViewer) + if shutil.which("xv"): + register(XVViewer) + + +class IPythonViewer(Viewer): + """The viewer for IPython frontends.""" + + def show_image(self, image, **options): + ipython_display(image) + return 1 + + +try: + from IPython.display import display as ipython_display +except ImportError: + pass +else: + register(IPythonViewer) + + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print("Syntax: python3 ImageShow.py imagefile [title]") + sys.exit() + + with Image.open(sys.argv[1]) as im: + print(show(im, *sys.argv[2:])) diff --git a/venv/Lib/site-packages/PIL/ImageStat.py b/venv/Lib/site-packages/PIL/ImageStat.py new file mode 100644 index 0000000..ef4a1d6 --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageStat.py @@ -0,0 +1,147 @@ +# +# The Python Imaging Library. +# $Id$ +# +# global image statistics +# +# History: +# 1996-04-05 fl Created +# 1997-05-21 fl Added mask; added rms, var, stddev attributes +# 1997-08-05 fl Added median +# 1998-07-05 hk Fixed integer overflow error +# +# Notes: +# This class shows how to implement delayed evaluation of attributes. +# To get a certain value, simply access the corresponding attribute. +# The __getattr__ dispatcher takes care of the rest. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +import functools +import math +import operator + + +class Stat: + def __init__(self, image_or_list, mask=None): + try: + if mask: + self.h = image_or_list.histogram(mask) + else: + self.h = image_or_list.histogram() + except AttributeError: + self.h = image_or_list # assume it to be a histogram list + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) + + def __getattr__(self, id): + """Calculate missing attribute""" + if id[:4] == "_get": + raise AttributeError(id) + # calculate missing attribute + v = getattr(self, "_get" + id)() + setattr(self, id, v) + return v + + def _getextrema(self): + """Get min/max values for each band in the image""" + + def minmax(histogram): + n = 255 + x = 0 + for i in range(256): + if histogram[i]: + n = min(n, i) + x = max(x, i) + return n, x # returns (255, 0) if there's no data in the histogram + + v = [] + for i in range(0, len(self.h), 256): + v.append(minmax(self.h[i:])) + return v + + def _getcount(self): + """Get total number of pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + v.append(functools.reduce(operator.add, self.h[i : i + 256])) + return v + + def _getsum(self): + """Get sum of all pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + layerSum = 0.0 + for j in range(256): + layerSum += j * self.h[i + j] + v.append(layerSum) + return v + + def _getsum2(self): + """Get squared sum of all pixels in each layer""" + + v = [] + for i in range(0, len(self.h), 256): + sum2 = 0.0 + for j in range(256): + sum2 += (j**2) * float(self.h[i + j]) + v.append(sum2) + return v + + def _getmean(self): + """Get average pixel level for each layer""" + + v = [] + for i in self.bands: + v.append(self.sum[i] / self.count[i]) + return v + + def _getmedian(self): + """Get median pixel level for each layer""" + + v = [] + for i in self.bands: + s = 0 + half = self.count[i] // 2 + b = i * 256 + for j in range(256): + s = s + self.h[b + j] + if s > half: + break + v.append(j) + return v + + def _getrms(self): + """Get RMS for each layer""" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.sum2[i] / self.count[i])) + return v + + def _getvar(self): + """Get variance for each layer""" + + v = [] + for i in self.bands: + n = self.count[i] + v.append((self.sum2[i] - (self.sum[i] ** 2.0) / n) / n) + return v + + def _getstddev(self): + """Get standard deviation for each layer""" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.var[i])) + return v + + +Global = Stat # compatibility diff --git a/venv/Lib/site-packages/PIL/ImageTk.py b/venv/Lib/site-packages/PIL/ImageTk.py new file mode 100644 index 0000000..d151b9b --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageTk.py @@ -0,0 +1,301 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Tk display interface +# +# History: +# 96-04-08 fl Created +# 96-09-06 fl Added getimage method +# 96-11-01 fl Rewritten, removed image attribute and crop method +# 97-05-09 fl Use PyImagingPaste method instead of image type +# 97-05-12 fl Minor tweaks to match the IFUNC95 interface +# 97-05-17 fl Support the "pilbitmap" booster patch +# 97-06-05 fl Added file= and data= argument to image constructors +# 98-03-09 fl Added width and height methods to Image classes +# 98-07-02 fl Use default mode for "P" images without palette attribute +# 98-07-02 fl Explicitly destroy Tkinter image objects +# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) +# 99-07-26 fl Automatically hook into Tkinter (if possible) +# 99-08-15 fl Hook uses _imagingtk instead of _imaging +# +# Copyright (c) 1997-1999 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import tkinter +from io import BytesIO + +from . import Image + +# -------------------------------------------------------------------- +# Check for Tkinter interface hooks + +_pilbitmap_ok = None + + +def _pilbitmap_check(): + global _pilbitmap_ok + if _pilbitmap_ok is None: + try: + im = Image.new("1", (1, 1)) + tkinter.BitmapImage(data=f"PIL:{im.im.id}") + _pilbitmap_ok = 1 + except tkinter.TclError: + _pilbitmap_ok = 0 + return _pilbitmap_ok + + +def _get_image_from_kw(kw): + source = None + if "file" in kw: + source = kw.pop("file") + elif "data" in kw: + source = BytesIO(kw.pop("data")) + if source: + return Image.open(source) + + +def _pyimagingtkcall(command, photo, id): + tk = photo.tk + try: + tk.call(command, photo, id) + except tkinter.TclError: + # activate Tkinter hook + # may raise an error if it cannot attach to Tkinter + from . import _imagingtk + + try: + if hasattr(tk, "interp"): + # Required for PyPy, which always has CFFI installed + from cffi import FFI + + ffi = FFI() + + # PyPy is using an FFI CDATA element + # (Pdb) self.tk.interp + # + _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) + else: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call(command, photo, id) + + +# -------------------------------------------------------------------- +# PhotoImage + + +class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. + + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the ``file`` or ``data`` options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ + + def __init__(self, image=None, size=None, **kw): + + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + if hasattr(image, "mode") and hasattr(image, "size"): + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.load() + try: + mode = image.palette.mode + except AttributeError: + mode = "RGB" # default + size = image.size + kw["width"], kw["height"] = size + else: + mode = image + image = None + + if mode not in ["1", "L", "RGB", "RGBA"]: + mode = Image.getmodebase(mode) + + self.__mode = mode + self.__size = size + self.__photo = tkinter.PhotoImage(**kw) + self.tk = self.__photo.tk + if image: + self.paste(image) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ + return str(self.__photo) + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. See :ref:`coordinate-system`. If None is given + instead of a tuple, all of the image is assumed. + """ + + # convert to blittable + im.load() + image = im.im + if image.isblock() and im.mode == self.__mode: + block = image + else: + block = image.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + + _pyimagingtkcall("PyImagingPhoto", self.__photo, block.id) + + +# -------------------------------------------------------------------- +# BitmapImage + + +class BitmapImage: + """ + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is ``foreground``, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ + + def __init__(self, image=None, **kw): + + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + self.__mode = image.mode + self.__size = image.size + + if _pilbitmap_check(): + # fast way (requires the pilbitmap booster patch) + image.load() + kw["data"] = f"PIL:{image.im.id}" + self.__im = image # must keep a reference + else: + # slow but safe way + kw["data"] = image.tobitmap() + self.__photo = tkinter.BitmapImage(**kw) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ + return str(self.__photo) + + +def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" + im = Image.new("RGBA", (photo.width(), photo.height())) + block = im.im + + _pyimagingtkcall("PyImagingPhotoGet", photo, block.id) + + return im + + +def _show(image, title): + """Helper for the Image.show method.""" + + class UI(tkinter.Label): + def __init__(self, master, im): + if im.mode == "1": + self.image = BitmapImage(im, foreground="white", master=master) + else: + self.image = PhotoImage(im, master=master) + super().__init__(master, image=self.image, bg="black", bd=0) + + if not tkinter._default_root: + raise OSError("tkinter not initialized") + top = tkinter.Toplevel() + if title: + top.title(title) + UI(top, image).pack() diff --git a/venv/Lib/site-packages/PIL/ImageTransform.py b/venv/Lib/site-packages/PIL/ImageTransform.py new file mode 100644 index 0000000..7881f0d --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageTransform.py @@ -0,0 +1,102 @@ +# +# The Python Imaging Library. +# $Id$ +# +# transform wrappers +# +# History: +# 2002-04-08 fl Created +# +# Copyright (c) 2002 by Secret Labs AB +# Copyright (c) 2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +class Transform(Image.ImageTransformHandler): + def __init__(self, data): + self.data = data + + def getdata(self): + return self.method, self.data + + def transform(self, size, image, **options): + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + + +class AffineTransform(Transform): + """ + Define an affine image transform. + + This function takes a 6-tuple (a, b, c, d, e, f) which contain the first + two rows from an affine transform matrix. For each pixel (x, y) in the + output image, the new value is taken from a position (a x + b y + c, + d x + e y + f) in the input image, rounded to nearest pixel. + + This function can be used to scale, translate, rotate, and shear the + original image. + + See :py:meth:`~PIL.Image.Image.transform` + + :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows + from an affine transform matrix. + """ + + method = Image.Transform.AFFINE + + +class ExtentTransform(Transform): + """ + Define a transform to extract a subregion from an image. + + Maps a rectangle (defined by two corners) from the image to a rectangle of + the given size. The resulting image will contain data sampled from between + the corners, such that (x0, y0) in the input image will end up at (0,0) in + the output image, and (x1, y1) at size. + + This method can be used to crop, stretch, shrink, or mirror an arbitrary + rectangle in the current image. It is slightly slower than crop, but about + as fast as a corresponding resize operation. + + See :py:meth:`~PIL.Image.Image.transform` + + :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the + input image's coordinate system. See :ref:`coordinate-system`. + """ + + method = Image.Transform.EXTENT + + +class QuadTransform(Transform): + """ + Define a quad image transform. + + Maps a quadrilateral (a region defined by four corners) from the image to a + rectangle of the given size. + + See :py:meth:`~PIL.Image.Image.transform` + + :param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the + upper left, lower left, lower right, and upper right corner of the + source quadrilateral. + """ + + method = Image.Transform.QUAD + + +class MeshTransform(Transform): + """ + Define a mesh image transform. A mesh transform consists of one or more + individual quad transforms. + + See :py:meth:`~PIL.Image.Image.transform` + + :param data: A list of (bbox, quad) tuples. + """ + + method = Image.Transform.MESH diff --git a/venv/Lib/site-packages/PIL/ImageWin.py b/venv/Lib/site-packages/PIL/ImageWin.py new file mode 100644 index 0000000..ca9b14c --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImageWin.py @@ -0,0 +1,230 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Windows DIB display interface +# +# History: +# 1996-05-20 fl Created +# 1996-09-20 fl Fixed subregion exposure +# 1997-09-21 fl Added draw primitive (for tzPrint) +# 2003-05-21 fl Added experimental Window/ImageWindow classes +# 2003-09-05 fl Added fromstring/tostring methods +# +# Copyright (c) Secret Labs AB 1997-2003. +# Copyright (c) Fredrik Lundh 1996-2003. +# +# See the README file for information on usage and redistribution. +# + +from . import Image + + +class HDC: + """ + Wraps an HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ + + def __init__(self, dc): + self.dc = dc + + def __int__(self): + return self.dc + + +class HWND: + """ + Wraps an HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ + + def __init__(self, wnd): + self.wnd = wnd + + def __int__(self): + return self.wnd + + +class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". + + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + ``palette`` method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ + + def __init__(self, image, size=None): + if hasattr(image, "mode") and hasattr(image, "size"): + mode = image.mode + size = image.size + else: + mode = image + image = None + if mode not in ["1", "L", "P", "RGB"]: + mode = Image.getmodebase(mode) + self.image = Image.core.display(mode, size) + self.mode = mode + self.size = size + if image: + self.paste(image) + + def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. In PythonWin, you can use + ``CDC.GetHandleAttrib()`` to get a suitable handle. + """ + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.expose(dc) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.expose(handle) + return result + + def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ + if not src: + src = (0, 0) + self.size + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.draw(handle, dst, src) + return result + + def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ + if isinstance(handle, HWND): + handle = self.image.getdc(handle) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle) + return result + + def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. See :ref:`coordinate-system`. If + None is given instead of a tuple, all of the image is + assumed. + """ + im.load() + if self.mode != im.mode: + im = im.convert(self.mode) + if box: + self.image.paste(im.im, box) + else: + self.image.paste(im.im) + + def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) + """ + return self.image.frombytes(buffer) + + def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() + + +class Window: + """Create a Window with the given title size.""" + + def __init__(self, title="PIL", width=None, height=None): + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action, *args): + return getattr(self, "ui_handle_" + action)(*args) + + def ui_handle_clear(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_damage(self, x0, y0, x1, y1): + pass + + def ui_handle_destroy(self): + pass + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_resize(self, width, height): + pass + + def mainloop(self): + Image.core.eventloop() + + +class ImageWindow(Window): + """Create an image window which displays the given image.""" + + def __init__(self, image, title="PIL"): + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + super().__init__(title, width=width, height=height) + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/venv/Lib/site-packages/PIL/ImtImagePlugin.py b/venv/Lib/site-packages/PIL/ImtImagePlugin.py new file mode 100644 index 0000000..5790acd --- /dev/null +++ b/venv/Lib/site-packages/PIL/ImtImagePlugin.py @@ -0,0 +1,93 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IM Tools support for PIL +# +# history: +# 1996-05-27 fl Created (read 8-bit images only) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +import re + +from . import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(rb"([a-z]*) ([^ \r\n]*)") + + +## +# Image plugin for IM Tools images. + + +class ImtImageFile(ImageFile.ImageFile): + + format = "IMT" + format_description = "IM Tools" + + def _open(self): + + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + xsize = ysize = 0 + + while True: + + s = self.fp.read(1) + if not s: + break + + if s == b"\x0C": + + # image data begins + self.tile = [ + ("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1)) + ] + + break + + else: + + # read key/value pair + # FIXME: dangerous, may read whole file + s = s + self.fp.readline() + if len(s) == 1 or len(s) > 100: + break + if s[0] == ord(b"*"): + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1, 2) + if k == "width": + xsize = int(v) + self._size = xsize, ysize + elif k == "height": + ysize = int(v) + self._size = xsize, ysize + elif k == "pixel" and v == "n8": + self.mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open(ImtImageFile.format, ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/venv/Lib/site-packages/PIL/IptcImagePlugin.py b/venv/Lib/site-packages/PIL/IptcImagePlugin.py new file mode 100644 index 0000000..0bbe506 --- /dev/null +++ b/venv/Lib/site-packages/PIL/IptcImagePlugin.py @@ -0,0 +1,230 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IPTC/NAA file handling +# +# history: +# 1995-10-01 fl Created +# 1998-03-09 fl Cleaned up and added to PIL +# 2002-06-18 fl Added getiptcinfo helper +# +# Copyright (c) Secret Labs AB 1997-2002. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# +import os +import tempfile + +from . import Image, ImageFile +from ._binary import i8 +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 + +COMPRESSION = {1: "raw", 5: "jpeg"} + +PAD = o8(0) * 4 + + +# +# Helpers + + +def i(c): + return i32((PAD + c)[-4:]) + + +def dump(c): + for i in c: + print("%02x" % i8(i), end=" ") + print() + + +## +# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields +# from TIFF and JPEG files, use the getiptcinfo function. + + +class IptcImageFile(ImageFile.ImageFile): + + format = "IPTC" + format_description = "IPTC/NAA" + + def getint(self, key): + return i(self.info[key]) + + def field(self): + # + # get a IPTC field header + s = self.fp.read(5) + if not len(s): + return None, 0 + + tag = s[1], s[2] + + # syntax + if s[0] != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") + + # field size + size = s[3] + if size > 132: + raise OSError("illegal field length in IPTC/NAA file") + elif size == 128: + size = 0 + elif size > 128: + size = i(self.fp.read(size - 128)) + else: + size = i16(s, 3) + + return tag, size + + def _open(self): + + # load descriptive fields + while True: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8, 10): + break + if size: + tagdata = self.fp.read(size) + else: + tagdata = None + if tag in self.info: + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # mode + layers = i8(self.info[(3, 60)][0]) + component = i8(self.info[(3, 60)][1]) + if (3, 65) in self.info: + id = i8(self.info[(3, 65)][0]) - 1 + else: + id = 0 + if layers == 1 and not component: + self.mode = "L" + elif layers == 3 and component: + self.mode = "RGB"[id] + elif layers == 4 and component: + self.mode = "CMYK"[id] + + # size + self._size = self.getint((3, 20)), self.getint((3, 30)) + + # compression + try: + compression = COMPRESSION[self.getint((3, 120))] + except KeyError as e: + raise OSError("Unknown IPTC image compression") from e + + # tile + if tag == (8, 10): + self.tile = [ + ("iptc", (compression, offset), (0, 0, self.size[0], self.size[1])) + ] + + def load(self): + + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + type, tile, box = self.tile[0] + + encoding, offset = tile + + self.fp.seek(offset) + + # Copy image data to temporary file + o_fd, outfile = tempfile.mkstemp(text=False) + o = os.fdopen(o_fd) + if encoding == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write("P5\n%d %d\n255\n" % self.size) + while True: + type, size = self.field() + if type != (8, 10): + break + while size > 0: + s = self.fp.read(min(size, 8192)) + if not s: + break + o.write(s) + size -= len(s) + o.close() + + try: + with Image.open(outfile) as _im: + _im.load() + self.im = _im.im + finally: + try: + os.unlink(outfile) + except OSError: + pass + + +Image.register_open(IptcImageFile.format, IptcImageFile) + +Image.register_extension(IptcImageFile.format, ".iim") + + +def getiptcinfo(im): + """ + Get IPTC information from TIFF, JPEG, or IPTC file. + + :param im: An image containing IPTC data. + :returns: A dictionary containing IPTC information, or None if + no IPTC information block was found. + """ + import io + + from . import JpegImagePlugin, TiffImagePlugin + + data = None + + if isinstance(im, IptcImageFile): + # return info dictionary right away + return im.info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + photoshop = im.info.get("photoshop") + if photoshop: + data = photoshop.get(0x0404) + + elif isinstance(im, TiffImagePlugin.TiffImageFile): + # get raw data from the IPTC/NAA tag (PhotoShop tags the data + # as 4-byte integers, so we cannot use the get method...) + try: + data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except (AttributeError, KeyError): + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + + im = FakeImage() + im.__class__ = IptcImageFile + + # parse the IPTC information chunk + im.info = {} + im.fp = io.BytesIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info diff --git a/venv/Lib/site-packages/PIL/Jpeg2KImagePlugin.py b/venv/Lib/site-packages/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 0000000..fb5d70c --- /dev/null +++ b/venv/Lib/site-packages/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,362 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# 2021-06-30 rogermb Extract dpi information from the 'resc' header box +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# +import io +import os +import struct + +from . import Image, ImageFile + + +class BoxReader: + """ + A small helper class to read fields stored in JPEG2000 header boxes + and to easily step into and read sub-boxes. + """ + + def __init__(self, fp, length=-1): + self.fp = fp + self.has_length = length >= 0 + self.length = length + self.remaining_in_box = -1 + + def _can_read(self, num_bytes): + if self.has_length and self.fp.tell() + num_bytes > self.length: + # Outside box: ensure we don't read past the known file length + return False + if self.remaining_in_box >= 0: + # Inside box contents: ensure read does not go past box boundaries + return num_bytes <= self.remaining_in_box + else: + return True # No length known, just read + + def _read_bytes(self, num_bytes): + if not self._can_read(num_bytes): + raise SyntaxError("Not enough data in header") + + data = self.fp.read(num_bytes) + if len(data) < num_bytes: + raise OSError( + f"Expected to read {num_bytes} bytes but only got {len(data)}." + ) + + if self.remaining_in_box > 0: + self.remaining_in_box -= num_bytes + return data + + def read_fields(self, field_format): + size = struct.calcsize(field_format) + data = self._read_bytes(size) + return struct.unpack(field_format, data) + + def read_boxes(self): + size = self.remaining_in_box + data = self._read_bytes(size) + return BoxReader(io.BytesIO(data), size) + + def has_next_box(self): + if self.has_length: + return self.fp.tell() + self.remaining_in_box < self.length + else: + return True + + def next_box_type(self): + # Skip the rest of the box if it has not been read + if self.remaining_in_box > 0: + self.fp.seek(self.remaining_in_box, os.SEEK_CUR) + self.remaining_in_box = -1 + + # Read the length and type of the next box + lbox, tbox = self.read_fields(">I4s") + if lbox == 1: + lbox = self.read_fields(">Q")[0] + hlen = 16 + else: + hlen = 8 + + if lbox < hlen or not self._can_read(lbox - hlen): + raise SyntaxError("Invalid header length") + + self.remaining_in_box = lbox - hlen + return tbox + + +def _parse_codestream(fp): + """Parse the JPEG 2000 codestream to extract the size and component + count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" + + hdr = fp.read(2) + lsiz = struct.unpack(">H", hdr)[0] + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( + ">HHIIIIIIIIH", siz + ) + ssiz = [None] * csiz + xrsiz = [None] * csiz + yrsiz = [None] * csiz + for i in range(csiz): + ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 36 + 3 * i) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + if (yrsiz[0] & 0x7F) > 8: + mode = "I;16" + else: + mode = "L" + elif csiz == 2: + mode = "LA" + elif csiz == 3: + mode = "RGB" + elif csiz == 4: + mode = "RGBA" + else: + mode = None + + return (size, mode) + + +def _res_to_dpi(num, denom, exp): + """Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution, + calculated as (num / denom) * 10^exp and stored in dots per meter, + to floating-point dots per inch.""" + if denom != 0: + return (254 * num * (10**exp)) / (10000 * denom) + + +def _parse_jp2_header(fp): + """Parse the JP2 header box to extract size, component count, + color space information, and optionally DPI information, + returning a (size, mode, mimetype, dpi) tuple.""" + + # Find the JP2 header box + reader = BoxReader(fp) + header = None + mimetype = None + while reader.has_next_box(): + tbox = reader.next_box_type() + + if tbox == b"jp2h": + header = reader.read_boxes() + break + elif tbox == b"ftyp": + if reader.read_fields(">4s")[0] == b"jpx ": + mimetype = "image/jpx" + + size = None + mode = None + bpc = None + nc = None + dpi = None # 2-tuple of DPI info, or None + + while header.has_next_box(): + tbox = header.next_box_type() + + if tbox == b"ihdr": + height, width, nc, bpc = header.read_fields(">IIHB") + size = (width, height) + if nc == 1 and (bpc & 0x7F) > 8: + mode = "I;16" + elif nc == 1: + mode = "L" + elif nc == 2: + mode = "LA" + elif nc == 3: + mode = "RGB" + elif nc == 4: + mode = "RGBA" + elif tbox == b"res ": + res = header.read_boxes() + while res.has_next_box(): + tres = res.next_box_type() + if tres == b"resc": + vrcn, vrcd, hrcn, hrcd, vrce, hrce = res.read_fields(">HHHHBB") + hres = _res_to_dpi(hrcn, hrcd, hrce) + vres = _res_to_dpi(vrcn, vrcd, vrce) + if hres is not None and vres is not None: + dpi = (hres, vres) + break + + if size is None or mode is None: + raise SyntaxError("Malformed JP2 header") + + return (size, mode, mimetype, dpi) + + +## +# Image plugin for JPEG2000 images. + + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self): + sig = self.fp.read(4) + if sig == b"\xff\x4f\xff\x51": + self.codec = "j2k" + self._size, self.mode = _parse_codestream(self.fp) + else: + sig = sig + self.fp.read(8) + + if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a": + self.codec = "jp2" + header = _parse_jp2_header(self.fp) + self._size, self.mode, self.custom_mimetype, dpi = header + if dpi is not None: + self.info["dpi"] = dpi + else: + raise SyntaxError("not a JPEG 2000 file") + + if self.size is None or self.mode is None: + raise SyntaxError("unable to determine size/mode") + + self._reduce = 0 + self.layers = 0 + + fd = -1 + length = -1 + + try: + fd = self.fp.fileno() + length = os.fstat(fd).st_size + except Exception: + fd = -1 + try: + pos = self.fp.tell() + self.fp.seek(0, io.SEEK_END) + length = self.fp.tell() + self.fp.seek(pos) + except Exception: + length = -1 + + self.tile = [ + ( + "jpeg2k", + (0, 0) + self.size, + 0, + (self.codec, self._reduce, self.layers, fd, length), + ) + ] + + @property + def reduce(self): + # https://github.com/python-pillow/Pillow/issues/4343 found that the + # new Image 'reduce' method was shadowed by this plugin's 'reduce' + # property. This attempts to allow for both scenarios + return self._reduce or super().reduce + + @reduce.setter + def reduce(self, value): + self._reduce = value + + def load(self): + if self.tile and self._reduce: + power = 1 << self._reduce + adjust = power >> 1 + self._size = ( + int((self.size[0] + adjust) / power), + int((self.size[1] + adjust) / power), + ) + + # Update the reduce and layers settings + t = self.tile[0] + t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) + self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] + + return ImageFile.ImageFile.load(self) + + +def _accept(prefix): + return ( + prefix[:4] == b"\xff\x4f\xff\x51" + or prefix[:12] == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" + ) + + +# ------------------------------------------------------------ +# Save support + + +def _save(im, fp, filename): + # Get the keyword arguments + info = im.encoderinfo + + if filename.endswith(".j2k") or info.get("no_jp2", False): + kind = "j2k" + else: + kind = "jp2" + + offset = info.get("offset", None) + tile_offset = info.get("tile_offset", None) + tile_size = info.get("tile_size", None) + quality_mode = info.get("quality_mode", "rates") + quality_layers = info.get("quality_layers", None) + if quality_layers is not None and not ( + isinstance(quality_layers, (list, tuple)) + and all( + [ + isinstance(quality_layer, (int, float)) + for quality_layer in quality_layers + ] + ) + ): + raise ValueError("quality_layers must be a sequence of numbers") + + num_resolutions = info.get("num_resolutions", 0) + cblk_size = info.get("codeblock_size", None) + precinct_size = info.get("precinct_size", None) + irreversible = info.get("irreversible", False) + progression = info.get("progression", "LRCP") + cinema_mode = info.get("cinema_mode", "no") + mct = info.get("mct", 0) + fd = -1 + + if hasattr(fp, "fileno"): + try: + fd = fp.fileno() + except Exception: + fd = -1 + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + precinct_size, + irreversible, + progression, + cinema_mode, + mct, + fd, + ) + + ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)]) + + +# ------------------------------------------------------------ +# Registry stuff + + +Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) +Image.register_save(Jpeg2KImageFile.format, _save) + +Image.register_extensions( + Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"] +) + +Image.register_mime(Jpeg2KImageFile.format, "image/jp2") diff --git a/venv/Lib/site-packages/PIL/JpegImagePlugin.py b/venv/Lib/site-packages/PIL/JpegImagePlugin.py new file mode 100644 index 0000000..93741ec --- /dev/null +++ b/venv/Lib/site-packages/PIL/JpegImagePlugin.py @@ -0,0 +1,830 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continuous-Tone Still Images, +# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) +# +# History: +# 1995-09-09 fl Created +# 1995-09-13 fl Added full parser +# 1996-03-25 fl Added hack to use the IJG command line utilities +# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug +# 1996-05-28 fl Added draft support, JFIF version (0.1) +# 1996-12-30 fl Added encoder options, added progression property (0.2) +# 1997-08-27 fl Save mode 1 images as BW (0.3) +# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) +# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) +# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) +# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) +# 2003-04-25 fl Added experimental EXIF decoder (0.5) +# 2003-06-06 fl Added experimental EXIF GPSinfo decoder +# 2003-09-13 fl Extract COM markers +# 2009-09-06 fl Added icc_profile support (from Florian Hoech) +# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) +# 2009-03-08 fl Added subsampling support (from Justin Huff). +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# +import array +import io +import math +import os +import struct +import subprocess +import sys +import tempfile +import warnings + +from . import Image, ImageFile, TiffImagePlugin +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from .JpegPresets import presets + +# +# Parser + + +def Skip(self, marker): + n = i16(self.fp.read(2)) - 2 + ImageFile._safe_read(self.fp, n) + + +def APP(self, marker): + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + app = "APP%d" % (marker & 15) + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s[:4] == b"JFIF": + # extract JFIF information + self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif_version"] = divmod(version, 256) + # extract JFIF properties + try: + jfif_unit = s[7] + jfif_density = i16(s, 8), i16(s, 10) + except Exception: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s[:5] == b"Exif\0": + if "exif" not in self.info: + # extract EXIF information (incomplete) + self.info["exif"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": + # Since an ICC profile can be larger than the maximum size of + # a JPEG marker (64K), we need provisions to split it into + # multiple markers. The format defined by the ICC specifies + # one or more APP2 markers containing the following data: + # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + # Marker sequence number 1, 2, etc (1 byte) + # Number of markers Total of APP2's used (1 byte) + # Profile data (remainder of APP2 data) + # Decoders should use the marker sequence numbers to + # reassemble the profile, rather than assuming that the APP2 + # markers appear in the correct sequence. + self.icclist.append(s) + elif marker == 0xFFED and s[:14] == b"Photoshop 3.0\x00": + # parse the image resource block + offset = 14 + photoshop = self.info.setdefault("photoshop", {}) + while s[offset : offset + 4] == b"8BIM": + try: + offset += 4 + # resource code + code = i16(s, offset) + offset += 2 + # resource name (usually empty) + name_len = s[offset] + # name = s[offset+1:offset+1+name_len] + offset += 1 + name_len + offset += offset & 1 # align + # resource data block + size = i32(s, offset) + offset += 4 + data = s[offset : offset + size] + if code == 0x03ED: # ResolutionInfo + data = { + "XResolution": i32(data, 0) / 65536, + "DisplayedUnitsX": i16(data, 4), + "YResolution": i32(data, 8) / 65536, + "DisplayedUnitsY": i16(data, 12), + } + photoshop[code] = data + offset += size + offset += offset & 1 # align + except struct.error: + break # insufficient data + + elif marker == 0xFFEE and s[:5] == b"Adobe": + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = s[11] + except IndexError: + pass + else: + self.info["adobe_transform"] = adobe_transform + elif marker == 0xFFE2 and s[:4] == b"MPF\0": + # extract MPO information + self.info["mp"] = s[4:] + # offset is current location minus buffer size + # plus constant header size + self.info["mpoffset"] = self.fp.tell() - n + 4 + + # If DPI isn't in JPEG header, fetch from EXIF + if "dpi" not in self.info and "exif" in self.info: + try: + exif = self.getexif() + resolution_unit = exif[0x0128] + x_resolution = exif[0x011A] + try: + dpi = float(x_resolution[0]) / x_resolution[1] + except TypeError: + dpi = x_resolution + if math.isnan(dpi): + raise ValueError + if resolution_unit == 3: # cm + # 1 dpcm = 2.54 dpi + dpi *= 2.54 + self.info["dpi"] = dpi, dpi + except (TypeError, KeyError, SyntaxError, ValueError, ZeroDivisionError): + # SyntaxError for invalid/unreadable EXIF + # KeyError for dpi not included + # ZeroDivisionError for invalid dpi rational value + # ValueError or TypeError for dpi being an invalid float + self.info["dpi"] = 72, 72 + + +def COM(self, marker): + # + # Comment marker. Store these in the APP dictionary. + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + self.info["comment"] = s + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + + +def SOF(self, marker): + # + # Start of frame marker. Defines the size and mode of the + # image. JPEG is colour blind, so we use some simple + # heuristics to map the number of layers to an appropriate + # mode. Note that this could be made a bit brighter, by + # looking for JFIF and Adobe APP markers. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + self._size = i16(s, 3), i16(s, 1) + + self.bits = s[0] + if self.bits != 8: + raise SyntaxError(f"cannot handle {self.bits}-bit layers") + + self.layers = s[5] + if self.layers == 1: + self.mode = "L" + elif self.layers == 3: + self.mode = "RGB" + elif self.layers == 4: + self.mode = "CMYK" + else: + raise SyntaxError(f"cannot handle {self.layers}-layer images") + + if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: + self.info["progressive"] = self.info["progression"] = 1 + + if self.icclist: + # fixup icc profile + self.icclist.sort() # sort by sequence number + if self.icclist[0][13] == len(self.icclist): + profile = [] + for p in self.icclist: + profile.append(p[14:]) + icc_profile = b"".join(profile) + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = [] + + for i in range(6, len(s), 3): + t = s[i : i + 3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) + + +def DQT(self, marker): + # + # Define quantization table. Note that there might be more + # than one table in each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + v = s[0] + precision = 1 if (v // 16 == 0) else 2 # in bytes + qt_length = 1 + precision * 64 + if len(s) < qt_length: + raise SyntaxError("bad quantization table marker") + data = array.array("B" if precision == 1 else "H", s[1:qt_length]) + if sys.byteorder == "little" and precision > 1: + data.byteswap() # the values are always big-endian + self.quantization[v & 15] = [data[i] for i in zigzag_index] + s = s[qt_length:] + + +# +# JPEG marker table + +MARKER = { + 0xFFC0: ("SOF0", "Baseline DCT", SOF), + 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), + 0xFFC2: ("SOF2", "Progressive DCT", SOF), + 0xFFC3: ("SOF3", "Spatial lossless", SOF), + 0xFFC4: ("DHT", "Define Huffman table", Skip), + 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), + 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), + 0xFFC7: ("SOF7", "Differential spatial", SOF), + 0xFFC8: ("JPG", "Extension", None), + 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), + 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), + 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), + 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), + 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), + 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), + 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), + 0xFFD0: ("RST0", "Restart 0", None), + 0xFFD1: ("RST1", "Restart 1", None), + 0xFFD2: ("RST2", "Restart 2", None), + 0xFFD3: ("RST3", "Restart 3", None), + 0xFFD4: ("RST4", "Restart 4", None), + 0xFFD5: ("RST5", "Restart 5", None), + 0xFFD6: ("RST6", "Restart 6", None), + 0xFFD7: ("RST7", "Restart 7", None), + 0xFFD8: ("SOI", "Start of image", None), + 0xFFD9: ("EOI", "End of image", None), + 0xFFDA: ("SOS", "Start of scan", Skip), + 0xFFDB: ("DQT", "Define quantization table", DQT), + 0xFFDC: ("DNL", "Define number of lines", Skip), + 0xFFDD: ("DRI", "Define restart interval", Skip), + 0xFFDE: ("DHP", "Define hierarchical progression", SOF), + 0xFFDF: ("EXP", "Expand reference component", Skip), + 0xFFE0: ("APP0", "Application segment 0", APP), + 0xFFE1: ("APP1", "Application segment 1", APP), + 0xFFE2: ("APP2", "Application segment 2", APP), + 0xFFE3: ("APP3", "Application segment 3", APP), + 0xFFE4: ("APP4", "Application segment 4", APP), + 0xFFE5: ("APP5", "Application segment 5", APP), + 0xFFE6: ("APP6", "Application segment 6", APP), + 0xFFE7: ("APP7", "Application segment 7", APP), + 0xFFE8: ("APP8", "Application segment 8", APP), + 0xFFE9: ("APP9", "Application segment 9", APP), + 0xFFEA: ("APP10", "Application segment 10", APP), + 0xFFEB: ("APP11", "Application segment 11", APP), + 0xFFEC: ("APP12", "Application segment 12", APP), + 0xFFED: ("APP13", "Application segment 13", APP), + 0xFFEE: ("APP14", "Application segment 14", APP), + 0xFFEF: ("APP15", "Application segment 15", APP), + 0xFFF0: ("JPG0", "Extension 0", None), + 0xFFF1: ("JPG1", "Extension 1", None), + 0xFFF2: ("JPG2", "Extension 2", None), + 0xFFF3: ("JPG3", "Extension 3", None), + 0xFFF4: ("JPG4", "Extension 4", None), + 0xFFF5: ("JPG5", "Extension 5", None), + 0xFFF6: ("JPG6", "Extension 6", None), + 0xFFF7: ("JPG7", "Extension 7", None), + 0xFFF8: ("JPG8", "Extension 8", None), + 0xFFF9: ("JPG9", "Extension 9", None), + 0xFFFA: ("JPG10", "Extension 10", None), + 0xFFFB: ("JPG11", "Extension 11", None), + 0xFFFC: ("JPG12", "Extension 12", None), + 0xFFFD: ("JPG13", "Extension 13", None), + 0xFFFE: ("COM", "Comment", COM), +} + + +def _accept(prefix): + # Magic number was taken from https://en.wikipedia.org/wiki/JPEG + return prefix[0:3] == b"\xFF\xD8\xFF" + + +## +# Image plugin for JPEG and JFIF images. + + +class JpegImageFile(ImageFile.ImageFile): + + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self): + + s = self.fp.read(3) + + if not _accept(s): + raise SyntaxError("not a JPEG file") + s = b"\xFF" + + # Create attributes + self.bits = self.layers = 0 + + # JPEG specifics (internal) + self.layer = [] + self.huffman_dc = {} + self.huffman_ac = {} + self.quantization = {} + self.app = {} # compatibility + self.applist = [] + self.icclist = [] + + while True: + + i = s[0] + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = self.fp.read(1) + continue + + if i in MARKER: + name, description, handler = MARKER[i] + if handler is not None: + handler(self, i) + if i == 0xFFDA: # start of scan + rawmode = self.mode + if self.mode == "CMYK": + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i == 0 or i == 0xFFFF: + # padded marker or junk; move on + s = b"\xff" + elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) + s = self.fp.read(1) + else: + raise SyntaxError("no marker found") + + def load_read(self, read_bytes): + """ + internal: read more image data + For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker + so libjpeg can finish decoding + """ + s = self.fp.read(read_bytes) + + if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"): + # Premature EOF. + # Pretend file is finished adding EOI marker + self._ended = True + return b"\xFF\xD9" + + return s + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + # Protect from second call + if self.decoderconfig: + return + + d, e, o, a = self.tile[0] + scale = 1 + original_size = self.size + + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self.mode = mode + a = mode, "" + + if size: + scale = min(self.size[0] // size[0], self.size[1] // size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + e = ( + e[0], + e[1], + (e[2] - e[0] + s - 1) // s + e[0], + (e[3] - e[1] + s - 1) // s + e[1], + ) + self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s) + scale = s + + self.tile = [(d, e, o, a)] + self.decoderconfig = (scale, 0) + + box = (0, 0, original_size[0] / scale, original_size[1] / scale) + return (self.mode, box) + + def load_djpeg(self): + + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + f, path = tempfile.mkstemp() + os.close(f) + if os.path.exists(self.filename): + subprocess.check_call(["djpeg", "-outfile", path, self.filename]) + else: + raise ValueError("Invalid Filename") + + try: + with Image.open(path) as _im: + _im.load() + self.im = _im.im + finally: + try: + os.unlink(path) + except OSError: + pass + + self.mode = self.im.mode + self._size = self.im.size + + self.tile = [] + + def _getexif(self): + return _getexif(self) + + def _getmp(self): + return _getmp(self) + + def getxmp(self): + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ + + for segment, content in self.applist: + if segment == "APP1": + marker, xmp_tags = content.rsplit(b"\x00", 1) + if marker == b"http://ns.adobe.com/xap/1.0/": + return self._getxmp(xmp_tags) + return {} + + +def _getexif(self): + if "exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + +def _getmp(self): + # Extract MP information. This method was inspired by the "highly + # experimental" _getexif version that's been in use for years now, + # itself based on the ImageFileDirectory class in the TIFF plugin. + + # The MP record essentially consists of a TIFF file embedded in a JPEG + # application marker. + try: + data = self.info["mp"] + except KeyError: + return None + file_contents = io.BytesIO(data) + head = file_contents.read(8) + endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<" + # process dictionary + try: + info = TiffImagePlugin.ImageFileDirectory_v2(head) + file_contents.seek(info.next) + info.load(file_contents) + mp = dict(info) + except Exception as e: + raise SyntaxError("malformed MP Index (unreadable directory)") from e + # it's an error not to have a number of images + try: + quant = mp[0xB001] + except KeyError as e: + raise SyntaxError("malformed MP Index (no number of images)") from e + # get MP entries + mpentries = [] + try: + rawmpentries = mp[0xB002] + for entrynum in range(0, quant): + unpackedentry = struct.unpack_from( + f"{endianness}LLLHH", rawmpentries, entrynum * 16 + ) + labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2") + mpentry = dict(zip(labels, unpackedentry)) + mpentryattr = { + "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)), + "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)), + "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)), + "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27, + "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24, + "MPType": mpentry["Attribute"] & 0x00FFFFFF, + } + if mpentryattr["ImageDataFormat"] == 0: + mpentryattr["ImageDataFormat"] = "JPEG" + else: + raise SyntaxError("unsupported picture format in MPO") + mptypemap = { + 0x000000: "Undefined", + 0x010001: "Large Thumbnail (VGA Equivalent)", + 0x010002: "Large Thumbnail (Full HD Equivalent)", + 0x020001: "Multi-Frame Image (Panorama)", + 0x020002: "Multi-Frame Image: (Disparity)", + 0x020003: "Multi-Frame Image: (Multi-Angle)", + 0x030000: "Baseline MP Primary Image", + } + mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown") + mpentry["Attribute"] = mpentryattr + mpentries.append(mpentry) + mp[0xB002] = mpentries + except KeyError as e: + raise SyntaxError("malformed MP Index (bad MP Entry)") from e + # Next we should try and parse the individual image unique ID list; + # we don't because I've never seen this actually used in a real MPO + # file and so can't test it. + return mp + + +# -------------------------------------------------------------------- +# stuff to save JPEG files + +RAWMODE = { + "1": "L", + "L": "L", + "RGB": "RGB", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +# fmt: off +zigzag_index = ( + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63, +) + +samplings = { + (1, 1, 1, 1, 1, 1): 0, + (2, 1, 1, 1, 1, 1): 1, + (2, 2, 1, 1, 1, 1): 2, +} +# fmt: on + + +def convert_dict_qtables(qtables): + warnings.warn( + "convert_dict_qtables is deprecated and will be removed in Pillow 10" + "(2023-07-01). Conversion is no longer needed.", + DeprecationWarning, + ) + return qtables + + +def get_sampling(im): + # There's no subsampling when images have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to the default value. + # + # NOTE: currently Pillow can't encode JPEG to YCCK format. + # If YCCK support is added in the future, subsampling code will have + # to be updated (here and in JpegEncode.c) to deal with 4 layers. + if not hasattr(im, "layers") or im.layers in (1, 4): + return -1 + sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] + return samplings.get(sampling, -1) + + +def _save(im, fp, filename): + if im.width == 0 or im.height == 0: + raise ValueError("cannot write empty image as JPEG") + + try: + rawmode = RAWMODE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as JPEG") from e + + info = im.encoderinfo + + dpi = [round(x) for x in info.get("dpi", (0, 0))] + + quality = info.get("quality", -1) + subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = -1 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = -1 + subsampling = preset.get("subsampling", -1) + qtables = preset.get("quantization") + elif not isinstance(quality, int): + raise ValueError("Invalid quality setting") + else: + if subsampling in presets: + subsampling = presets[subsampling].get("subsampling", -1) + if isinstance(qtables, str) and qtables in presets: + qtables = presets[qtables].get("quantization") + + if subsampling == "4:4:4": + subsampling = 0 + elif subsampling == "4:2:2": + subsampling = 1 + elif subsampling == "4:2:0": + subsampling = 2 + elif subsampling == "4:1:1": + # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. + # Set 4:2:0 if someone is still using that value. + subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + subsampling = get_sampling(im) + + def validate_qtables(qtables): + if qtables is None: + return qtables + if isinstance(qtables, str): + try: + lines = [ + int(num) + for line in qtables.splitlines() + for num in line.split("#", 1)[0].split() + ] + except ValueError as e: + raise ValueError("Invalid quantization table") from e + else: + qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)] + if isinstance(qtables, (tuple, list, dict)): + if isinstance(qtables, dict): + qtables = [ + qtables[key] for key in range(len(qtables)) if key in qtables + ] + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + raise ValueError("None or too many quantization tables") + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + raise TypeError + table = array.array("H", table) + except TypeError as e: + raise ValueError("Invalid quantization table") from e + else: + qtables[idx] = list(table) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = b"" + + icc_profile = info.get("icc_profile") + if icc_profile: + ICC_OVERHEAD_LEN = 14 + MAX_BYTES_IN_MARKER = 65533 + MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN + markers = [] + while icc_profile: + markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER]) + icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] + i = 1 + for marker in markers: + size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + extra += ( + b"\xFF\xE2" + + size + + b"ICC_PROFILE\0" + + o8(i) + + o8(len(markers)) + + marker + ) + i += 1 + + # "progressive" is the official name, but older documentation + # says "progression" + # FIXME: issue a warning if the wrong form is used (post-1.1.7) + progressive = info.get("progressive", False) or info.get("progression", False) + + optimize = info.get("optimize", False) + + exif = info.get("exif", b"") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + + # get keyword arguments + im.encoderconfig = ( + quality, + progressive, + info.get("smooth", 0), + optimize, + info.get("streamtype", 0), + dpi[0], + dpi[1], + subsampling, + qtables, + extra, + exif, + ) + + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is + # channels*size, this is a value that's been used in a django patch. + # https://github.com/matthewwithanm/django-imagekit/issues/50 + bufsize = 0 + if optimize or progressive: + # CMYK can be bigger + if im.mode == "CMYK": + bufsize = 4 * im.size[0] * im.size[1] + # keep sets quality to -1, but the actual value may be high. + elif quality >= 95 or quality == -1: + bufsize = 2 * im.size[0] * im.size[1] + else: + bufsize = im.size[0] * im.size[1] + + # The EXIF info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough. Same with the icc_profile block. + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5, len(extra) + 1) + + ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize) + + +def _save_cjpeg(im, fp, filename): + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + tempfile = im._dump() + subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) + try: + os.unlink(tempfile) + except OSError: + pass + + +## +# Factory for making JPEG and MPO instances +def jpeg_factory(fp=None, filename=None): + im = JpegImageFile(fp, filename) + try: + mpheader = im._getmp() + if mpheader[45057] > 1: + # It's actually an MPO + from .MpoImagePlugin import MpoImageFile + + # Don't reload everything, just convert it. + im = MpoImageFile.adopt(im, mpheader) + except (TypeError, IndexError): + # It is really a JPEG + pass + except SyntaxError: + warnings.warn( + "Image appears to be a malformed MPO file, it will be " + "interpreted as a base JPEG file" + ) + return im + + +# --------------------------------------------------------------------- +# Registry stuff + +Image.register_open(JpegImageFile.format, jpeg_factory, _accept) +Image.register_save(JpegImageFile.format, _save) + +Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) + +Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/venv/Lib/site-packages/PIL/JpegPresets.py b/venv/Lib/site-packages/PIL/JpegPresets.py new file mode 100644 index 0000000..e5a5d17 --- /dev/null +++ b/venv/Lib/site-packages/PIL/JpegPresets.py @@ -0,0 +1,240 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. +Can be used when saving JPEG files. + +The following presets are available by default: +``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, +``low``, ``medium``, ``high``, ``maximum``. +More presets can be added to the :py:data:`presets` dict if needed. + +To apply the preset, specify:: + + quality="preset_name" + +To apply only the quantization table:: + + qtables="preset_name" + +To apply only the subsampling setting:: + + subsampling="preset_name" + +Example:: + + im.save("image_name.jpg", quality="web_high") + +Subsampling +----------- + +Subsampling is the practice of encoding images by implementing less resolution +for chroma information than for luma information. +(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) + +Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and +4:2:0. + +You can get the subsampling of a JPEG with the +:func:`.JpegImagePlugin.get_sampling` function. + +In JPEG compressed data a JPEG marker is used instead of an EXIF tag. +(ref.: https://www.exiv2.org/tags.html) + + +Quantization tables +------------------- + +They are values use by the DCT (Discrete cosine transform) to remove +*unnecessary* information from the image (the lossy part of the compression). +(ref.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +https://en.wikipedia.org/wiki/JPEG#Quantization) + +You can get the quantization tables of a JPEG with:: + + im.quantization + +This will return a dict with a number of lists. You can pass this dict +directly as the qtables argument when saving a JPEG. + +The quantization table format in presets is a list with sublists. These formats +are interchangeable. + +Libjpeg ref.: +https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +# fmt: off +presets = { + 'web_low': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68], + [21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68] + ]}, + 'web_medium': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64], + [17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64] + ]}, + 'web_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, + 11, 10, 12, 15, 20, 27, 31, 31, + 12, 12, 14, 19, 27, 31, 31, 31, + 16, 12, 19, 28, 31, 31, 31, 31], + [7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, + 13, 16, 17, 31, 31, 31, 31, 31, + 24, 21, 31, 31, 31, 31, 31, 31, + 26, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31] + ]}, + 'web_very_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'web_maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 3, + 1, 1, 1, 1, 2, 2, 3, 3, + 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 2, 2, 3, 3, 3, 3], + [1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 1, 2, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, + 2, 2, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3] + ]}, + 'low': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [18, 14, 14, 21, 30, 35, 34, 17, + 14, 16, 16, 19, 26, 23, 12, 12, + 14, 16, 17, 21, 23, 12, 12, 12, + 21, 19, 21, 23, 12, 12, 12, 12, + 30, 26, 23, 12, 12, 12, 12, 12, + 35, 23, 12, 12, 12, 12, 12, 12, + 34, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [20, 19, 22, 27, 20, 20, 17, 17, + 19, 25, 23, 14, 14, 12, 12, 12, + 22, 23, 14, 14, 12, 12, 12, 12, + 27, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'medium': {'subsampling': 2, # "4:2:0" + 'quantization': [ + [12, 8, 8, 12, 17, 21, 24, 17, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, + 12, 11, 12, 21, 12, 12, 12, 12, + 17, 15, 19, 12, 12, 12, 12, 12, + 21, 19, 12, 12, 12, 12, 12, 12, + 24, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [13, 11, 13, 16, 20, 20, 17, 17, + 11, 14, 14, 14, 14, 12, 12, 12, + 13, 14, 14, 14, 12, 12, 12, 12, + 16, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, + 11, 10, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 12, 12, 12, 12, 12, 12, 12], + [7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, + 13, 16, 14, 14, 12, 12, 12, 12, + 24, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, +} +# fmt: on diff --git a/venv/Lib/site-packages/PIL/McIdasImagePlugin.py b/venv/Lib/site-packages/PIL/McIdasImagePlugin.py new file mode 100644 index 0000000..cd047fe --- /dev/null +++ b/venv/Lib/site-packages/PIL/McIdasImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Basic McIdas support for PIL +# +# History: +# 1997-05-05 fl Created (8-bit images only) +# 2009-03-08 fl Added 16/32-bit support. +# +# Thanks to Richard Jones and Craig Swank for specs and samples. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +import struct + +from . import Image, ImageFile + + +def _accept(s): + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" + + +## +# Image plugin for McIdas area images. + + +class McIdasImageFile(ImageFile.ImageFile): + + format = "MCIDAS" + format_description = "McIdas area file" + + def _open(self): + + # parse area file directory + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + raise SyntaxError("not an McIdas area file") + + self.area_descriptor_raw = s + self.area_descriptor = w = [0] + list(struct.unpack("!64i", s)) + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + # FIXME: add memory map support + mode = "I" + rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I" + rawmode = "I;32B" + else: + raise SyntaxError("unsupported McIdas format") + + self.mode = mode + self._size = w[10], w[9] + + offset = w[34] + w[15] + stride = w[15] + w[10] * w[11] * w[14] + + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] + + +# -------------------------------------------------------------------- +# registry + +Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) + +# no default extension diff --git a/venv/Lib/site-packages/PIL/MicImagePlugin.py b/venv/Lib/site-packages/PIL/MicImagePlugin.py new file mode 100644 index 0000000..9248b1b --- /dev/null +++ b/venv/Lib/site-packages/PIL/MicImagePlugin.py @@ -0,0 +1,107 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Microsoft Image Composer support for PIL +# +# Notes: +# uses TiffImagePlugin.py to read the actual image streams +# +# History: +# 97-01-20 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +import olefile + +from . import Image, TiffImagePlugin + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == olefile.MAGIC + + +## +# Image plugin for Microsoft's Image Composer file format. + + +class MicImageFile(TiffImagePlugin.TiffImageFile): + + format = "MIC" + format_description = "Microsoft Image Composer" + _close_exclusive_fp_after_loading = False + + def _open(self): + + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + raise SyntaxError("not an MIC file; invalid OLE file") from e + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [] + for path in self.ole.listdir(): + if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image": + self.images.append(path) + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + raise SyntaxError("not an MIC file; no image entries") + + self.__fp = self.fp + self.frame = None + self._n_frames = len(self.images) + self.is_animated = self._n_frames > 1 + + if len(self.images) > 1: + self._category = Image.CONTAINER + + self.seek(0) + + def seek(self, frame): + if not self._seek_check(frame): + return + try: + filename = self.images[frame] + except IndexError as e: + raise EOFError("no such frame") from e + + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self): + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- + +Image.register_open(MicImageFile.format, MicImageFile, _accept) + +Image.register_extension(MicImageFile.format, ".mic") diff --git a/venv/Lib/site-packages/PIL/MpegImagePlugin.py b/venv/Lib/site-packages/PIL/MpegImagePlugin.py new file mode 100644 index 0000000..a358dfd --- /dev/null +++ b/venv/Lib/site-packages/PIL/MpegImagePlugin.py @@ -0,0 +1,83 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPEG file handling +# +# History: +# 95-09-09 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile +from ._binary import i8 + +# +# Bitstream parser + + +class BitStream: + def __init__(self, fp): + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self): + return i8(self.fp.read(1)) + + def peek(self, bits): + while self.bits < bits: + c = self.next() + if c < 0: + self.bits = 0 + continue + self.bitbuffer = (self.bitbuffer << 8) + c + self.bits += 8 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 + + def skip(self, bits): + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) + self.bits += 8 + self.bits = self.bits - bits + + def read(self, bits): + v = self.peek(bits) + self.bits = self.bits - bits + return v + + +## +# Image plugin for MPEG streams. This plugin can identify a stream, +# but it cannot read it. + + +class MpegImageFile(ImageFile.ImageFile): + + format = "MPEG" + format_description = "MPEG" + + def _open(self): + + s = BitStream(self.fp) + + if s.read(32) != 0x1B3: + raise SyntaxError("not an MPEG file") + + self.mode = "RGB" + self._size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(MpegImageFile.format, MpegImageFile) + +Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) + +Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/venv/Lib/site-packages/PIL/MpoImagePlugin.py b/venv/Lib/site-packages/PIL/MpoImagePlugin.py new file mode 100644 index 0000000..88c1bfc --- /dev/null +++ b/venv/Lib/site-packages/PIL/MpoImagePlugin.py @@ -0,0 +1,137 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPO file handling +# +# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the +# Camera & Imaging Products Association) +# +# The multi-picture object combines multiple JPEG images (with a modified EXIF +# data format) into a single file. While it can theoretically be used much like +# a GIF animation, it is commonly used to represent 3D photographs and is (as +# of this writing) the most commonly used format by 3D cameras. +# +# History: +# 2014-03-13 Feneric Created +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile, JpegImagePlugin +from ._binary import i16be as i16 + +# def _accept(prefix): +# return JpegImagePlugin._accept(prefix) + + +def _save(im, fp, filename): + # Note that we can only save the current frame at present + return JpegImagePlugin._save(im, fp, filename) + + +## +# Image plugin for MPO images. + + +class MpoImageFile(JpegImagePlugin.JpegImageFile): + + format = "MPO" + format_description = "MPO (CIPA DC-007)" + _close_exclusive_fp_after_loading = False + + def _open(self): + self.fp.seek(0) # prep the fp in order to pass the JPEG test + JpegImagePlugin.JpegImageFile._open(self) + self._after_jpeg_open() + + def _after_jpeg_open(self, mpheader=None): + self._initial_size = self.size + self.mpinfo = mpheader if mpheader is not None else self._getmp() + self.n_frames = self.mpinfo[0xB001] + self.__mpoffsets = [ + mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] + ] + self.__mpoffsets[0] = 0 + # Note that the following assertion will only be invalid if something + # gets broken within JpegImagePlugin. + assert self.n_frames == len(self.__mpoffsets) + del self.info["mpoffset"] # no longer needed + self.is_animated = self.n_frames > 1 + self.__fp = self.fp # FIXME: hack + self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame + self.__frame = 0 + self.offset = 0 + # for now we can only handle reading and individual frame extraction + self.readonly = 1 + + def load_seek(self, pos): + self.__fp.seek(pos) + + def seek(self, frame): + if not self._seek_check(frame): + return + self.fp = self.__fp + self.offset = self.__mpoffsets[frame] + + self.fp.seek(self.offset + 2) # skip SOI marker + segment = self.fp.read(2) + if not segment: + raise ValueError("No data found for frame") + self._size = self._initial_size + if i16(segment) == 0xFFE1: # APP1 + n = i16(self.fp.read(2)) - 2 + self.info["exif"] = ImageFile._safe_read(self.fp, n) + + mptype = self.mpinfo[0xB002][frame]["Attribute"]["MPType"] + if mptype.startswith("Large Thumbnail"): + exif = self.getexif().get_ifd(0x8769) + if 40962 in exif and 40963 in exif: + self._size = (exif[40962], exif[40963]) + elif "exif" in self.info: + del self.info["exif"] + + self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))] + self.__frame = frame + + def tell(self): + return self.__frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + @staticmethod + def adopt(jpeg_instance, mpheader=None): + """ + Transform the instance of JpegImageFile into + an instance of MpoImageFile. + After the call, the JpegImageFile is extended + to be an MpoImageFile. + + This is essentially useful when opening a JPEG + file that reveals itself as an MPO, to avoid + double call to _open. + """ + jpeg_instance.__class__ = MpoImageFile + jpeg_instance._after_jpeg_open(mpheader) + return jpeg_instance + + +# --------------------------------------------------------------------- +# Registry stuff + +# Note that since MPO shares a factory with JPEG, we do not need to do a +# separate registration for it here. +# Image.register_open(MpoImageFile.format, +# JpegImagePlugin.jpeg_factory, _accept) +Image.register_save(MpoImageFile.format, _save) + +Image.register_extension(MpoImageFile.format, ".mpo") + +Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/venv/Lib/site-packages/PIL/MspImagePlugin.py b/venv/Lib/site-packages/PIL/MspImagePlugin.py new file mode 100644 index 0000000..c4d7ddb --- /dev/null +++ b/venv/Lib/site-packages/PIL/MspImagePlugin.py @@ -0,0 +1,194 @@ +# +# The Python Imaging Library. +# +# MSP file handling +# +# This is the format used by the Paint program in Windows 1 and 2. +# +# History: +# 95-09-05 fl Created +# 97-01-03 fl Read/write MSP images +# 17-02-21 es Fixed RLE interpretation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# Copyright (c) Eric Soroos 2017. +# +# See the README file for information on usage and redistribution. +# +# More info on this format: https://archive.org/details/gg243631 +# Page 313: +# Figure 205. Windows Paint Version 1: "DanM" Format +# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 +# +# See also: https://www.fileformat.info/format/mspaint/egff.htm + +import io +import struct + +from . import Image, ImageFile +from ._binary import i16le as i16 +from ._binary import o16le as o16 + +# +# read MSP files + + +def _accept(prefix): + return prefix[:4] in [b"DanM", b"LinS"] + + +## +# Image plugin for Windows MSP images. This plugin supports both +# uncompressed (Windows 1.0). + + +class MspImageFile(ImageFile.ImageFile): + + format = "MSP" + format_description = "Windows Paint" + + def _open(self): + + # Header + s = self.fp.read(32) + if not _accept(s): + raise SyntaxError("not an MSP file") + + # Header checksum + checksum = 0 + for i in range(0, 32, 2): + checksum = checksum ^ i16(s, i) + if checksum != 0: + raise SyntaxError("bad MSP checksum") + + self.mode = "1" + self._size = i16(s, 4), i16(s, 6) + + if s[:4] == b"DanM": + self.tile = [("raw", (0, 0) + self.size, 32, ("1", 0, 1))] + else: + self.tile = [("MSP", (0, 0) + self.size, 32, None)] + + +class MspDecoder(ImageFile.PyDecoder): + # The algo for the MSP decoder is from + # https://www.fileformat.info/format/mspaint/egff.htm + # cc-by-attribution -- That page references is taken from the + # Encyclopedia of Graphics File Formats and is licensed by + # O'Reilly under the Creative Common/Attribution license + # + # For RLE encoded files, the 32byte header is followed by a scan + # line map, encoded as one 16bit word of encoded byte length per + # line. + # + # NOTE: the encoded length of the line can be 0. This was not + # handled in the previous version of this encoder, and there's no + # mention of how to handle it in the documentation. From the few + # examples I've seen, I've assumed that it is a fill of the + # background color, in this case, white. + # + # + # Pseudocode of the decoder: + # Read a BYTE value as the RunType + # If the RunType value is zero + # Read next byte as the RunCount + # Read the next byte as the RunValue + # Write the RunValue byte RunCount times + # If the RunType value is non-zero + # Use this value as the RunCount + # Read and write the next RunCount bytes literally + # + # e.g.: + # 0x00 03 ff 05 00 01 02 03 04 + # would yield the bytes: + # 0xff ff ff 00 01 02 03 04 + # + # which are then interpreted as a bit packed mode '1' image + + _pulls_fd = True + + def decode(self, buffer): + + img = io.BytesIO() + blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8)) + try: + self.fd.seek(32) + rowmap = struct.unpack_from( + f"<{self.state.ysize}H", self.fd.read(self.state.ysize * 2) + ) + except struct.error as e: + raise OSError("Truncated MSP file in row map") from e + + for x, rowlen in enumerate(rowmap): + try: + if rowlen == 0: + img.write(blank_line) + continue + row = self.fd.read(rowlen) + if len(row) != rowlen: + raise OSError( + "Truncated MSP file, expected %d bytes on row %s", (rowlen, x) + ) + idx = 0 + while idx < rowlen: + runtype = row[idx] + idx += 1 + if runtype == 0: + (runcount, runval) = struct.unpack_from("Bc", row, idx) + img.write(runval * runcount) + idx += 2 + else: + runcount = runtype + img.write(row[idx : idx + runcount]) + idx += runcount + + except struct.error as e: + raise OSError(f"Corrupted MSP file in row {x}") from e + + self.set_as_raw(img.getvalue(), ("1", 0, 1)) + + return -1, 0 + + +Image.register_decoder("MSP", MspDecoder) + + +# +# write MSP files (uncompressed only) + + +def _save(im, fp, filename): + + if im.mode != "1": + raise OSError(f"cannot write mode {im.mode} as MSP") + + # create MSP header + header = [0] * 16 + + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 + header[2], header[3] = im.size + header[4], header[5] = 1, 1 + header[6], header[7] = 1, 1 + header[8], header[9] = im.size + + checksum = 0 + for h in header: + checksum = checksum ^ h + header[12] = checksum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 32, ("1", 0, 1))]) + + +# +# registry + +Image.register_open(MspImageFile.format, MspImageFile, _accept) +Image.register_save(MspImageFile.format, _save) + +Image.register_extension(MspImageFile.format, ".msp") diff --git a/venv/Lib/site-packages/PIL/PSDraw.py b/venv/Lib/site-packages/PIL/PSDraw.py new file mode 100644 index 0000000..743c35f --- /dev/null +++ b/venv/Lib/site-packages/PIL/PSDraw.py @@ -0,0 +1,235 @@ +# +# The Python Imaging Library +# $Id$ +# +# Simple PostScript graphics interface +# +# History: +# 1996-04-20 fl Created +# 1999-01-10 fl Added gsave/grestore to image method +# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) +# +# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import sys + +from . import EpsImagePlugin + +## +# Simple PostScript graphics interface. + + +class PSDraw: + """ + Sets up printing to the given file. If ``fp`` is omitted, + ``sys.stdout.buffer`` or ``sys.stdout`` is assumed. + """ + + def __init__(self, fp=None): + if not fp: + try: + fp = sys.stdout.buffer + except AttributeError: + fp = sys.stdout + self.fp = fp + + def begin_document(self, id=None): + """Set up printing of a document. (Write PostScript DSC header.)""" + # FIXME: incomplete + self.fp.write( + b"%!PS-Adobe-3.0\n" + b"save\n" + b"/showpage { } def\n" + b"%%EndComments\n" + b"%%BeginDocument\n" + ) + # self.fp.write(ERROR_PS) # debugging! + self.fp.write(EDROFF_PS) + self.fp.write(VDI_PS) + self.fp.write(b"%%EndProlog\n") + self.isofont = {} + + def end_document(self): + """Ends printing. (Write PostScript DSC footer.)""" + self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A PostScript font name + :param size: Size in points. + """ + font = bytes(font, "UTF-8") + if font not in self.isofont: + # reencode font + self.fp.write(b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font, font)) + self.isofont[font] = 1 + # rough + self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font)) + + def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + PostScript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ + self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1)) + + def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ + self.fp.write(b"%d %d M %d %d 0 Vr\n" % box) + + def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text = bytes(text, "UTF-8") + text = b"\\(".join(text.split(b"(")) + text = b"\\)".join(text.split(b")")) + xy += (text,) + self.fp.write(b"%d %d M (%s) S\n" % xy) + + def image(self, box, im, dpi=None): + """Draw a PIL image, centered in the given box.""" + # default resolution depends on mode + if not dpi: + if im.mode == "1": + dpi = 200 # fax + else: + dpi = 100 # greyscale + # image size (on paper) + x = im.size[0] * 72 / dpi + y = im.size[1] * 72 / dpi + # max allowed size + xmax = float(box[2] - box[0]) + ymax = float(box[3] - box[1]) + if x > xmax: + y = y * xmax / x + x = xmax + if y > ymax: + x = x * ymax / y + y = ymax + dx = (xmax - x) / 2 + box[0] + dy = (ymax - y) / 2 + box[1] + self.fp.write(b"gsave\n%f %f translate\n" % (dx, dy)) + if (x, y) != im.size: + # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) + sx = x / im.size[0] + sy = y / im.size[1] + self.fp.write(b"%f %f scale\n" % (sx, sy)) + EpsImagePlugin._save(im, self.fp, None, 0) + self.fp.write(b"\ngrestore\n") + + +# -------------------------------------------------------------------- +# PostScript driver + +# +# EDROFF.PS -- PostScript driver for Edroff 2 +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + + +EDROFF_PS = b"""\ +/S { show } bind def +/P { moveto show } bind def +/M { moveto } bind def +/X { 0 rmoveto } bind def +/Y { 0 exch rmoveto } bind def +/E { findfont + dup maxlength dict begin + { + 1 index /FID ne { def } { pop pop } ifelse + } forall + /Encoding exch def + dup /FontName exch def + currentdict end definefont pop +} bind def +/F { findfont exch scalefont dup setfont + [ exch /setfont cvx ] cvx bind def +} bind def +""" + +# +# VDI.PS -- PostScript driver for VDI meta commands +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +VDI_PS = b"""\ +/Vm { moveto } bind def +/Va { newpath arcn stroke } bind def +/Vl { moveto lineto stroke } bind def +/Vc { newpath 0 360 arc closepath } bind def +/Vr { exch dup 0 rlineto + exch dup neg 0 exch rlineto + exch neg 0 rlineto + 0 exch rlineto + 100 div setgray fill 0 setgray } bind def +/Tm matrix def +/Ve { Tm currentmatrix pop + translate scale newpath 0 0 .5 0 360 arc closepath + Tm setmatrix +} bind def +/Vf { currentgray exch setgray fill setgray } bind def +""" + +# +# ERROR.PS -- Error handler +# +# History: +# 89-11-21 fl: created (pslist 1.10) +# + +ERROR_PS = b"""\ +/landscape false def +/errorBUF 200 string def +/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def +errordict begin /handleerror { + initmatrix /Courier findfont 10 scalefont setfont + newpath 72 720 moveto $error begin /newerror false def + (PostScript Error) show errorNL errorNL + (Error: ) show + /errorname load errorBUF cvs show errorNL errorNL + (Command: ) show + /command load dup type /stringtype ne { errorBUF cvs } if show + errorNL errorNL + (VMstatus: ) show + vmstatus errorBUF cvs show ( bytes available, ) show + errorBUF cvs show ( bytes used at level ) show + errorBUF cvs show errorNL errorNL + (Operand stargck: ) show errorNL /ostargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall errorNL + (Execution stargck: ) show errorNL /estargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall + end showpage +} def end +""" diff --git a/venv/Lib/site-packages/PIL/PaletteFile.py b/venv/Lib/site-packages/PIL/PaletteFile.py new file mode 100644 index 0000000..6ccaa1f --- /dev/null +++ b/venv/Lib/site-packages/PIL/PaletteFile.py @@ -0,0 +1,53 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read simple, teragon-style palette files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from ._binary import o8 + + +class PaletteFile: + """File handler for Teragon-style palette files.""" + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [(i, i, i) for i in range(256)] + + while True: + + s = fp.readline() + + if not s: + break + if s[0:1] == b"#": + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = [int(x) for x in s.split()] + try: + [i, r, g, b] = v + except ValueError: + [i, r] = v + g = b = r + + if 0 <= i <= 255: + self.palette[i] = o8(r) + o8(g) + o8(b) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/venv/Lib/site-packages/PIL/PalmImagePlugin.py b/venv/Lib/site-packages/PIL/PalmImagePlugin.py new file mode 100644 index 0000000..700f10e --- /dev/null +++ b/venv/Lib/site-packages/PIL/PalmImagePlugin.py @@ -0,0 +1,227 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## + +from . import Image, ImageFile +from ._binary import o8 +from ._binary import o16be as o16b + +# fmt: off +_Palm8BitColormapValues = ( + (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), + (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), + (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), + (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), + (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), + (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), + (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), + (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), + (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), + (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), + (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), + (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), + (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), + (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), + (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), + (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), + (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), + (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), + (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), + (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), + (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), + (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), + (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), + (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), + (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), + (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), + (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), + (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), + (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), + (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), + (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), + (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), + (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), + (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), + (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), + (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), + (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), + (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), + (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), + (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), + (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), + (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), + (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), + (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), + (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), + (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), + (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), + (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), + (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), + (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), + (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), + (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), + (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), + (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), + (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), + (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), + (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), + (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) +# fmt: on + + +# so build a prototype image to be used for palette resampling +def build_prototype_image(): + image = Image.new("L", (1, len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) + palettedata = () + for colormapValue in _Palm8BitColormapValues: + palettedata += colormapValue + palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) + image.putpalette(palettedata) + return image + + +Palm8BitColormapImage = build_prototype_image() + +# OK, we now have in Palm8BitColormapImage, +# a "P"-mode image with the right palette +# +# -------------------------------------------------------------------- + +_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000} + +_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} + + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + + +def _save(im, fp, filename): + + if im.mode == "P": + + # we assume this is a color Palm image with the standard colormap, + # unless the "info" dict has a "custom-colormap" field + + rawmode = "P" + bpp = 8 + version = 1 + + elif im.mode == "L": + if im.encoderinfo.get("bpp") in (1, 2, 4): + # this is 8-bit grayscale, so we shift it to get the high-order bits, + # and invert it because + # Palm does greyscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + im = im.point( + lambda x, shift=8 - bpp, maxval=(1 << bpp) - 1: maxval - (x >> shift) + ) + elif im.info.get("bpp") in (1, 2, 4): + # here we assume that even though the inherent mode is 8-bit grayscale, + # only the lower bpp bits are significant. + # We invert them to match the Palm. + bpp = im.info["bpp"] + im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval)) + else: + raise OSError(f"cannot write mode {im.mode} as Palm") + + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "1": + + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + + raise OSError(f"cannot write mode {im.mode} as Palm") + + # + # make sure image data is available + im.load() + + # write header + + cols = im.size[0] + rows = im.size[1] + + rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2 + transparent_index = 0 + compression_type = _COMPRESSION_TYPES["none"] + + flags = 0 + if im.mode == "P" and "custom-colormap" in im.info: + flags = flags & _FLAGS["custom-colormap"] + colormapsize = 4 * 256 + 2 + colormapmode = im.palette.mode + colormap = im.getdata().getpalette() + else: + colormapsize = 0 + + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 + else: + offset = 0 + + fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) + fp.write(o8(bpp)) + fp.write(o8(version)) + fp.write(o16b(offset)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) + fp.write(o16b(0)) # reserved by Palm + + # now write colormap if necessary + + if colormapsize > 0: + fp.write(o16b(256)) + for i in range(256): + fp.write(o8(i)) + if colormapmode == "RGB": + fp.write( + o8(colormap[3 * i]) + + o8(colormap[3 * i + 1]) + + o8(colormap[3 * i + 2]) + ) + elif colormapmode == "RGBA": + fp.write( + o8(colormap[4 * i]) + + o8(colormap[4 * i + 1]) + + o8(colormap[4 * i + 2]) + ) + + # now convert data to raw form + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))]) + + if hasattr(fp, "flush"): + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/venv/Lib/site-packages/PIL/PcdImagePlugin.py b/venv/Lib/site-packages/PIL/PcdImagePlugin.py new file mode 100644 index 0000000..38caf5c --- /dev/null +++ b/venv/Lib/site-packages/PIL/PcdImagePlugin.py @@ -0,0 +1,63 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCD file handling +# +# History: +# 96-05-10 fl Created +# 96-05-27 fl Added draft mode (128x192, 256x384) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile + +## +# Image plugin for PhotoCD images. This plugin only reads the 768x512 +# image from the file; higher resolutions are encoded in a proprietary +# encoding. + + +class PcdImageFile(ImageFile.ImageFile): + + format = "PCD" + format_description = "Kodak PhotoCD" + + def _open(self): + + # rough + self.fp.seek(2048) + s = self.fp.read(2048) + + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") + + orientation = s[1538] & 3 + self.tile_post_rotate = None + if orientation == 1: + self.tile_post_rotate = 90 + elif orientation == 3: + self.tile_post_rotate = -90 + + self.mode = "RGB" + self._size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [("pcd", (0, 0) + self.size, 96 * 2048, None)] + + def load_end(self): + if self.tile_post_rotate: + # Handle rotated PCDs + self.im = self.im.rotate(self.tile_post_rotate) + self._size = self.im.size + + +# +# registry + +Image.register_open(PcdImageFile.format, PcdImageFile) + +Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/venv/Lib/site-packages/PIL/PcfFontFile.py b/venv/Lib/site-packages/PIL/PcfFontFile.py new file mode 100644 index 0000000..6a4eb22 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PcfFontFile.py @@ -0,0 +1,248 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# portable compiled font file parser +# +# history: +# 1997-08-19 fl created +# 2003-09-13 fl fixed loading of unicode fonts +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import FontFile, Image +from ._binary import i8 +from ._binary import i16be as b16 +from ._binary import i16le as l16 +from ._binary import i32be as b32 +from ._binary import i32le as l32 + +# -------------------------------------------------------------------- +# declarations + +PCF_MAGIC = 0x70636601 # "\x01fcp" + +PCF_PROPERTIES = 1 << 0 +PCF_ACCELERATORS = 1 << 1 +PCF_METRICS = 1 << 2 +PCF_BITMAPS = 1 << 3 +PCF_INK_METRICS = 1 << 4 +PCF_BDF_ENCODINGS = 1 << 5 +PCF_SWIDTHS = 1 << 6 +PCF_GLYPH_NAMES = 1 << 7 +PCF_BDF_ACCELERATORS = 1 << 8 + +BYTES_PER_ROW = [ + lambda bits: ((bits + 7) >> 3), + lambda bits: ((bits + 15) >> 3) & ~1, + lambda bits: ((bits + 31) >> 3) & ~3, + lambda bits: ((bits + 63) >> 3) & ~7, +] + + +def sz(s, o): + return s[o : s.index(b"\0", o)] + + +class PcfFontFile(FontFile.FontFile): + """Font file plugin for the X11 PCF format.""" + + name = "name" + + def __init__(self, fp, charset_encoding="iso8859-1"): + + self.charset_encoding = charset_encoding + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + raise SyntaxError("not a PCF file") + + super().__init__() + + count = l32(fp.read(4)) + self.toc = {} + for i in range(count): + type = l32(fp.read(4)) + self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) + + self.fp = fp + + self.info = self._load_properties() + + metrics = self._load_metrics() + bitmaps = self._load_bitmaps(metrics) + encoding = self._load_encoding() + + # + # create glyph structure + + for ch in range(256): + ix = encoding[ch] + if ix is not None: + x, y, l, r, w, a, d, f = metrics[ix] + glyph = (w, 0), (l, d - y, x + l, d), (0, 0, x, y), bitmaps[ix] + self.glyph[ch] = glyph + + def _getformat(self, tag): + + format, size, offset = self.toc[tag] + + fp = self.fp + fp.seek(offset) + + format = l32(fp.read(4)) + + if format & 4: + i16, i32 = b16, b32 + else: + i16, i32 = l16, l32 + + return fp, format, i16, i32 + + def _load_properties(self): + + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [] + for i in range(nprops): + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) + if nprops & 3: + fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + k = sz(data, k) + if s: + v = sz(data, v) + properties[k] = v + + return properties + + def _load_metrics(self): + + # + # font metrics + + metrics = [] + + fp, format, i16, i32 = self._getformat(PCF_METRICS) + + append = metrics.append + + if (format & 0xFF00) == 0x100: + + # "compressed" metrics + for i in range(i16(fp.read(2))): + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 + xsize = right - left + ysize = ascent + descent + append((xsize, ysize, left, right, width, ascent, descent, 0)) + + else: + + # "jumbo" metrics + for i in range(i32(fp.read(4))): + left = i16(fp.read(2)) + right = i16(fp.read(2)) + width = i16(fp.read(2)) + ascent = i16(fp.read(2)) + descent = i16(fp.read(2)) + attributes = i16(fp.read(2)) + xsize = right - left + ysize = ascent + descent + append((xsize, ysize, left, right, width, ascent, descent, attributes)) + + return metrics + + def _load_bitmaps(self, metrics): + + # + # bitmap data + + bitmaps = [] + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + raise OSError("Wrong number of bitmaps") + + offsets = [] + for i in range(nbitmaps): + offsets.append(i32(fp.read(4))) + + bitmapSizes = [] + for i in range(4): + bitmapSizes.append(i32(fp.read(4))) + + # byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmapSizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + for i in range(nbitmaps): + x, y, l, r, w, a, d, f = metrics[i] + b, e = offsets[i], offsets[i + 1] + bitmaps.append(Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x))) + + return bitmaps + + def _load_encoding(self): + + # map character code to bitmap index + encoding = [None] * 256 + + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) + firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + + i16(fp.read(2)) # default + + nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + + encodingOffsets = [i16(fp.read(2)) for _ in range(nencoding)] + + for i in range(firstCol, len(encoding)): + try: + encodingOffset = encodingOffsets[ + ord(bytearray([i]).decode(self.charset_encoding)) + ] + if encodingOffset != 0xFFFF: + encoding[i] = encodingOffset + except UnicodeDecodeError: + # character is not supported in selected encoding + pass + + return encoding diff --git a/venv/Lib/site-packages/PIL/PcxImagePlugin.py b/venv/Lib/site-packages/PIL/PcxImagePlugin.py new file mode 100644 index 0000000..d2e166b --- /dev/null +++ b/venv/Lib/site-packages/PIL/PcxImagePlugin.py @@ -0,0 +1,218 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCX file handling +# +# This format was originally used by ZSoft's popular PaintBrush +# program for the IBM PC. It is also supported by many MS-DOS and +# Windows applications, including the Windows PaintBrush program in +# Windows 3. +# +# history: +# 1995-09-01 fl Created +# 1996-05-20 fl Fixed RGB support +# 1997-01-03 fl Fixed 2-bit and 4-bit support +# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) +# 1999-02-07 fl Added write support +# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust +# 2002-07-30 fl Seek from to current position, not beginning of file +# 2003-06-03 fl Extract DPI settings (info["dpi"]) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import io +import logging + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +logger = logging.getLogger(__name__) + + +def _accept(prefix): + return prefix[0] == 10 and prefix[1] in [0, 2, 3, 5] + + +## +# Image plugin for Paintbrush images. + + +class PcxImageFile(ImageFile.ImageFile): + + format = "PCX" + format_description = "Paintbrush" + + def _open(self): + + # header + s = self.fp.read(128) + if not _accept(s): + raise SyntaxError("not a PCX file") + + # image + bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1 + if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: + raise SyntaxError("bad PCX image size") + logger.debug("BBox: %s %s %s %s", *bbox) + + # format + version = s[1] + bits = s[3] + planes = s[65] + provided_stride = i16(s, 66) + logger.debug( + "PCX version %s, bits %s, planes %s, stride %s", + version, + bits, + planes, + provided_stride, + ) + + self.info["dpi"] = i16(s, 12), i16(s, 14) + + if bits == 1 and planes == 1: + mode = rawmode = "1" + + elif bits == 1 and planes in (2, 4): + mode = "P" + rawmode = "P;%dL" % planes + self.palette = ImagePalette.raw("RGB", s[16:64]) + + elif version == 5 and bits == 8 and planes == 1: + mode = rawmode = "L" + # FIXME: hey, this doesn't work with the incremental loader !!! + self.fp.seek(-769, io.SEEK_END) + s = self.fp.read(769) + if len(s) == 769 and s[0] == 12: + # check if the palette is linear greyscale + for i in range(256): + if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3: + mode = rawmode = "P" + break + if mode == "P": + self.palette = ImagePalette.raw("RGB", s[1:]) + self.fp.seek(128) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + raise OSError("unknown PCX mode") + + self.mode = mode + self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] + + # Don't trust the passed in stride. + # Calculate the approximate position for ourselves. + # CVE-2020-35653 + stride = (self._size[0] * bits + 7) // 8 + + # While the specification states that this must be even, + # not all images follow this + if provided_stride != stride: + stride += stride % 2 + + bbox = (0, 0) + self.size + logger.debug("size: %sx%s", *self.size) + + self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] + + +# -------------------------------------------------------------------- +# save PCX files + + +SAVE = { + # mode: (version, bits, planes, raw mode) + "1": (2, 1, 1, "1"), + "L": (5, 8, 1, "L"), + "P": (5, 8, 1, "P"), + "RGB": (5, 8, 3, "RGB;L"), +} + + +def _save(im, fp, filename): + + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError as e: + raise ValueError(f"Cannot save {im.mode} images as PCX") from e + + # bytes per plane + stride = (im.size[0] * bits + 7) // 8 + # stride should be even + stride += stride % 2 + # Stride needs to be kept in sync with the PcxEncode.c version. + # Ideally it should be passed in in the state, but the bytes value + # gets overwritten. + + logger.debug( + "PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d", + im.size[0], + bits, + stride, + ) + + # under windows, we could determine the current screen size with + # "Image.core.display_mode()[1]", but I think that's overkill... + + screen = im.size + + dpi = 100, 100 + + # PCX header + fp.write( + o8(10) + + o8(version) + + o8(1) + + o8(bits) + + o16(0) + + o16(0) + + o16(im.size[0] - 1) + + o16(im.size[1] - 1) + + o16(dpi[0]) + + o16(dpi[1]) + + b"\0" * 24 + + b"\xFF" * 24 + + b"\0" + + o8(planes) + + o16(stride) + + o16(1) + + o16(screen[0]) + + o16(screen[1]) + + b"\0" * 54 + ) + + assert fp.tell() == 128 + + ImageFile._save(im, fp, [("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))]) + + if im.mode == "P": + # colour palette + fp.write(o8(12)) + fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + elif im.mode == "L": + # greyscale palette + fp.write(o8(12)) + for i in range(256): + fp.write(o8(i) * 3) + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PcxImageFile.format, PcxImageFile, _accept) +Image.register_save(PcxImageFile.format, _save) + +Image.register_extension(PcxImageFile.format, ".pcx") + +Image.register_mime(PcxImageFile.format, "image/x-pcx") diff --git a/venv/Lib/site-packages/PIL/PdfImagePlugin.py b/venv/Lib/site-packages/PIL/PdfImagePlugin.py new file mode 100644 index 0000000..544035c --- /dev/null +++ b/venv/Lib/site-packages/PIL/PdfImagePlugin.py @@ -0,0 +1,239 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PDF (Acrobat) file handling +# +# History: +# 1996-07-16 fl Created +# 1997-01-18 fl Fixed header +# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. +# 2004-02-24 fl Fixes for 1 and P images. +# +# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996-1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# Image plugin for PDF images (output only). +## + +import io +import os +import time + +from . import Image, ImageFile, ImageSequence, PdfParser, __version__ + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +## +# (Internal) Image save plugin for the PDF format. + + +def _save(im, fp, filename, save_all=False): + is_appending = im.encoderinfo.get("append", False) + if is_appending: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b") + else: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b") + + resolution = im.encoderinfo.get("resolution", 72.0) + + info = { + "title": None + if is_appending + else os.path.splitext(os.path.basename(filename))[0], + "author": None, + "subject": None, + "keywords": None, + "creator": None, + "producer": None, + "creationDate": None if is_appending else time.gmtime(), + "modDate": None if is_appending else time.gmtime(), + } + for k, default in info.items(): + v = im.encoderinfo.get(k) if k in im.encoderinfo else default + if v: + existing_pdf.info[k[0].upper() + k[1:]] = v + + # + # make sure image data is available + im.load() + + existing_pdf.start_writing() + existing_pdf.write_header() + existing_pdf.write_comment(f"created by Pillow {__version__} PDF driver") + + # + # pages + ims = [im] + if save_all: + append_images = im.encoderinfo.get("append_images", []) + for append_im in append_images: + append_im.encoderinfo = im.encoderinfo.copy() + ims.append(append_im) + numberOfPages = 0 + image_refs = [] + page_refs = [] + contents_refs = [] + for im in ims: + im_numberOfPages = 1 + if save_all: + try: + im_numberOfPages = im.n_frames + except AttributeError: + # Image format does not have n_frames. + # It is a single frame image + pass + numberOfPages += im_numberOfPages + for i in range(im_numberOfPages): + image_refs.append(existing_pdf.next_object_id(0)) + page_refs.append(existing_pdf.next_object_id(0)) + contents_refs.append(existing_pdf.next_object_id(0)) + existing_pdf.pages.append(page_refs[-1]) + + # + # catalog and list of pages + existing_pdf.write_catalog() + + pageNumber = 0 + for imSequence in ims: + im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence] + for im in im_pages: + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode + # (packbits) or LZWDecode (tiff/lzw compression). Note that + # PDF 1.2 also supports Flatedecode (zip compression). + + bits = 8 + params = None + decode = None + + if im.mode == "1": + filter = "DCTDecode" + colorspace = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + elif im.mode == "L": + filter = "DCTDecode" + # params = f"<< /Predictor 15 /Columns {width-2} >>" + colorspace = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + elif im.mode == "P": + filter = "ASCIIHexDecode" + palette = im.getpalette() + colorspace = [ + PdfParser.PdfName("Indexed"), + PdfParser.PdfName("DeviceRGB"), + 255, + PdfParser.PdfBinary(palette), + ] + procset = "ImageI" # indexed color + elif im.mode == "RGB": + filter = "DCTDecode" + colorspace = PdfParser.PdfName("DeviceRGB") + procset = "ImageC" # color images + elif im.mode == "CMYK": + filter = "DCTDecode" + colorspace = PdfParser.PdfName("DeviceCMYK") + procset = "ImageC" # color images + decode = [1, 0, 1, 0, 1, 0, 1, 0] + else: + raise ValueError(f"cannot save mode {im.mode}") + + # + # image + + op = io.BytesIO() + + if filter == "ASCIIHexDecode": + ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)]) + elif filter == "DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif filter == "FlateDecode": + ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)]) + elif filter == "RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)]) + else: + raise ValueError(f"unsupported PDF filter ({filter})") + + # + # Get image characteristics + + width, height = im.size + + existing_pdf.write_obj( + image_refs[pageNumber], + stream=op.getvalue(), + Type=PdfParser.PdfName("XObject"), + Subtype=PdfParser.PdfName("Image"), + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Filter=PdfParser.PdfName(filter), + BitsPerComponent=bits, + Decode=decode, + DecodeParams=params, + ColorSpace=colorspace, + ) + + # + # page + + existing_pdf.write_page( + page_refs[pageNumber], + Resources=PdfParser.PdfDict( + ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], + XObject=PdfParser.PdfDict(image=image_refs[pageNumber]), + ), + MediaBox=[ + 0, + 0, + width * 72.0 / resolution, + height * 72.0 / resolution, + ], + Contents=contents_refs[pageNumber], + ) + + # + # page contents + + page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % ( + width * 72.0 / resolution, + height * 72.0 / resolution, + ) + + existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents) + + pageNumber += 1 + + # + # trailer + existing_pdf.write_xref_and_trailer() + if hasattr(fp, "flush"): + fp.flush() + existing_pdf.close() + + +# +# -------------------------------------------------------------------- + + +Image.register_save("PDF", _save) +Image.register_save_all("PDF", _save_all) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/venv/Lib/site-packages/PIL/PdfParser.py b/venv/Lib/site-packages/PIL/PdfParser.py new file mode 100644 index 0000000..9aa0fd6 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PdfParser.py @@ -0,0 +1,998 @@ +import calendar +import codecs +import collections +import mmap +import os +import re +import time +import zlib + + +# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set +# on page 656 +def encode_text(s): + return codecs.BOM_UTF16_BE + s.encode("utf_16_be") + + +PDFDocEncoding = { + 0x16: "\u0017", + 0x18: "\u02D8", + 0x19: "\u02C7", + 0x1A: "\u02C6", + 0x1B: "\u02D9", + 0x1C: "\u02DD", + 0x1D: "\u02DB", + 0x1E: "\u02DA", + 0x1F: "\u02DC", + 0x80: "\u2022", + 0x81: "\u2020", + 0x82: "\u2021", + 0x83: "\u2026", + 0x84: "\u2014", + 0x85: "\u2013", + 0x86: "\u0192", + 0x87: "\u2044", + 0x88: "\u2039", + 0x89: "\u203A", + 0x8A: "\u2212", + 0x8B: "\u2030", + 0x8C: "\u201E", + 0x8D: "\u201C", + 0x8E: "\u201D", + 0x8F: "\u2018", + 0x90: "\u2019", + 0x91: "\u201A", + 0x92: "\u2122", + 0x93: "\uFB01", + 0x94: "\uFB02", + 0x95: "\u0141", + 0x96: "\u0152", + 0x97: "\u0160", + 0x98: "\u0178", + 0x99: "\u017D", + 0x9A: "\u0131", + 0x9B: "\u0142", + 0x9C: "\u0153", + 0x9D: "\u0161", + 0x9E: "\u017E", + 0xA0: "\u20AC", +} + + +def decode_text(b): + if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: + return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") + else: + return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) + + +class PdfFormatError(RuntimeError): + """An error that probably indicates a syntactic or semantic error in the + PDF file structure""" + + pass + + +def check_format_condition(condition, error_message): + if not condition: + raise PdfFormatError(error_message) + + +class IndirectReference( + collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"]) +): + def __str__(self): + return "%s %s R" % self + + def __bytes__(self): + return self.__str__().encode("us-ascii") + + def __eq__(self, other): + return ( + other.__class__ is self.__class__ + and other.object_id == self.object_id + and other.generation == self.generation + ) + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash((self.object_id, self.generation)) + + +class IndirectObjectDef(IndirectReference): + def __str__(self): + return "%s %s obj" % self + + +class XrefTable: + def __init__(self): + self.existing_entries = {} # object ID => (offset, generation) + self.new_entries = {} # object ID => (offset, generation) + self.deleted_entries = {0: 65536} # object ID => generation + self.reading_finished = False + + def __setitem__(self, key, value): + if self.reading_finished: + self.new_entries[key] = value + else: + self.existing_entries[key] = value + if key in self.deleted_entries: + del self.deleted_entries[key] + + def __getitem__(self, key): + try: + return self.new_entries[key] + except KeyError: + return self.existing_entries[key] + + def __delitem__(self, key): + if key in self.new_entries: + generation = self.new_entries[key][1] + 1 + del self.new_entries[key] + self.deleted_entries[key] = generation + elif key in self.existing_entries: + generation = self.existing_entries[key][1] + 1 + self.deleted_entries[key] = generation + elif key in self.deleted_entries: + generation = self.deleted_entries[key] + else: + raise IndexError( + "object ID " + str(key) + " cannot be deleted because it doesn't exist" + ) + + def __contains__(self, key): + return key in self.existing_entries or key in self.new_entries + + def __len__(self): + return len( + set(self.existing_entries.keys()) + | set(self.new_entries.keys()) + | set(self.deleted_entries.keys()) + ) + + def keys(self): + return ( + set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) + ) | set(self.new_entries.keys()) + + def write(self, f): + keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) + deleted_keys = sorted(set(self.deleted_entries.keys())) + startxref = f.tell() + f.write(b"xref\n") + while keys: + # find a contiguous sequence of object IDs + prev = None + for index, key in enumerate(keys): + if prev is None or prev + 1 == key: + prev = key + else: + contiguous_keys = keys[:index] + keys = keys[index:] + break + else: + contiguous_keys = keys + keys = None + f.write(b"%d %d\n" % (contiguous_keys[0], len(contiguous_keys))) + for object_id in contiguous_keys: + if object_id in self.new_entries: + f.write(b"%010d %05d n \n" % self.new_entries[object_id]) + else: + this_deleted_object_id = deleted_keys.pop(0) + check_format_condition( + object_id == this_deleted_object_id, + f"expected the next deleted object ID to be {object_id}, " + f"instead found {this_deleted_object_id}", + ) + try: + next_in_linked_list = deleted_keys[0] + except IndexError: + next_in_linked_list = 0 + f.write( + b"%010d %05d f \n" + % (next_in_linked_list, self.deleted_entries[object_id]) + ) + return startxref + + +class PdfName: + def __init__(self, name): + if isinstance(name, PdfName): + self.name = name.name + elif isinstance(name, bytes): + self.name = name + else: + self.name = name.encode("us-ascii") + + def name_as_str(self): + return self.name.decode("us-ascii") + + def __eq__(self, other): + return ( + isinstance(other, PdfName) and other.name == self.name + ) or other == self.name + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return f"PdfName({repr(self.name)})" + + @classmethod + def from_pdf_stream(cls, data): + return cls(PdfParser.interpret_name(data)) + + allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} + + def __bytes__(self): + result = bytearray(b"/") + for b in self.name: + if b in self.allowed_chars: + result.append(b) + else: + result.extend(b"#%02X" % b) + return bytes(result) + + +class PdfArray(list): + def __bytes__(self): + return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" + + +class PdfDict(collections.UserDict): + def __setattr__(self, key, value): + if key == "data": + collections.UserDict.__setattr__(self, key, value) + else: + self[key.encode("us-ascii")] = value + + def __getattr__(self, key): + try: + value = self[key.encode("us-ascii")] + except KeyError as e: + raise AttributeError(key) from e + if isinstance(value, bytes): + value = decode_text(value) + if key.endswith("Date"): + if value.startswith("D:"): + value = value[2:] + + relationship = "Z" + if len(value) > 17: + relationship = value[14] + offset = int(value[15:17]) * 60 + if len(value) > 20: + offset += int(value[18:20]) + + format = "%Y%m%d%H%M%S"[: len(value) - 2] + value = time.strptime(value[: len(format) + 2], format) + if relationship in ["+", "-"]: + offset *= 60 + if relationship == "+": + offset *= -1 + value = time.gmtime(calendar.timegm(value) + offset) + return value + + def __bytes__(self): + out = bytearray(b"<<") + for key, value in self.items(): + if value is None: + continue + value = pdf_repr(value) + out.extend(b"\n") + out.extend(bytes(PdfName(key))) + out.extend(b" ") + out.extend(value) + out.extend(b"\n>>") + return bytes(out) + + +class PdfBinary: + def __init__(self, data): + self.data = data + + def __bytes__(self): + return b"<%s>" % b"".join(b"%02X" % b for b in self.data) + + +class PdfStream: + def __init__(self, dictionary, buf): + self.dictionary = dictionary + self.buf = buf + + def decode(self): + try: + filter = self.dictionary.Filter + except AttributeError: + return self.buf + if filter == b"FlateDecode": + try: + expected_length = self.dictionary.DL + except AttributeError: + expected_length = self.dictionary.Length + return zlib.decompress(self.buf, bufsize=int(expected_length)) + else: + raise NotImplementedError( + f"stream filter {repr(self.dictionary.Filter)} unknown/unsupported" + ) + + +def pdf_repr(x): + if x is True: + return b"true" + elif x is False: + return b"false" + elif x is None: + return b"null" + elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): + return bytes(x) + elif isinstance(x, int): + return str(x).encode("us-ascii") + elif isinstance(x, float): + return str(x).encode("us-ascii") + elif isinstance(x, time.struct_time): + return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")" + elif isinstance(x, dict): + return bytes(PdfDict(x)) + elif isinstance(x, list): + return bytes(PdfArray(x)) + elif isinstance(x, str): + return pdf_repr(encode_text(x)) + elif isinstance(x, bytes): + # XXX escape more chars? handle binary garbage + x = x.replace(b"\\", b"\\\\") + x = x.replace(b"(", b"\\(") + x = x.replace(b")", b"\\)") + return b"(" + x + b")" + else: + return bytes(x) + + +class PdfParser: + """Based on + https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf + Supports PDF up to 1.4 + """ + + def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): + if buf and f: + raise RuntimeError("specify buf or f or filename, but not both buf and f") + self.filename = filename + self.buf = buf + self.f = f + self.start_offset = start_offset + self.should_close_buf = False + self.should_close_file = False + if filename is not None and f is None: + self.f = f = open(filename, mode) + self.should_close_file = True + if f is not None: + self.buf = buf = self.get_buf_from_file(f) + self.should_close_buf = True + if not filename and hasattr(f, "name"): + self.filename = f.name + self.cached_objects = {} + if buf: + self.read_pdf_info() + else: + self.file_size_total = self.file_size_this = 0 + self.root = PdfDict() + self.root_ref = None + self.info = PdfDict() + self.info_ref = None + self.page_tree_root = {} + self.pages = [] + self.orig_pages = [] + self.pages_ref = None + self.last_xref_section_offset = None + self.trailer_dict = {} + self.xref_table = XrefTable() + self.xref_table.reading_finished = True + if f: + self.seek_end() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + return False # do not suppress exceptions + + def start_writing(self): + self.close_buf() + self.seek_end() + + def close_buf(self): + try: + self.buf.close() + except AttributeError: + pass + self.buf = None + + def close(self): + if self.should_close_buf: + self.close_buf() + if self.f is not None and self.should_close_file: + self.f.close() + self.f = None + + def seek_end(self): + self.f.seek(0, os.SEEK_END) + + def write_header(self): + self.f.write(b"%PDF-1.4\n") + + def write_comment(self, s): + self.f.write(f"% {s}\n".encode()) + + def write_catalog(self): + self.del_root() + self.root_ref = self.next_object_id(self.f.tell()) + self.pages_ref = self.next_object_id(0) + self.rewrite_pages() + self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref) + self.write_obj( + self.pages_ref, + Type=PdfName(b"Pages"), + Count=len(self.pages), + Kids=self.pages, + ) + return self.root_ref + + def rewrite_pages(self): + pages_tree_nodes_to_delete = [] + for i, page_ref in enumerate(self.orig_pages): + page_info = self.cached_objects[page_ref] + del self.xref_table[page_ref.object_id] + pages_tree_nodes_to_delete.append(page_info[PdfName(b"Parent")]) + if page_ref not in self.pages: + # the page has been deleted + continue + # make dict keys into strings for passing to write_page + stringified_page_info = {} + for key, value in page_info.items(): + # key should be a PdfName + stringified_page_info[key.name_as_str()] = value + stringified_page_info["Parent"] = self.pages_ref + new_page_ref = self.write_page(None, **stringified_page_info) + for j, cur_page_ref in enumerate(self.pages): + if cur_page_ref == page_ref: + # replace the page reference with the new one + self.pages[j] = new_page_ref + # delete redundant Pages tree nodes from xref table + for pages_tree_node_ref in pages_tree_nodes_to_delete: + while pages_tree_node_ref: + pages_tree_node = self.cached_objects[pages_tree_node_ref] + if pages_tree_node_ref.object_id in self.xref_table: + del self.xref_table[pages_tree_node_ref.object_id] + pages_tree_node_ref = pages_tree_node.get(b"Parent", None) + self.orig_pages = [] + + def write_xref_and_trailer(self, new_root_ref=None): + if new_root_ref: + self.del_root() + self.root_ref = new_root_ref + if self.info: + self.info_ref = self.write_obj(None, self.info) + start_xref = self.xref_table.write(self.f) + num_entries = len(self.xref_table) + trailer_dict = {b"Root": self.root_ref, b"Size": num_entries} + if self.last_xref_section_offset is not None: + trailer_dict[b"Prev"] = self.last_xref_section_offset + if self.info: + trailer_dict[b"Info"] = self.info_ref + self.last_xref_section_offset = start_xref + self.f.write( + b"trailer\n" + + bytes(PdfDict(trailer_dict)) + + b"\nstartxref\n%d\n%%%%EOF" % start_xref + ) + + def write_page(self, ref, *objs, **dict_obj): + if isinstance(ref, int): + ref = self.pages[ref] + if "Type" not in dict_obj: + dict_obj["Type"] = PdfName(b"Page") + if "Parent" not in dict_obj: + dict_obj["Parent"] = self.pages_ref + return self.write_obj(ref, *objs, **dict_obj) + + def write_obj(self, ref, *objs, **dict_obj): + f = self.f + if ref is None: + ref = self.next_object_id(f.tell()) + else: + self.xref_table[ref.object_id] = (f.tell(), ref.generation) + f.write(bytes(IndirectObjectDef(*ref))) + stream = dict_obj.pop("stream", None) + if stream is not None: + dict_obj["Length"] = len(stream) + if dict_obj: + f.write(pdf_repr(dict_obj)) + for obj in objs: + f.write(pdf_repr(obj)) + if stream is not None: + f.write(b"stream\n") + f.write(stream) + f.write(b"\nendstream\n") + f.write(b"endobj\n") + return ref + + def del_root(self): + if self.root_ref is None: + return + del self.xref_table[self.root_ref.object_id] + del self.xref_table[self.root[b"Pages"].object_id] + + @staticmethod + def get_buf_from_file(f): + if hasattr(f, "getbuffer"): + return f.getbuffer() + elif hasattr(f, "getvalue"): + return f.getvalue() + else: + try: + return mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) + except ValueError: # cannot mmap an empty file + return b"" + + def read_pdf_info(self): + self.file_size_total = len(self.buf) + self.file_size_this = self.file_size_total - self.start_offset + self.read_trailer() + self.root_ref = self.trailer_dict[b"Root"] + self.info_ref = self.trailer_dict.get(b"Info", None) + self.root = PdfDict(self.read_indirect(self.root_ref)) + if self.info_ref is None: + self.info = PdfDict() + else: + self.info = PdfDict(self.read_indirect(self.info_ref)) + check_format_condition(b"Type" in self.root, "/Type missing in Root") + check_format_condition( + self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog" + ) + check_format_condition(b"Pages" in self.root, "/Pages missing in Root") + check_format_condition( + isinstance(self.root[b"Pages"], IndirectReference), + "/Pages in Root is not an indirect reference", + ) + self.pages_ref = self.root[b"Pages"] + self.page_tree_root = self.read_indirect(self.pages_ref) + self.pages = self.linearize_page_tree(self.page_tree_root) + # save the original list of page references + # in case the user modifies, adds or deletes some pages + # and we need to rewrite the pages and their list + self.orig_pages = self.pages[:] + + def next_object_id(self, offset=None): + try: + # TODO: support reuse of deleted objects + reference = IndirectReference(max(self.xref_table.keys()) + 1, 0) + except ValueError: + reference = IndirectReference(1, 0) + if offset is not None: + self.xref_table[reference.object_id] = (offset, 0) + return reference + + delimiter = rb"[][()<>{}/%]" + delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]" + whitespace = rb"[\000\011\012\014\015\040]" + whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]" + whitespace_optional = whitespace + b"*" + whitespace_mandatory = whitespace + b"+" + # No "\012" aka "\n" or "\015" aka "\r": + whitespace_optional_no_nl = rb"[\000\011\014\040]*" + newline_only = rb"[\r\n]+" + newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl + re_trailer_end = re.compile( + whitespace_mandatory + + rb"trailer" + + whitespace_optional + + rb"\<\<(.*\>\>)" + + newline + + rb"startxref" + + newline + + rb"([0-9]+)" + + newline + + rb"%%EOF" + + whitespace_optional + + rb"$", + re.DOTALL, + ) + re_trailer_prev = re.compile( + whitespace_optional + + rb"trailer" + + whitespace_optional + + rb"\<\<(.*?\>\>)" + + newline + + rb"startxref" + + newline + + rb"([0-9]+)" + + newline + + rb"%%EOF" + + whitespace_optional, + re.DOTALL, + ) + + def read_trailer(self): + search_start_offset = len(self.buf) - 16384 + if search_start_offset < self.start_offset: + search_start_offset = self.start_offset + m = self.re_trailer_end.search(self.buf, search_start_offset) + check_format_condition(m, "trailer end not found") + # make sure we found the LAST trailer + last_match = m + while m: + last_match = m + m = self.re_trailer_end.search(self.buf, m.start() + 16) + if not m: + m = last_match + trailer_data = m.group(1) + self.last_xref_section_offset = int(m.group(2)) + self.trailer_dict = self.interpret_trailer(trailer_data) + self.xref_table = XrefTable() + self.read_xref_table(xref_section_offset=self.last_xref_section_offset) + if b"Prev" in self.trailer_dict: + self.read_prev_trailer(self.trailer_dict[b"Prev"]) + + def read_prev_trailer(self, xref_section_offset): + trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) + m = self.re_trailer_prev.search( + self.buf[trailer_offset : trailer_offset + 16384] + ) + check_format_condition(m, "previous trailer not found") + trailer_data = m.group(1) + check_format_condition( + int(m.group(2)) == xref_section_offset, + "xref section offset in previous trailer doesn't match what was expected", + ) + trailer_dict = self.interpret_trailer(trailer_data) + if b"Prev" in trailer_dict: + self.read_prev_trailer(trailer_dict[b"Prev"]) + + re_whitespace_optional = re.compile(whitespace_optional) + re_name = re.compile( + whitespace_optional + + rb"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + + delimiter_or_ws + + rb")" + ) + re_dict_start = re.compile(whitespace_optional + rb"\<\<") + re_dict_end = re.compile(whitespace_optional + rb"\>\>" + whitespace_optional) + + @classmethod + def interpret_trailer(cls, trailer_data): + trailer = {} + offset = 0 + while True: + m = cls.re_name.match(trailer_data, offset) + if not m: + m = cls.re_dict_end.match(trailer_data, offset) + check_format_condition( + m and m.end() == len(trailer_data), + "name not found in trailer, remaining data: " + + repr(trailer_data[offset:]), + ) + break + key = cls.interpret_name(m.group(1)) + value, offset = cls.get_value(trailer_data, m.end()) + trailer[key] = value + check_format_condition( + b"Size" in trailer and isinstance(trailer[b"Size"], int), + "/Size not in trailer or not an integer", + ) + check_format_condition( + b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), + "/Root not in trailer or not an indirect reference", + ) + return trailer + + re_hashes_in_name = re.compile(rb"([^#]*)(#([0-9a-fA-F]{2}))?") + + @classmethod + def interpret_name(cls, raw, as_text=False): + name = b"" + for m in cls.re_hashes_in_name.finditer(raw): + if m.group(3): + name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) + else: + name += m.group(1) + if as_text: + return name.decode("utf-8") + else: + return bytes(name) + + re_null = re.compile(whitespace_optional + rb"null(?=" + delimiter_or_ws + rb")") + re_true = re.compile(whitespace_optional + rb"true(?=" + delimiter_or_ws + rb")") + re_false = re.compile(whitespace_optional + rb"false(?=" + delimiter_or_ws + rb")") + re_int = re.compile( + whitespace_optional + rb"([-+]?[0-9]+)(?=" + delimiter_or_ws + rb")" + ) + re_real = re.compile( + whitespace_optional + + rb"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + + delimiter_or_ws + + rb")" + ) + re_array_start = re.compile(whitespace_optional + rb"\[") + re_array_end = re.compile(whitespace_optional + rb"]") + re_string_hex = re.compile( + whitespace_optional + rb"\<(" + whitespace_or_hex + rb"*)\>" + ) + re_string_lit = re.compile(whitespace_optional + rb"\(") + re_indirect_reference = re.compile( + whitespace_optional + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"R(?=" + + delimiter_or_ws + + rb")" + ) + re_indirect_def_start = re.compile( + whitespace_optional + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"obj(?=" + + delimiter_or_ws + + rb")" + ) + re_indirect_def_end = re.compile( + whitespace_optional + rb"endobj(?=" + delimiter_or_ws + rb")" + ) + re_comment = re.compile( + rb"(" + whitespace_optional + rb"%[^\r\n]*" + newline + rb")*" + ) + re_stream_start = re.compile(whitespace_optional + rb"stream\r?\n") + re_stream_end = re.compile( + whitespace_optional + rb"endstream(?=" + delimiter_or_ws + rb")" + ) + + @classmethod + def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): + if max_nesting == 0: + return None, None + m = cls.re_comment.match(data, offset) + if m: + offset = m.end() + m = cls.re_indirect_def_start.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object definition: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object definition: generation must be non-negative", + ) + check_format_condition( + expect_indirect is None + or expect_indirect + == IndirectReference(int(m.group(1)), int(m.group(2))), + "indirect object definition different than expected", + ) + object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting - 1) + if offset is None: + return object, None + m = cls.re_indirect_def_end.match(data, offset) + check_format_condition(m, "indirect object definition end not found") + return object, m.end() + check_format_condition( + not expect_indirect, "indirect object definition not found" + ) + m = cls.re_indirect_reference.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object reference: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object reference: generation must be non-negative", + ) + return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() + m = cls.re_dict_start.match(data, offset) + if m: + offset = m.end() + result = {} + m = cls.re_dict_end.match(data, offset) + while not m: + key, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + if offset is None: + return result, None + value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + result[key] = value + if offset is None: + return result, None + m = cls.re_dict_end.match(data, offset) + offset = m.end() + m = cls.re_stream_start.match(data, offset) + if m: + try: + stream_len = int(result[b"Length"]) + except (TypeError, KeyError, ValueError) as e: + raise PdfFormatError( + "bad or missing Length in stream dict (%r)" + % result.get(b"Length", None) + ) from e + stream_data = data[m.end() : m.end() + stream_len] + m = cls.re_stream_end.match(data, m.end() + stream_len) + check_format_condition(m, "stream end not found") + offset = m.end() + result = PdfStream(PdfDict(result), stream_data) + else: + result = PdfDict(result) + return result, offset + m = cls.re_array_start.match(data, offset) + if m: + offset = m.end() + result = [] + m = cls.re_array_end.match(data, offset) + while not m: + value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) + result.append(value) + if offset is None: + return result, None + m = cls.re_array_end.match(data, offset) + return result, m.end() + m = cls.re_null.match(data, offset) + if m: + return None, m.end() + m = cls.re_true.match(data, offset) + if m: + return True, m.end() + m = cls.re_false.match(data, offset) + if m: + return False, m.end() + m = cls.re_name.match(data, offset) + if m: + return PdfName(cls.interpret_name(m.group(1))), m.end() + m = cls.re_int.match(data, offset) + if m: + return int(m.group(1)), m.end() + m = cls.re_real.match(data, offset) + if m: + # XXX Decimal instead of float??? + return float(m.group(1)), m.end() + m = cls.re_string_hex.match(data, offset) + if m: + # filter out whitespace + hex_string = bytearray( + b for b in m.group(1) if b in b"0123456789abcdefABCDEF" + ) + if len(hex_string) % 2 == 1: + # append a 0 if the length is not even - yes, at the end + hex_string.append(ord(b"0")) + return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() + m = cls.re_string_lit.match(data, offset) + if m: + return cls.get_literal_string(data, m.end()) + # return None, offset # fallback (only for debugging) + raise PdfFormatError("unrecognized object: " + repr(data[offset : offset + 32])) + + re_lit_str_token = re.compile( + rb"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" + ) + escaped_chars = { + b"n": b"\n", + b"r": b"\r", + b"t": b"\t", + b"b": b"\b", + b"f": b"\f", + b"(": b"(", + b")": b")", + b"\\": b"\\", + ord(b"n"): b"\n", + ord(b"r"): b"\r", + ord(b"t"): b"\t", + ord(b"b"): b"\b", + ord(b"f"): b"\f", + ord(b"("): b"(", + ord(b")"): b")", + ord(b"\\"): b"\\", + } + + @classmethod + def get_literal_string(cls, data, offset): + nesting_depth = 0 + result = bytearray() + for m in cls.re_lit_str_token.finditer(data, offset): + result.extend(data[offset : m.start()]) + if m.group(1): + result.extend(cls.escaped_chars[m.group(1)[1]]) + elif m.group(2): + result.append(int(m.group(2)[1:], 8)) + elif m.group(3): + pass + elif m.group(5): + result.extend(b"\n") + elif m.group(6): + result.extend(b"(") + nesting_depth += 1 + elif m.group(7): + if nesting_depth == 0: + return bytes(result), m.end() + result.extend(b")") + nesting_depth -= 1 + offset = m.end() + raise PdfFormatError("unfinished literal string") + + re_xref_section_start = re.compile(whitespace_optional + rb"xref" + newline) + re_xref_subsection_start = re.compile( + whitespace_optional + + rb"([0-9]+)" + + whitespace_mandatory + + rb"([0-9]+)" + + whitespace_optional + + newline_only + ) + re_xref_entry = re.compile(rb"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") + + def read_xref_table(self, xref_section_offset): + subsection_found = False + m = self.re_xref_section_start.match( + self.buf, xref_section_offset + self.start_offset + ) + check_format_condition(m, "xref section start not found") + offset = m.end() + while True: + m = self.re_xref_subsection_start.match(self.buf, offset) + if not m: + check_format_condition( + subsection_found, "xref subsection start not found" + ) + break + subsection_found = True + offset = m.end() + first_object = int(m.group(1)) + num_objects = int(m.group(2)) + for i in range(first_object, first_object + num_objects): + m = self.re_xref_entry.match(self.buf, offset) + check_format_condition(m, "xref entry not found") + offset = m.end() + is_free = m.group(3) == b"f" + generation = int(m.group(2)) + if not is_free: + new_entry = (int(m.group(1)), generation) + check_format_condition( + i not in self.xref_table or self.xref_table[i] == new_entry, + "xref entry duplicated (and not identical)", + ) + self.xref_table[i] = new_entry + return offset + + def read_indirect(self, ref, max_nesting=-1): + offset, generation = self.xref_table[ref[0]] + check_format_condition( + generation == ref[1], + f"expected to find generation {ref[1]} for object ID {ref[0]} in xref " + f"table, instead found generation {generation} at offset {offset}", + ) + value = self.get_value( + self.buf, + offset + self.start_offset, + expect_indirect=IndirectReference(*ref), + max_nesting=max_nesting, + )[0] + self.cached_objects[ref] = value + return value + + def linearize_page_tree(self, node=None): + if node is None: + node = self.page_tree_root + check_format_condition( + node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" + ) + pages = [] + for kid in node[b"Kids"]: + kid_object = self.read_indirect(kid) + if kid_object[b"Type"] == b"Page": + pages.append(kid) + else: + pages.extend(self.linearize_page_tree(node=kid_object)) + return pages diff --git a/venv/Lib/site-packages/PIL/PixarImagePlugin.py b/venv/Lib/site-packages/PIL/PixarImagePlugin.py new file mode 100644 index 0000000..c4860b6 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PixarImagePlugin.py @@ -0,0 +1,70 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIXAR raster support for PIL +# +# history: +# 97-01-29 fl Created +# +# notes: +# This is incomplete; it is based on a few samples created with +# Photoshop 2.5 and 3.0, and a summary description provided by +# Greg Coats . Hopefully, "L" and +# "RGBA" support will be added in future versions. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from . import Image, ImageFile +from ._binary import i16le as i16 + +# +# helpers + + +def _accept(prefix): + return prefix[:4] == b"\200\350\000\000" + + +## +# Image plugin for PIXAR raster images. + + +class PixarImageFile(ImageFile.ImageFile): + + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self): + + # assuming a 4-byte magic label + s = self.fp.read(4) + if not _accept(s): + raise SyntaxError("not a PIXAR file") + + # read rest of header + s = s + self.fp.read(508) + + self._size = i16(s, 418), i16(s, 416) + + # get channel/depth descriptions + mode = i16(s, 424), i16(s, 426) + + if mode == (14, 2): + self.mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))] + + +# +# -------------------------------------------------------------------- + +Image.register_open(PixarImageFile.format, PixarImageFile, _accept) + +Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/venv/Lib/site-packages/PIL/PngImagePlugin.py b/venv/Lib/site-packages/PIL/PngImagePlugin.py new file mode 100644 index 0000000..53525e2 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PngImagePlugin.py @@ -0,0 +1,1432 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PNG support code +# +# See "PNG (Portable Network Graphics) Specification, version 1.0; +# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). +# +# history: +# 1996-05-06 fl Created (couldn't resist it) +# 1996-12-14 fl Upgraded, added read and verify support (0.2) +# 1996-12-15 fl Separate PNG stream parser +# 1996-12-29 fl Added write support, added getchunks +# 1996-12-30 fl Eliminated circular references in decoder (0.3) +# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) +# 2001-02-08 fl Added transparency support (from Zircon) (0.5) +# 2001-04-16 fl Don't close data source in "open" method (0.6) +# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) +# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) +# 2004-09-20 fl Added PngInfo chunk container +# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) +# 2008-08-13 fl Added tRNS support for RGB images +# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) +# 2009-03-08 fl Added zTXT support (from Lowell Alleman) +# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) +# +# Copyright (c) 1997-2009 by Secret Labs AB +# Copyright (c) 1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import itertools +import logging +import re +import struct +import warnings +import zlib +from enum import IntEnum + +from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from ._binary import o16be as o16 +from ._binary import o32be as o32 + +logger = logging.getLogger(__name__) + +is_cid = re.compile(rb"\w\w\w\w").match + + +_MAGIC = b"\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + # Greyscale + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16, 0): ("I", "I;16B"), + # Truecolour + (8, 2): ("RGB", "RGB"), + (16, 2): ("RGB", "RGB;16B"), + # Indexed-colour + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + # Greyscale with alpha + (8, 4): ("LA", "LA"), + (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + # Truecolour with alpha + (8, 6): ("RGBA", "RGBA"), + (16, 6): ("RGBA", "RGBA;16B"), +} + + +_simple_palette = re.compile(b"^\xff*\x00\xff*$") + +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +""" +Maximum decompressed size for a iTXt or zTXt chunk. +Eliminates decompression bombs where compressed chunks can expand 1000x. +See :ref:`Text in PNG File Format`. +""" +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK +""" +Set the maximum total text chunk size. +See :ref:`Text in PNG File Format`. +""" + + +# APNG frame disposal modes +class Disposal(IntEnum): + OP_NONE = 0 + """ + No disposal is done on this frame before rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_BACKGROUND = 1 + """ + This frame’s modified region is cleared to fully transparent black before rendering + the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_PREVIOUS = 2 + """ + This frame’s modified region is reverted to the previous frame’s contents before + rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + + +# APNG frame blend modes +class Blend(IntEnum): + OP_SOURCE = 0 + """ + All color components of this frame, including alpha, overwrite the previous output + image contents. + See :ref:`Saving APNG sequences`. + """ + OP_OVER = 1 + """ + This frame should be alpha composited with the previous output image contents. + See :ref:`Saving APNG sequences`. + """ + + +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + + +def _crc32(data, seed=0): + return zlib.crc32(data, seed) & 0xFFFFFFFF + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + + +class ChunkStream: + def __init__(self, fp): + + self.fp = fp + self.queue = [] + + def read(self): + """Fetch a new chunk. Returns header information.""" + cid = None + + if self.queue: + cid, pos, length = self.queue.pop() + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) + + if not is_cid(cid): + if not ImageFile.LOAD_TRUNCATED_IMAGES: + raise SyntaxError(f"broken PNG file (chunk {repr(cid)})") + + return cid, pos, length + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self.queue = self.crc = self.fp = None + + def push(self, cid, pos, length): + + self.queue.append((cid, pos, length)) + + def call(self, cid, pos, length): + """Call the appropriate chunk handler""" + + logger.debug("STREAM %r %s %s", cid, pos, length) + return getattr(self, "chunk_" + cid.decode("ascii"))(pos, length) + + def crc(self, cid, data): + """Read and verify checksum""" + + # Skip CRC checks for ancillary chunks if allowed to load truncated + # images + # 5th byte of first char is 1 [specs, section 5.4] + if ImageFile.LOAD_TRUNCATED_IMAGES and (cid[0] >> 5 & 1): + self.crc_skip(cid, data) + return + + try: + crc1 = _crc32(data, _crc32(cid)) + crc2 = i32(self.fp.read(4)) + if crc1 != crc2: + raise SyntaxError( + f"broken PNG file (bad header checksum in {repr(cid)})" + ) + except struct.error as e: + raise SyntaxError( + f"broken PNG file (incomplete checksum in {repr(cid)})" + ) from e + + def crc_skip(self, cid, data): + """Read checksum. Used if the C module is not present""" + + self.fp.read(4) + + def verify(self, endchunk=b"IEND"): + + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + while True: + try: + cid, pos, length = self.read() + except struct.error as e: + raise OSError("truncated PNG file") from e + + if cid == endchunk: + break + self.crc(cid, ImageFile._safe_read(self.fp, length)) + cids.append(cid) + + return cids + + +class iTXt(str): + """ + Subclass of string to allow iTXt chunks to look like strings while + keeping their extra information + + """ + + @staticmethod + def __new__(cls, text, lang=None, tkey=None): + """ + :param cls: the class to use when creating the instance + :param text: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + """ + + self = str.__new__(cls, text) + self.lang = lang + self.tkey = tkey + return self + + +class PngInfo: + """ + PNG chunk container (for use with save(pnginfo=)) + + """ + + def __init__(self): + self.chunks = [] + + def add(self, cid, data, after_idat=False): + """Appends an arbitrary chunk. Use with caution. + + :param cid: a byte string, 4 bytes long. + :param data: a byte string of the encoded data + :param after_idat: for use with private chunks. Whether the chunk + should be written after IDAT + + """ + + chunk = [cid, data] + if after_idat: + chunk.append(True) + self.chunks.append(tuple(chunk)) + + def add_itxt(self, key, value, lang="", tkey="", zip=False): + """Appends an iTXt chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + :param zip: compression flag + + """ + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + if not isinstance(value, bytes): + value = value.encode("utf-8", "strict") + if not isinstance(lang, bytes): + lang = lang.encode("utf-8", "strict") + if not isinstance(tkey, bytes): + tkey = tkey.encode("utf-8", "strict") + + if zip: + self.add( + b"iTXt", + key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value), + ) + else: + self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value) + + def add_text(self, key, value, zip=False): + """Appends a text chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key, text or an + :py:class:`PIL.PngImagePlugin.iTXt` instance + :param zip: compression flag + + """ + if isinstance(value, iTXt): + return self.add_itxt(key, value, value.lang, value.tkey, zip=zip) + + # The tEXt chunk stores latin-1 text + if not isinstance(value, bytes): + try: + value = value.encode("latin-1", "strict") + except UnicodeError: + return self.add_itxt(key, value, zip=zip) + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + + if zip: + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) + else: + self.add(b"tEXt", key + b"\0" + value) + + +# -------------------------------------------------------------------- +# PNG image stream (IHDR/IEND) + + +class PngStream(ChunkStream): + def __init__(self, fp): + super().__init__(fp) + + # local copies of Image attributes + self.im_info = {} + self.im_text = {} + self.im_size = (0, 0) + self.im_mode = None + self.im_tile = None + self.im_palette = None + self.im_custom_mimetype = None + self.im_n_frames = None + self._seq_num = None + self.rewind_state = None + + self.text_memory = 0 + + def check_text_memory(self, chunklen): + self.text_memory += chunklen + if self.text_memory > MAX_TEXT_MEMORY: + raise ValueError( + "Too much memory used in text chunks: " + f"{self.text_memory}>MAX_TEXT_MEMORY" + ) + + def save_rewind(self): + self.rewind_state = { + "info": self.im_info.copy(), + "tile": self.im_tile, + "seq_num": self._seq_num, + } + + def rewind(self): + self.im_info = self.rewind_state["info"] + self.im_tile = self.rewind_state["tile"] + self._seq_num = self.rewind_state["seq_num"] + + def chunk_iCCP(self, pos, length): + + # ICC profile + s = ImageFile._safe_read(self.fp, length) + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + i = s.find(b"\0") + logger.debug("iCCP profile name %r", s[:i]) + logger.debug("Compression method %s", s[i]) + comp_method = s[i] + if comp_method != 0: + raise SyntaxError(f"Unknown compression method {comp_method} in iCCP chunk") + try: + icc_profile = _safe_zlib_decompress(s[i + 2 :]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + icc_profile = None + else: + raise + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos, length): + + # image header + s = ImageFile._safe_read(self.fp, length) + self.im_size = i32(s, 0), i32(s, 4) + try: + self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] + except Exception: + pass + if s[12]: + self.im_info["interlace"] = 1 + if s[11]: + raise SyntaxError("unknown filter category") + return s + + def chunk_IDAT(self, pos, length): + + # image data + if "bbox" in self.im_info: + tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)] + else: + if self.im_n_frames is not None: + self.im_info["default_image"] = True + tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] + self.im_tile = tile + self.im_idat = length + raise EOFError + + def chunk_IEND(self, pos, length): + + # end of PNG image + raise EOFError + + def chunk_PLTE(self, pos, length): + + # palette + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos, length): + + # transparency + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + if _simple_palette.match(s): + # tRNS contains only one full-transparent entry, + # other entries are full opaque + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + # otherwise, we have a byte string with one alpha value + # for each palette entry + self.im_info["transparency"] = s + elif self.im_mode in ("1", "L", "I"): + self.im_info["transparency"] = i16(s) + elif self.im_mode == "RGB": + self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) + return s + + def chunk_gAMA(self, pos, length): + # gamma setting + s = ImageFile._safe_read(self.fp, length) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_cHRM(self, pos, length): + # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 + # WP x,y, Red x,y, Green x,y Blue x,y + + s = ImageFile._safe_read(self.fp, length) + raw_vals = struct.unpack(">%dI" % (len(s) // 4), s) + self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) + return s + + def chunk_sRGB(self, pos, length): + # srgb rendering intent, 1 byte + # 0 perceptual + # 1 relative colorimetric + # 2 saturation + # 3 absolute colorimetric + + s = ImageFile._safe_read(self.fp, length) + self.im_info["srgb"] = s[0] + return s + + def chunk_pHYs(self, pos, length): + + # pixels per unit + s = ImageFile._safe_read(self.fp, length) + px, py = i32(s, 0), i32(s, 4) + unit = s[8] + if unit == 1: # meter + dpi = px * 0.0254, py * 0.0254 + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos, length): + + # text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + # fallback for broken tEXt tags + k = s + v = b"" + if k: + k = k.decode("latin-1", "strict") + v_str = v.decode("latin-1", "replace") + + self.im_info[k] = v if k == "exif" else v_str + self.im_text[k] = v_str + self.check_text_memory(len(v_str)) + + return s + + def chunk_zTXt(self, pos, length): + + # compressed text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + k = s + v = b"" + if v: + comp_method = v[0] + else: + comp_method = 0 + if comp_method != 0: + raise SyntaxError(f"Unknown compression method {comp_method} in zTXt chunk") + try: + v = _safe_zlib_decompress(v[1:]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + v = b"" + else: + raise + except zlib.error: + v = b"" + + if k: + k = k.decode("latin-1", "strict") + v = v.decode("latin-1", "replace") + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_iTXt(self, pos, length): + + # international text + r = s = ImageFile._safe_read(self.fp, length) + try: + k, r = r.split(b"\0", 1) + except ValueError: + return s + if len(r) < 2: + return s + cf, cm, r = r[0], r[1], r[2:] + try: + lang, tk, v = r.split(b"\0", 2) + except ValueError: + return s + if cf != 0: + if cm == 0: + try: + v = _safe_zlib_decompress(v) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + else: + raise + except zlib.error: + return s + else: + return s + try: + k = k.decode("latin-1", "strict") + lang = lang.decode("utf-8", "strict") + tk = tk.decode("utf-8", "strict") + v = v.decode("utf-8", "strict") + except UnicodeError: + return s + + self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) + self.check_text_memory(len(v)) + + return s + + def chunk_eXIf(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + self.im_info["exif"] = b"Exif\x00\x00" + s + return s + + # APNG chunks + def chunk_acTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + if self.im_n_frames is not None: + self.im_n_frames = None + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + n_frames = i32(s) + if n_frames == 0 or n_frames > 0x80000000: + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + self.im_n_frames = n_frames + self.im_info["loop"] = i32(s, 4) + self.im_custom_mimetype = "image/apng" + return s + + def chunk_fcTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + seq = i32(s) + if (self._seq_num is None and seq != 0) or ( + self._seq_num is not None and self._seq_num != seq - 1 + ): + raise SyntaxError("APNG contains frame sequence errors") + self._seq_num = seq + width, height = i32(s, 4), i32(s, 8) + px, py = i32(s, 12), i32(s, 16) + im_w, im_h = self.im_size + if px + width > im_w or py + height > im_h: + raise SyntaxError("APNG contains invalid frames") + self.im_info["bbox"] = (px, py, px + width, py + height) + delay_num, delay_den = i16(s, 20), i16(s, 22) + if delay_den == 0: + delay_den = 100 + self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000 + self.im_info["disposal"] = s[24] + self.im_info["blend"] = s[25] + return s + + def chunk_fdAT(self, pos, length): + s = ImageFile._safe_read(self.fp, 4) + seq = i32(s) + if self._seq_num != seq - 1: + raise SyntaxError("APNG contains frame sequence errors") + self._seq_num = seq + return self.chunk_IDAT(pos + 4, length - 4) + + +# -------------------------------------------------------------------- +# PNG reader + + +def _accept(prefix): + return prefix[:8] == _MAGIC + + +## +# Image plugin for PNG images. + + +class PngImageFile(ImageFile.ImageFile): + + format = "PNG" + format_description = "Portable network graphics" + + def _open(self): + + if not _accept(self.fp.read(8)): + raise SyntaxError("not a PNG file") + self.__fp = self.fp + self.__frame = 0 + + # + # Parse headers up to the first IDAT or fDAT chunk + + self.private_chunks = [] + self.png = PngStream(self.fp) + + while True: + + # + # get next chunk + + cid, pos, length = self.png.read() + + try: + s = self.png.call(cid, pos, length) + except EOFError: + break + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s)) + + self.png.crc(cid, s) + + # + # Copy relevant attributes from the PngStream. An alternative + # would be to let the PngStream class modify these attributes + # directly, but that introduces circular references which are + # difficult to break if things go wrong in the decoder... + # (believe me, I've tried ;-) + + self.mode = self.png.im_mode + self._size = self.png.im_size + self.info = self.png.im_info + self._text = None + self.tile = self.png.im_tile + self.custom_mimetype = self.png.im_custom_mimetype + self.n_frames = self.png.im_n_frames or 1 + self.default_image = self.info.get("default_image", False) + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + if cid == b"fdAT": + self.__prepare_idat = length - 4 + else: + self.__prepare_idat = length # used by load_prepare() + + if self.png.im_n_frames is not None: + self._close_exclusive_fp_after_loading = False + self.png.save_rewind() + self.__rewind_idat = self.__prepare_idat + self.__rewind = self.__fp.tell() + if self.default_image: + # IDAT chunk contains default image and not first animation frame + self.n_frames += 1 + self._seek(0) + self.is_animated = self.n_frames > 1 + + @property + def text(self): + # experimental + if self._text is None: + # iTxt, tEXt and zTXt chunks may appear at the end of the file + # So load the file to ensure that they are read + if self.is_animated: + frame = self.__frame + # for APNG, seek to the final frame before loading + self.seek(self.n_frames - 1) + self.load() + if self.is_animated: + self.seek(frame) + return self._text + + def verify(self): + """Verify PNG file""" + + if self.fp is None: + raise RuntimeError("verify must be called directly after open") + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + self.png.verify() + self.png.close() + + if self._exclusive_fp: + self.fp.close() + self.fp = None + + def seek(self, frame): + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0, True) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + raise EOFError("no more images in APNG file") from e + + def _seek(self, frame, rewind=False): + if frame == 0: + if rewind: + self.__fp.seek(self.__rewind) + self.png.rewind() + self.__prepare_idat = self.__rewind_idat + self.im = None + if self.pyaccess: + self.pyaccess = None + self.info = self.png.im_info + self.tile = self.png.im_tile + self.fp = self.__fp + self._prev_im = None + self.dispose = None + self.default_image = self.info.get("default_image", False) + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + self.dispose_extent = self.info.get("bbox") + self.__frame = 0 + else: + if frame != self.__frame + 1: + raise ValueError(f"cannot seek to frame {frame}") + + # ensure previous frame was loaded + self.load() + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + self._prev_im = self.im.copy() + + self.fp = self.__fp + + # advance to the next frame + if self.__prepare_idat: + ImageFile._safe_read(self.fp, self.__prepare_idat) + self.__prepare_idat = 0 + frame_start = False + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + raise EOFError("No more images in APNG file") + if cid == b"fcTL": + if frame_start: + # there must be at least one fdAT chunk between fcTL chunks + raise SyntaxError("APNG missing frame data") + frame_start = True + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + if frame_start: + self.__prepare_idat = length + break + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + ImageFile._safe_read(self.fp, length) + + self.__frame = frame + self.tile = self.png.im_tile + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + self.dispose_extent = self.info.get("bbox") + + if not self.tile: + raise EOFError + + # setup frame disposal (actual disposal done when needed in the next _seek()) + if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: + self.dispose_op = Disposal.OP_BACKGROUND + + if self.dispose_op == Disposal.OP_PREVIOUS: + self.dispose = self._prev_im.copy() + self.dispose = self._crop(self.dispose, self.dispose_extent) + elif self.dispose_op == Disposal.OP_BACKGROUND: + self.dispose = Image.core.fill(self.mode, self.size) + self.dispose = self._crop(self.dispose, self.dispose_extent) + else: + self.dispose = None + + def tell(self): + return self.__frame + + def load_prepare(self): + """internal: prepare to read PNG file""" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + self.__idat = self.__prepare_idat # used by load_read() + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, read_bytes): + """internal: read more image data""" + + while self.__idat == 0: + # end of chunk, skip forward to next one + + self.fp.read(4) # CRC + + cid, pos, length = self.png.read() + + if cid not in [b"IDAT", b"DDAT", b"fdAT"]: + self.png.push(cid, pos, length) + return b"" + + if cid == b"fdAT": + try: + self.png.call(cid, pos, length) + except EOFError: + pass + self.__idat = length - 4 # sequence_num has already been read + else: + self.__idat = length # empty chunks are allowed + + # read more data from this chunk + if read_bytes <= 0: + read_bytes = self.__idat + else: + read_bytes = min(read_bytes, self.__idat) + + self.__idat = self.__idat - read_bytes + + return self.fp.read(read_bytes) + + def load_end(self): + """internal: finished reading image data""" + if self.__idat != 0: + self.fp.read(self.__idat) + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + break + elif cid == b"fcTL" and self.is_animated: + # start of the next frame, stop reading + self.__prepare_idat = 0 + self.png.push(cid, pos, length) + break + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s, True)) + self._text = self.png.im_text + if not self.is_animated: + self.png.close() + self.png = None + else: + if self._prev_im and self.blend_op == Blend.OP_OVER: + updated = self._crop(self.im, self.dispose_extent) + self._prev_im.paste( + updated, self.dispose_extent, updated.convert("RGBA") + ) + self.im = self._prev_im + if self.pyaccess: + self.pyaccess = None + + def _getexif(self): + if "exif" not in self.info: + self.load() + if "exif" not in self.info and "Raw profile type exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + def getexif(self): + if "exif" not in self.info: + self.load() + + return super().getexif() + + def getxmp(self): + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ + return ( + self._getxmp(self.info["XML:com.adobe.xmp"]) + if "XML:com.adobe.xmp" in self.info + else {} + ) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# PNG writer + +_OUTMODES = { + # supported PIL modes, and corresponding rawmodes/bits/color combinations + "1": ("1", b"\x01\x00"), + "L;1": ("L;1", b"\x01\x00"), + "L;2": ("L;2", b"\x02\x00"), + "L;4": ("L;4", b"\x04\x00"), + "L": ("L", b"\x08\x00"), + "LA": ("LA", b"\x08\x04"), + "I": ("I;16B", b"\x10\x00"), + "I;16": ("I;16B", b"\x10\x00"), + "P;1": ("P;1", b"\x01\x03"), + "P;2": ("P;2", b"\x02\x03"), + "P;4": ("P;4", b"\x04\x03"), + "P": ("P", b"\x08\x03"), + "RGB": ("RGB", b"\x08\x02"), + "RGBA": ("RGBA", b"\x08\x06"), +} + + +def putchunk(fp, cid, *data): + """Write a PNG chunk (including CRC field)""" + + data = b"".join(data) + + fp.write(o32(len(data)) + cid) + fp.write(data) + crc = _crc32(data, _crc32(cid)) + fp.write(o32(crc)) + + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp, chunk): + self.fp = fp + self.chunk = chunk + + def write(self, data): + self.chunk(self.fp, b"IDAT", data) + + +class _fdat: + # wrap encoder output in fdAT chunks + + def __init__(self, fp, chunk, seq_num): + self.fp = fp + self.chunk = chunk + self.seq_num = seq_num + + def write(self, data): + self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) + self.seq_num += 1 + + +def _write_multiple_frames(im, fp, chunk, rawmode): + default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) + duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) + loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) + blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) + + if default_image: + chain = itertools.chain(im.encoderinfo.get("append_images", [])) + else: + chain = itertools.chain([im], im.encoderinfo.get("append_images", [])) + + im_frames = [] + frame_count = 0 + for im_seq in chain: + for im_frame in ImageSequence.Iterator(im_seq): + im_frame = im_frame.copy() + if im_frame.mode != im.mode: + if im.mode == "P": + im_frame = im_frame.convert(im.mode, palette=im.palette) + else: + im_frame = im_frame.convert(im.mode) + encoderinfo = im.encoderinfo.copy() + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + if isinstance(blend, (list, tuple)): + encoderinfo["blend"] = blend[frame_count] + frame_count += 1 + + if im_frames: + previous = im_frames[-1] + prev_disposal = previous["encoderinfo"].get("disposal") + prev_blend = previous["encoderinfo"].get("blend") + if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: + prev_disposal = Disposal.OP_BACKGROUND + + if prev_disposal == Disposal.OP_BACKGROUND: + base_im = previous["im"] + dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) + bbox = previous["bbox"] + if bbox: + dispose = dispose.crop(bbox) + else: + bbox = (0, 0) + im.size + base_im.paste(dispose, bbox) + elif prev_disposal == Disposal.OP_PREVIOUS: + base_im = im_frames[-2]["im"] + else: + base_im = previous["im"] + delta = ImageChops.subtract_modulo( + im_frame.convert("RGB"), base_im.convert("RGB") + ) + bbox = delta.getbbox() + if ( + not bbox + and prev_disposal == encoderinfo.get("disposal") + and prev_blend == encoderinfo.get("blend") + ): + if isinstance(duration, (list, tuple)): + previous["encoderinfo"]["duration"] += encoderinfo["duration"] + continue + else: + bbox = None + im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) + + # animation control + chunk( + fp, + b"acTL", + o32(len(im_frames)), # 0: num_frames + o32(loop), # 4: num_plays + ) + + # default image IDAT (if it exists) + if default_image: + ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) + + seq_num = 0 + for frame, frame_data in enumerate(im_frames): + im_frame = frame_data["im"] + if not frame_data["bbox"]: + bbox = (0, 0) + im_frame.size + else: + bbox = frame_data["bbox"] + im_frame = im_frame.crop(bbox) + size = im_frame.size + encoderinfo = frame_data["encoderinfo"] + frame_duration = int(round(encoderinfo.get("duration", duration))) + frame_disposal = encoderinfo.get("disposal", disposal) + frame_blend = encoderinfo.get("blend", blend) + # frame control + chunk( + fp, + b"fcTL", + o32(seq_num), # sequence_number + o32(size[0]), # width + o32(size[1]), # height + o32(bbox[0]), # x_offset + o32(bbox[1]), # y_offset + o16(frame_duration), # delay_numerator + o16(1000), # delay_denominator + o8(frame_disposal), # dispose_op + o8(frame_blend), # blend_op + ) + seq_num += 1 + # frame data + if frame == 0 and not default_image: + # first frame must be in IDAT chunks for backwards compatibility + ImageFile._save( + im_frame, + _idat(fp, chunk), + [("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + else: + fdat_chunks = _fdat(fp, chunk, seq_num) + ImageFile._save( + im_frame, + fdat_chunks, + [("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + seq_num = fdat_chunks.seq_num + + +def _save_all(im, fp, filename): + _save(im, fp, filename, save_all=True) + + +def _save(im, fp, filename, chunk=putchunk, save_all=False): + # save an image to disk (called by the save method) + + mode = im.mode + + if mode == "P": + + # + # attempt to minimize storage requirements for palette images + if "bits" in im.encoderinfo: + # number of bits specified by user + colors = min(1 << im.encoderinfo["bits"], 256) + else: + # check palette contents + if im.palette: + colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1) + else: + colors = 256 + + if colors <= 16: + if colors <= 2: + bits = 1 + elif colors <= 4: + bits = 2 + else: + bits = 4 + mode = f"{mode};{bits}" + + # encoder options + im.encoderconfig = ( + im.encoderinfo.get("optimize", False), + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + im.encoderinfo.get("dictionary", b""), + ) + + # get the corresponding PNG mode + try: + rawmode, mode = _OUTMODES[mode] + except KeyError as e: + raise OSError(f"cannot write mode {mode} as PNG") from e + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk( + fp, + b"IHDR", + o32(im.size[0]), # 0: size + o32(im.size[1]), + mode, # 8: depth/type + b"\0", # 10: compression + b"\0", # 11: filter category + b"\0", # 12: interlace flag + ) + + chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"] + + icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + # ICC profile + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(icc) + chunk(fp, b"iCCP", data) + + # You must either have sRGB or iCCP. + # Disallow sRGB chunks when an iCCP-chunk has been emitted. + chunks.remove(b"sRGB") + + info = im.encoderinfo.get("pnginfo") + if info: + chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + elif cid in chunks_multiple_allowed: + chunk(fp, cid, data) + elif cid[1:2].islower(): + # Private chunk + after_idat = info_chunk[2:3] + if not after_idat: + chunk(fp, cid, data) + + if im.mode == "P": + palette_byte_number = colors * 3 + palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + while len(palette_bytes) < palette_byte_number: + palette_bytes += b"\0" + chunk(fp, b"PLTE", palette_bytes) + + transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None)) + + if transparency or transparency == 0: + if im.mode == "P": + # limit to actual palette size + alpha_bytes = colors + if isinstance(transparency, bytes): + chunk(fp, b"tRNS", transparency[:alpha_bytes]) + else: + transparency = max(0, min(255, transparency)) + alpha = b"\xFF" * transparency + b"\0" + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + elif im.mode in ("1", "L", "I"): + transparency = max(0, min(65535, transparency)) + chunk(fp, b"tRNS", o16(transparency)) + elif im.mode == "RGB": + red, green, blue = transparency + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) + else: + if "transparency" in im.encoderinfo: + # don't bother with transparency if it's an RGBA + # and it's in the info dict. It's probably just stale. + raise OSError("cannot use transparency for this mode") + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = colors + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + + dpi = im.encoderinfo.get("dpi") + if dpi: + chunk( + fp, + b"pHYs", + o32(int(dpi[0] / 0.0254 + 0.5)), + o32(int(dpi[1] / 0.0254 + 0.5)), + b"\x01", + ) + + if info: + chunks = [b"bKGD", b"hIST"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + + exif = im.encoderinfo.get("exif", im.info.get("exif")) + if exif: + if isinstance(exif, Image.Exif): + exif = exif.tobytes(8) + if exif.startswith(b"Exif\x00\x00"): + exif = exif[6:] + chunk(fp, b"eXIf", exif) + + if save_all: + _write_multiple_frames(im, fp, chunk, rawmode) + else: + ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) + + if info: + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid[1:2].islower(): + # Private chunk + after_idat = info_chunk[2:3] + if after_idat: + chunk(fp, cid, data) + + chunk(fp, b"IEND", b"") + + if hasattr(fp, "flush"): + fp.flush() + + +# -------------------------------------------------------------------- +# PNG chunk converter + + +def getchunks(im, **params): + """Return a list of PNG chunks representing this image.""" + + class collector: + data = [] + + def write(self, data): + pass + + def append(self, chunk): + self.data.append(chunk) + + def append(fp, cid, *data): + data = b"".join(data) + crc = o32(_crc32(data, _crc32(cid))) + fp.append((cid, data, crc)) + + fp = collector() + + try: + im.encoderinfo = params + _save(im, fp, None, append) + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(PngImageFile.format, PngImageFile, _accept) +Image.register_save(PngImageFile.format, _save) +Image.register_save_all(PngImageFile.format, _save_all) + +Image.register_extensions(PngImageFile.format, [".png", ".apng"]) + +Image.register_mime(PngImageFile.format, "image/png") diff --git a/venv/Lib/site-packages/PIL/PpmImagePlugin.py b/venv/Lib/site-packages/PIL/PpmImagePlugin.py new file mode 100644 index 0000000..b760e22 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PpmImagePlugin.py @@ -0,0 +1,199 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PPM support for PIL +# +# History: +# 96-03-24 fl Created +# 98-03-06 fl Write RGBA images (as RGB, that is) +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import o8 +from ._binary import o32le as o32 + +# +# -------------------------------------------------------------------- + +b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" + +MODES = { + # standard + b"P4": "1", + b"P5": "L", + b"P6": "RGB", + # extensions + b"P0CMYK": "CMYK", + # PIL extensions (for test purposes only) + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK", +} + + +def _accept(prefix): + return prefix[0:1] == b"P" and prefix[1] in b"0456y" + + +## +# Image plugin for PBM, PGM, and PPM images. + + +class PpmImageFile(ImageFile.ImageFile): + + format = "PPM" + format_description = "Pbmplus image" + + def _read_magic(self): + magic = b"" + # read until whitespace or longest available magic number + for _ in range(6): + c = self.fp.read(1) + if not c or c in b_whitespace: + break + magic += c + return magic + + def _read_token(self): + token = b"" + while len(token) <= 10: # read until next whitespace or limit of 10 characters + c = self.fp.read(1) + if not c: + break + elif c in b_whitespace: # token ended + if not token: + # skip whitespace at start + continue + break + elif c == b"#": + # ignores rest of the line; stops at CR, LF or EOF + while self.fp.read(1) not in b"\r\n": + pass + continue + token += c + if not token: + # Token was not even 1 byte + raise ValueError("Reached EOF while reading header") + elif len(token) > 10: + raise ValueError(f"Token too long in file header: {token}") + return token + + def _open(self): + magic_number = self._read_magic() + try: + mode = MODES[magic_number] + except KeyError: + raise SyntaxError("not a PPM file") + + self.custom_mimetype = { + b"P4": "image/x-portable-bitmap", + b"P5": "image/x-portable-graymap", + b"P6": "image/x-portable-pixmap", + }.get(magic_number) + + if mode == "1": + self.mode = "1" + rawmode = "1;I" + else: + self.mode = rawmode = mode + + decoder_name = "raw" + for ix in range(3): + token = int(self._read_token()) + if ix == 0: # token is the x size + xsize = token + elif ix == 1: # token is the y size + ysize = token + if mode == "1": + break + elif ix == 2: # token is maxval + maxval = token + if maxval > 255 and mode == "L": + self.mode = "I" + + # If maxval matches a bit depth, use the raw decoder directly + if maxval == 65535 and mode == "L": + rawmode = "I;16B" + elif maxval != 255: + decoder_name = "ppm" + args = (rawmode, 0, 1) if decoder_name == "raw" else (rawmode, maxval) + + self._size = xsize, ysize + self.tile = [(decoder_name, (0, 0, xsize, ysize), self.fp.tell(), args)] + + +class PpmDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + data = bytearray() + maxval = min(self.args[-1], 65535) + in_byte_count = 1 if maxval < 256 else 2 + out_byte_count = 4 if self.mode == "I" else 1 + out_max = 65535 if self.mode == "I" else 255 + bands = Image.getmodebands(self.mode) + while len(data) < self.state.xsize * self.state.ysize * bands * out_byte_count: + pixels = self.fd.read(in_byte_count * bands) + if len(pixels) < in_byte_count * bands: + # eof + break + for b in range(bands): + value = ( + pixels[b] if in_byte_count == 1 else i16(pixels, b * in_byte_count) + ) + value = min(out_max, round(value / maxval * out_max)) + data += o32(value) if self.mode == "I" else o8(value) + rawmode = "I;32" if self.mode == "I" else self.mode + self.set_as_raw(bytes(data), (rawmode, 0, 1)) + return -1, 0 + + +# +# -------------------------------------------------------------------- + + +def _save(im, fp, filename): + if im.mode == "1": + rawmode, head = "1;I", b"P4" + elif im.mode == "L": + rawmode, head = "L", b"P5" + elif im.mode == "I": + rawmode, head = "I;16B", b"P5" + elif im.mode in ("RGB", "RGBA"): + rawmode, head = "RGB", b"P6" + else: + raise OSError(f"cannot write mode {im.mode} as PPM") + fp.write(head + b"\n%d %d\n" % im.size) + if head == b"P6": + fp.write(b"255\n") + elif head == b"P5": + if rawmode == "L": + fp.write(b"255\n") + else: + fp.write(b"65535\n") + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) + + # ALTERNATIVE: save via builtin debug function + # im._dump(filename) + + +# +# -------------------------------------------------------------------- + + +Image.register_open(PpmImageFile.format, PpmImageFile, _accept) +Image.register_save(PpmImageFile.format, _save) + +Image.register_decoder("ppm", PpmDecoder) + +Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm"]) + +Image.register_mime(PpmImageFile.format, "image/x-portable-anymap") diff --git a/venv/Lib/site-packages/PIL/PsdImagePlugin.py b/venv/Lib/site-packages/PIL/PsdImagePlugin.py new file mode 100644 index 0000000..2832195 --- /dev/null +++ b/venv/Lib/site-packages/PIL/PsdImagePlugin.py @@ -0,0 +1,311 @@ +# +# The Python Imaging Library +# $Id$ +# +# Adobe PSD 2.5/3.0 file handling +# +# History: +# 1995-09-01 fl Created +# 1997-01-03 fl Read most PSD images +# 1997-01-18 fl Fixed P and CMYK support +# 2001-10-21 fl Added seek/tell support (for layers) +# +# Copyright (c) 1997-2001 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import Image, ImageFile, ImagePalette +from ._binary import i8 +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import si16be as si16 + +MODES = { + # (photoshop mode, bits) -> (pil mode, required channels) + (0, 1): ("1", 1), + (0, 8): ("L", 1), + (1, 8): ("L", 1), + (2, 8): ("P", 1), + (3, 8): ("RGB", 3), + (4, 8): ("CMYK", 4), + (7, 8): ("L", 1), # FIXME: multilayer + (8, 8): ("L", 1), # duotone + (9, 8): ("LAB", 3), +} + + +# --------------------------------------------------------------------. +# read PSD images + + +def _accept(prefix): + return prefix[:4] == b"8BPS" + + +## +# Image plugin for Photoshop images. + + +class PsdImageFile(ImageFile.ImageFile): + + format = "PSD" + format_description = "Adobe Photoshop" + _close_exclusive_fp_after_loading = False + + def _open(self): + + read = self.fp.read + + # + # header + + s = read(26) + if not _accept(s) or i16(s, 4) != 1: + raise SyntaxError("not a PSD file") + + psd_bits = i16(s, 22) + psd_channels = i16(s, 12) + psd_mode = i16(s, 24) + + mode, channels = MODES[(psd_mode, psd_bits)] + + if channels > psd_channels: + raise OSError("not enough channels") + + self.mode = mode + self._size = i32(s, 18), i32(s, 14) + + # + # color mode data + + size = i32(read(4)) + if size: + data = read(size) + if mode == "P" and size == 768: + self.palette = ImagePalette.raw("RGB;L", data) + + # + # image resources + + self.resources = [] + + size = i32(read(4)) + if size: + # load resources + end = self.fp.tell() + size + while self.fp.tell() < end: + read(4) # signature + id = i16(read(2)) + name = read(i8(read(1))) + if not (len(name) & 1): + read(1) # padding + data = read(i32(read(4))) + if len(data) & 1: + read(1) # padding + self.resources.append((id, name, data)) + if id == 1039: # ICC profile + self.info["icc_profile"] = data + + # + # layer and mask information + + self.layers = [] + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + _layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size)) + self.layers = _layerinfo(_layer_data, size) + self.fp.seek(end) + self.n_frames = len(self.layers) + self.is_animated = self.n_frames > 1 + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self.__fp = self.fp + self.frame = 1 + self._min_frame = 1 + + def seek(self, layer): + if not self._seek_check(layer): + return + + # seek to given layer (1..max) + try: + name, mode, bbox, tile = self.layers[layer - 1] + self.mode = mode + self.tile = tile + self.frame = layer + self.fp = self.__fp + return name, bbox + except IndexError as e: + raise EOFError("no such layer") from e + + def tell(self): + # return layer number (0=image, 1..max=layers) + return self.frame + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +def _layerinfo(fp, ct_bytes): + # read layerinfo block + layers = [] + + def read(size): + return ImageFile._safe_read(fp, size) + + ct = si16(read(2)) + + # sanity check + if ct_bytes < (abs(ct) * 20): + raise SyntaxError("Layer block too short for number of layers requested") + + for i in range(abs(ct)): + + # bounding box + y0 = i32(read(4)) + x0 = i32(read(4)) + y1 = i32(read(4)) + x1 = i32(read(4)) + + # image info + mode = [] + ct_types = i16(read(2)) + types = list(range(ct_types)) + if len(types) > 4: + continue + + for i in types: + type = i16(read(2)) + + if type == 65535: + m = "A" + else: + m = "RGBA"[type] + + mode.append(m) + read(4) # size + + # figure out the image mode + mode.sort() + if mode == ["R"]: + mode = "L" + elif mode == ["B", "G", "R"]: + mode = "RGB" + elif mode == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = None # unknown + + # skip over blend flags and extra information + read(12) # filler + name = "" + size = i32(read(4)) # length of the extra data field + if size: + data_end = fp.tell() + size + + length = i32(read(4)) + if length: + fp.seek(length - 16, io.SEEK_CUR) + + length = i32(read(4)) + if length: + fp.seek(length, io.SEEK_CUR) + + length = i8(read(1)) + if length: + # Don't know the proper encoding, + # Latin-1 should be a good guess + name = read(length).decode("latin-1", "replace") + + fp.seek(data_end) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + i = 0 + for name, mode, bbox in layers: + tile = [] + for m in mode: + t = _maketile(fp, m, bbox, 1) + if t: + tile.extend(t) + layers[i] = name, mode, bbox, tile + i += 1 + + return layers + + +def _maketile(file, mode, bbox, channels): + + tile = None + read = file.read + + compression = i16(read(2)) + + xsize = bbox[2] - bbox[0] + ysize = bbox[3] - bbox[1] + + offset = file.tell() + + if compression == 0: + # + # raw compression + tile = [] + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("raw", bbox, offset, layer)) + offset = offset + xsize * ysize + + elif compression == 1: + # + # packbits compression + i = 0 + tile = [] + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("packbits", bbox, offset, layer)) + for y in range(ysize): + offset = offset + i16(bytecount, i) + i += 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tile + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PsdImageFile.format, PsdImageFile, _accept) + +Image.register_extension(PsdImageFile.format, ".psd") + +Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop") diff --git a/venv/Lib/site-packages/PIL/PyAccess.py b/venv/Lib/site-packages/PIL/PyAccess.py new file mode 100644 index 0000000..eeaa0cc --- /dev/null +++ b/venv/Lib/site-packages/PIL/PyAccess.py @@ -0,0 +1,353 @@ +# +# The Python Imaging Library +# Pillow fork +# +# Python implementation of the PixelAccess Object +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# Copyright (c) 2013 Eric Soroos +# +# See the README file for information on usage and redistribution +# + +# Notes: +# +# * Implements the pixel access object following Access. +# * Does not implement the line functions, as they don't appear to be used +# * Taking only the tuple form, which is used from python. +# * Fill.c uses the integer form, but it's still going to use the old +# Access.c implementation. +# + +import logging +import sys + +try: + from cffi import FFI + + defs = """ + struct Pixel_RGBA { + unsigned char r,g,b,a; + }; + struct Pixel_I16 { + unsigned char l,r; + }; + """ + ffi = FFI() + ffi.cdef(defs) +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + FFI = ffi = deferred_error(ex) + +logger = logging.getLogger(__name__) + + +class PyAccess: + def __init__(self, img, readonly=False): + vals = dict(img.im.unsafe_ptrs) + self.readonly = readonly + self.image8 = ffi.cast("unsigned char **", vals["image8"]) + self.image32 = ffi.cast("int **", vals["image32"]) + self.image = ffi.cast("unsigned char **", vals["image"]) + self.xsize, self.ysize = img.im.size + self._img = img + + # Keep pointer to im object to prevent dereferencing. + self._im = img.im + if self._im.mode == "P": + self._palette = img.palette + + # Debugging is polluting test traces, only useful here + # when hacking on PyAccess + # logger.debug("%s", vals) + self._post_init() + + def _post_init(self): + pass + + def __setitem__(self, xy, color): + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :param color: The pixel value. + """ + if self.readonly: + raise ValueError("Attempt to putpixel a read only image") + (x, y) = xy + if x < 0: + x = self.xsize + x + if y < 0: + y = self.ysize + y + (x, y) = self.check_xy((x, y)) + + if ( + self._im.mode == "P" + and isinstance(color, (list, tuple)) + and len(color) in [3, 4] + ): + # RGB or RGBA value for a P image + color = self._palette.getcolor(color, self._img) + + return self.set_pixel(x, y, color) + + def __getitem__(self, xy): + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multiple band + images + + :param xy: The pixel coordinate, given as (x, y). See + :ref:`coordinate-system`. + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + """ + (x, y) = xy + if x < 0: + x = self.xsize + x + if y < 0: + y = self.ysize + y + (x, y) = self.check_xy((x, y)) + return self.get_pixel(x, y) + + putpixel = __setitem__ + getpixel = __getitem__ + + def check_xy(self, xy): + (x, y) = xy + if not (0 <= x < self.xsize and 0 <= y < self.ysize): + raise ValueError("pixel location out of range") + return xy + + +class _PyAccess32_2(PyAccess): + """PA, LA, stored in first and last bytes of a 32 bit word""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.a = min(color[1], 255) + + +class _PyAccess32_3(PyAccess): + """RGB and friends, stored in the first three bytes of a 32 bit word""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = 255 + + +class _PyAccess32_4(PyAccess): + """RGBA etc, all 4 bytes of a 32 bit word""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = min(color[3], 255) + + +class _PyAccess8(PyAccess): + """1, L, P, 8 bit images stored as uint8""" + + def _post_init(self, *args, **kwargs): + self.pixels = self.image8 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 255) + except TypeError: + # tuple + self.pixels[y][x] = min(color[0], 255) + + +class _PyAccessI16_N(PyAccess): + """I;16 access, native bitendian without conversion""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("unsigned short **", self.image) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 65535) + except TypeError: + # tuple + self.pixels[y][x] = min(color[0], 65535) + + +class _PyAccessI16_L(PyAccess): + """I;16L access, with conversion""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_I16 **", self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l + pixel.r * 256 + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except TypeError: + color = min(color[0], 65535) + + pixel.l = color & 0xFF # noqa: E741 + pixel.r = color >> 8 + + +class _PyAccessI16_B(PyAccess): + """I;16B access, with conversion""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_I16 **", self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l * 256 + pixel.r + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except Exception: + color = min(color[0], 65535) + + pixel.l = color >> 8 # noqa: E741 + pixel.r = color & 0xFF + + +class _PyAccessI32_N(PyAccess): + """Signed Int32 access, native endian""" + + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + self.pixels[y][x] = color + + +class _PyAccessI32_Swap(PyAccess): + """I;32L/B access, with byteswapping conversion""" + + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def reverse(self, i): + orig = ffi.new("int *", i) + chars = ffi.cast("unsigned char *", orig) + chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] + return ffi.cast("int *", chars)[0] + + def get_pixel(self, x, y): + return self.reverse(self.pixels[y][x]) + + def set_pixel(self, x, y, color): + self.pixels[y][x] = self.reverse(color) + + +class _PyAccessF(PyAccess): + """32 bit float access""" + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("float **", self.image32) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # not a tuple + self.pixels[y][x] = color + except TypeError: + # tuple + self.pixels[y][x] = color[0] + + +mode_map = { + "1": _PyAccess8, + "L": _PyAccess8, + "P": _PyAccess8, + "LA": _PyAccess32_2, + "La": _PyAccess32_2, + "PA": _PyAccess32_2, + "RGB": _PyAccess32_3, + "LAB": _PyAccess32_3, + "HSV": _PyAccess32_3, + "YCbCr": _PyAccess32_3, + "RGBA": _PyAccess32_4, + "RGBa": _PyAccess32_4, + "RGBX": _PyAccess32_4, + "CMYK": _PyAccess32_4, + "F": _PyAccessF, + "I": _PyAccessI32_N, +} + +if sys.byteorder == "little": + mode_map["I;16"] = _PyAccessI16_N + mode_map["I;16L"] = _PyAccessI16_N + mode_map["I;16B"] = _PyAccessI16_B + + mode_map["I;32L"] = _PyAccessI32_N + mode_map["I;32B"] = _PyAccessI32_Swap +else: + mode_map["I;16"] = _PyAccessI16_L + mode_map["I;16L"] = _PyAccessI16_L + mode_map["I;16B"] = _PyAccessI16_N + + mode_map["I;32L"] = _PyAccessI32_Swap + mode_map["I;32B"] = _PyAccessI32_N + + +def new(img, readonly=False): + access_type = mode_map.get(img.mode, None) + if not access_type: + logger.debug("PyAccess Not Implemented: %s", img.mode) + return None + return access_type(img, readonly) diff --git a/venv/Lib/site-packages/PIL/SgiImagePlugin.py b/venv/Lib/site-packages/PIL/SgiImagePlugin.py new file mode 100644 index 0000000..5f1ef6e --- /dev/null +++ b/venv/Lib/site-packages/PIL/SgiImagePlugin.py @@ -0,0 +1,230 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# +# History: +# 2017-22-07 mb Add RLE decompression +# 2016-16-10 mb Add save method without compression +# 1995-09-10 fl Created +# +# Copyright (c) 2016 by Mickael Bonfill. +# Copyright (c) 2008 by Karsten Hiddemann. +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1995 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +import os +import struct + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import o8 + + +def _accept(prefix): + return len(prefix) >= 2 and i16(prefix) == 474 + + +MODES = { + (1, 1, 1): "L", + (1, 2, 1): "L", + (2, 1, 1): "L;16B", + (2, 2, 1): "L;16B", + (1, 3, 3): "RGB", + (2, 3, 3): "RGB;16B", + (1, 3, 4): "RGBA", + (2, 3, 4): "RGBA;16B", +} + + +## +# Image plugin for SGI images. +class SgiImageFile(ImageFile.ImageFile): + + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self): + + # HEAD + headlen = 512 + s = self.fp.read(headlen) + + if not _accept(s): + raise ValueError("Not an SGI image file") + + # compression : verbatim or RLE + compression = s[2] + + # bpc : 1 or 2 bytes (8bits or 16bits) + bpc = s[3] + + # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) + dimension = i16(s, 4) + + # xsize : width + xsize = i16(s, 6) + + # ysize : height + ysize = i16(s, 8) + + # zsize : channels count + zsize = i16(s, 10) + + # layout + layout = bpc, dimension, zsize + + # determine mode from bits/zsize + rawmode = "" + try: + rawmode = MODES[layout] + except KeyError: + pass + + if rawmode == "": + raise ValueError("Unsupported SGI image mode") + + self._size = xsize, ysize + self.mode = rawmode.split(";")[0] + if self.mode == "RGB": + self.custom_mimetype = "image/rgb" + + # orientation -1 : scanlines begins at the bottom-left corner + orientation = -1 + + # decoder info + if compression == 0: + pagesize = xsize * ysize * bpc + if bpc == 2: + self.tile = [ + ("SGI16", (0, 0) + self.size, headlen, (self.mode, 0, orientation)) + ] + else: + self.tile = [] + offset = headlen + for layer in self.mode: + self.tile.append( + ("raw", (0, 0) + self.size, offset, (layer, 0, orientation)) + ) + offset += pagesize + elif compression == 1: + self.tile = [ + ("sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc)) + ] + + +def _save(im, fp, filename): + if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L": + raise ValueError("Unsupported SGI image mode") + + # Get the keyword arguments + info = im.encoderinfo + + # Byte-per-pixel precision, 1 = 8bits per pixel + bpc = info.get("bpc", 1) + + if bpc not in (1, 2): + raise ValueError("Unsupported number of bytes per pixel") + + # Flip the image, since the origin of SGI file is the bottom-left corner + orientation = -1 + # Define the file as SGI File Format + magicNumber = 474 + # Run-Length Encoding Compression - Unsupported at this time + rle = 0 + + # Number of dimensions (x,y,z) + dim = 3 + # X Dimension = width / Y Dimension = height + x, y = im.size + if im.mode == "L" and y == 1: + dim = 1 + elif im.mode == "L": + dim = 2 + # Z Dimension: Number of channels + z = len(im.mode) + + if dim == 1 or dim == 2: + z = 1 + + # assert we've got the right number of bands. + if len(im.getbands()) != z: + raise ValueError( + f"incorrect number of bands in SGI write: {z} vs {len(im.getbands())}" + ) + + # Minimum Byte value + pinmin = 0 + # Maximum Byte value (255 = 8bits per pixel) + pinmax = 255 + # Image name (79 characters max, truncated below in write) + imgName = os.path.splitext(os.path.basename(filename))[0] + imgName = imgName.encode("ascii", "ignore") + # Standard representation of pixel in the file + colormap = 0 + fp.write(struct.pack(">h", magicNumber)) + fp.write(o8(rle)) + fp.write(o8(bpc)) + fp.write(struct.pack(">H", dim)) + fp.write(struct.pack(">H", x)) + fp.write(struct.pack(">H", y)) + fp.write(struct.pack(">H", z)) + fp.write(struct.pack(">l", pinmin)) + fp.write(struct.pack(">l", pinmax)) + fp.write(struct.pack("4s", b"")) # dummy + fp.write(struct.pack("79s", imgName)) # truncates to 79 chars + fp.write(struct.pack("s", b"")) # force null byte after imgname + fp.write(struct.pack(">l", colormap)) + fp.write(struct.pack("404s", b"")) # dummy + + rawmode = "L" + if bpc == 2: + rawmode = "L;16B" + + for channel in im.split(): + fp.write(channel.tobytes("raw", rawmode, 0, orientation)) + + if hasattr(fp, "flush"): + fp.flush() + + +class SGI16Decoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + rawmode, stride, orientation = self.args + pagesize = self.state.xsize * self.state.ysize + zsize = len(self.mode) + self.fd.seek(512) + + for band in range(zsize): + channel = Image.new("L", (self.state.xsize, self.state.ysize)) + channel.frombytes( + self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation + ) + self.im.putband(channel.im, band) + + return -1, 0 + + +# +# registry + + +Image.register_decoder("SGI16", SGI16Decoder) +Image.register_open(SgiImageFile.format, SgiImageFile, _accept) +Image.register_save(SgiImageFile.format, _save) +Image.register_mime(SgiImageFile.format, "image/sgi") + +Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) + +# End of file diff --git a/venv/Lib/site-packages/PIL/SpiderImagePlugin.py b/venv/Lib/site-packages/PIL/SpiderImagePlugin.py new file mode 100644 index 0000000..1a72f5c --- /dev/null +++ b/venv/Lib/site-packages/PIL/SpiderImagePlugin.py @@ -0,0 +1,322 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html +# +import os +import struct +import sys + +from PIL import Image, ImageFile + + +def isInt(f): + try: + i = int(f) + if f - i == 0: + return 1 + else: + return 0 + except (ValueError, OverflowError): + return 0 + + +iforms = [1, 3, -11, -12, -21, -22] + + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no. of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1, 2, 5, 12, 13, 22, 23]: + if not isInt(h[i]): + return 0 + # check iform + iform = int(h[5]) + if iform not in iforms: + return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + if labbyt != (labrec * lenbyt): + return 0 + # looks like a valid header + return labbyt + + +def isSpiderImage(filename): + with open(filename, "rb") as fp: + f = fp.read(92) # read 23 * 4 bytes + t = struct.unpack(">23f", f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + t = struct.unpack("<23f", f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + _close_exclusive_fp_after_loading = False + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack(">27f", f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack("<27f", f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError("not a valid Spider file") + except struct.error as e: + raise SyntaxError("not a valid Spider file") from e + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError("not a Spider 2D image") + + self._size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self._nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self._nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError("inconsistent stack header values") + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + @property + def n_frames(self): + return self._nimages + + @property + def is_animated(self): + return self._nimages > 1 + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + raise EOFError("attempt to seek in a non-stack file") + if not self._seek_check(frame): + return + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (minimum, maximum) = self.getextrema() + m = 1 + if maximum != minimum: + m = depth / (maximum - minimum) + b = -m * minimum + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + from PIL import ImageTk + + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" + if filelist is None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print(f"unable to find {img}") + continue + try: + with Image.open(img) as im: + im = im.convert2byte() + except Exception: + if not isSpiderImage(img): + print(img + " is not a Spider image file") + continue + im.info["filename"] = img + imglist.append(im) + return imglist + + +# -------------------------------------------------------------------- +# For saving images in Spider format + + +def makeSpiderHeader(im): + nsam, nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = int(1024 / lenbyt) + if 1024 % lenbyt != 0: + labrec += 1 + labbyt = labrec * lenbyt + nvalues = int(labbyt / 4) + if nvalues < 23: + return [] + + hdr = [] + for i in range(nvalues): + hdr.append(0.0) + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[3] = float(nrow) # number of records in the image + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + return [struct.pack("f", v) for v in hdr] + + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert("F") + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise OSError("Error creating Spider header") + + # write the SPIDER header + fp.writelines(hdr) + + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) + + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + ext = os.path.splitext(filename)[1] + Image.register_extension(SpiderImageFile.format, ext) + _save(im, fp, filename) + + +# -------------------------------------------------------------------- + + +Image.register_open(SpiderImageFile.format, SpiderImageFile) +Image.register_save(SpiderImageFile.format, _save_spider) + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]") + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print("input image must be in Spider format") + sys.exit() + + with Image.open(filename) as im: + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=" ") + print(im.getextrema()) + + if len(sys.argv) > 2: + outfile = sys.argv[2] + + # perform some image operation + im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + print( + f"saving a flipped version of {os.path.basename(filename)} " + f"as {outfile} " + ) + im.save(outfile, SpiderImageFile.format) diff --git a/venv/Lib/site-packages/PIL/SunImagePlugin.py b/venv/Lib/site-packages/PIL/SunImagePlugin.py new file mode 100644 index 0000000..c03759a --- /dev/null +++ b/venv/Lib/site-packages/PIL/SunImagePlugin.py @@ -0,0 +1,136 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Sun image file handling +# +# History: +# 1995-09-10 fl Created +# 1996-05-28 fl Fixed 32-bit alignment +# 1998-12-29 fl Import ImagePalette module +# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995-1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +from . import Image, ImageFile, ImagePalette +from ._binary import i32be as i32 + + +def _accept(prefix): + return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 + + +## +# Image plugin for Sun raster files. + + +class SunImageFile(ImageFile.ImageFile): + + format = "SUN" + format_description = "Sun Raster File" + + def _open(self): + + # The Sun Raster file header is 32 bytes in length + # and has the following format: + + # typedef struct _SunRaster + # { + # DWORD MagicNumber; /* Magic (identification) number */ + # DWORD Width; /* Width of image in pixels */ + # DWORD Height; /* Height of image in pixels */ + # DWORD Depth; /* Number of bits per pixel */ + # DWORD Length; /* Size of image data in bytes */ + # DWORD Type; /* Type of raster file */ + # DWORD ColorMapType; /* Type of color map */ + # DWORD ColorMapLength; /* Size of the color map in bytes */ + # } SUNRASTER; + + # HEAD + s = self.fp.read(32) + if not _accept(s): + raise SyntaxError("not an SUN raster file") + + offset = 32 + + self._size = i32(s, 4), i32(s, 8) + + depth = i32(s, 12) + # data_length = i32(s, 16) # unreliable, ignore. + file_type = i32(s, 20) + palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary + palette_length = i32(s, 28) + + if depth == 1: + self.mode, rawmode = "1", "1;I" + elif depth == 4: + self.mode, rawmode = "L", "L;4" + elif depth == 8: + self.mode = rawmode = "L" + elif depth == 24: + if file_type == 3: + self.mode, rawmode = "RGB", "RGB" + else: + self.mode, rawmode = "RGB", "BGR" + elif depth == 32: + if file_type == 3: + self.mode, rawmode = "RGB", "RGBX" + else: + self.mode, rawmode = "RGB", "BGRX" + else: + raise SyntaxError("Unsupported Mode/Bit Depth") + + if palette_length: + if palette_length > 1024: + raise SyntaxError("Unsupported Color Palette Length") + + if palette_type != 1: + raise SyntaxError("Unsupported Palette Type") + + offset = offset + palette_length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) + if self.mode == "L": + self.mode = "P" + rawmode = rawmode.replace("L", "P") + + # 16 bit boundaries on stride + stride = ((self.size[0] * depth + 15) // 16) * 2 + + # file type: Type is the version (or flavor) of the bitmap + # file. The following values are typically found in the Type + # field: + # 0000h Old + # 0001h Standard + # 0002h Byte-encoded + # 0003h RGB format + # 0004h TIFF format + # 0005h IFF format + # FFFFh Experimental + + # Old and standard are the same, except for the length tag. + # byte-encoded is run-length-encoded + # RGB looks similar to standard, but RGB byte order + # TIFF and IFF mean that they were converted from T/IFF + # Experimental means that it's something else. + # (https://www.fileformat.info/format/sunraster/egff.htm) + + if file_type in (0, 1, 3, 4, 5): + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))] + elif file_type == 2: + self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)] + else: + raise SyntaxError("Unsupported Sun Raster file type") + + +# +# registry + + +Image.register_open(SunImageFile.format, SunImageFile, _accept) + +Image.register_extension(SunImageFile.format, ".ras") diff --git a/venv/Lib/site-packages/PIL/TarIO.py b/venv/Lib/site-packages/PIL/TarIO.py new file mode 100644 index 0000000..d108362 --- /dev/null +++ b/venv/Lib/site-packages/PIL/TarIO.py @@ -0,0 +1,65 @@ +# +# The Python Imaging Library. +# $Id$ +# +# read files from within a tar file +# +# History: +# 95-06-18 fl Created +# 96-05-28 fl Open files in binary mode +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-96. +# +# See the README file for information on usage and redistribution. +# + +import io + +from . import ContainerIO + + +class TarIO(ContainerIO.ContainerIO): + """A file object that provides read access to a given member of a TAR file.""" + + def __init__(self, tarfile, file): + """ + Create file object. + + :param tarfile: Name of TAR file. + :param file: Name of member file. + """ + self.fh = open(tarfile, "rb") + + while True: + + s = self.fh.read(512) + if len(s) != 512: + raise OSError("unexpected end of tar file") + + name = s[:100].decode("utf-8") + i = name.find("\0") + if i == 0: + raise OSError("cannot find subfile") + if i > 0: + name = name[:i] + + size = int(s[124:135], 8) + + if file == name: + break + + self.fh.seek((size + 511) & (~511), io.SEEK_CUR) + + # Open region + super().__init__(self.fh, self.fh.tell(), size) + + # Context manager support + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self.fh.close() diff --git a/venv/Lib/site-packages/PIL/TgaImagePlugin.py b/venv/Lib/site-packages/PIL/TgaImagePlugin.py new file mode 100644 index 0000000..59b89e9 --- /dev/null +++ b/venv/Lib/site-packages/PIL/TgaImagePlugin.py @@ -0,0 +1,253 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TGA file handling +# +# History: +# 95-09-01 fl created (reads 24-bit files only) +# 97-01-04 fl support more TGA versions, including compressed images +# 98-07-04 fl fixed orientation and alpha layer bugs +# 98-09-11 fl fixed orientation for runlength decoder +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +import warnings + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +# +# -------------------------------------------------------------------- +# Read RGA file + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (3, 16): "LA", + (2, 16): "BGR;5", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +## +# Image plugin for Targa files. + + +class TgaImageFile(ImageFile.ImageFile): + + format = "TGA" + format_description = "Targa" + + def _open(self): + + # process header + s = self.fp.read(18) + + id_len = s[0] + + colormaptype = s[1] + imagetype = s[2] + + depth = s[16] + + flags = s[17] + + self._size = i16(s, 12), i16(s, 14) + + # validate header fields + if ( + colormaptype not in (0, 1) + or self.size[0] <= 0 + or self.size[1] <= 0 + or depth not in (1, 8, 16, 24, 32) + ): + raise SyntaxError("not a TGA file") + + # image mode + if imagetype in (3, 11): + self.mode = "L" + if depth == 1: + self.mode = "1" # ??? + elif depth == 16: + self.mode = "LA" + elif imagetype in (1, 9): + self.mode = "P" + elif imagetype in (2, 10): + self.mode = "RGB" + if depth == 32: + self.mode = "RGBA" + else: + raise SyntaxError("unknown TGA mode") + + # orientation + orientation = flags & 0x30 + self._flip_horizontally = orientation in [0x10, 0x30] + if orientation in [0x20, 0x30]: + orientation = 1 + elif orientation in [0, 0x10]: + orientation = -1 + else: + raise SyntaxError("unknown TGA orientation") + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if id_len: + self.info["id_section"] = self.fp.read(id_len) + + if colormaptype: + # read palette + start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] + if mapdepth == 16: + self.palette = ImagePalette.raw( + "BGR;15", b"\0" * 2 * start + self.fp.read(2 * size) + ) + elif mapdepth == 24: + self.palette = ImagePalette.raw( + "BGR", b"\0" * 3 * start + self.fp.read(3 * size) + ) + elif mapdepth == 32: + self.palette = ImagePalette.raw( + "BGRA", b"\0" * 4 * start + self.fp.read(4 * size) + ) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype & 7, depth)] + if imagetype & 8: + # compressed + self.tile = [ + ( + "tga_rle", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, orientation, depth), + ) + ] + else: + self.tile = [ + ( + "raw", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, 0, orientation), + ) + ] + except KeyError: + pass # cannot decode + + def load_end(self): + if self._flip_horizontally: + self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + + +# +# -------------------------------------------------------------------- +# Write TGA file + + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "LA": ("LA", 16, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + + +def _save(im, fp, filename): + + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as TGA") from e + + if "rle" in im.encoderinfo: + rle = im.encoderinfo["rle"] + else: + compression = im.encoderinfo.get("compression", im.info.get("compression")) + rle = compression == "tga_rle" + if rle: + imagetype += 8 + + id_section = im.encoderinfo.get("id_section", im.info.get("id_section", "")) + id_len = len(id_section) + if id_len > 255: + id_len = 255 + id_section = id_section[:255] + warnings.warn("id_section has been trimmed to 255 characters") + + if colormaptype: + colormapfirst, colormaplength, colormapentry = 0, 256, 24 + else: + colormapfirst, colormaplength, colormapentry = 0, 0, 0 + + if im.mode in ("LA", "RGBA"): + flags = 8 + else: + flags = 0 + + orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1)) + if orientation > 0: + flags = flags | 0x20 + + fp.write( + o8(id_len) + + o8(colormaptype) + + o8(imagetype) + + o16(colormapfirst) + + o16(colormaplength) + + o8(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + o8(bits) + + o8(flags) + ) + + if id_section: + fp.write(id_section) + + if colormaptype: + fp.write(im.im.getpalette("RGB", "BGR")) + + if rle: + ImageFile._save( + im, fp, [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))] + ) + else: + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))] + ) + + # write targa version 2 footer + fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000") + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(TgaImageFile.format, TgaImageFile) +Image.register_save(TgaImageFile.format, _save) + +Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"]) + +Image.register_mime(TgaImageFile.format, "image/x-tga") diff --git a/venv/Lib/site-packages/PIL/TiffImagePlugin.py b/venv/Lib/site-packages/PIL/TiffImagePlugin.py new file mode 100644 index 0000000..e96f584 --- /dev/null +++ b/venv/Lib/site-packages/PIL/TiffImagePlugin.py @@ -0,0 +1,2112 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF file handling +# +# TIFF is a flexible, if somewhat aged, image file format originally +# defined by Aldus. Although TIFF supports a wide variety of pixel +# layouts and compression methods, the name doesn't really stand for +# "thousands of incompatible file formats," it just feels that way. +# +# To read TIFF data from a stream, the stream must be seekable. For +# progressive decoding, make sure to use TIFF files where the tag +# directory is placed first in the file. +# +# History: +# 1995-09-01 fl Created +# 1996-05-04 fl Handle JPEGTABLES tag +# 1996-05-18 fl Fixed COLORMAP support +# 1997-01-05 fl Fixed PREDICTOR support +# 1997-08-27 fl Added support for rational tags (from Perry Stoll) +# 1998-01-10 fl Fixed seek/tell (from Jan Blom) +# 1998-07-15 fl Use private names for internal variables +# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) +# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) +# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) +# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) +# 2001-12-18 fl Added workaround for broken Matrox library +# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) +# 2003-05-19 fl Check FILLORDER tag +# 2003-09-26 fl Added RGBa support +# 2004-02-24 fl Added DPI support; fixed rational write support +# 2005-02-07 fl Added workaround for broken Corel Draw 10 files +# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) +# +# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# +import io +import itertools +import logging +import math +import os +import struct +import warnings +from collections.abc import MutableMapping +from fractions import Fraction +from numbers import Number, Rational + +from . import Image, ImageFile, ImageOps, ImagePalette, TiffTags +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from .TiffTags import TYPES + +logger = logging.getLogger(__name__) + +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF = False +IFD_LEGACY_API = True +STRIP_SIZE = 65536 + +II = b"II" # little-endian (Intel style) +MM = b"MM" # big-endian (Motorola style) + +# +# -------------------------------------------------------------------- +# Read TIFF files + +# a few tag names, just to make the code below a bit more readable +IMAGEWIDTH = 256 +IMAGELENGTH = 257 +BITSPERSAMPLE = 258 +COMPRESSION = 259 +PHOTOMETRIC_INTERPRETATION = 262 +FILLORDER = 266 +IMAGEDESCRIPTION = 270 +STRIPOFFSETS = 273 +SAMPLESPERPIXEL = 277 +ROWSPERSTRIP = 278 +STRIPBYTECOUNTS = 279 +X_RESOLUTION = 282 +Y_RESOLUTION = 283 +PLANAR_CONFIGURATION = 284 +RESOLUTION_UNIT = 296 +TRANSFERFUNCTION = 301 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEWIDTH = 322 +TILELENGTH = 323 +TILEOFFSETS = 324 +TILEBYTECOUNTS = 325 +SUBIFD = 330 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +YCBCRSUBSAMPLING = 530 +REFERENCEBLACKWHITE = 532 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 +JPEGQUALITY = 65537 # pseudo-tag by libtiff + +# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +IMAGEJ_META_DATA_BYTE_COUNTS = 50838 +IMAGEJ_META_DATA = 50839 + +COMPRESSION_INFO = { + # Compression => pil compression name + 1: "raw", + 2: "tiff_ccitt", + 3: "group3", + 4: "group4", + 5: "tiff_lzw", + 6: "tiff_jpeg", # obsolete + 7: "jpeg", + 8: "tiff_adobe_deflate", + 32771: "tiff_raw_16", # 16-bit padding + 32773: "packbits", + 32809: "tiff_thunderscan", + 32946: "tiff_deflate", + 34676: "tiff_sgilog", + 34677: "tiff_sgilog24", + 34925: "lzma", + 50000: "zstd", + 50001: "webp", +} + +COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} + +OPEN_INFO = { + # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, + # ExtraSamples) => mode, rawmode + (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), + (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (MM, 1, (1,), 1, (8,), ()): ("L", "L"), + (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), + (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), + (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"), + (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"), + (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"), + (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), + (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), + (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"), + (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"), + (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"), + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (II, 3, (1,), 1, (8,), ()): ("P", "P"), + (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"), + # JPEG compressed images handled by LibTiff and auto-converted to RGBX + # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel + (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), + (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), +} + +PREFIXES = [ + b"MM\x00\x2A", # Valid TIFF header with big-endian byte order + b"II\x2A\x00", # Valid TIFF header with little-endian byte order + b"MM\x2A\x00", # Invalid TIFF header, assume big-endian + b"II\x00\x2A", # Invalid TIFF header, assume little-endian + b"MM\x00\x2B", # BigTIFF with big-endian byte order + b"II\x2B\x00", # BigTIFF with little-endian byte order +] + + +def _accept(prefix): + return prefix[:4] in PREFIXES + + +def _limit_rational(val, max_val): + inv = abs(val) > 1 + n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) + return n_d[::-1] if inv else n_d + + +def _limit_signed_rational(val, max_val, min_val): + frac = Fraction(val) + n_d = frac.numerator, frac.denominator + + if min(n_d) < min_val: + n_d = _limit_rational(val, abs(min_val)) + + if max(n_d) > max_val: + val = Fraction(*n_d) + n_d = _limit_rational(val, max_val) + + return n_d + + +## +# Wrapper for TIFF IFDs. + +_load_dispatch = {} +_write_dispatch = {} + + +class IFDRational(Rational): + """Implements a rational class where 0/0 is a legal value to match + the in the wild use of exif rationals. + + e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used + """ + + """ If the denominator is 0, store this as a float('nan'), otherwise store + as a fractions.Fraction(). Delegate as appropriate + + """ + + __slots__ = ("_numerator", "_denominator", "_val") + + def __init__(self, value, denominator=1): + """ + :param value: either an integer numerator, a + float/rational/other number, or an IFDRational + :param denominator: Optional integer denominator + """ + if isinstance(value, IFDRational): + self._numerator = value.numerator + self._denominator = value.denominator + self._val = value._val + return + + if isinstance(value, Fraction): + self._numerator = value.numerator + self._denominator = value.denominator + else: + self._numerator = value + self._denominator = denominator + + if denominator == 0: + self._val = float("nan") + elif denominator == 1: + self._val = Fraction(value) + else: + self._val = Fraction(value, denominator) + + @property + def numerator(a): + return a._numerator + + @property + def denominator(a): + return a._denominator + + def limit_rational(self, max_denominator): + """ + + :param max_denominator: Integer, the maximum denominator value + :returns: Tuple of (numerator, denominator) + """ + + if self.denominator == 0: + return (self.numerator, self.denominator) + + f = self._val.limit_denominator(max_denominator) + return (f.numerator, f.denominator) + + def __repr__(self): + return str(float(self._val)) + + def __hash__(self): + return self._val.__hash__() + + def __eq__(self, other): + val = self._val + if isinstance(other, IFDRational): + other = other._val + if isinstance(other, float): + val = float(val) + return val == other + + def __getstate__(self): + return [self._val, self._numerator, self._denominator] + + def __setstate__(self, state): + IFDRational.__init__(self, 0) + _val, _numerator, _denominator = state + self._val = _val + self._numerator = _numerator + self._denominator = _denominator + + def _delegate(op): + def delegate(self, *args): + return getattr(self._val, op)(*args) + + return delegate + + """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', + 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', + 'mod','rmod', 'pow','rpow', 'pos', 'neg', + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', + 'ceil', 'floor', 'round'] + print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) + """ + + __add__ = _delegate("__add__") + __radd__ = _delegate("__radd__") + __sub__ = _delegate("__sub__") + __rsub__ = _delegate("__rsub__") + __mul__ = _delegate("__mul__") + __rmul__ = _delegate("__rmul__") + __truediv__ = _delegate("__truediv__") + __rtruediv__ = _delegate("__rtruediv__") + __floordiv__ = _delegate("__floordiv__") + __rfloordiv__ = _delegate("__rfloordiv__") + __mod__ = _delegate("__mod__") + __rmod__ = _delegate("__rmod__") + __pow__ = _delegate("__pow__") + __rpow__ = _delegate("__rpow__") + __pos__ = _delegate("__pos__") + __neg__ = _delegate("__neg__") + __abs__ = _delegate("__abs__") + __trunc__ = _delegate("__trunc__") + __lt__ = _delegate("__lt__") + __gt__ = _delegate("__gt__") + __le__ = _delegate("__le__") + __ge__ = _delegate("__ge__") + __bool__ = _delegate("__bool__") + __ceil__ = _delegate("__ceil__") + __floor__ = _delegate("__floor__") + __round__ = _delegate("__round__") + + +class ImageFileDirectory_v2(MutableMapping): + """This class represents a TIFF tag directory. To speed things up, we + don't decode tags unless they're asked for. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v2() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + 'Some Data' + + Individual values are returned as the strings or numbers, sequences are + returned as tuples of the values. + + The tiff metadata type of each item is stored in a dictionary of + tag types in + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types + are read from a tiff file, guessed from the type added, or added + manually. + + Data Structures: + + * ``self.tagtype = {}`` + + * Key: numerical TIFF tag number + * Value: integer corresponding to the data type from + :py:data:`.TiffTags.TYPES` + + .. versionadded:: 3.0.0 + + 'Internal' data structures: + + * ``self._tags_v2 = {}`` + + * Key: numerical TIFF tag number + * Value: decoded data, as tuple for multiple values + + * ``self._tagdata = {}`` + + * Key: numerical TIFF tag number + * Value: undecoded byte string from file + + * ``self._tags_v1 = {}`` + + * Key: numerical TIFF tag number + * Value: decoded data in the v1 format + + Tags will be found in the private attributes ``self._tagdata``, and in + ``self._tags_v2`` once decoded. + + ``self.legacy_api`` is a value for internal use, and shouldn't be changed + from outside code. In cooperation with + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`, if ``legacy_api`` + is true, then decoded tags will be populated into both ``_tags_v1`` and + ``_tags_v2``. ``_tags_v2`` will be used if this IFD is used in the TIFF + save routine. Tags should be read from ``_tags_v1`` if + ``legacy_api == true``. + + """ + + def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None): + """Initialize an ImageFileDirectory. + + To construct an ImageFileDirectory from a real file, pass the 8-byte + magic header to the constructor. To only set the endianness, pass it + as the 'prefix' keyword argument. + + :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets + endianness. + :param prefix: Override the endianness of the file. + """ + if not _accept(ifh): + raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)") + self._prefix = prefix if prefix is not None else ifh[:2] + if self._prefix == MM: + self._endian = ">" + elif self._prefix == II: + self._endian = "<" + else: + raise SyntaxError("not a TIFF IFD") + self._bigtiff = ifh[2] == 43 + self.group = group + self.tagtype = {} + """ Dictionary of tag types """ + self.reset() + (self.next,) = ( + self._unpack("Q", ifh[8:]) if self._bigtiff else self._unpack("L", ifh[4:]) + ) + self._legacy_api = False + + prefix = property(lambda self: self._prefix) + offset = property(lambda self: self._offset) + legacy_api = property(lambda self: self._legacy_api) + + @legacy_api.setter + def legacy_api(self, value): + raise Exception("Not allowing setting of legacy api") + + def reset(self): + self._tags_v1 = {} # will remain empty if legacy_api is false + self._tags_v2 = {} # main tag storage + self._tagdata = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self._next = None + self._offset = None + + def __str__(self): + return str(dict(self)) + + def named(self): + """ + :returns: dict of name|key: value + + Returns the complete tag dictionary, with named tags where possible. + """ + return { + TiffTags.lookup(code, self.group).name: value + for code, value in self.items() + } + + def __len__(self): + return len(set(self._tagdata) | set(self._tags_v2)) + + def __getitem__(self, tag): + if tag not in self._tags_v2: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + self[tag] = handler(self, data, self.legacy_api) # check type + val = self._tags_v2[tag] + if self.legacy_api and not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + def __contains__(self, tag): + return tag in self._tags_v2 or tag in self._tagdata + + def __setitem__(self, tag, value): + self._setitem(tag, value, self.legacy_api) + + def _setitem(self, tag, value, legacy_api): + basetypes = (Number, bytes, str) + + info = TiffTags.lookup(tag, self.group) + values = [value] if isinstance(value, basetypes) else value + + if tag not in self.tagtype: + if info.type: + self.tagtype[tag] = info.type + else: + self.tagtype[tag] = TiffTags.UNDEFINED + if all(isinstance(v, IFDRational) for v in values): + self.tagtype[tag] = ( + TiffTags.RATIONAL + if all(v >= 0 for v in values) + else TiffTags.SIGNED_RATIONAL + ) + elif all(isinstance(v, int) for v in values): + if all(0 <= v < 2**16 for v in values): + self.tagtype[tag] = TiffTags.SHORT + elif all(-(2**15) < v < 2**15 for v in values): + self.tagtype[tag] = TiffTags.SIGNED_SHORT + else: + self.tagtype[tag] = ( + TiffTags.LONG + if all(v >= 0 for v in values) + else TiffTags.SIGNED_LONG + ) + elif all(isinstance(v, float) for v in values): + self.tagtype[tag] = TiffTags.DOUBLE + elif all(isinstance(v, str) for v in values): + self.tagtype[tag] = TiffTags.ASCII + elif all(isinstance(v, bytes) for v in values): + self.tagtype[tag] = TiffTags.BYTE + + if self.tagtype[tag] == TiffTags.UNDEFINED: + values = [ + v.encode("ascii", "replace") if isinstance(v, str) else v + for v in values + ] + elif self.tagtype[tag] == TiffTags.RATIONAL: + values = [float(v) if isinstance(v, int) else v for v in values] + + is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) + if not is_ifd: + values = tuple(info.cvt_enum(value) for value in values) + + dest = self._tags_v1 if legacy_api else self._tags_v2 + + # Three branches: + # Spec'd length == 1, Actual length 1, store as element + # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. + # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. + # Don't mess with the legacy api, since it's frozen. + if not is_ifd and ( + (info.length == 1) + or self.tagtype[tag] == TiffTags.BYTE + or (info.length is None and len(values) == 1 and not legacy_api) + ): + # Don't mess with the legacy api, since it's frozen. + if legacy_api and self.tagtype[tag] in [ + TiffTags.RATIONAL, + TiffTags.SIGNED_RATIONAL, + ]: # rationals + values = (values,) + try: + (dest[tag],) = values + except ValueError: + # We've got a builtin tag with 1 expected entry + warnings.warn( + f"Metadata Warning, tag {tag} had too many entries: " + f"{len(values)}, expected 1" + ) + dest[tag] = values[0] + + else: + # Spec'd length > 1 or undefined + # Unspec'd, and length > 1 + dest[tag] = values + + def __delitem__(self, tag): + self._tags_v2.pop(tag, None) + self._tags_v1.pop(tag, None) + self._tagdata.pop(tag, None) + + def __iter__(self): + return iter(set(self._tagdata) | set(self._tags_v2)) + + def _unpack(self, fmt, data): + return struct.unpack(self._endian + fmt, data) + + def _pack(self, fmt, *values): + return struct.pack(self._endian + fmt, *values) + + def _register_loader(idx, size): + def decorator(func): + from .TiffTags import TYPES + + if func.__name__.startswith("load_"): + TYPES[idx] = func.__name__[5:].replace("_", " ") + _load_dispatch[idx] = size, func # noqa: F821 + return func + + return decorator + + def _register_writer(idx): + def decorator(func): + _write_dispatch[idx] = func # noqa: F821 + return func + + return decorator + + def _register_basic(idx_fmt_name): + from .TiffTags import TYPES + + idx, fmt, name = idx_fmt_name + TYPES[idx] = name + size = struct.calcsize("=" + fmt) + _load_dispatch[idx] = ( # noqa: F821 + size, + lambda self, data, legacy_api=True: ( + self._unpack(f"{len(data) // size}{fmt}", data) + ), + ) + _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 + b"".join(self._pack(fmt, value) for value in values) + ) + + list( + map( + _register_basic, + [ + (TiffTags.SHORT, "H", "short"), + (TiffTags.LONG, "L", "long"), + (TiffTags.SIGNED_BYTE, "b", "signed byte"), + (TiffTags.SIGNED_SHORT, "h", "signed short"), + (TiffTags.SIGNED_LONG, "l", "signed long"), + (TiffTags.FLOAT, "f", "float"), + (TiffTags.DOUBLE, "d", "double"), + (TiffTags.IFD, "L", "long"), + (TiffTags.LONG8, "Q", "long8"), + ], + ) + ) + + @_register_loader(1, 1) # Basic type, except for the legacy API. + def load_byte(self, data, legacy_api=True): + return data + + @_register_writer(1) # Basic type, except for the legacy API. + def write_byte(self, data): + return data + + @_register_loader(2, 1) + def load_string(self, data, legacy_api=True): + if data.endswith(b"\0"): + data = data[:-1] + return data.decode("latin-1", "replace") + + @_register_writer(2) + def write_string(self, value): + # remerge of https://github.com/python-pillow/Pillow/pull/1416 + return b"" + value.encode("ascii", "replace") + b"\0" + + @_register_loader(5, 8) + def load_rational(self, data, legacy_api=True): + vals = self._unpack(f"{len(data) // 4}L", data) + + def combine(a, b): + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(5) + def write_rational(self, *values): + return b"".join( + self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values + ) + + @_register_loader(7, 1) + def load_undefined(self, data, legacy_api=True): + return data + + @_register_writer(7) + def write_undefined(self, value): + return value + + @_register_loader(10, 8) + def load_signed_rational(self, data, legacy_api=True): + vals = self._unpack(f"{len(data) // 4}l", data) + + def combine(a, b): + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(10) + def write_signed_rational(self, *values): + return b"".join( + self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) + for frac in values + ) + + def _ensure_read(self, fp, size): + ret = fp.read(size) + if len(ret) != size: + raise OSError( + "Corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(ret)}. " + ) + return ret + + def load(self, fp): + + self.reset() + self._offset = fp.tell() + + try: + tag_count = ( + self._unpack("Q", self._ensure_read(fp, 8)) + if self._bigtiff + else self._unpack("H", self._ensure_read(fp, 2)) + )[0] + for i in range(tag_count): + tag, typ, count, data = ( + self._unpack("HHQ8s", self._ensure_read(fp, 20)) + if self._bigtiff + else self._unpack("HHL4s", self._ensure_read(fp, 12)) + ) + + tagname = TiffTags.lookup(tag, self.group).name + typname = TYPES.get(typ, "unknown") + msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" + + try: + unit_size, handler = self._load_dispatch[typ] + except KeyError: + logger.debug(msg + f" - unsupported type {typ}") + continue # ignore unsupported type + size = count * unit_size + if size > (8 if self._bigtiff else 4): + here = fp.tell() + (offset,) = self._unpack("Q" if self._bigtiff else "L", data) + msg += f" Tag Location: {here} - Data Location: {offset}" + fp.seek(offset) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = data[:size] + + if len(data) != size: + warnings.warn( + "Possibly corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(data)}." + f" Skipping tag {tag}" + ) + logger.debug(msg) + continue + + if not data: + logger.debug(msg) + continue + + self._tagdata[tag] = data + self.tagtype[tag] = typ + + msg += " - value: " + ( + "" % size if size > 32 else repr(data) + ) + logger.debug(msg) + + (self.next,) = ( + self._unpack("Q", self._ensure_read(fp, 8)) + if self._bigtiff + else self._unpack("L", self._ensure_read(fp, 4)) + ) + except OSError as msg: + warnings.warn(str(msg)) + return + + def tobytes(self, offset=0): + # FIXME What about tagdata? + result = self._pack("H", len(self._tags_v2)) + + entries = [] + offset = offset + len(result) + len(self._tags_v2) * 12 + 4 + stripoffsets = None + + # pass 1: convert tags to binary format + # always write tags in ascending order + for tag, value in sorted(self._tags_v2.items()): + if tag == STRIPOFFSETS: + stripoffsets = len(entries) + typ = self.tagtype.get(tag) + logger.debug(f"Tag {tag}, Type: {typ}, Value: {repr(value)}") + is_ifd = typ == TiffTags.LONG and isinstance(value, dict) + if is_ifd: + if self._endian == "<": + ifh = b"II\x2A\x00\x08\x00\x00\x00" + else: + ifh = b"MM\x00\x2A\x00\x00\x00\x08" + ifd = ImageFileDirectory_v2(ifh, group=tag) + values = self._tags_v2[tag] + for ifd_tag, ifd_value in values.items(): + ifd[ifd_tag] = ifd_value + data = ifd.tobytes(offset) + else: + values = value if isinstance(value, tuple) else (value,) + data = self._write_dispatch[typ](self, *values) + + tagname = TiffTags.lookup(tag, self.group).name + typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") + msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})" + msg += " - value: " + ( + "" % len(data) if len(data) >= 16 else str(values) + ) + logger.debug(msg) + + # count is sum of lengths for string and arbitrary data + if is_ifd: + count = 1 + elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: + count = len(data) + else: + count = len(values) + # figure out if data fits into the entry + if len(data) <= 4: + entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) + else: + entries.append((tag, typ, count, self._pack("L", offset), data)) + offset += (len(data) + 1) // 2 * 2 # pad to word + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = entries[stripoffsets] + if data: + raise NotImplementedError("multistrip support not yet implemented") + value = self._pack("L", self._unpack("L", value)[0] + offset) + entries[stripoffsets] = tag, typ, count, value, data + + # pass 2: write entries to file + for tag, typ, count, value, data in entries: + logger.debug(f"{tag} {typ} {count} {repr(value)} {repr(data)}") + result += self._pack("HHL4s", tag, typ, count, value) + + # -- overwrite here for multi-page -- + result += b"\0\0\0\0" # end of entries + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in entries: + result += data + if len(data) & 1: + result += b"\0" + + return result + + def save(self, fp): + + if fp.tell() == 0: # skip TIFF header on subsequent pages + # tiff header -- PIL always starts the first IFD at offset 8 + fp.write(self._prefix + self._pack("HL", 42, 8)) + + offset = fp.tell() + result = self.tobytes(offset) + fp.write(result) + return offset + len(result) + + +ImageFileDirectory_v2._load_dispatch = _load_dispatch +ImageFileDirectory_v2._write_dispatch = _write_dispatch +for idx, name in TYPES.items(): + name = name.replace(" ", "_") + setattr(ImageFileDirectory_v2, "load_" + name, _load_dispatch[idx][1]) + setattr(ImageFileDirectory_v2, "write_" + name, _write_dispatch[idx]) +del _load_dispatch, _write_dispatch, idx, name + + +# Legacy ImageFileDirectory support. +class ImageFileDirectory_v1(ImageFileDirectory_v2): + """This class represents the **legacy** interface to a TIFF tag directory. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v1() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + ('Some Data',) + + Also contains a dictionary of tag types as read from the tiff image file, + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. + + Values are returned as a tuple. + + .. deprecated:: 3.0.0 + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._legacy_api = True + + tags = property(lambda self: self._tags_v1) + tagdata = property(lambda self: self._tagdata) + + # defined in ImageFileDirectory_v2 + tagtype: dict + """Dictionary of tag types""" + + @classmethod + def from_v2(cls, original): + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + + """ + + ifd = cls(prefix=original.prefix) + ifd._tagdata = original._tagdata + ifd.tagtype = original.tagtype + ifd.next = original.next # an indicator for multipage tiffs + return ifd + + def to_v2(self): + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + + """ + + ifd = ImageFileDirectory_v2(prefix=self.prefix) + ifd._tagdata = dict(self._tagdata) + ifd.tagtype = dict(self.tagtype) + ifd._tags_v2 = dict(self._tags_v2) + return ifd + + def __contains__(self, tag): + return tag in self._tags_v1 or tag in self._tagdata + + def __len__(self): + return len(set(self._tagdata) | set(self._tags_v1)) + + def __iter__(self): + return iter(set(self._tagdata) | set(self._tags_v1)) + + def __setitem__(self, tag, value): + for legacy_api in (False, True): + self._setitem(tag, value, legacy_api) + + def __getitem__(self, tag): + if tag not in self._tags_v1: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + for legacy in (False, True): + self._setitem(tag, handler(self, data, legacy), legacy) + val = self._tags_v1[tag] + if not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + +# undone -- switch this pointer when IFD_LEGACY_API == False +ImageFileDirectory = ImageFileDirectory_v1 + + +## +# Image plugin for TIFF files. + + +class TiffImageFile(ImageFile.ImageFile): + + format = "TIFF" + format_description = "Adobe TIFF" + _close_exclusive_fp_after_loading = False + + def __init__(self, fp=None, filename=None): + self.tag_v2 = None + """ Image file directory (tag dictionary) """ + + self.tag = None + """ Legacy tag entries """ + + super().__init__(fp, filename) + + def _open(self): + """Open the first image in a TIFF file""" + + # Header + ifh = self.fp.read(8) + if ifh[2] == 43: + ifh += self.fp.read(8) + + self.tag_v2 = ImageFileDirectory_v2(ifh) + + # legacy IFD entries will be filled in later + self.ifd = None + + # setup frame pointers + self.__first = self.__next = self.tag_v2.next + self.__frame = -1 + self.__fp = self.fp + self._frame_pos = [] + self._n_frames = None + + logger.debug("*** TiffImageFile._open ***") + logger.debug(f"- __first: {self.__first}") + logger.debug(f"- ifh: {repr(ifh)}") # Use repr to avoid str(bytes) + + # and load the first frame + self._seek(0) + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + self._seek(len(self._frame_pos)) + while self._n_frames is None: + self._seek(self.tell() + 1) + self.seek(current) + return self._n_frames + + def seek(self, frame): + """Select a given frame as current image""" + if not self._seek_check(frame): + return + self._seek(frame) + # Create a new core image object on second and + # subsequent frames in the image. Image may be + # different size/mode. + Image._decompression_bomb_check(self.size) + self.im = Image.core.new(self.mode, self.size) + + def _seek(self, frame): + self.fp = self.__fp + + # reset buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() + + while len(self._frame_pos) <= frame: + if not self.__next: + raise EOFError("no more images in TIFF file") + logger.debug( + f"Seeking to frame {frame}, on frame {self.__frame}, " + f"__next {self.__next}, location: {self.fp.tell()}" + ) + self.fp.seek(self.__next) + self._frame_pos.append(self.__next) + logger.debug("Loading tags, location: %s" % self.fp.tell()) + self.tag_v2.load(self.fp) + if self.tag_v2.next in self._frame_pos: + # This IFD has already been processed + # Declare this to be the end of the image + self.__next = 0 + else: + self.__next = self.tag_v2.next + if self.__next == 0: + self._n_frames = frame + 1 + if len(self._frame_pos) == 1: + self.is_animated = self.__next != 0 + self.__frame += 1 + self.fp.seek(self._frame_pos[frame]) + self.tag_v2.load(self.fp) + # fill the legacy tag/ifd entries + self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) + self.__frame = frame + self._setup() + + def tell(self): + """Return the current frame number""" + return self.__frame + + def getxmp(self): + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ + return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {} + + def get_photoshop_blocks(self): + """ + Returns a dictionary of Photoshop "Image Resource Blocks". + The keys are the image resource ID. For more information, see + https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727 + + :returns: Photoshop "Image Resource Blocks" in a dictionary. + """ + blocks = {} + val = self.tag_v2.get(0x8649) + if val: + while val[:4] == b"8BIM": + id = i16(val[4:6]) + n = math.ceil((val[6] + 1) / 2) * 2 + size = i32(val[6 + n : 10 + n]) + data = val[10 + n : 10 + n + size] + blocks[id] = {"data": data} + + val = val[math.ceil((10 + n + size) / 2) * 2 :] + return blocks + + def load(self): + if self.tile and self.use_load_libtiff: + return self._load_libtiff() + return super().load() + + def load_end(self): + if self._tile_orientation: + method = { + 2: Image.Transpose.FLIP_LEFT_RIGHT, + 3: Image.Transpose.ROTATE_180, + 4: Image.Transpose.FLIP_TOP_BOTTOM, + 5: Image.Transpose.TRANSPOSE, + 6: Image.Transpose.ROTATE_270, + 7: Image.Transpose.TRANSVERSE, + 8: Image.Transpose.ROTATE_90, + }.get(self._tile_orientation) + if method is not None: + self.im = self.im.transpose(method) + self._size = self.im.size + + # allow closing if we're on the first frame, there's no next + # This is the ImageFile.load path only, libtiff specific below. + if not self.is_animated: + self._close_exclusive_fp_after_loading = True + + # reset buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() + + # load IFD data from fp before it is closed + exif = self.getexif() + for key in TiffTags.TAGS_V2_GROUPS.keys(): + if key not in exif: + continue + exif.get_ifd(key) + + def _load_libtiff(self): + """Overload method triggered when we detect a compressed tiff + Calls out to libtiff""" + + Image.Image.load(self) + + self.load_prepare() + + if not len(self.tile) == 1: + raise OSError("Not exactly one tile") + + # (self._compression, (extents tuple), + # 0, (rawmode, self._compression, fp)) + extents = self.tile[0][1] + args = list(self.tile[0][3]) + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + # libtiff closes the file descriptor, so pass in a dup. + try: + fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno()) + # flush the file descriptor, prevents error on pypy 2.4+ + # should also eliminate the need for fp.tell + # in _seek + if hasattr(self.fp, "flush"): + self.fp.flush() + except OSError: + # io.BytesIO have a fileno, but returns an OSError if + # it doesn't use a file descriptor. + fp = False + + if fp: + args[2] = fp + + decoder = Image._getdecoder( + self.mode, "libtiff", tuple(args), self.decoderconfig + ) + try: + decoder.setimage(self.im, extents) + except ValueError as e: + raise OSError("Couldn't set the image") from e + + close_self_fp = self._exclusive_fp and not self.is_animated + if hasattr(self.fp, "getvalue"): + # We've got a stringio like thing passed in. Yay for all in memory. + # The decoder needs the entire file in one shot, so there's not + # a lot we can do here other than give it the entire file. + # unless we could do something like get the address of the + # underlying string for stringio. + # + # Rearranging for supporting byteio items, since they have a fileno + # that returns an OSError if there's no underlying fp. Easier to + # deal with here by reordering. + logger.debug("have getvalue. just sending in a string from getvalue") + n, err = decoder.decode(self.fp.getvalue()) + elif fp: + # we've got a actual file on disk, pass in the fp. + logger.debug("have fileno, calling fileno version of the decoder.") + if not close_self_fp: + self.fp.seek(0) + # 4 bytes, otherwise the trace might error out + n, err = decoder.decode(b"fpfp") + else: + # we have something else. + logger.debug("don't have fileno or getvalue. just reading") + self.fp.seek(0) + # UNDONE -- so much for that buffer size thing. + n, err = decoder.decode(self.fp.read()) + + if fp: + try: + os.close(fp) + except OSError: + pass + + self.tile = [] + self.readonly = 0 + + self.load_end() + + # libtiff closed the fp in a, we need to close self.fp, if possible + if close_self_fp: + self.fp.close() + self.fp = None # might be shared + + if err < 0: + raise OSError(err) + + return Image.Image.load(self) + + def _setup(self): + """Setup this image object based on current tags""" + + if 0xBC01 in self.tag_v2: + raise OSError("Windows Media Photo files not yet supported") + + # extract relevant tags + self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] + self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) + + # old style jpeg compression images most certainly are YCbCr + if self._compression == "tiff_jpeg": + photo = 6 + + fillorder = self.tag_v2.get(FILLORDER, 1) + + logger.debug("*** Summary ***") + logger.debug(f"- compression: {self._compression}") + logger.debug(f"- photometric_interpretation: {photo}") + logger.debug(f"- planar_configuration: {self._planar_configuration}") + logger.debug(f"- fill_order: {fillorder}") + logger.debug(f"- YCbCr subsampling: {self.tag.get(530)}") + + # size + xsize = int(self.tag_v2.get(IMAGEWIDTH)) + ysize = int(self.tag_v2.get(IMAGELENGTH)) + self._size = xsize, ysize + + logger.debug(f"- size: {self.size}") + + sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,)) + if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1: + # SAMPLEFORMAT is properly per band, so an RGB image will + # be (1,1,1). But, we don't support per band pixel types, + # and anything more than one band is a uint8. So, just + # take the first element. Revisit this if adding support + # for more exotic images. + sampleFormat = (1,) + + bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) + extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) + if photo in (2, 6, 8): # RGB, YCbCr, LAB + bps_count = 3 + elif photo == 5: # CMYK + bps_count = 4 + else: + bps_count = 1 + bps_count += len(extra_tuple) + bps_actual_count = len(bps_tuple) + if bps_count < bps_actual_count: + # If a file has more values in bps_tuple than expected, + # remove the excess. + bps_tuple = bps_tuple[:bps_count] + elif bps_count > bps_actual_count and bps_actual_count == 1: + # If a file has only one value in bps_tuple, when it should have more, + # presume it is the same number of bits for all of the samples. + bps_tuple = bps_tuple * bps_count + + samplesPerPixel = self.tag_v2.get( + SAMPLESPERPIXEL, + 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, + ) + if len(bps_tuple) != samplesPerPixel: + raise SyntaxError("unknown data organization") + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag_v2.prefix, + photo, + sampleFormat, + fillorder, + bps_tuple, + extra_tuple, + ) + logger.debug(f"format key: {key}") + try: + self.mode, rawmode = OPEN_INFO[key] + except KeyError as e: + logger.debug("- unsupported format") + raise SyntaxError("unknown pixel mode") from e + + logger.debug(f"- raw mode: {rawmode}") + logger.debug(f"- pil mode: {self.mode}") + + self.info["compression"] = self._compression + + xres = self.tag_v2.get(X_RESOLUTION, 1) + yres = self.tag_v2.get(Y_RESOLUTION, 1) + + if xres and yres: + resunit = self.tag_v2.get(RESOLUTION_UNIT) + if resunit == 2: # dots per inch + self.info["dpi"] = (xres, yres) + elif resunit == 3: # dots per centimeter. convert to dpi + self.info["dpi"] = (xres * 2.54, yres * 2.54) + elif resunit is None: # used to default to 1, but now 2) + self.info["dpi"] = (xres, yres) + # For backward compatibility, + # we also preserve the old behavior + self.info["resolution"] = xres, yres + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = layer = 0 + self.tile = [] + self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw" + if self.use_load_libtiff: + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # use the _load_libtiff function. + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-pillow/Pillow/issues/279 + if fillorder == 2: + # Replace fillorder with fillorder=1 + key = key[:3] + (1,) + key[4:] + logger.debug(f"format key: {key}") + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if rawmode == "I;16": + rawmode = "I;16N" + if ";16B" in rawmode: + rawmode = rawmode.replace(";16B", ";16N") + if ";16L" in rawmode: + rawmode = rawmode.replace(";16L", ";16N") + + # YCbCr images with new jpeg compression with pixels in one plane + # unpacked straight into RGB values + if ( + photo == 6 + and self._compression == "jpeg" + and self._planar_configuration == 1 + ): + rawmode = "RGB" + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, False, self.tag_v2.offset) + self.tile.append(("libtiff", (0, 0, xsize, ysize), 0, a)) + + elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: + # striped image + if STRIPOFFSETS in self.tag_v2: + offsets = self.tag_v2[STRIPOFFSETS] + h = self.tag_v2.get(ROWSPERSTRIP, ysize) + w = self.size[0] + else: + # tiled image + offsets = self.tag_v2[TILEOFFSETS] + w = self.tag_v2.get(322) + h = self.tag_v2.get(323) + + for offset in offsets: + if x + w > xsize: + stride = w * sum(bps_tuple) / 8 # bytes per line + else: + stride = 0 + + tile_rawmode = rawmode + if self._planar_configuration == 2: + # each band on it's own layer + tile_rawmode = rawmode[layer] + # adjust stride width accordingly + stride /= bps_count + + a = (tile_rawmode, int(stride), 1) + self.tile.append( + ( + self._compression, + (x, y, min(x + w, xsize), min(y + h, ysize)), + offset, + a, + ) + ) + x = x + w + if x >= self.size[0]: + x, y = 0, y + h + if y >= self.size[1]: + x = y = 0 + layer += 1 + else: + logger.debug("- unsupported data organization") + raise SyntaxError("unknown data organization") + + # Fix up info. + if ICCPROFILE in self.tag_v2: + self.info["icc_profile"] = self.tag_v2[ICCPROFILE] + + # fixup palette descriptor + + if self.mode in ["P", "PA"]: + palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) + + self._tile_orientation = self.tag_v2.get(0x0112) + + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + + +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with +# explicit big endian byte-order + +SAVE_INFO = { + # mode => rawmode, byteorder, photometrics, + # sampleformat, bitspersample, extra + "1": ("1", II, 1, 1, (1,), None), + "L": ("L", II, 1, 1, (8,), None), + "LA": ("LA", II, 1, 1, (8, 8), 2), + "P": ("P", II, 3, 1, (8,), None), + "PA": ("PA", II, 3, 1, (8, 8), 2), + "I": ("I;32S", II, 1, 2, (32,), None), + "I;16": ("I;16", II, 1, 1, (16,), None), + "I;16S": ("I;16S", II, 1, 2, (16,), None), + "F": ("F;32F", II, 1, 3, (32,), None), + "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), + "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), + "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), + "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), + "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), + "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), + "I;32BS": ("I;32BS", MM, 1, 2, (32,), None), + "I;16B": ("I;16B", MM, 1, 1, (16,), None), + "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), + "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), +} + + +def _save(im, fp, filename): + + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError as e: + raise OSError(f"cannot write mode {im.mode} as TIFF") from e + + ifd = ImageFileDirectory_v2(prefix=prefix) + + encoderinfo = im.encoderinfo + encoderconfig = im.encoderconfig + compression = encoderinfo.get("compression", im.info.get("compression")) + if compression is None: + compression = "raw" + elif compression == "tiff_jpeg": + # OJPEG is obsolete, so use new-style JPEG compression instead + compression = "jpeg" + elif compression == "tiff_deflate": + compression = "tiff_adobe_deflate" + + libtiff = WRITE_LIBTIFF or compression != "raw" + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = 1 + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # write any arbitrary tags passed in as an ImageFileDirectory + if "tiffinfo" in encoderinfo: + info = encoderinfo["tiffinfo"] + elif "exif" in encoderinfo: + info = encoderinfo["exif"] + if isinstance(info, bytes): + exif = Image.Exif() + exif.load(info) + info = exif + else: + info = {} + logger.debug("Tiffinfo Keys: %s" % list(info)) + if isinstance(info, ImageFileDirectory_v1): + info = info.to_v2() + for key in info: + if isinstance(info, Image.Exif) and key in TiffTags.TAGS_V2_GROUPS.keys(): + ifd[key] = info.get_ifd(key) + else: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except Exception: + pass # might not be an IFD. Might not have populated type + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, "tag_v2"): + # preserve tags from original TIFF image file + for key in ( + RESOLUTION_UNIT, + X_RESOLUTION, + Y_RESOLUTION, + IPTC_NAA_CHUNK, + PHOTOSHOP_CHUNK, + XMP, + ): + if key in im.tag_v2: + ifd[key] = im.tag_v2[key] + ifd.tagtype[key] = im.tag_v2.tagtype[key] + + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + icc = encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + ifd[ICCPROFILE] = icc + + for key, name in [ + (IMAGEDESCRIPTION, "description"), + (X_RESOLUTION, "resolution"), + (Y_RESOLUTION, "resolution"), + (X_RESOLUTION, "x_resolution"), + (Y_RESOLUTION, "y_resolution"), + (RESOLUTION_UNIT, "resolution_unit"), + (SOFTWARE, "software"), + (DATE_TIME, "date_time"), + (ARTIST, "artist"), + (COPYRIGHT, "copyright"), + ]: + if name in encoderinfo: + ifd[key] = encoderinfo[name] + + dpi = encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = dpi[0] + ifd[Y_RESOLUTION] = dpi[1] + + if bits != (1,): + ifd[BITSPERSAMPLE] = bits + if len(bits) != 1: + ifd[SAMPLESPERPIXEL] = len(bits) + if extra is not None: + ifd[EXTRASAMPLES] = extra + if format != 1: + ifd[SAMPLEFORMAT] = format + + if PHOTOMETRIC_INTERPRETATION not in ifd: + ifd[PHOTOMETRIC_INTERPRETATION] = photo + elif im.mode in ("1", "L") and ifd[PHOTOMETRIC_INTERPRETATION] == 0: + if im.mode == "1": + inverted_im = im.copy() + px = inverted_im.load() + for y in range(inverted_im.height): + for x in range(inverted_im.width): + px[x, y] = 0 if px[x, y] == 255 else 255 + im = inverted_im + else: + im = ImageOps.invert(im) + + if im.mode in ["P", "PA"]: + lut = im.im.getpalette("RGB", "RGB;L") + ifd[COLORMAP] = tuple(v * 256 for v in lut) + # data orientation + stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) + # aim for given strip size (64 KB by default) when using libtiff writer + if libtiff: + rows_per_strip = 1 if stride == 0 else min(STRIP_SIZE // stride, im.size[1]) + # JPEG encoder expects multiple of 8 rows + if compression == "jpeg": + rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, im.size[1]) + else: + rows_per_strip = im.size[1] + if rows_per_strip == 0: + rows_per_strip = 1 + strip_byte_counts = 1 if stride == 0 else stride * rows_per_strip + strips_per_image = (im.size[1] + rows_per_strip - 1) // rows_per_strip + ifd[ROWSPERSTRIP] = rows_per_strip + if strip_byte_counts >= 2**16: + ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG + ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + ( + stride * im.size[1] - strip_byte_counts * (strips_per_image - 1), + ) + ifd[STRIPOFFSETS] = tuple( + range(0, strip_byte_counts * strips_per_image, strip_byte_counts) + ) # this is adjusted by IFD writer + # no compression by default: + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) + + if im.mode == "YCbCr": + for tag, value in { + YCBCRSUBSAMPLING: (1, 1), + REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255), + }.items(): + ifd.setdefault(tag, value) + + blocklist = [TILEWIDTH, TILELENGTH, TILEOFFSETS, TILEBYTECOUNTS] + if libtiff: + if "quality" in encoderinfo: + quality = encoderinfo["quality"] + if not isinstance(quality, int) or quality < 0 or quality > 100: + raise ValueError("Invalid quality setting") + if compression != "jpeg": + raise ValueError( + "quality setting only supported for 'jpeg' compression" + ) + ifd[JPEGQUALITY] = quality + + logger.debug("Saving using libtiff encoder") + logger.debug("Items: %s" % sorted(ifd.items())) + _fp = 0 + if hasattr(fp, "fileno"): + try: + fp.seek(0) + _fp = os.dup(fp.fileno()) + except io.UnsupportedOperation: + pass + + # optional types for non core tags + types = {} + # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library + # based on the data in the strip. + # The other tags expect arrays with a certain length (fixed or depending on + # BITSPERSAMPLE, etc), passing arrays with a different length will result in + # segfaults. Block these tags until we add extra validation. + # SUBIFD may also cause a segfault. + blocklist += [ + REFERENCEBLACKWHITE, + STRIPBYTECOUNTS, + STRIPOFFSETS, + TRANSFERFUNCTION, + SUBIFD, + ] + + atts = {} + # bits per sample is a single short in the tiff directory, not a list. + atts[BITSPERSAMPLE] = bits[0] + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + legacy_ifd = {} + if hasattr(im, "tag"): + legacy_ifd = im.tag.to_v2() + + # SAMPLEFORMAT is determined by the image format and should not be copied + # from legacy_ifd. + supplied_tags = {**getattr(im, "tag_v2", {}), **legacy_ifd} + if SAMPLEFORMAT in supplied_tags: + del supplied_tags[SAMPLEFORMAT] + + for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): + # Libtiff can only process certain core items without adding + # them to the custom dictionary. + # Custom items are supported for int, float, unicode, string and byte + # values. Other types and tuples require a tagtype. + if tag not in TiffTags.LIBTIFF_CORE: + if not Image.core.libtiff_support_custom_tags: + continue + + if tag in ifd.tagtype: + types[tag] = ifd.tagtype[tag] + elif not (isinstance(value, (int, float, str, bytes))): + continue + else: + type = TiffTags.lookup(tag).type + if type: + types[tag] = type + if tag not in atts and tag not in blocklist: + if isinstance(value, str): + atts[tag] = value.encode("ascii", "replace") + b"\0" + elif isinstance(value, IFDRational): + atts[tag] = float(value) + else: + atts[tag] = value + + if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: + atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] + + logger.debug("Converted items: %s" % sorted(atts.items())) + + # libtiff always expects the bytes in native order. + # we're storing image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ("I;16B", "I;16"): + rawmode = "I;16N" + + # Pass tags as sorted list so that the tags are set in a fixed order. + # This is required by libtiff for some tags. For example, the JPEGQUALITY + # pseudo tag requires that the COMPRESS tag was already set. + tags = list(atts.items()) + tags.sort() + a = (rawmode, compression, _fp, filename, tags, types) + e = Image._getencoder(im.mode, "libtiff", a, encoderconfig) + e.setimage(im.im, (0, 0) + im.size) + while True: + # undone, change to self.decodermaxblock: + l, s, d = e.encode(16 * 1024) + if not _fp: + fp.write(d) + if s: + break + if s < 0: + raise OSError(f"encoder error {s} when writing image file") + + else: + for tag in blocklist: + del ifd[tag] + offset = ifd.save(fp) + + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))] + ) + + # -- helper for multi-page save -- + if "_debug_multipage" in encoderinfo: + # just to access o32 and o16 (using correct byte order) + im._debug_multipage = ifd + + +class AppendingTiffWriter: + fieldSizes = [ + 0, # None + 1, # byte + 1, # ascii + 2, # short + 4, # long + 8, # rational + 1, # sbyte + 1, # undefined + 2, # sshort + 4, # slong + 8, # srational + 4, # float + 8, # double + ] + + # StripOffsets = 273 + # FreeOffsets = 288 + # TileOffsets = 324 + # JPEGQTables = 519 + # JPEGDCTables = 520 + # JPEGACTables = 521 + Tags = {273, 288, 324, 519, 520, 521} + + def __init__(self, fn, new=False): + if hasattr(fn, "read"): + self.f = fn + self.close_fp = False + else: + self.name = fn + self.close_fp = True + try: + self.f = open(fn, "w+b" if new else "r+b") + except OSError: + self.f = open(fn, "w+b") + self.beginning = self.f.tell() + self.setup() + + def setup(self): + # Reset everything. + self.f.seek(self.beginning, os.SEEK_SET) + + self.whereToWriteNewIFDOffset = None + self.offsetOfNewPage = 0 + + self.IIMM = IIMM = self.f.read(4) + if not IIMM: + # empty file - first page + self.isFirst = True + return + + self.isFirst = False + if IIMM == b"II\x2a\x00": + self.setEndian("<") + elif IIMM == b"MM\x00\x2a": + self.setEndian(">") + else: + raise RuntimeError("Invalid TIFF file header") + + self.skipIFDs() + self.goToEnd() + + def finalize(self): + if self.isFirst: + return + + # fix offsets + self.f.seek(self.offsetOfNewPage) + + IIMM = self.f.read(4) + if not IIMM: + # raise RuntimeError("nothing written into new page") + # Make it easy to finish a frame without committing to a new one. + return + + if IIMM != self.IIMM: + raise RuntimeError("IIMM of new page doesn't match IIMM of first page") + + IFDoffset = self.readLong() + IFDoffset += self.offsetOfNewPage + self.f.seek(self.whereToWriteNewIFDOffset) + self.writeLong(IFDoffset) + self.f.seek(IFDoffset) + self.fixIFD() + + def newFrame(self): + # Call this to finish a frame. + self.finalize() + self.setup() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.close_fp: + self.close() + return False + + def tell(self): + return self.f.tell() - self.offsetOfNewPage + + def seek(self, offset, whence=io.SEEK_SET): + if whence == os.SEEK_SET: + offset += self.offsetOfNewPage + + self.f.seek(offset, whence) + return self.tell() + + def goToEnd(self): + self.f.seek(0, os.SEEK_END) + pos = self.f.tell() + + # pad to 16 byte boundary + padBytes = 16 - pos % 16 + if 0 < padBytes < 16: + self.f.write(bytes(padBytes)) + self.offsetOfNewPage = self.f.tell() + + def setEndian(self, endian): + self.endian = endian + self.longFmt = self.endian + "L" + self.shortFmt = self.endian + "H" + self.tagFormat = self.endian + "HHL" + + def skipIFDs(self): + while True: + IFDoffset = self.readLong() + if IFDoffset == 0: + self.whereToWriteNewIFDOffset = self.f.tell() - 4 + break + + self.f.seek(IFDoffset) + numTags = self.readShort() + self.f.seek(numTags * 12, os.SEEK_CUR) + + def write(self, data): + return self.f.write(data) + + def readShort(self): + (value,) = struct.unpack(self.shortFmt, self.f.read(2)) + return value + + def readLong(self): + (value,) = struct.unpack(self.longFmt, self.f.read(4)) + return value + + def rewriteLastShortToLong(self, value): + self.f.seek(-2, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def rewriteLastShort(self, value): + self.f.seek(-2, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) + if bytesWritten is not None and bytesWritten != 2: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + + def rewriteLastLong(self, value): + self.f.seek(-4, os.SEEK_CUR) + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def writeShort(self, value): + bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) + if bytesWritten is not None and bytesWritten != 2: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + + def writeLong(self, value): + bytesWritten = self.f.write(struct.pack(self.longFmt, value)) + if bytesWritten is not None and bytesWritten != 4: + raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + + def close(self): + self.finalize() + self.f.close() + + def fixIFD(self): + numTags = self.readShort() + + for i in range(numTags): + tag, fieldType, count = struct.unpack(self.tagFormat, self.f.read(8)) + + fieldSize = self.fieldSizes[fieldType] + totalSize = fieldSize * count + isLocal = totalSize <= 4 + if not isLocal: + offset = self.readLong() + offset += self.offsetOfNewPage + self.rewriteLastLong(offset) + + if tag in self.Tags: + curPos = self.f.tell() + + if isLocal: + self.fixOffsets( + count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + ) + self.f.seek(curPos + 4) + else: + self.f.seek(offset) + self.fixOffsets( + count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + ) + self.f.seek(curPos) + + offset = curPos = None + + elif isLocal: + # skip the locally stored value that is not an offset + self.f.seek(4, os.SEEK_CUR) + + def fixOffsets(self, count, isShort=False, isLong=False): + if not isShort and not isLong: + raise RuntimeError("offset is neither short nor long") + + for i in range(count): + offset = self.readShort() if isShort else self.readLong() + offset += self.offsetOfNewPage + if isShort and offset >= 65536: + # offset is now too large - we must convert shorts to longs + if count != 1: + raise RuntimeError("not implemented") # XXX TODO + + # simple case - the offset is just one and therefore it is + # local (not referenced with another offset) + self.rewriteLastShortToLong(offset) + self.f.seek(-10, os.SEEK_CUR) + self.writeShort(TiffTags.LONG) # rewrite the type to LONG + self.f.seek(8, os.SEEK_CUR) + elif isShort: + self.rewriteLastShort(offset) + else: + self.rewriteLastLong(offset) + + +def _save_all(im, fp, filename): + encoderinfo = im.encoderinfo.copy() + encoderconfig = im.encoderconfig + append_images = list(encoderinfo.get("append_images", [])) + if not hasattr(im, "n_frames") and not append_images: + return _save(im, fp, filename) + + cur_idx = im.tell() + try: + with AppendingTiffWriter(fp) as tf: + for ims in [im] + append_images: + ims.encoderinfo = encoderinfo + ims.encoderconfig = encoderconfig + if not hasattr(ims, "n_frames"): + nfr = 1 + else: + nfr = ims.n_frames + + for idx in range(nfr): + ims.seek(idx) + ims.load() + _save(ims, tf, filename) + tf.newFrame() + finally: + im.seek(cur_idx) + + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open(TiffImageFile.format, TiffImageFile, _accept) +Image.register_save(TiffImageFile.format, _save) +Image.register_save_all(TiffImageFile.format, _save_all) + +Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) + +Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/venv/Lib/site-packages/PIL/TiffTags.py b/venv/Lib/site-packages/PIL/TiffTags.py new file mode 100644 index 0000000..b37c8cf --- /dev/null +++ b/venv/Lib/site-packages/PIL/TiffTags.py @@ -0,0 +1,522 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF tags +# +# This module provides clear-text names for various well-known +# TIFF tags. the TIFF codec works just fine without it. +# +# Copyright (c) Secret Labs AB 1999. +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known TIFF tags. +## + +from collections import namedtuple + + +class TagInfo(namedtuple("_TagInfo", "value name type length enum")): + __slots__ = [] + + def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): + return super().__new__(cls, value, name, type, length, enum or {}) + + def cvt_enum(self, value): + # Using get will call hash(value), which can be expensive + # for some types (e.g. Fraction). Since self.enum is rarely + # used, it's usually better to test it first. + return self.enum.get(value, value) if self.enum else value + + +def lookup(tag, group=None): + """ + :param tag: Integer tag number + :returns: Taginfo namedtuple, From the TAGS_V2 info if possible, + otherwise just populating the value and name from TAGS. + If the tag is not recognized, "unknown" is returned for the name + + """ + + if group is not None: + info = TAGS_V2_GROUPS[group].get(tag) if group in TAGS_V2_GROUPS else None + else: + info = TAGS_V2.get(tag) + return info or TagInfo(tag, TAGS.get(tag, "unknown")) + + +## +# Map tag numbers to tag info. +# +# id: (Name, Type, Length, enum_values) +# +# The length here differs from the length in the tiff spec. For +# numbers, the tiff spec is for the number of fields returned. We +# agree here. For string-like types, the tiff spec uses the length of +# field in bytes. In Pillow, we are using the number of expected +# fields, in general 1 for string-like types. + + +BYTE = 1 +ASCII = 2 +SHORT = 3 +LONG = 4 +RATIONAL = 5 +SIGNED_BYTE = 6 +UNDEFINED = 7 +SIGNED_SHORT = 8 +SIGNED_LONG = 9 +SIGNED_RATIONAL = 10 +FLOAT = 11 +DOUBLE = 12 +IFD = 13 +LONG8 = 16 + +TAGS_V2 = { + 254: ("NewSubfileType", LONG, 1), + 255: ("SubfileType", SHORT, 1), + 256: ("ImageWidth", LONG, 1), + 257: ("ImageLength", LONG, 1), + 258: ("BitsPerSample", SHORT, 0), + 259: ( + "Compression", + SHORT, + 1, + { + "Uncompressed": 1, + "CCITT 1d": 2, + "Group 3 Fax": 3, + "Group 4 Fax": 4, + "LZW": 5, + "JPEG": 6, + "PackBits": 32773, + }, + ), + 262: ( + "PhotometricInterpretation", + SHORT, + 1, + { + "WhiteIsZero": 0, + "BlackIsZero": 1, + "RGB": 2, + "RGB Palette": 3, + "Transparency Mask": 4, + "CMYK": 5, + "YCbCr": 6, + "CieLAB": 8, + "CFA": 32803, # TIFF/EP, Adobe DNG + "LinearRaw": 32892, # Adobe DNG + }, + ), + 263: ("Threshholding", SHORT, 1), + 264: ("CellWidth", SHORT, 1), + 265: ("CellLength", SHORT, 1), + 266: ("FillOrder", SHORT, 1), + 269: ("DocumentName", ASCII, 1), + 270: ("ImageDescription", ASCII, 1), + 271: ("Make", ASCII, 1), + 272: ("Model", ASCII, 1), + 273: ("StripOffsets", LONG, 0), + 274: ("Orientation", SHORT, 1), + 277: ("SamplesPerPixel", SHORT, 1), + 278: ("RowsPerStrip", LONG, 1), + 279: ("StripByteCounts", LONG, 0), + 280: ("MinSampleValue", SHORT, 0), + 281: ("MaxSampleValue", SHORT, 0), + 282: ("XResolution", RATIONAL, 1), + 283: ("YResolution", RATIONAL, 1), + 284: ("PlanarConfiguration", SHORT, 1, {"Contiguous": 1, "Separate": 2}), + 285: ("PageName", ASCII, 1), + 286: ("XPosition", RATIONAL, 1), + 287: ("YPosition", RATIONAL, 1), + 288: ("FreeOffsets", LONG, 1), + 289: ("FreeByteCounts", LONG, 1), + 290: ("GrayResponseUnit", SHORT, 1), + 291: ("GrayResponseCurve", SHORT, 0), + 292: ("T4Options", LONG, 1), + 293: ("T6Options", LONG, 1), + 296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}), + 297: ("PageNumber", SHORT, 2), + 301: ("TransferFunction", SHORT, 0), + 305: ("Software", ASCII, 1), + 306: ("DateTime", ASCII, 1), + 315: ("Artist", ASCII, 1), + 316: ("HostComputer", ASCII, 1), + 317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}), + 318: ("WhitePoint", RATIONAL, 2), + 319: ("PrimaryChromaticities", RATIONAL, 6), + 320: ("ColorMap", SHORT, 0), + 321: ("HalftoneHints", SHORT, 2), + 322: ("TileWidth", LONG, 1), + 323: ("TileLength", LONG, 1), + 324: ("TileOffsets", LONG, 0), + 325: ("TileByteCounts", LONG, 0), + 332: ("InkSet", SHORT, 1), + 333: ("InkNames", ASCII, 1), + 334: ("NumberOfInks", SHORT, 1), + 336: ("DotRange", SHORT, 0), + 337: ("TargetPrinter", ASCII, 1), + 338: ("ExtraSamples", SHORT, 0), + 339: ("SampleFormat", SHORT, 0), + 340: ("SMinSampleValue", DOUBLE, 0), + 341: ("SMaxSampleValue", DOUBLE, 0), + 342: ("TransferRange", SHORT, 6), + 347: ("JPEGTables", UNDEFINED, 1), + # obsolete JPEG tags + 512: ("JPEGProc", SHORT, 1), + 513: ("JPEGInterchangeFormat", LONG, 1), + 514: ("JPEGInterchangeFormatLength", LONG, 1), + 515: ("JPEGRestartInterval", SHORT, 1), + 517: ("JPEGLosslessPredictors", SHORT, 0), + 518: ("JPEGPointTransforms", SHORT, 0), + 519: ("JPEGQTables", LONG, 0), + 520: ("JPEGDCTables", LONG, 0), + 521: ("JPEGACTables", LONG, 0), + 529: ("YCbCrCoefficients", RATIONAL, 3), + 530: ("YCbCrSubSampling", SHORT, 2), + 531: ("YCbCrPositioning", SHORT, 1), + 532: ("ReferenceBlackWhite", RATIONAL, 6), + 700: ("XMP", BYTE, 0), + 33432: ("Copyright", ASCII, 1), + 33723: ("IptcNaaInfo", UNDEFINED, 1), + 34377: ("PhotoshopInfo", BYTE, 0), + # FIXME add more tags here + 34665: ("ExifIFD", LONG, 1), + 34675: ("ICCProfile", UNDEFINED, 1), + 34853: ("GPSInfoIFD", LONG, 1), + 36864: ("ExifVersion", UNDEFINED, 1), + 40965: ("InteroperabilityIFD", LONG, 1), + 41730: ("CFAPattern", UNDEFINED, 1), + # MPInfo + 45056: ("MPFVersion", UNDEFINED, 1), + 45057: ("NumberOfImages", LONG, 1), + 45058: ("MPEntry", UNDEFINED, 1), + 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check + 45060: ("TotalFrames", LONG, 1), + 45313: ("MPIndividualNum", LONG, 1), + 45569: ("PanOrientation", LONG, 1), + 45570: ("PanOverlap_H", RATIONAL, 1), + 45571: ("PanOverlap_V", RATIONAL, 1), + 45572: ("BaseViewpointNum", LONG, 1), + 45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1), + 45574: ("BaselineLength", RATIONAL, 1), + 45575: ("VerticalDivergence", SIGNED_RATIONAL, 1), + 45576: ("AxisDistance_X", SIGNED_RATIONAL, 1), + 45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1), + 45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1), + 45579: ("YawAngle", SIGNED_RATIONAL, 1), + 45580: ("PitchAngle", SIGNED_RATIONAL, 1), + 45581: ("RollAngle", SIGNED_RATIONAL, 1), + 40960: ("FlashPixVersion", UNDEFINED, 1), + 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), + 50780: ("BestQualityScale", RATIONAL, 1), + 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one + 50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006 +} +TAGS_V2_GROUPS = { + # ExifIFD + 34665: { + 36864: ("ExifVersion", UNDEFINED, 1), + 40960: ("FlashPixVersion", UNDEFINED, 1), + 40965: ("InteroperabilityIFD", LONG, 1), + 41730: ("CFAPattern", UNDEFINED, 1), + }, + # GPSInfoIFD + 34853: {}, + # InteroperabilityIFD + 40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)}, +} + +# Legacy Tags structure +# these tags aren't included above, but were in the previous versions +TAGS = { + 347: "JPEGTables", + 700: "XMP", + # Additional Exif Info + 32932: "Wang Annotation", + 33434: "ExposureTime", + 33437: "FNumber", + 33445: "MD FileTag", + 33446: "MD ScalePixel", + 33447: "MD ColorTable", + 33448: "MD LabName", + 33449: "MD SampleInfo", + 33450: "MD PrepDate", + 33451: "MD PrepTime", + 33452: "MD FileUnits", + 33550: "ModelPixelScaleTag", + 33723: "IptcNaaInfo", + 33918: "INGR Packet Data Tag", + 33919: "INGR Flag Registers", + 33920: "IrasB Transformation Matrix", + 33922: "ModelTiepointTag", + 34264: "ModelTransformationTag", + 34377: "PhotoshopInfo", + 34735: "GeoKeyDirectoryTag", + 34736: "GeoDoubleParamsTag", + 34737: "GeoAsciiParamsTag", + 34850: "ExposureProgram", + 34852: "SpectralSensitivity", + 34855: "ISOSpeedRatings", + 34856: "OECF", + 34864: "SensitivityType", + 34865: "StandardOutputSensitivity", + 34866: "RecommendedExposureIndex", + 34867: "ISOSpeed", + 34868: "ISOSpeedLatitudeyyy", + 34869: "ISOSpeedLatitudezzz", + 34908: "HylaFAX FaxRecvParams", + 34909: "HylaFAX FaxSubAddress", + 34910: "HylaFAX FaxRecvTime", + 36864: "ExifVersion", + 36867: "DateTimeOriginal", + 36868: "DateTImeDigitized", + 37121: "ComponentsConfiguration", + 37122: "CompressedBitsPerPixel", + 37724: "ImageSourceData", + 37377: "ShutterSpeedValue", + 37378: "ApertureValue", + 37379: "BrightnessValue", + 37380: "ExposureBiasValue", + 37381: "MaxApertureValue", + 37382: "SubjectDistance", + 37383: "MeteringMode", + 37384: "LightSource", + 37385: "Flash", + 37386: "FocalLength", + 37396: "SubjectArea", + 37500: "MakerNote", + 37510: "UserComment", + 37520: "SubSec", + 37521: "SubSecTimeOriginal", + 37522: "SubsecTimeDigitized", + 40960: "FlashPixVersion", + 40961: "ColorSpace", + 40962: "PixelXDimension", + 40963: "PixelYDimension", + 40964: "RelatedSoundFile", + 40965: "InteroperabilityIFD", + 41483: "FlashEnergy", + 41484: "SpatialFrequencyResponse", + 41486: "FocalPlaneXResolution", + 41487: "FocalPlaneYResolution", + 41488: "FocalPlaneResolutionUnit", + 41492: "SubjectLocation", + 41493: "ExposureIndex", + 41495: "SensingMethod", + 41728: "FileSource", + 41729: "SceneType", + 41730: "CFAPattern", + 41985: "CustomRendered", + 41986: "ExposureMode", + 41987: "WhiteBalance", + 41988: "DigitalZoomRatio", + 41989: "FocalLengthIn35mmFilm", + 41990: "SceneCaptureType", + 41991: "GainControl", + 41992: "Contrast", + 41993: "Saturation", + 41994: "Sharpness", + 41995: "DeviceSettingDescription", + 41996: "SubjectDistanceRange", + 42016: "ImageUniqueID", + 42032: "CameraOwnerName", + 42033: "BodySerialNumber", + 42034: "LensSpecification", + 42035: "LensMake", + 42036: "LensModel", + 42037: "LensSerialNumber", + 42112: "GDAL_METADATA", + 42113: "GDAL_NODATA", + 42240: "Gamma", + 50215: "Oce Scanjob Description", + 50216: "Oce Application Selector", + 50217: "Oce Identification Number", + 50218: "Oce ImageLogic Characteristics", + # Adobe DNG + 50706: "DNGVersion", + 50707: "DNGBackwardVersion", + 50708: "UniqueCameraModel", + 50709: "LocalizedCameraModel", + 50710: "CFAPlaneColor", + 50711: "CFALayout", + 50712: "LinearizationTable", + 50713: "BlackLevelRepeatDim", + 50714: "BlackLevel", + 50715: "BlackLevelDeltaH", + 50716: "BlackLevelDeltaV", + 50717: "WhiteLevel", + 50718: "DefaultScale", + 50719: "DefaultCropOrigin", + 50720: "DefaultCropSize", + 50721: "ColorMatrix1", + 50722: "ColorMatrix2", + 50723: "CameraCalibration1", + 50724: "CameraCalibration2", + 50725: "ReductionMatrix1", + 50726: "ReductionMatrix2", + 50727: "AnalogBalance", + 50728: "AsShotNeutral", + 50729: "AsShotWhiteXY", + 50730: "BaselineExposure", + 50731: "BaselineNoise", + 50732: "BaselineSharpness", + 50733: "BayerGreenSplit", + 50734: "LinearResponseLimit", + 50735: "CameraSerialNumber", + 50736: "LensInfo", + 50737: "ChromaBlurRadius", + 50738: "AntiAliasStrength", + 50740: "DNGPrivateData", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50784: "Alias Layer Metadata", +} + + +def _populate(): + for k, v in TAGS_V2.items(): + # Populate legacy structure. + TAGS[k] = v[0] + if len(v) == 4: + for sk, sv in v[3].items(): + TAGS[(k, sv)] = sk + + TAGS_V2[k] = TagInfo(k, *v) + + for group, tags in TAGS_V2_GROUPS.items(): + for k, v in tags.items(): + tags[k] = TagInfo(k, *v) + + +_populate() +## +# Map type numbers to type names -- defined in ImageFileDirectory. + +TYPES = {} + +# was: +# TYPES = { +# 1: "byte", +# 2: "ascii", +# 3: "short", +# 4: "long", +# 5: "rational", +# 6: "signed byte", +# 7: "undefined", +# 8: "signed short", +# 9: "signed long", +# 10: "signed rational", +# 11: "float", +# 12: "double", +# } + +# +# These tags are handled by default in libtiff, without +# adding to the custom dictionary. From tif_dir.c, searching for +# case TIFFTAG in the _TIFFVSetField function: +# Line: item. +# 148: case TIFFTAG_SUBFILETYPE: +# 151: case TIFFTAG_IMAGEWIDTH: +# 154: case TIFFTAG_IMAGELENGTH: +# 157: case TIFFTAG_BITSPERSAMPLE: +# 181: case TIFFTAG_COMPRESSION: +# 202: case TIFFTAG_PHOTOMETRIC: +# 205: case TIFFTAG_THRESHHOLDING: +# 208: case TIFFTAG_FILLORDER: +# 214: case TIFFTAG_ORIENTATION: +# 221: case TIFFTAG_SAMPLESPERPIXEL: +# 228: case TIFFTAG_ROWSPERSTRIP: +# 238: case TIFFTAG_MINSAMPLEVALUE: +# 241: case TIFFTAG_MAXSAMPLEVALUE: +# 244: case TIFFTAG_SMINSAMPLEVALUE: +# 247: case TIFFTAG_SMAXSAMPLEVALUE: +# 250: case TIFFTAG_XRESOLUTION: +# 256: case TIFFTAG_YRESOLUTION: +# 262: case TIFFTAG_PLANARCONFIG: +# 268: case TIFFTAG_XPOSITION: +# 271: case TIFFTAG_YPOSITION: +# 274: case TIFFTAG_RESOLUTIONUNIT: +# 280: case TIFFTAG_PAGENUMBER: +# 284: case TIFFTAG_HALFTONEHINTS: +# 288: case TIFFTAG_COLORMAP: +# 294: case TIFFTAG_EXTRASAMPLES: +# 298: case TIFFTAG_MATTEING: +# 305: case TIFFTAG_TILEWIDTH: +# 316: case TIFFTAG_TILELENGTH: +# 327: case TIFFTAG_TILEDEPTH: +# 333: case TIFFTAG_DATATYPE: +# 344: case TIFFTAG_SAMPLEFORMAT: +# 361: case TIFFTAG_IMAGEDEPTH: +# 364: case TIFFTAG_SUBIFD: +# 376: case TIFFTAG_YCBCRPOSITIONING: +# 379: case TIFFTAG_YCBCRSUBSAMPLING: +# 383: case TIFFTAG_TRANSFERFUNCTION: +# 389: case TIFFTAG_REFERENCEBLACKWHITE: +# 393: case TIFFTAG_INKNAMES: + +# Following pseudo-tags are also handled by default in libtiff: +# TIFFTAG_JPEGQUALITY 65537 + +# some of these are not in our TAGS_V2 dict and were included from tiff.h + +# This list also exists in encode.c +LIBTIFF_CORE = { + 255, + 256, + 257, + 258, + 259, + 262, + 263, + 266, + 274, + 277, + 278, + 280, + 281, + 340, + 341, + 282, + 283, + 284, + 286, + 287, + 296, + 297, + 321, + 320, + 338, + 32995, + 322, + 323, + 32998, + 32996, + 339, + 32997, + 330, + 531, + 530, + 301, + 532, + 333, + # as above + 269, # this has been in our tests forever, and works + 65537, +} + +LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes +LIBTIFF_CORE.remove(322) # We don't have support for writing tiled images with libtiff +LIBTIFF_CORE.remove(323) # Tiled images +LIBTIFF_CORE.remove(333) # Ink Names either + +# Note to advanced users: There may be combinations of these +# parameters and values that when added properly, will work and +# produce valid tiff images that may work in your application. +# It is safe to add and remove tags from this set from Pillow's point +# of view so long as you test against libtiff. diff --git a/venv/Lib/site-packages/PIL/WalImageFile.py b/venv/Lib/site-packages/PIL/WalImageFile.py new file mode 100644 index 0000000..0dc695a --- /dev/null +++ b/venv/Lib/site-packages/PIL/WalImageFile.py @@ -0,0 +1,124 @@ +# +# The Python Imaging Library. +# $Id$ +# +# WAL file handling +# +# History: +# 2003-04-23 fl created +# +# Copyright (c) 2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +""" +This reader is based on the specification available from: +https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +and has been tested with a few sample files found using google. + +.. note:: + This format cannot be automatically recognized, so the reader + is not registered for use with :py:func:`PIL.Image.open()`. + To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. +""" + +from . import Image, ImageFile +from ._binary import i32le as i32 + + +class WalImageFile(ImageFile.ImageFile): + + format = "WAL" + format_description = "Quake2 Texture" + + def _open(self): + self.mode = "P" + + # read header fields + header = self.fp.read(32 + 24 + 32 + 12) + self._size = i32(header, 32), i32(header, 36) + Image._decompression_bomb_check(self.size) + + # load pixel data + offset = i32(header, 40) + self.fp.seek(offset) + + # strings are null-terminated + self.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56 : 56 + 32].split(b"\0", 1)[0] + if next_name: + self.info["next_name"] = next_name + + def load(self): + if not self.im: + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self.size[0] * self.size[1])) + self.putpalette(quake2palette) + return Image.Image.load(self) + + +def open(filename): + """ + Load texture from a Quake2 WAL texture file. + + By default, a Quake2 standard palette is attached to the texture. + To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method. + + :param filename: WAL file name, or an opened file handle. + :returns: An image instance. + """ + return WalImageFile(filename) + + +quake2palette = ( + # default palette taken from piffo 0.93 by Hans Häggström + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" +) diff --git a/venv/Lib/site-packages/PIL/WebPImagePlugin.py b/venv/Lib/site-packages/PIL/WebPImagePlugin.py new file mode 100644 index 0000000..7dd3f52 --- /dev/null +++ b/venv/Lib/site-packages/PIL/WebPImagePlugin.py @@ -0,0 +1,353 @@ +from io import BytesIO + +from . import Image, ImageFile + +try: + from . import _webp + + SUPPORTED = True +except ImportError: + SUPPORTED = False + + +_VALID_WEBP_MODES = {"RGBX": True, "RGBA": True, "RGB": True} + +_VALID_WEBP_LEGACY_MODES = {"RGB": True, "RGBA": True} + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless +} + + +def _accept(prefix): + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + if is_riff_file_format and is_webp_file and is_valid_vp8_mode: + if not SUPPORTED: + return ( + "image file could not be identified because WEBP support not installed" + ) + return True + + +class WebPImageFile(ImageFile.ImageFile): + + format = "WEBP" + format_description = "WebP image" + __loaded = 0 + __logical_frame = 0 + + def _open(self): + if not _webp.HAVE_WEBPANIM: + # Legacy mode + data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode( + self.fp.read() + ) + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + self._size = width, height + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + self.n_frames = 1 + self.is_animated = False + return + + # Use the newer AnimDecoder API to parse the (possibly) animated file, + # and access muxed chunks like ICC/EXIF/XMP. + self._decoder = _webp.WebPAnimDecoder(self.fp.read()) + + # Get info from decoder + width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() + self._size = width, height + self.info["loop"] = loop_count + bg_a, bg_r, bg_g, bg_b = ( + (bgcolor >> 24) & 0xFF, + (bgcolor >> 16) & 0xFF, + (bgcolor >> 8) & 0xFF, + bgcolor & 0xFF, + ) + self.info["background"] = (bg_r, bg_g, bg_b, bg_a) + self.n_frames = frame_count + self.is_animated = self.n_frames > 1 + self.mode = "RGB" if mode == "RGBX" else mode + self.rawmode = mode + self.tile = [] + + # Attempt to read ICC / EXIF / XMP chunks from file + icc_profile = self._decoder.get_chunk("ICCP") + exif = self._decoder.get_chunk("EXIF") + xmp = self._decoder.get_chunk("XMP ") + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + if xmp: + self.info["xmp"] = xmp + + # Initialize seek state + self._reset(reset=False) + + def _getexif(self): + if "exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + def seek(self, frame): + if not self._seek_check(frame): + return + + # Set logical frame to requested position + self.__logical_frame = frame + + def _reset(self, reset=True): + if reset: + self._decoder.reset() + self.__physical_frame = 0 + self.__loaded = -1 + self.__timestamp = 0 + + def _get_next(self): + # Get next frame + ret = self._decoder.get_next() + self.__physical_frame += 1 + + # Check if an error occurred + if ret is None: + self._reset() # Reset just to be safe + self.seek(0) + raise EOFError("failed to decode next frame in WebP file") + + # Compute duration + data, timestamp = ret + duration = timestamp - self.__timestamp + self.__timestamp = timestamp + + # libwebp gives frame end, adjust to start of frame + timestamp -= duration + return data, timestamp, duration + + def _seek(self, frame): + if self.__physical_frame == frame: + return # Nothing to do + if frame < self.__physical_frame: + self._reset() # Rewind to beginning + while self.__physical_frame < frame: + self._get_next() # Advance to the requested frame + + def load(self): + if _webp.HAVE_WEBPANIM: + if self.__loaded != self.__logical_frame: + self._seek(self.__logical_frame) + + # We need to load the image data for this frame + data, timestamp, duration = self._get_next() + self.info["timestamp"] = timestamp + self.info["duration"] = duration + self.__loaded = self.__logical_frame + + # Set tile + if self.fp and self._exclusive_fp: + self.fp.close() + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] + + return super().load() + + def tell(self): + if not _webp.HAVE_WEBPANIM: + return super().tell() + + return self.__logical_frame + + +def _save_all(im, fp, filename): + encoderinfo = im.encoderinfo.copy() + append_images = list(encoderinfo.get("append_images", [])) + + # If total frame count is 1, then save using the legacy API, which + # will preserve non-alpha modes + total = 0 + for ims in [im] + append_images: + total += getattr(ims, "n_frames", 1) + if total == 1: + _save(im, fp, filename) + return + + background = (0, 0, 0, 0) + if "background" in encoderinfo: + background = encoderinfo["background"] + elif "background" in im.info: + background = im.info["background"] + if isinstance(background, int): + # GifImagePlugin stores a global color table index in + # info["background"]. So it must be converted to an RGBA value + palette = im.getpalette() + if palette: + r, g, b = palette[background * 3 : (background + 1) * 3] + background = (r, g, b, 255) + else: + background = (background, background, background, 255) + + duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) + loop = im.encoderinfo.get("loop", 0) + minimize_size = im.encoderinfo.get("minimize_size", False) + kmin = im.encoderinfo.get("kmin", None) + kmax = im.encoderinfo.get("kmax", None) + allow_mixed = im.encoderinfo.get("allow_mixed", False) + verbose = False + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + method = im.encoderinfo.get("method", 0) + icc_profile = im.encoderinfo.get("icc_profile") or "" + exif = im.encoderinfo.get("exif", "") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + xmp = im.encoderinfo.get("xmp", "") + if allow_mixed: + lossless = False + + # Sensible keyframe defaults are from gif2webp.c script + if kmin is None: + kmin = 9 if lossless else 3 + if kmax is None: + kmax = 17 if lossless else 5 + + # Validate background color + if ( + not isinstance(background, (list, tuple)) + or len(background) != 4 + or not all(v >= 0 and v < 256 for v in background) + ): + raise OSError( + "Background color is not an RGBA tuple clamped to (0-255): %s" + % str(background) + ) + + # Convert to packed uint + bg_r, bg_g, bg_b, bg_a = background + background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) + + # Setup the WebP animation encoder + enc = _webp.WebPAnimEncoder( + im.size[0], + im.size[1], + background, + loop, + minimize_size, + kmin, + kmax, + allow_mixed, + verbose, + ) + + # Add each frame + frame_idx = 0 + timestamp = 0 + cur_idx = im.tell() + try: + for ims in [im] + append_images: + # Get # of frames in this image + nfr = getattr(ims, "n_frames", 1) + + for idx in range(nfr): + ims.seek(idx) + ims.load() + + # Make sure image mode is supported + frame = ims + rawmode = ims.mode + if ims.mode not in _VALID_WEBP_MODES: + alpha = ( + "A" in ims.mode + or "a" in ims.mode + or (ims.mode == "P" and "A" in ims.im.getpalettemode()) + ) + rawmode = "RGBA" if alpha else "RGB" + frame = ims.convert(rawmode) + + if rawmode == "RGB": + # For faster conversion, use RGBX + rawmode = "RGBX" + + # Append the frame to the animation encoder + enc.add( + frame.tobytes("raw", rawmode), + timestamp, + frame.size[0], + frame.size[1], + rawmode, + lossless, + quality, + method, + ) + + # Update timestamp and frame index + if isinstance(duration, (list, tuple)): + timestamp += duration[frame_idx] + else: + timestamp += duration + frame_idx += 1 + + finally: + im.seek(cur_idx) + + # Force encoder to flush frames + enc.add(None, timestamp, 0, 0, "", lossless, quality, 0) + + # Get the final output from the encoder + data = enc.assemble(icc_profile, exif, xmp) + if data is None: + raise OSError("cannot write file as WebP (encoder returned None)") + + fp.write(data) + + +def _save(im, fp, filename): + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + icc_profile = im.encoderinfo.get("icc_profile") or "" + exif = im.encoderinfo.get("exif", "") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + xmp = im.encoderinfo.get("xmp", "") + method = im.encoderinfo.get("method", 4) + + if im.mode not in _VALID_WEBP_LEGACY_MODES: + alpha = ( + "A" in im.mode + or "a" in im.mode + or (im.mode == "P" and "transparency" in im.info) + ) + im = im.convert("RGBA" if alpha else "RGB") + + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + lossless, + float(quality), + im.mode, + icc_profile, + method, + exif, + xmp, + ) + if data is None: + raise OSError("cannot write file as WebP (encoder returned None)") + + fp.write(data) + + +Image.register_open(WebPImageFile.format, WebPImageFile, _accept) +if SUPPORTED: + Image.register_save(WebPImageFile.format, _save) + if _webp.HAVE_WEBPANIM: + Image.register_save_all(WebPImageFile.format, _save_all) + Image.register_extension(WebPImageFile.format, ".webp") + Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/venv/Lib/site-packages/PIL/WmfImagePlugin.py b/venv/Lib/site-packages/PIL/WmfImagePlugin.py new file mode 100644 index 0000000..2f54cde --- /dev/null +++ b/venv/Lib/site-packages/PIL/WmfImagePlugin.py @@ -0,0 +1,177 @@ +# +# The Python Imaging Library +# $Id$ +# +# WMF stub codec +# +# history: +# 1996-12-14 fl Created +# 2004-02-22 fl Turned into a stub driver +# 2004-02-23 fl Added EMF support +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# +# WMF/EMF reference documentation: +# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf +# http://wvware.sourceforge.net/caolan/index.html +# http://wvware.sourceforge.net/caolan/ora-wmf.html + +from . import Image, ImageFile +from ._binary import i16le as word +from ._binary import si16le as short +from ._binary import si32le as _long + +_handler = None + + +def register_handler(handler): + """ + Install application-specific WMF image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler: + def open(self, im): + im.mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im): + im.fp.seek(0) # rewind + return Image.frombytes( + "RGB", + im.size, + Image.core.drawwmf(im.fp.read(), im.size, self.bbox), + "raw", + "BGR", + (im.size[0] * 3 + 3) & -4, + -1, + ) + + register_handler(WmfHandler()) + +# +# -------------------------------------------------------------------- +# Read WMF file + + +def _accept(prefix): + return ( + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00" + ) + + +## +# Image plugin for Windows metafiles. + + +class WmfStubImageFile(ImageFile.StubImageFile): + + format = "WMF" + format_description = "Windows Metafile" + + def _open(self): + self._inch = None + + # check placable header + s = self.fp.read(80) + + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": + + # placeable windows metafile + + # get units per inch + self._inch = word(s, 14) + + # get bounding box + x0 = short(s, 6) + y0 = short(s, 8) + x1 = short(s, 10) + y1 = short(s, 12) + + # normalize size to 72 dots per inch + self.info["dpi"] = 72 + size = ( + (x1 - x0) * self.info["dpi"] // self._inch, + (y1 - y0) * self.info["dpi"] // self._inch, + ) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + # sanity check (standard metafile header) + if s[22:26] != b"\x01\x00\t\x00": + raise SyntaxError("Unsupported WMF file format") + + elif s[:4] == b"\x01\x00\x00\x00" and s[40:44] == b" EMF": + # enhanced metafile + + # get bounding box + x0 = _long(s, 8) + y0 = _long(s, 12) + x1 = _long(s, 16) + y1 = _long(s, 20) + + # get frame (in 0.01 millimeter units) + frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) + + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = 2540.0 * (x1 - y0) / (frame[2] - frame[0]) + ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1]) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + if xdpi == ydpi: + self.info["dpi"] = xdpi + else: + self.info["dpi"] = xdpi, ydpi + + else: + raise SyntaxError("Unsupported file format") + + self.mode = "RGB" + self._size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + def load(self, dpi=None): + if dpi is not None and self._inch is not None: + self.info["dpi"] = dpi + x0, y0, x1, y1 = self.info["wmf_bbox"] + self._size = ( + (x1 - x0) * self.info["dpi"] // self._inch, + (y1 - y0) * self.info["dpi"] // self._inch, + ) + return super().load() + + +def _save(im, fp, filename): + if _handler is None or not hasattr(_handler, "save"): + raise OSError("WMF save handler not installed") + _handler.save(im, fp, filename) + + +# +# -------------------------------------------------------------------- +# Registry stuff + + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"]) diff --git a/venv/Lib/site-packages/PIL/XVThumbImagePlugin.py b/venv/Lib/site-packages/PIL/XVThumbImagePlugin.py new file mode 100644 index 0000000..4efedb7 --- /dev/null +++ b/venv/Lib/site-packages/PIL/XVThumbImagePlugin.py @@ -0,0 +1,78 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XV Thumbnail file handler by Charles E. "Gene" Cash +# (gcash@magicnet.net) +# +# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, +# available from ftp://ftp.cis.upenn.edu/pub/xv/ +# +# history: +# 98-08-15 cec created (b/w only) +# 98-12-09 cec added color palette +# 98-12-28 fl added to PIL (with only a few very minor modifications) +# +# To do: +# FIXME: make save work (this requires quantization support) +# + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +_MAGIC = b"P7 332" + +# standard color palette for thumbnails (RGB332) +PALETTE = b"" +for r in range(8): + for g in range(8): + for b in range(4): + PALETTE = PALETTE + ( + o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3) + ) + + +def _accept(prefix): + return prefix[:6] == _MAGIC + + +## +# Image plugin for XV thumbnail images. + + +class XVThumbImageFile(ImageFile.ImageFile): + + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self): + + # check magic + if not _accept(self.fp.read(6)): + raise SyntaxError("not an XV thumbnail file") + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != 35: # ie. when not a comment: '#' + break + + # parse header line (already read) + s = s.strip().split() + + self.mode = "P" + self._size = int(s[0]), int(s[1]) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1))] + + +# -------------------------------------------------------------------- + +Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/venv/Lib/site-packages/PIL/XbmImagePlugin.py b/venv/Lib/site-packages/PIL/XbmImagePlugin.py new file mode 100644 index 0000000..15379ce --- /dev/null +++ b/venv/Lib/site-packages/PIL/XbmImagePlugin.py @@ -0,0 +1,95 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XBM File handling +# +# History: +# 1995-09-08 fl Created +# 1996-11-01 fl Added save support +# 1997-07-07 fl Made header parser more tolerant +# 1997-07-22 fl Fixed yet another parser bug +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) +# 2004-02-24 fl Allow some whitespace before first #define +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import re + +from . import Image, ImageFile + +# XBM header +xbm_head = re.compile( + rb"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" +) + + +def _accept(prefix): + return prefix.lstrip()[:7] == b"#define" + + +## +# Image plugin for X11 bitmaps. + + +class XbmImageFile(ImageFile.ImageFile): + + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self): + + m = xbm_head.match(self.fp.read(512)) + + if not m: + raise SyntaxError("not a XBM file") + + xsize = int(m.group("width")) + ysize = int(m.group("height")) + + if m.group("hotspot"): + self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot"))) + + self.mode = "1" + self._size = xsize, ysize + + self.tile = [("xbm", (0, 0) + self.size, m.end(), None)] + + +def _save(im, fp, filename): + + if im.mode != "1": + raise OSError(f"cannot write mode {im.mode} as XBM") + + fp.write(f"#define im_width {im.size[0]}\n".encode("ascii")) + fp.write(f"#define im_height {im.size[1]}\n".encode("ascii")) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write(f"#define im_x_hot {hotspot[0]}\n".encode("ascii")) + fp.write(f"#define im_y_hot {hotspot[1]}\n".encode("ascii")) + + fp.write(b"static char im_bits[] = {\n") + + ImageFile._save(im, fp, [("xbm", (0, 0) + im.size, 0, None)]) + + fp.write(b"};\n") + + +Image.register_open(XbmImageFile.format, XbmImageFile, _accept) +Image.register_save(XbmImageFile.format, _save) + +Image.register_extension(XbmImageFile.format, ".xbm") + +Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/venv/Lib/site-packages/PIL/XpmImagePlugin.py b/venv/Lib/site-packages/PIL/XpmImagePlugin.py new file mode 100644 index 0000000..ebd65ba --- /dev/null +++ b/venv/Lib/site-packages/PIL/XpmImagePlugin.py @@ -0,0 +1,130 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XPM File handling +# +# History: +# 1996-12-29 fl Created +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +import re + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +# XPM header +xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') + + +def _accept(prefix): + return prefix[:9] == b"/* XPM */" + + +## +# Image plugin for X11 pixel maps. + + +class XpmImageFile(ImageFile.ImageFile): + + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self): + + if not _accept(self.fp.read(9)): + raise SyntaxError("not an XPM file") + + # skip forward to next string + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("broken XPM file") + m = xpm_head.match(s) + if m: + break + + self._size = int(m.group(1)), int(m.group(2)) + + pal = int(m.group(3)) + bpp = int(m.group(4)) + + if pal > 256 or bpp != 1: + raise ValueError("cannot read this XPM file") + + # + # load palette description + + palette = [b"\0\0\0"] * 256 + + for i in range(pal): + + s = self.fp.readline() + if s[-2:] == b"\r\n": + s = s[:-2] + elif s[-1:] in b"\r\n": + s = s[:-1] + + c = s[1] + s = s[2:-2].split() + + for i in range(0, len(s), 2): + + if s[i] == b"c": + + # process colour key + rgb = s[i + 1] + if rgb == b"None": + self.info["transparency"] = c + elif rgb[0:1] == b"#": + # FIXME: handle colour names (see ImagePalette.py) + rgb = int(rgb[1:], 16) + palette[c] = ( + o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255) + ) + else: + # unknown colour + raise ValueError("cannot read this XPM file") + break + + else: + + # missing colour key + raise ValueError("cannot read this XPM file") + + self.mode = "P" + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))] + + def load_read(self, bytes): + + # + # load all image data in one chunk + + xsize, ysize = self.size + + s = [None] * ysize + + for i in range(ysize): + s[i] = self.fp.readline()[1 : xsize + 1].ljust(xsize) + + return b"".join(s) + + +# +# Registry + + +Image.register_open(XpmImageFile.format, XpmImageFile, _accept) + +Image.register_extension(XpmImageFile.format, ".xpm") + +Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/venv/Lib/site-packages/PIL/__init__.py b/venv/Lib/site-packages/PIL/__init__.py new file mode 100644 index 0000000..e65b155 --- /dev/null +++ b/venv/Lib/site-packages/PIL/__init__.py @@ -0,0 +1,80 @@ +"""Pillow (Fork of the Python Imaging Library) + +Pillow is the friendly PIL fork by Alex Clark and Contributors. + https://github.com/python-pillow/Pillow/ + +Pillow is forked from PIL 1.1.7. + +PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +Copyright (c) 1999 by Secret Labs AB. + +Use PIL.__version__ for this Pillow version. + +;-) +""" + +from . import _version + +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION was removed in Pillow 9.0.0. +# Use __version__ instead. +__version__ = _version.__version__ +del _version + + +_plugins = [ + "BlpImagePlugin", + "BmpImagePlugin", + "BufrStubImagePlugin", + "CurImagePlugin", + "DcxImagePlugin", + "DdsImagePlugin", + "EpsImagePlugin", + "FitsImagePlugin", + "FitsStubImagePlugin", + "FliImagePlugin", + "FpxImagePlugin", + "FtexImagePlugin", + "GbrImagePlugin", + "GifImagePlugin", + "GribStubImagePlugin", + "Hdf5StubImagePlugin", + "IcnsImagePlugin", + "IcoImagePlugin", + "ImImagePlugin", + "ImtImagePlugin", + "IptcImagePlugin", + "JpegImagePlugin", + "Jpeg2KImagePlugin", + "McIdasImagePlugin", + "MicImagePlugin", + "MpegImagePlugin", + "MpoImagePlugin", + "MspImagePlugin", + "PalmImagePlugin", + "PcdImagePlugin", + "PcxImagePlugin", + "PdfImagePlugin", + "PixarImagePlugin", + "PngImagePlugin", + "PpmImagePlugin", + "PsdImagePlugin", + "SgiImagePlugin", + "SpiderImagePlugin", + "SunImagePlugin", + "TgaImagePlugin", + "TiffImagePlugin", + "WebPImagePlugin", + "WmfImagePlugin", + "XbmImagePlugin", + "XpmImagePlugin", + "XVThumbImagePlugin", +] + + +class UnidentifiedImageError(OSError): + """ + Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. + """ + + pass diff --git a/venv/Lib/site-packages/PIL/__main__.py b/venv/Lib/site-packages/PIL/__main__.py new file mode 100644 index 0000000..a05323f --- /dev/null +++ b/venv/Lib/site-packages/PIL/__main__.py @@ -0,0 +1,3 @@ +from .features import pilinfo + +pilinfo() diff --git a/venv/Lib/site-packages/PIL/_binary.py b/venv/Lib/site-packages/PIL/_binary.py new file mode 100644 index 0000000..a74ee9e --- /dev/null +++ b/venv/Lib/site-packages/PIL/_binary.py @@ -0,0 +1,102 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + + +"""Binary input/output support routines.""" + + +from struct import pack, unpack_from + + +def i8(c): + return c if c.__class__ is int else c[0] + + +def o8(i): + return bytes((i & 255,)) + + +# Input, le = little endian, be = big endian +def i16le(c, o=0): + """ + Converts a 2-bytes (16 bits) string to an unsigned integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from("h", c, o)[0] + + +def i32le(c, o=0): + """ + Converts a 4-bytes (32 bits) string to an unsigned integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from("H", c, o)[0] + + +def i32be(c, o=0): + return unpack_from(">I", c, o)[0] + + +# Output, le = little endian, be = big endian +def o16le(i): + return pack("H", i) + + +def o32be(i): + return pack(">I", i) diff --git a/venv/Lib/site-packages/PIL/_imaging.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imaging.cp39-win_amd64.pyd new file mode 100644 index 0000000..627da43 Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imaging.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_imagingcms.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imagingcms.cp39-win_amd64.pyd new file mode 100644 index 0000000..9cd964a Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imagingcms.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_imagingft.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imagingft.cp39-win_amd64.pyd new file mode 100644 index 0000000..53ae545 Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imagingft.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_imagingmath.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imagingmath.cp39-win_amd64.pyd new file mode 100644 index 0000000..f4d4e7f Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imagingmath.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_imagingmorph.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imagingmorph.cp39-win_amd64.pyd new file mode 100644 index 0000000..0339847 Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imagingmorph.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_imagingtk.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_imagingtk.cp39-win_amd64.pyd new file mode 100644 index 0000000..da53933 Binary files /dev/null and b/venv/Lib/site-packages/PIL/_imagingtk.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/_tkinter_finder.py b/venv/Lib/site-packages/PIL/_tkinter_finder.py new file mode 100644 index 0000000..5253f07 --- /dev/null +++ b/venv/Lib/site-packages/PIL/_tkinter_finder.py @@ -0,0 +1,25 @@ +""" Find compiled module linking to Tcl / Tk libraries +""" +import sys +import tkinter +import warnings +from tkinter import _tkinter as tk + +try: + if hasattr(sys, "pypy_find_executable"): + TKINTER_LIB = tk.tklib_cffi.__file__ + else: + TKINTER_LIB = tk.__file__ +except AttributeError: + # _tkinter may be compiled directly into Python, in which case __file__ is + # not available. load_tkinter_funcs will check the binary first in any case. + TKINTER_LIB = None + +tk_version = str(tkinter.TkVersion) +if tk_version == "8.4": + warnings.warn( + "Support for Tk/Tcl 8.4 is deprecated and will be removed" + " in Pillow 10 (2023-07-01). Please upgrade to Tk/Tcl 8.5 " + "or newer.", + DeprecationWarning, + ) diff --git a/venv/Lib/site-packages/PIL/_util.py b/venv/Lib/site-packages/PIL/_util.py new file mode 100644 index 0000000..0c5d389 --- /dev/null +++ b/venv/Lib/site-packages/PIL/_util.py @@ -0,0 +1,19 @@ +import os +from pathlib import Path + + +def isPath(f): + return isinstance(f, (bytes, str, Path)) + + +# Checks if an object is a string, and that it points to a directory. +def isDirectory(f): + return isPath(f) and os.path.isdir(f) + + +class deferred_error: + def __init__(self, ex): + self.ex = ex + + def __getattr__(self, elt): + raise self.ex diff --git a/venv/Lib/site-packages/PIL/_version.py b/venv/Lib/site-packages/PIL/_version.py new file mode 100644 index 0000000..62708c7 --- /dev/null +++ b/venv/Lib/site-packages/PIL/_version.py @@ -0,0 +1,2 @@ +# Master version for Pillow +__version__ = "9.1.0" diff --git a/venv/Lib/site-packages/PIL/_webp.cp39-win_amd64.pyd b/venv/Lib/site-packages/PIL/_webp.cp39-win_amd64.pyd new file mode 100644 index 0000000..b5a8181 Binary files /dev/null and b/venv/Lib/site-packages/PIL/_webp.cp39-win_amd64.pyd differ diff --git a/venv/Lib/site-packages/PIL/concrt140.dll b/venv/Lib/site-packages/PIL/concrt140.dll new file mode 100644 index 0000000..9752449 Binary files /dev/null and b/venv/Lib/site-packages/PIL/concrt140.dll differ diff --git a/venv/Lib/site-packages/PIL/features.py b/venv/Lib/site-packages/PIL/features.py new file mode 100644 index 0000000..3838568 --- /dev/null +++ b/venv/Lib/site-packages/PIL/features.py @@ -0,0 +1,320 @@ +import collections +import os +import sys +import warnings + +import PIL + +from . import Image + +modules = { + "pil": ("PIL._imaging", "PILLOW_VERSION"), + "tkinter": ("PIL._tkinter_finder", "tk_version"), + "freetype2": ("PIL._imagingft", "freetype2_version"), + "littlecms2": ("PIL._imagingcms", "littlecms_version"), + "webp": ("PIL._webp", "webpdecoder_version"), +} + + +def check_module(feature): + """ + Checks if a module is available. + + :param feature: The module to check for. + :returns: ``True`` if available, ``False`` otherwise. + :raises ValueError: If the module is not defined in this version of Pillow. + """ + if not (feature in modules): + raise ValueError(f"Unknown module {feature}") + + module, ver = modules[feature] + + try: + __import__(module) + return True + except ImportError: + return False + + +def version_module(feature): + """ + :param feature: The module to check for. + :returns: + The loaded version number as a string, or ``None`` if unknown or not available. + :raises ValueError: If the module is not defined in this version of Pillow. + """ + if not check_module(feature): + return None + + module, ver = modules[feature] + + if ver is None: + return None + + return getattr(__import__(module, fromlist=[ver]), ver) + + +def get_supported_modules(): + """ + :returns: A list of all supported modules. + """ + return [f for f in modules if check_module(f)] + + +codecs = { + "jpg": ("jpeg", "jpeglib"), + "jpg_2000": ("jpeg2k", "jp2klib"), + "zlib": ("zip", "zlib"), + "libtiff": ("libtiff", "libtiff"), +} + + +def check_codec(feature): + """ + Checks if a codec is available. + + :param feature: The codec to check for. + :returns: ``True`` if available, ``False`` otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ + if feature not in codecs: + raise ValueError(f"Unknown codec {feature}") + + codec, lib = codecs[feature] + + return codec + "_encoder" in dir(Image.core) + + +def version_codec(feature): + """ + :param feature: The codec to check for. + :returns: + The version number as a string, or ``None`` if not available. + Checked at compile time for ``jpg``, run-time otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ + if not check_codec(feature): + return None + + codec, lib = codecs[feature] + + version = getattr(Image.core, lib + "_version") + + if feature == "libtiff": + return version.split("\n")[0].split("Version ")[1] + + return version + + +def get_supported_codecs(): + """ + :returns: A list of all supported codecs. + """ + return [f for f in codecs if check_codec(f)] + + +features = { + "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), + "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), + "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None), + "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), + "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), + "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), + "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), + "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), + "xcb": ("PIL._imaging", "HAVE_XCB", None), +} + + +def check_feature(feature): + """ + Checks if a feature is available. + + :param feature: The feature to check for. + :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + if feature not in features: + raise ValueError(f"Unknown feature {feature}") + + module, flag, ver = features[feature] + + try: + imported_module = __import__(module, fromlist=["PIL"]) + return getattr(imported_module, flag) + except ImportError: + return None + + +def version_feature(feature): + """ + :param feature: The feature to check for. + :returns: The version number as a string, or ``None`` if not available. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + if not check_feature(feature): + return None + + module, flag, ver = features[feature] + + if ver is None: + return None + + return getattr(__import__(module, fromlist=[ver]), ver) + + +def get_supported_features(): + """ + :returns: A list of all supported features. + """ + return [f for f in features if check_feature(f)] + + +def check(feature): + """ + :param feature: A module, codec, or feature name. + :returns: + ``True`` if the module, codec, or feature is available, + ``False`` or ``None`` otherwise. + """ + + if feature in modules: + return check_module(feature) + if feature in codecs: + return check_codec(feature) + if feature in features: + return check_feature(feature) + warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) + return False + + +def version(feature): + """ + :param feature: + The module, codec, or feature to check for. + :returns: + The version number as a string, or ``None`` if unknown or not available. + """ + if feature in modules: + return version_module(feature) + if feature in codecs: + return version_codec(feature) + if feature in features: + return version_feature(feature) + return None + + +def get_supported(): + """ + :returns: A list of all supported modules, features, and codecs. + """ + + ret = get_supported_modules() + ret.extend(get_supported_features()) + ret.extend(get_supported_codecs()) + return ret + + +def pilinfo(out=None, supported_formats=True): + """ + Prints information about this installation of Pillow. + This function can be called with ``python3 -m PIL``. + + :param out: + The output stream to print to. Defaults to ``sys.stdout`` if ``None``. + :param supported_formats: + If ``True``, a list of all supported image file formats will be printed. + """ + + if out is None: + out = sys.stdout + + Image.init() + + print("-" * 68, file=out) + print(f"Pillow {PIL.__version__}", file=out) + py_version = sys.version.splitlines() + print(f"Python {py_version[0].strip()}", file=out) + for py_version in py_version[1:]: + print(f" {py_version.strip()}", file=out) + print("-" * 68, file=out) + print( + f"Python modules loaded from {os.path.dirname(Image.__file__)}", + file=out, + ) + print( + f"Binary modules loaded from {os.path.dirname(Image.core.__file__)}", + file=out, + ) + print("-" * 68, file=out) + + for name, feature in [ + ("pil", "PIL CORE"), + ("tkinter", "TKINTER"), + ("freetype2", "FREETYPE2"), + ("littlecms2", "LITTLECMS2"), + ("webp", "WEBP"), + ("transp_webp", "WEBP Transparency"), + ("webp_mux", "WEBPMUX"), + ("webp_anim", "WEBP Animation"), + ("jpg", "JPEG"), + ("jpg_2000", "OPENJPEG (JPEG2000)"), + ("zlib", "ZLIB (PNG/ZIP)"), + ("libtiff", "LIBTIFF"), + ("raqm", "RAQM (Bidirectional Text)"), + ("libimagequant", "LIBIMAGEQUANT (Quantization method)"), + ("xcb", "XCB (X protocol)"), + ]: + if check(name): + if name == "jpg" and check_feature("libjpeg_turbo"): + v = "libjpeg-turbo " + version_feature("libjpeg_turbo") + else: + v = version(name) + if v is not None: + version_static = name in ("pil", "jpg") + if name == "littlecms2": + # this check is also in src/_imagingcms.c:setup_module() + version_static = tuple(int(x) for x in v.split(".")) < (2, 7) + t = "compiled for" if version_static else "loaded" + if name == "raqm": + for f in ("fribidi", "harfbuzz"): + v2 = version_feature(f) + if v2 is not None: + v += f", {f} {v2}" + print("---", feature, "support ok,", t, v, file=out) + else: + print("---", feature, "support ok", file=out) + else: + print("***", feature, "support not installed", file=out) + print("-" * 68, file=out) + + if supported_formats: + extensions = collections.defaultdict(list) + for ext, i in Image.EXTENSION.items(): + extensions[i].append(ext) + + for i in sorted(Image.ID): + line = f"{i}" + if i in Image.MIME: + line = f"{line} {Image.MIME[i]}" + print(line, file=out) + + if i in extensions: + print( + "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out + ) + + features = [] + if i in Image.OPEN: + features.append("open") + if i in Image.SAVE: + features.append("save") + if i in Image.SAVE_ALL: + features.append("save_all") + if i in Image.DECODERS: + features.append("decode") + if i in Image.ENCODERS: + features.append("encode") + + print("Features: {}".format(", ".join(features)), file=out) + print("-" * 68, file=out) diff --git a/venv/Lib/site-packages/PIL/msvcp140.dll b/venv/Lib/site-packages/PIL/msvcp140.dll new file mode 100644 index 0000000..130f84a Binary files /dev/null and b/venv/Lib/site-packages/PIL/msvcp140.dll differ diff --git a/venv/Lib/site-packages/PIL/msvcp140_1.dll b/venv/Lib/site-packages/PIL/msvcp140_1.dll new file mode 100644 index 0000000..5c2f46d Binary files /dev/null and b/venv/Lib/site-packages/PIL/msvcp140_1.dll differ diff --git a/venv/Lib/site-packages/PIL/msvcp140_2.dll b/venv/Lib/site-packages/PIL/msvcp140_2.dll new file mode 100644 index 0000000..737b70a Binary files /dev/null and b/venv/Lib/site-packages/PIL/msvcp140_2.dll differ diff --git a/venv/Lib/site-packages/PIL/msvcp140_atomic_wait.dll b/venv/Lib/site-packages/PIL/msvcp140_atomic_wait.dll new file mode 100644 index 0000000..c92fcc3 Binary files /dev/null and b/venv/Lib/site-packages/PIL/msvcp140_atomic_wait.dll differ diff --git a/venv/Lib/site-packages/PIL/msvcp140_codecvt_ids.dll b/venv/Lib/site-packages/PIL/msvcp140_codecvt_ids.dll new file mode 100644 index 0000000..9879454 Binary files /dev/null and b/venv/Lib/site-packages/PIL/msvcp140_codecvt_ids.dll differ diff --git a/venv/Lib/site-packages/PIL/vccorlib140.dll b/venv/Lib/site-packages/PIL/vccorlib140.dll new file mode 100644 index 0000000..7194329 Binary files /dev/null and b/venv/Lib/site-packages/PIL/vccorlib140.dll differ diff --git a/venv/Lib/site-packages/PIL/vcruntime140.dll b/venv/Lib/site-packages/PIL/vcruntime140.dll new file mode 100644 index 0000000..1d6afaa Binary files /dev/null and b/venv/Lib/site-packages/PIL/vcruntime140.dll differ diff --git a/venv/Lib/site-packages/PIL/vcruntime140_1.dll b/venv/Lib/site-packages/PIL/vcruntime140_1.dll new file mode 100644 index 0000000..7bf05d3 Binary files /dev/null and b/venv/Lib/site-packages/PIL/vcruntime140_1.dll differ diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/INSTALLER b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/LICENSE b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/LICENSE new file mode 100644 index 0000000..40aabc3 --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/LICENSE @@ -0,0 +1,30 @@ +The Python Imaging Library (PIL) is + + Copyright © 1997-2011 by Secret Labs AB + Copyright © 1995-2011 by Fredrik Lundh + +Pillow is the friendly PIL fork. It is + + Copyright © 2010-2022 by Alex Clark and contributors + +Like PIL, Pillow is licensed under the open source HPND License: + +By obtaining, using, and/or copying this software and/or its associated +documentation, you agree that you have read, understood, and will comply +with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies, and that +both that copyright notice and this permission notice appear in supporting +documentation, and that the name of Secret Labs AB or the author not be +used in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/METADATA b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/METADATA new file mode 100644 index 0000000..81b7b1c --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/METADATA @@ -0,0 +1,167 @@ +Metadata-Version: 2.1 +Name: Pillow +Version: 9.1.0 +Summary: Python Imaging Library (Fork) +Home-page: https://python-pillow.org +Author: Alex Clark (PIL Fork Author) +Author-email: aclark@python-pillow.org +License: HPND +Project-URL: Documentation, https://pillow.readthedocs.io +Project-URL: Source, https://github.com/python-pillow/Pillow +Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=pypi +Project-URL: Release notes, https://pillow.readthedocs.io/en/stable/releasenotes/index.html +Project-URL: Changelog, https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst +Project-URL: Twitter, https://twitter.com/PythonPillow +Keywords: Imaging +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND) +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: docs +Requires-Dist: olefile ; extra == 'docs' +Requires-Dist: sphinx (>=2.4) ; extra == 'docs' +Requires-Dist: sphinx-copybutton ; extra == 'docs' +Requires-Dist: sphinx-issues (>=3.0.1) ; extra == 'docs' +Requires-Dist: sphinx-removed-in ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme (>=1.0) ; extra == 'docs' +Requires-Dist: sphinxext-opengraph ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: check-manifest ; extra == 'tests' +Requires-Dist: coverage ; extra == 'tests' +Requires-Dist: defusedxml ; extra == 'tests' +Requires-Dist: markdown2 ; extra == 'tests' +Requires-Dist: olefile ; extra == 'tests' +Requires-Dist: packaging ; extra == 'tests' +Requires-Dist: pyroma ; extra == 'tests' +Requires-Dist: pytest ; extra == 'tests' +Requires-Dist: pytest-cov ; extra == 'tests' +Requires-Dist: pytest-timeout ; extra == 'tests' + +

+ Pillow logo +

+ +# Pillow + +## Python Imaging Library (Fork) + +Pillow is the friendly PIL fork by [Alex Clark and +Contributors](https://github.com/python-pillow/Pillow/graphs/contributors). +PIL is the Python Imaging Library by Fredrik Lundh and Contributors. +As of 2019, Pillow development is +[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise). + + + + + + + + + + + + + + + + + + +
docs + Documentation Status +
tests + GitHub Actions build status (Lint) + GitHub Actions build status (Test Linux and macOS) + GitHub Actions build status (Test Windows) + GitHub Actions build status (Test MinGW) + GitHub Actions build status (Test Docker) + AppVeyor CI build status (Windows) + GitHub Actions wheels build status (Wheels) + Travis CI wheels build status (aarch64) + Code coverage + Tidelift Align +
package + Zenodo + Tidelift + Newest PyPI version + Number of PyPI downloads +
social + Join the chat at https://gitter.im/python-pillow/Pillow + Follow on https://twitter.com/PythonPillow +
+ +## Overview + +The Python Imaging Library adds image processing capabilities to your Python interpreter. + +This library provides extensive file format support, an efficient internal representation, and fairly powerful image processing capabilities. + +The core image library is designed for fast access to data stored in a few basic pixel formats. It should provide a solid foundation for a general image processing tool. + +## More Information + +- [Documentation](https://pillow.readthedocs.io/) + - [Installation](https://pillow.readthedocs.io/en/latest/installation.html) + - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html) +- [Contribute](https://github.com/python-pillow/Pillow/blob/main/.github/CONTRIBUTING.md) + - [Issues](https://github.com/python-pillow/Pillow/issues) + - [Pull requests](https://github.com/python-pillow/Pillow/pulls) +- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html) +- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) + - [Pre-fork](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst#pre-fork) + +## Report a Vulnerability + +To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security). + + diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/RECORD b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/RECORD new file mode 100644 index 0000000..93f9a74 --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/RECORD @@ -0,0 +1,210 @@ +PIL/BdfFontFile.py,sha256=hRnSgFZOIiTgWfJIaRHRQpU4TKVok2E31KJY6sbZPwc,2817 +PIL/BlpImagePlugin.py,sha256=SVs3I88sIWw7ibWRnAzDd9T-dIrWD6x00ZAf2HgNjh8,16143 +PIL/BmpImagePlugin.py,sha256=d9hGPxD0wjT_qhchHtiigxYLlFAGG61WcdUqEHqleTk,16252 +PIL/BufrStubImagePlugin.py,sha256=DE_t_ch4-YH_oimXYNMCefin4kcru6Uc2H_OTmwR6y4,1518 +PIL/ContainerIO.py,sha256=1U15zUXjWO8uWK-MyCp66Eh7djQEU-oUeCDoBqewNkA,2883 +PIL/CurImagePlugin.py,sha256=er_bI3V1Ezly0QfFJq0fZMlGwrD5izDutwF1FrOwiMA,1679 +PIL/DcxImagePlugin.py,sha256=bfESLTji9GerqI4oYsy5oTFyRMlr2mjSsXzpY9IuLsk,2145 +PIL/DdsImagePlugin.py,sha256=-sz60zvpuz89nyUobCPhdf-KWaT1yyeEa5PbRlxLMOw,8071 +PIL/EpsImagePlugin.py,sha256=qUxbQVsnzRyveDs9b8Co98sd0klsKr5GLCWtY3xbmB8,11949 +PIL/ExifTags.py,sha256=0YRoKyMwPabWOZZgVeLL6mlaGjbZgfF-z8WuUc6Ibb0,9446 +PIL/FitsImagePlugin.py,sha256=15BrvLXsw0F8WjBbP6-1RPHbJ4Lbd39OP4wkikcstC0,1971 +PIL/FitsStubImagePlugin.py,sha256=ETXbjvAFVMPfm51RNvPuo2B_pNRrJVuNNX-7-RmLUqw,1718 +PIL/FliImagePlugin.py,sha256=fR-Z9uY1udQu6FvzSqZJ3DAmIAaJUKsCNbO7OHN39cY,4239 +PIL/FontFile.py,sha256=LkQcbwUu1C4fokMnbg-ao9ksp2RX-saaPRie-z2rpH4,2765 +PIL/FpxImagePlugin.py,sha256=nKGioxa5C0q9X9qva3t_htRV_3jXQcFkclVxTEaSusk,6658 +PIL/FtexImagePlugin.py,sha256=TkvwTKeFRd1Qhcg6GyTBuFPI118MnegxC-JUoJMQVyY,4175 +PIL/GbrImagePlugin.py,sha256=K-olSg1M2bF2IofUeLABXfI1JLdrWsgiiU6yUTPhSWM,2795 +PIL/GdImageFile.py,sha256=JFWSUssG1z1r884GQtBbZ3T7uhPF4cDXSuW3ctgf3TU,2465 +PIL/GifImagePlugin.py,sha256=xA8QdF_rPNvr7Ceegl0I58FxsTxYaxuM4xpk9JoSZ3k,34458 +PIL/GimpGradientFile.py,sha256=G0ClRmjRHIJoU0nmG-P-tgehLHZip5i0rY4-5pjJ7bc,3353 +PIL/GimpPaletteFile.py,sha256=MGpf0WF_yTtMAXWvO_wlurgv_y80SX66EXprl6UIunM,1274 +PIL/GribStubImagePlugin.py,sha256=CocpZJIN8ckBtMQbq1VMA7NEKW5gwlzQ_mRZhHoyZho,1513 +PIL/Hdf5StubImagePlugin.py,sha256=FJ7-Vz1KY-DEOfrZg3cCMmG_wTa_qf6p41991P2Wfks,1515 +PIL/IcnsImagePlugin.py,sha256=x8JjanvXt_2BS-Qg8Jqt9XPsrCkhN2ESYGoKIoJ2WII,11755 +PIL/IcoImagePlugin.py,sha256=ZSfs8e9qJxIzcNUxuRC8S4KJgmvdH7KZOBuc70Ho9H0,11551 +PIL/ImImagePlugin.py,sha256=76DvUbRkFQ_DkEdthbApsuliNc5-FQHX3mnrYZdOkt4,10729 +PIL/Image.py,sha256=wQ34jHUxgvY6Udbz9u398kzpnEeZybBo5_ic8rQ4fZ0,125350 +PIL/ImageChops.py,sha256=HOGSnuU4EcCbdeUzEGPm54zewppHWWe12XLyOLLPgCw,7297 +PIL/ImageCms.py,sha256=MJHg18tKXzGIV0KZib3NQDIyaGI8XTJGIwzKoswfVbk,37951 +PIL/ImageColor.py,sha256=2e9xfO08S6afUzoahUIzyMN8RJcQsMz9E92rFnEhfP0,8727 +PIL/ImageDraw.py,sha256=rvMmVCjqAo_PRk41fOuOh3kkXYYTY8KinMvLkQ0RhO8,34710 +PIL/ImageDraw2.py,sha256=oBhpBTZhx3bd4D0s8E2kDjBzgThRkDU_TE_987l501k,5019 +PIL/ImageEnhance.py,sha256=CJnCouiBmxN2fE0xW7m_uMdBqcm-Fp0S3ruHhkygal4,3190 +PIL/ImageFile.py,sha256=_zfHA4FUsh2szsv0kd3LK1PywWNRH8krkvUVMi66DX8,22680 +PIL/ImageFilter.py,sha256=Sx99ij57imObeBdiR5w6cuhEG682SkfqtXx_vW7T_mk,16142 +PIL/ImageFont.py,sha256=TcCig_Hw5DbOnsZsWdLQeDEqplJotI2wG_Viw7P9W6o,45963 +PIL/ImageGrab.py,sha256=4W_qGYMJv7-5kWIvKnb3PzFMqdQERV32c43z-onj1CI,3823 +PIL/ImageMath.py,sha256=OsrEDBmoonjeOdcbuYQFEoU1sRT4sSCNO95EAq_CA_s,7253 +PIL/ImageMode.py,sha256=ZyTPlast0KeEp0-lbRcBoztKQzUY3FRaMAZza0Lm_mE,3006 +PIL/ImageMorph.py,sha256=KL2843wgfLyXPOWEJnTXRvySfbpRrlTqA_0M1j5xuD0,7773 +PIL/ImageOps.py,sha256=-MBNR_kztrdN6IAwTVXXHL2vvdO8ZkZjK-vMXVpmv5w,20504 +PIL/ImagePalette.py,sha256=rOpqcuH5DhJXPEvREna3Dg1N7ZK3TfnXHu5eZyltZTs,7841 +PIL/ImagePath.py,sha256=lVmH1-lCd0SyrFoqyhlstAFW2iJuC14fPcW8iewvxCQ,336 +PIL/ImageQt.py,sha256=hECe1rZpv1teaR5exrP39NbWBKwNGD7X5zoA5id_UJo,6698 +PIL/ImageSequence.py,sha256=3djA7vDH6wafTGbt4e_lPlVhy2TaKfdSrA1XQ4n-Uoc,1850 +PIL/ImageShow.py,sha256=Q_c_v9sy3wNnCnz7Ce1aM5vG1q74lFJ_ur6XvlNQXqc,12249 +PIL/ImageStat.py,sha256=Wdxu473_-bf3MeXLEj-9GrRftp6Ju_F7Sl_EKgzKd1Y,3899 +PIL/ImageTk.py,sha256=f6GGmApnpacVAHyOOVgG5PSLG6OCQInb5-2CSYfyTKg,9148 +PIL/ImageTransform.py,sha256=oO7Ir7j_5r4DeoZ-ZgqW9FO099cP2gHdE32SQdfmW_s,2883 +PIL/ImageWin.py,sha256=1MQBJS7tVrQzI9jN0nmeNeFpIaq8fXra9kQocHkiFxM,7191 +PIL/ImtImagePlugin.py,sha256=v_P09UT1Ae_HNUS-lTcMWfDTedfBDf-krhJRckDW6tg,2203 +PIL/IptcImagePlugin.py,sha256=-RZBUUodHcF5wLKanW1MxJj7cbLOpx5LvXqm0vDM22U,5714 +PIL/Jpeg2KImagePlugin.py,sha256=M8xsol1019D8hwtooNey-AGiNGaPPOqOat_0w4Tojaw,10455 +PIL/JpegImagePlugin.py,sha256=LRZGSeeoCbOyF3ISZp2VDYZGg5uL2JXLDf5AOCv3ghQ,28561 +PIL/JpegPresets.py,sha256=6nVnX_H8eA8ZO7AOVvkUx8gEN6QfI8zKnV6od16XgWE,12347 +PIL/McIdasImagePlugin.py,sha256=LrP5nA7l8IQG3WhlMI0Xs8fGXY_uf6IDmzNCERl3tGw,1754 +PIL/MicImagePlugin.py,sha256=Eh94vjTurXYkmm27hhooyNm9NkWWyVxP8Nq4thNLV6Y,2607 +PIL/MpegImagePlugin.py,sha256=n16Zgdy8Hcfke16lQwZWs53PZq4BA_OxPCMPDkW62nw,1803 +PIL/MpoImagePlugin.py,sha256=C-oosMx-C7dZT4QODBNYbX6LtfeEUxdpQ15Ychx9SuY,4478 +PIL/MspImagePlugin.py,sha256=ftTl14BpW1i3os_OUfusc7t4tRzBP4RrLxp76Sf9X4I,5527 +PIL/PSDraw.py,sha256=xmJ6GVUvDm1SC3QuUpYdeNfGu9lYBLX1ndCt96tObcc,6719 +PIL/PaletteFile.py,sha256=s3KtsDuY5S04MKDyiXK3iIbiOGzV9PvCDUpOQHI7yqc,1106 +PIL/PalmImagePlugin.py,sha256=lTVwwSPFrQ-IPFGU8_gRCMZ1Lb73cuVhQ-nkx1Q0oqc,9108 +PIL/PcdImagePlugin.py,sha256=cnBm_xKcpLGT6hZ8QKai9Up0gZERMxZwhDXl1hQtBm0,1476 +PIL/PcfFontFile.py,sha256=njhgblsjSVcITVz1DpWdEligmJgPMh5nTk_zDDWWTik,6348 +PIL/PcxImagePlugin.py,sha256=J-Pm2QBt5Hi4ObPeXDnc87X7nl1hbtTGqy4sTov6tug,5864 +PIL/PdfImagePlugin.py,sha256=f3foSWC1anwbnVBXVi-4wmtEnOR4_dbmqrbiQ--48Bk,7311 +PIL/PdfParser.py,sha256=Kxq4ZLMoayNODnpURMIcXljGJS-rX8AMBKA5iA0O29M,34561 +PIL/PixarImagePlugin.py,sha256=5MMcrrShVr511QKevK1ziKyJn0WllokWQxBhs8NWttY,1631 +PIL/PngImagePlugin.py,sha256=377uheEGeWvhlmTda0wRsQdVqAmOuboJWUMkHCl3Fs4,45016 +PIL/PpmImagePlugin.py,sha256=FclF4DGFyqWmqCOexRpzX47YuoylGNnVK1_VffYrP_s,5850 +PIL/PsdImagePlugin.py,sha256=8pYj9Sc4FYHl997QnJ6-79rAcS1flv7mIAMVR4_o1ws,7572 +PIL/PyAccess.py,sha256=SaGs2ZE4kjh-dybpAA5_Og4wuhA6d0LTPKK8t2aHffY,9607 +PIL/SgiImagePlugin.py,sha256=mqpi0G4aiKzWmJHk22WKZ0oGqsglcTNgDfp4H8S-GCM,6097 +PIL/SpiderImagePlugin.py,sha256=3weeJ7kc2t6gA-Hau9QdKgDdbXPcY8zrcTbR4cfAU-g,9554 +PIL/SunImagePlugin.py,sha256=bnjnVFRjvApCH1QC1F9HeynoCe5AZk3wa1tOhPvHzKU,4282 +PIL/TarIO.py,sha256=E_pjAxk9wHezXUuR_99liySBXfJoL2wjzdNDf0g1hTo,1440 +PIL/TgaImagePlugin.py,sha256=geeOJJJ-5Xz3u4JiDMrouyr-XFSqZ6Z48OuOaOY7_lI,6485 +PIL/TiffImagePlugin.py,sha256=uYKFj4zJivvZI_QSHRjR4uWJC_tHh4VgsegOAJPZCfY,75049 +PIL/TiffTags.py,sha256=CPaXv9s7T2oNFZFVbD-Kwz-K2V5ZcHKFkw3rT-Llkp4,15297 +PIL/WalImageFile.py,sha256=MhlGQBmSA_4OPBv6EL9bqFYe0YAf5rYtgAI_y0T920U,5520 +PIL/WebPImagePlugin.py,sha256=buw7FnrHviRmiYMcVSslJNohK3-OcwOUcnAkbZYJu-o,10924 +PIL/WmfImagePlugin.py,sha256=wvJeH9k4XJoUE2wVcf5G_8eeIuuO9BuGiV8jOZlcWrM,4625 +PIL/XVThumbImagePlugin.py,sha256=zmZ8Z4B8Kr6NOdUqSipW9_X5mKiLBLs-wxvPRRg1l0M,1940 +PIL/XbmImagePlugin.py,sha256=kuyd690rupwLFZj5r8hbGmI0Wr8sD_CceCuRew_PUew,2454 +PIL/XpmImagePlugin.py,sha256=1EBt-g678p0A0NXOkxq7sGM8dymneDMHHQmwJzAbrlw,3062 +PIL/__init__.py,sha256=3Z8lwq0danRE7WQFZxa7vMvfSjv_C4-Q73FUr_gHt4Y,1763 +PIL/__main__.py,sha256=axR7PO-HtXp-o0rBhKIxs0wark0rBfaDIhAIWqtWUo4,41 +PIL/__pycache__/BdfFontFile.cpython-39.pyc,, +PIL/__pycache__/BlpImagePlugin.cpython-39.pyc,, +PIL/__pycache__/BmpImagePlugin.cpython-39.pyc,, +PIL/__pycache__/BufrStubImagePlugin.cpython-39.pyc,, +PIL/__pycache__/ContainerIO.cpython-39.pyc,, +PIL/__pycache__/CurImagePlugin.cpython-39.pyc,, +PIL/__pycache__/DcxImagePlugin.cpython-39.pyc,, +PIL/__pycache__/DdsImagePlugin.cpython-39.pyc,, +PIL/__pycache__/EpsImagePlugin.cpython-39.pyc,, +PIL/__pycache__/ExifTags.cpython-39.pyc,, +PIL/__pycache__/FitsImagePlugin.cpython-39.pyc,, +PIL/__pycache__/FitsStubImagePlugin.cpython-39.pyc,, +PIL/__pycache__/FliImagePlugin.cpython-39.pyc,, +PIL/__pycache__/FontFile.cpython-39.pyc,, +PIL/__pycache__/FpxImagePlugin.cpython-39.pyc,, +PIL/__pycache__/FtexImagePlugin.cpython-39.pyc,, +PIL/__pycache__/GbrImagePlugin.cpython-39.pyc,, +PIL/__pycache__/GdImageFile.cpython-39.pyc,, +PIL/__pycache__/GifImagePlugin.cpython-39.pyc,, +PIL/__pycache__/GimpGradientFile.cpython-39.pyc,, +PIL/__pycache__/GimpPaletteFile.cpython-39.pyc,, +PIL/__pycache__/GribStubImagePlugin.cpython-39.pyc,, +PIL/__pycache__/Hdf5StubImagePlugin.cpython-39.pyc,, +PIL/__pycache__/IcnsImagePlugin.cpython-39.pyc,, +PIL/__pycache__/IcoImagePlugin.cpython-39.pyc,, +PIL/__pycache__/ImImagePlugin.cpython-39.pyc,, +PIL/__pycache__/Image.cpython-39.pyc,, +PIL/__pycache__/ImageChops.cpython-39.pyc,, +PIL/__pycache__/ImageCms.cpython-39.pyc,, +PIL/__pycache__/ImageColor.cpython-39.pyc,, +PIL/__pycache__/ImageDraw.cpython-39.pyc,, +PIL/__pycache__/ImageDraw2.cpython-39.pyc,, +PIL/__pycache__/ImageEnhance.cpython-39.pyc,, +PIL/__pycache__/ImageFile.cpython-39.pyc,, +PIL/__pycache__/ImageFilter.cpython-39.pyc,, +PIL/__pycache__/ImageFont.cpython-39.pyc,, +PIL/__pycache__/ImageGrab.cpython-39.pyc,, +PIL/__pycache__/ImageMath.cpython-39.pyc,, +PIL/__pycache__/ImageMode.cpython-39.pyc,, +PIL/__pycache__/ImageMorph.cpython-39.pyc,, +PIL/__pycache__/ImageOps.cpython-39.pyc,, +PIL/__pycache__/ImagePalette.cpython-39.pyc,, +PIL/__pycache__/ImagePath.cpython-39.pyc,, +PIL/__pycache__/ImageQt.cpython-39.pyc,, +PIL/__pycache__/ImageSequence.cpython-39.pyc,, +PIL/__pycache__/ImageShow.cpython-39.pyc,, +PIL/__pycache__/ImageStat.cpython-39.pyc,, +PIL/__pycache__/ImageTk.cpython-39.pyc,, +PIL/__pycache__/ImageTransform.cpython-39.pyc,, +PIL/__pycache__/ImageWin.cpython-39.pyc,, +PIL/__pycache__/ImtImagePlugin.cpython-39.pyc,, +PIL/__pycache__/IptcImagePlugin.cpython-39.pyc,, +PIL/__pycache__/Jpeg2KImagePlugin.cpython-39.pyc,, +PIL/__pycache__/JpegImagePlugin.cpython-39.pyc,, +PIL/__pycache__/JpegPresets.cpython-39.pyc,, +PIL/__pycache__/McIdasImagePlugin.cpython-39.pyc,, +PIL/__pycache__/MicImagePlugin.cpython-39.pyc,, +PIL/__pycache__/MpegImagePlugin.cpython-39.pyc,, +PIL/__pycache__/MpoImagePlugin.cpython-39.pyc,, +PIL/__pycache__/MspImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PSDraw.cpython-39.pyc,, +PIL/__pycache__/PaletteFile.cpython-39.pyc,, +PIL/__pycache__/PalmImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PcdImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PcfFontFile.cpython-39.pyc,, +PIL/__pycache__/PcxImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PdfImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PdfParser.cpython-39.pyc,, +PIL/__pycache__/PixarImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PngImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PpmImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PsdImagePlugin.cpython-39.pyc,, +PIL/__pycache__/PyAccess.cpython-39.pyc,, +PIL/__pycache__/SgiImagePlugin.cpython-39.pyc,, +PIL/__pycache__/SpiderImagePlugin.cpython-39.pyc,, +PIL/__pycache__/SunImagePlugin.cpython-39.pyc,, +PIL/__pycache__/TarIO.cpython-39.pyc,, +PIL/__pycache__/TgaImagePlugin.cpython-39.pyc,, +PIL/__pycache__/TiffImagePlugin.cpython-39.pyc,, +PIL/__pycache__/TiffTags.cpython-39.pyc,, +PIL/__pycache__/WalImageFile.cpython-39.pyc,, +PIL/__pycache__/WebPImagePlugin.cpython-39.pyc,, +PIL/__pycache__/WmfImagePlugin.cpython-39.pyc,, +PIL/__pycache__/XVThumbImagePlugin.cpython-39.pyc,, +PIL/__pycache__/XbmImagePlugin.cpython-39.pyc,, +PIL/__pycache__/XpmImagePlugin.cpython-39.pyc,, +PIL/__pycache__/__init__.cpython-39.pyc,, +PIL/__pycache__/__main__.cpython-39.pyc,, +PIL/__pycache__/_binary.cpython-39.pyc,, +PIL/__pycache__/_tkinter_finder.cpython-39.pyc,, +PIL/__pycache__/_util.cpython-39.pyc,, +PIL/__pycache__/_version.cpython-39.pyc,, +PIL/__pycache__/features.cpython-39.pyc,, +PIL/_binary.py,sha256=E5qhxNJ7hhbEoqu0mODOXHT8z-FDRShXG3jTJhsDdas,2043 +PIL/_imaging.cp39-win_amd64.pyd,sha256=rjBx1Em4OZEdaaB5EUE6ptH7zLbSBIHA54FxMdfCieM,3202048 +PIL/_imagingcms.cp39-win_amd64.pyd,sha256=T63FsiwkHW_aOGKZGm34AagOcdC2EyXluthlmv9f9iU,254976 +PIL/_imagingft.cp39-win_amd64.pyd,sha256=oudclQKBA5NhGs5sY2hVGBwtUsw81gkspvMS4QIhxkA,1505280 +PIL/_imagingmath.cp39-win_amd64.pyd,sha256=lSTNqJ-7fsT0uxDXrWisXD0H0ReS3r4XzoSTswIBbNk,25088 +PIL/_imagingmorph.cp39-win_amd64.pyd,sha256=PSQSThxHCz0soHLNAgh83gJyVfGtPSKxHLg_FbYCR4g,13824 +PIL/_imagingtk.cp39-win_amd64.pyd,sha256=kd8eB3Yid40OlxZToaIFyVZqDKyCbLn94wxfG22CUXs,15360 +PIL/_tkinter_finder.py,sha256=_h4IyntUxL3ZCMnuKGxvW5VwN9k8Yiel0E4j_i41nxk,752 +PIL/_util.py,sha256=pbjX5KY1W2oZyYVC4TE9ai2PfrJZrAsO5hAnz_JMees,359 +PIL/_version.py,sha256=TSDtIA_HTVdlnbVHs_Qn2GMMtftmiSHLYUnuWLa1_rk,50 +PIL/_webp.cp39-win_amd64.pyd,sha256=LdSJ36RBE2X0JvsZn5_DyMuhpgYf_gJGVKX2FROtna8,522752 +PIL/concrt140.dll,sha256=VzIpoH84q50vwuGluY6SQ7mzkQAyMYDIOtfdr5ju5Go,317864 +PIL/features.py,sha256=j2LT6v78cHWbR8z8OVaAGIbJWI-Bs62pfiB1i1fminM,9387 +PIL/msvcp140.dll,sha256=n-5vNlR9b26nygM4ZVVV26a7D3mLxgM00puU0VR9pNo,566704 +PIL/msvcp140_1.dll,sha256=hzGpPlGcJZXJ_UiebZrAfpZESMDaHI7p7lAKeYlIJhc,23944 +PIL/msvcp140_2.dll,sha256=Nmr44HHwBNpdlagypGsuiCGo4ClDQKk_fJXPSMRBBn4,186800 +PIL/msvcp140_atomic_wait.dll,sha256=xhoocR-Mbpv9SHnPX1OwE9ZTutrTCKvj6IfGlLIj1vA,57264 +PIL/msvcp140_codecvt_ids.dll,sha256=m0X9BpvQB22Kv-t8PDCh9cX8jnEkAXhTqT2DGjRsPSE,21424 +PIL/vccorlib140.dll,sha256=eShGWb9DAhYjAnN9JROxfgl0LN77lUDoD5fTDJMHfXw,335792 +PIL/vcruntime140.dll,sha256=nStA8DlcxdG01eoXuElwwplx1EjDcQRnbbV3WG1K0bE,98224 +PIL/vcruntime140_1.dll,sha256=NASKuqBw7ME7MYzqMUJfTKPt0TPTUDGKxlJZ5gWMizI,37256 +Pillow-9.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Pillow-9.1.0.dist-info/LICENSE,sha256=plVMtze6bJtH0zAfeN4DtO0NPwjWz5QAcU89TIlPaUM,1444 +Pillow-9.1.0.dist-info/METADATA,sha256=DLY0qLsMqaQS2OR2dtHBPI2XrMvxgvmrahFZ5Bdnq4U,8722 +Pillow-9.1.0.dist-info/RECORD,, +Pillow-9.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Pillow-9.1.0.dist-info/WHEEL,sha256=fVcVlLzi8CGi_Ul8vjMdn8gER25dn5GBg9E6k9z41-Y,100 +Pillow-9.1.0.dist-info/top_level.txt,sha256=riZqrk-hyZqh5f1Z0Zwii3dKfxEsByhu9cU9IODF-NY,4 +Pillow-9.1.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2 diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/REQUESTED b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/WHEEL b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/WHEEL new file mode 100644 index 0000000..d5a9837 --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/top_level.txt b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..b338169 --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +PIL diff --git a/venv/Lib/site-packages/Pillow-9.1.0.dist-info/zip-safe b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/Pillow-9.1.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/django/__init__.py b/venv/Lib/site-packages/django/__init__.py index 321c6dc..bcdeaf2 100644 --- a/venv/Lib/site-packages/django/__init__.py +++ b/venv/Lib/site-packages/django/__init__.py @@ -1,6 +1,6 @@ from django.utils.version import get_version -VERSION = (4, 0, 4, "final", 0) +VERSION = (3, 2, 5, 'final', 0) __version__ = get_version(VERSION) @@ -19,6 +19,6 @@ def setup(set_prefix=True): configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) if set_prefix: set_script_prefix( - "/" if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME + '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME ) apps.populate(settings.INSTALLED_APPS) diff --git a/venv/Lib/site-packages/django/apps/__init__.py b/venv/Lib/site-packages/django/apps/__init__.py index 96674be..79091dc 100644 --- a/venv/Lib/site-packages/django/apps/__init__.py +++ b/venv/Lib/site-packages/django/apps/__init__.py @@ -1,4 +1,4 @@ from .config import AppConfig from .registry import apps -__all__ = ["AppConfig", "apps"] +__all__ = ['AppConfig', 'apps'] diff --git a/venv/Lib/site-packages/django/apps/config.py b/venv/Lib/site-packages/django/apps/config.py index 53d3852..bced53d 100644 --- a/venv/Lib/site-packages/django/apps/config.py +++ b/venv/Lib/site-packages/django/apps/config.py @@ -8,8 +8,8 @@ from django.utils.deprecation import RemovedInDjango41Warning from django.utils.functional import cached_property from django.utils.module_loading import import_string, module_has_submodule -APPS_MODULE_NAME = "apps" -MODELS_MODULE_NAME = "models" +APPS_MODULE_NAME = 'apps' +MODELS_MODULE_NAME = 'models' class AppConfig: @@ -32,7 +32,7 @@ class AppConfig: # Last component of the Python path to the application e.g. 'admin'. # This value must be unique across a Django project. - if not hasattr(self, "label"): + if not hasattr(self, 'label'): self.label = app_name.rpartition(".")[2] if not self.label.isidentifier(): raise ImproperlyConfigured( @@ -40,12 +40,12 @@ class AppConfig: ) # Human-readable name for the application e.g. "Admin". - if not hasattr(self, "verbose_name"): + if not hasattr(self, 'verbose_name'): self.verbose_name = self.label.title() # Filesystem path to the application directory e.g. # '/path/to/django/contrib/admin'. - if not hasattr(self, "path"): + if not hasattr(self, 'path'): self.path = self._path_from_module(app_module) # Module containing models e.g. " % (self.__class__.__name__, self.label) + return '<%s: %s>' % (self.__class__.__name__, self.label) @cached_property def default_auto_field(self): from django.conf import settings - return settings.DEFAULT_AUTO_FIELD @property @@ -74,10 +73,11 @@ class AppConfig: """Attempt to determine app's filesystem path from its module.""" # See #21874 for extended discussion of the behavior of this method in # various cases. - # Convert to list because __path__ may not support indexing. - paths = list(getattr(module, "__path__", [])) + # Convert paths to list because Python's _NamespacePath doesn't support + # indexing. + paths = list(getattr(module, '__path__', [])) if len(paths) != 1: - filename = getattr(module, "__file__", None) + filename = getattr(module, '__file__', None) if filename is not None: paths = [os.path.dirname(filename)] else: @@ -88,14 +88,12 @@ class AppConfig: raise ImproperlyConfigured( "The app module %r has multiple filesystem locations (%r); " "you must configure this app with an AppConfig subclass " - "with a 'path' class attribute." % (module, paths) - ) + "with a 'path' class attribute." % (module, paths)) elif not paths: raise ImproperlyConfigured( "The app module %r has no filesystem location, " "you must configure this app with an AppConfig subclass " - "with a 'path' class attribute." % module - ) + "with a 'path' class attribute." % module) return paths[0] @classmethod @@ -122,7 +120,7 @@ class AppConfig: # If the apps module defines more than one AppConfig subclass, # the default one can declare default = True. if module_has_submodule(app_module, APPS_MODULE_NAME): - mod_path = "%s.%s" % (entry, APPS_MODULE_NAME) + mod_path = '%s.%s' % (entry, APPS_MODULE_NAME) mod = import_module(mod_path) # Check if there's exactly one AppConfig candidate, # excluding those that explicitly define default = False. @@ -130,31 +128,31 @@ class AppConfig: (name, candidate) for name, candidate in inspect.getmembers(mod, inspect.isclass) if ( - issubclass(candidate, cls) - and candidate is not cls - and getattr(candidate, "default", True) + issubclass(candidate, cls) and + candidate is not cls and + getattr(candidate, 'default', True) ) ] if len(app_configs) == 1: app_config_class = app_configs[0][1] - app_config_name = "%s.%s" % (mod_path, app_configs[0][0]) + app_config_name = '%s.%s' % (mod_path, app_configs[0][0]) else: # Check if there's exactly one AppConfig subclass, # among those that explicitly define default = True. app_configs = [ (name, candidate) for name, candidate in app_configs - if getattr(candidate, "default", False) + if getattr(candidate, 'default', False) ] if len(app_configs) > 1: candidates = [repr(name) for name, _ in app_configs] raise RuntimeError( - "%r declares more than one default AppConfig: " - "%s." % (mod_path, ", ".join(candidates)) + '%r declares more than one default AppConfig: ' + '%s.' % (mod_path, ', '.join(candidates)) ) elif len(app_configs) == 1: app_config_class = app_configs[0][1] - app_config_name = "%s.%s" % (mod_path, app_configs[0][0]) + app_config_name = '%s.%s' % (mod_path, app_configs[0][0]) # If app_module specifies a default_app_config, follow the link. # default_app_config is deprecated, but still takes over the @@ -168,11 +166,13 @@ class AppConfig: app_config_class = cls app_name = entry else: - message = "%r defines default_app_config = %r. " % (entry, new_entry) + message = ( + '%r defines default_app_config = %r. ' % (entry, new_entry) + ) if new_entry == app_config_name: message += ( - "Django now detects this configuration automatically. " - "You can remove default_app_config." + 'Django now detects this configuration automatically. ' + 'You can remove default_app_config.' ) else: message += ( @@ -180,8 +180,7 @@ class AppConfig: "move the default config class to the apps submodule " "of your application and, if this module defines " "several config classes, mark the default one with " - "default = True." - % ( + "default = True." % ( "picked another configuration, %r" % app_config_name if app_config_name else "did not find this configuration" @@ -203,7 +202,7 @@ class AppConfig: # If the last component of entry starts with an uppercase letter, # then it was likely intended to be an app config class; if not, # an app module. Provide a nice error message in both cases. - mod_path, _, cls_name = entry.rpartition(".") + mod_path, _, cls_name = entry.rpartition('.') if mod_path and cls_name[0].isupper(): # We could simply re-trigger the string import exception, but # we're going the extra mile and providing a better error @@ -216,12 +215,9 @@ class AppConfig: for name, candidate in inspect.getmembers(mod, inspect.isclass) if issubclass(candidate, cls) and candidate is not cls ] - msg = "Module '%s' does not contain a '%s' class." % ( - mod_path, - cls_name, - ) + msg = "Module '%s' does not contain a '%s' class." % (mod_path, cls_name) if candidates: - msg += " Choices are: %s." % ", ".join(candidates) + msg += ' Choices are: %s.' % ', '.join(candidates) raise ImportError(msg) else: # Re-trigger the module import exception. @@ -230,7 +226,8 @@ class AppConfig: # Check for obvious errors. (This check prevents duck typing, but # it could be removed if it became a problem in practice.) if not issubclass(app_config_class, AppConfig): - raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry) + raise ImproperlyConfigured( + "'%s' isn't a subclass of AppConfig." % entry) # Obtain app name here rather than in AppClass.__init__ to keep # all error checking for entries in INSTALLED_APPS in one place. @@ -238,15 +235,16 @@ class AppConfig: try: app_name = app_config_class.name except AttributeError: - raise ImproperlyConfigured("'%s' must supply a name attribute." % entry) + raise ImproperlyConfigured( + "'%s' must supply a name attribute." % entry + ) # Ensure app_name points to a valid module. try: app_module = import_module(app_name) except ImportError: raise ImproperlyConfigured( - "Cannot import '%s'. Check that '%s.%s.name' is correct." - % ( + "Cannot import '%s'. Check that '%s.%s.name' is correct." % ( app_name, app_config_class.__module__, app_config_class.__qualname__, @@ -270,8 +268,7 @@ class AppConfig: return self.models[model_name.lower()] except KeyError: raise LookupError( - "App '%s' doesn't have a '%s' model." % (self.label, model_name) - ) + "App '%s' doesn't have a '%s' model." % (self.label, model_name)) def get_models(self, include_auto_created=False, include_swapped=False): """ @@ -300,7 +297,7 @@ class AppConfig: self.models = self.apps.all_models[self.label] if module_has_submodule(self.module, MODELS_MODULE_NAME): - models_module_name = "%s.%s" % (self.name, MODELS_MODULE_NAME) + models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME) self.models_module = import_module(models_module_name) def ready(self): diff --git a/venv/Lib/site-packages/django/apps/registry.py b/venv/Lib/site-packages/django/apps/registry.py index c5ba3a3..62650ca 100644 --- a/venv/Lib/site-packages/django/apps/registry.py +++ b/venv/Lib/site-packages/django/apps/registry.py @@ -21,7 +21,7 @@ class Apps: # installed_apps is set to None when creating the master registry # because it cannot be populated at that point. Other registries must # provide a list of installed apps and are populated immediately. - if installed_apps is None and hasattr(sys.modules[__name__], "apps"): + if installed_apps is None and hasattr(sys.modules[__name__], 'apps'): raise RuntimeError("You must supply an installed_apps argument.") # Mapping of app labels => model names => model classes. Every time a @@ -92,22 +92,20 @@ class Apps: if app_config.label in self.app_configs: raise ImproperlyConfigured( "Application labels aren't unique, " - "duplicates: %s" % app_config.label - ) + "duplicates: %s" % app_config.label) self.app_configs[app_config.label] = app_config app_config.apps = self # Check for duplicate app names. counts = Counter( - app_config.name for app_config in self.app_configs.values() - ) - duplicates = [name for name, count in counts.most_common() if count > 1] + app_config.name for app_config in self.app_configs.values()) + duplicates = [ + name for name, count in counts.most_common() if count > 1] if duplicates: raise ImproperlyConfigured( "Application names aren't unique, " - "duplicates: %s" % ", ".join(duplicates) - ) + "duplicates: %s" % ", ".join(duplicates)) self.apps_ready = True @@ -203,7 +201,7 @@ class Apps: self.check_apps_ready() if model_name is None: - app_label, model_name = app_label.split(".") + app_label, model_name = app_label.split('.') app_config = self.get_app_config(app_label) @@ -219,22 +217,17 @@ class Apps: model_name = model._meta.model_name app_models = self.all_models[app_label] if model_name in app_models: - if ( - model.__name__ == app_models[model_name].__name__ - and model.__module__ == app_models[model_name].__module__ - ): + if (model.__name__ == app_models[model_name].__name__ and + model.__module__ == app_models[model_name].__module__): warnings.warn( - "Model '%s.%s' was already registered. Reloading models is not " - "advised as it can lead to inconsistencies, most notably with " - "related models." % (app_label, model_name), - RuntimeWarning, - stacklevel=2, - ) + "Model '%s.%s' was already registered. " + "Reloading models is not advised as it can lead to inconsistencies, " + "most notably with related models." % (app_label, model_name), + RuntimeWarning, stacklevel=2) else: raise RuntimeError( - "Conflicting '%s' models in application '%s': %s and %s." - % (model_name, app_label, app_models[model_name], model) - ) + "Conflicting '%s' models in application '%s': %s and %s." % + (model_name, app_label, app_models[model_name], model)) app_models[model_name] = model self.do_pending_operations(model) self.clear_cache() @@ -261,8 +254,8 @@ class Apps: candidates = [] for app_config in self.app_configs.values(): if object_name.startswith(app_config.name): - subpath = object_name[len(app_config.name) :] - if subpath == "" or subpath[0] == ".": + subpath = object_name[len(app_config.name):] + if subpath == '' or subpath[0] == '.': candidates.append(app_config) if candidates: return sorted(candidates, key=lambda ac: -len(ac.name))[0] @@ -277,7 +270,8 @@ class Apps: """ model = self.all_models[app_label].get(model_name.lower()) if model is None: - raise LookupError("Model '%s.%s' not registered." % (app_label, model_name)) + raise LookupError( + "Model '%s.%s' not registered." % (app_label, model_name)) return model @functools.lru_cache(maxsize=None) @@ -292,14 +286,13 @@ class Apps: change after Django has loaded the settings, there is no reason to get the respective settings attribute over and over again. """ - to_string = to_string.lower() for model in self.get_models(include_swapped=True): swapped = model._meta.swapped # Is this model swapped out for the model given by to_string? - if swapped and swapped.lower() == to_string: + if swapped and swapped == to_string: return model._meta.swappable # Is this model swappable and the one given by to_string? - if model._meta.swappable and model._meta.label_lower == to_string: + if model._meta.swappable and model._meta.label == to_string: return model._meta.swappable return None @@ -409,7 +402,6 @@ class Apps: def apply_next_model(model): next_function = partial(apply_next_model.func, model) self.lazy_model_operation(next_function, *more_models) - apply_next_model.func = function # If the model has already been imported and registered, partially diff --git a/venv/Lib/site-packages/django/bin/django-admin.py b/venv/Lib/site-packages/django/bin/django-admin.py new file mode 100644 index 0000000..594b0f1 --- /dev/null +++ b/venv/Lib/site-packages/django/bin/django-admin.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# When the django-admin.py deprecation ends, remove this script. +import warnings + +from django.core import management + +try: + from django.utils.deprecation import RemovedInDjango40Warning +except ImportError: + raise ImportError( + 'django-admin.py was deprecated in Django 3.1 and removed in Django ' + '4.0. Please manually remove this script from your virtual environment ' + 'and use django-admin instead.' + ) + +if __name__ == "__main__": + warnings.warn( + 'django-admin.py is deprecated in favor of django-admin.', + RemovedInDjango40Warning, + ) + management.execute_from_command_line() diff --git a/venv/Lib/site-packages/django/conf/__init__.py b/venv/Lib/site-packages/django/conf/__init__.py index 2fda24a..2830244 100644 --- a/venv/Lib/site-packages/django/conf/__init__.py +++ b/venv/Lib/site-packages/django/conf/__init__.py @@ -16,22 +16,20 @@ from pathlib import Path import django from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured -from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import LazyObject, empty ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" -# RemovedInDjango50Warning -USE_DEPRECATED_PYTZ_DEPRECATED_MSG = ( - "The USE_DEPRECATED_PYTZ setting, and support for pytz timezones is " - "deprecated in favor of the stdlib zoneinfo module. Please update your " - "code to use zoneinfo and remove the USE_DEPRECATED_PYTZ setting." +PASSWORD_RESET_TIMEOUT_DAYS_DEPRECATED_MSG = ( + 'The PASSWORD_RESET_TIMEOUT_DAYS setting is deprecated. Use ' + 'PASSWORD_RESET_TIMEOUT instead.' ) -USE_L10N_DEPRECATED_MSG = ( - "The USE_L10N setting is deprecated. Starting with Django 5.0, localized " - "formatting of data will always be enabled. For example Django will " - "display numbers and dates using the format of the current locale." +DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG = ( + 'The DEFAULT_HASHING_ALGORITHM transitional setting is deprecated. ' + 'Support for it and tokens, cookies, sessions, and signatures that use ' + 'SHA-1 hashing algorithm will be removed in Django 4.0.' ) @@ -40,7 +38,6 @@ class SettingsReference(str): String subclass which references a current settings value. It's treated as the value in memory but serializes to a settings.NAME attribute reference. """ - def __new__(self, value, setting_name): return str.__new__(self, value) @@ -54,7 +51,6 @@ class LazySettings(LazyObject): The user can manually configure settings prior to using them. Otherwise, Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE. """ - def _setup(self, name=None): """ Load the settings module pointed to by the environment variable. This @@ -68,17 +64,16 @@ class LazySettings(LazyObject): "Requested %s, but settings are not configured. " "You must either define the environment variable %s " "or call settings.configure() before accessing settings." - % (desc, ENVIRONMENT_VARIABLE) - ) + % (desc, ENVIRONMENT_VARIABLE)) self._wrapped = Settings(settings_module) def __repr__(self): # Hardcode the class name as otherwise it yields 'Settings'. if self._wrapped is empty: - return "" + return '' return '' % { - "settings_module": self._wrapped.SETTINGS_MODULE, + 'settings_module': self._wrapped.SETTINGS_MODULE, } def __getattr__(self, name): @@ -89,9 +84,9 @@ class LazySettings(LazyObject): # Special case some settings which require further modification. # This is done here for performance reasons so the modified value is cached. - if name in {"MEDIA_URL", "STATIC_URL"} and val is not None: + if name in {'MEDIA_URL', 'STATIC_URL'} and val is not None: val = self._add_script_prefix(val) - elif name == "SECRET_KEY" and not val: + elif name == 'SECRET_KEY' and not val: raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") self.__dict__[name] = val @@ -102,7 +97,7 @@ class LazySettings(LazyObject): Set the value of setting. Clear all cached values if _wrapped changes (@override_settings does this) or clear single values when set. """ - if name == "_wrapped": + if name == '_wrapped': self.__dict__.clear() else: self.__dict__.pop(name, None) @@ -120,11 +115,11 @@ class LazySettings(LazyObject): argument must support attribute access (__getattr__)). """ if self._wrapped is not empty: - raise RuntimeError("Settings already configured.") + raise RuntimeError('Settings already configured.') holder = UserSettingsHolder(default_settings) for name, value in options.items(): if not name.isupper(): - raise TypeError("Setting %r must be uppercase." % name) + raise TypeError('Setting %r must be uppercase.' % name) setattr(holder, name, value) self._wrapped = holder @@ -137,11 +132,10 @@ class LazySettings(LazyObject): subpath to STATIC_URL and MEDIA_URL in settings is inconvenient. """ # Don't apply prefix to absolute paths and URLs. - if value.startswith(("http://", "https://", "/")): + if value.startswith(('http://', 'https://', '/')): return value from django.urls import get_script_prefix - - return "%s%s" % (get_script_prefix(), value) + return '%s%s' % (get_script_prefix(), value) @property def configured(self): @@ -149,25 +143,18 @@ class LazySettings(LazyObject): return self._wrapped is not empty @property - def USE_L10N(self): + def PASSWORD_RESET_TIMEOUT_DAYS(self): stack = traceback.extract_stack() # Show a warning if the setting is used outside of Django. # Stack index: -1 this line, -2 the caller. filename, _, _, _ = stack[-2] if not filename.startswith(os.path.dirname(django.__file__)): warnings.warn( - USE_L10N_DEPRECATED_MSG, - RemovedInDjango50Warning, + PASSWORD_RESET_TIMEOUT_DAYS_DEPRECATED_MSG, + RemovedInDjango40Warning, stacklevel=2, ) - return self.__getattr__("USE_L10N") - - # RemovedInDjango50Warning. - @property - def _USE_L10N_INTERNAL(self): - # Special hook to avoid checking a traceback in internal use on hot - # paths. - return self.__getattr__("USE_L10N") + return self.__getattr__('PASSWORD_RESET_TIMEOUT_DAYS') class Settings: @@ -183,7 +170,6 @@ class Settings: mod = importlib.import_module(self.SETTINGS_MODULE) tuple_settings = ( - "ALLOWED_HOSTS", "INSTALLED_APPS", "TEMPLATE_DIRS", "LOCALE_PATHS", @@ -193,54 +179,48 @@ class Settings: if setting.isupper(): setting_value = getattr(mod, setting) - if setting in tuple_settings and not isinstance( - setting_value, (list, tuple) - ): - raise ImproperlyConfigured( - "The %s setting must be a list or a tuple." % setting - ) + if (setting in tuple_settings and + not isinstance(setting_value, (list, tuple))): + raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting) setattr(self, setting, setting_value) self._explicit_settings.add(setting) - if self.USE_TZ is False and not self.is_overridden("USE_TZ"): - warnings.warn( - "The default value of USE_TZ will change from False to True " - "in Django 5.0. Set USE_TZ to False in your project settings " - "if you want to keep the current default behavior.", - category=RemovedInDjango50Warning, - ) + if self.is_overridden('PASSWORD_RESET_TIMEOUT_DAYS'): + if self.is_overridden('PASSWORD_RESET_TIMEOUT'): + raise ImproperlyConfigured( + 'PASSWORD_RESET_TIMEOUT_DAYS/PASSWORD_RESET_TIMEOUT are ' + 'mutually exclusive.' + ) + setattr(self, 'PASSWORD_RESET_TIMEOUT', self.PASSWORD_RESET_TIMEOUT_DAYS * 60 * 60 * 24) + warnings.warn(PASSWORD_RESET_TIMEOUT_DAYS_DEPRECATED_MSG, RemovedInDjango40Warning) - if self.is_overridden("USE_DEPRECATED_PYTZ"): - warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning) + if self.is_overridden('DEFAULT_HASHING_ALGORITHM'): + warnings.warn(DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, RemovedInDjango40Warning) - if hasattr(time, "tzset") and self.TIME_ZONE: + if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. - zoneinfo_root = Path("/usr/share/zoneinfo") - zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split("/")) + zoneinfo_root = Path('/usr/share/zoneinfo') + zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split('/')) if zoneinfo_root.exists() and not zone_info_file.exists(): raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE) # Move the time zone info into os.environ. See ticket #2315 for why # we don't do this unconditionally (breaks Windows). - os.environ["TZ"] = self.TIME_ZONE + os.environ['TZ'] = self.TIME_ZONE time.tzset() - if self.is_overridden("USE_L10N"): - warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning) - def is_overridden(self, setting): return setting in self._explicit_settings def __repr__(self): return '<%(cls)s "%(settings_module)s">' % { - "cls": self.__class__.__name__, - "settings_module": self.SETTINGS_MODULE, + 'cls': self.__class__.__name__, + 'settings_module': self.SETTINGS_MODULE, } class UserSettingsHolder: """Holder for user configured settings.""" - # SETTINGS_MODULE doesn't make much sense in the manually configured # (standalone) case. SETTINGS_MODULE = None @@ -250,7 +230,7 @@ class UserSettingsHolder: Requests for configuration variables not in this class are satisfied from the module specified in default_settings (if possible). """ - self.__dict__["_deleted"] = set() + self.__dict__['_deleted'] = set() self.default_settings = default_settings def __getattr__(self, name): @@ -260,11 +240,12 @@ class UserSettingsHolder: def __setattr__(self, name, value): self._deleted.discard(name) - if name == "USE_L10N": - warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning) + if name == 'PASSWORD_RESET_TIMEOUT_DAYS': + setattr(self, 'PASSWORD_RESET_TIMEOUT', value * 60 * 60 * 24) + warnings.warn(PASSWORD_RESET_TIMEOUT_DAYS_DEPRECATED_MSG, RemovedInDjango40Warning) + if name == 'DEFAULT_HASHING_ALGORITHM': + warnings.warn(DEFAULT_HASHING_ALGORITHM_DEPRECATED_MSG, RemovedInDjango40Warning) super().__setattr__(name, value) - if name == "USE_DEPRECATED_PYTZ": - warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning) def __delattr__(self, name): self._deleted.add(name) @@ -273,22 +254,19 @@ class UserSettingsHolder: def __dir__(self): return sorted( - s - for s in [*self.__dict__, *dir(self.default_settings)] + s for s in [*self.__dict__, *dir(self.default_settings)] if s not in self._deleted ) def is_overridden(self, setting): - deleted = setting in self._deleted - set_locally = setting in self.__dict__ - set_on_default = getattr( - self.default_settings, "is_overridden", lambda s: False - )(setting) + deleted = (setting in self._deleted) + set_locally = (setting in self.__dict__) + set_on_default = getattr(self.default_settings, 'is_overridden', lambda s: False)(setting) return deleted or set_locally or set_on_default def __repr__(self): - return "<%(cls)s>" % { - "cls": self.__class__.__name__, + return '<%(cls)s>' % { + 'cls': self.__class__.__name__, } diff --git a/venv/Lib/site-packages/django/conf/global_settings.py b/venv/Lib/site-packages/django/conf/global_settings.py index ba0f391..cf9fae4 100644 --- a/venv/Lib/site-packages/django/conf/global_settings.py +++ b/venv/Lib/site-packages/django/conf/global_settings.py @@ -21,8 +21,8 @@ DEBUG = False # on a live site. DEBUG_PROPAGATE_EXCEPTIONS = False -# People who get code error notifications. In the format -# [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')] +# People who get code error notifications. +# In the format [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')] ADMINS = [] # List of IP addresses, as strings, that: @@ -38,119 +38,113 @@ ALLOWED_HOSTS = [] # 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 = 'America/Chicago' # If you set this to True, Django will use timezone-aware datetimes. USE_TZ = False -# RemovedInDjango50Warning: It's a transitional setting helpful in migrating -# from pytz tzinfo to ZoneInfo(). Set True to continue using pytz tzinfo -# objects during the Django 4.x release cycle. -USE_DEPRECATED_PYTZ = False - # 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 = 'en-us' # Languages we provide translations for, out of the box. LANGUAGES = [ - ("af", gettext_noop("Afrikaans")), - ("ar", gettext_noop("Arabic")), - ("ar-dz", gettext_noop("Algerian 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")), - ("hy", gettext_noop("Armenian")), - ("ia", gettext_noop("Interlingua")), - ("id", gettext_noop("Indonesian")), - ("ig", gettext_noop("Igbo")), - ("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")), - ("ky", gettext_noop("Kyrgyz")), - ("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")), - ("ms", gettext_noop("Malay")), - ("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")), - ("tg", gettext_noop("Tajik")), - ("th", gettext_noop("Thai")), - ("tk", gettext_noop("Turkmen")), - ("tr", gettext_noop("Turkish")), - ("tt", gettext_noop("Tatar")), - ("udm", gettext_noop("Udmurt")), - ("uk", gettext_noop("Ukrainian")), - ("ur", gettext_noop("Urdu")), - ("uz", gettext_noop("Uzbek")), - ("vi", gettext_noop("Vietnamese")), - ("zh-hans", gettext_noop("Simplified Chinese")), - ("zh-hant", gettext_noop("Traditional Chinese")), + ('af', gettext_noop('Afrikaans')), + ('ar', gettext_noop('Arabic')), + ('ar-dz', gettext_noop('Algerian 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')), + ('hy', gettext_noop('Armenian')), + ('ia', gettext_noop('Interlingua')), + ('id', gettext_noop('Indonesian')), + ('ig', gettext_noop('Igbo')), + ('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')), + ('ky', gettext_noop('Kyrgyz')), + ('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')), + ('tg', gettext_noop('Tajik')), + ('th', gettext_noop('Thai')), + ('tk', gettext_noop('Turkmen')), + ('tr', gettext_noop('Turkish')), + ('tt', gettext_noop('Tatar')), + ('udm', gettext_noop('Udmurt')), + ('uk', gettext_noop('Ukrainian')), + ('ur', gettext_noop('Urdu')), + ('uz', gettext_noop('Uzbek')), + ('vi', gettext_noop('Vietnamese')), + ('zh-hans', gettext_noop('Simplified Chinese')), + ('zh-hant', gettext_noop('Traditional Chinese')), ] # Languages using BiDi (right-to-left) layout @@ -162,10 +156,10 @@ USE_I18N = True LOCALE_PATHS = [] # Settings for language cookie -LANGUAGE_COOKIE_NAME = "django_language" +LANGUAGE_COOKIE_NAME = 'django_language' LANGUAGE_COOKIE_AGE = None LANGUAGE_COOKIE_DOMAIN = None -LANGUAGE_COOKIE_PATH = "/" +LANGUAGE_COOKIE_PATH = '/' LANGUAGE_COOKIE_SECURE = False LANGUAGE_COOKIE_HTTPONLY = False LANGUAGE_COOKIE_SAMESITE = None @@ -173,7 +167,7 @@ LANGUAGE_COOKIE_SAMESITE = None # If you set this to True, Django will format dates, numbers and calendars # according to user current locale. -USE_L10N = True +USE_L10N = False # Not-necessarily-technical managers of the site. They get broken link # notifications and other various emails. @@ -181,10 +175,10 @@ MANAGERS = ADMINS # Default charset to use for all HttpResponse objects, if a MIME type isn't # manually specified. It's used to construct the Content-Type header. -DEFAULT_CHARSET = "utf-8" +DEFAULT_CHARSET = 'utf-8' # Email address that error messages come from. -SERVER_EMAIL = "root@localhost" +SERVER_EMAIL = 'root@localhost' # Database connection info. If left empty, will default to the dummy backend. DATABASES = {} @@ -196,10 +190,10 @@ DATABASE_ROUTERS = [] # 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 = 'django.core.mail.backends.smtp.EmailBackend' # Host for sending email. -EMAIL_HOST = "localhost" +EMAIL_HOST = 'localhost' # Port for sending email. EMAIL_PORT = 25 @@ -208,8 +202,8 @@ EMAIL_PORT = 25 EMAIL_USE_LOCALTIME = False # Optional SMTP authentication information for EMAIL_HOST. -EMAIL_HOST_USER = "" -EMAIL_HOST_PASSWORD = "" +EMAIL_HOST_USER = '' +EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False EMAIL_USE_SSL = False EMAIL_SSL_CERTFILE = None @@ -222,15 +216,15 @@ INSTALLED_APPS = [] TEMPLATES = [] # Default form rendering class. -FORM_RENDERER = "django.forms.renderers.DjangoTemplates" +FORM_RENDERER = 'django.forms.renderers.DjangoTemplates' # Default email address to use for various automated correspondence from # the site managers. -DEFAULT_FROM_EMAIL = "webmaster@localhost" +DEFAULT_FROM_EMAIL = 'webmaster@localhost' # 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 = '[Django] ' # Whether to append trailing slashes to URLs. APPEND_SLASH = True @@ -270,18 +264,18 @@ IGNORABLE_404_URLS = [] # 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 = '' # Default file storage mechanism that holds media. -DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" +DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/var/www/example.com/media/" -MEDIA_ROOT = "" +MEDIA_ROOT = '' # URL that handles the media served from MEDIA_ROOT. # Examples: "http://example.com/media/", "http://media.example.com/" -MEDIA_URL = "" +MEDIA_URL = '' # Absolute path to the directory static files should be collected to. # Example: "/var/www/example.com/static/" @@ -293,8 +287,8 @@ STATIC_URL = None # List of upload handler classes to be applied in order. FILE_UPLOAD_HANDLERS = [ - "django.core.files.uploadhandler.MemoryFileUploadHandler", - "django.core.files.uploadhandler.TemporaryFileUploadHandler", + 'django.core.files.uploadhandler.MemoryFileUploadHandler', + 'django.core.files.uploadhandler.TemporaryFileUploadHandler', ] # Maximum size, in bytes, of a request before it will be streamed to the @@ -315,8 +309,7 @@ DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000 FILE_UPLOAD_TEMP_DIR = None # 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. +# you'd pass directly to os.chmod; see https://docs.python.org/library/os.html#files-and-directories. FILE_UPLOAD_PERMISSIONS = 0o644 # The numeric mode to assign to newly-created directories, when uploading files. @@ -332,51 +325,45 @@ FORMAT_MODULE_PATH = None # 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 = 'N j, Y' # 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 = 'N j, Y, P' # 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 = 'P' # 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 = 'F Y' # 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 = 'F j' # 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 = 'm/d/Y' # 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 = 'm/d/Y P' # 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", # '2006-10-25' - "%m/%d/%Y", # '10/25/2006' - "%m/%d/%y", # '10/25/06' - "%b %d %Y", # 'Oct 25 2006' - "%b %d, %Y", # 'Oct 25, 2006' - "%d %b %Y", # '25 Oct 2006' - "%d %b, %Y", # '25 Oct, 2006' - "%B %d %Y", # 'October 25 2006' - "%B %d, %Y", # 'October 25, 2006' - "%d %B %Y", # '25 October 2006' - "%d %B, %Y", # '25 October, 2006' + '%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' ] # Default formats to be used when parsing times from input boxes, in order @@ -384,9 +371,9 @@ DATE_INPUT_FORMATS = [ # 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", # '14:30:59' - "%H:%M:%S.%f", # '14:30:59.000200' - "%H:%M", # '14:30' + '%H:%M:%S', # '14:30:59' + '%H:%M:%S.%f', # '14:30:59.000200' + '%H:%M', # '14:30' ] # Default formats to be used when parsing dates and times from input boxes, @@ -395,15 +382,15 @@ TIME_INPUT_FORMATS = [ # 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' - "%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 %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' + '%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' + '%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 %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' ] # First day of week, to be used on calendars @@ -411,7 +398,7 @@ DATETIME_INPUT_FORMATS = [ FIRST_DAY_OF_WEEK = 0 # Decimal separator symbol -DECIMAL_SEPARATOR = "." +DECIMAL_SEPARATOR = '.' # Boolean that sets whether to add thousand separator when formatting numbers USE_THOUSAND_SEPARATOR = False @@ -421,17 +408,17 @@ USE_THOUSAND_SEPARATOR = False NUMBER_GROUPING = 0 # Thousand separator symbol -THOUSAND_SEPARATOR = "," +THOUSAND_SEPARATOR = ',' # The tablespaces to use for each model when not specified otherwise. -DEFAULT_TABLESPACE = "" -DEFAULT_INDEX_TABLESPACE = "" +DEFAULT_TABLESPACE = '' +DEFAULT_INDEX_TABLESPACE = '' # Default primary key field type. -DEFAULT_AUTO_FIELD = "django.db.models.AutoField" +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # Default X-Frame-Options header value -X_FRAME_OPTIONS = "DENY" +X_FRAME_OPTIONS = 'DENY' USE_X_FORWARDED_HOST = False USE_X_FORWARDED_PORT = False @@ -452,6 +439,12 @@ WSGI_APPLICATION = None # you may be opening yourself up to a security risk. SECURE_PROXY_SSL_HEADER = None +# Default hashing algorithm to use for encoding cookies, password reset tokens +# in the admin site, user sessions, and signatures. It's a transitional setting +# helpful in migrating multiple instance of the same project to Django 3.1+. +# Algorithm must be 'sha1' or 'sha256'. +DEFAULT_HASHING_ALGORITHM = 'sha256' + ############## # MIDDLEWARE # ############## @@ -466,9 +459,9 @@ MIDDLEWARE = [] ############ # Cache to store session data if using the cache session backend. -SESSION_CACHE_ALIAS = "default" +SESSION_CACHE_ALIAS = 'default' # Cookie name. This can be whatever you want. -SESSION_COOKIE_NAME = "sessionid" +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. @@ -476,23 +469,23 @@ SESSION_COOKIE_DOMAIN = None # Whether the session cookie should be secure (https:// only). SESSION_COOKIE_SECURE = False # The path of the session cookie. -SESSION_COOKIE_PATH = "/" +SESSION_COOKIE_PATH = '/' # Whether to use the HttpOnly flag. SESSION_COOKIE_HTTPONLY = True # Whether to set the flag restricting cookie leaks on cross-site requests. # This can be 'Lax', 'Strict', 'None', or False to disable the flag. -SESSION_COOKIE_SAMESITE = "Lax" +SESSION_COOKIE_SAMESITE = 'Lax' # Whether to save the session data on every request. SESSION_SAVE_EVERY_REQUEST = False -# Whether a user's session cookie expires when the web browser is closed. +# Whether a user's session cookie expires when the Web browser is closed. SESSION_EXPIRE_AT_BROWSER_CLOSE = False # The module to store session data -SESSION_ENGINE = "django.contrib.sessions.backends.db" +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 # class to serialize session data -SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer" +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' ######### # CACHE # @@ -500,28 +493,31 @@ SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer" # The cache backends to use. CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', } } -CACHE_MIDDLEWARE_KEY_PREFIX = "" +CACHE_MIDDLEWARE_KEY_PREFIX = '' CACHE_MIDDLEWARE_SECONDS = 600 -CACHE_MIDDLEWARE_ALIAS = "default" +CACHE_MIDDLEWARE_ALIAS = 'default' ################## # AUTHENTICATION # ################## -AUTH_USER_MODEL = "auth.User" +AUTH_USER_MODEL = 'auth.User' -AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"] +AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend'] -LOGIN_URL = "/accounts/login/" +LOGIN_URL = '/accounts/login/' -LOGIN_REDIRECT_URL = "/accounts/profile/" +LOGIN_REDIRECT_URL = '/accounts/profile/' LOGOUT_REDIRECT_URL = None +# The number of days a password reset link is valid for +PASSWORD_RESET_TIMEOUT_DAYS = 3 + # The number of seconds a password reset link is valid for (default: 3 days). PASSWORD_RESET_TIMEOUT = 60 * 60 * 24 * 3 @@ -529,11 +525,10 @@ PASSWORD_RESET_TIMEOUT = 60 * 60 * 24 * 3 # 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", - "django.contrib.auth.hashers.ScryptPasswordHasher", + 'django.contrib.auth.hashers.PBKDF2PasswordHasher', + 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', + 'django.contrib.auth.hashers.Argon2PasswordHasher', + 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ] AUTH_PASSWORD_VALIDATORS = [] @@ -542,7 +537,7 @@ AUTH_PASSWORD_VALIDATORS = [] # SIGNING # ########### -SIGNING_BACKEND = "django.core.signing.TimestampSigner" +SIGNING_BACKEND = 'django.core.signing.TimestampSigner' ######## # CSRF # @@ -550,17 +545,17 @@ SIGNING_BACKEND = "django.core.signing.TimestampSigner" # Dotted path to callable to be used as view when a request is # rejected by the CSRF middleware. -CSRF_FAILURE_VIEW = "django.views.csrf.csrf_failure" +CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure' # Settings for CSRF cookie. -CSRF_COOKIE_NAME = "csrftoken" +CSRF_COOKIE_NAME = 'csrftoken' CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52 CSRF_COOKIE_DOMAIN = None -CSRF_COOKIE_PATH = "/" +CSRF_COOKIE_PATH = '/' CSRF_COOKIE_SECURE = False CSRF_COOKIE_HTTPONLY = False -CSRF_COOKIE_SAMESITE = "Lax" -CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN" +CSRF_COOKIE_SAMESITE = 'Lax' +CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN' CSRF_TRUSTED_ORIGINS = [] CSRF_USE_SESSIONS = False @@ -569,7 +564,7 @@ CSRF_USE_SESSIONS = False ############ # Class to use as messages backend -MESSAGE_STORAGE = "django.contrib.messages.storage.fallback.FallbackStorage" +MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage' # Default values of MESSAGE_LEVEL and MESSAGE_TAGS are defined within # django.contrib.messages to avoid imports in this settings file. @@ -579,25 +574,25 @@ MESSAGE_STORAGE = "django.contrib.messages.storage.fallback.FallbackStorage" ########### # The callable to use to configure logging -LOGGING_CONFIG = "logging.config.dictConfig" +LOGGING_CONFIG = 'logging.config.dictConfig' # Custom logging configuration. LOGGING = {} # Default exception reporter class used in case none has been # specifically assigned to the HttpRequest instance. -DEFAULT_EXCEPTION_REPORTER = "django.views.debug.ExceptionReporter" +DEFAULT_EXCEPTION_REPORTER = 'django.views.debug.ExceptionReporter' # Default exception reporter filter class used in case none has been # specifically assigned to the HttpRequest instance. -DEFAULT_EXCEPTION_REPORTER_FILTER = "django.views.debug.SafeExceptionReporterFilter" +DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter' ########### # TESTING # ########### # The name of the class to use to run the test suite -TEST_RUNNER = "django.test.runner.DiscoverRunner" +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) @@ -618,13 +613,13 @@ FIXTURE_DIRS = [] STATICFILES_DIRS = [] # The default file storage backend used during the build process -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage" +STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' # 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.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ] @@ -648,12 +643,12 @@ SILENCED_SYSTEM_CHECKS = [] ####################### # SECURITY MIDDLEWARE # ####################### +SECURE_BROWSER_XSS_FILTER = False SECURE_CONTENT_TYPE_NOSNIFF = True -SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin" SECURE_HSTS_INCLUDE_SUBDOMAINS = False SECURE_HSTS_PRELOAD = False SECURE_HSTS_SECONDS = 0 SECURE_REDIRECT_EXEMPT = [] -SECURE_REFERRER_POLICY = "same-origin" +SECURE_REFERRER_POLICY = 'same-origin' SECURE_SSL_HOST = None SECURE_SSL_REDIRECT = False diff --git a/venv/Lib/site-packages/django/conf/locale/__init__.py b/venv/Lib/site-packages/django/conf/locale/__init__.py index a18d577..6285f20 100644 --- a/venv/Lib/site-packages/django/conf/locale/__init__.py +++ b/venv/Lib/site-packages/django/conf/locale/__init__.py @@ -8,610 +8,604 @@ follow the traditional 'fr-ca' -> 'fr' fallback logic. """ LANG_INFO = { - "af": { - "bidi": False, - "code": "af", - "name": "Afrikaans", - "name_local": "Afrikaans", - }, - "ar": { - "bidi": True, - "code": "ar", - "name": "Arabic", - "name_local": "العربيّة", - }, - "ar-dz": { - "bidi": True, - "code": "ar-dz", - "name": "Algerian Arabic", - "name_local": "العربية الجزائرية", - }, - "ast": { - "bidi": False, - "code": "ast", - "name": "Asturian", - "name_local": "asturianu", - }, - "az": { - "bidi": True, - "code": "az", - "name": "Azerbaijani", - "name_local": "Azərbaycanca", - }, - "be": { - "bidi": False, - "code": "be", - "name": "Belarusian", - "name_local": "беларуская", - }, - "bg": { - "bidi": False, - "code": "bg", - "name": "Bulgarian", - "name_local": "български", - }, - "bn": { - "bidi": False, - "code": "bn", - "name": "Bengali", - "name_local": "বাংলা", - }, - "br": { - "bidi": False, - "code": "br", - "name": "Breton", - "name_local": "brezhoneg", - }, - "bs": { - "bidi": False, - "code": "bs", - "name": "Bosnian", - "name_local": "bosanski", - }, - "ca": { - "bidi": False, - "code": "ca", - "name": "Catalan", - "name_local": "català", - }, - "cs": { - "bidi": False, - "code": "cs", - "name": "Czech", - "name_local": "česky", - }, - "cy": { - "bidi": False, - "code": "cy", - "name": "Welsh", - "name_local": "Cymraeg", - }, - "da": { - "bidi": False, - "code": "da", - "name": "Danish", - "name_local": "dansk", - }, - "de": { - "bidi": False, - "code": "de", - "name": "German", - "name_local": "Deutsch", - }, - "dsb": { - "bidi": False, - "code": "dsb", - "name": "Lower Sorbian", - "name_local": "dolnoserbski", - }, - "el": { - "bidi": False, - "code": "el", - "name": "Greek", - "name_local": "Ελληνικά", - }, - "en": { - "bidi": False, - "code": "en", - "name": "English", - "name_local": "English", - }, - "en-au": { - "bidi": False, - "code": "en-au", - "name": "Australian English", - "name_local": "Australian English", - }, - "en-gb": { - "bidi": False, - "code": "en-gb", - "name": "British English", - "name_local": "British English", - }, - "eo": { - "bidi": False, - "code": "eo", - "name": "Esperanto", - "name_local": "Esperanto", - }, - "es": { - "bidi": False, - "code": "es", - "name": "Spanish", - "name_local": "español", - }, - "es-ar": { - "bidi": False, - "code": "es-ar", - "name": "Argentinian Spanish", - "name_local": "español de Argentina", - }, - "es-co": { - "bidi": False, - "code": "es-co", - "name": "Colombian Spanish", - "name_local": "español de Colombia", - }, - "es-mx": { - "bidi": False, - "code": "es-mx", - "name": "Mexican Spanish", - "name_local": "español de Mexico", - }, - "es-ni": { - "bidi": False, - "code": "es-ni", - "name": "Nicaraguan Spanish", - "name_local": "español de Nicaragua", - }, - "es-ve": { - "bidi": False, - "code": "es-ve", - "name": "Venezuelan Spanish", - "name_local": "español de Venezuela", - }, - "et": { - "bidi": False, - "code": "et", - "name": "Estonian", - "name_local": "eesti", - }, - "eu": { - "bidi": False, - "code": "eu", - "name": "Basque", - "name_local": "Basque", - }, - "fa": { - "bidi": True, - "code": "fa", - "name": "Persian", - "name_local": "فارسی", - }, - "fi": { - "bidi": False, - "code": "fi", - "name": "Finnish", - "name_local": "suomi", - }, - "fr": { - "bidi": False, - "code": "fr", - "name": "French", - "name_local": "français", - }, - "fy": { - "bidi": False, - "code": "fy", - "name": "Frisian", - "name_local": "frysk", - }, - "ga": { - "bidi": False, - "code": "ga", - "name": "Irish", - "name_local": "Gaeilge", - }, - "gd": { - "bidi": False, - "code": "gd", - "name": "Scottish Gaelic", - "name_local": "Gàidhlig", - }, - "gl": { - "bidi": False, - "code": "gl", - "name": "Galician", - "name_local": "galego", - }, - "he": { - "bidi": True, - "code": "he", - "name": "Hebrew", - "name_local": "עברית", - }, - "hi": { - "bidi": False, - "code": "hi", - "name": "Hindi", - "name_local": "हिंदी", - }, - "hr": { - "bidi": False, - "code": "hr", - "name": "Croatian", - "name_local": "Hrvatski", - }, - "hsb": { - "bidi": False, - "code": "hsb", - "name": "Upper Sorbian", - "name_local": "hornjoserbsce", - }, - "hu": { - "bidi": False, - "code": "hu", - "name": "Hungarian", - "name_local": "Magyar", - }, - "hy": { - "bidi": False, - "code": "hy", - "name": "Armenian", - "name_local": "հայերեն", - }, - "ia": { - "bidi": False, - "code": "ia", - "name": "Interlingua", - "name_local": "Interlingua", - }, - "io": { - "bidi": False, - "code": "io", - "name": "Ido", - "name_local": "ido", - }, - "id": { - "bidi": False, - "code": "id", - "name": "Indonesian", - "name_local": "Bahasa Indonesia", - }, - "ig": { - "bidi": False, - "code": "ig", - "name": "Igbo", - "name_local": "Asụsụ Ìgbò", - }, - "is": { - "bidi": False, - "code": "is", - "name": "Icelandic", - "name_local": "Íslenska", - }, - "it": { - "bidi": False, - "code": "it", - "name": "Italian", - "name_local": "italiano", - }, - "ja": { - "bidi": False, - "code": "ja", - "name": "Japanese", - "name_local": "日本語", - }, - "ka": { - "bidi": False, - "code": "ka", - "name": "Georgian", - "name_local": "ქართული", - }, - "kab": { - "bidi": False, - "code": "kab", - "name": "Kabyle", - "name_local": "taqbaylit", - }, - "kk": { - "bidi": False, - "code": "kk", - "name": "Kazakh", - "name_local": "Қазақ", - }, - "km": { - "bidi": False, - "code": "km", - "name": "Khmer", - "name_local": "Khmer", - }, - "kn": { - "bidi": False, - "code": "kn", - "name": "Kannada", - "name_local": "Kannada", - }, - "ko": { - "bidi": False, - "code": "ko", - "name": "Korean", - "name_local": "한국어", - }, - "ky": { - "bidi": False, - "code": "ky", - "name": "Kyrgyz", - "name_local": "Кыргызча", - }, - "lb": { - "bidi": False, - "code": "lb", - "name": "Luxembourgish", - "name_local": "Lëtzebuergesch", - }, - "lt": { - "bidi": False, - "code": "lt", - "name": "Lithuanian", - "name_local": "Lietuviškai", - }, - "lv": { - "bidi": False, - "code": "lv", - "name": "Latvian", - "name_local": "latviešu", - }, - "mk": { - "bidi": False, - "code": "mk", - "name": "Macedonian", - "name_local": "Македонски", - }, - "ml": { - "bidi": False, - "code": "ml", - "name": "Malayalam", - "name_local": "മലയാളം", - }, - "mn": { - "bidi": False, - "code": "mn", - "name": "Mongolian", - "name_local": "Mongolian", - }, - "mr": { - "bidi": False, - "code": "mr", - "name": "Marathi", - "name_local": "मराठी", - }, - "ms": { - "bidi": False, - "code": "ms", - "name": "Malay", - "name_local": "Bahasa Melayu", - }, - "my": { - "bidi": False, - "code": "my", - "name": "Burmese", - "name_local": "မြန်မာဘာသာ", - }, - "nb": { - "bidi": False, - "code": "nb", - "name": "Norwegian Bokmal", - "name_local": "norsk (bokmål)", - }, - "ne": { - "bidi": False, - "code": "ne", - "name": "Nepali", - "name_local": "नेपाली", - }, - "nl": { - "bidi": False, - "code": "nl", - "name": "Dutch", - "name_local": "Nederlands", - }, - "nn": { - "bidi": False, - "code": "nn", - "name": "Norwegian Nynorsk", - "name_local": "norsk (nynorsk)", - }, - "no": { - "bidi": False, - "code": "no", - "name": "Norwegian", - "name_local": "norsk", - }, - "os": { - "bidi": False, - "code": "os", - "name": "Ossetic", - "name_local": "Ирон", - }, - "pa": { - "bidi": False, - "code": "pa", - "name": "Punjabi", - "name_local": "Punjabi", - }, - "pl": { - "bidi": False, - "code": "pl", - "name": "Polish", - "name_local": "polski", - }, - "pt": { - "bidi": False, - "code": "pt", - "name": "Portuguese", - "name_local": "Português", - }, - "pt-br": { - "bidi": False, - "code": "pt-br", - "name": "Brazilian Portuguese", - "name_local": "Português Brasileiro", - }, - "ro": { - "bidi": False, - "code": "ro", - "name": "Romanian", - "name_local": "Română", - }, - "ru": { - "bidi": False, - "code": "ru", - "name": "Russian", - "name_local": "Русский", - }, - "sk": { - "bidi": False, - "code": "sk", - "name": "Slovak", - "name_local": "Slovensky", - }, - "sl": { - "bidi": False, - "code": "sl", - "name": "Slovenian", - "name_local": "Slovenščina", - }, - "sq": { - "bidi": False, - "code": "sq", - "name": "Albanian", - "name_local": "shqip", - }, - "sr": { - "bidi": False, - "code": "sr", - "name": "Serbian", - "name_local": "српски", - }, - "sr-latn": { - "bidi": False, - "code": "sr-latn", - "name": "Serbian Latin", - "name_local": "srpski (latinica)", - }, - "sv": { - "bidi": False, - "code": "sv", - "name": "Swedish", - "name_local": "svenska", - }, - "sw": { - "bidi": False, - "code": "sw", - "name": "Swahili", - "name_local": "Kiswahili", - }, - "ta": { - "bidi": False, - "code": "ta", - "name": "Tamil", - "name_local": "தமிழ்", - }, - "te": { - "bidi": False, - "code": "te", - "name": "Telugu", - "name_local": "తెలుగు", - }, - "tg": { - "bidi": False, - "code": "tg", - "name": "Tajik", - "name_local": "тоҷикӣ", - }, - "th": { - "bidi": False, - "code": "th", - "name": "Thai", - "name_local": "ภาษาไทย", - }, - "tk": { - "bidi": False, - "code": "tk", - "name": "Turkmen", - "name_local": "Türkmençe", - }, - "tr": { - "bidi": False, - "code": "tr", - "name": "Turkish", - "name_local": "Türkçe", - }, - "tt": { - "bidi": False, - "code": "tt", - "name": "Tatar", - "name_local": "Татарча", - }, - "udm": { - "bidi": False, - "code": "udm", - "name": "Udmurt", - "name_local": "Удмурт", - }, - "uk": { - "bidi": False, - "code": "uk", - "name": "Ukrainian", - "name_local": "Українська", - }, - "ur": { - "bidi": True, - "code": "ur", - "name": "Urdu", - "name_local": "اردو", - }, - "uz": { - "bidi": False, - "code": "uz", - "name": "Uzbek", - "name_local": "oʻzbek tili", - }, - "vi": { - "bidi": False, - "code": "vi", - "name": "Vietnamese", - "name_local": "Tiếng Việt", - }, - "zh-cn": { - "fallback": ["zh-hans"], - }, - "zh-hans": { - "bidi": False, - "code": "zh-hans", - "name": "Simplified Chinese", - "name_local": "简体中文", - }, - "zh-hant": { - "bidi": False, - "code": "zh-hant", - "name": "Traditional Chinese", - "name_local": "繁體中文", - }, - "zh-hk": { - "fallback": ["zh-hant"], - }, - "zh-mo": { - "fallback": ["zh-hant"], - }, - "zh-my": { - "fallback": ["zh-hans"], - }, - "zh-sg": { - "fallback": ["zh-hans"], - }, - "zh-tw": { - "fallback": ["zh-hant"], + 'af': { + 'bidi': False, + 'code': 'af', + 'name': 'Afrikaans', + 'name_local': 'Afrikaans', + }, + 'ar': { + 'bidi': True, + 'code': 'ar', + 'name': 'Arabic', + 'name_local': 'العربيّة', + }, + 'ar-dz': { + 'bidi': True, + 'code': 'ar-dz', + 'name': 'Algerian Arabic', + 'name_local': 'العربية الجزائرية', + }, + 'ast': { + 'bidi': False, + 'code': 'ast', + 'name': 'Asturian', + 'name_local': 'asturianu', + }, + 'az': { + 'bidi': True, + 'code': 'az', + 'name': 'Azerbaijani', + 'name_local': 'Azərbaycanca', + }, + 'be': { + 'bidi': False, + 'code': 'be', + 'name': 'Belarusian', + 'name_local': 'беларуская', + }, + 'bg': { + 'bidi': False, + 'code': 'bg', + 'name': 'Bulgarian', + 'name_local': 'български', + }, + 'bn': { + 'bidi': False, + 'code': 'bn', + 'name': 'Bengali', + 'name_local': 'বাংলা', + }, + 'br': { + 'bidi': False, + 'code': 'br', + 'name': 'Breton', + 'name_local': 'brezhoneg', + }, + 'bs': { + 'bidi': False, + 'code': 'bs', + 'name': 'Bosnian', + 'name_local': 'bosanski', + }, + 'ca': { + 'bidi': False, + 'code': 'ca', + 'name': 'Catalan', + 'name_local': 'català', + }, + 'cs': { + 'bidi': False, + 'code': 'cs', + 'name': 'Czech', + 'name_local': 'česky', + }, + 'cy': { + 'bidi': False, + 'code': 'cy', + 'name': 'Welsh', + 'name_local': 'Cymraeg', + }, + 'da': { + 'bidi': False, + 'code': 'da', + 'name': 'Danish', + 'name_local': 'dansk', + }, + 'de': { + 'bidi': False, + 'code': 'de', + 'name': 'German', + 'name_local': 'Deutsch', + }, + 'dsb': { + 'bidi': False, + 'code': 'dsb', + 'name': 'Lower Sorbian', + 'name_local': 'dolnoserbski', + }, + 'el': { + 'bidi': False, + 'code': 'el', + 'name': 'Greek', + 'name_local': 'Ελληνικά', + }, + 'en': { + 'bidi': False, + 'code': 'en', + 'name': 'English', + 'name_local': 'English', + }, + 'en-au': { + 'bidi': False, + 'code': 'en-au', + 'name': 'Australian English', + 'name_local': 'Australian English', + }, + 'en-gb': { + 'bidi': False, + 'code': 'en-gb', + 'name': 'British English', + 'name_local': 'British English', + }, + 'eo': { + 'bidi': False, + 'code': 'eo', + 'name': 'Esperanto', + 'name_local': 'Esperanto', + }, + 'es': { + 'bidi': False, + 'code': 'es', + 'name': 'Spanish', + 'name_local': 'español', + }, + 'es-ar': { + 'bidi': False, + 'code': 'es-ar', + 'name': 'Argentinian Spanish', + 'name_local': 'español de Argentina', + }, + 'es-co': { + 'bidi': False, + 'code': 'es-co', + 'name': 'Colombian Spanish', + 'name_local': 'español de Colombia', + }, + 'es-mx': { + 'bidi': False, + 'code': 'es-mx', + 'name': 'Mexican Spanish', + 'name_local': 'español de Mexico', + }, + 'es-ni': { + 'bidi': False, + 'code': 'es-ni', + 'name': 'Nicaraguan Spanish', + 'name_local': 'español de Nicaragua', + }, + 'es-ve': { + 'bidi': False, + 'code': 'es-ve', + 'name': 'Venezuelan Spanish', + 'name_local': 'español de Venezuela', + }, + 'et': { + 'bidi': False, + 'code': 'et', + 'name': 'Estonian', + 'name_local': 'eesti', + }, + 'eu': { + 'bidi': False, + 'code': 'eu', + 'name': 'Basque', + 'name_local': 'Basque', + }, + 'fa': { + 'bidi': True, + 'code': 'fa', + 'name': 'Persian', + 'name_local': 'فارسی', + }, + 'fi': { + 'bidi': False, + 'code': 'fi', + 'name': 'Finnish', + 'name_local': 'suomi', + }, + 'fr': { + 'bidi': False, + 'code': 'fr', + 'name': 'French', + 'name_local': 'français', + }, + 'fy': { + 'bidi': False, + 'code': 'fy', + 'name': 'Frisian', + 'name_local': 'frysk', + }, + 'ga': { + 'bidi': False, + 'code': 'ga', + 'name': 'Irish', + 'name_local': 'Gaeilge', + }, + 'gd': { + 'bidi': False, + 'code': 'gd', + 'name': 'Scottish Gaelic', + 'name_local': 'Gàidhlig', + }, + 'gl': { + 'bidi': False, + 'code': 'gl', + 'name': 'Galician', + 'name_local': 'galego', + }, + 'he': { + 'bidi': True, + 'code': 'he', + 'name': 'Hebrew', + 'name_local': 'עברית', + }, + 'hi': { + 'bidi': False, + 'code': 'hi', + 'name': 'Hindi', + 'name_local': 'हिंदी', + }, + 'hr': { + 'bidi': False, + 'code': 'hr', + 'name': 'Croatian', + 'name_local': 'Hrvatski', + }, + 'hsb': { + 'bidi': False, + 'code': 'hsb', + 'name': 'Upper Sorbian', + 'name_local': 'hornjoserbsce', + }, + 'hu': { + 'bidi': False, + 'code': 'hu', + 'name': 'Hungarian', + 'name_local': 'Magyar', + }, + 'hy': { + 'bidi': False, + 'code': 'hy', + 'name': 'Armenian', + 'name_local': 'հայերեն', + }, + 'ia': { + 'bidi': False, + 'code': 'ia', + 'name': 'Interlingua', + 'name_local': 'Interlingua', + }, + 'io': { + 'bidi': False, + 'code': 'io', + 'name': 'Ido', + 'name_local': 'ido', + }, + 'id': { + 'bidi': False, + 'code': 'id', + 'name': 'Indonesian', + 'name_local': 'Bahasa Indonesia', + }, + 'ig': { + 'bidi': False, + 'code': 'ig', + 'name': 'Igbo', + 'name_local': 'Asụsụ Ìgbò', + }, + 'is': { + 'bidi': False, + 'code': 'is', + 'name': 'Icelandic', + 'name_local': 'Íslenska', + }, + 'it': { + 'bidi': False, + 'code': 'it', + 'name': 'Italian', + 'name_local': 'italiano', + }, + 'ja': { + 'bidi': False, + 'code': 'ja', + 'name': 'Japanese', + 'name_local': '日本語', + }, + 'ka': { + 'bidi': False, + 'code': 'ka', + 'name': 'Georgian', + 'name_local': 'ქართული', + }, + 'kab': { + 'bidi': False, + 'code': 'kab', + 'name': 'Kabyle', + 'name_local': 'taqbaylit', + }, + 'kk': { + 'bidi': False, + 'code': 'kk', + 'name': 'Kazakh', + 'name_local': 'Қазақ', + }, + 'km': { + 'bidi': False, + 'code': 'km', + 'name': 'Khmer', + 'name_local': 'Khmer', + }, + 'kn': { + 'bidi': False, + 'code': 'kn', + 'name': 'Kannada', + 'name_local': 'Kannada', + }, + 'ko': { + 'bidi': False, + 'code': 'ko', + 'name': 'Korean', + 'name_local': '한국어', + }, + 'ky': { + 'bidi': False, + 'code': 'ky', + 'name': 'Kyrgyz', + 'name_local': 'Кыргызча', + }, + 'lb': { + 'bidi': False, + 'code': 'lb', + 'name': 'Luxembourgish', + 'name_local': 'Lëtzebuergesch', + }, + 'lt': { + 'bidi': False, + 'code': 'lt', + 'name': 'Lithuanian', + 'name_local': 'Lietuviškai', + }, + 'lv': { + 'bidi': False, + 'code': 'lv', + 'name': 'Latvian', + 'name_local': 'latviešu', + }, + 'mk': { + 'bidi': False, + 'code': 'mk', + 'name': 'Macedonian', + 'name_local': 'Македонски', + }, + 'ml': { + 'bidi': False, + 'code': 'ml', + 'name': 'Malayalam', + 'name_local': 'മലയാളം', + }, + 'mn': { + 'bidi': False, + 'code': 'mn', + 'name': 'Mongolian', + 'name_local': 'Mongolian', + }, + 'mr': { + 'bidi': False, + 'code': 'mr', + 'name': 'Marathi', + 'name_local': 'मराठी', + }, + 'my': { + 'bidi': False, + 'code': 'my', + 'name': 'Burmese', + 'name_local': 'မြန်မာဘာသာ', + }, + 'nb': { + 'bidi': False, + 'code': 'nb', + 'name': 'Norwegian Bokmal', + 'name_local': 'norsk (bokmål)', + }, + 'ne': { + 'bidi': False, + 'code': 'ne', + 'name': 'Nepali', + 'name_local': 'नेपाली', + }, + 'nl': { + 'bidi': False, + 'code': 'nl', + 'name': 'Dutch', + 'name_local': 'Nederlands', + }, + 'nn': { + 'bidi': False, + 'code': 'nn', + 'name': 'Norwegian Nynorsk', + 'name_local': 'norsk (nynorsk)', + }, + 'no': { + 'bidi': False, + 'code': 'no', + 'name': 'Norwegian', + 'name_local': 'norsk', + }, + 'os': { + 'bidi': False, + 'code': 'os', + 'name': 'Ossetic', + 'name_local': 'Ирон', + }, + 'pa': { + 'bidi': False, + 'code': 'pa', + 'name': 'Punjabi', + 'name_local': 'Punjabi', + }, + 'pl': { + 'bidi': False, + 'code': 'pl', + 'name': 'Polish', + 'name_local': 'polski', + }, + 'pt': { + 'bidi': False, + 'code': 'pt', + 'name': 'Portuguese', + 'name_local': 'Português', + }, + 'pt-br': { + 'bidi': False, + 'code': 'pt-br', + 'name': 'Brazilian Portuguese', + 'name_local': 'Português Brasileiro', + }, + 'ro': { + 'bidi': False, + 'code': 'ro', + 'name': 'Romanian', + 'name_local': 'Română', + }, + 'ru': { + 'bidi': False, + 'code': 'ru', + 'name': 'Russian', + 'name_local': 'Русский', + }, + 'sk': { + 'bidi': False, + 'code': 'sk', + 'name': 'Slovak', + 'name_local': 'Slovensky', + }, + 'sl': { + 'bidi': False, + 'code': 'sl', + 'name': 'Slovenian', + 'name_local': 'Slovenščina', + }, + 'sq': { + 'bidi': False, + 'code': 'sq', + 'name': 'Albanian', + 'name_local': 'shqip', + }, + 'sr': { + 'bidi': False, + 'code': 'sr', + 'name': 'Serbian', + 'name_local': 'српски', + }, + 'sr-latn': { + 'bidi': False, + 'code': 'sr-latn', + 'name': 'Serbian Latin', + 'name_local': 'srpski (latinica)', + }, + 'sv': { + 'bidi': False, + 'code': 'sv', + 'name': 'Swedish', + 'name_local': 'svenska', + }, + 'sw': { + 'bidi': False, + 'code': 'sw', + 'name': 'Swahili', + 'name_local': 'Kiswahili', + }, + 'ta': { + 'bidi': False, + 'code': 'ta', + 'name': 'Tamil', + 'name_local': 'தமிழ்', + }, + 'te': { + 'bidi': False, + 'code': 'te', + 'name': 'Telugu', + 'name_local': 'తెలుగు', + }, + 'tg': { + 'bidi': False, + 'code': 'tg', + 'name': 'Tajik', + 'name_local': 'тоҷикӣ', + }, + 'th': { + 'bidi': False, + 'code': 'th', + 'name': 'Thai', + 'name_local': 'ภาษาไทย', + }, + 'tk': { + 'bidi': False, + 'code': 'tk', + 'name': 'Turkmen', + 'name_local': 'Türkmençe', + }, + 'tr': { + 'bidi': False, + 'code': 'tr', + 'name': 'Turkish', + 'name_local': 'Türkçe', + }, + 'tt': { + 'bidi': False, + 'code': 'tt', + 'name': 'Tatar', + 'name_local': 'Татарча', + }, + 'udm': { + 'bidi': False, + 'code': 'udm', + 'name': 'Udmurt', + 'name_local': 'Удмурт', + }, + 'uk': { + 'bidi': False, + 'code': 'uk', + 'name': 'Ukrainian', + 'name_local': 'Українська', + }, + 'ur': { + 'bidi': True, + 'code': 'ur', + 'name': 'Urdu', + 'name_local': 'اردو', + }, + 'uz': { + 'bidi': False, + 'code': 'uz', + 'name': 'Uzbek', + 'name_local': 'oʻzbek tili', + }, + 'vi': { + 'bidi': False, + 'code': 'vi', + 'name': 'Vietnamese', + 'name_local': 'Tiếng Việt', + }, + 'zh-cn': { + 'fallback': ['zh-hans'], + }, + 'zh-hans': { + 'bidi': False, + 'code': 'zh-hans', + 'name': 'Simplified Chinese', + 'name_local': '简体中文', + }, + 'zh-hant': { + 'bidi': False, + 'code': 'zh-hant', + 'name': 'Traditional Chinese', + 'name_local': '繁體中文', + }, + 'zh-hk': { + 'fallback': ['zh-hant'], + }, + 'zh-mo': { + 'fallback': ['zh-hant'], + }, + 'zh-my': { + 'fallback': ['zh-hans'], + }, + 'zh-sg': { + 'fallback': ['zh-hans'], + }, + 'zh-tw': { + 'fallback': ['zh-hant'], }, } diff --git a/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.mo index f0a0412..d65a93a 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.po index 25a491b..ccda018 100644 --- a/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ar/LC_MESSAGES/django.po @@ -1,11 +1,10 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2015-2016,2020-2021 +# Bashar Al-Abdulhadi, 2015-2016,2020 # Bashar Al-Abdulhadi, 2014 # Eyad Toma , 2013-2014 # Jannis Leidel , 2011 -# Mariusz Felisiak , 2021 # Muaaz Alsaied, 2020 # Omar Al-Ithawi , 2020 # Ossama Khayat , 2011 @@ -15,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:27+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-15 00:40+0000\n" +"Last-Translator: Bashar Al-Abdulhadi\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -215,9 +214,6 @@ msgstr "المنغوليّة" msgid "Marathi" msgstr "المهاراتية" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "البورمية" @@ -329,11 +325,6 @@ msgstr "الملفات الثابتة" msgid "Syndication" msgstr "توظيف النشر" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "..." - msgid "That page number is not an integer" msgstr "رقم الصفحة هذا ليس عدداً طبيعياً" @@ -617,9 +608,6 @@ msgstr "عدد صحيح" msgid "Big (8 byte) integer" msgstr "عدد صحيح كبير (8 بايت)" -msgid "Small integer" -msgstr "عدد صحيح صغير" - msgid "IPv4 address" msgstr "عنوان IPv4" @@ -646,6 +634,9 @@ msgstr "عدد صحيح صغير موجب" msgid "Slug (up to %(max_length)s)" msgstr "Slug (حتى %(max_length)s)" +msgid "Small integer" +msgstr "عدد صحيح صغير" + msgid "Text" msgstr "نص" @@ -807,33 +798,28 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(الحقل الخفي %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" -"بيانات نموذج الإدارة مفقودة أو تم العبث بها. الحقول المفقودة: " -"%(field_names)s. قد تحتاج إلى تقديم تقرير خطأ إذا استمرت المشكلة." +msgid "ManagementForm data is missing or has been tampered with" +msgstr "بيانات ManagementForm مفقودة أو تم العبث بها" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "الرجاء إرسال %d إستمارة على الأكثر." -msgstr[1] "الرجاء إرسال %d إستمارة على الأكثر." -msgstr[2] "الرجاء إرسال %d إستمارة على الأكثر." -msgstr[3] "الرجاء إرسال %d إستمارة على الأكثر." -msgstr[4] "الرجاء إرسال %d إستمارة على الأكثر." -msgstr[5] "الرجاء إرسال %d إستمارة على الأكثر." +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "الرجاء إرسال %d إستمارة أو أقل." +msgstr[1] "الرجاء إرسال إستمارة %d أو أقل" +msgstr[2] "الرجاء إرسال %d إستمارتين أو أقل" +msgstr[3] "الرجاء إرسال %d إستمارة أو أقل" +msgstr[4] "الرجاء إرسال %d إستمارة أو أقل" +msgstr[5] "الرجاء إرسال %d إستمارة أو أقل" #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "الرجاء إرسال %d إستمارة على الأقل." -msgstr[1] "الرجاء إرسال %d إستمارة على الأقل." -msgstr[2] "الرجاء إرسال %d إستمارة على الأقل." -msgstr[3] "الرجاء إرسال %d إستمارة على الأقل." -msgstr[4] "الرجاء إرسال %d إستمارة على الأقل." -msgstr[5] "الرجاء إرسال %d إستمارة على الأقل." +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "الرجاء إرسال %d إستمارة أو أكثر." +msgstr[1] "الرجاء إرسال إستمارة %d أو أكثر." +msgstr[2] "الرجاء إرسال %d إستمارتين أو أكثر." +msgstr[3] "الرجاء إرسال %d إستمارة أو أكثر." +msgstr[4] "الرجاء إرسال %d إستمارة أو أكثر." +msgstr[5] "الرجاء إرسال %d إستمارة أو أكثر." msgid "Order" msgstr "الترتيب" @@ -1164,7 +1150,7 @@ msgstr "هذا ليس عنوان IPv6 صحيح." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "أو" @@ -1174,64 +1160,64 @@ msgid ", " msgstr "، " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d سنة" -msgstr[1] "%(num)d سنة" -msgstr[2] "%(num)d سنتين" -msgstr[3] "%(num)d سنوات" -msgstr[4] "%(num)d سنوات" -msgstr[5] "%(num)d سنوات" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d سنة" +msgstr[1] "%d سنة" +msgstr[2] "%d سنوات" +msgstr[3] "%d سنوات" +msgstr[4] "%d سنوات" +msgstr[5] "%d سنوات" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d شهر" -msgstr[1] "%(num)d شهر" -msgstr[2] "%(num)d شهرين" -msgstr[3] "%(num)d أشهر" -msgstr[4] "%(num)d أشهر" -msgstr[5] "%(num)d أشهر" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d شهر" +msgstr[1] "%d شهر" +msgstr[2] "%d شهرين" +msgstr[3] "%d أشهر" +msgstr[4] "%d شهر" +msgstr[5] "%d شهر" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d أسبوع" -msgstr[1] "%(num)d أسبوع" -msgstr[2] "%(num)d أسبوعين" -msgstr[3] "%(num)d أسابيع" -msgstr[4] "%(num)d أسابيع" -msgstr[5] "%(num)d أسابيع" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d اسبوع." +msgstr[1] "%d اسبوع." +msgstr[2] "%d أسبوعين" +msgstr[3] "%d أسابيع" +msgstr[4] "%d اسبوع." +msgstr[5] "%d أسبوع" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d يوم" -msgstr[1] "%(num)d يوم" -msgstr[2] "%(num)d يومين" -msgstr[3] "%(num)d أيام" -msgstr[4] "%(num)d يوم" -msgstr[5] "%(num)d أيام" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d يوم" +msgstr[1] "%d يوم" +msgstr[2] "%d يومان" +msgstr[3] "%d أيام" +msgstr[4] "%d يوم" +msgstr[5] "%d يوم" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ساعة" -msgstr[1] "%(num)d ساعة" -msgstr[2] "%(num)d ساعتين" -msgstr[3] "%(num)d ساعات" -msgstr[4] "%(num)d ساعة" -msgstr[5] "%(num)d ساعات" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ساعة" +msgstr[1] "%d ساعة واحدة" +msgstr[2] "%d ساعتين" +msgstr[3] "%d ساعات" +msgstr[4] "%d ساعة" +msgstr[5] "%d ساعة" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d دقيقة" -msgstr[1] "%(num)d دقيقة" -msgstr[2] "%(num)d دقيقتين" -msgstr[3] "%(num)d دقائق" -msgstr[4] "%(num)d دقيقة" -msgstr[5] "%(num)d دقيقة" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d دقيقة" +msgstr[1] "%d دقيقة" +msgstr[2] "%d دقيقتين" +msgstr[3] "%d دقائق" +msgstr[4] "%d دقيقة" +msgstr[5] "%d دقيقة" msgid "Forbidden" msgstr "ممنوع" @@ -1241,13 +1227,13 @@ msgstr "تم الفشل للتحقق من CSRF. تم إنهاء الطلب." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"أنت ترى هذه الرسالة لأن موقع HTTPS هذا يتطلب إرسال “Referer header” بواسطة " -"متصفح الويب الخاص بك، ولكن لم يتم إرسال أي منها. هذا مطلوب لأسباب أمنية، " -"لضمان عدم اختطاف متصفحك من قبل أطراف ثالثة." +"تظهر لك هذه الرسالة لأن موقع HTTPS يتطلب \"رأس مرجعي\" ليتم إرساله بواسطة " +"مستعرض الويب الخاص بك ، ولكن لم يتم إرسال أي منها. هذا العنوان مطلوب لأسباب " +"أمنية ، للتأكد من أن متصفحك لا يتم اختراقه من قبل أطراف ثالثة." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1348,8 +1334,8 @@ msgstr "”%(path)s“ غير موجود" msgid "Index of %(directory)s" msgstr "فهرس لـ %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "تمت عملية التنصيب بنجاح! تهانينا!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "جانغو: إطار الويب للمهتمين بالكمال و لديهم مواعيد تسليم نهائية." #, python-format msgid "" @@ -1359,6 +1345,9 @@ msgstr "" "استعراض ملاحظات الإصدار لجانغو %(version)s" +msgid "The install worked successfully! Congratulations!" +msgstr "تمت عملية التنصيب بنجاح! تهانينا!" + #, python-format msgid "" "You are seeing this page because \n" "Language-Team: Belarusian (http://www.transifex.com/django/django/language/" "be/)\n" @@ -210,9 +210,6 @@ msgstr "Манґольская" msgid "Marathi" msgstr "Маратхі" -msgid "Malay" -msgstr "Малайская" - msgid "Burmese" msgstr "Бірманская" @@ -1149,52 +1146,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d год" -msgstr[1] "%(num)d гадоў" -msgstr[2] "%(num)d гадоў" -msgstr[3] "%(num)d гадоў" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d год" +msgstr[1] "%d гады" +msgstr[2] "%d гадоў" +msgstr[3] "%d гадоў" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месяц" -msgstr[1] "%(num)d месяцаў" -msgstr[2] "%(num)d месяцаў" -msgstr[3] "%(num)d месяцаў" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месяц" +msgstr[1] "%d месяцы" +msgstr[2] "%d месяцаў" +msgstr[3] "%d месяцаў" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d тыдзень" -msgstr[1] "%(num)d тыдняў" -msgstr[2] "%(num)d тыдняў" -msgstr[3] "%(num)d тыдняў" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d тыдзень" +msgstr[1] "%d тыдні" +msgstr[2] "%d тыдняў" +msgstr[3] "%d тыдняў" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d дзень" -msgstr[1] "%(num)d дзён" -msgstr[2] "%(num)d дзён" -msgstr[3] "%(num)d дзён" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d дзень" +msgstr[1] "%d дні" +msgstr[2] "%d дзён" +msgstr[3] "%d дзён" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d гадзіна" -msgstr[1] "%(num)d гадзін" -msgstr[2] "%(num)d гадзін" -msgstr[3] "%(num)d гадзін" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d гадзіна" +msgstr[1] "%d гадзіны" +msgstr[2] "%d гадзін" +msgstr[3] "%d гадзін" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d хвіліна" -msgstr[1] "%(num)d хвілін" -msgstr[2] "%(num)d хвілін" -msgstr[3] "%(num)d хвілін" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d хвіліна" +msgstr[1] "%d хвіліны" +msgstr[2] "%d хвілінаў" +msgstr[3] "%d хвілінаў" msgid "Forbidden" msgstr "Забаронена" @@ -1204,13 +1201,13 @@ msgstr "CSRF-праверка не атрымалася. Запыт спынен msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "Вы бачыце гэта паведамленне, таму што гэты HTTPS-сайт патрабуе каб Referer " -"загаловак быў адасланы вашым аглядальнікам, але гэтага не адбылося. Гэты " -"загаловак неабходны для бяспекі, каб пераканацца, што ваш аглядальнік не " +"загаловак быў адасланы вашым вэб-браўзэрам, але гэтага не адбылося. Гэты " +"загаловак неабходны для бяспекі, каб пераканацца, што ваш браўзэр не " "ўзаламаны трэцімі асобамі." msgid "" diff --git a/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.mo index 5b85f33..a7886df 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.po index 0d4bf7a..e911c00 100644 --- a/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/bg/LC_MESSAGES/django.po @@ -1,12 +1,11 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec , 2022 # Boris Chervenkov , 2012 # Claude Paroz , 2020 # Jannis Leidel , 2011 # Lyuboslav Petrov , 2014 -# Todor Lubenov , 2013-2015 +# Todor Lubenov , 2013-2015 # Venelin Stoykov , 2015-2017 # vestimir , 2014 # Alexander Atanasov , 2012 @@ -14,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-14 11:26+0000\n" -"Last-Translator: arneatec \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-14 21:42+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -26,13 +25,13 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Afrikaans" -msgstr "африкаански" +msgstr "Африкански" msgid "Arabic" msgstr "арабски език" msgid "Algerian Arabic" -msgstr "алжирски арабски" +msgstr "" msgid "Asturian" msgstr "Астурийски" @@ -56,82 +55,82 @@ msgid "Bosnian" msgstr "босненски език" msgid "Catalan" -msgstr "каталански" +msgstr "каталунски език" msgid "Czech" -msgstr "чешки" +msgstr "чешки език" msgid "Welsh" -msgstr "уелски" +msgstr "уелски език" msgid "Danish" -msgstr "датски" +msgstr "датски език" msgid "German" -msgstr "немски" +msgstr "немски език" msgid "Lower Sorbian" -msgstr "долносорбски" +msgstr "" msgid "Greek" -msgstr "гръцки" +msgstr "гръцки език" msgid "English" -msgstr "английски" +msgstr "английски език" msgid "Australian English" -msgstr "австралийски английски" +msgstr "Австралийски Английски" msgid "British English" msgstr "британски английски" msgid "Esperanto" -msgstr "есперанто" +msgstr "Есперанто" msgid "Spanish" -msgstr "испански" +msgstr "испански език" msgid "Argentinian Spanish" msgstr "кастилски" msgid "Colombian Spanish" -msgstr "колумбийски испански" +msgstr "Колумбийски Испански" msgid "Mexican Spanish" -msgstr "мексикански испански" +msgstr "Мексикански испански" msgid "Nicaraguan Spanish" msgstr "никарагуански испански" msgid "Venezuelan Spanish" -msgstr "венецуелски испански" +msgstr "Испански Венецуелски" msgid "Estonian" -msgstr "естонски" +msgstr "естонски език" msgid "Basque" msgstr "баски" msgid "Persian" -msgstr "персийски" +msgstr "персийски език" msgid "Finnish" -msgstr "финландски" +msgstr "финландски език" msgid "French" -msgstr "френски" +msgstr "френски език" msgid "Frisian" -msgstr "фризийски" +msgstr "фризийски език" msgid "Irish" -msgstr "ирландски" +msgstr "ирландски език" msgid "Scottish Gaelic" -msgstr "шотландски галски" +msgstr "" msgid "Galician" -msgstr "галицейски" +msgstr "галицейски език" msgid "Hebrew" msgstr "иврит" @@ -140,178 +139,175 @@ msgid "Hindi" msgstr "хинди" msgid "Croatian" -msgstr "хърватски" +msgstr "хърватски език" msgid "Upper Sorbian" -msgstr "горносорбски" +msgstr "" msgid "Hungarian" -msgstr "унгарски" +msgstr "унгарски език" msgid "Armenian" -msgstr "арменски" +msgstr "" msgid "Interlingua" -msgstr "интерлингва" +msgstr "Международен" msgid "Indonesian" -msgstr "индонезийски" +msgstr "индонезийски език" msgid "Igbo" -msgstr "игбо" +msgstr "" msgid "Ido" -msgstr "идо" +msgstr "Идо" msgid "Icelandic" -msgstr "исландски" +msgstr "исландски език" msgid "Italian" -msgstr "италиански" +msgstr "италиански език" msgid "Japanese" -msgstr "японски" +msgstr "японски език" msgid "Georgian" -msgstr "грузински" +msgstr "грузински език" msgid "Kabyle" -msgstr "кабилски" +msgstr "" msgid "Kazakh" -msgstr "казахски" +msgstr "Казахски" msgid "Khmer" -msgstr "кхмерски" +msgstr "кхмерски език" msgid "Kannada" msgstr "каннада" msgid "Korean" -msgstr "корейски" +msgstr "Корейски" msgid "Kyrgyz" -msgstr "киргизки" +msgstr "" msgid "Luxembourgish" -msgstr "люксембургски" +msgstr "Люксембургски" msgid "Lithuanian" -msgstr "литовски" +msgstr "Литовски" msgid "Latvian" -msgstr "латвийски" +msgstr "Латвийски" msgid "Macedonian" -msgstr "македонски" +msgstr "Македонски" msgid "Malayalam" msgstr "малаялам" msgid "Mongolian" -msgstr "монголски" +msgstr "Монголски" msgid "Marathi" -msgstr "марати" - -msgid "Malay" -msgstr "малайски" +msgstr "Марати" msgid "Burmese" -msgstr "бирмански" +msgstr "Бурмесе" msgid "Norwegian Bokmål" -msgstr "норвежки букмол" +msgstr "" msgid "Nepali" -msgstr "непалски" +msgstr "Непалски" msgid "Dutch" -msgstr "нидерландски" +msgstr "холандски" msgid "Norwegian Nynorsk" -msgstr "съвременен норвежки" +msgstr "норвежки съвременен език" msgid "Ossetic" -msgstr "осетски" +msgstr "Осетски" msgid "Punjabi" -msgstr "панджабски" +msgstr "пенджаби" msgid "Polish" -msgstr "полски" +msgstr "полски език" msgid "Portuguese" -msgstr "португалски" +msgstr "португалски език" msgid "Brazilian Portuguese" msgstr "бразилски португалски" msgid "Romanian" -msgstr "румънски" +msgstr "румънски език" msgid "Russian" -msgstr "руски" +msgstr "руски език" msgid "Slovak" -msgstr "словашки" +msgstr "словашки език" msgid "Slovenian" -msgstr "словенски" +msgstr "словенски език" msgid "Albanian" -msgstr "албански" +msgstr "албански език" msgid "Serbian" -msgstr "сръбски" +msgstr "сръбски език" msgid "Serbian Latin" -msgstr "сръбски - латиница" +msgstr "сръбски с латински букви" msgid "Swedish" -msgstr "шведски" +msgstr "шведски език" msgid "Swahili" -msgstr "суахили" +msgstr "Суахили" msgid "Tamil" -msgstr "тамилски" +msgstr "тамил" msgid "Telugu" msgstr "телугу" msgid "Tajik" -msgstr "таджикски" +msgstr "" msgid "Thai" -msgstr "тайландски" +msgstr "тайландски език" msgid "Turkmen" -msgstr "туркменски" +msgstr "" msgid "Turkish" -msgstr "турски" +msgstr "турски език" msgid "Tatar" -msgstr "татарски" +msgstr "Татарски" msgid "Udmurt" -msgstr "удмурт" +msgstr "Удмурт" msgid "Ukrainian" -msgstr "украински" +msgstr "украински език" msgid "Urdu" -msgstr "урду" +msgstr "Урду" msgid "Uzbek" -msgstr "узбекски" +msgstr "" msgid "Vietnamese" -msgstr "виетнамски" +msgstr "виетнамски език" msgid "Simplified Chinese" -msgstr "китайски" +msgstr "китайски език" msgid "Traditional Chinese" msgstr "традиционен китайски" @@ -320,18 +316,13 @@ msgid "Messages" msgstr "Съобщения" msgid "Site Maps" -msgstr "Карти на сайта" +msgstr "Бързи Maps" msgid "Static Files" msgstr "Статични файлове" msgid "Syndication" -msgstr "Синдикация" - -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "..." +msgstr "Syndication" msgid "That page number is not an integer" msgstr "Номерът на страницата не е цяло число" @@ -349,7 +340,7 @@ msgid "Enter a valid URL." msgstr "Въведете валиден URL адрес." msgid "Enter a valid integer." -msgstr "Въведете валидно целочислено число." +msgstr "Въведете валидно число." msgid "Enter a valid email address." msgstr "Въведете валиден имейл адрес." @@ -358,14 +349,11 @@ msgstr "Въведете валиден имейл адрес." msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Въведете валиден 'слъг', състоящ се от букви, цифри, тирета или долни тирета." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Въведете валиден 'слъг', състоящ се от Уникод букви, цифри, тирета или долни " -"тирета." msgid "Enter a valid IPv4 address." msgstr "Въведете валиден IPv4 адрес." @@ -426,7 +414,7 @@ msgstr "Въведете число." #, python-format msgid "Ensure that there are no more than %(max)s digit in total." msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "Уверете се, че има не повече от %(max)s цифри общо." +msgstr[0] "Уверете се, че има не повече от %(max)s цифри в общо." msgstr[1] "Уверете се, че има не повече от %(max)s цифри общо." #, python-format @@ -443,7 +431,7 @@ msgid "" msgid_plural "" "Ensure that there are no more than %(max)s digits before the decimal point." msgstr[0] "" -"Уверете се, че има не повече от %(max)s цифра преди десетичната запетая." +"Уверете се, че има не повече от %(max)s цифри преди десетичната запетая." msgstr[1] "" "Уверете се, че има не повече от %(max)s цифри преди десетичната запетая." @@ -452,18 +440,16 @@ msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -"Не са разрешени файлове с раширение \"%(extension)s\". Позволените " -"разширения са: %(allowed_extensions)s." msgid "Null characters are not allowed." -msgstr "Празни знаци не са разрешени." +msgstr "" msgid "and" msgstr "и" #, python-format msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "%(model_name)s с този %(field_labels)s вече съществува." +msgstr "%(model_name)s с тези %(field_labels)s вече съществува." #, python-format msgid "Value %(value)r is not a valid choice." @@ -485,7 +471,8 @@ msgstr "%(model_name)s с този %(field_label)s вече съществува msgid "" "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." msgstr "" -"%(field_label)s трябва да е уникално за %(date_field_label)s %(lookup_type)s." +"%(field_label)s трябва да са уникални за %(date_field_label)s " +"%(lookup_type)s." #, python-format msgid "Field of type: %(field_type)s" @@ -493,14 +480,14 @@ msgstr "Поле от тип: %(field_type)s" #, python-format msgid "“%(value)s” value must be either True or False." -msgstr "Стойността на \"%(value)s\" трябва да бъде или True, или False." +msgstr "" #, python-format msgid "“%(value)s” value must be either True, False, or None." -msgstr "Стойност \"%(value)s\" трябва да бъде или True, или False или None." +msgstr "" msgid "Boolean (Either True or False)" -msgstr "Булево (True или False)" +msgstr "Boolean (True или False)" #, python-format msgid "String (up to %(max_length)s)" @@ -514,16 +501,12 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"Стойността \"%(value)s\" е с невалиден формат за дата. Тя трябва да бъде в " -"ГГГГ-ММ-ДД формат." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -"Стойността \"%(value)s\" е в правилния формат (ГГГГ-ММ-ДД), но самата дата е " -"невалидна." msgid "Date (without time)" msgstr "Дата (без час)" @@ -533,23 +516,19 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"Стойността '%(value)s' е с невалиден формат. Трябва да бъде във формат ГГГГ-" -"ММ-ДД ЧЧ:ММ[:сс[.uuuuuu]][TZ]" #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -"Стойността '%(value)s' е с правилен формат ( ГГГГ-ММ-ДД ЧЧ:ММ[:сс[.μμμμμμ]]" -"[TZ]), но датата/часът са невалидни" msgid "Date (with time)" msgstr "Дата (и час)" #, python-format msgid "“%(value)s” value must be a decimal number." -msgstr "Стойността \"%(value)s\" трябва да е десетично число." +msgstr "" msgid "Decimal number" msgstr "Десетична дроб" @@ -559,28 +538,26 @@ msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -"Стойността “%(value)s” е с невалиден формат. Трябва да бъде във формат [ДД] " -"[[ЧЧ:]ММ:]сс[.uuuuuu] format." msgid "Duration" msgstr "Продължителност" msgid "Email address" -msgstr "Имейл адрес" +msgstr "Email адрес" msgid "File path" msgstr "Път към файл" #, python-format msgid "“%(value)s” value must be a float." -msgstr "Стойността '%(value)s' трябва да е число с плаваща запетая." +msgstr "" msgid "Floating point number" msgstr "Число с плаваща запетая" #, python-format msgid "“%(value)s” value must be an integer." -msgstr "Стойността \"%(value)s\" трябва да е цяло число." +msgstr "" msgid "Integer" msgstr "Цяло число" @@ -588,9 +565,6 @@ msgstr "Цяло число" msgid "Big (8 byte) integer" msgstr "Голямо (8 байта) цяло число" -msgid "Small integer" -msgstr "2 байта цяло число" - msgid "IPv4 address" msgstr "IPv4 адрес" @@ -599,13 +573,13 @@ msgstr "IP адрес" #, python-format msgid "“%(value)s” value must be either None, True or False." -msgstr "Стойността '%(value)s' трябва да бъде None, True или False." +msgstr "" msgid "Boolean (Either True, False or None)" -msgstr "булев (възможните стойности са True, False или None)" +msgstr "Boolean (Възможните стойности са True, False или None)" msgid "Positive big integer" -msgstr "Положително голямо цяло число." +msgstr "" msgid "Positive integer" msgstr "Положително цяло число" @@ -615,7 +589,10 @@ msgstr "Положително 2 байта цяло число" #, python-format msgid "Slug (up to %(max_length)s)" -msgstr "Слъг (до %(max_length)s )" +msgstr "Slug (до %(max_length)s )" + +msgid "Small integer" +msgstr "2 байта цяло число" msgid "Text" msgstr "Текст" @@ -625,16 +602,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"Стойността \"%(value)s\" е с невалиден формат. Тя трябва да бъде в ЧЧ:ММ [:" -"сс[.μμμμμμ]]" #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"Стойността \"%(value)s\" е в правилния формат (ЧЧ:ММ [:сс[.μμμμμμ]]), но " -"часът е невалиден." msgid "Time" msgstr "Време" @@ -647,10 +620,10 @@ msgstr "сурови двоични данни" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "\"%(value)s\" не е валиден UUID." +msgstr "" msgid "Universally unique identifier" -msgstr "Универсално уникален идентификатор" +msgstr "" msgid "File" msgstr "Файл" @@ -659,10 +632,10 @@ msgid "Image" msgstr "Изображение" msgid "A JSON object" -msgstr "Обект във формат JSON" +msgstr "" msgid "Value must be valid JSON." -msgstr "Стойността трябва да е валиден JSON." +msgstr "" #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." @@ -672,18 +645,18 @@ msgid "Foreign Key (type determined by related field)" msgstr "Външен ключ (тип, определен от свързаното поле)" msgid "One-to-one relationship" -msgstr "едно-към-едно релация " +msgstr "словенски език" #, python-format msgid "%(from)s-%(to)s relationship" -msgstr "%(from)s-%(to)s релация" +msgstr "" #, python-format msgid "%(from)s-%(to)s relationships" -msgstr "%(from)s-%(to)s релации" +msgstr "" msgid "Many-to-many relationship" -msgstr "Много-към-много релация" +msgstr "Много-към-много връзка" #. Translators: If found as last label character, these punctuation #. characters will prevent the default label_suffix to be appended to the @@ -698,7 +671,7 @@ msgid "Enter a whole number." msgstr "Въведете цяло число. " msgid "Enter a valid date." -msgstr "Въведете валидна дата." +msgstr "Въведете валидна дата. " msgid "Enter a valid time." msgstr "Въведете валиден час." @@ -711,16 +684,16 @@ msgstr "Въведете валидна продължителност." #, python-brace-format msgid "The number of days must be between {min_days} and {max_days}." -msgstr "Броят на дните трябва да е между {min_days} и {max_days}." +msgstr "" msgid "No file was submitted. Check the encoding type on the form." -msgstr "Няма изпратен файл. Проверете типа кодиране на формата. " +msgstr "Не е получен файл. Проверете типа кодиране на формата. " msgid "No file was submitted." msgstr "Няма изпратен файл." msgid "The submitted file is empty." -msgstr "Изпратеният файл е празен. " +msgstr "Каченият файл е празен. " #, python-format msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." @@ -756,7 +729,7 @@ msgid "Enter a valid UUID." msgstr "Въведете валиден UUID." msgid "Enter a valid JSON." -msgstr "Въведете валиден JSON." +msgstr "" #. Translators: This is the default suffix added to form field labels msgid ":" @@ -766,26 +739,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Скрито поле %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" -"ManagementForm данните липсват или са променяни неправомерно. Липсващи " -"полета: %(field_names)s. Трябва да изпратите уведомление за бъг, ако този " -"проблем продължава." +msgid "ManagementForm data is missing or has been tampered with" +msgstr "Данни за мениджърската форма липсват или са били променени." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "Моля изпратете не повече от %d формуляр." -msgstr[1] "Моля изпратете не повече от %d формуляри." +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Моля, въведете %d по-малко форми." +msgstr[1] "Моля, въведете %d по-малко форми." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Моля изпратете поне %d формуляр." -msgstr[1] "Моля изпратете поне %d формуляра." +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Моля, въведете %d или по-вече форми." +msgstr[1] "Моля, въведете %d или по-вече форми." msgid "Order" msgstr "Ред" @@ -815,22 +782,20 @@ msgid "Please correct the duplicate values below." msgstr "Моля, коригирайте повтарящите се стойности по-долу." msgid "The inline value did not match the parent instance." -msgstr "Стойността в реда не отговаря на родителската инстанция." +msgstr "" msgid "Select a valid choice. That choice is not one of the available choices." msgstr "Направете валиден избор. Този не е един от възможните избори. " #, python-format msgid "“%(pk)s” is not a valid value." -msgstr "“%(pk)s” не е валидна стойност." +msgstr "" #, python-format msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " "may be ambiguous or it may not exist." msgstr "" -"%(datetime)s не може да се интерпретира в часова зона %(current_timezone)s; " -"вероятно стойността е нееднозначна или не съществува изобщо." msgid "Clear" msgstr "Изчисти" @@ -858,19 +823,19 @@ msgstr "да,не,може би" msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d, байт" -msgstr[1] "%(size)d байта" +msgstr[1] "%(size)d, байта" #, python-format msgid "%s KB" -msgstr "%s KБ" +msgstr "%s KB" #, python-format msgid "%s MB" -msgstr "%s МБ" +msgstr "%s MB" #, python-format msgid "%s GB" -msgstr "%s ГБ" +msgstr "%s GB" #, python-format msgid "%s TB" @@ -878,7 +843,7 @@ msgstr "%s ТБ" #, python-format msgid "%s PB" -msgstr "%s ПБ" +msgstr "%s PB" msgid "p.m." msgstr "след обяд" @@ -1007,7 +972,7 @@ msgid "oct" msgstr "окт" msgid "nov" -msgstr "ноем" +msgstr "ноев" msgid "dec" msgstr "дек" @@ -1026,7 +991,7 @@ msgstr "Март" msgctxt "abbrev. month" msgid "April" -msgstr "Апр." +msgstr "Април" msgctxt "abbrev. month" msgid "May" @@ -1054,7 +1019,7 @@ msgstr "Окт." msgctxt "abbrev. month" msgid "Nov." -msgstr "Ноем." +msgstr "Ноев." msgctxt "abbrev. month" msgid "Dec." @@ -1098,7 +1063,7 @@ msgstr "Септември" msgctxt "alt. month" msgid "October" -msgstr "Октомври" +msgstr "след обяд" msgctxt "alt. month" msgid "November" @@ -1114,7 +1079,7 @@ msgstr "Въведете валиден IPv6 адрес." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "" msgid "or" msgstr "или" @@ -1124,40 +1089,40 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d година" -msgstr[1] "%(num)d години" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d година" +msgstr[1] "%d години" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месец" -msgstr[1] "%(num)d месеца" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месец" +msgstr[1] "%d месеца" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d седмица" -msgstr[1] "%(num)d седмици" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d седмица" +msgstr[1] "%d седмици" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d ден" -msgstr[1] "%(num)d дни" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d дни" +msgstr[1] "%d дни" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d час" -msgstr[1] "%(num)d часа" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d час" +msgstr[1] "%d часа" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d минута" -msgstr[1] "%(num)d минути" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d минута" +msgstr[1] "%d минути" msgid "Forbidden" msgstr "Забранен" @@ -1167,23 +1132,16 @@ msgstr "CSRF проверката се провали. Заявката прек msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Вие виждате това съобщение, защото този HTTPS сайт изисква да бъде изпратен " -"'Referer header' от вашият уеб браузър, но такъв не бе изпратен. Този " -"header е задължителен от съображения за сигурност, за да се гарантира, че " -"вашият браузър не е компрометиран от трети страни." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" "enable them, at least for this site, or for HTTPS connections, or for “same-" "origin” requests." msgstr "" -"Ако сте настроили вашия браузър да деактивира 'Referer' headers, моля да ги " -"активирате отново, поне за този сайт, или за HTTPS връзки, или за 'same-" -"origin' заявки." msgid "" "If you are using the tag or " @@ -1192,18 +1150,13 @@ msgid "" "If you’re concerned about privacy, use alternatives like for links to third-party sites." msgstr "" -"Ако използвате таг или " -"включвате “Referrer-Policy: no-referrer” header, моля премахнете ги. CSRF " -"защитата изисква “Referer” header, за да извърши стриктна проверка на " -"изпращача. Ако сте притеснени за поверителността, използвайте алтернативи " -"като за връзки към сайтове на трети страни." msgid "" "You are seeing this message because this site requires a CSRF cookie when " "submitting forms. This cookie is required for security reasons, to ensure " "that your browser is not being hijacked by third parties." msgstr "" -"Вие виждате това съобщение, защото този сайт изисква CSRF бисквитка, когато " +"Вие виждате това съобщение, защото този сайт изисква CSRF бисквитка когато " "се подават формуляри. Тази бисквитка е задължителна от съображения за " "сигурност, за да се гарантира, че вашият браузър не е компрометиран от трети " "страни." @@ -1212,8 +1165,6 @@ msgid "" "If you have configured your browser to disable cookies, please re-enable " "them, at least for this site, or for “same-origin” requests." msgstr "" -"Ако сте конфигурирали браузъра си да забрани бисквитките, моля да ги " -"активирате отново, поне за този сайт, или за \"same-origin\" заявки." msgid "More information is available with DEBUG=True." msgstr "Повече информация е на разположение с DEBUG=True." @@ -1222,13 +1173,13 @@ msgid "No year specified" msgstr "Не е посочена година" msgid "Date out of range" -msgstr "Датата е в невалиден диапазон" +msgstr "" msgid "No month specified" msgstr "Не е посочен месец" msgid "No day specified" -msgstr "Не е посочен ден" +msgstr "ноев" msgid "No week specified" msgstr "Не е посочена седмица" @@ -1242,22 +1193,19 @@ msgid "" "Future %(verbose_name_plural)s not available because %(class_name)s." "allow_future is False." msgstr "" -"Бъдещo %(verbose_name_plural)s е недостъпно, тъй като %(class_name)s." +"Бъдещo %(verbose_name_plural)s е достъпно, тъй като %(class_name)s." "allow_future е False." #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" msgstr "" -"Невалидна текстова стойност на датата “%(datestr)s” при зададен формат " -"“%(format)s”" #, python-format msgid "No %(verbose_name)s found matching the query" -msgstr "Няма %(verbose_name)s, съвпадащи със заявката" +msgstr "Няма %(verbose_name)s , съвпадащи със заявката" msgid "Page is not “last”, nor can it be converted to an int." msgstr "" -"Страницата не е \"последна\", нито може да се преобразува в цяло число." #, python-format msgid "Invalid page (%(page_number)s): %(message)s" @@ -1265,29 +1213,30 @@ msgstr "Невалидна страница (%(page_number)s): %(message)s" #, python-format msgid "Empty list and “%(class_name)s.allow_empty” is False." -msgstr "Празен списък и \"%(class_name)s.allow_empty\" e False." +msgstr "" msgid "Directory indexes are not allowed here." msgstr "Тук не е позволено индексиране на директория." #, python-format msgid "“%(path)s” does not exist" -msgstr "\"%(path)s\" не съществува" +msgstr "" #, python-format msgid "Index of %(directory)s" msgstr "Индекс %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "Инсталацията Ви заработи успешно! Поздравления!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "Django: Фреймуоркът за перфекционисти с крайни срокове." #, python-format msgid "" "View release notes for Django %(version)s" msgstr "" -"Разгледайте release notes за Django %(version)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "" #, python-format msgid "" @@ -1296,25 +1245,25 @@ msgid "" "\">DEBUG=True is in your settings file and you have not configured any " "URLs." msgstr "" -"Вие виждате тази страница, защото DEBUG=True е във вашия файл с настройки и не сте конфигурирали " -"никакви URL-и." +"Вие виждате тази страница защото DEBUG=True е във вашият settings файл и не сте конфигурирали никакви " +"URL-и" msgid "Django Documentation" -msgstr "Django документация" +msgstr "Django Документация" msgid "Topics, references, & how-to’s" -msgstr "Теми, наръчници, & друга документация" +msgstr "" msgid "Tutorial: A Polling App" -msgstr "Урок: Приложение за анкета" +msgstr "" msgid "Get started with Django" msgstr "Започнете с Django" msgid "Django Community" -msgstr "Django общност" +msgstr "" msgid "Connect, get help, or contribute" -msgstr "Свържете се, получете помощ или допринесете" +msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/bg/formats.py b/venv/Lib/site-packages/django/conf/locale/bg/formats.py index ee90c5b..b7d0c3b 100644 --- a/venv/Lib/site-packages/django/conf/locale/bg/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/bg/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d F Y" -TIME_FORMAT = "H:i" +DATE_FORMAT = 'd F Y' +TIME_FORMAT = 'H:i' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = @@ -16,6 +16,6 @@ SHORT_DATE_FORMAT = "d.m.Y" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = " " # Non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = ' ' # Non-breaking space # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/bn/formats.py b/venv/Lib/site-packages/django/conf/locale/bn/formats.py index 9d1bb09..6205fb9 100644 --- a/venv/Lib/site-packages/django/conf/locale/bn/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/bn/formats.py @@ -2,31 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F, Y" -TIME_FORMAT = "g:i A" +DATE_FORMAT = 'j F, Y' +TIME_FORMAT = 'g:i A' # DATETIME_FORMAT = -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M, Y" +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M, Y' # SHORT_DATETIME_FORMAT = FIRST_DAY_OF_WEEK = 6 # Saturday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # 25/10/2016 - "%d/%m/%y", # 25/10/16 - "%d-%m-%Y", # 25-10-2016 - "%d-%m-%y", # 25-10-16 + '%d/%m/%Y', # 25/10/2016 + '%d/%m/%y', # 25/10/16 + '%d-%m-%Y', # 25-10-2016 + '%d-%m-%y', # 25-10-16 ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # 14:30:59 - "%H:%M", # 14:30 + '%H:%M:%S', # 14:30:59 + '%H:%M', # 14:30 ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # 25/10/2006 14:30:59 - "%d/%m/%Y %H:%M", # 25/10/2006 14:30 + '%d/%m/%Y %H:%M:%S', # 25/10/2006 14:30:59 + '%d/%m/%Y %H:%M', # 25/10/2006 14:30 ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.mo index d864abe..a9419c0 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.po index 3b1a759..9ab9cf0 100644 --- a/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/br/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-02-28 17:37+0000\n" +"Last-Translator: Ewen \n" "Language-Team: Breton (http://www.transifex.com/django/django/language/br/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -92,7 +92,7 @@ msgid "Argentinian Spanish" msgstr "Spagnoleg Arc'hantina" msgid "Colombian Spanish" -msgstr "Spagnoleg Kolombia" +msgstr "" msgid "Mexican Spanish" msgstr "Spagnoleg Mec'hiko" @@ -211,9 +211,6 @@ msgstr "Mongoleg" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Burmeg" @@ -337,7 +334,7 @@ msgid "That page number is less than 1" msgstr "An niver a bajenn mañ a zo bihanoc'h eget 1." msgid "That page contains no results" -msgstr "N'eus disoc'h er pajenn-mañ." +msgstr "" msgid "Enter a valid value." msgstr "Merkit un talvoud reizh" @@ -346,7 +343,7 @@ msgid "Enter a valid URL." msgstr "Merkit un URL reizh" msgid "Enter a valid integer." -msgstr "Merkit un niver anterin reizh." +msgstr "" msgid "Enter a valid email address." msgstr "Merkit ur chomlec'h postel reizh" @@ -1109,58 +1106,58 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d bloaz" +msgstr[1] "%d bloaz" +msgstr[2] "%d bloaz" +msgstr[3] "%d bloaz" +msgstr[4] "%d bloaz" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d miz" +msgstr[1] "%d miz" +msgstr[2] "%d miz" +msgstr[3] "%d miz" +msgstr[4] "%d miz" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d sizhun" +msgstr[1] "%d sizhun" +msgstr[2] "%d sizhun" +msgstr[3] "%d sizhun" +msgstr[4] "%d sizhun" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d deiz" +msgstr[1] "%d deiz" +msgstr[2] "%d deiz" +msgstr[3] "%d deiz" +msgstr[4] "%d deiz" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d eur" +msgstr[1] "%d eur" +msgstr[2] "%d eur" +msgstr[3] "%d eur" +msgstr[4] "%d eur" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d munud" +msgstr[1] "%d munud" +msgstr[2] "%d munud" +msgstr[3] "%d munud" +msgstr[4] "%d munud" msgid "Forbidden" msgstr "Difennet" @@ -1170,7 +1167,7 @@ msgstr "" msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/bs/formats.py b/venv/Lib/site-packages/django/conf/locale/bs/formats.py index a15e709..25d9b40 100644 --- a/venv/Lib/site-packages/django/conf/locale/bs/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/bs/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. N Y." -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j. N. Y. G:i T" -YEAR_MONTH_FORMAT = "F Y." -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "Y M j" +DATE_FORMAT = 'j. N Y.' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j. N. Y. G:i T' +YEAR_MONTH_FORMAT = 'F Y.' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'Y M j' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = @@ -16,6 +16,6 @@ SHORT_DATE_FORMAT = "Y M j" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.mo index ca8826b..ea6425f 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.po index d315fd5..721e90a 100644 --- a/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ca/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy , 2012,2015-2017,2021 +# Antoni Aloy , 2012,2015-2017 # Carles Barrobés , 2011-2012,2014,2020 # duub qnnp, 2015 # Gil Obradors Via , 2019 @@ -9,16 +9,15 @@ # Jannis Leidel , 2011 # Manel Clos , 2020 # Manuel Miranda , 2015 -# Mariusz Felisiak , 2021 # Roger Pons , 2015 # Santiago Lamora , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:29+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 12:25+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" "MIME-Version: 1.0\n" @@ -160,7 +159,7 @@ msgid "Indonesian" msgstr "indonesi" msgid "Igbo" -msgstr "lgbo" +msgstr "" msgid "Ido" msgstr "Ido" @@ -193,7 +192,7 @@ msgid "Korean" msgstr "coreà" msgid "Kyrgyz" -msgstr "Kyrgyz" +msgstr "" msgid "Luxembourgish" msgstr "Luxemburguès" @@ -216,9 +215,6 @@ msgstr "mongol" msgid "Marathi" msgstr "Maratí" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Burmès" @@ -283,13 +279,13 @@ msgid "Telugu" msgstr "telugu" msgid "Tajik" -msgstr "Tajik" +msgstr "" msgid "Thai" msgstr "tailandès" msgid "Turkmen" -msgstr "Turkmen" +msgstr "" msgid "Turkish" msgstr "turc" @@ -333,7 +329,7 @@ msgstr "Sindicació" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "..." +msgstr "" msgid "That page number is not an integer" msgstr "Aquest número de plana no és un enter" @@ -778,21 +774,18 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"Les dades de ManagementForm no hi són o han estat modificades. Camps que " -"falten: %(field_names)s. . Necessitaràs omplir una incidència si el problema " -"persisteix." #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Si uns plau, envia com a màxim %d formulari" -msgstr[1] "Si us plau, envia com a màxim %d formularis" +msgstr[0] "" +msgstr[1] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Sisplau envieu com a mínim %d formulari." -msgstr[1] "Si us plau envieu com a mínim %d formularis." +msgstr[0] "" +msgstr[1] "" msgid "Order" msgstr "Ordre" @@ -1123,7 +1116,7 @@ msgstr "Aquesta no és una adreça IPv6 vàlida." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "o" @@ -1133,40 +1126,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d any" -msgstr[1] "%(num)d anys" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d any" +msgstr[1] "%d anys" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d mesos" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d mesos" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d setmana" -msgstr[1] "%(num)d setmanes" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d setmana" +msgstr[1] "%d setmanes" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dia" -msgstr[1] "%(num)d dies" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dia" +msgstr[1] "%d dies" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d hores" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d hores" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minuts" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minuts" msgid "Forbidden" msgstr "Prohibit" @@ -1176,14 +1169,14 @@ msgstr "La verificació de CSRF ha fallat. Petició abortada." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "Esteu veient aquest missatge perquè aquest lloc HTTPS requereix que el " "vostre navegador enviï una capçalera “Referer\", i no n'ha arribada cap. " "Aquesta capçalera es requereix per motius de seguretat, per garantir que el " -"vostre navegador no està sent segrestat per tercers." +"vostre navegador no està sent infiltrat per tercers." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/ca/formats.py b/venv/Lib/site-packages/django/conf/locale/ca/formats.py index 2f91009..746d08f 100644 --- a/venv/Lib/site-packages/django/conf/locale/ca/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ca/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\e\s G:i" -YEAR_MONTH_FORMAT = r"F \d\e\l Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y G:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\e\s G:i' +YEAR_MONTH_FORMAT = r'F \d\e\l Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y G:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '31/12/2009' - "%d/%m/%y", # '31/12/09' + # '31/12/2009', '31/12/09' + '%d/%m/%Y', '%d/%m/%y' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.mo index addcb50..121d7d4 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.po index bf2afb9..33c32e1 100644 --- a/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/cs/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-10 16:05+0200\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" "PO-Revision-Date: 2021-03-18 23:20+0000\n" "Last-Translator: Vláďa Macek \n" "Language-Team: Czech (http://www.transifex.com/django/django/language/cs/)\n" @@ -1143,52 +1143,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rok" -msgstr[1] "%(num)d roky" -msgstr[2] "%(num)d roku" -msgstr[3] "%(num)d let" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d roky" +msgstr[2] "%d roku" +msgstr[3] "%d let" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsíc" -msgstr[1] "%(num)d měsíce" -msgstr[2] "%(num)d měsíců" -msgstr[3] "%(num)d měsíců" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsíc" +msgstr[1] "%d měsíce" +msgstr[2] "%d měsíců" +msgstr[3] "%d měsíců" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d týden" -msgstr[1] "%(num)d týdny" -msgstr[2] "%(num)d týdne" -msgstr[3] "%(num)d týdnů" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d týden" +msgstr[1] "%d týdny" +msgstr[2] "%d týdne" +msgstr[3] "%d týdnů" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d den" -msgstr[1] "%(num)d dny" -msgstr[2] "%(num)d dní" -msgstr[3] "%(num)d dní" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d den" +msgstr[1] "%d dny" +msgstr[2] "%d dní" +msgstr[3] "%d dní" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodina" -msgstr[1] "%(num)d hodiny" -msgstr[2] "%(num)d hodiny" -msgstr[3] "%(num)d hodin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodina" +msgstr[1] "%d hodiny" +msgstr[2] "%d hodiny" +msgstr[3] "%d hodin" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuty" -msgstr[2] "%(num)d minut" -msgstr[3] "%(num)d minut" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minuty" +msgstr[2] "%d minut" +msgstr[3] "%d minut" msgid "Forbidden" msgstr "Nepřístupné (Forbidden)" diff --git a/venv/Lib/site-packages/django/conf/locale/cs/formats.py b/venv/Lib/site-packages/django/conf/locale/cs/formats.py index e4a7ab9..c01af8b 100644 --- a/venv/Lib/site-packages/django/conf/locale/cs/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/cs/formats.py @@ -2,42 +2,39 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. E Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j. E Y G:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y G:i" +DATE_FORMAT = 'j. E Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j. E Y G:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y G:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '05.01.2006' - "%d.%m.%y", # '05.01.06' - "%d. %m. %Y", # '5. 1. 2006' - "%d. %m. %y", # '5. 1. 06' - # "%d. %B %Y", # '25. October 2006' - # "%d. %b. %Y", # '25. Oct. 2006' + '%d.%m.%Y', '%d.%m.%y', # '05.01.2006', '05.01.06' + '%d. %m. %Y', '%d. %m. %y', # '5. 1. 2006', '5. 1. 06' + # '%d. %B %Y', '%d. %b. %Y', # '25. October 2006', '25. Oct. 2006' ] # Kept ISO formats as one is in first position TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '04:30:59' - "%H.%M", # '04.30' - "%H:%M", # '04:30' + '%H:%M:%S', # '04:30:59' + '%H.%M', # '04.30' + '%H:%M', # '04:30' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '05.01.2006 04:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '05.01.2006 04:30:59.000200' - "%d.%m.%Y %H.%M", # '05.01.2006 04.30' - "%d.%m.%Y %H:%M", # '05.01.2006 04:30' - "%d. %m. %Y %H:%M:%S", # '05. 01. 2006 04:30:59' - "%d. %m. %Y %H:%M:%S.%f", # '05. 01. 2006 04:30:59.000200' - "%d. %m. %Y %H.%M", # '05. 01. 2006 04.30' - "%d. %m. %Y %H:%M", # '05. 01. 2006 04:30' - "%Y-%m-%d %H.%M", # '2006-01-05 04.30' + '%d.%m.%Y %H:%M:%S', # '05.01.2006 04:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '05.01.2006 04:30:59.000200' + '%d.%m.%Y %H.%M', # '05.01.2006 04.30' + '%d.%m.%Y %H:%M', # '05.01.2006 04:30' + '%d. %m. %Y %H:%M:%S', # '05. 01. 2006 04:30:59' + '%d. %m. %Y %H:%M:%S.%f', # '05. 01. 2006 04:30:59.000200' + '%d. %m. %Y %H.%M', # '05. 01. 2006 04.30' + '%d. %m. %Y %H:%M', # '05. 01. 2006 04:30' + '%Y-%m-%d %H.%M', # '2006-01-05 04.30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/cy/formats.py b/venv/Lib/site-packages/django/conf/locale/cy/formats.py index eaef6a6..db40cab 100644 --- a/venv/Lib/site-packages/django/conf/locale/cy/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/cy/formats.py @@ -2,32 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" # '25 Hydref 2006' -TIME_FORMAT = "P" # '2:30 y.b.' -DATETIME_FORMAT = "j F Y, P" # '25 Hydref 2006, 2:30 y.b.' -YEAR_MONTH_FORMAT = "F Y" # 'Hydref 2006' -MONTH_DAY_FORMAT = "j F" # '25 Hydref' -SHORT_DATE_FORMAT = "d/m/Y" # '25/10/2006' -SHORT_DATETIME_FORMAT = "d/m/Y P" # '25/10/2006 2:30 y.b.' -FIRST_DAY_OF_WEEK = 1 # 'Dydd Llun' +DATE_FORMAT = 'j F Y' # '25 Hydref 2006' +TIME_FORMAT = 'P' # '2:30 y.b.' +DATETIME_FORMAT = 'j F Y, P' # '25 Hydref 2006, 2:30 y.b.' +YEAR_MONTH_FORMAT = 'F Y' # 'Hydref 2006' +MONTH_DAY_FORMAT = 'j F' # '25 Hydref' +SHORT_DATE_FORMAT = 'd/m/Y' # '25/10/2006' +SHORT_DATETIME_FORMAT = 'd/m/Y P' # '25/10/2006 2:30 y.b.' +FIRST_DAY_OF_WEEK = 1 # 'Dydd Llun' # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' ] 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' - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' + '%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' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.mo index 1829715..e1db921 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.po index 8509537..6bde3e6 100644 --- a/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/da/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 19:17+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 18:48+0000\n" "Last-Translator: Erik Ramsgaard Wognsen \n" "Language-Team: Danish (http://www.transifex.com/django/django/language/da/)\n" "MIME-Version: 1.0\n" @@ -205,16 +205,13 @@ msgid "Macedonian" msgstr "makedonsk" msgid "Malayalam" -msgstr "malayalam" +msgstr "malaysisk" msgid "Mongolian" msgstr "mongolsk" msgid "Marathi" -msgstr "marathi" - -msgid "Malay" -msgstr "malajisk" +msgstr "Marathi" msgid "Burmese" msgstr "burmesisk" @@ -1116,40 +1113,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d år" +msgstr[1] "%d år" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d måned" -msgstr[1] "%(num)d måneder" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d måned" +msgstr[1] "%d måneder" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d uge" -msgstr[1] "%(num)d uger" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d uge" +msgstr[1] "%d uger" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dag" +msgstr[1] "%d dage" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timer" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d time" +msgstr[1] "%d timer" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutter" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutter" msgid "Forbidden" msgstr "Forbudt" @@ -1159,12 +1156,12 @@ msgstr "CSRF-verifikationen mislykkedes. Forespørgslen blev afbrudt." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "Du ser denne besked fordi denne HTTPS-webside kræver at din browser sender " -"en “Referer header”, som ikke blev sendt. Denne header er påkrævet af " +"en “Referer header”, men den blev ikke sendt. Denne header er påkrævet af " "sikkerhedsmæssige grunde for at sikre at din browser ikke bliver kapret af " "tredjepart." diff --git a/venv/Lib/site-packages/django/conf/locale/da/formats.py b/venv/Lib/site-packages/django/conf/locale/da/formats.py index 5829208..6237a72 100644 --- a/venv/Lib/site-packages/django/conf/locale/da/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/da/formats.py @@ -2,25 +2,25 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j. F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' + '%d.%m.%Y', # '25.10.2006' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.mo index 59a12cf..09c7bcf 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.po index c543838..1405164 100644 --- a/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/de/LC_MESSAGES/django.po @@ -4,18 +4,17 @@ # André Hagenbruch, 2011-2012 # Florian Apolloner , 2011 # Daniel Roschka , 2016 -# Florian Apolloner , 2018,2020-2021 +# Florian Apolloner , 2018,2020 # Jannis Vajen, 2011,2013 # Jannis Leidel , 2013-2018,2020 # Jannis Vajen, 2016 # Markus Holtermann , 2013,2015 -# Raphael Michel , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-28 18:34+0000\n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-17 07:52+0000\n" "Last-Translator: Florian Apolloner \n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" @@ -213,9 +212,6 @@ msgstr "Mongolisch" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "Malaiisch" - msgid "Burmese" msgstr "Birmanisch" @@ -327,11 +323,6 @@ msgstr "Statische Dateien" msgid "Syndication" msgstr "Syndication" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "…" - msgid "That page number is not an integer" msgstr "Diese Seitennummer ist keine Ganzzahl" @@ -593,9 +584,6 @@ msgstr "Ganzzahl" msgid "Big (8 byte) integer" msgstr "Große Ganzzahl (8 Byte)" -msgid "Small integer" -msgstr "Kleine Ganzzahl" - msgid "IPv4 address" msgstr "IPv4-Adresse" @@ -622,6 +610,9 @@ msgstr "Positive kleine Ganzzahl" msgid "Slug (up to %(max_length)s)" msgstr "Kürzel (bis zu %(max_length)s)" +msgid "Small integer" +msgstr "Kleine Ganzzahl" + msgid "Text" msgstr "Text" @@ -776,26 +767,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Verstecktes Feld %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" -"Daten für das Management-Formular fehlen oder wurden manipuliert. Fehlende " -"Felder: %(field_names)s. Bitte erstellen Sie einen Bug-Report falls der " -"Fehler dauerhaft besteht." +msgid "ManagementForm data is missing or has been tampered with" +msgstr "ManagementForm-Daten fehlen oder wurden manipuliert." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." msgstr[0] "Bitte höchstens %d Formular abschicken." msgstr[1] "Bitte höchstens %d Formulare abschicken." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Bitte mindestens %d Formular abschicken." -msgstr[1] "Bitte mindestens %d Formulare abschicken." +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Bitte %d oder mehr Formulare abschicken." +msgstr[1] "Bitte %d oder mehr Formulare abschicken." msgid "Order" msgstr "Reihenfolge" @@ -1133,40 +1118,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d Jahr" -msgstr[1] "%(num)d Jahre" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d Jahr" +msgstr[1] "%d Jahre" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d Monat" -msgstr[1] "%(num)d Monate" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d Monat" +msgstr[1] "%d Monate" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d Woche" -msgstr[1] "%(num)d Wochen" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d Woche" +msgstr[1] "%d Wochen" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d Tag" -msgstr[1] "%(num)d Tage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d Tag" +msgstr[1] "%d Tage" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d Stunde" -msgstr[1] "%(num)d Stunden" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d Stunde" +msgstr[1] "%d Stunden" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d Minute" -msgstr[1] "%(num)d Minuten" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d Minute" +msgstr[1] "%d Minuten" msgid "Forbidden" msgstr "Verboten" @@ -1176,11 +1161,11 @@ msgstr "CSRF-Verifizierung fehlgeschlagen. Anfrage abgebrochen." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Sie sehen diese Fehlermeldung, da diese HTTPS-Seite einen „Referer“-Header " +"Sie sehen diese Fehlermeldung da diese HTTPS-Seite einen „Referer“-Header " "von Ihrem Webbrowser erwartet, aber keinen erhalten hat. Dieser Header ist " "aus Sicherheitsgründen notwendig, um sicherzustellen, dass Ihr Webbrowser " "nicht von Dritten missbraucht wird." @@ -1287,8 +1272,8 @@ msgstr "„%(path)s“ ist nicht vorhanden" msgid "Index of %(directory)s" msgstr "Verzeichnis %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "Die Installation war erfolgreich. Herzlichen Glückwunsch!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "Django: Das Webframework für Perfektionisten mit Termindruck." #, python-format msgid "" @@ -1299,6 +1284,9 @@ msgstr "" "\"_blank\" rel=\"noopener\">Versionshinweise für Django %(version)s " "anzeigen" +msgid "The install worked successfully! Congratulations!" +msgstr "Die Installation war erfolgreich. Herzlichen Glückwunsch!" + #, python-format msgid "" "You are seeing this page because \n" "Language-Team: Lower Sorbian (http://www.transifex.com/django/django/" "language/dsb/)\n" @@ -207,9 +207,6 @@ msgstr "Mongolšćina" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "Malayzišćina" - msgid "Burmese" msgstr "Myanmaršćina" @@ -1151,52 +1148,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)d lěśe" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d lěto" +msgstr[1] "%d lěśe" +msgstr[2] "%d lěta" +msgstr[3] "%d lět" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mjasec" -msgstr[1] "%(num)d mjaseca" -msgstr[2] "%(num)d mjasece" -msgstr[3] "%(num)dmjasecow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mjasec" +msgstr[1] "%d mjaseca" +msgstr[2] "%d mjasece" +msgstr[3] "%d mjasecow" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tyźeń" -msgstr[1] "%(num)d tyźenja" -msgstr[2] "%(num)d tyźenje" -msgstr[3] "%(num)d tyźenjow" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tyźeń" +msgstr[1] "%d tyéznja" +msgstr[2] "%d tyźenje" +msgstr[3] "%d tyźenjow" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d źeń " -msgstr[1] "%(num)d dnja" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d źeń" +msgstr[1] "%d dnja" +msgstr[2] "%d dny" +msgstr[3] "%d dnjow" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d góźina" -msgstr[1] "%(num)d góźinje" -msgstr[2] "%(num)d góźiny" -msgstr[3] "%(num)d góźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d góźina" +msgstr[1] "%d góźinje" +msgstr[2] "%d góźiny" +msgstr[3] "%d góźin" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuśe" -msgstr[2] "%(num)d minuty" -msgstr[3] "%(num)d minutow" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minuśe" +msgstr[2] "%d minuty" +msgstr[3] "%d minutow" msgid "Forbidden" msgstr "Zakazany" @@ -1206,12 +1203,12 @@ msgstr "CSRF-pśeglědanje njejo se raźiło. Napšašowanje jo se pśetergnuło msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Wiźiśo toś tu powěźeńku, dokulaž toś to HTTPS-sedło trjeba \"Referer header" -"\", aby se pśez waš webwobglědowak słało, ale žedna njejo se pósłała. Toś ta " +"Wiźiśo toś tu powěźeńku, dokulaž toś to HTTPS-sedło trjeba głowu 'Referer', " +"aby se pśez waš webwobglědowak słało, ale žedna njejo se pósłała. Toś ta " "głowa jo trěbna z pśicynow wěstoty, aby so zawěsćiło, až waš wobglědowak " "njekaprujo se wót tśeśich." diff --git a/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.mo index 1b07550..e4acdcd 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.po index 003a36c..dd6b95a 100644 --- a/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/el/LC_MESSAGES/django.po @@ -3,7 +3,6 @@ # Translators: # Apostolis Bessas , 2013 # Dimitris Glezos , 2011,2013,2017 -# Fotis Athineos , 2021 # Giannis Meletakis , 2015 # Jannis Leidel , 2011 # Nick Mavrakis , 2017-2020 @@ -18,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 12:25+0000\n" "Last-Translator: Transifex Bot <>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" @@ -217,9 +216,6 @@ msgstr "Μογγολικά" msgid "Marathi" msgstr "Μαράθι" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Βιρμανικά" @@ -789,14 +785,14 @@ msgstr "" #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Παρακαλώ υποβάλλετε το πολύ %d φόρμα." -msgstr[1] "Παρακαλώ υποβάλλετε το πολύ %d φόρμες." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Παρακαλώ υποβάλλετε τουλάχιστον %d φόρμα." -msgstr[1] "Παρακαλώ υποβάλλετε τουλάχιστον %d φόρμες." +msgstr[0] "" +msgstr[1] "" msgid "Order" msgstr "Ταξινόμηση" @@ -1137,40 +1133,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d χρόνος" +msgstr[1] "%d χρόνια" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d μήνας" +msgstr[1] "%d μήνες" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d βδομάδα" +msgstr[1] "%d βδομάδες" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d μέρα" +msgstr[1] "%d μέρες" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ώρα" +msgstr[1] "%d ώρες" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d λεπτό" +msgstr[1] "%d λεπτά" msgid "Forbidden" msgstr "Απαγορευμένο" @@ -1180,10 +1176,14 @@ msgstr "Η πιστοποίηση CSRF απέτυχε. Το αίτημα ματ msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Βλέπετε αυτό το μήνυμα επειδή αυτή η HTTPS σελίδα απαιτεί από τον Web " +"browser σας να σταλεί ένας 'Referer header', όμως τίποτα δεν στάλθηκε. Αυτός " +"ο header είναι απαραίτητος για λόγους ασφαλείας, για να εξασφαλιστεί ότι ο " +"browser δεν έχει γίνει hijacked από τρίτους." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/el/formats.py b/venv/Lib/site-packages/django/conf/locale/el/formats.py index 25c8ef7..8d3175a 100644 --- a/venv/Lib/site-packages/django/conf/locale/el/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/el/formats.py @@ -2,33 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d/m/Y" -TIME_FORMAT = "P" -DATETIME_FORMAT = "d/m/Y P" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y P" +DATE_FORMAT = 'd/m/Y' +TIME_FORMAT = 'P' +DATETIME_FORMAT = 'd/m/Y P' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y P' FIRST_DAY_OF_WEEK = 0 # Sunday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%Y-%m-%d", # '2006-10-25' + '%d/%m/%Y', '%d/%m/%y', '%Y-%m-%d', # '25/10/2006', '25/10/06', '2006-10-25', ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' - "%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' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' + '%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' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/en/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/en/LC_MESSAGES/django.po index 4aef1af..e3d7690 100644 --- a/venv/Lib/site-packages/django/conf/locale/en/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English \n" @@ -14,391 +14,387 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: conf/global_settings.py:57 +#: conf/global_settings.py:52 msgid "Afrikaans" msgstr "" -#: conf/global_settings.py:58 +#: conf/global_settings.py:53 msgid "Arabic" msgstr "" -#: conf/global_settings.py:59 +#: conf/global_settings.py:54 msgid "Algerian Arabic" msgstr "" -#: conf/global_settings.py:60 +#: conf/global_settings.py:55 msgid "Asturian" msgstr "" -#: conf/global_settings.py:61 +#: conf/global_settings.py:56 msgid "Azerbaijani" msgstr "" -#: conf/global_settings.py:62 +#: conf/global_settings.py:57 msgid "Bulgarian" msgstr "" -#: conf/global_settings.py:63 +#: conf/global_settings.py:58 msgid "Belarusian" msgstr "" -#: conf/global_settings.py:64 +#: conf/global_settings.py:59 msgid "Bengali" msgstr "" -#: conf/global_settings.py:65 +#: conf/global_settings.py:60 msgid "Breton" msgstr "" -#: conf/global_settings.py:66 +#: conf/global_settings.py:61 msgid "Bosnian" msgstr "" -#: conf/global_settings.py:67 +#: conf/global_settings.py:62 msgid "Catalan" msgstr "" -#: conf/global_settings.py:68 +#: conf/global_settings.py:63 msgid "Czech" msgstr "" -#: conf/global_settings.py:69 +#: conf/global_settings.py:64 msgid "Welsh" msgstr "" -#: conf/global_settings.py:70 +#: conf/global_settings.py:65 msgid "Danish" msgstr "" -#: conf/global_settings.py:71 +#: conf/global_settings.py:66 msgid "German" msgstr "" -#: conf/global_settings.py:72 +#: conf/global_settings.py:67 msgid "Lower Sorbian" msgstr "" -#: conf/global_settings.py:73 +#: conf/global_settings.py:68 msgid "Greek" msgstr "" -#: conf/global_settings.py:74 +#: conf/global_settings.py:69 msgid "English" msgstr "" -#: conf/global_settings.py:75 +#: conf/global_settings.py:70 msgid "Australian English" msgstr "" -#: conf/global_settings.py:76 +#: conf/global_settings.py:71 msgid "British English" msgstr "" -#: conf/global_settings.py:77 +#: conf/global_settings.py:72 msgid "Esperanto" msgstr "" -#: conf/global_settings.py:78 +#: conf/global_settings.py:73 msgid "Spanish" msgstr "" -#: conf/global_settings.py:79 +#: conf/global_settings.py:74 msgid "Argentinian Spanish" msgstr "" -#: conf/global_settings.py:80 +#: conf/global_settings.py:75 msgid "Colombian Spanish" msgstr "" -#: conf/global_settings.py:81 +#: conf/global_settings.py:76 msgid "Mexican Spanish" msgstr "" -#: conf/global_settings.py:82 +#: conf/global_settings.py:77 msgid "Nicaraguan Spanish" msgstr "" -#: conf/global_settings.py:83 +#: conf/global_settings.py:78 msgid "Venezuelan Spanish" msgstr "" -#: conf/global_settings.py:84 +#: conf/global_settings.py:79 msgid "Estonian" msgstr "" -#: conf/global_settings.py:85 +#: conf/global_settings.py:80 msgid "Basque" msgstr "" -#: conf/global_settings.py:86 +#: conf/global_settings.py:81 msgid "Persian" msgstr "" -#: conf/global_settings.py:87 +#: conf/global_settings.py:82 msgid "Finnish" msgstr "" -#: conf/global_settings.py:88 +#: conf/global_settings.py:83 msgid "French" msgstr "" -#: conf/global_settings.py:89 +#: conf/global_settings.py:84 msgid "Frisian" msgstr "" -#: conf/global_settings.py:90 +#: conf/global_settings.py:85 msgid "Irish" msgstr "" -#: conf/global_settings.py:91 +#: conf/global_settings.py:86 msgid "Scottish Gaelic" msgstr "" -#: conf/global_settings.py:92 +#: conf/global_settings.py:87 msgid "Galician" msgstr "" -#: conf/global_settings.py:93 +#: conf/global_settings.py:88 msgid "Hebrew" msgstr "" -#: conf/global_settings.py:94 +#: conf/global_settings.py:89 msgid "Hindi" msgstr "" -#: conf/global_settings.py:95 +#: conf/global_settings.py:90 msgid "Croatian" msgstr "" -#: conf/global_settings.py:96 +#: conf/global_settings.py:91 msgid "Upper Sorbian" msgstr "" -#: conf/global_settings.py:97 +#: conf/global_settings.py:92 msgid "Hungarian" msgstr "" -#: conf/global_settings.py:98 +#: conf/global_settings.py:93 msgid "Armenian" msgstr "" -#: conf/global_settings.py:99 +#: conf/global_settings.py:94 msgid "Interlingua" msgstr "" -#: conf/global_settings.py:100 +#: conf/global_settings.py:95 msgid "Indonesian" msgstr "" -#: conf/global_settings.py:101 +#: conf/global_settings.py:96 msgid "Igbo" msgstr "" -#: conf/global_settings.py:102 +#: conf/global_settings.py:97 msgid "Ido" msgstr "" -#: conf/global_settings.py:103 +#: conf/global_settings.py:98 msgid "Icelandic" msgstr "" -#: conf/global_settings.py:104 +#: conf/global_settings.py:99 msgid "Italian" msgstr "" -#: conf/global_settings.py:105 +#: conf/global_settings.py:100 msgid "Japanese" msgstr "" -#: conf/global_settings.py:106 +#: conf/global_settings.py:101 msgid "Georgian" msgstr "" -#: conf/global_settings.py:107 +#: conf/global_settings.py:102 msgid "Kabyle" msgstr "" -#: conf/global_settings.py:108 +#: conf/global_settings.py:103 msgid "Kazakh" msgstr "" -#: conf/global_settings.py:109 +#: conf/global_settings.py:104 msgid "Khmer" msgstr "" -#: conf/global_settings.py:110 +#: conf/global_settings.py:105 msgid "Kannada" msgstr "" -#: conf/global_settings.py:111 +#: conf/global_settings.py:106 msgid "Korean" msgstr "" -#: conf/global_settings.py:112 +#: conf/global_settings.py:107 msgid "Kyrgyz" msgstr "" -#: conf/global_settings.py:113 +#: conf/global_settings.py:108 msgid "Luxembourgish" msgstr "" -#: conf/global_settings.py:114 +#: conf/global_settings.py:109 msgid "Lithuanian" msgstr "" -#: conf/global_settings.py:115 +#: conf/global_settings.py:110 msgid "Latvian" msgstr "" -#: conf/global_settings.py:116 +#: conf/global_settings.py:111 msgid "Macedonian" msgstr "" -#: conf/global_settings.py:117 +#: conf/global_settings.py:112 msgid "Malayalam" msgstr "" -#: conf/global_settings.py:118 +#: conf/global_settings.py:113 msgid "Mongolian" msgstr "" -#: conf/global_settings.py:119 +#: conf/global_settings.py:114 msgid "Marathi" msgstr "" -#: conf/global_settings.py:120 -msgid "Malay" -msgstr "" - -#: conf/global_settings.py:120 +#: conf/global_settings.py:115 msgid "Burmese" msgstr "" -#: conf/global_settings.py:121 +#: conf/global_settings.py:116 msgid "Norwegian Bokmål" msgstr "" -#: conf/global_settings.py:122 +#: conf/global_settings.py:117 msgid "Nepali" msgstr "" -#: conf/global_settings.py:123 +#: conf/global_settings.py:118 msgid "Dutch" msgstr "" -#: conf/global_settings.py:124 +#: conf/global_settings.py:119 msgid "Norwegian Nynorsk" msgstr "" -#: conf/global_settings.py:125 +#: conf/global_settings.py:120 msgid "Ossetic" msgstr "" -#: conf/global_settings.py:126 +#: conf/global_settings.py:121 msgid "Punjabi" msgstr "" -#: conf/global_settings.py:127 +#: conf/global_settings.py:122 msgid "Polish" msgstr "" -#: conf/global_settings.py:128 +#: conf/global_settings.py:123 msgid "Portuguese" msgstr "" -#: conf/global_settings.py:129 +#: conf/global_settings.py:124 msgid "Brazilian Portuguese" msgstr "" -#: conf/global_settings.py:130 +#: conf/global_settings.py:125 msgid "Romanian" msgstr "" -#: conf/global_settings.py:131 +#: conf/global_settings.py:126 msgid "Russian" msgstr "" -#: conf/global_settings.py:132 +#: conf/global_settings.py:127 msgid "Slovak" msgstr "" -#: conf/global_settings.py:133 +#: conf/global_settings.py:128 msgid "Slovenian" msgstr "" -#: conf/global_settings.py:134 +#: conf/global_settings.py:129 msgid "Albanian" msgstr "" -#: conf/global_settings.py:135 +#: conf/global_settings.py:130 msgid "Serbian" msgstr "" -#: conf/global_settings.py:136 +#: conf/global_settings.py:131 msgid "Serbian Latin" msgstr "" -#: conf/global_settings.py:137 +#: conf/global_settings.py:132 msgid "Swedish" msgstr "" -#: conf/global_settings.py:138 +#: conf/global_settings.py:133 msgid "Swahili" msgstr "" -#: conf/global_settings.py:139 +#: conf/global_settings.py:134 msgid "Tamil" msgstr "" -#: conf/global_settings.py:140 +#: conf/global_settings.py:135 msgid "Telugu" msgstr "" -#: conf/global_settings.py:141 +#: conf/global_settings.py:136 msgid "Tajik" msgstr "" -#: conf/global_settings.py:142 +#: conf/global_settings.py:137 msgid "Thai" msgstr "" -#: conf/global_settings.py:143 +#: conf/global_settings.py:138 msgid "Turkmen" msgstr "" -#: conf/global_settings.py:144 +#: conf/global_settings.py:139 msgid "Turkish" msgstr "" -#: conf/global_settings.py:145 +#: conf/global_settings.py:140 msgid "Tatar" msgstr "" -#: conf/global_settings.py:146 +#: conf/global_settings.py:141 msgid "Udmurt" msgstr "" -#: conf/global_settings.py:147 +#: conf/global_settings.py:142 msgid "Ukrainian" msgstr "" -#: conf/global_settings.py:148 +#: conf/global_settings.py:143 msgid "Urdu" msgstr "" -#: conf/global_settings.py:149 +#: conf/global_settings.py:144 msgid "Uzbek" msgstr "" -#: conf/global_settings.py:150 +#: conf/global_settings.py:145 msgid "Vietnamese" msgstr "" -#: conf/global_settings.py:151 +#: conf/global_settings.py:146 msgid "Simplified Chinese" msgstr "" -#: conf/global_settings.py:152 +#: conf/global_settings.py:147 msgid "Traditional Chinese" msgstr "" @@ -440,62 +436,62 @@ msgstr "" msgid "Enter a valid value." msgstr "" -#: core/validators.py:93 forms/fields.py:674 +#: core/validators.py:93 forms/fields.py:664 msgid "Enter a valid URL." msgstr "" -#: core/validators.py:150 +#: core/validators.py:147 msgid "Enter a valid integer." msgstr "" -#: core/validators.py:161 +#: core/validators.py:158 msgid "Enter a valid email address." msgstr "" #. Translators: "letters" means latin letters: a-z and A-Z. -#: core/validators.py:262 +#: core/validators.py:259 msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -#: core/validators.py:269 +#: core/validators.py:266 msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -#: core/validators.py:278 core/validators.py:288 core/validators.py:311 +#: core/validators.py:275 core/validators.py:295 msgid "Enter a valid IPv4 address." msgstr "" -#: core/validators.py:296 core/validators.py:312 +#: core/validators.py:280 core/validators.py:296 msgid "Enter a valid IPv6 address." msgstr "" -#: core/validators.py:306 core/validators.py:310 +#: core/validators.py:290 core/validators.py:294 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: core/validators.py:340 +#: core/validators.py:324 msgid "Enter only digits separated by commas." msgstr "" -#: core/validators.py:346 +#: core/validators.py:330 #, python-format msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "" -#: core/validators.py:379 +#: core/validators.py:363 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "" -#: core/validators.py:388 +#: core/validators.py:372 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "" -#: core/validators.py:398 +#: core/validators.py:382 #, python-format msgid "" "Ensure this value has at least %(limit_value)d character (it has " @@ -506,7 +502,7 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: core/validators.py:413 +#: core/validators.py:397 #, python-format msgid "" "Ensure this value has at most %(limit_value)d character (it has " @@ -517,25 +513,25 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: core/validators.py:432 forms/fields.py:292 forms/fields.py:327 +#: core/validators.py:416 forms/fields.py:292 forms/fields.py:327 msgid "Enter a number." msgstr "" -#: core/validators.py:434 +#: core/validators.py:418 #, python-format msgid "Ensure that there are no more than %(max)s digit in total." msgid_plural "Ensure that there are no more than %(max)s digits in total." msgstr[0] "" msgstr[1] "" -#: core/validators.py:439 +#: core/validators.py:423 #, python-format msgid "Ensure that there are no more than %(max)s decimal place." msgid_plural "Ensure that there are no more than %(max)s decimal places." msgstr[0] "" msgstr[1] "" -#: core/validators.py:444 +#: core/validators.py:428 #, python-format msgid "" "Ensure that there are no more than %(max)s digit before the decimal point." @@ -544,22 +540,22 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: core/validators.py:506 +#: core/validators.py:490 #, python-format msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -#: core/validators.py:559 +#: core/validators.py:543 msgid "Null characters are not allowed." msgstr "" -#: db/models/base.py:1201 forms/models.py:772 +#: db/models/base.py:1197 forms/models.py:768 msgid "and" msgstr "" -#: db/models/base.py:1203 +#: db/models/base.py:1199 #, python-format msgid "%(model_name)s with this %(field_labels)s already exists." msgstr "" @@ -595,197 +591,197 @@ msgstr "" msgid "Field of type: %(field_type)s" msgstr "" -#: db/models/fields/__init__.py:954 +#: db/models/fields/__init__.py:958 #, python-format msgid "“%(value)s” value must be either True or False." msgstr "" -#: db/models/fields/__init__.py:955 +#: db/models/fields/__init__.py:959 #, python-format msgid "“%(value)s” value must be either True, False, or None." msgstr "" -#: db/models/fields/__init__.py:957 +#: db/models/fields/__init__.py:961 msgid "Boolean (Either True or False)" msgstr "" -#: db/models/fields/__init__.py:998 +#: db/models/fields/__init__.py:1002 #, python-format msgid "String (up to %(max_length)s)" msgstr "" -#: db/models/fields/__init__.py:1092 +#: db/models/fields/__init__.py:1096 msgid "Comma-separated integers" msgstr "" -#: db/models/fields/__init__.py:1187 +#: db/models/fields/__init__.py:1145 #, python-format msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -#: db/models/fields/__init__.py:1189 db/models/fields/__init__.py:1311 +#: db/models/fields/__init__.py:1147 db/models/fields/__init__.py:1290 #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -#: db/models/fields/__init__.py:1192 +#: db/models/fields/__init__.py:1150 msgid "Date (without time)" msgstr "" -#: db/models/fields/__init__.py:1309 +#: db/models/fields/__init__.py:1288 #, python-format msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -#: db/models/fields/__init__.py:1313 +#: db/models/fields/__init__.py:1292 #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -#: db/models/fields/__init__.py:1317 +#: db/models/fields/__init__.py:1296 msgid "Date (with time)" msgstr "" -#: db/models/fields/__init__.py:1436 +#: db/models/fields/__init__.py:1444 #, python-format msgid "“%(value)s” value must be a decimal number." msgstr "" -#: db/models/fields/__init__.py:1438 +#: db/models/fields/__init__.py:1446 msgid "Decimal number" msgstr "" -#: db/models/fields/__init__.py:1577 +#: db/models/fields/__init__.py:1585 #, python-format msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -#: db/models/fields/__init__.py:1580 +#: db/models/fields/__init__.py:1588 msgid "Duration" msgstr "" -#: db/models/fields/__init__.py:1630 +#: db/models/fields/__init__.py:1638 msgid "Email address" msgstr "" -#: db/models/fields/__init__.py:1653 +#: db/models/fields/__init__.py:1661 msgid "File path" msgstr "" -#: db/models/fields/__init__.py:1719 +#: db/models/fields/__init__.py:1727 #, python-format msgid "“%(value)s” value must be a float." msgstr "" -#: db/models/fields/__init__.py:1721 +#: db/models/fields/__init__.py:1729 msgid "Floating point number" msgstr "" -#: db/models/fields/__init__.py:1759 +#: db/models/fields/__init__.py:1767 #, python-format msgid "“%(value)s” value must be an integer." msgstr "" -#: db/models/fields/__init__.py:1761 +#: db/models/fields/__init__.py:1769 msgid "Integer" msgstr "" -#: db/models/fields/__init__.py:1844 +#: db/models/fields/__init__.py:1852 msgid "Big (8 byte) integer" msgstr "" -#: db/models/fields/__init__.py:1859 +#: db/models/fields/__init__.py:1867 msgid "Small integer" msgstr "" -#: db/models/fields/__init__.py:1867 +#: db/models/fields/__init__.py:1875 msgid "IPv4 address" msgstr "" -#: db/models/fields/__init__.py:1898 +#: db/models/fields/__init__.py:1906 msgid "IP address" msgstr "" -#: db/models/fields/__init__.py:1978 db/models/fields/__init__.py:1979 +#: db/models/fields/__init__.py:1986 db/models/fields/__init__.py:1987 #, python-format msgid "“%(value)s” value must be either None, True or False." msgstr "" -#: db/models/fields/__init__.py:1981 +#: db/models/fields/__init__.py:1989 msgid "Boolean (Either True, False or None)" msgstr "" -#: db/models/fields/__init__.py:2035 +#: db/models/fields/__init__.py:2043 msgid "Positive big integer" msgstr "" -#: db/models/fields/__init__.py:2048 +#: db/models/fields/__init__.py:2056 msgid "Positive integer" msgstr "" -#: db/models/fields/__init__.py:2061 +#: db/models/fields/__init__.py:2069 msgid "Positive small integer" msgstr "" -#: db/models/fields/__init__.py:2075 +#: db/models/fields/__init__.py:2083 #, python-format msgid "Slug (up to %(max_length)s)" msgstr "" -#: db/models/fields/__init__.py:2107 +#: db/models/fields/__init__.py:2115 msgid "Text" msgstr "" -#: db/models/fields/__init__.py:2173 +#: db/models/fields/__init__.py:2181 #, python-format msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -#: db/models/fields/__init__.py:2175 +#: db/models/fields/__init__.py:2183 #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -#: db/models/fields/__init__.py:2178 +#: db/models/fields/__init__.py:2186 msgid "Time" msgstr "" -#: db/models/fields/__init__.py:2283 +#: db/models/fields/__init__.py:2312 msgid "URL" msgstr "" -#: db/models/fields/__init__.py:2305 +#: db/models/fields/__init__.py:2334 msgid "Raw binary data" msgstr "" -#: db/models/fields/__init__.py:2370 +#: db/models/fields/__init__.py:2399 #, python-format msgid "“%(value)s” is not a valid UUID." msgstr "" -#: db/models/fields/__init__.py:2372 +#: db/models/fields/__init__.py:2401 msgid "Universally unique identifier" msgstr "" -#: db/models/fields/files.py:226 +#: db/models/fields/files.py:225 msgid "File" msgstr "" -#: db/models/fields/files.py:375 +#: db/models/fields/files.py:373 msgid "Image" msgstr "" @@ -797,36 +793,36 @@ msgstr "" msgid "Value must be valid JSON." msgstr "" -#: db/models/fields/related.py:808 +#: db/models/fields/related.py:790 #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." msgstr "" -#: db/models/fields/related.py:810 +#: db/models/fields/related.py:792 msgid "Foreign Key (type determined by related field)" msgstr "" -#: db/models/fields/related.py:1066 +#: db/models/fields/related.py:1045 msgid "One-to-one relationship" msgstr "" -#: db/models/fields/related.py:1120 +#: db/models/fields/related.py:1099 #, python-format msgid "%(from)s-%(to)s relationship" msgstr "" -#: db/models/fields/related.py:1121 +#: db/models/fields/related.py:1100 #, python-format msgid "%(from)s-%(to)s relationships" msgstr "" -#: db/models/fields/related.py:1163 +#: db/models/fields/related.py:1142 msgid "Many-to-many relationship" msgstr "" #. Translators: If found as last label character, these punctuation #. characters will prevent the default label_suffix to be appended to the label -#: forms/boundfield.py:165 +#: forms/boundfield.py:150 msgid ":?.!" msgstr "" @@ -838,40 +834,40 @@ msgstr "" msgid "Enter a whole number." msgstr "" -#: forms/fields.py:401 forms/fields.py:1144 +#: forms/fields.py:391 forms/fields.py:1132 msgid "Enter a valid date." msgstr "" -#: forms/fields.py:425 forms/fields.py:1145 +#: forms/fields.py:415 forms/fields.py:1133 msgid "Enter a valid time." msgstr "" -#: forms/fields.py:453 +#: forms/fields.py:443 msgid "Enter a valid date/time." msgstr "" -#: forms/fields.py:487 +#: forms/fields.py:477 msgid "Enter a valid duration." msgstr "" -#: forms/fields.py:488 +#: forms/fields.py:478 #, python-brace-format msgid "The number of days must be between {min_days} and {max_days}." msgstr "" -#: forms/fields.py:548 +#: forms/fields.py:538 msgid "No file was submitted. Check the encoding type on the form." msgstr "" -#: forms/fields.py:549 +#: forms/fields.py:539 msgid "No file was submitted." msgstr "" -#: forms/fields.py:550 +#: forms/fields.py:540 msgid "The submitted file is empty." msgstr "" -#: forms/fields.py:552 +#: forms/fields.py:542 #, python-format msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." msgid_plural "" @@ -879,111 +875,111 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: forms/fields.py:555 +#: forms/fields.py:545 msgid "Please either submit a file or check the clear checkbox, not both." msgstr "" -#: forms/fields.py:616 +#: forms/fields.py:606 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: forms/fields.py:778 forms/fields.py:868 forms/models.py:1331 +#: forms/fields.py:768 forms/fields.py:858 forms/models.py:1309 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" -#: forms/fields.py:869 forms/fields.py:984 forms/models.py:1330 +#: forms/fields.py:859 forms/fields.py:974 forms/models.py:1308 msgid "Enter a list of values." msgstr "" -#: forms/fields.py:985 +#: forms/fields.py:975 msgid "Enter a complete value." msgstr "" -#: forms/fields.py:1203 +#: forms/fields.py:1191 msgid "Enter a valid UUID." msgstr "" -#: forms/fields.py:1233 +#: forms/fields.py:1221 msgid "Enter a valid JSON." msgstr "" #. Translators: This is the default suffix added to form field labels -#: forms/forms.py:84 +#: forms/forms.py:76 msgid ":" msgstr "" -#: forms/forms.py:230 forms/forms.py:304 +#: forms/forms.py:203 #, python-format msgid "(Hidden field %(name)s) %(error)s" msgstr "" -#: forms/formsets.py:60 +#: forms/formsets.py:61 #, python-format msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -#: forms/formsets.py:381 +#: forms/formsets.py:370 #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." msgstr[0] "" msgstr[1] "" -#: forms/formsets.py:388 +#: forms/formsets.py:377 #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." msgstr[0] "" msgstr[1] "" -#: forms/formsets.py:420 forms/formsets.py:427 +#: forms/formsets.py:405 forms/formsets.py:412 msgid "Order" msgstr "" -#: forms/formsets.py:433 +#: forms/formsets.py:417 msgid "Delete" msgstr "" -#: forms/models.py:767 +#: forms/models.py:763 #, python-format msgid "Please correct the duplicate data for %(field)s." msgstr "" -#: forms/models.py:771 +#: forms/models.py:767 #, python-format msgid "Please correct the duplicate data for %(field)s, which must be unique." msgstr "" -#: forms/models.py:777 +#: forms/models.py:773 #, python-format msgid "" "Please correct the duplicate data for %(field_name)s which must be unique " "for the %(lookup)s in %(date_field)s." msgstr "" -#: forms/models.py:786 +#: forms/models.py:782 msgid "Please correct the duplicate values below." msgstr "" -#: forms/models.py:1127 +#: forms/models.py:1109 msgid "The inline value did not match the parent instance." msgstr "" -#: forms/models.py:1211 +#: forms/models.py:1193 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" -#: forms/models.py:1333 +#: forms/models.py:1311 #, python-format msgid "“%(pk)s” is not a valid value." msgstr "" -#: forms/utils.py:198 +#: forms/utils.py:167 #, python-format msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " @@ -1002,76 +998,76 @@ msgstr "" msgid "Change" msgstr "" -#: forms/widgets.py:712 +#: forms/widgets.py:714 msgid "Unknown" msgstr "" -#: forms/widgets.py:713 +#: forms/widgets.py:715 msgid "Yes" msgstr "" -#: forms/widgets.py:714 +#: forms/widgets.py:716 msgid "No" msgstr "" #. Translators: Please do not add spaces around commas. -#: template/defaultfilters.py:827 +#: template/defaultfilters.py:805 msgid "yes,no,maybe" msgstr "" -#: template/defaultfilters.py:856 template/defaultfilters.py:873 +#: template/defaultfilters.py:834 template/defaultfilters.py:851 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "" msgstr[1] "" -#: template/defaultfilters.py:875 +#: template/defaultfilters.py:853 #, python-format msgid "%s KB" msgstr "" -#: template/defaultfilters.py:877 +#: template/defaultfilters.py:855 #, python-format msgid "%s MB" msgstr "" -#: template/defaultfilters.py:879 +#: template/defaultfilters.py:857 #, python-format msgid "%s GB" msgstr "" -#: template/defaultfilters.py:881 +#: template/defaultfilters.py:859 #, python-format msgid "%s TB" msgstr "" -#: template/defaultfilters.py:883 +#: template/defaultfilters.py:861 #, python-format msgid "%s PB" msgstr "" -#: utils/dateformat.py:72 +#: utils/dateformat.py:65 msgid "p.m." msgstr "" -#: utils/dateformat.py:73 +#: utils/dateformat.py:66 msgid "a.m." msgstr "" -#: utils/dateformat.py:78 +#: utils/dateformat.py:71 msgid "PM" msgstr "" -#: utils/dateformat.py:79 +#: utils/dateformat.py:72 msgid "AM" msgstr "" -#: utils/dateformat.py:150 +#: utils/dateformat.py:145 msgid "midnight" msgstr "" -#: utils/dateformat.py:152 +#: utils/dateformat.py:147 msgid "noon" msgstr "" @@ -1351,60 +1347,60 @@ msgstr "" msgid "This is not a valid IPv6 address." msgstr "" -#: utils/text.py:73 +#: utils/text.py:70 #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" msgstr "" -#: utils/text.py:242 +#: utils/text.py:236 msgid "or" msgstr "" #. Translators: This string is used as a separator between list elements -#: utils/text.py:261 utils/timesince.py:94 +#: utils/text.py:255 utils/timesince.py:94 msgid ", " msgstr "" #: utils/timesince.py:9 #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" +msgid "%d year" +msgid_plural "%d years" msgstr[0] "" msgstr[1] "" #: utils/timesince.py:10 #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" +msgid "%d month" +msgid_plural "%d months" msgstr[0] "" msgstr[1] "" #: utils/timesince.py:11 #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" +msgid "%d week" +msgid_plural "%d weeks" msgstr[0] "" msgstr[1] "" #: utils/timesince.py:12 #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" +msgid "%d day" +msgid_plural "%d days" msgstr[0] "" msgstr[1] "" #: utils/timesince.py:13 #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" +msgid "%d hour" +msgid_plural "%d hours" msgstr[0] "" msgstr[1] "" #: utils/timesince.py:14 #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" @@ -1419,7 +1415,7 @@ msgstr "" #: views/csrf.py:115 msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/en/formats.py b/venv/Lib/site-packages/django/conf/locale/en/formats.py index f9d143b..ccace3d 100644 --- a/venv/Lib/site-packages/django/conf/locale/en/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/en/formats.py @@ -2,64 +2,36 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date +DATE_FORMAT = 'N j, Y' +TIME_FORMAT = 'P' +DATETIME_FORMAT = 'N j, Y, P' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'F j' +SHORT_DATE_FORMAT = 'm/d/Y' +SHORT_DATETIME_FORMAT = 'm/d/Y P' +FIRST_DAY_OF_WEEK = 0 # Sunday -# Formatting for date objects. -DATE_FORMAT = "N j, Y" -# Formatting for time objects. -TIME_FORMAT = "P" -# Formatting for datetime objects. -DATETIME_FORMAT = "N j, Y, P" -# Formatting for date objects when only the year and month are relevant. -YEAR_MONTH_FORMAT = "F Y" -# Formatting for date objects when only the month and day are relevant. -MONTH_DAY_FORMAT = "F j" -# Short formatting for date objects. -SHORT_DATE_FORMAT = "m/d/Y" -# Short formatting for datetime objects. -SHORT_DATETIME_FORMAT = "m/d/Y P" -# First day of week, to be used on calendars. -# 0 means Sunday, 1 means Monday... -FIRST_DAY_OF_WEEK = 0 - -# Formats to be used when parsing dates from input boxes, in order. # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior -# Note that these format strings are different from the ones to display dates. # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%m/%d/%Y", # '10/25/2006' - "%m/%d/%y", # '10/25/06' - "%b %d %Y", # 'Oct 25 2006' - "%b %d, %Y", # 'Oct 25, 2006' - "%d %b %Y", # '25 Oct 2006' - "%d %b, %Y", # '25 Oct, 2006' - "%B %d %Y", # 'October 25 2006' - "%B %d, %Y", # 'October 25, 2006' - "%d %B %Y", # '25 October 2006' - "%d %B, %Y", # '25 October, 2006' + '%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' ] 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' - "%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 %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' + '%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' + '%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 %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' ] -TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '14:30:59' - "%H:%M:%S.%f", # '14:30:59.000200' - "%H:%M", # '14:30' -] - -# Decimal separator symbol. -DECIMAL_SEPARATOR = "." -# Thousand separator symbol. -THOUSAND_SEPARATOR = "," -# Number of digits that will be together, when splitting them by -# THOUSAND_SEPARATOR. 0 means no grouping, 3 means splitting by thousands. +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.mo index d31b977..2850447 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.po index a0a3ed8..a7e39e6 100644 --- a/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/en_AU/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Tom Fifield , 2014 -# Tom Fifield , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: English (Australia) (http://www.transifex.com/django/django/" "language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -24,11 +23,8 @@ msgstr "Afrikaans" msgid "Arabic" msgstr "Arabic" -msgid "Algerian Arabic" -msgstr "Algerian Arabic" - msgid "Asturian" -msgstr "Asturian" +msgstr "" msgid "Azerbaijani" msgstr "Azerbaijani" @@ -64,7 +60,7 @@ msgid "German" msgstr "German" msgid "Lower Sorbian" -msgstr "Lower Sorbian" +msgstr "" msgid "Greek" msgstr "Greek" @@ -73,7 +69,7 @@ msgid "English" msgstr "English" msgid "Australian English" -msgstr "Australian English" +msgstr "" msgid "British English" msgstr "British English" @@ -88,7 +84,7 @@ msgid "Argentinian Spanish" msgstr "Argentinian Spanish" msgid "Colombian Spanish" -msgstr "Colombian Spanish" +msgstr "" msgid "Mexican Spanish" msgstr "Mexican Spanish" @@ -121,7 +117,7 @@ msgid "Irish" msgstr "Irish" msgid "Scottish Gaelic" -msgstr "Scottish Gaelic" +msgstr "" msgid "Galician" msgstr "Galician" @@ -136,13 +132,13 @@ msgid "Croatian" msgstr "Croatian" msgid "Upper Sorbian" -msgstr "Upper Sorbian" +msgstr "" msgid "Hungarian" msgstr "Hungarian" msgid "Armenian" -msgstr "Armenian" +msgstr "" msgid "Interlingua" msgstr "Interlingua" @@ -150,11 +146,8 @@ msgstr "Interlingua" msgid "Indonesian" msgstr "Indonesian" -msgid "Igbo" -msgstr "Igbo" - msgid "Ido" -msgstr "Ido" +msgstr "" msgid "Icelandic" msgstr "Icelandic" @@ -169,7 +162,7 @@ msgid "Georgian" msgstr "Georgian" msgid "Kabyle" -msgstr "Kabyle" +msgstr "" msgid "Kazakh" msgstr "Kazakh" @@ -183,9 +176,6 @@ msgstr "Kannada" msgid "Korean" msgstr "Korean" -msgid "Kyrgyz" -msgstr "Kyrgyz" - msgid "Luxembourgish" msgstr "Luxembourgish" @@ -205,16 +195,13 @@ msgid "Mongolian" msgstr "Mongolian" msgid "Marathi" -msgstr "Marathi" - -msgid "Malay" msgstr "" msgid "Burmese" msgstr "Burmese" msgid "Norwegian Bokmål" -msgstr "Norwegian Bokmål" +msgstr "" msgid "Nepali" msgstr "Nepali" @@ -273,15 +260,9 @@ msgstr "Tamil" msgid "Telugu" msgstr "Telugu" -msgid "Tajik" -msgstr "Tajik" - msgid "Thai" msgstr "Thai" -msgid "Turkmen" -msgstr "Turkmen" - msgid "Turkish" msgstr "Turkish" @@ -298,7 +279,7 @@ msgid "Urdu" msgstr "Urdu" msgid "Uzbek" -msgstr "Uzbek" +msgstr "" msgid "Vietnamese" msgstr "Vietnamese" @@ -310,30 +291,25 @@ msgid "Traditional Chinese" msgstr "Traditional Chinese" msgid "Messages" -msgstr "Messages" +msgstr "" msgid "Site Maps" -msgstr "Site Maps" +msgstr "" msgid "Static Files" -msgstr "Static Files" +msgstr "" msgid "Syndication" -msgstr "Syndication" - -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" -msgstr "That page number is not an integer" +msgstr "" msgid "That page number is less than 1" -msgstr "That page number is less than 1" +msgstr "" msgid "That page contains no results" -msgstr "That page contains no results" +msgstr "" msgid "Enter a valid value." msgstr "Enter a valid value." @@ -342,7 +318,7 @@ msgid "Enter a valid URL." msgstr "Enter a valid URL." msgid "Enter a valid integer." -msgstr "Enter a valid integer." +msgstr "" msgid "Enter a valid email address." msgstr "Enter a valid email address." @@ -351,14 +327,11 @@ msgstr "Enter a valid email address." msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " -"hyphens." msgid "Enter a valid IPv4 address." msgstr "Enter a valid IPv4 address." @@ -442,22 +415,20 @@ msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -"File extension “%(extension)s” is not allowed. Allowed extensions are: " -"%(allowed_extensions)s." msgid "Null characters are not allowed." -msgstr "Null characters are not allowed." +msgstr "" msgid "and" msgstr "and" #, python-format msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "%(model_name)s with this %(field_labels)s already exists." +msgstr "" #, python-format msgid "Value %(value)r is not a valid choice." -msgstr "Value %(value)r is not a valid choice." +msgstr "" msgid "This field cannot be null." msgstr "This field cannot be null." @@ -475,7 +446,6 @@ msgstr "%(model_name)s with this %(field_label)s already exists." msgid "" "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." msgstr "" -"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." #, python-format msgid "Field of type: %(field_type)s" @@ -483,11 +453,11 @@ msgstr "Field of type: %(field_type)s" #, python-format msgid "“%(value)s” value must be either True or False." -msgstr "“%(value)s” value must be either True or False." +msgstr "" #, python-format msgid "“%(value)s” value must be either True, False, or None." -msgstr "“%(value)s” value must be either True, False, or None." +msgstr "" msgid "Boolean (Either True or False)" msgstr "Boolean (Either True or False)" @@ -504,16 +474,12 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " -"format." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " -"date." msgid "Date (without time)" msgstr "Date (without time)" @@ -523,23 +489,19 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ] format." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -"[TZ]) but it is an invalid date/time." msgid "Date (with time)" msgstr "Date (with time)" #, python-format msgid "“%(value)s” value must be a decimal number." -msgstr "“%(value)s” value must be a decimal number." +msgstr "" msgid "Decimal number" msgstr "Decimal number" @@ -549,11 +511,9 @@ msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." -"uuuuuu] format." msgid "Duration" -msgstr "Duration" +msgstr "" msgid "Email address" msgstr "Email address" @@ -563,14 +523,14 @@ msgstr "File path" #, python-format msgid "“%(value)s” value must be a float." -msgstr "“%(value)s” value must be a float." +msgstr "" msgid "Floating point number" msgstr "Floating point number" #, python-format msgid "“%(value)s” value must be an integer." -msgstr "“%(value)s” value must be an integer." +msgstr "" msgid "Integer" msgstr "Integer" @@ -578,9 +538,6 @@ msgstr "Integer" msgid "Big (8 byte) integer" msgstr "Big (8 byte) integer" -msgid "Small integer" -msgstr "Small integer" - msgid "IPv4 address" msgstr "IPv4 address" @@ -589,14 +546,11 @@ msgstr "IP address" #, python-format msgid "“%(value)s” value must be either None, True or False." -msgstr "“%(value)s” value must be either None, True or False." +msgstr "" msgid "Boolean (Either True, False or None)" msgstr "Boolean (Either True, False or None)" -msgid "Positive big integer" -msgstr "Positive big integer" - msgid "Positive integer" msgstr "Positive integer" @@ -607,6 +561,9 @@ msgstr "Positive small integer" msgid "Slug (up to %(max_length)s)" msgstr "Slug (up to %(max_length)s)" +msgid "Small integer" +msgstr "Small integer" + msgid "Text" msgstr "Text" @@ -615,16 +572,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " -"format." #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " -"invalid time." msgid "Time" msgstr "Time" @@ -637,10 +590,10 @@ msgstr "Raw binary data" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "“%(value)s” is not a valid UUID." +msgstr "" msgid "Universally unique identifier" -msgstr "Universally unique identifier" +msgstr "" msgid "File" msgstr "File" @@ -648,15 +601,9 @@ msgstr "File" msgid "Image" msgstr "Image" -msgid "A JSON object" -msgstr "A JSON object" - -msgid "Value must be valid JSON." -msgstr "Value must be valid JSON." - #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" msgid "Foreign Key (type determined by related field)" msgstr "Foreign Key (type determined by related field)" @@ -666,11 +613,11 @@ msgstr "One-to-one relationship" #, python-format msgid "%(from)s-%(to)s relationship" -msgstr "%(from)s-%(to)s relationship" +msgstr "" #, python-format msgid "%(from)s-%(to)s relationships" -msgstr "%(from)s-%(to)s relationships" +msgstr "" msgid "Many-to-many relationship" msgstr "Many-to-many relationship" @@ -697,11 +644,11 @@ msgid "Enter a valid date/time." msgstr "Enter a valid date/time." msgid "Enter a valid duration." -msgstr "Enter a valid duration." +msgstr "" #, python-brace-format msgid "The number of days must be between {min_days} and {max_days}." -msgstr "The number of days must be between {min_days} and {max_days}." +msgstr "" msgid "No file was submitted. Check the encoding type on the form." msgstr "No file was submitted. Check the encoding type on the form." @@ -739,13 +686,10 @@ msgid "Enter a list of values." msgstr "Enter a list of values." msgid "Enter a complete value." -msgstr "Enter a complete value." +msgstr "" msgid "Enter a valid UUID." -msgstr "Enter a valid UUID." - -msgid "Enter a valid JSON." -msgstr "Enter a valid JSON." +msgstr "" #. Translators: This is the default suffix added to form field labels msgid ":" @@ -755,25 +699,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Hidden field %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." +msgid "ManagementForm data is missing or has been tampered with" msgstr "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "Please submit at most %d form." -msgstr[1] "Please submit at most %d forms." +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Please submit %d or fewer forms." +msgstr[1] "Please submit %d or fewer forms." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Please submit at least %d form." -msgstr[1] "Please submit at least %d forms." +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" msgid "Order" msgstr "Order" @@ -801,7 +740,7 @@ msgid "Please correct the duplicate values below." msgstr "Please correct the duplicate values below." msgid "The inline value did not match the parent instance." -msgstr "The inline value did not match the parent instance." +msgstr "" msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" @@ -809,15 +748,13 @@ msgstr "" #, python-format msgid "“%(pk)s” is not a valid value." -msgstr "“%(pk)s” is not a valid value." +msgstr "" #, python-format msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " "may be ambiguous or it may not exist." msgstr "" -"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " -"may be ambiguous or it may not exist." msgid "Clear" msgstr "Clear" @@ -837,7 +774,15 @@ msgstr "Yes" msgid "No" msgstr "No" -#. Translators: Please do not add spaces around commas. +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + msgid "yes,no,maybe" msgstr "yes,no,maybe" @@ -1096,12 +1041,12 @@ msgid "December" msgstr "December" msgid "This is not a valid IPv6 address." -msgstr "This is not a valid IPv6 address." +msgstr "" #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "" msgid "or" msgstr "or" @@ -1111,50 +1056,53 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d year" +msgstr[1] "%d years" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d month" +msgstr[1] "%d months" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d week" +msgstr[1] "%d weeks" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d day" +msgstr[1] "%d days" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hour" +msgstr[1] "%d hours" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minute" +msgstr[1] "%d minutes" + +msgid "0 minutes" +msgstr "0 minutes" msgid "Forbidden" -msgstr "Forbidden" +msgstr "" msgid "CSRF verification failed. Request aborted." -msgstr "CSRF verification failed. Request aborted." +msgstr "" msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1164,9 +1112,6 @@ msgid "" "enable them, at least for this site, or for HTTPS connections, or for “same-" "origin” requests." msgstr "" -"If you have configured your browser to disable “Referer” headers, please re-" -"enable them, at least for this site, or for HTTPS connections, or for “same-" -"origin” requests." msgid "" "If you are using the tag or " @@ -1175,36 +1120,26 @@ msgid "" "If you’re concerned about privacy, use alternatives like for links to third-party sites." msgstr "" -"If you are using the tag or " -"including the “Referrer-Policy: no-referrer” header, please remove them. The " -"CSRF protection requires the “Referer” header to do strict referer checking. " -"If you’re concerned about privacy, use alternatives like for links to third-party sites." msgid "" "You are seeing this message because this site requires a CSRF cookie when " "submitting forms. This cookie is required for security reasons, to ensure " "that your browser is not being hijacked by third parties." msgstr "" -"You are seeing this message because this site requires a CSRF cookie when " -"submitting forms. This cookie is required for security reasons, to ensure " -"that your browser is not being hijacked by third parties." msgid "" "If you have configured your browser to disable cookies, please re-enable " "them, at least for this site, or for “same-origin” requests." msgstr "" -"If you have configured your browser to disable cookies, please re-enable " -"them, at least for this site, or for “same-origin” requests." msgid "More information is available with DEBUG=True." -msgstr "More information is available with DEBUG=True." +msgstr "" msgid "No year specified" msgstr "No year specified" msgid "Date out of range" -msgstr "Date out of range" +msgstr "" msgid "No month specified" msgstr "No month specified" @@ -1229,14 +1164,14 @@ msgstr "" #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" #, python-format msgid "No %(verbose_name)s found matching the query" msgstr "No %(verbose_name)s found matching the query" msgid "Page is not “last”, nor can it be converted to an int." -msgstr "Page is not “last”, nor can it be converted to an int." +msgstr "" #, python-format msgid "Invalid page (%(page_number)s): %(message)s" @@ -1244,29 +1179,30 @@ msgstr "Invalid page (%(page_number)s): %(message)s" #, python-format msgid "Empty list and “%(class_name)s.allow_empty” is False." -msgstr "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" msgid "Directory indexes are not allowed here." msgstr "Directory indexes are not allowed here." #, python-format msgid "“%(path)s” does not exist" -msgstr "“%(path)s” does not exist" +msgstr "" #, python-format msgid "Index of %(directory)s" msgstr "Index of %(directory)s" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + msgid "The install worked successfully! Congratulations!" -msgstr "The install worked successfully! Congratulations!" - -#, python-format -msgid "" -"View release notes for Django %(version)s" msgstr "" -"View release notes for Django %(version)s" #, python-format msgid "" @@ -1275,25 +1211,21 @@ msgid "" "\">DEBUG=True is in your settings file and you have not configured any " "URLs." msgstr "" -"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " -"URLs." msgid "Django Documentation" -msgstr "Django Documentation" +msgstr "" msgid "Topics, references, & how-to’s" -msgstr "Topics, references, & how-to’s" +msgstr "" msgid "Tutorial: A Polling App" -msgstr "Tutorial: A Polling App" +msgstr "" msgid "Get started with Django" -msgstr "Get started with Django" +msgstr "" msgid "Django Community" -msgstr "Django Community" +msgstr "" msgid "Connect, get help, or contribute" -msgstr "Connect, get help, or contribute" +msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/en_AU/formats.py b/venv/Lib/site-packages/django/conf/locale/en_AU/formats.py index caa6f72..310577c 100644 --- a/venv/Lib/site-packages/django/conf/locale/en_AU/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/en_AU/formats.py @@ -2,40 +2,35 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j M Y" # '25 Oct 2006' -TIME_FORMAT = "P" # '2:30 p.m.' -DATETIME_FORMAT = "j M Y, P" # '25 Oct 2006, 2:30 p.m.' -YEAR_MONTH_FORMAT = "F Y" # 'October 2006' -MONTH_DAY_FORMAT = "j F" # '25 October' -SHORT_DATE_FORMAT = "d/m/Y" # '25/10/2006' -SHORT_DATETIME_FORMAT = "d/m/Y P" # '25/10/2006 2:30 p.m.' -FIRST_DAY_OF_WEEK = 0 # Sunday +DATE_FORMAT = 'j M Y' # '25 Oct 2006' +TIME_FORMAT = 'P' # '2:30 p.m.' +DATETIME_FORMAT = 'j M Y, P' # '25 Oct 2006, 2:30 p.m.' +YEAR_MONTH_FORMAT = 'F Y' # 'October 2006' +MONTH_DAY_FORMAT = 'j F' # '25 October' +SHORT_DATE_FORMAT = 'd/m/Y' # '25/10/2006' +SHORT_DATETIME_FORMAT = 'd/m/Y P' # '25/10/2006 2:30 p.m.' +FIRST_DAY_OF_WEEK = 0 # Sunday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - # "%b %d %Y", # 'Oct 25 2006' - # "%b %d, %Y", # 'Oct 25, 2006' - # "%d %b %Y", # '25 Oct 2006' - # "%d %b, %Y", # '25 Oct, 2006' - # "%B %d %Y", # 'October 25 2006' - # "%B %d, %Y", # 'October 25, 2006' - # "%d %B %Y", # '25 October 2006' - # "%d %B, %Y", # '25 October, 2006' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/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' ] 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' - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' + '%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' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/en_GB/formats.py b/venv/Lib/site-packages/django/conf/locale/en_GB/formats.py index bc90da5..8895179 100644 --- a/venv/Lib/site-packages/django/conf/locale/en_GB/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/en_GB/formats.py @@ -2,40 +2,35 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j M Y" # '25 Oct 2006' -TIME_FORMAT = "P" # '2:30 p.m.' -DATETIME_FORMAT = "j M Y, P" # '25 Oct 2006, 2:30 p.m.' -YEAR_MONTH_FORMAT = "F Y" # 'October 2006' -MONTH_DAY_FORMAT = "j F" # '25 October' -SHORT_DATE_FORMAT = "d/m/Y" # '25/10/2006' -SHORT_DATETIME_FORMAT = "d/m/Y P" # '25/10/2006 2:30 p.m.' -FIRST_DAY_OF_WEEK = 1 # Monday +DATE_FORMAT = 'j M Y' # '25 Oct 2006' +TIME_FORMAT = 'P' # '2:30 p.m.' +DATETIME_FORMAT = 'j M Y, P' # '25 Oct 2006, 2:30 p.m.' +YEAR_MONTH_FORMAT = 'F Y' # 'October 2006' +MONTH_DAY_FORMAT = 'j F' # '25 October' +SHORT_DATE_FORMAT = 'd/m/Y' # '25/10/2006' +SHORT_DATETIME_FORMAT = 'd/m/Y P' # '25/10/2006 2:30 p.m.' +FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - # "%b %d %Y", # 'Oct 25 2006' - # "%b %d, %Y", # 'Oct 25, 2006' - # "%d %b %Y", # '25 Oct 2006' - # "%d %b, %Y", # '25 Oct, 2006' - # "%B %d %Y", # 'October 25 2006' - # "%B %d, %Y", # 'October 25, 2006' - # "%d %B %Y", # '25 October 2006' - # "%d %B, %Y", # '25 October, 2006' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/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' ] 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' - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' + '%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' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/eo/formats.py b/venv/Lib/site-packages/django/conf/locale/eo/formats.py index d1346d1..604e5f5 100644 --- a/venv/Lib/site-packages/django/conf/locale/eo/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/eo/formats.py @@ -2,43 +2,46 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j\-\a \d\e F Y" # '26-a de julio 1887' -TIME_FORMAT = "H:i" # '18:59' -DATETIME_FORMAT = r"j\-\a \d\e F Y\, \j\e H:i" # '26-a de julio 1887, je 18:59' -YEAR_MONTH_FORMAT = r"F \d\e Y" # 'julio de 1887' -MONTH_DAY_FORMAT = r"j\-\a \d\e F" # '26-a de julio' -SHORT_DATE_FORMAT = "Y-m-d" # '1887-07-26' -SHORT_DATETIME_FORMAT = "Y-m-d H:i" # '1887-07-26 18:59' +DATE_FORMAT = r'j\-\a \d\e F Y' # '26-a de julio 1887' +TIME_FORMAT = 'H:i' # '18:59' +DATETIME_FORMAT = r'j\-\a \d\e F Y\, \j\e H:i' # '26-a de julio 1887, je 18:59' +YEAR_MONTH_FORMAT = r'F \d\e Y' # 'julio de 1887' +MONTH_DAY_FORMAT = r'j\-\a \d\e F' # '26-a de julio' +SHORT_DATE_FORMAT = 'Y-m-d' # '1887-07-26' +SHORT_DATETIME_FORMAT = 'Y-m-d H:i' # '1887-07-26 18:59' FIRST_DAY_OF_WEEK = 1 # Monday (lundo) # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '1887-07-26' - "%y-%m-%d", # '87-07-26' - "%Y %m %d", # '1887 07 26' - "%Y.%m.%d", # '1887.07.26' - "%d-a de %b %Y", # '26-a de jul 1887' - "%d %b %Y", # '26 jul 1887' - "%d-a de %B %Y", # '26-a de julio 1887' - "%d %B %Y", # '26 julio 1887' - "%d %m %Y", # '26 07 1887' - "%d/%m/%Y", # '26/07/1887' + '%Y-%m-%d', # '1887-07-26' + '%y-%m-%d', # '87-07-26' + '%Y %m %d', # '1887 07 26' + '%Y.%m.%d', # '1887.07.26' + '%d-a de %b %Y', # '26-a de jul 1887' + '%d %b %Y', # '26 jul 1887' + '%d-a de %B %Y', # '26-a de julio 1887' + '%d %B %Y', # '26 julio 1887' + '%d %m %Y', # '26 07 1887' + '%d/%m/%Y', # '26/07/1887' ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '18:59:00' - "%H:%M", # '18:59' + '%H:%M:%S', # '18:59:00' + '%H:%M', # '18:59' ] DATETIME_INPUT_FORMATS = [ - "%Y-%m-%d %H:%M:%S", # '1887-07-26 18:59:00' - "%Y-%m-%d %H:%M", # '1887-07-26 18:59' - "%Y.%m.%d %H:%M:%S", # '1887.07.26 18:59:00' - "%Y.%m.%d %H:%M", # '1887.07.26 18:59' - "%d/%m/%Y %H:%M:%S", # '26/07/1887 18:59:00' - "%d/%m/%Y %H:%M", # '26/07/1887 18:59' - "%y-%m-%d %H:%M:%S", # '87-07-26 18:59:00' - "%y-%m-%d %H:%M", # '87-07-26 18:59' + '%Y-%m-%d %H:%M:%S', # '1887-07-26 18:59:00' + '%Y-%m-%d %H:%M', # '1887-07-26 18:59' + + '%Y.%m.%d %H:%M:%S', # '1887.07.26 18:59:00' + '%Y.%m.%d %H:%M', # '1887.07.26 18:59' + + '%d/%m/%Y %H:%M:%S', # '26/07/1887 18:59:00' + '%d/%m/%Y %H:%M', # '26/07/1887 18:59' + + '%y-%m-%d %H:%M:%S', # '87-07-26 18:59:00' + '%y-%m-%d %H:%M', # '87-07-26 18:59' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.mo index f48ccad..f40f75a 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.po index 56e3c7b..a07e001 100644 --- a/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/es/LC_MESSAGES/django.po @@ -21,7 +21,6 @@ # Leonardo J. Caballero G. , 2011,2013 # Luigy, 2019 # Marc Garcia , 2011 -# Mariusz Felisiak , 2021 # monobotsoft , 2012 # ntrrgc , 2013 # ntrrgc , 2013 @@ -33,9 +32,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:30+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-02-11 06:03+0000\n" +"Last-Translator: Uriel Medina \n" "Language-Team: Spanish (http://www.transifex.com/django/django/language/" "es/)\n" "MIME-Version: 1.0\n" @@ -233,9 +232,6 @@ msgstr "Mongol" msgid "Marathi" msgstr "Maratí" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Birmano" @@ -1137,7 +1133,7 @@ msgstr "No es una dirección IPv6 válida." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "o" @@ -1147,40 +1143,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d años" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutos" -msgstr[1] "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" msgid "Forbidden" msgstr "Prohibido" @@ -1190,11 +1186,11 @@ msgstr "La verificación CSRF ha fallado. Solicitud abortada." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Estás viendo este mensaje porque este sitio HTTPS requiere que tu navegador " +"Está viendo este mensaje porque este sitio HTTPS requiere que su navegador " "web envíe un \"encabezado de referencia\", pero no se envió ninguno. Este " "encabezado es necesario por razones de seguridad, para garantizar que su " "navegador no sea secuestrado por terceros." diff --git a/venv/Lib/site-packages/django/conf/locale/es/formats.py b/venv/Lib/site-packages/django/conf/locale/es/formats.py index ff9690b..b7aca78 100644 --- a/venv/Lib/site-packages/django/conf/locale/es/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\a\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\a\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '31/12/2009' - "%d/%m/%y", # '31/12/09' + # '31/12/2009', '31/12/09' + '%d/%m/%Y', '%d/%m/%y' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.mo index 47f8863..9d8e897 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.po index 8123a09..f4519de 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 14:56+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-21 12:52+0000\n" "Last-Translator: Ramiro Morales\n" "Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/" "language/es_AR/)\n" @@ -209,9 +209,6 @@ msgstr "mongol" msgid "Marathi" msgstr "maratí" -msgid "Malay" -msgstr "malayo" - msgid "Burmese" msgstr "burmés" @@ -1123,40 +1120,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d año" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minutos" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" msgid "Forbidden" msgstr "Prohibido" @@ -1166,15 +1163,15 @@ msgstr "Verificación CSRF fallida. Petición abortada." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "Ud. está viendo este mensaje porque este sitio HTTPS tiene como " -"requerimiento que su navegador web envíe un encabezado “Referer” pero el " -"mismo no ha enviado uno. El hecho de que este encabezado sea obligatorio es " -"una medida de seguridad para comprobar que su navegador no está siendo " -"controlado por terceros." +"requerimiento que su browser Web envíe una cabecera “Referer” pero el mismo " +"no ha enviado una. El hecho de que esta cabecera sea obligatoria es una " +"medida de seguridad para comprobar que su browser no está siendo controlado " +"por terceros." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/es_AR/formats.py b/venv/Lib/site-packages/django/conf/locale/es_AR/formats.py index 601b458..e856c4a 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_AR/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es_AR/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j N Y" -TIME_FORMAT = r"H:i" -DATETIME_FORMAT = r"j N Y H:i" -YEAR_MONTH_FORMAT = r"F Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = r"d/m/Y" -SHORT_DATETIME_FORMAT = r"d/m/Y H:i" +DATE_FORMAT = r'j N Y' +TIME_FORMAT = r'H:i' +DATETIME_FORMAT = r'j N Y H:i' +YEAR_MONTH_FORMAT = r'F Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = r'd/m/Y' +SHORT_DATETIME_FORMAT = r'd/m/Y H:i' FIRST_DAY_OF_WEEK = 0 # 0: Sunday, 1: Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '31/12/2009' - "%d/%m/%y", # '31/12/09' + '%d/%m/%Y', # '31/12/2009' + '%d/%m/%y', # '31/12/09' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es_CO/formats.py b/venv/Lib/site-packages/django/conf/locale/es_CO/formats.py index 056d0ad..cefbe26 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_CO/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es_CO/formats.py @@ -1,26 +1,26 @@ # This file is distributed under the same license as the Django package. # -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\a\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\a\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 1 DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%Y%m%d", # '20061025' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' + '%Y%m%d', # '20061025' + ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es_MX/formats.py b/venv/Lib/site-packages/django/conf/locale/es_MX/formats.py index d675d79..760edcf 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_MX/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es_MX/formats.py @@ -1,26 +1,25 @@ # This file is distributed under the same license as the Django package. # -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\a\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\a\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday: ISO 8601 DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%Y%m%d", # '20061025' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' + '%Y%m%d', # '20061025' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "." # ',' is also official (less common): NOM-008-SCFI-2002 -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' # ',' is also official (less common): NOM-008-SCFI-2002 +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es_NI/formats.py b/venv/Lib/site-packages/django/conf/locale/es_NI/formats.py index 0c8112a..2eacf50 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_NI/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es_NI/formats.py @@ -1,26 +1,26 @@ # This file is distributed under the same license as the Django package. # -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\a\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\a\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday: ISO 8601 DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%Y%m%d", # '20061025' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' + '%Y%m%d', # '20061025' + ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/es_PR/formats.py b/venv/Lib/site-packages/django/conf/locale/es_PR/formats.py index d50fe5d..7f53ef9 100644 --- a/venv/Lib/site-packages/django/conf/locale/es_PR/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/es_PR/formats.py @@ -1,27 +1,27 @@ # This file is distributed under the same license as the Django package. # -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \a \l\a\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \a \l\a\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 0 # Sunday DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '31/12/2009' - "%d/%m/%y", # '31/12/09' + # '31/12/2009', '31/12/09' + '%d/%m/%Y', '%d/%m/%y' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M", - "%d/%m/%y %H:%M:%S", - "%d/%m/%y %H:%M:%S.%f", - "%d/%m/%y %H:%M", + '%d/%m/%Y %H:%M:%S', + '%d/%m/%Y %H:%M:%S.%f', + '%d/%m/%Y %H:%M', + '%d/%m/%y %H:%M:%S', + '%d/%m/%y %H:%M:%S.%f', + '%d/%m/%y %H:%M', ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.mo index 9eb0945..d4f7c64 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.po index 07cf4fb..d38446f 100644 --- a/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/et/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 11:27+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-22 11:50+0000\n" "Last-Translator: Martin \n" "Language-Team: Estonian (http://www.transifex.com/django/django/language/" "et/)\n" @@ -214,9 +214,6 @@ msgstr "mongoolia" msgid "Marathi" msgstr "marathi" -msgid "Malay" -msgstr "malai" - msgid "Burmese" msgstr "birma" @@ -1119,40 +1116,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d aasta" -msgstr[1] "%(num)d aastat" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d aasta" +msgstr[1] "%d aastat" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d kuu" -msgstr[1] "%(num)d kuud" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuu" +msgstr[1] "%d kuud" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nädal" -msgstr[1] "%(num)d nädalat" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nädal" +msgstr[1] "%d nädalat" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d päev" -msgstr[1] "%(num)d päeva" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päev" +msgstr[1] "%d päeva" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tund" -msgstr[1] "%(num)d tundi" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tund" +msgstr[1] "%d tundi" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutit" msgid "Forbidden" msgstr "Keelatud" @@ -1162,7 +1159,7 @@ msgstr "CSRF verifitseerimine ebaõnnestus. Päring katkestati." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/et/formats.py b/venv/Lib/site-packages/django/conf/locale/et/formats.py index 3b2d9ba..1e1e458 100644 --- a/venv/Lib/site-packages/django/conf/locale/et/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/et/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y" -TIME_FORMAT = "G:i" +DATE_FORMAT = 'j. F Y' +TIME_FORMAT = 'G:i' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = @@ -16,6 +16,6 @@ SHORT_DATE_FORMAT = "d.m.Y" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = " " # Non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = ' ' # Non-breaking space # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/eu/formats.py b/venv/Lib/site-packages/django/conf/locale/eu/formats.py index 61b16fb..33e6305 100644 --- a/venv/Lib/site-packages/django/conf/locale/eu/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/eu/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"Y\k\o N j\a" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"Y\k\o N j\a, H:i" -YEAR_MONTH_FORMAT = r"Y\k\o F" -MONTH_DAY_FORMAT = r"F\r\e\n j\a" -SHORT_DATE_FORMAT = "Y-m-d" -SHORT_DATETIME_FORMAT = "Y-m-d H:i" +DATE_FORMAT = r'Y\k\o N j\a' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'Y\k\o N j\a, H:i' +YEAR_MONTH_FORMAT = r'Y\k\o F' +MONTH_DAY_FORMAT = r'F\r\e\n j\a' +SHORT_DATE_FORMAT = 'Y-m-d' +SHORT_DATETIME_FORMAT = 'Y-m-d H:i' FIRST_DAY_OF_WEEK = 1 # Astelehena # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ FIRST_DAY_OF_WEEK = 1 # Astelehena # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.mo index 906097c..6037b1f 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.po index 795602f..45af4c2 100644 --- a/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/fa/LC_MESSAGES/django.po @@ -2,13 +2,10 @@ # # Translators: # Ahmad Hosseini , 2020 -# alirezamastery , 2021 # Ali Vakilzade , 2015 # Arash Fazeli , 2012 # Eric Hamiter , 2019 -# Farshad Asadpour, 2021 # Jannis Leidel , 2011 -# Mariusz Felisiak , 2021 # Mazdak Badakhshan , 2014 # Milad Hazrati , 2019 # MJafar Mashhadi , 2018 @@ -23,9 +20,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:28+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-03 06:19+0000\n" +"Last-Translator: rahim agh \n" "Language-Team: Persian (http://www.transifex.com/django/django/language/" "fa/)\n" "MIME-Version: 1.0\n" @@ -223,9 +220,6 @@ msgstr "مغولی" msgid "Marathi" msgstr "مِراتی" -msgid "Malay" -msgstr "Malay" - msgid "Burmese" msgstr "برمه‌ای" @@ -774,8 +768,6 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"اطلاعات ManagementForm مفقود یا دستکاری شده است. ردیف های مفقود شده: " -"%(field_names)s. اگر این مشکل ادامه داشت، آن را گزارش کنید." #, python-format msgid "Please submit at most %d form." @@ -1114,7 +1106,7 @@ msgstr "این مقدار آدرس IPv6 معتبری نیست." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s ..." msgid "or" msgstr "یا" @@ -1124,40 +1116,40 @@ msgid ", " msgstr "،" #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d سال" -msgstr[1] "%(num)d سال ها" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d سال" +msgstr[1] "%d سال" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ماه" -msgstr[1] "%(num)d ماه ها" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ماه" +msgstr[1] "%d ماه" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d هفته" -msgstr[1] "%(num)d هفته ها" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d هفته" +msgstr[1] "%d هفته" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d روز" -msgstr[1] "%(num)d روزها" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d روز" +msgstr[1] "%d روز" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ساعت" -msgstr[1] "%(num)d ساعت ها" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ساعت" +msgstr[1] "%d ساعت" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d دقیقه" -msgstr[1] "%(num)d دقیقه ها" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d دقیقه" +msgstr[1] "%d دقیقه" msgid "Forbidden" msgstr "ممنوع" @@ -1167,14 +1159,14 @@ msgstr "‏CSRF تأیید نشد. درخواست لغو شد." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"شما این پیغام را مشاهده میکنید برای اینکه این HTTPS site نیازمند یک " -"\"Referer header\" برای ارسال توسط مرورگر شما دارد،‌اما مقداری ارسال " -"نمیشود . این هدر الزامی میباشد برای امنیت ، در واقع برای اینکه مرورگر شما " -"مطمین شود hijack به عنوان نفر سوم (third parties) در میان نیست" +"شما این پیغام را می‌بینید چون این وب‌گاه HTTPS نیازمند یک \"Referer header\" " +"یا سرتیتر ارجاع دهنده است که باید توسط مرورگر شما ارسال شود. این سرتیتر به " +"دلایل امنیتی مورد نیاز است تا اطمینان حاصل شود که مرورگر شما توسط شخص سومی " +"مورد سوءاستفاده قرار نگرفته باشد." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/fa/formats.py b/venv/Lib/site-packages/django/conf/locale/fa/formats.py index e7019bc..c8666f7 100644 --- a/venv/Lib/site-packages/django/conf/locale/fa/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/fa/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j F Y، ساعت G:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "Y/n/j" -SHORT_DATETIME_FORMAT = "Y/n/j،‏ G:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j F Y، ساعت G:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'Y/n/j' +SHORT_DATETIME_FORMAT = 'Y/n/j،‏ G:i' FIRST_DAY_OF_WEEK = 6 # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ FIRST_DAY_OF_WEEK = 6 # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.mo index e805013..2f42d35 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.po index d599ae3..4f52bf6 100644 --- a/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/fi/LC_MESSAGES/django.po @@ -4,7 +4,6 @@ # Aarni Koskela, 2015,2017-2018,2020-2021 # Antti Kaihola , 2011 # Jannis Leidel , 2011 -# Jiri Grönroos , 2021 # Lasse Liehu , 2015 # Mika Mäkelä , 2018 # Klaus Dahlén , 2011 @@ -12,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-25 07:24+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-13 07:20+0000\n" "Last-Translator: Aarni Koskela\n" "Language-Team: Finnish (http://www.transifex.com/django/django/language/" "fi/)\n" @@ -126,7 +125,7 @@ msgid "Irish" msgstr "irlanti" msgid "Scottish Gaelic" -msgstr "skottilainen gaeli" +msgstr "Skottilainen gaeli" msgid "Galician" msgstr "galicia" @@ -212,9 +211,6 @@ msgstr "mongolia" msgid "Marathi" msgstr "marathi" -msgid "Malay" -msgstr "malaiji" - msgid "Burmese" msgstr "burman kieli" @@ -367,7 +363,7 @@ msgstr "" "ja tavuviivoista." msgid "Enter a valid IPv4 address." -msgstr "Syötä kelvollinen IPv4-osoite." +msgstr "DSyötä kelvollinen IPv4-osoite." msgid "Enter a valid IPv6 address." msgstr "Syötä kelvollinen IPv6-osoite." @@ -600,7 +596,7 @@ msgid "Boolean (Either True, False or None)" msgstr "Totuusarvo: joko tosi (True), epätosi (False) tai ei mikään (None)" msgid "Positive big integer" -msgstr "Suuri positiivinen kokonaisluku" +msgstr "suuri positiivinen kokonaisluku" msgid "Positive integer" msgstr "Positiivinen kokonaisluku" @@ -665,7 +661,7 @@ msgid "Foreign Key (type determined by related field)" msgstr "Vierasavain (tyyppi määräytyy liittyvän kentän mukaan)" msgid "One-to-one relationship" -msgstr "Yksi-yhteen -relaatio" +msgstr "Yksi-yhteen relaatio" #, python-format msgid "%(from)s-%(to)s relationship" @@ -676,7 +672,7 @@ msgid "%(from)s-%(to)s relationships" msgstr "%(from)s-%(to)s -suhteet" msgid "Many-to-many relationship" -msgstr "Moni-moneen -relaatio" +msgstr "Moni-moneen relaatio" #. Translators: If found as last label character, these punctuation #. characters will prevent the default label_suffix to be appended to the @@ -803,7 +799,7 @@ msgstr "" "for the %(lookup)s in %(date_field)s." msgid "Please correct the duplicate values below." -msgstr "Korjaa alla olevat kaksoisarvot." +msgstr "Korjaa allaolevat kaksoisarvot." msgid "The inline value did not match the parent instance." msgstr "Liittyvä arvo ei vastannut vanhempaa instanssia." @@ -1115,40 +1111,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d vuosi" -msgstr[1] "%(num)d vuotta" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d vuosi" +msgstr[1] "%d vuotta" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d kuukausi" -msgstr[1] "%(num)d kuukautta " +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuukausi" +msgstr[1] "%d kuukautta" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d viikko" -msgstr[1] "%(num)d viikkoa" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d viikko" +msgstr[1] "%d viikkoa" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d päivä" -msgstr[1] "%(num)d päivää" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päivä" +msgstr[1] "%d päivää" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tunti" -msgstr[1] "%(num)d tuntia" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tunti" +msgstr[1] "%d tuntia" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuutti" -msgstr[1] "%(num)d minuuttia" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuutti" +msgstr[1] "%d minuuttia" msgid "Forbidden" msgstr "Kielletty" @@ -1158,7 +1154,7 @@ msgstr "CSRF-vahvistus epäonnistui. Pyyntö hylätty." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1274,9 +1270,8 @@ msgid "" "View release notes for Django %(version)s" msgstr "" -"Katso Djangon version %(version)s julkaisutiedot" +"Katso Django %(version)s julkaisutiedot" #, python-format msgid "" diff --git a/venv/Lib/site-packages/django/conf/locale/fi/formats.py b/venv/Lib/site-packages/django/conf/locale/fi/formats.py index d9fb6d2..0a56b37 100644 --- a/venv/Lib/site-packages/django/conf/locale/fi/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/fi/formats.py @@ -2,35 +2,36 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. E Y" -TIME_FORMAT = "G.i" -DATETIME_FORMAT = r"j. E Y \k\e\l\l\o G.i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "j.n.Y" -SHORT_DATETIME_FORMAT = "j.n.Y G.i" +DATE_FORMAT = 'j. E Y' +TIME_FORMAT = 'G.i' +DATETIME_FORMAT = r'j. E Y \k\e\l\l\o G.i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'j.n.Y' +SHORT_DATETIME_FORMAT = 'j.n.Y G.i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '20.3.2014' - "%d.%m.%y", # '20.3.14' + '%d.%m.%Y', # '20.3.2014' + '%d.%m.%y', # '20.3.14' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H.%M.%S", # '20.3.2014 14.30.59' - "%d.%m.%Y %H.%M.%S.%f", # '20.3.2014 14.30.59.000200' - "%d.%m.%Y %H.%M", # '20.3.2014 14.30' - "%d.%m.%y %H.%M.%S", # '20.3.14 14.30.59' - "%d.%m.%y %H.%M.%S.%f", # '20.3.14 14.30.59.000200' - "%d.%m.%y %H.%M", # '20.3.14 14.30' + '%d.%m.%Y %H.%M.%S', # '20.3.2014 14.30.59' + '%d.%m.%Y %H.%M.%S.%f', # '20.3.2014 14.30.59.000200' + '%d.%m.%Y %H.%M', # '20.3.2014 14.30' + + '%d.%m.%y %H.%M.%S', # '20.3.14 14.30.59' + '%d.%m.%y %H.%M.%S.%f', # '20.3.14 14.30.59.000200' + '%d.%m.%y %H.%M', # '20.3.14 14.30' ] TIME_INPUT_FORMATS = [ - "%H.%M.%S", # '14.30.59' - "%H.%M.%S.%f", # '14.30.59.000200' - "%H.%M", # '14.30' + '%H.%M.%S', # '14.30.59' + '%H.%M.%S.%f', # '14.30.59.000200' + '%H.%M', # '14.30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # Non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # Non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.mo index af26f61..72747ef 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.po index 72bee50..dcec3a7 100644 --- a/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/fr/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-23 17:19+0000\n" -"Last-Translator: Claude Paroz \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-02 13:13+0000\n" +"Last-Translator: Bruno Brouard \n" "Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -212,9 +212,6 @@ msgstr "Mongole" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "Malais" - msgid "Burmese" msgstr "Birman" @@ -1133,40 +1130,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d année" -msgstr[1] "%(num)d années" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d année" +msgstr[1] "%d années" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mois" -msgstr[1] "%(num)d mois" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mois" +msgstr[1] "%d mois" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semaine" -msgstr[1] "%(num)d semaines" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semaine" +msgstr[1] "%d semaines" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d jour" -msgstr[1] "%(num)d jours" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d jour" +msgstr[1] "%d jours" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d heure" -msgstr[1] "%(num)d heures" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d heure" +msgstr[1] "%d heures" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minute" -msgstr[1] "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minute" +msgstr[1] "%d minutes" msgid "Forbidden" msgstr "Interdit" @@ -1176,11 +1173,11 @@ msgstr "La vérification CSRF a échoué. La requête a été interrompue." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Vous voyez ce message parce que ce site HTTPS exige que le navigateur web " +"Vous voyez ce message parce que ce site HTTPS exige que le navigateur Web " "envoie un en-tête « Referer », ce qu’il n'a pas fait. Cet en-tête est exigé " "pour des raisons de sécurité, afin de s’assurer que le navigateur n’ait pas " "été piraté par un intervenant externe." diff --git a/venv/Lib/site-packages/django/conf/locale/fr/formats.py b/venv/Lib/site-packages/django/conf/locale/fr/formats.py index 5845e6a..f24e77f 100644 --- a/venv/Lib/site-packages/django/conf/locale/fr/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/fr/formats.py @@ -2,32 +2,30 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j N Y" -SHORT_DATETIME_FORMAT = "j N Y H:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j N Y' +SHORT_DATETIME_FORMAT = 'j N Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%d.%m.%Y", # Swiss [fr_CH] '25.10.2006' - "%d.%m.%y", # Swiss [fr_CH] '25.10.06' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' + '%d.%m.%Y', '%d.%m.%y', # Swiss [fr_CH), '25.10.2006', '25.10.06' # '%d %B %Y', '%d %b %Y', # '25 octobre 2006', '25 oct. 2006' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d.%m.%Y %H:%M:%S", # Swiss [fr_CH), '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # Swiss (fr_CH), '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # Swiss (fr_CH), '25.10.2006 14:30' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d.%m.%Y %H:%M:%S', # Swiss [fr_CH), '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # Swiss (fr_CH), '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # Swiss (fr_CH), '25.10.2006 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ga/formats.py b/venv/Lib/site-packages/django/conf/locale/ga/formats.py index 7cde1a5..eb3614a 100644 --- a/venv/Lib/site-packages/django/conf/locale/ga/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ga/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "H:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'H:i' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = @@ -16,6 +16,6 @@ SHORT_DATE_FORMAT = "j M Y" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.mo index f177bbd..5173b1a 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.po index ba28564..8c46e5a 100644 --- a/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/gd/LC_MESSAGES/django.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-20 14:00+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-05 09:31+0000\n" "Last-Translator: GunChleoc\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" @@ -211,9 +211,6 @@ msgstr "Mongolais" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "Malaidhis" - msgid "Burmese" msgstr "Burmais" @@ -328,7 +325,7 @@ msgstr "Siondacaideadh" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" msgstr "Chan eil àireamh na duilleige seo 'na àireamh slàn" @@ -813,25 +810,22 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"Tha dàta an fhoirm stiùiridh a dhìth no chaidh beantainn ris. Seo na " -"raointean a tha a dhìth: %(field_names)s. Ma mhaireas an duilgheadas, saoil " -"an cuir thu aithris air buga thugainn?" #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Na cuir a-null barrachd air %d fhoirm." -msgstr[1] "Na cuir a-null barrachd air %d fhoirm." -msgstr[2] "Na cuir a-null barrachd air %d foirmean." -msgstr[3] "Na cuir a-null barrachd air %d foirm." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Cuir a-null %d fhoirm air a char as lugha." -msgstr[1] "Cuir a-null %d fhoirm air a char as lugha." -msgstr[2] "Cuir a-null %d foirmichean air a char as lugha." -msgstr[3] "Cuir a-null %d foirm air a char as lugha." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" msgid "Order" msgstr "Òrdugh" @@ -1173,52 +1167,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d bliadhna" -msgstr[1] "%(num)d bhliadhna" -msgstr[2] "%(num)d bliadhnaichean" -msgstr[3] "%(num)d bliadhna" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d bhliadhna" +msgstr[1] "%d bhliadhna" +msgstr[2] "%d bliadhnaichean" +msgstr[3] "%d bliadhna" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mhìos" -msgstr[1] "%(num)d mhìos" -msgstr[2] "%(num)d mìosan" -msgstr[3] "%(num)d mìos" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mhìos" +msgstr[1] "%d mhìos" +msgstr[2] "%d mìosan" +msgstr[3] "%d mìos" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d seachdain" -msgstr[1] "%(num)d sheachdain" -msgstr[2] "%(num)d seachdainean" -msgstr[3] "%(num)d seachdain" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d seachdain" +msgstr[1] "%d sheachdain" +msgstr[2] "%d seachdainean" +msgstr[3] "%d seachdain" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d latha" -msgstr[1] "%(num)d latha" -msgstr[2] "%(num)d làithean" -msgstr[3] "%(num)d latha" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d latha" +msgstr[1] "%d latha" +msgstr[2] "%d làithean" +msgstr[3] "%d latha" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d uair a thìde" -msgstr[1] "%(num)d uair a thìde" -msgstr[2] "%(num)d uairean a thìde" -msgstr[3] "%(num)d uair a thìde" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d uair" +msgstr[1] "%d uair" +msgstr[2] "%d uairean" +msgstr[3] "%d uair" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d mhionaid" -msgstr[1] "%(num)d mhionaid" -msgstr[2] "%(num)d mionaidean" -msgstr[3] "%(num)d mionaid" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d mhionaid" +msgstr[1] "%d mhionaid" +msgstr[2] "%d mionaidean" +msgstr[3] "%d mionaid" msgid "Forbidden" msgstr "Toirmisgte" @@ -1228,7 +1222,7 @@ msgstr "Dh’fhàillig le dearbhadh CSRF. chaidh sgur dhen iarrtas." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/gd/formats.py b/venv/Lib/site-packages/django/conf/locale/gd/formats.py index 5ef6774..19b42ee 100644 --- a/venv/Lib/site-packages/django/conf/locale/gd/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/gd/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "h:ia" -DATETIME_FORMAT = "j F Y h:ia" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'h:ia' +DATETIME_FORMAT = 'j F Y h:ia' # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" -SHORT_DATETIME_FORMAT = "j M Y h:ia" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' +SHORT_DATETIME_FORMAT = 'j M Y h:ia' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ FIRST_DAY_OF_WEEK = 1 # Monday # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/gl/formats.py b/venv/Lib/site-packages/django/conf/locale/gl/formats.py index 7372935..9f29c23 100644 --- a/venv/Lib/site-packages/django/conf/locale/gl/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/gl/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y \á\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d-m-Y" -SHORT_DATETIME_FORMAT = "d-m-Y, H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y \á\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd-m-Y' +SHORT_DATETIME_FORMAT = 'd-m-Y, H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ FIRST_DAY_OF_WEEK = 1 # Monday # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.mo index 6cd5aba..03d57e2 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.po index cd074cd..00ada75 100644 --- a/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/he/LC_MESSAGES/django.po @@ -4,16 +4,14 @@ # 534b44a19bf18d20b71ecc4eb77c572f_db336e9 , 2011-2012 # Jannis Leidel , 2011 # Meir Kriheli , 2011-2015,2017,2019-2020 -# Menachem G., 2021 -# Yaron Shahrabani , 2021 -# Uri Rodberg , 2021 +# אורי רודברג , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-08-02 13:17+0000\n" +"Last-Translator: Meir Kriheli \n" "Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -211,9 +209,6 @@ msgstr "מונגולי" msgid "Marathi" msgstr "מראטהי" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "בּוּרְמֶזִית" @@ -325,11 +320,6 @@ msgstr "קבצים סטטיים" msgid "Syndication" msgstr "הפצת תכנים" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "" - msgid "That page number is not an integer" msgstr "מספר העמוד אינו מספר שלם" @@ -586,9 +576,6 @@ msgstr "מספר שלם" msgid "Big (8 byte) integer" msgstr "מספר שלם גדול (8 בתים)" -msgid "Small integer" -msgstr "מספר שלם קטן" - msgid "IPv4 address" msgstr "כתובת IPv4" @@ -615,6 +602,9 @@ msgstr "מספר שלם חיובי קטן" msgid "Slug (up to %(max_length)s)" msgstr "Slug (עד %(max_length)s תווים)" +msgid "Small integer" +msgstr "מספר שלם קטן" + msgid "Text" msgstr "טקסט" @@ -763,29 +753,24 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(שדה מוסתר %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" -"המידע של ManagementForm חסר או שובש. שדות חסרים: %(field_names)s. יתכן " -"שתצטרך להגיש דיווח באג אם הבעיה נמשכת." +msgid "ManagementForm data is missing or has been tampered with" +msgstr "מידע ManagementForm חסר או התעסקו איתו." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "נא לשלוח טופס %d לכל היותר." +msgstr[1] "נא לשלוח %d טפסים לכל היותר." +msgstr[2] "נא לשלוח %d טפסים לכל היותר." +msgstr[3] "נא לשלוח %d טפסים לכל היותר." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "נא לשלוח טופס %d או יותר." +msgstr[1] "נא לשלוח %d טפסים או יותר." +msgstr[2] "נא לשלוח %d טפסים או יותר." +msgstr[3] "נא לשלוח %d טפסים או יותר." msgid "Order" msgstr "מיון" @@ -1124,52 +1109,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "שנה" -msgstr[1] "שנתיים" -msgstr[2] "%(num)d שנים" -msgstr[3] "%(num)d שנים" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "שנה %d" +msgstr[1] "%d שנים" +msgstr[2] "%d שנים" +msgstr[3] "%d שנים" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "חודש" -msgstr[1] "חודשיים" -msgstr[2] "%(num)d חודשים" -msgstr[3] "%(num)d חודשים" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "חודש %d" +msgstr[1] "%d חודשים" +msgstr[2] "%d חודשים" +msgstr[3] "%d חודשים" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "שבוע" -msgstr[1] "שבועיים" -msgstr[2] "%(num)d שבועות" -msgstr[3] "%(num)d שבועות" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "שבוע %d" +msgstr[1] "%d שבועות" +msgstr[2] "%d שבועות" +msgstr[3] "%d שבועות" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "יום" -msgstr[1] "יומיים" -msgstr[2] "%(num)d ימים" -msgstr[3] "%(num)d ימים" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "יום %d" +msgstr[1] "%d ימים" +msgstr[2] "%d ימים" +msgstr[3] "%d ימים" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "שעה" -msgstr[1] "שעתיים" -msgstr[2] "%(num)d שעות" -msgstr[3] "%(num)d שעות" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "שעה %d" +msgstr[1] "%d שעות" +msgstr[2] "%d שעות" +msgstr[3] "%d שעות" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "דקה" -msgstr[1] "%(num)d דקות" -msgstr[2] "%(num)d דקות" -msgstr[3] "%(num)d דקות" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "דקה %d" +msgstr[1] "%d דקות" +msgstr[2] "%d דקות" +msgstr[3] "%d דקות" msgid "Forbidden" msgstr "אסור" @@ -1179,10 +1164,13 @@ msgstr "אימות CSRF נכשל. הבקשה בוטלה." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"הודעה זו מופיעה מאחר ואתר ה־HTTPS הזה דורש מהדפדפן שלך לשלוח \"Referer header" +"\", אך הוא לא נשלח. זה נדרש מסיבות אבטחה, כדי להבטיח שהדפדפן שלך לא נחטף ע" +"\"י צד שלישי." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1223,19 +1211,19 @@ msgid "More information is available with DEBUG=True." msgstr "מידע נוסף זמין עם " msgid "No year specified" -msgstr "לא צוינה שנה" +msgstr "לא צויינה שנה" msgid "Date out of range" msgstr "תאריך מחוץ לטווח" msgid "No month specified" -msgstr "לא צוין חודש" +msgstr "לא צויין חודש" msgid "No day specified" -msgstr "לא צוין יום" +msgstr "לא צויין יום" msgid "No week specified" -msgstr "לא צוין שבוע" +msgstr "לא צויין שבוע" #, python-format msgid "No %(verbose_name_plural)s available" @@ -1279,8 +1267,8 @@ msgstr "\"%(path)s\" אינו קיים" msgid "Index of %(directory)s" msgstr "אינדקס של %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "ההתקנה עברה בהצלחה! מזל טוב!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "Django: תשתית הווב לפרפקציוניסטים עם תאריכי יעד." #, python-format msgid "" @@ -1290,6 +1278,9 @@ msgstr "" "ראו הערות השחרור עבור Django %(version)s" +msgid "The install worked successfully! Congratulations!" +msgstr "ההתקנה עברה בהצלחה! מזל טוב!" + #, python-format msgid "" "You are seeing this page because \n" "Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" "language/hsb/)\n" @@ -207,9 +207,6 @@ msgstr "Mongolšćina" msgid "Marathi" msgstr "Marathišćina" -msgid "Malay" -msgstr "Malajšćina" - msgid "Burmese" msgstr "Myanmaršćina" @@ -1145,52 +1142,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)dlěće" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d lěto" +msgstr[1] "%d lěće" +msgstr[2] "%d lěta" +msgstr[3] "%d lět" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsac" -msgstr[1] "%(num)d měsacaj" -msgstr[2] "%(num)d měsacy" -msgstr[3] "%(num)d měsacow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsac" +msgstr[1] "%d měsacaj" +msgstr[2] "%d měsacy" +msgstr[3] "%d měsacow" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydźeń" -msgstr[1] "%(num)d njedźeli" -msgstr[2] "%(num)d njedźele" -msgstr[3] "%(num)d njedźel" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydźeń" +msgstr[1] "%d njedźeli" +msgstr[2] "%d njedźele" +msgstr[3] "%d njedźel" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dźeń" -msgstr[1] "%(num)d dnjej" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dźeń" +msgstr[1] "%d njej" +msgstr[2] "%d dny" +msgstr[3] "%d dnjow" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodźina" -msgstr[1] "%(num)d hodźinje" -msgstr[2] "%(num)d hodźiny" -msgstr[3] "%(num)d hodźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodźina" +msgstr[1] "%d hodźinje" +msgstr[2] "%d hodźiny" +msgstr[3] "%d hodźin" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d mjeńšina" -msgstr[1] "%(num)d mjeńšinje" -msgstr[2] "%(num)d mjeńšiny" -msgstr[3] "%(num)d mjeńšin" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d mjeńšina" +msgstr[1] "%d mjeńšinje" +msgstr[2] "%d mjeńšiny" +msgstr[3] "%d mjeńšin" msgid "Forbidden" msgstr "Zakazany" @@ -1200,14 +1197,14 @@ msgstr "CSRF-přepruwowanje je so nimokuliło. Naprašowanje je so přetorhnył msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Widźiće tutu zdźělenku, dokelž tute HTTPS-sydło \"Referer header\" trjeba, " -"kotryž so ma na waš webwobhladowak pósłać, ale žadyn njeje so pósłał. Tutón " -"header je z wěstotnych přičinow trěbny, zo by so zawěsćiło, zo waš " -"wobhladowak so wot třećich njekapruje." +"Widźiće tutu zdźělenku, dokelž HTTPS-sydło „hłowu Referer“ trjeba, zo by so " +"do webwobhladowaka słało, ale njeje so pósłała. Tuta hłowa je z přičinow " +"wěstoty trěbna, zo by so zawěsćiło, zo waš wobhladowak so wot třećich " +"njekapruje." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/hu/formats.py b/venv/Lib/site-packages/django/conf/locale/hu/formats.py index c17f2c7..f0bfa21 100644 --- a/venv/Lib/site-packages/django/conf/locale/hu/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/hu/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "Y. F j." -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "Y. F j. H:i" -YEAR_MONTH_FORMAT = "Y. F" -MONTH_DAY_FORMAT = "F j." -SHORT_DATE_FORMAT = "Y.m.d." -SHORT_DATETIME_FORMAT = "Y.m.d. H:i" +DATE_FORMAT = 'Y. F j.' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'Y. F j. H:i' +YEAR_MONTH_FORMAT = 'Y. F' +MONTH_DAY_FORMAT = 'F j.' +SHORT_DATE_FORMAT = 'Y.m.d.' +SHORT_DATETIME_FORMAT = 'Y.m.d. H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%Y.%m.%d.", # '2006.10.25.' + '%Y.%m.%d.', # '2006.10.25.' ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '14:30:59' - "%H:%M", # '14:30' + '%H:%M:%S', # '14:30:59' + '%H:%M', # '14:30' ] 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. %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' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = " " # Non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = ' ' # Non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.mo index e22136f..4ff3ff5 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.po index adb852f..46d8e54 100644 --- a/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ia/LC_MESSAGES/django.po @@ -1,14 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Martijn Dekker , 2012,2014,2016,2021 +# Martijn Dekker , 2012,2014,2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: Interlingua (http://www.transifex.com/django/django/language/" "ia/)\n" "MIME-Version: 1.0\n" @@ -23,9 +23,6 @@ msgstr "afrikaans" msgid "Arabic" msgstr "arabe" -msgid "Algerian Arabic" -msgstr "Arabe algerian" - msgid "Asturian" msgstr "asturiano" @@ -141,7 +138,7 @@ msgid "Hungarian" msgstr "hungaro" msgid "Armenian" -msgstr "Armenio" +msgstr "" msgid "Interlingua" msgstr "interlingua" @@ -149,9 +146,6 @@ msgstr "interlingua" msgid "Indonesian" msgstr "indonesiano" -msgid "Igbo" -msgstr "Igbo" - msgid "Ido" msgstr "ido" @@ -168,7 +162,7 @@ msgid "Georgian" msgstr "georgiano" msgid "Kabyle" -msgstr "Kabyle" +msgstr "" msgid "Kazakh" msgstr "kazakh" @@ -182,9 +176,6 @@ msgstr "kannada" msgid "Korean" msgstr "coreano" -msgid "Kyrgyz" -msgstr "Kyrgyz" - msgid "Luxembourgish" msgstr "luxemburgese" @@ -206,9 +197,6 @@ msgstr "mongolico" msgid "Marathi" msgstr "marathi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "burmese" @@ -272,15 +260,9 @@ msgstr "tamil" msgid "Telugu" msgstr "telugu" -msgid "Tajik" -msgstr "Tadzhik" - msgid "Thai" msgstr "thailandese" -msgid "Turkmen" -msgstr "Turkmen" - msgid "Turkish" msgstr "turco" @@ -297,7 +279,7 @@ msgid "Urdu" msgstr "urdu" msgid "Uzbek" -msgstr "Uzbek" +msgstr "" msgid "Vietnamese" msgstr "vietnamese" @@ -320,19 +302,14 @@ msgstr "Files static" msgid "Syndication" msgstr "Syndication" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "…" - msgid "That page number is not an integer" -msgstr "Le numero de pagina non es un numero integre" +msgstr "" msgid "That page number is less than 1" -msgstr "Le numero de pagina es minus de 1" +msgstr "" msgid "That page contains no results" -msgstr "Le pagina non contine resultatos" +msgstr "" msgid "Enter a valid value." msgstr "Specifica un valor valide." @@ -350,15 +327,11 @@ msgstr "Specifica un adresse de e-mail valide." msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Scribe un denotation (\"slug\") valide, consistente de litteras, numeros, " -"tractos de sublineamento o tractos de union." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Scribe un denotation (\"slug\") valide, consistente de litteras Unicode, " -"numeros, tractos de sublineamento o tractos de union." msgid "Enter a valid IPv4 address." msgstr "Specifica un adresse IPv4 valide." @@ -445,11 +418,9 @@ msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -"Le extension de nomine de file “%(extension)s” non es permittite. Le " -"extensiones permittite es: %(allowed_extensions)s." msgid "Null characters are not allowed." -msgstr "Characteres nulle non es permittite." +msgstr "" msgid "and" msgstr "e" @@ -486,11 +457,11 @@ msgstr "Campo de typo: %(field_type)s" #, python-format msgid "“%(value)s” value must be either True or False." -msgstr "Le valor “%(value)s” debe esser o True/Ver o False." +msgstr "" #, python-format msgid "“%(value)s” value must be either True, False, or None." -msgstr "Le valor “%(value)s” debe esser True/Ver, False o None/Necun." +msgstr "" msgid "Boolean (Either True or False)" msgstr "Booleano (ver o false)" @@ -507,16 +478,12 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"Le valor “%(value)s” ha un formato de data invalide. Debe esser in formato " -"AAAA-MM-DD." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -"Le valor “%(value)s” ha le formato correcte (AAAA-MM-DD) ma es un data " -"invalide." msgid "Date (without time)" msgstr "Data (sin hora)" @@ -526,23 +493,19 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"Le valor “%(value)s” es in un formato invalide. Debe esser in formato AAAA-" -"MM-DD HH:MM[:ss[.uuuuuu]][FH]." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -"Le valor “%(value)s” es in le formato correcte (YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][FH]) ma es un data/hora invalide." msgid "Date (with time)" msgstr "Data (con hora)" #, python-format msgid "“%(value)s” value must be a decimal number." -msgstr "Le valor “%(value)s” debe esser un numero decimal." +msgstr "" msgid "Decimal number" msgstr "Numero decimal" @@ -552,8 +515,6 @@ msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -"Le valor “%(value)s” es in un formato invalide. Debe esser in formato [DD] " -"[HH:[MM:]]ss[.uuuuuu]." msgid "Duration" msgstr "Duration" @@ -566,14 +527,14 @@ msgstr "Cammino de file" #, python-format msgid "“%(value)s” value must be a float." -msgstr "Le valor “%(value)s” debe esser un numero a comma flottante." +msgstr "" msgid "Floating point number" msgstr "Numero a comma flottante" #, python-format msgid "“%(value)s” value must be an integer." -msgstr "Le valor “%(value)s” debe esser un numero integre." +msgstr "" msgid "Integer" msgstr "Numero integre" @@ -581,9 +542,6 @@ msgstr "Numero integre" msgid "Big (8 byte) integer" msgstr "Numero integre grande (8 bytes)" -msgid "Small integer" -msgstr "Parve numero integre" - msgid "IPv4 address" msgstr "Adresse IPv4" @@ -592,14 +550,11 @@ msgstr "Adresse IP" #, python-format msgid "“%(value)s” value must be either None, True or False." -msgstr "Le valor “%(value)s” debe esser None/Nulle, True/Ver o False." +msgstr "" msgid "Boolean (Either True, False or None)" msgstr "Booleano (ver, false o nulle)" -msgid "Positive big integer" -msgstr "Grande numero integre positive" - msgid "Positive integer" msgstr "Numero integre positive" @@ -610,6 +565,9 @@ msgstr "Parve numero integre positive" msgid "Slug (up to %(max_length)s)" msgstr "Denotation (longitude maxime: %(max_length)s)" +msgid "Small integer" +msgstr "Parve numero integre" + msgid "Text" msgstr "Texto" @@ -618,16 +576,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"Le valor “%(value)s” es in un formato invalide. Debe esser in formato HH:MM[:" -"ss[.uuuuuu]] ." #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"Le valor “%(value)s” es in le formato correcte (HH:MM[:ss[.uuuuuu]]) ma es " -"un hora invalide." msgid "Time" msgstr "Hora" @@ -640,7 +594,7 @@ msgstr "Datos binari crude" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "“%(value)s” non es un UUID valide." +msgstr "" msgid "Universally unique identifier" msgstr "" @@ -651,12 +605,6 @@ msgstr "File" msgid "Image" msgstr "Imagine" -msgid "A JSON object" -msgstr "" - -msgid "Value must be valid JSON." -msgstr "" - #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." msgstr "Le instantia de %(model)s con %(field)s %(value)r non existe." @@ -751,9 +699,6 @@ msgstr "Specifica un valor complete." msgid "Enter a valid UUID." msgstr "Specifica un UUID valide." -msgid "Enter a valid JSON." -msgstr "" - #. Translators: This is the default suffix added to form field labels msgid ":" msgstr "" @@ -762,23 +707,20 @@ msgstr "" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Campo celate %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "Le datos ManagementForm manca o ha essite manipulate" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Per favor, submitte %d o minus formularios." +msgstr[1] "Per favor, submitte %d o minus formularios." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Per favor, submitte %d o plus formularios." +msgstr[1] "Per favor, submitte %d o plus formularios." msgid "Order" msgstr "Ordine" @@ -842,7 +784,15 @@ msgstr "Si" msgid "No" msgstr "No" -#. Translators: Please do not add spaces around commas. +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + msgid "yes,no,maybe" msgstr "si,no,forsan" @@ -1116,40 +1066,43 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d anno" +msgstr[1] "%d annos" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mense" +msgstr[1] "%d menses" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d septimana" +msgstr[1] "%d septimanas" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d die" +msgstr[1] "%d dies" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d horas" +msgstr[1] "%d horas" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minutas" + +msgid "0 minutes" +msgstr "0 minutas" msgid "Forbidden" msgstr "Prohibite" @@ -1159,7 +1112,7 @@ msgstr "Verification CSRF fallite. Requesta abortate." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1253,7 +1206,7 @@ msgstr "" msgid "Index of %(directory)s" msgstr "Indice de %(directory)s" -msgid "The install worked successfully! Congratulations!" +msgid "Django: the Web framework for perfectionists with deadlines." msgstr "" #, python-format @@ -1262,6 +1215,9 @@ msgid "" "target=\"_blank\" rel=\"noopener\">release notes for Django %(version)s" msgstr "" +msgid "The install worked successfully! Congratulations!" +msgstr "" + #, python-format msgid "" "You are seeing this page because , 2017 # Claude Paroz , 2018 -# Fery Setiawan , 2015-2019,2021 +# Fery Setiawan , 2015-2019 # Jannis Leidel , 2011 # M Asep Indrayana , 2015 # oon arfiandwi , 2016,2020 # rodin , 2011 # rodin , 2013-2016 -# sag᠎e , 2018-2019 +# sage , 2018-2019 # Sutrisno Efendi , 2015,2017 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 12:25+0000\n" "Last-Translator: Transifex Bot <>\n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" @@ -215,9 +215,6 @@ msgstr "Mongolia" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Burma" @@ -332,7 +329,7 @@ msgstr "Sindikasi" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" msgstr "Nomor halaman itu bukan sebuah integer" @@ -763,19 +760,16 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"Data ManagementForm telah hilang atau telah dirusak. Bidang yang hilang: " -"%(field_names)s. Anda mungkin butuh memberkaskan laporan kesalahan jika " -"masalah masih ada." #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Harap ajukan paling banyak %d formulir." +msgstr[0] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Harap ajukan setidaknya %d formulir." +msgstr[0] "" msgid "Order" msgstr "Urutan" @@ -1113,34 +1107,34 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d tahun" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d tahun" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d bulan" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d bulan" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d minggu" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d minggu" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d hari" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d hari" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d jam" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d jam" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d menit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d menit" msgid "Forbidden" msgstr "Terlarang" @@ -1150,14 +1144,14 @@ msgstr "Verifikasi CSRF gagal, Permintaan dibatalkan." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Anda melihat pesan ini karena jaringan HTTPS ini membutuhkan “Referer " -"header” untuk dikirim oleh peramban jaringan anda, tetapi tidak ada yg " -"dikirim. Kepala ini diwajibkan untuk alasan keamanan, untuk memastikan bahwa " -"peramban anda tidak sedang dibajak oleh pihak ketiga." +"Anda sedang melihat pesan ini karena situs HTTPS ini membutuhkan “Referer " +"header” dikirim oleh peramban Web Anda, tetapi tidak terkirim. Bagian kepala " +"tersebut dibutuhkan karena alasan keamanan, untuk memastikan bahwa peramban " +"Anda tidak sedang dibajak oleh pihak ketiga." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/id/formats.py b/venv/Lib/site-packages/django/conf/locale/id/formats.py index 91a2559..b1e08f1 100644 --- a/venv/Lib/site-packages/django/conf/locale/id/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/id/formats.py @@ -2,48 +2,45 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j N Y" +DATE_FORMAT = 'j N Y' DATETIME_FORMAT = "j N Y, G.i" -TIME_FORMAT = "G.i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d-m-Y" -SHORT_DATETIME_FORMAT = "d-m-Y G.i" +TIME_FORMAT = 'G.i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd-m-Y' +SHORT_DATETIME_FORMAT = 'd-m-Y G.i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d-%m-%Y", # '25-10-2009' - "%d/%m/%Y", # '25/10/2009' - "%d-%m-%y", # '25-10-09' - "%d/%m/%y", # '25/10/09' - "%d %b %Y", # '25 Oct 2006', - "%d %B %Y", # '25 October 2006' - "%m/%d/%y", # '10/25/06' - "%m/%d/%Y", # '10/25/2009' + '%d-%m-%Y', '%d/%m/%Y', # '25-10-2009', 25/10/2009' + '%d-%m-%y', '%d/%m/%y', # '25-10-09', 25/10/09' + '%d %b %Y', # '25 Oct 2006', + '%d %B %Y', # '25 October 2006' + '%m/%d/%y', '%m/%d/%Y', # '10/25/06', '10/25/2009' ] TIME_INPUT_FORMATS = [ - "%H.%M.%S", # '14.30.59' - "%H.%M", # '14.30' + '%H.%M.%S', # '14.30.59' + '%H.%M', # '14.30' ] DATETIME_INPUT_FORMATS = [ - "%d-%m-%Y %H.%M.%S", # '25-10-2009 14.30.59' - "%d-%m-%Y %H.%M.%S.%f", # '25-10-2009 14.30.59.000200' - "%d-%m-%Y %H.%M", # '25-10-2009 14.30' - "%d-%m-%y %H.%M.%S", # '25-10-09' 14.30.59' - "%d-%m-%y %H.%M.%S.%f", # '25-10-09' 14.30.59.000200' - "%d-%m-%y %H.%M", # '25-10-09' 14.30' - "%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 %H.%M.%S", # '25/10/2009 14.30.59' - "%m/%d/%Y %H.%M.%S.%f", # '25/10/2009 14.30.59.000200' - "%m/%d/%Y %H.%M", # '25/10/2009 14.30' + '%d-%m-%Y %H.%M.%S', # '25-10-2009 14.30.59' + '%d-%m-%Y %H.%M.%S.%f', # '25-10-2009 14.30.59.000200' + '%d-%m-%Y %H.%M', # '25-10-2009 14.30' + '%d-%m-%y %H.%M.%S', # '25-10-09' 14.30.59' + '%d-%m-%y %H.%M.%S.%f', # '25-10-09' 14.30.59.000200' + '%d-%m-%y %H.%M', # '25-10-09' 14.30' + '%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 %H.%M.%S', # '25/10/2009 14.30.59' + '%m/%d/%Y %H.%M.%S.%f', # '25/10/2009 14.30.59.000200' + '%m/%d/%Y %H.%M', # '25/10/2009 14.30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ig/formats.py b/venv/Lib/site-packages/django/conf/locale/ig/formats.py index cb0b4de..61fc2c0 100644 --- a/venv/Lib/site-packages/django/conf/locale/ig/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ig/formats.py @@ -2,31 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "P" -DATETIME_FORMAT = "j F Y P" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'P' +DATETIME_FORMAT = 'j F Y P' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y', # '25.10.06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d.%m.%y', # '25.10.06' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.mo index 951078f..6d631ac 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.po index be73f3d..27607b8 100644 --- a/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/is/LC_MESSAGES/django.po @@ -7,13 +7,13 @@ # Matt R, 2018 # saevarom , 2011 # saevarom , 2013,2015 -# Thordur Sigurdsson , 2016-2021 +# Thordur Sigurdsson , 2016-2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-14 21:42+0000\n" "Last-Translator: Transifex Bot <>\n" "Language-Team: Icelandic (http://www.transifex.com/django/django/language/" "is/)\n" @@ -212,9 +212,6 @@ msgstr "Mongólska" msgid "Marathi" msgstr "Maratí" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Búrmíska" @@ -326,11 +323,6 @@ msgstr "" msgid "Syndication" msgstr "" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "…" - msgid "That page number is not an integer" msgstr "Þetta síðunúmer er ekki heiltala" @@ -585,9 +577,6 @@ msgstr "Heiltala" msgid "Big (8 byte) integer" msgstr "Stór (8 bæta) heiltala" -msgid "Small integer" -msgstr "Lítil heiltala" - msgid "IPv4 address" msgstr "IPv4 vistfang" @@ -614,6 +603,9 @@ msgstr "Jákvæð lítil heiltala" msgid "Slug (up to %(max_length)s)" msgstr "Slögg (allt að %(max_length)s)" +msgid "Small integer" +msgstr "Lítil heiltala" + msgid "Text" msgstr "Texti" @@ -763,23 +755,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Falinn reitur %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "Gögn fyrir ManagementForm vantar eða hefur verið breytt" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "Vinsamlegast sendu ekki meira en %d form." -msgstr[1] "Vinsamlegast sendu ekki meira en %d form." +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Vinsamlegast sendu %d eða færri form." +msgstr[1] "Vinsamlegast sendu %d eða færri form." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Vinsamlegast sendu að minnsta kosta %d form." -msgstr[1] "Vinsamlegast sendu að minnsta kosta %d form." +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Vinsamlegast sendu %d eða fleiri form." +msgstr[1] "Vinsamlegast sendu %d eða fleiri form." msgid "Order" msgstr "Röð" @@ -1119,40 +1108,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d ár" +msgstr[1] "%d ár" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mánuður" +msgstr[1] "%d mánuðir" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d vika" +msgstr[1] "%d vikur" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dagur" +msgstr[1] "%d dagar" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d klukkustund" +msgstr[1] "%d klukkustundir" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d mínúta" +msgstr[1] "%d mínútur" msgid "Forbidden" msgstr "" @@ -1162,10 +1151,14 @@ msgstr "CSRF auðkenning tókst ekki." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Þú ert að fá þessi skilaboð því þetta HTTPS vefsvæði þarfnast að vafrinn " +"þinn sendi „Referer“ haus (e. referer header) sem var ekki sendur. Þessi " +"haus er nauðsynlegur af öryggisástæðum til að ganga úr skugga um að " +"utanaðkomandi aðili sé ekki að senda fyrirspurnir úr vafranum þínum." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1262,7 +1255,7 @@ msgstr "„%(path)s“ er ekki til" msgid "Index of %(directory)s" msgstr "Innihald %(directory)s " -msgid "The install worked successfully! Congratulations!" +msgid "Django: the Web framework for perfectionists with deadlines." msgstr "" #, python-format @@ -1271,6 +1264,9 @@ msgid "" "target=\"_blank\" rel=\"noopener\">release notes for Django %(version)s" msgstr "" +msgid "The install worked successfully! Congratulations!" +msgstr "" + #, python-format msgid "" "You are seeing this page because , 2011 -# Carlo Miron , 2014 -# Carlo Miron , 2018-2019 -# Davide Targa , 2021 +# Carlo Miron , 2011 +# Carlo Miron , 2014 +# Carlo Miron , 2018-2019 # Denis Darii , 2011 # Flavio Curella , 2013,2016 # Jannis Leidel , 2011 @@ -22,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 16:42+0000\n" +"Last-Translator: palmux \n" "Language-Team: Italian (http://www.transifex.com/django/django/language/" "it/)\n" "MIME-Version: 1.0\n" @@ -222,9 +221,6 @@ msgstr "Mongolo" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Birmano" @@ -1134,40 +1130,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d anno" -msgstr[1] "%(num)d anni" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d anno" +msgstr[1] "%d anni" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mese" -msgstr[1] "%(num)d mesi" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mese" +msgstr[1] "%d mesi" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d settimana" -msgstr[1] "%(num)d settimane" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d settimana" +msgstr[1] "%d settimane" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d giorno" -msgstr[1] "%(num)d giorni" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d giorno" +msgstr[1] "%d giorni" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ora" -msgstr[1] "%(num)d ore" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ora" +msgstr[1] "%d ore" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minuti" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minuti" msgid "Forbidden" msgstr "Proibito" @@ -1177,14 +1173,14 @@ msgstr "Verifica CSRF fallita. Richiesta interrotta." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Vedi questo messaggio perchè questo sito HTTPS richiede l'invio da parte del " -"tuo browser del “Referer header”, che non è invece stato inviato. Questo " -"header è richiesto per motivi di sicurezza, per assicurare che il tuo " -"browser non sia stato sabotato da terzi." +"Stai vedendo questo messaggio perché questo sito HTTPS richiede una " +"\"Referer header\" che deve essere inviata dal tuo browser web, ma non è " +"stato inviato nulla. Questo header è richiesto per ragioni di sicurezza, per " +"assicurare che il tuo browser non sia stato dirottato da terze parti." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/it/formats.py b/venv/Lib/site-packages/django/conf/locale/it/formats.py index bb9e027..8562aef 100644 --- a/venv/Lib/site-packages/django/conf/locale/it/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/it/formats.py @@ -2,42 +2,39 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d F Y" # 25 Ottobre 2006 -TIME_FORMAT = "H:i" # 14:30 -DATETIME_FORMAT = "l d F Y H:i" # Mercoledì 25 Ottobre 2006 14:30 -YEAR_MONTH_FORMAT = "F Y" # Ottobre 2006 -MONTH_DAY_FORMAT = "j F" # 25 Ottobre -SHORT_DATE_FORMAT = "d/m/Y" # 25/12/2009 -SHORT_DATETIME_FORMAT = "d/m/Y H:i" # 25/10/2009 14:30 +DATE_FORMAT = 'd F Y' # 25 Ottobre 2006 +TIME_FORMAT = 'H:i' # 14:30 +DATETIME_FORMAT = 'l d F Y H:i' # Mercoledì 25 Ottobre 2006 14:30 +YEAR_MONTH_FORMAT = 'F Y' # Ottobre 2006 +MONTH_DAY_FORMAT = 'j F' # 25 Ottobre +SHORT_DATE_FORMAT = 'd/m/Y' # 25/12/2009 +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' # 25/10/2009 14:30 FIRST_DAY_OF_WEEK = 1 # Lunedì # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%Y/%m/%d", # '2006/10/25' - "%d-%m-%Y", # '25-10-2006' - "%Y-%m-%d", # '2006-10-25' - "%d-%m-%y", # '25-10-06' - "%d/%m/%y", # '25/10/06' + '%d/%m/%Y', '%Y/%m/%d', # '25/10/2006', '2008/10/25' + '%d-%m-%Y', '%Y-%m-%d', # '25-10-2006', '2008-10-25' + '%d-%m-%y', '%d/%m/%y', # '25-10-06', '25/10/06' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' - "%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' - "%d-%m-%Y %H:%M:%S", # '25-10-2006 14:30:59' - "%d-%m-%Y %H:%M:%S.%f", # '25-10-2006 14:30:59.000200' - "%d-%m-%Y %H:%M", # '25-10-2006 14:30' - "%d-%m-%y %H:%M:%S", # '25-10-06 14:30:59' - "%d-%m-%y %H:%M:%S.%f", # '25-10-06 14:30:59.000200' - "%d-%m-%y %H:%M", # '25-10-06 14:30' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' + '%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' + '%d-%m-%Y %H:%M:%S', # '25-10-2006 14:30:59' + '%d-%m-%Y %H:%M:%S.%f', # '25-10-2006 14:30:59.000200' + '%d-%m-%Y %H:%M', # '25-10-2006 14:30' + '%d-%m-%y %H:%M:%S', # '25-10-06 14:30:59' + '%d-%m-%y %H:%M:%S.%f', # '25-10-06 14:30:59.000200' + '%d-%m-%y %H:%M', # '25-10-06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.mo index 2fd64a8..70d2d5b 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.po index 3663dae..bb4c2c4 100644 --- a/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ja/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # xiu1 , 2016 # tadasu , 2020 -# Goto Hayato , 2021 -# Goto Hayato , 2019 +# GOTO Hayato , 2019 # Jannis Leidel , 2011 # Kentaro Matsuzaki , 2015 # Masashi SHIBATA , 2017 # Nikita K , 2019 # Shinichi Katsumata , 2019 -# Shinya Okano , 2012-2019,2021 +# Shinya Okano , 2012-2019 # Takuro Onoue , 2020 # Takuya N , 2020 # Tetsuya Morimoto , 2011 @@ -18,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 12:25+0000\n" "Last-Translator: Transifex Bot <>\n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" @@ -218,9 +217,6 @@ msgstr "モンゴル語" msgid "Marathi" msgstr "マラーティー語" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "ビルマ語" @@ -335,7 +331,7 @@ msgstr "シンジケーション" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" msgstr "このページ番号は整数ではありません。" @@ -760,19 +756,16 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"ManagementForm のデータが不足しているか改竄されています。不足するフィールドの" -"数: %(field_names)s 。問題が続くようならバグレポートを出す必要があるかもしれ" -"ません。" #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "最多 %d のフォームを送信してください。" +msgstr[0] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "最少 %d のフォームを送信してください。" +msgstr[0] "" msgid "Order" msgstr "並び変え" @@ -1110,34 +1103,34 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)dヶ月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ヶ月" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d週間" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 週間" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d 日" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d時間" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 時間" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d分" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分" msgid "Forbidden" msgstr "アクセス禁止" @@ -1147,14 +1140,14 @@ msgstr "CSRF検証に失敗したため、リクエストは中断されまし msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "このメッセージが表示されている理由は、このHTTPSのサイトはウェブブラウザからリ" "ファラーヘッダが送信されることを必須としていますが、送信されなかったためで" -"す。このヘッダはセキュリティ上の理由(使用中のブラウザが第三者によってハイ" -"ジャックされていないことを確認するため)で必要です。" +"す。このヘッダはセキュリティ上の理由(使用中のブラウザが第三者によってハイ" +"ジャックされていないことを確認するため)で必要です。" msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/ja/formats.py b/venv/Lib/site-packages/django/conf/locale/ja/formats.py index aaf5f98..2f1faa6 100644 --- a/venv/Lib/site-packages/django/conf/locale/ja/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ja/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "Y年n月j日" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "Y年n月j日G:i" -YEAR_MONTH_FORMAT = "Y年n月" -MONTH_DAY_FORMAT = "n月j日" -SHORT_DATE_FORMAT = "Y/m/d" -SHORT_DATETIME_FORMAT = "Y/m/d G:i" +DATE_FORMAT = 'Y年n月j日' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'Y年n月j日G:i' +YEAR_MONTH_FORMAT = 'Y年n月' +MONTH_DAY_FORMAT = 'n月j日' +SHORT_DATE_FORMAT = 'Y/m/d' +SHORT_DATETIME_FORMAT = 'Y/m/d G:i' # FIRST_DAY_OF_WEEK = # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ SHORT_DATETIME_FORMAT = "Y/m/d G:i" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/ka/formats.py b/venv/Lib/site-packages/django/conf/locale/ka/formats.py index 661b71e..86308e3 100644 --- a/venv/Lib/site-packages/django/conf/locale/ka/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ka/formats.py @@ -2,47 +2,41 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "l, j F, Y" -TIME_FORMAT = "h:i a" -DATETIME_FORMAT = "j F, Y h:i a" -YEAR_MONTH_FORMAT = "F, Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j.M.Y" -SHORT_DATETIME_FORMAT = "j.M.Y H:i" +DATE_FORMAT = 'l, j F, Y' +TIME_FORMAT = 'h:i a' +DATETIME_FORMAT = 'j F, Y h:i a' +YEAR_MONTH_FORMAT = 'F, Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j.M.Y' +SHORT_DATETIME_FORMAT = 'j.M.Y H:i' FIRST_DAY_OF_WEEK = 1 # (Monday) # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%m/%d/%Y", # '10/25/2006' - "%m/%d/%y", # '10/25/06' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - # "%d %b %Y", # '25 Oct 2006' - # "%d %b, %Y", # '25 Oct, 2006' - # "%d %b. %Y", # '25 Oct. 2006' - # "%d %B %Y", # '25 October 2006' - # "%d %B, %Y", # '25 October, 2006' + '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' + '%d.%m.%Y', '%d.%m.%y', # '25.10.2006', '25.10.06' + # '%d %b %Y', '%d %b, %Y', '%d %b. %Y', # '25 Oct 2006', '25 Oct, 2006', '25 Oct. 2006' + # '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006' ] 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' - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%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 %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' + '%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' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%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 %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' ] -DECIMAL_SEPARATOR = "." +DECIMAL_SEPARATOR = '.' THOUSAND_SEPARATOR = " " NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/km/formats.py b/venv/Lib/site-packages/django/conf/locale/km/formats.py index 5923437..b704e9c 100644 --- a/venv/Lib/site-packages/django/conf/locale/km/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/km/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j ខែ F ឆ្នាំ Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j ខែ F ឆ្នាំ Y, G:i" +DATE_FORMAT = 'j ខែ F ឆ្នាំ Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j ខែ F ឆ្នាំ Y, G:i' # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" -SHORT_DATETIME_FORMAT = "j M Y, G:i" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' +SHORT_DATETIME_FORMAT = 'j M Y, G:i' # FIRST_DAY_OF_WEEK = # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ SHORT_DATETIME_FORMAT = "j M Y, G:i" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/kn/formats.py b/venv/Lib/site-packages/django/conf/locale/kn/formats.py index d212fd5..5003c64 100644 --- a/venv/Lib/site-packages/django/conf/locale/kn/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/kn/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "h:i A" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'h:i A' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = diff --git a/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.mo index a3b88a5..28903f3 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.po index 3b687c5..fe8727c 100644 --- a/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ko/LC_MESSAGES/django.po @@ -19,7 +19,6 @@ # JunGu Kang , 2015 # JunGu Kang , 2019 # Kagami Sascha Rosylight , 2017 -# Mariusz Felisiak , 2021 # Seho Noh , 2018 # Subin Choi , 2016 # Taesik Yoon , 2015 @@ -27,9 +26,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:30+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-09-05 12:57+0000\n" +"Last-Translator: DONGHO JEONG \n" "Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -226,9 +225,6 @@ msgstr "몽고어" msgid "Marathi" msgstr "마라티어" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "룩셈부르크어" @@ -340,11 +336,6 @@ msgstr "정적 파일" msgid "Syndication" msgstr "신디케이션" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "" - msgid "That page number is not an integer" msgstr "페이지 번호가 정수가 아닙니다." @@ -585,9 +576,6 @@ msgstr "정수" msgid "Big (8 byte) integer" msgstr "큰 정수 (8 byte)" -msgid "Small integer" -msgstr "작은 정수" - msgid "IPv4 address" msgstr "IPv4 주소" @@ -614,6 +602,9 @@ msgstr "작은 양의 정수" msgid "Slug (up to %(max_length)s)" msgstr "슬러그(%(max_length)s 까지)" +msgid "Small integer" +msgstr "작은 정수" + msgid "Text" msgstr "텍스트" @@ -760,21 +751,18 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(%(name)s hidden 필드) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "관리폼 데이터가 없거나 변조되었습니다." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "%d 개 이하의 양식을 제출하세요." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "%d 개 이상의 양식을 제출하세요." msgid "Order" msgstr "순서:" @@ -1100,7 +1088,7 @@ msgstr "올바른 IPv6 주소가 아닙니다." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "또는" @@ -1110,34 +1098,34 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d년" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d개월" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d주" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d일" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d시간" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d분" msgid "Forbidden" msgstr "Forbidden" @@ -1147,10 +1135,14 @@ msgstr "CSRF 검증에 실패했습니다. 요청을 중단하였습니다." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"이 메세지가 보이는 이유는 이 HTTPS 사이트가 당신의 웹 브라우저로부터 '참조 헤" +"더'를 요구하지만, 아무것도 받기 못하였기 때문입니다. 이 헤더는 보안상의 문제" +"로 필요하며, 제3자에 의해 당신의 웹 브라우저가 해킹당하고 있지 않다는 것을 보" +"장합니다." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1249,8 +1241,8 @@ msgstr "\"%(path)s\" 이/가 존재하지 않습니다." msgid "Index of %(directory)s" msgstr "Index of %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "성공적으로 설치되었습니다! 축하합니다!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "Django: 마감에 쫓기는 완벽주의자를 위한 웹 프레임워크" #, python-format msgid "" @@ -1260,6 +1252,9 @@ msgstr "" "Django %(version)s릴리스 노트 보기" +msgid "The install worked successfully! Congratulations!" +msgstr "성공적으로 설치되었습니다! 축하합니다!" + #, python-format msgid "" "You are seeing this page because , 2021 # Soyuzbek Orozbek uulu , 2020-2021 # Soyuzbek Orozbek uulu , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-27 14:11+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-02 11:54+0000\n" "Last-Translator: Soyuzbek Orozbek uulu \n" "Language-Team: Kyrgyz (http://www.transifex.com/django/django/language/ky/)\n" "MIME-Version: 1.0\n" @@ -207,9 +206,6 @@ msgstr "Монголчо" msgid "Marathi" msgstr "Марати" -msgid "Malay" -msgstr "Малай" - msgid "Burmese" msgstr "Бурмача" @@ -1083,7 +1079,7 @@ msgstr "Бул туура эмес IPv6 дареги" #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "же" @@ -1093,34 +1089,34 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d жыл" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%dжыл" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ай" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%dай" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d апта" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%dжума" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d күн" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%dкүн" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d саат" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%dсаат" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d мүнөт" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%dмүнөт" msgid "Forbidden" msgstr "Тыйылган" @@ -1130,13 +1126,14 @@ msgstr "CSRF текшерүү кыйрады. Суроо четке кагылд msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Браузер тараптан \"Referer header\" HTTPS сайтына жиберилбей калгандыгы үчүн " -"бул билдирүүнү көрүп турасыз. Бул хэдэр сиздин браузер үчүнчү жактан " -"чабуулга учурабаганын текшерүүгө коопсуздук үчүн керек." +"Сиз бул билдирүүнү HTTPS сайты, браузер тарабынан жөнөтүлчү “Referer header” " +"тарабынан талап кылынганы бирок жөнөтүлбөгөндүгү үчүн көрүп атасыз. Бул " +"хэдер коопсуздук чаралары үчүн керек болот. Сиздин броузер үчүнчү тараптан " +"барымтага алынбаганын текшериңиз." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/ky/formats.py b/venv/Lib/site-packages/django/conf/locale/ky/formats.py index 25a0928..1dc42c4 100644 --- a/venv/Lib/site-packages/django/conf/locale/ky/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ky/formats.py @@ -2,31 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j E Y ж." -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j E Y ж. G:i" -YEAR_MONTH_FORMAT = "F Y ж." -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j E Y ж.' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j E Y ж. G:i' +YEAR_MONTH_FORMAT = 'F Y ж.' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Дүйшөмбү, Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y', # '25.10.06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d.%m.%y', # '25.10.06' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.mo index ee14fec..aa88229 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.po index 0c055a2..66e31a9 100644 --- a/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/lt/LC_MESSAGES/django.po @@ -4,7 +4,6 @@ # Jannis Leidel , 2011 # Kostas , 2011 # lauris , 2011 -# Mariusz Felisiak , 2021 # Matas Dailyda , 2015-2019 # naktinis , 2012 # Nikolajus Krauklis , 2013 @@ -15,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:28+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: Lithuanian (http://www.transifex.com/django/django/language/" "lt/)\n" "MIME-Version: 1.0\n" @@ -34,9 +33,6 @@ msgstr "Afrikiečių" msgid "Arabic" msgstr "Arabų" -msgid "Algerian Arabic" -msgstr "" - msgid "Asturian" msgstr "Austrų" @@ -160,9 +156,6 @@ msgstr "Interlingua" msgid "Indonesian" msgstr "Indoneziečių" -msgid "Igbo" -msgstr "" - msgid "Ido" msgstr "Ido" @@ -193,9 +186,6 @@ msgstr "Dravidų" msgid "Korean" msgstr "Korėjiečių" -msgid "Kyrgyz" -msgstr "" - msgid "Luxembourgish" msgstr "Liuksemburgų" @@ -217,9 +207,6 @@ msgstr "Mongolų" msgid "Marathi" msgstr "Marati" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Mjanmų" @@ -283,15 +270,9 @@ msgstr "Tamilų" msgid "Telugu" msgstr "Telugų" -msgid "Tajik" -msgstr "" - msgid "Thai" msgstr "Tailando" -msgid "Turkmen" -msgstr "" - msgid "Turkish" msgstr "Turkų" @@ -331,11 +312,6 @@ msgstr "Statiniai failai" msgid "Syndication" msgstr "Sindikacija" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "" - msgid "That page number is not an integer" msgstr "To puslapio numeris nėra sveikasis skaičius." @@ -594,9 +570,6 @@ msgstr "Sveikas skaičius" msgid "Big (8 byte) integer" msgstr "Didelis (8 baitų) sveikas skaičius" -msgid "Small integer" -msgstr "Nedidelis sveikasis skaičius" - msgid "IPv4 address" msgstr "IPv4 adresas" @@ -610,9 +583,6 @@ msgstr "" msgid "Boolean (Either True, False or None)" msgstr "Loginė reikšmė (Tiesa, Netiesa arba Nieko)" -msgid "Positive big integer" -msgstr "" - msgid "Positive integer" msgstr "Teigiamas sveikasis skaičius" @@ -623,6 +593,9 @@ msgstr "Nedidelis teigiamas sveikasis skaičius" msgid "Slug (up to %(max_length)s)" msgstr "Unikalus adresas (iki %(max_length)s ženklų)" +msgid "Small integer" +msgstr "Nedidelis sveikasis skaičius" + msgid "Text" msgstr "Tekstas" @@ -660,12 +633,6 @@ msgstr "Failas" msgid "Image" msgstr "Paveiksliukas" -msgid "A JSON object" -msgstr "" - -msgid "Value must be valid JSON." -msgstr "" - #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." msgstr "%(model)s objektas su %(field)s %(value)r neegzistuoja." @@ -764,9 +731,6 @@ msgstr "Įveskite pilną reikšmę." msgid "Enter a valid UUID." msgstr "Įveskite tinkamą UUID." -msgid "Enter a valid JSON." -msgstr "" - #. Translators: This is the default suffix added to form field labels msgid ":" msgstr ":" @@ -775,27 +739,24 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Paslėptas laukelis %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "ManagementForm duomenys buvo sugadinti arba neegzistuoja" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Prašome pateikti %d arba mažiau formų." +msgstr[1] "Prašome pateikti %d arba mažiau formų." +msgstr[2] "Prašome pateikti %d arba mažiau formų." +msgstr[3] "Prašome pateikti %d arba mažiau formų." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Prašome pateikti %d arba daugiau formų." +msgstr[1] "Prašome pateikti %d arba daugiau formų." +msgstr[2] "Prašome pateikti %d arba daugiau formų." +msgstr[3] "Prašome pateikti %d arba daugiau formų." msgid "Order" msgstr "Nurodyti" @@ -858,7 +819,15 @@ msgstr "Taip" msgid "No" msgstr "Ne" -#. Translators: Please do not add spaces around commas. +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + msgid "yes,no,maybe" msgstr "taip,ne,galbūt" @@ -1124,7 +1093,7 @@ msgstr "Tai nėra teisingas IPv6 adresas." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "arba" @@ -1134,52 +1103,55 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d metas" +msgstr[1] "%d metai" +msgstr[2] "%d metų" +msgstr[3] "%d metų" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mėnuo" +msgstr[1] "%d mėnesiai" +msgstr[2] "%d mėnesių" +msgstr[3] "%d mėnesių" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d savaitė" +msgstr[1] "%d savaitės" +msgstr[2] "%d savaičių" +msgstr[3] "%d savaičių" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d diena" +msgstr[1] "%d dienos" +msgstr[2] "%d dienų" +msgstr[3] "%d dienų" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d valanda" +msgstr[1] "%d valandos" +msgstr[2] "%d valandų" +msgstr[3] "%d valandų" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutė" +msgstr[1] "%d minutės" +msgstr[2] "%d minučių" +msgstr[3] "%d minučių" + +msgid "0 minutes" +msgstr "0 minučių" msgid "Forbidden" msgstr "Uždrausta" @@ -1189,7 +1161,7 @@ msgstr "Nepavyko CSRF patvirtinimas. Užklausa nutraukta." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1282,8 +1254,8 @@ msgstr "" msgid "Index of %(directory)s" msgstr "%(directory)s indeksas" -msgid "The install worked successfully! Congratulations!" -msgstr "Diegimas pavyko! Sveikiname!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "Django: Žiniatinklio karkasas perfekcionistams su terminais." #, python-format msgid "" @@ -1294,6 +1266,9 @@ msgstr "" "%(version)s/releases/\" target=\"_blank\" rel=\"noopener\">išleidimo " "pastabas" +msgid "The install worked successfully! Congratulations!" +msgstr "Diegimas pavyko! Sveikiname!" + #, python-format msgid "" "You are seeing this page because , 2011 # krikulis , 2014 # Māris Nartišs , 2016 -# Mariusz Felisiak , 2021 # Mārtiņš Šulcs , 2018 # NullIsNot0 , 2018-2021 # peterisb , 2016-2017 @@ -15,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:30+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-25 23:29+0000\n" +"Last-Translator: NullIsNot0 \n" "Language-Team: Latvian (http://www.transifex.com/django/django/language/" "lv/)\n" "MIME-Version: 1.0\n" @@ -154,7 +153,7 @@ msgid "Armenian" msgstr "Armēņu" msgid "Interlingua" -msgstr "modernā latīņu" +msgstr "modernā latīņu valoda" msgid "Indonesian" msgstr "indonēziešu" @@ -172,10 +171,10 @@ msgid "Italian" msgstr "itāļu" msgid "Japanese" -msgstr "japāņu" +msgstr "Japāņu" msgid "Georgian" -msgstr "gruzīnu" +msgstr "vācu" msgid "Kabyle" msgstr "kabiliešu" @@ -216,9 +215,6 @@ msgstr "mongoļu" msgid "Marathi" msgstr "maratiešu" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "birmiešu" @@ -360,15 +356,15 @@ msgstr "Ievadiet korektu e-pasta adresi" msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Ievadiet korektu \"identifikatora\" vērtību, kas satur tikai burtus, " +"Ievadiet korektu \"vienkāršotā teksta\" vērtību, kas satur tikai burtus, " "ciparus, apakšsvītras vai defises." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Ievadiet korektu \"identifikatora\" vērtību, kas satur tikai Unikoda burtus, " -"ciparus, apakšsvītras vai defises." +"Ievadiet korektu \"vienkāršotā teksta\" vērtību, kas satur tikai Unikoda " +"burtus, ciparus, apakšsvītras vai defises." msgid "Enter a valid IPv4 address." msgstr "Ievadiet korektu IPv4 adresi." @@ -1122,7 +1118,7 @@ msgstr "Šī nav derīga IPv6 adrese." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "vai" @@ -1132,46 +1128,46 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d gadi" -msgstr[1] "%(num)d gads" -msgstr[2] "%(num)d gadi" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d gadi" +msgstr[1] "%d gads" +msgstr[2] "%d gadi" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mēneši" -msgstr[1] "%(num)d mēnesis" -msgstr[2] "%(num)d mēneši" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mēneši" +msgstr[1] "%d mēnesis" +msgstr[2] "%d mēneši" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nedēļas" -msgstr[1] "%(num)d nedēļa" -msgstr[2] "%(num)d nedēļas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nedēļas" +msgstr[1] "%d nedēļa" +msgstr[2] "%d nedēļas" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dienas" -msgstr[1] "%(num)d diena" -msgstr[2] "%(num)d dienas" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dienas" +msgstr[1] "%d diena" +msgstr[2] "%d dienas" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d stundas" -msgstr[1] "%(num)d stunda" -msgstr[2] "%(num)d stubdas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d stundas" +msgstr[1] "%d stunda" +msgstr[2] "%d stundas" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minūtes" -msgstr[1] "%(num)d minūte" -msgstr[2] "%(num)d minūtes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minūtes" +msgstr[1] "%d minūte" +msgstr[2] "%d minūtes" msgid "Forbidden" msgstr "Aizliegts" @@ -1181,14 +1177,14 @@ msgstr "CSRF pārbaude neizdevās. Pieprasījums pārtrauks." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Jūs redzat šo paziņojumu, jo jūsu pārlūkprogrammai ir jānosūta “Referer " -"header” šai HTTPS vietnei, taču tā netika nosūtīta. Šī galvene ir " -"nepieciešama drošības apsvērumu dēļ, lai pārliecinātos, ka jūsu " -"pārlūkprogrammas komunikācijas datus nepārtver trešās puses." +"Jūs redzat šo ziņojumu, jo šai HTTPS vietnei nepieciešams “Referer header”, " +"kuru bija paredzēts, ka nosūtīs jūsu tīmekļa pārlūkprogramma, bet tas netika " +"nosūtīts. Šis headeris ir vajadzīgs drošības apsvērumu dēļ, lai " +"pārliecinātos, ka trešās puses nepārņems kontroli pār jūsu pārlūkprogrammu." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/lv/formats.py b/venv/Lib/site-packages/django/conf/locale/lv/formats.py index bb34444..bc5f3b2 100644 --- a/venv/Lib/site-packages/django/conf/locale/lv/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/lv/formats.py @@ -2,45 +2,43 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"Y. \g\a\d\a j. F" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"Y. \g\a\d\a j. F, H:i" -YEAR_MONTH_FORMAT = r"Y. \g. F" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = r"j.m.Y" -SHORT_DATETIME_FORMAT = "j.m.Y H:i" +DATE_FORMAT = r'Y. \g\a\d\a j. F' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'Y. \g\a\d\a j. F, H:i' +YEAR_MONTH_FORMAT = r'Y. \g. F' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = r'j.m.Y' +SHORT_DATETIME_FORMAT = 'j.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' + '%Y-%m-%d', '%d.%m.%Y', '%d.%m.%y', # '2006-10-25', '25.10.2006', '25.10.06' ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '14:30:59' - "%H:%M:%S.%f", # '14:30:59.000200' - "%H:%M", # '14:30' - "%H.%M.%S", # '14.30.59' - "%H.%M.%S.%f", # '14.30.59.000200' - "%H.%M", # '14.30' + '%H:%M:%S', # '14:30:59' + '%H:%M:%S.%f', # '14:30:59.000200' + '%H:%M', # '14:30' + '%H.%M.%S', # '14.30.59' + '%H.%M.%S.%f', # '14.30.59.000200' + '%H.%M', # '14.30' ] 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' - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d.%m.%y %H.%M.%S", # '25.10.06 14.30.59' - "%d.%m.%y %H.%M.%S.%f", # '25.10.06 14.30.59.000200' - "%d.%m.%y %H.%M", # '25.10.06 14.30' + '%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' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d.%m.%y %H.%M.%S', # '25.10.06 14.30.59' + '%d.%m.%y %H.%M.%S.%f', # '25.10.06 14.30.59.000200' + '%d.%m.%y %H.%M', # '25.10.06 14.30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = " " # Non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = ' ' # Non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/mk/formats.py b/venv/Lib/site-packages/django/conf/locale/mk/formats.py index fbb577f..18d4782 100644 --- a/venv/Lib/site-packages/django/conf/locale/mk/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/mk/formats.py @@ -2,39 +2,37 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "j.m.Y" -SHORT_DATETIME_FORMAT = "j.m.Y H:i" +DATE_FORMAT = 'd F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'j.m.Y' +SHORT_DATETIME_FORMAT = 'j.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - "%d. %m. %Y", # '25. 10. 2006' - "%d. %m. %y", # '25. 10. 06' + '%d.%m.%Y', '%d.%m.%y', # '25.10.2006', '25.10.06' + '%d. %m. %Y', '%d. %m. %y', # '25. 10. 2006', '25. 10. 06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d. %m. %Y %H:%M:%S", # '25. 10. 2006 14:30:59' - "%d. %m. %Y %H:%M:%S.%f", # '25. 10. 2006 14:30:59.000200' - "%d. %m. %Y %H:%M", # '25. 10. 2006 14:30' - "%d. %m. %y %H:%M:%S", # '25. 10. 06 14:30:59' - "%d. %m. %y %H:%M:%S.%f", # '25. 10. 06 14:30:59.000200' - "%d. %m. %y %H:%M", # '25. 10. 06 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d. %m. %Y %H:%M:%S', # '25. 10. 2006 14:30:59' + '%d. %m. %Y %H:%M:%S.%f', # '25. 10. 2006 14:30:59.000200' + '%d. %m. %Y %H:%M', # '25. 10. 2006 14:30' + '%d. %m. %y %H:%M:%S', # '25. 10. 06 14:30:59' + '%d. %m. %y %H:%M:%S.%f', # '25. 10. 06 14:30:59.000200' + '%d. %m. %y %H:%M', # '25. 10. 06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.mo index 17d15c6..bf62b26 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.po index 7e1945d..4078bd1 100644 --- a/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ml/LC_MESSAGES/django.po @@ -8,16 +8,15 @@ # Jaseem KM , 2019 # Jeffy , 2012 # Jibin Mathew , 2019 -# Mariusz Felisiak , 2021 # Rag sagar , 2016 # Rajeesh Nair , 2011-2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:29+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-14 21:42+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Malayalam (http://www.transifex.com/django/django/language/" "ml/)\n" "MIME-Version: 1.0\n" @@ -215,9 +214,6 @@ msgstr "മംഗോളിയന്‍" msgid "Marathi" msgstr "മറാത്തി" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "ബര്‍മീസ്" @@ -329,11 +325,6 @@ msgstr " സ്റ്റാറ്റിൿ ഫയലുകൾ" msgid "Syndication" msgstr "വിതരണം " -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "" - msgid "That page number is not an integer" msgstr "ആ പേജ് നമ്പർ ഒരു ഇന്റിജറല്ല" @@ -576,9 +567,6 @@ msgstr "പൂര്‍ണ്ണസംഖ്യ" msgid "Big (8 byte) integer" msgstr "8 ബൈറ്റ് പൂര്‍ണസംഖ്യ." -msgid "Small integer" -msgstr "ഹ്രസ്വ പൂര്‍ണസംഖ്യ" - msgid "IPv4 address" msgstr "IPv4 വിലാസം" @@ -605,6 +593,9 @@ msgstr "ധന ഹ്രസ്വ പൂര്‍ണസംഖ്യ" msgid "Slug (up to %(max_length)s)" msgstr "സ്ലഗ് (%(max_length)s വരെ)" +msgid "Small integer" +msgstr "ഹ്രസ്വ പൂര്‍ണസംഖ്യ" + msgid "Text" msgstr "ടെക്സ്റ്റ്" @@ -751,23 +742,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(ഹിഡൻ ഫീൽഡ് %(name)s)%(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "ManagementForm ടാറ്റ കാണ്മാനില്ല അല്ലെങ്കിൽ തിരിമറി നടത്തപ്പെട്ടു ." #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "ദയവായി%d അല്ലെങ്കിൽ കുറവ് ഫോമുകൾ സമർപ്പിക്കുക." +msgstr[1] "ദയവായി%d അല്ലെങ്കിൽ കുറവ് ഫോമുകൾ സമർപ്പിക്കുക." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "ദയവായി %d അല്ലെങ്കിൽ കൂടുതൽ ഫോമുകൾ സമർപ്പിക്കുക. " +msgstr[1] "ദയവായി%d അല്ലെങ്കിൽ കൂടുതൽ ഫോമുകൾ സമർപ്പിക്കുക. " msgid "Order" msgstr "ക്രമം" @@ -1092,7 +1080,7 @@ msgstr "ഇതു സാധുവായ IPv6 വിലാസമല്ല." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "അഥവാ" @@ -1102,40 +1090,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d വർഷം" +msgstr[1] "%d വർഷങ്ങൾ " #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d മാസം" +msgstr[1] "%d മാസങ്ങൾ" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d ആഴ്ച" +msgstr[1] "%d ആഴ്ചകൾ" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d ദിവസം" +msgstr[1] "%d ദിവസങ്ങൾ" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d മണിക്കൂർ" +msgstr[1] "%d മണിക്കൂറുകൾ" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d മിനിറ്റ്" +msgstr[1] "%d മിനിറ്റുകൾ" msgid "Forbidden" msgstr "വിലക്കപ്പെട്ടത്" @@ -1145,7 +1133,7 @@ msgstr "സി എസ് ആർ എഫ് പരിശോധന പരാജയ msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1238,8 +1226,8 @@ msgstr "" msgid "Index of %(directory)s" msgstr "%(directory)s യുടെ സൂചിക" -msgid "The install worked successfully! Congratulations!" -msgstr "ഇൻസ്ടാൾ ഭംഗിയായി നടന്നു! അഭിനന്ദനങ്ങൾ !" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "ജാംഗോ: സമയപരിമിതികളുള്ള പൂർണ്ണതാമോഹികൾക്കായുള്ള വെബ് ഫ്രെയിംവർക്ക്. " #, python-format msgid "" @@ -1247,6 +1235,9 @@ msgid "" "target=\"_blank\" rel=\"noopener\">release notes for Django %(version)s" msgstr "" +msgid "The install worked successfully! Congratulations!" +msgstr "ഇൻസ്ടാൾ ഭംഗിയായി നടന്നു! അഭിനന്ദനങ്ങൾ !" + #, python-format msgid "" "You are seeing this page because , 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-06 07:43+0000\n" -"Last-Translator: Mariusz Felisiak \n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Afrikaans" -msgstr "Bahasa Afrikaans" - -msgid "Arabic" -msgstr "Bahasa Arab" - -msgid "Algerian Arabic" -msgstr "Bahasa Arab Algeria" - -msgid "Asturian" -msgstr "Bahasa Asturia" - -msgid "Azerbaijani" -msgstr "Bahasa Azerbaijan" - -msgid "Bulgarian" -msgstr "Bahasa Bulgaria" - -msgid "Belarusian" -msgstr "Bahasa Belarus" - -msgid "Bengali" -msgstr "Bahasa Benggali" - -msgid "Breton" -msgstr "Bahasa Breton" - -msgid "Bosnian" -msgstr "Bahasa Bosnia" - -msgid "Catalan" -msgstr "Bahasa Catalonia" - -msgid "Czech" -msgstr "Bahasa Czech" - -msgid "Welsh" -msgstr "Bahasa Wales" - -msgid "Danish" -msgstr "Bahasa Denmark" - -msgid "German" -msgstr "Bahasa Jerman" - -msgid "Lower Sorbian" -msgstr "Bahasa Sorbian Rendah" - -msgid "Greek" -msgstr "Bahasa Yunani" - -msgid "English" -msgstr "Bahasa Inggeris" - -msgid "Australian English" -msgstr "Bahasa Inggeris Australia" - -msgid "British English" -msgstr "Bahasa Inggeris British" - -msgid "Esperanto" -msgstr "Bahasa Esperanto" - -msgid "Spanish" -msgstr "Bahasa Sepanyol" - -msgid "Argentinian Spanish" -msgstr "Bahasa Sepanyol Argentina" - -msgid "Colombian Spanish" -msgstr "Bahasa Sepanyol Kolumbia" - -msgid "Mexican Spanish" -msgstr "Bahasa Sepanyol Mexico" - -msgid "Nicaraguan Spanish" -msgstr "Bahasa Sepanyol Nicaragua" - -msgid "Venezuelan Spanish" -msgstr "Bahasa Sepanyol Venezuela" - -msgid "Estonian" -msgstr "Bahasa Estonia" - -msgid "Basque" -msgstr "Bahasa Bask" - -msgid "Persian" -msgstr "Bahasa Farsi" - -msgid "Finnish" -msgstr "Bahassa Finland" - -msgid "French" -msgstr "Bahasa Perancis" - -msgid "Frisian" -msgstr "Bahasa Frisia" - -msgid "Irish" -msgstr "Bahasa Ireland" - -msgid "Scottish Gaelic" -msgstr "Bahasa Gael Scotland" - -msgid "Galician" -msgstr "Bahasa Galisia" - -msgid "Hebrew" -msgstr "Bahasa Ibrani" - -msgid "Hindi" -msgstr "Bahasa Hindi" - -msgid "Croatian" -msgstr "Bahasa Kroatia" - -msgid "Upper Sorbian" -msgstr "Bahasa Sorbia Atasan" - -msgid "Hungarian" -msgstr "Bahasa Hungary" - -msgid "Armenian" -msgstr "Bahasa Armenia" - -msgid "Interlingua" -msgstr "Bahasa Interlingua" - -msgid "Indonesian" -msgstr "Bahasa Indonesia" - -msgid "Igbo" -msgstr "Bahasa Igbo" - -msgid "Ido" -msgstr "Bahasa Ido" - -msgid "Icelandic" -msgstr "Bahasa Iceland" - -msgid "Italian" -msgstr "Bahasa Itali" - -msgid "Japanese" -msgstr "Bahasa Jepun" - -msgid "Georgian" -msgstr "Bahasa Georgia" - -msgid "Kabyle" -msgstr "Bahasa Kabylia" - -msgid "Kazakh" -msgstr "Bahasa Kazakhstan" - -msgid "Khmer" -msgstr "Bahasa Kambodia" - -msgid "Kannada" -msgstr "Bahasa Kannada" - -msgid "Korean" -msgstr "Bahasa Korea" - -msgid "Kyrgyz" -msgstr "Bahasa Kyrgyzstan" - -msgid "Luxembourgish" -msgstr "Bahasa Luxemborg" - -msgid "Lithuanian" -msgstr "Bahasa Lithuania" - -msgid "Latvian" -msgstr "Bahasa Latvia" - -msgid "Macedonian" -msgstr "Bahasa Masedonia" - -msgid "Malayalam" -msgstr "Malayalam" - -msgid "Mongolian" -msgstr "Bahasa Mongol" - -msgid "Marathi" -msgstr "Marathi" - -msgid "Malay" -msgstr "Bahasa Melayu" - -msgid "Burmese" -msgstr "Bahasa Burma" - -msgid "Norwegian Bokmål" -msgstr "Bahasa Bokmal Norway" - -msgid "Nepali" -msgstr "Bahasa Nepal" - -msgid "Dutch" -msgstr "Belanda" - -msgid "Norwegian Nynorsk" -msgstr "Bahasa Nynorsk Norway" - -msgid "Ossetic" -msgstr "Bahasa Ossetic" - -msgid "Punjabi" -msgstr "Bahasa Punjab" - -msgid "Polish" -msgstr "Bahasa Poland" - -msgid "Portuguese" -msgstr "Bahasa Portugal" - -msgid "Brazilian Portuguese" -msgstr "Bahasa Portugal Brazil" - -msgid "Romanian" -msgstr "Bahasa Romania" - -msgid "Russian" -msgstr "Bahasa Rusia" - -msgid "Slovak" -msgstr "Bahasa Slovakia" - -msgid "Slovenian" -msgstr "Bahasa Slovenia" - -msgid "Albanian" -msgstr "Bahasa Albania" - -msgid "Serbian" -msgstr "Bahasa Serbia" - -msgid "Serbian Latin" -msgstr "Bahasa Latin Serbia" - -msgid "Swedish" -msgstr "Bahasa Sweden" - -msgid "Swahili" -msgstr "Bahasa Swahili" - -msgid "Tamil" -msgstr "Bahasa Tamil" - -msgid "Telugu" -msgstr "Bahasa Telugu" - -msgid "Tajik" -msgstr "Bahasa Tajik" - -msgid "Thai" -msgstr "Bahasa Siam" - -msgid "Turkmen" -msgstr "Bahasa Turkmenistan" - -msgid "Turkish" -msgstr "Bahasa Turki" - -msgid "Tatar" -msgstr "Bahasa Tatar" - -msgid "Udmurt" -msgstr "Bahasa Udmurt" - -msgid "Ukrainian" -msgstr "Bahasa Ukraine" - -msgid "Urdu" -msgstr "Bahasa Urdu" - -msgid "Uzbek" -msgstr "Bahasa Uzbekistan" - -msgid "Vietnamese" -msgstr "Bahasa Vietnam" - -msgid "Simplified Chinese" -msgstr "Bahasa Cina (Dipermudahkan)" - -msgid "Traditional Chinese" -msgstr "Bahasa Cina Tradisional" - -msgid "Messages" -msgstr "Mesej" - -msgid "Site Maps" -msgstr "Peta Laman" - -msgid "Static Files" -msgstr "Fail Statik" - -msgid "Syndication" -msgstr "Sindikasi" - -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "..." - -msgid "That page number is not an integer" -msgstr "Nombor ruangan itu bukanlah integer" - -msgid "That page number is less than 1" -msgstr "Nombor ruangan itu kurang daripada 1" - -msgid "That page contains no results" -msgstr "Ruangan itu tiada keputusan" - -msgid "Enter a valid value." -msgstr "Masukkan nilai yang sah." - -msgid "Enter a valid URL." -msgstr "Masukkan URL yang sah." - -msgid "Enter a valid integer." -msgstr "Masukkan integer yang sah." - -msgid "Enter a valid email address." -msgstr "Masukkan alamat emel yang sah." - -#. Translators: "letters" means latin letters: a-z and A-Z. -msgid "" -"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." -msgstr "" -"Masukkan \"slug\" yang sah yang mengandungi huruf, nombor, garisan atau " -"tanda sempang." - -msgid "" -"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " -"hyphens." -msgstr "" -"Masukkan \"slug\" yang sah yang mengandungi huruf Unicode, nombor, garisan, " -"atau tanda sempang." - -msgid "Enter a valid IPv4 address." -msgstr "Masukkan alamat IPv4 yang sah." - -msgid "Enter a valid IPv6 address." -msgstr "Masukkan alamat IPv6 yang sah." - -msgid "Enter a valid IPv4 or IPv6 address." -msgstr "Masukkan alamat IPv4 atau IPv6 yang sah." - -msgid "Enter only digits separated by commas." -msgstr "Hanya masukkan digit yang dipisahkan oleh koma." - -#, python-format -msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." -msgstr "Pastikan nilai ini adalah %(limit_value)s (ia adalah %(show_value)s)." - -#, python-format -msgid "Ensure this value is less than or equal to %(limit_value)s." -msgstr "Pastikan nilai ini kurang daripada atau sama dengan %(limit_value)s." - -#, python-format -msgid "Ensure this value is greater than or equal to %(limit_value)s." -msgstr "Pastikan nilai ini lebih daripada atau sama dengan %(limit_value)s." - -#, python-format -msgid "" -"Ensure this value has at least %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at least %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -"Pastikan nilai ini mempunyai sekurang-kurangnya %(limit_value)d karater (ia " -"mempunyai %(show_value)d)." - -#, python-format -msgid "" -"Ensure this value has at most %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at most %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -"Pastikan nilai ini mempunyai sepalingnya %(limit_value)d karakter (ia " -"mempunyai %(show_value)d)." - -msgid "Enter a number." -msgstr "Masukkan nombor." - -#, python-format -msgid "Ensure that there are no more than %(max)s digit in total." -msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "Pastikan jumlah tidak melebihi %(max)s digit." - -#, python-format -msgid "Ensure that there are no more than %(max)s decimal place." -msgid_plural "Ensure that there are no more than %(max)s decimal places." -msgstr[0] "Pastikan titik perpuluhan tidak melebihi %(max)s." - -#, python-format -msgid "" -"Ensure that there are no more than %(max)s digit before the decimal point." -msgid_plural "" -"Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "" -"Pastikan jumlah digit tidak melebihi %(max)s sebelum titik perpuluhan." - -#, python-format -msgid "" -"File extension “%(extension)s” is not allowed. Allowed extensions are: " -"%(allowed_extensions)s." -msgstr "" -"Sambungan fail \"%(extension)s\" tidak dibenarkan. Sambungan yang dibenarkan " -"adalah: %(allowed_extensions)s." - -msgid "Null characters are not allowed." -msgstr "Karakter Null tidak dibenarkan." - -msgid "and" -msgstr "dan" - -#, python-format -msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "%(model_name)s dengan %(field_labels)s ini sudah wujud." - -#, python-format -msgid "Value %(value)r is not a valid choice." -msgstr "Nilai %(value)r bukan pilihan yang sah." - -msgid "This field cannot be null." -msgstr "Medan ini tidak boleh null." - -msgid "This field cannot be blank." -msgstr "Medan ini tidak boleh dibiarkan kosong." - -#, python-format -msgid "%(model_name)s with this %(field_label)s already exists." -msgstr "%(model_name)s dengan %(field_label)s ini sudah wujud." - -#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. -#. Eg: "Title must be unique for pub_date year" -#, python-format -msgid "" -"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -msgstr "%(field_label)s mesti unik untuk %(date_field_label)s %(lookup_type)s." - -#, python-format -msgid "Field of type: %(field_type)s" -msgstr "Jenis medan: %(field_type)s" - -#, python-format -msgid "“%(value)s” value must be either True or False." -msgstr "Nilai \"%(value)s\" mesti samada True atau False." - -#, python-format -msgid "“%(value)s” value must be either True, False, or None." -msgstr "Nilai \"%(value)s\" mesti samada True, False, atau None." - -msgid "Boolean (Either True or False)" -msgstr "Boolean (Samada True atau False)" - -#, python-format -msgid "String (up to %(max_length)s)" -msgstr "String (sehingga %(max_length)s)" - -msgid "Comma-separated integers" -msgstr "Integer dipisahkan dengan koma" - -#, python-format -msgid "" -"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " -"format." -msgstr "" -"Nilai \"%(value)s\" mempunyai format tarikh yang tidak sah. Format harus " -"berbentuk YYYY-MM-DD." - -#, python-format -msgid "" -"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " -"date." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang betul (YYYY-MM-DD) tetapi ia " -"adalah tarikh yang tidak sah." - -msgid "Date (without time)" -msgstr "Tarikh (tanpa masa)" - -#, python-format -msgid "" -"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ] format." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang tidak sah. Format harus berbentuk " -"YYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]." - -#, python-format -msgid "" -"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -"[TZ]) but it is an invalid date/time." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang betul (YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ]) tetapi ia adalah tarikh/masa yang tidak sah." - -msgid "Date (with time)" -msgstr "Tarikh (dengan masa)" - -#, python-format -msgid "“%(value)s” value must be a decimal number." -msgstr "Nilai \"%(value)s\" mesti dalam bentuk nombor titik perpuluhan." - -msgid "Decimal number" -msgstr "Nombor titik perpuluhan" - -#, python-format -msgid "" -"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." -"uuuuuu] format." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang tidak sah. Format harus berbentuk " -"[DD] [[HH:]MM:]ss[.uuuuuu]." - -msgid "Duration" -msgstr "Jangka-masa" - -msgid "Email address" -msgstr "Alama emel" - -msgid "File path" -msgstr "Laluan fail" - -#, python-format -msgid "“%(value)s” value must be a float." -msgstr "Nilai \"%(value)s\" mesti dalam bentuk titik terapung." - -msgid "Floating point number" -msgstr "Nombor titik terapung" - -#, python-format -msgid "“%(value)s” value must be an integer." -msgstr "Nilai \"%(value)s\" mesti dalam bentuk integer." - -msgid "Integer" -msgstr "Integer" - -msgid "Big (8 byte) integer" -msgstr "Integer besar (8 bait)" - -msgid "Small integer" -msgstr "Integer kecil" - -msgid "IPv4 address" -msgstr "Alamat IPv4" - -msgid "IP address" -msgstr "Alamat IP" - -#, python-format -msgid "“%(value)s” value must be either None, True or False." -msgstr "Nilai \"%(value)s\" mesti samada None, True, atau False." - -msgid "Boolean (Either True, False or None)" -msgstr "Boolean (samada True, False, None)" - -msgid "Positive big integer" -msgstr "Integer besar positif" - -msgid "Positive integer" -msgstr "Integer positif" - -msgid "Positive small integer" -msgstr "Integer kecil positif" - -#, python-format -msgid "Slug (up to %(max_length)s)" -msgstr "Slug (sehingga %(max_length)s)" - -msgid "Text" -msgstr "Teks" - -#, python-format -msgid "" -"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " -"format." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang tidak sah. Format harus berbentuk " -"HH:MM[:ss[.uuuuuu]]." - -#, python-format -msgid "" -"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " -"invalid time." -msgstr "" -"Nilai \"%(value)s\" mempunyai format yang betul (HH:MM[:ss[.uuuuuu]]) tetapi " -"ia mempunyai masa yang tidak sah." - -msgid "Time" -msgstr "Masa" - -msgid "URL" -msgstr "URL" - -msgid "Raw binary data" -msgstr "Data binari mentah" - -#, python-format -msgid "“%(value)s” is not a valid UUID." -msgstr "UUID \"%(value)s\" tidak sah." - -msgid "Universally unique identifier" -msgstr "Pengecam unik universal" - -msgid "File" -msgstr "Fail" - -msgid "Image" -msgstr "Imej" - -msgid "A JSON object" -msgstr "Objek JSON" - -msgid "Value must be valid JSON." -msgstr "Nilai harus dalam bentuk JSON yang sah." - -#, python-format -msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "%(model)s dengan %(field)s %(value)r tidak wujud." - -msgid "Foreign Key (type determined by related field)" -msgstr "Kunci Asing (jenis ditentukan oleh medan yang berkaitan)" - -msgid "One-to-one relationship" -msgstr "Hubungan satu-ke-satu" - -#, python-format -msgid "%(from)s-%(to)s relationship" -msgstr "Hubungan %(from)s-%(to)s" - -#, python-format -msgid "%(from)s-%(to)s relationships" -msgstr "Hubungan %(from)s-%(to)s" - -msgid "Many-to-many relationship" -msgstr "Hubungan banyak-ke-banyak" - -#. Translators: If found as last label character, these punctuation -#. characters will prevent the default label_suffix to be appended to the -#. label -msgid ":?.!" -msgstr ":?.!" - -msgid "This field is required." -msgstr "Medan ini diperlukan." - -msgid "Enter a whole number." -msgstr "Masukkan nombor bulat." - -msgid "Enter a valid date." -msgstr "Masukkan tarikh yang sah." - -msgid "Enter a valid time." -msgstr "Masukkan masa yang sah." - -msgid "Enter a valid date/time." -msgstr "Masukkan tarikh/masa yang sah." - -msgid "Enter a valid duration." -msgstr "Masukkan jangka-masa yang sah." - -#, python-brace-format -msgid "The number of days must be between {min_days} and {max_days}." -msgstr "Jumlah hari mesti diantara {min_days} ke {max_days}." - -msgid "No file was submitted. Check the encoding type on the form." -msgstr "Tiada fail yang dihantar. Periksa jenis encoding pada borang." - -msgid "No file was submitted." -msgstr "Tiada fail yang dihantar." - -msgid "The submitted file is empty." -msgstr "Fail yang dihantar kosong." - -#, python-format -msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." -msgid_plural "" -"Ensure this filename has at most %(max)d characters (it has %(length)d)." -msgstr[0] "" -"Pastikan nama fial ini tidak melebihi %(max)d karakter (ia mempunyai " -"%(length)d)." - -msgid "Please either submit a file or check the clear checkbox, not both." -msgstr "Sila hantar fail atau tandakan pada kotak, bukan kedua-duanya sekali. " - -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" -"Muatnaik imej yang sah. Fail yang anda muatnaik samada bukan imej atau imej " -"yang rosak." - -#, python-format -msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "Pilih pilihan yang sah. %(value)s bukan pilihan yang tersedia." - -msgid "Enter a list of values." -msgstr "Masukkan senarai nilai." - -msgid "Enter a complete value." -msgstr "Masukkan nilai yang lengkap." - -msgid "Enter a valid UUID." -msgstr "Masukkan UUID yang sah." - -msgid "Enter a valid JSON." -msgstr "Masukkan JSON yang sah." - -#. Translators: This is the default suffix added to form field labels -msgid ":" -msgstr ":" - -#, python-format -msgid "(Hidden field %(name)s) %(error)s" -msgstr "%(error)s (Medan tersorok %(name)s)" - -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" -"Data ManagementForm tidak dijumpai atau telah diusik. Medan yang hilang: " -"%(field_names)s. Anda mungkin perlu menghantar laporan pepijat sekiranya " -"masalah masih berterusan." - -#, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "Sila hantar tidak lebih dari %d borang." - -#, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Sila hantar sekurang-kurangnya %d borang." - -msgid "Order" -msgstr "Susunan" - -msgid "Delete" -msgstr "Padam" - -#, python-format -msgid "Please correct the duplicate data for %(field)s." -msgstr "Sila betulkan data duplikasi bagi %(field)s" - -#, python-format -msgid "Please correct the duplicate data for %(field)s, which must be unique." -msgstr "Sila betulkan data duplikasi bagi %(field)s, yang mana mestilah unik." - -#, python-format -msgid "" -"Please correct the duplicate data for %(field_name)s which must be unique " -"for the %(lookup)s in %(date_field)s." -msgstr "" -"Sila betulkan data duplikasi bagi %(field_name)s yang mana mestilah unik " -"untuk %(lookup)s didalam %(date_field)s." - -msgid "Please correct the duplicate values below." -msgstr "Sila betulkan nilai-nilai duplikasi dibawah." - -msgid "The inline value did not match the parent instance." -msgstr "Nilai didalam barisan tidak sepadan dengan parent instance." - -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "Pilih pilihan yang sah. Pilihan itu tidak ada didalam senarai pilihan." - -#, python-format -msgid "“%(pk)s” is not a valid value." -msgstr "\"%(pk)s\" bukan nilai yang sah." - -#, python-format -msgid "" -"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " -"may be ambiguous or it may not exist." -msgstr "" -"%(datetime)s tidak dapat diterjemahkan ke dalam zon masa " -"%(current_timezone)s; ia mungkin samar-samar atau tidak wujud." - -msgid "Clear" -msgstr "Kosongkan" - -msgid "Currently" -msgstr "Kini" - -msgid "Change" -msgstr "Tukar" - -msgid "Unknown" -msgstr "Tidak diketahui" - -msgid "Yes" -msgstr "Ya" - -msgid "No" -msgstr "Tidak" - -#. Translators: Please do not add spaces around commas. -msgid "yes,no,maybe" -msgstr "ya,tidak,mungkin" - -#, python-format -msgid "%(size)d byte" -msgid_plural "%(size)d bytes" -msgstr[0] "%(size)d bait" - -#, python-format -msgid "%s KB" -msgstr "%s KB" - -#, python-format -msgid "%s MB" -msgstr "%s MB" - -#, python-format -msgid "%s GB" -msgstr "%s GB" - -#, python-format -msgid "%s TB" -msgstr "%s TB" - -#, python-format -msgid "%s PB" -msgstr "%s PB" - -msgid "p.m." -msgstr "malam" - -msgid "a.m." -msgstr "pagi" - -msgid "PM" -msgstr "MALAM" - -msgid "AM" -msgstr "PAGI" - -msgid "midnight" -msgstr "tengah malam" - -msgid "noon" -msgstr "tengahari" - -msgid "Monday" -msgstr "Isnin" - -msgid "Tuesday" -msgstr "Selasa" - -msgid "Wednesday" -msgstr "Rabu" - -msgid "Thursday" -msgstr "Khamis" - -msgid "Friday" -msgstr "Jumaat" - -msgid "Saturday" -msgstr "Sabtu" - -msgid "Sunday" -msgstr "Ahad" - -msgid "Mon" -msgstr "Isn" - -msgid "Tue" -msgstr "Sel" - -msgid "Wed" -msgstr "Rab" - -msgid "Thu" -msgstr "Kha" - -msgid "Fri" -msgstr "Jum" - -msgid "Sat" -msgstr "Sab" - -msgid "Sun" -msgstr "Aha" - -msgid "January" -msgstr "Januari" - -msgid "February" -msgstr "Februari" - -msgid "March" -msgstr "Mac" - -msgid "April" -msgstr "April" - -msgid "May" -msgstr "Mei" - -msgid "June" -msgstr "Jun" - -msgid "July" -msgstr "Julai" - -msgid "August" -msgstr "Ogos" - -msgid "September" -msgstr "September" - -msgid "October" -msgstr "Oktober" - -msgid "November" -msgstr "November" - -msgid "December" -msgstr "Disember" - -msgid "jan" -msgstr "jan" - -msgid "feb" -msgstr "feb" - -msgid "mar" -msgstr "mar" - -msgid "apr" -msgstr "apr" - -msgid "may" -msgstr "mei" - -msgid "jun" -msgstr "jun" - -msgid "jul" -msgstr "jul" - -msgid "aug" -msgstr "ogo" - -msgid "sep" -msgstr "sep" - -msgid "oct" -msgstr "okt" - -msgid "nov" -msgstr "nov" - -msgid "dec" -msgstr "dis" - -msgctxt "abbrev. month" -msgid "Jan." -msgstr "Jan." - -msgctxt "abbrev. month" -msgid "Feb." -msgstr "Feb" - -msgctxt "abbrev. month" -msgid "March" -msgstr "Mac" - -msgctxt "abbrev. month" -msgid "April" -msgstr "April" - -msgctxt "abbrev. month" -msgid "May" -msgstr "Mei" - -msgctxt "abbrev. month" -msgid "June" -msgstr "Jun" - -msgctxt "abbrev. month" -msgid "July" -msgstr "Julai" - -msgctxt "abbrev. month" -msgid "Aug." -msgstr "Ogo." - -msgctxt "abbrev. month" -msgid "Sept." -msgstr "Sept." - -msgctxt "abbrev. month" -msgid "Oct." -msgstr "Okt." - -msgctxt "abbrev. month" -msgid "Nov." -msgstr "Nov." - -msgctxt "abbrev. month" -msgid "Dec." -msgstr "Dis." - -msgctxt "alt. month" -msgid "January" -msgstr "Januari" - -msgctxt "alt. month" -msgid "February" -msgstr "Februari" - -msgctxt "alt. month" -msgid "March" -msgstr "Mac" - -msgctxt "alt. month" -msgid "April" -msgstr "April" - -msgctxt "alt. month" -msgid "May" -msgstr "Mei" - -msgctxt "alt. month" -msgid "June" -msgstr "Jun" - -msgctxt "alt. month" -msgid "July" -msgstr "Julai" - -msgctxt "alt. month" -msgid "August" -msgstr "Ogos" - -msgctxt "alt. month" -msgid "September" -msgstr "September" - -msgctxt "alt. month" -msgid "October" -msgstr "Oktober" - -msgctxt "alt. month" -msgid "November" -msgstr "November" - -msgctxt "alt. month" -msgid "December" -msgstr "Disember" - -msgid "This is not a valid IPv6 address." -msgstr "Alamat IPv6 ini tidak sah." - -#, python-format -msgctxt "String to return when truncating text" -msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s ..." - -msgid "or" -msgstr "atau" - -#. Translators: This string is used as a separator between list elements -msgid ", " -msgstr "," - -#, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d tahun" - -#, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d bulan" - -#, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d minggu " - -#, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d hari" - -#, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d jam" - -#, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minit" - -msgid "Forbidden" -msgstr "Dilarang" - -msgid "CSRF verification failed. Request aborted." -msgstr "Verifikasi VSRF gagal. Permintaan dihentikan." - -msgid "" -"You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " -"required for security reasons, to ensure that your browser is not being " -"hijacked by third parties." -msgstr "" -"Anda melihat mesej ini kerana laman HTTPS ini memerlukan \"Referer header\" " -"dihantar ke pelayar sesawang anda, tetapi ia tidak dihantar. Header ini " -"diperlukan bagi tujuan keselamatan, agar dapat memastikan pelayar anda tidak " -"dirampas oleh pihak ketiga." - -msgid "" -"If you have configured your browser to disable “Referer” headers, please re-" -"enable them, at least for this site, or for HTTPS connections, or for “same-" -"origin” requests." -msgstr "" -"Sekiranya anda telah menetapkan pelayar anda untuk mematikan header \"Referer" -"\", sila hidupkannya semula, sekurang-kurangya bagi laman ini, atau bagi " -"sambungan HTTPS, atau bagi permintaan \"same-origin\"." - -msgid "" -"If you are using the tag or " -"including the “Referrer-Policy: no-referrer” header, please remove them. The " -"CSRF protection requires the “Referer” header to do strict referer checking. " -"If you’re concerned about privacy, use alternatives like for links to third-party sites." -msgstr "" -"Sekiranya anda menggunakan tag atau memasukkan header \"Referer-Policy: no-referrer\", sila buangkan " -"ia. Perlindungan CSRF memerlukan header \"Referer\" untuk melakukan " -"penyemakan referer yang ketat. Sekiranya anda risau tentang privasi anda, " -"gunakan alternatif seperti bagi pautan laman pihak " -"ketiga." - -msgid "" -"You are seeing this message because this site requires a CSRF cookie when " -"submitting forms. This cookie is required for security reasons, to ensure " -"that your browser is not being hijacked by third parties." -msgstr "" -"Anda melihat mesej ini kerana laman ini memerlukan cookie CSRF apabila " -"menghantar borang. Cookie ini diperlukan bagi tujuan keselamatan, bagi " -"memastikan pelayar anda tidak dirampas oleh pihak ketiga." - -msgid "" -"If you have configured your browser to disable cookies, please re-enable " -"them, at least for this site, or for “same-origin” requests." -msgstr "" -"Sekiranya anda telah menetapkan pelayar anda untuk tidak menerima cookie, " -"sila hidupkannya semula, sekurang-kurangnya bagi laman ini, atau bagi " -"permintaan \"same-origin\"." - -msgid "More information is available with DEBUG=True." -msgstr "Maklumat lanjut boleh didapati dengan menetapkan DEBUG=True." - -msgid "No year specified" -msgstr "Tiada tahun diberikan" - -msgid "Date out of range" -msgstr "Tarikh diluar julat" - -msgid "No month specified" -msgstr "Tiada bulan diberikan" - -msgid "No day specified" -msgstr "Tiada hari diberikan" - -msgid "No week specified" -msgstr "Tiada minggu diberikan" - -#, python-format -msgid "No %(verbose_name_plural)s available" -msgstr "%(verbose_name_plural)s tiada" - -#, python-format -msgid "" -"Future %(verbose_name_plural)s not available because %(class_name)s." -"allow_future is False." -msgstr "" -"%(verbose_name_plural)s masa depan tiada kerana %(class_name)s.allow_future " -"adalah False. " - -#, python-format -msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "" -"\"%(datestr)s\" tarikh yang diberikan tidak sah mengikut format \"%(format)s" -"\"" - -#, python-format -msgid "No %(verbose_name)s found matching the query" -msgstr "Tiada %(verbose_name)s mengikut pertanyaan yang dimasukkan" - -msgid "Page is not “last”, nor can it be converted to an int." -msgstr "Ruangan ini bukan \"last\", dan tidak boleh ditukar kepada int." - -#, python-format -msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "Ruangan tidak sah (%(page_number)s): %(message)s" - -#, python-format -msgid "Empty list and “%(class_name)s.allow_empty” is False." -msgstr "Senarai kosong dan \"%(class_name)s.allow_empty\" adalah False." - -msgid "Directory indexes are not allowed here." -msgstr "Indeks Direktori tidak dibenarkan disini." - -#, python-format -msgid "“%(path)s” does not exist" -msgstr "\"%(path)s\" tidak wujud" - -#, python-format -msgid "Index of %(directory)s" -msgstr "Indeks %(directory)s" - -msgid "The install worked successfully! Congratulations!" -msgstr "Pemasangan berjaya dilakukan! Tahniah!" - -#, python-format -msgid "" -"View release notes for Django %(version)s" -msgstr "" -"Lihat nota pelepasan bagi Django %(version)s" - -#, python-format -msgid "" -"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " -"URLs." -msgstr "" -"Anda melihat ruangan ini kerana DEBUG=True terdapat didalam fail tetapan anda dan anda tidak " -"menetapkan sebarang URL." - -msgid "Django Documentation" -msgstr "Dokumentasi Django" - -msgid "Topics, references, & how-to’s" -msgstr "Topik, rujukan, & bagaimana-cara" - -msgid "Tutorial: A Polling App" -msgstr "Tutorial: App Soal-Selidik" - -msgid "Get started with Django" -msgstr "Mulakan dengan Django" - -msgid "Django Community" -msgstr "Komuniti Django" - -msgid "Connect, get help, or contribute" -msgstr "Sambung, minta bantuan, atau sumbang" diff --git a/venv/Lib/site-packages/django/conf/locale/ms/formats.py b/venv/Lib/site-packages/django/conf/locale/ms/formats.py deleted file mode 100644 index d06719f..0000000 --- a/venv/Lib/site-packages/django/conf/locale/ms/formats.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# The *_FORMAT strings use the Django date format syntax, -# see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j M Y" # '25 Oct 2006' -TIME_FORMAT = "P" # '2:30 p.m.' -DATETIME_FORMAT = "j M Y, P" # '25 Oct 2006, 2:30 p.m.' -YEAR_MONTH_FORMAT = "F Y" # 'October 2006' -MONTH_DAY_FORMAT = "j F" # '25 October' -SHORT_DATE_FORMAT = "d/m/Y" # '25/10/2006' -SHORT_DATETIME_FORMAT = "d/m/Y P" # '25/10/2006 2:30 p.m.' -FIRST_DAY_OF_WEEK = 0 # Sunday - -# The *_INPUT_FORMATS strings use the Python strftime format syntax, -# see https://docs.python.org/library/datetime.html#strftime-strptime-behavior -DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - "%d %b %Y", # '25 Oct 2006' - "%d %b, %Y", # '25 Oct, 2006' - "%d %B %Y", # '25 October 2006' - "%d %B, %Y", # '25 October, 2006' -] -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' - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' -] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," -NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/nb/formats.py b/venv/Lib/site-packages/django/conf/locale/nb/formats.py index 0ddb8fe..91dd9e6 100644 --- a/venv/Lib/site-packages/django/conf/locale/nb/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/nb/formats.py @@ -2,40 +2,35 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j. F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - # "%d. %b %Y", # '25. okt 2006' - # "%d %b %Y", # '25 okt 2006' - # "%d. %b. %Y", # '25. okt. 2006' - # "%d %b. %Y", # '25 okt. 2006' - # "%d. %B %Y", # '25. oktober 2006' - # "%d %B %Y", # '25 oktober 2006' + '%Y-%m-%d', '%d.%m.%Y', '%d.%m.%y', # '2006-10-25', '25.10.2006', '25.10.06' + # '%d. %b %Y', '%d %b %Y', # '25. okt 2006', '25 okt 2006' + # '%d. %b. %Y', '%d %b. %Y', # '25. okt. 2006', '25 okt. 2006' + # '%d. %B %Y', '%d %B %Y', # '25. oktober 2006', '25 oktober 2006' ] 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' - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' + '%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' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/nl/formats.py b/venv/Lib/site-packages/django/conf/locale/nl/formats.py index e9f52b9..afadb9f 100644 --- a/venv/Lib/site-packages/django/conf/locale/nl/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/nl/formats.py @@ -2,91 +2,65 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" # '20 januari 2009' -TIME_FORMAT = "H:i" # '15:23' -DATETIME_FORMAT = "j F Y H:i" # '20 januari 2009 15:23' -YEAR_MONTH_FORMAT = "F Y" # 'januari 2009' -MONTH_DAY_FORMAT = "j F" # '20 januari' -SHORT_DATE_FORMAT = "j-n-Y" # '20-1-2009' -SHORT_DATETIME_FORMAT = "j-n-Y H:i" # '20-1-2009 15:23' -FIRST_DAY_OF_WEEK = 1 # Monday (in Dutch 'maandag') +DATE_FORMAT = 'j F Y' # '20 januari 2009' +TIME_FORMAT = 'H:i' # '15:23' +DATETIME_FORMAT = 'j F Y H:i' # '20 januari 2009 15:23' +YEAR_MONTH_FORMAT = 'F Y' # 'januari 2009' +MONTH_DAY_FORMAT = 'j F' # '20 januari' +SHORT_DATE_FORMAT = 'j-n-Y' # '20-1-2009' +SHORT_DATETIME_FORMAT = 'j-n-Y H:i' # '20-1-2009 15:23' +FIRST_DAY_OF_WEEK = 1 # Monday (in Dutch 'maandag') # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d-%m-%Y", # '20-01-2009' - "%d-%m-%y", # '20-01-09' - "%d/%m/%Y", # '20/01/2009' - "%d/%m/%y", # '20/01/09' - "%Y/%m/%d", # '2009/01/20' - # "%d %b %Y", # '20 jan 2009' - # "%d %b %y", # '20 jan 09' - # "%d %B %Y", # '20 januari 2009' - # "%d %B %y", # '20 januari 09' + '%d-%m-%Y', '%d-%m-%y', # '20-01-2009', '20-01-09' + '%d/%m/%Y', '%d/%m/%y', # '20/01/2009', '20/01/09' + '%Y/%m/%d', # '2009/01/20' + # '%d %b %Y', '%d %b %y', # '20 jan 2009', '20 jan 09' + # '%d %B %Y', '%d %B %y', # '20 januari 2009', '20 januari 09' ] # Kept ISO formats as one is in first position TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '15:23:35' - "%H:%M:%S.%f", # '15:23:35.000200' - "%H.%M:%S", # '15.23:35' - "%H.%M:%S.%f", # '15.23:35.000200' - "%H.%M", # '15.23' - "%H:%M", # '15:23' + '%H:%M:%S', # '15:23:35' + '%H:%M:%S.%f', # '15:23:35.000200' + '%H.%M:%S', # '15.23:35' + '%H.%M:%S.%f', # '15.23:35.000200' + '%H.%M', # '15.23' + '%H:%M', # '15:23' ] DATETIME_INPUT_FORMATS = [ # With time in %H:%M:%S : - "%d-%m-%Y %H:%M:%S", # '20-01-2009 15:23:35' - "%d-%m-%y %H:%M:%S", # '20-01-09 15:23:35' - "%Y-%m-%d %H:%M:%S", # '2009-01-20 15:23:35' - "%d/%m/%Y %H:%M:%S", # '20/01/2009 15:23:35' - "%d/%m/%y %H:%M:%S", # '20/01/09 15:23:35' - "%Y/%m/%d %H:%M:%S", # '2009/01/20 15:23:35' - # "%d %b %Y %H:%M:%S", # '20 jan 2009 15:23:35' - # "%d %b %y %H:%M:%S", # '20 jan 09 15:23:35' - # "%d %B %Y %H:%M:%S", # '20 januari 2009 15:23:35' - # "%d %B %y %H:%M:%S", # '20 januari 2009 15:23:35' + '%d-%m-%Y %H:%M:%S', '%d-%m-%y %H:%M:%S', '%Y-%m-%d %H:%M:%S', + # '20-01-2009 15:23:35', '20-01-09 15:23:35', '2009-01-20 15:23:35' + '%d/%m/%Y %H:%M:%S', '%d/%m/%y %H:%M:%S', '%Y/%m/%d %H:%M:%S', + # '20/01/2009 15:23:35', '20/01/09 15:23:35', '2009/01/20 15:23:35' + # '%d %b %Y %H:%M:%S', '%d %b %y %H:%M:%S', # '20 jan 2009 15:23:35', '20 jan 09 15:23:35' + # '%d %B %Y %H:%M:%S', '%d %B %y %H:%M:%S', # '20 januari 2009 15:23:35', '20 januari 2009 15:23:35' # With time in %H:%M:%S.%f : - "%d-%m-%Y %H:%M:%S.%f", # '20-01-2009 15:23:35.000200' - "%d-%m-%y %H:%M:%S.%f", # '20-01-09 15:23:35.000200' - "%Y-%m-%d %H:%M:%S.%f", # '2009-01-20 15:23:35.000200' - "%d/%m/%Y %H:%M:%S.%f", # '20/01/2009 15:23:35.000200' - "%d/%m/%y %H:%M:%S.%f", # '20/01/09 15:23:35.000200' - "%Y/%m/%d %H:%M:%S.%f", # '2009/01/20 15:23:35.000200' + '%d-%m-%Y %H:%M:%S.%f', '%d-%m-%y %H:%M:%S.%f', '%Y-%m-%d %H:%M:%S.%f', + # '20-01-2009 15:23:35.000200', '20-01-09 15:23:35.000200', '2009-01-20 15:23:35.000200' + '%d/%m/%Y %H:%M:%S.%f', '%d/%m/%y %H:%M:%S.%f', '%Y/%m/%d %H:%M:%S.%f', + # '20/01/2009 15:23:35.000200', '20/01/09 15:23:35.000200', '2009/01/20 15:23:35.000200' # With time in %H.%M:%S : - "%d-%m-%Y %H.%M:%S", # '20-01-2009 15.23:35' - "%d-%m-%y %H.%M:%S", # '20-01-09 15.23:35' - "%d/%m/%Y %H.%M:%S", # '20/01/2009 15.23:35' - "%d/%m/%y %H.%M:%S", # '20/01/09 15.23:35' - # "%d %b %Y %H.%M:%S", # '20 jan 2009 15.23:35' - # "%d %b %y %H.%M:%S", # '20 jan 09 15.23:35' - # "%d %B %Y %H.%M:%S", # '20 januari 2009 15.23:35' - # "%d %B %y %H.%M:%S", # '20 januari 2009 15.23:35' + '%d-%m-%Y %H.%M:%S', '%d-%m-%y %H.%M:%S', # '20-01-2009 15.23:35', '20-01-09 15.23:35' + '%d/%m/%Y %H.%M:%S', '%d/%m/%y %H.%M:%S', # '20/01/2009 15.23:35', '20/01/09 15.23:35' + # '%d %b %Y %H.%M:%S', '%d %b %y %H.%M:%S', # '20 jan 2009 15.23:35', '20 jan 09 15.23:35' + # '%d %B %Y %H.%M:%S', '%d %B %y %H.%M:%S', # '20 januari 2009 15.23:35', '20 januari 2009 15.23:35' # With time in %H.%M:%S.%f : - "%d-%m-%Y %H.%M:%S.%f", # '20-01-2009 15.23:35.000200' - "%d-%m-%y %H.%M:%S.%f", # '20-01-09 15.23:35.000200' - "%d/%m/%Y %H.%M:%S.%f", # '20/01/2009 15.23:35.000200' - "%d/%m/%y %H.%M:%S.%f", # '20/01/09 15.23:35.000200' + '%d-%m-%Y %H.%M:%S.%f', '%d-%m-%y %H.%M:%S.%f', # '20-01-2009 15.23:35.000200', '20-01-09 15.23:35.000200' + '%d/%m/%Y %H.%M:%S.%f', '%d/%m/%y %H.%M:%S.%f', # '20/01/2009 15.23:35.000200', '20/01/09 15.23:35.000200' # With time in %H:%M : - "%d-%m-%Y %H:%M", # '20-01-2009 15:23' - "%d-%m-%y %H:%M", # '20-01-09 15:23' - "%Y-%m-%d %H:%M", # '2009-01-20 15:23' - "%d/%m/%Y %H:%M", # '20/01/2009 15:23' - "%d/%m/%y %H:%M", # '20/01/09 15:23' - "%Y/%m/%d %H:%M", # '2009/01/20 15:23' - # "%d %b %Y %H:%M", # '20 jan 2009 15:23' - # "%d %b %y %H:%M", # '20 jan 09 15:23' - # "%d %B %Y %H:%M", # '20 januari 2009 15:23' - # "%d %B %y %H:%M", # '20 januari 2009 15:23' + '%d-%m-%Y %H:%M', '%d-%m-%y %H:%M', '%Y-%m-%d %H:%M', # '20-01-2009 15:23', '20-01-09 15:23', '2009-01-20 15:23' + '%d/%m/%Y %H:%M', '%d/%m/%y %H:%M', '%Y/%m/%d %H:%M', # '20/01/2009 15:23', '20/01/09 15:23', '2009/01/20 15:23' + # '%d %b %Y %H:%M', '%d %b %y %H:%M', # '20 jan 2009 15:23', '20 jan 09 15:23' + # '%d %B %Y %H:%M', '%d %B %y %H:%M', # '20 januari 2009 15:23', '20 januari 2009 15:23' # With time in %H.%M : - "%d-%m-%Y %H.%M", # '20-01-2009 15.23' - "%d-%m-%y %H.%M", # '20-01-09 15.23' - "%d/%m/%Y %H.%M", # '20/01/2009 15.23' - "%d/%m/%y %H.%M", # '20/01/09 15.23' - # "%d %b %Y %H.%M", # '20 jan 2009 15.23' - # "%d %b %y %H.%M", # '20 jan 09 15.23' - # "%d %B %Y %H.%M", # '20 januari 2009 15.23' - # "%d %B %y %H.%M", # '20 januari 2009 15.23' + '%d-%m-%Y %H.%M', '%d-%m-%y %H.%M', # '20-01-2009 15.23', '20-01-09 15.23' + '%d/%m/%Y %H.%M', '%d/%m/%y %H.%M', # '20/01/2009 15.23', '20/01/09 15.23' + # '%d %b %Y %H.%M', '%d %b %y %H.%M', # '20 jan 2009 15.23', '20 jan 09 15.23' + # '%d %B %Y %H.%M', '%d %B %y %H.%M', # '20 januari 2009 15.23', '20 januari 2009 15.23' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.mo index 7698eda..5629cea 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.po index 04b872e..b95b0b0 100644 --- a/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/nn/LC_MESSAGES/django.po @@ -5,16 +5,14 @@ # Jannis Leidel , 2011 # jensadne , 2013 # Sigurd Gartmann , 2012 -# Sivert Olstad, 2021 # velmont , 2012 -# Vibeke Uthaug, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-25 14:49+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -29,11 +27,8 @@ msgstr "Afrikaans" msgid "Arabic" msgstr "Arabisk" -msgid "Algerian Arabic" -msgstr "Arabisk (algersk)" - msgid "Asturian" -msgstr "Asturiansk" +msgstr "" msgid "Azerbaijani" msgstr "Aserbajansk" @@ -69,7 +64,7 @@ msgid "German" msgstr "Tysk" msgid "Lower Sorbian" -msgstr "Lågsorbisk" +msgstr "" msgid "Greek" msgstr "Gresk" @@ -78,7 +73,7 @@ msgid "English" msgstr "Engelsk" msgid "Australian English" -msgstr "Engelsk (australsk)" +msgstr "" msgid "British English" msgstr "Engelsk (britisk)" @@ -93,7 +88,7 @@ msgid "Argentinian Spanish" msgstr "Spansk (argentinsk)" msgid "Colombian Spanish" -msgstr "Spansk (kolombiansk)" +msgstr "" msgid "Mexican Spanish" msgstr "Spansk (meksikansk)" @@ -126,7 +121,7 @@ msgid "Irish" msgstr "Irsk" msgid "Scottish Gaelic" -msgstr "Skotsk-gaelisk" +msgstr "" msgid "Galician" msgstr "Galisisk" @@ -141,25 +136,22 @@ msgid "Croatian" msgstr "Kroatisk" msgid "Upper Sorbian" -msgstr "Høgsorbisk" +msgstr "" msgid "Hungarian" msgstr "Ungarsk" msgid "Armenian" -msgstr "Armensk" +msgstr "" msgid "Interlingua" -msgstr "Interlingua" +msgstr "" msgid "Indonesian" msgstr "Indonesisk" -msgid "Igbo" -msgstr "Igbo" - msgid "Ido" -msgstr "Ido" +msgstr "" msgid "Icelandic" msgstr "Islandsk" @@ -174,7 +166,7 @@ msgid "Georgian" msgstr "Georgisk" msgid "Kabyle" -msgstr "Kabylsk" +msgstr "" msgid "Kazakh" msgstr "Kasakhisk" @@ -188,9 +180,6 @@ msgstr "Kannada" msgid "Korean" msgstr "Koreansk" -msgid "Kyrgyz" -msgstr "Kirgisisk" - msgid "Luxembourgish" msgstr "Luxembourgsk" @@ -210,16 +199,13 @@ msgid "Mongolian" msgstr "Mongolsk" msgid "Marathi" -msgstr "Marathi" - -msgid "Malay" -msgstr "Malayisk" +msgstr "" msgid "Burmese" msgstr "Burmesisk" msgid "Norwegian Bokmål" -msgstr "Norsk (bokmål)" +msgstr "" msgid "Nepali" msgstr "Nepali" @@ -278,15 +264,9 @@ msgstr "Tamil" msgid "Telugu" msgstr "Telugu" -msgid "Tajik" -msgstr "Tadsjikisk" - msgid "Thai" msgstr "Thai" -msgid "Turkmen" -msgstr "Turkmensk" - msgid "Turkish" msgstr "Tyrkisk" @@ -303,7 +283,7 @@ msgid "Urdu" msgstr "Urdu" msgid "Uzbek" -msgstr "Usbekisk" +msgstr "" msgid "Vietnamese" msgstr "Vietnamesisk" @@ -315,30 +295,25 @@ msgid "Traditional Chinese" msgstr "Tradisjonell kinesisk" msgid "Messages" -msgstr "Meldingar" +msgstr "" msgid "Site Maps" -msgstr "Sidekart" +msgstr "" msgid "Static Files" -msgstr "Statiske Filer" +msgstr "" msgid "Syndication" -msgstr "Syndikering" - -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" -msgstr "Sidenummeret er ikkje eit heiltal" +msgstr "" msgid "That page number is less than 1" -msgstr "Sidenummeret er mindre enn 1" +msgstr "" msgid "That page contains no results" -msgstr "Sida har ingen resultat" +msgstr "" msgid "Enter a valid value." msgstr "Oppgje ein gyldig verdi." @@ -347,7 +322,7 @@ msgid "Enter a valid URL." msgstr "Oppgje ei gyldig nettadresse." msgid "Enter a valid integer." -msgstr "Oppgje eit gyldig heiltal." +msgstr "" msgid "Enter a valid email address." msgstr "Oppgje ei gyldig e-postadresse." @@ -356,15 +331,11 @@ msgstr "Oppgje ei gyldig e-postadresse." msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Oppgje ein gyldig \"slug\" som består av bokstavar, nummer, understrekar " -"eller bindestrekar." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Oppgje ein gyldig \"slug\" som består av Unicode bokstavar, nummer, " -"understrekar eller bindestrekar." msgid "Enter a valid IPv4 address." msgstr "Oppgje ei gyldig IPv4-adresse." @@ -408,56 +379,50 @@ msgid_plural "" "Ensure this value has at most %(limit_value)d characters (it has " "%(show_value)d)." msgstr[0] "" -"Verdien kan ikkje ha fleire enn %(limit_value)d teikn (den har " -"%(show_value)d)." msgstr[1] "" -"Verdien kan ikkje ha fleire enn %(limit_value)d teikn (den har " -"%(show_value)d)." msgid "Enter a number." -msgstr "Oppgje eit tal." +msgstr "Oppgje eit tall." #, python-format msgid "Ensure that there are no more than %(max)s digit in total." msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "Verdien kan ikkje ha meir enn %(max)s siffer totalt." -msgstr[1] "Verdien kan ikkje ha meir enn %(max)s siffer totalt." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "Ensure that there are no more than %(max)s decimal place." msgid_plural "Ensure that there are no more than %(max)s decimal places." -msgstr[0] "Verdien kan ikkie ha meir enn %(max)s desimal." -msgstr[1] "Verdien kan ikkie ha meir enn %(max)s desimalar." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "" "Ensure that there are no more than %(max)s digit before the decimal point." msgid_plural "" "Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "Verdien kan ikkje ha meir enn %(max)s siffer framanfor komma." -msgstr[1] "Verdien kan ikkje ha meir enn %(max)s siffer framanfor komma." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -"Filtypen “%(extension)s” er ikkje tillate. Tillate filtypar er: " -"%(allowed_extensions)s." msgid "Null characters are not allowed." -msgstr "Null-teikn er ikkje tillate." +msgstr "" msgid "and" msgstr "og" #, python-format msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "%(model_name)s med %(field_labels)s fins allereie." +msgstr "" #, python-format msgid "Value %(value)r is not a valid choice." -msgstr "Verdi %(value)r er eit ugyldig val." +msgstr "" msgid "This field cannot be null." msgstr "Feltet kan ikkje vere tomt." @@ -474,7 +439,7 @@ msgstr "%(model_name)s med %(field_label)s fins allereie." #, python-format msgid "" "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -msgstr "%(field_label)s må vere unik for %(date_field_label)s %(lookup_type)s." +msgstr "" #, python-format msgid "Field of type: %(field_type)s" @@ -482,11 +447,11 @@ msgstr "Felt av typen: %(field_type)s" #, python-format msgid "“%(value)s” value must be either True or False." -msgstr "Verdien “%(value)s” må vere anten True eller False." +msgstr "" #, python-format msgid "“%(value)s” value must be either True, False, or None." -msgstr "Verdien “%(value)s” må vere anten True, False, eller None." +msgstr "" msgid "Boolean (Either True or False)" msgstr "Boolsk (True eller False)" @@ -503,15 +468,12 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"Verdien “%(value)s” har eit ugyldig datoformat. Det må vere på formen YYYY-" -"MM-DD." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -"Verdien “%(value)s” har rett format (YYYY-MM-DD) men er ein ugyldig dato." msgid "Date (without time)" msgstr "Dato (utan tid)" @@ -521,37 +483,31 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"Verdien “%(value)s” har eit ugyldig format. Det må vere på formen YYYY-MM-DD " -"HH:MM[:ss[.uuuuuu]][TZ]." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -"Verdien “%(value)s” har rett format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) men " -"er ein ugyldig dato eller klokkeslett." msgid "Date (with time)" msgstr "Dato (med tid)" #, python-format msgid "“%(value)s” value must be a decimal number." -msgstr "Verdien “%(value)s” må vere eit desimaltal." +msgstr "" msgid "Decimal number" -msgstr "Desimaltal" +msgstr "Desimaltall" #, python-format msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -"Verdien “%(value)s” har eit ugyldig format. Det må vere på formen [DD] " -"[[HH:]MM:]ss[.uuuuuu]." msgid "Duration" -msgstr "Varigskap" +msgstr "" msgid "Email address" msgstr "E-postadresse" @@ -561,14 +517,14 @@ msgstr "Filsti" #, python-format msgid "“%(value)s” value must be a float." -msgstr "Verdien “%(value)s” må vere eit flyttal." +msgstr "" msgid "Floating point number" -msgstr "Flyttal" +msgstr "Flyttall" #, python-format msgid "“%(value)s” value must be an integer." -msgstr "Verdien “%(value)s” må vere eit heiltal." +msgstr "" msgid "Integer" msgstr "Heiltal" @@ -576,9 +532,6 @@ msgstr "Heiltal" msgid "Big (8 byte) integer" msgstr "Stort (8 bitar) heiltal" -msgid "Small integer" -msgstr "Lite heiltal" - msgid "IPv4 address" msgstr "IPv4-adresse" @@ -587,14 +540,11 @@ msgstr "IP-adresse" #, python-format msgid "“%(value)s” value must be either None, True or False." -msgstr "Verdien “%(value)s” må vere anten None, True, eller False." +msgstr "" msgid "Boolean (Either True, False or None)" msgstr "Boolsk (True, False eller None)" -msgid "Positive big integer" -msgstr "Positivt stort heiltal" - msgid "Positive integer" msgstr "Positivt heiltal" @@ -605,6 +555,9 @@ msgstr "Positivt lite heiltal" msgid "Slug (up to %(max_length)s)" msgstr "Slug (opp til %(max_length)s)" +msgid "Small integer" +msgstr "Lite heiltal" + msgid "Text" msgstr "Tekst" @@ -613,16 +566,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"Verdien “%(value)s” har eit ugyldig format. Det må vere på formen HH:MM[:ss[." -"uuuuuu]]." #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"Verdien “%(value)s” har rett format (HH:MM[:ss[.uuuuuu]]), men er eit " -"ugyldig klokkeslett." msgid "Time" msgstr "Tid" @@ -631,14 +580,14 @@ msgid "URL" msgstr "Nettadresse" msgid "Raw binary data" -msgstr "Rå binærdata" +msgstr "" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "“%(value)s” er ikkje ein gyldig UUID." +msgstr "" msgid "Universally unique identifier" -msgstr "Universelt unik identifikator." +msgstr "" msgid "File" msgstr "Fil" @@ -646,29 +595,23 @@ msgstr "Fil" msgid "Image" msgstr "Bilete" -msgid "A JSON object" -msgstr "Eit JSON-objekt" - -msgid "Value must be valid JSON." -msgstr "Verdi må vere gyldig JSON." - #, python-format msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "%(model)s-instans med %(field)s %(value)r eksisterer ikkje." +msgstr "" msgid "Foreign Key (type determined by related field)" -msgstr "Fremmednøkkel (type bestemt av relatert felt)" +msgstr "Primærnøkkel (type bestemt av relatert felt)" msgid "One-to-one relationship" msgstr "Ein-til-ein-forhold" #, python-format msgid "%(from)s-%(to)s relationship" -msgstr "%(from)s-%(to)s-relasjon" +msgstr "" #, python-format msgid "%(from)s-%(to)s relationships" -msgstr "%(from)s-%(to)s-relasjonar" +msgstr "" msgid "Many-to-many relationship" msgstr "Mange-til-mange-forhold" @@ -677,13 +620,13 @@ msgstr "Mange-til-mange-forhold" #. characters will prevent the default label_suffix to be appended to the #. label msgid ":?.!" -msgstr ":?.!" +msgstr "" msgid "This field is required." msgstr "Feltet er påkravd." msgid "Enter a whole number." -msgstr "Oppgje eit heiltal." +msgstr "Oppgje eit heiltall." msgid "Enter a valid date." msgstr "Oppgje ein gyldig dato." @@ -695,11 +638,11 @@ msgid "Enter a valid date/time." msgstr "Oppgje gyldig dato og tidspunkt." msgid "Enter a valid duration." -msgstr "Oppgje ein gyldig varigskap." +msgstr "" #, python-brace-format msgid "The number of days must be between {min_days} and {max_days}." -msgstr "Antal dagar må vere mellom {min_days} og {max_days}." +msgstr "" msgid "No file was submitted. Check the encoding type on the form." msgstr "Inga fil vart sendt. Sjekk \"encoding\"-typen på skjemaet." @@ -715,9 +658,7 @@ msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." msgid_plural "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." msgstr[0] "" -"Filnamnet kan ikkje ha fleire enn %(max)d teikn (det har %(length)d)." msgstr[1] "" -"Filnamnet kan ikkje ha fleire enn %(max)d teikn (det har %(length)d)." msgid "Please either submit a file or check the clear checkbox, not both." msgstr "Last enten opp ei fil eller huk av i avkryssingsboksen." @@ -738,42 +679,33 @@ msgid "Enter a list of values." msgstr "Oppgje ei liste med verdiar." msgid "Enter a complete value." -msgstr "Oppgje ein fullstendig verdi." +msgstr "" msgid "Enter a valid UUID." -msgstr "Oppgje ein gyldig UUID." - -msgid "Enter a valid JSON." -msgstr "Oppgje gyldig JSON." +msgstr "" #. Translators: This is the default suffix added to form field labels msgid ":" -msgstr ":" +msgstr "" #, python-format msgid "(Hidden field %(name)s) %(error)s" -msgstr "(Gøymt felt %(name)s) %(error)s" - -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"ManagementForm data manglar eller har blitt tukla med. Felt som manglar: " -"%(field_names)s. Du burde kanskje sende ein feilrapport dersom problemet " -"fortset. " + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "Ver vennleg å ikkje sende inn fleire enn %d skjema. " -msgstr[1] "Ver vennleg å ikkje sende inn fleire enn %d skjema. " +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" +msgstr[1] "" #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "Ver vennleg å sende inn minst %d skjema. " -msgstr[1] "Ver vennleg å sende inn minst %d skjema. " +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" msgid "Order" msgstr "Rekkefølge" @@ -801,22 +733,20 @@ msgid "Please correct the duplicate values below." msgstr "Korriger dei dupliserte verdiane nedanfor." msgid "The inline value did not match the parent instance." -msgstr "Inline verdien stemmer ikkje overeins med forelder-instansen.  " +msgstr "" msgid "Select a valid choice. That choice is not one of the available choices." msgstr "Velg eit gyldig valg. Valget er ikkje eit av dei tilgjengelege valga." #, python-format msgid "“%(pk)s” is not a valid value." -msgstr "“%(pk)s” er ikkje ein gyldig verdi." +msgstr "" #, python-format msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " "may be ambiguous or it may not exist." msgstr "" -"%(datetime)s kunne ikkje bli tolka i tidssona %(current_timezone)s; Verdien " -"er anten tvetydig eller ugyldig." msgid "Clear" msgstr "Tøm" @@ -836,7 +766,15 @@ msgstr "Ja" msgid "No" msgstr "Nei" -#. Translators: Please do not add spaces around commas. +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + msgid "yes,no,maybe" msgstr "ja,nei,kanskje" @@ -1095,12 +1033,12 @@ msgid "December" msgstr "Desember" msgid "This is not a valid IPv6 address." -msgstr "Dette er ikkje ei gyldig IPv6-adresse." +msgstr "" #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "" msgid "or" msgstr "eller" @@ -1110,66 +1048,62 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d år" +msgstr[1] "%d år" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d månad" -msgstr[1] "%(num)d månader" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d månad" +msgstr[1] "%d månader" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d veke" -msgstr[1] "%(num)d veker" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d veke" +msgstr[1] "%d veker" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dagar" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dag" +msgstr[1] "%d dagar" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timar" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d time" +msgstr[1] "%d timar" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutt" -msgstr[1] "%(num)d minutt" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" +msgstr[1] "" + +msgid "0 minutes" +msgstr "0 minutt" msgid "Forbidden" -msgstr "Forbydd" +msgstr "" msgid "CSRF verification failed. Request aborted." -msgstr "CSRF-verifikasjon feila. Førespurnad avbrote." +msgstr "" msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Du ser denne meldinga på grunn av at det ikkje blei sendt nokon \"Referer\" " -"hovud frå din nettlesar, noko denne HTTPS-sida krev. Dette hovudet er eit " -"krav på grunn av sikkerheit, for å hindre at din nettlesar er kapra av " -"tredjepartar. " msgid "" "If you have configured your browser to disable “Referer” headers, please re-" "enable them, at least for this site, or for HTTPS connections, or for “same-" "origin” requests." msgstr "" -"Dersom du har konfigurert nettlesaren din til å deaktiverere \"Referer\"-" -"hovud må du aktivere dei på nytt, i det minste for denne nettsida, eller for " -"HTTPS-tilkoplingar eller for førespurnadar av same opphav. " msgid "" "If you are using the tag or " @@ -1178,38 +1112,26 @@ msgid "" "If you’re concerned about privacy, use alternatives like for links to third-party sites." msgstr "" -"Dersom du brukar -taggen " -"eller inkludera \"Referrer-Policy: no-referrer\" hovud, ver vennleg å fjerne " -"dei. CSRF-vern krev \"Referer\" hovud for å gjennomføre strenge kontrollar " -"av referer. Dersom du har bekymringar for personvern bruk alternativ som for lenkjer til tredepartssider" msgid "" "You are seeing this message because this site requires a CSRF cookie when " "submitting forms. This cookie is required for security reasons, to ensure " "that your browser is not being hijacked by third parties." msgstr "" -"Du ser denne meldinga fordi denne nettsida krev ein CSRF informasjonskapsel " -"når du sender inn skjema. Denne informasjonskapselen er eit krav på grunn av " -"sikkerheit, for å forsikre at nettlesaren din ikkje er kapra av " -"tredjepartar. " msgid "" "If you have configured your browser to disable cookies, please re-enable " "them, at least for this site, or for “same-origin” requests." msgstr "" -"Dersom du har konfigurert nettlesaren din til å deaktivere " -"informasjonskapslar, ver vennleg å aktiver dei på nytt, i det minste for " -"denne nettsida, eller for førespurnader av same opphav. " msgid "More information is available with DEBUG=True." -msgstr "Meir informasjon er tilgjengeleg med DEBUG=True." +msgstr "" msgid "No year specified" msgstr "Årstal ikkje spesifisert" msgid "Date out of range" -msgstr "Dato er utanfor rekkjevidde" +msgstr "" msgid "No month specified" msgstr "Månad ikkje spesifisert" @@ -1234,7 +1156,7 @@ msgstr "" #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "Ugyldig datostreng \"%(datestr)s\" grunna format \"%(format)s\"" +msgstr "" #, python-format msgid "No %(verbose_name)s found matching the query" @@ -1242,37 +1164,37 @@ msgstr "Fann ingen %(verbose_name)s som korresponderte med spørringa" msgid "Page is not “last”, nor can it be converted to an int." msgstr "" -"Sida er ikkje \"last\" og den kan heller ikkje konverterast til eit heiltal. " #, python-format msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "Ugyldig side (%(page_number)s): %(message)s" +msgstr "" #, python-format msgid "Empty list and “%(class_name)s.allow_empty” is False." -msgstr "Tom liste og \"%(class_name)s.allow_empty\" er False." +msgstr "" msgid "Directory indexes are not allowed here." msgstr "Mappeindeksar er ikkje tillate her." #, python-format msgid "“%(path)s” does not exist" -msgstr "\"%(path)s\" eksisterer ikkje" +msgstr "" #, python-format msgid "Index of %(directory)s" msgstr "Indeks for %(directory)s" -msgid "The install worked successfully! Congratulations!" -msgstr "Installasjonen var vellykka! Gratulerer!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" #, python-format msgid "" "View release notes for Django %(version)s" msgstr "" -"Sjå utgjevingsnotat for Django %(version)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "" #, python-format msgid "" @@ -1281,25 +1203,21 @@ msgid "" "\">DEBUG=True is in your settings file and you have not configured any " "URLs." msgstr "" -"Du ser denne sida fordi DEBUG=True er i innstillingsfila di og du ikkje har konfigurert noka " -"nettadresser." msgid "Django Documentation" -msgstr "Django-dokumentasjon" +msgstr "" msgid "Topics, references, & how-to’s" -msgstr "Tema, referansar, & how-tos" +msgstr "" msgid "Tutorial: A Polling App" -msgstr "Opplæring: Ein avstemmingsapp" +msgstr "" msgid "Get started with Django" -msgstr "Kom i gang med Django" +msgstr "" msgid "Django Community" -msgstr "Django Nettsamfunn" +msgstr "" msgid "Connect, get help, or contribute" -msgstr "Koble, få hjelp, eller bidra" +msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/nn/formats.py b/venv/Lib/site-packages/django/conf/locale/nn/formats.py index 0ddb8fe..91dd9e6 100644 --- a/venv/Lib/site-packages/django/conf/locale/nn/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/nn/formats.py @@ -2,40 +2,35 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j. F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - # "%d. %b %Y", # '25. okt 2006' - # "%d %b %Y", # '25 okt 2006' - # "%d. %b. %Y", # '25. okt. 2006' - # "%d %b. %Y", # '25 okt. 2006' - # "%d. %B %Y", # '25. oktober 2006' - # "%d %B %Y", # '25 oktober 2006' + '%Y-%m-%d', '%d.%m.%Y', '%d.%m.%y', # '2006-10-25', '25.10.2006', '25.10.06' + # '%d. %b %Y', '%d %b %Y', # '25. okt 2006', '25 okt 2006' + # '%d. %b. %Y', '%d %b. %Y', # '25. okt. 2006', '25 okt. 2006' + # '%d. %B %Y', '%d %B %Y', # '25. oktober 2006', '25 oktober 2006' ] 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' - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' + '%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' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.mo index 0d57432..790751b 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.po index fba6dfb..67c6609 100644 --- a/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/pl/LC_MESSAGES/django.po @@ -31,8 +31,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 23:25+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-01 19:45+0000\n" "Last-Translator: m_aciek \n" "Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" "MIME-Version: 1.0\n" @@ -232,9 +232,6 @@ msgstr "mongolski" msgid "Marathi" msgstr "marathi" -msgid "Malay" -msgstr "malajski" - msgid "Burmese" msgstr "birmański" @@ -1172,52 +1169,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rok" -msgstr[1] "%(num)d lata" -msgstr[2] "%(num)d lat" -msgstr[3] "%(num)d roku" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d lata" +msgstr[2] "%d lat" +msgstr[3] "%d lat" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d miesiąc" -msgstr[1] "%(num)d miesiące" -msgstr[2] "%(num)d miesięcy" -msgstr[3] "%(num)d miesiąca" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d miesiąc" +msgstr[1] "%d miesiące" +msgstr[2] "%d miesięcy" +msgstr[3] "%d miesięcy" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydzień" -msgstr[1] "%(num)d tygodnie" -msgstr[2] "%(num)d tygodni" -msgstr[3] "%(num)d tygodnia" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydzień" +msgstr[1] "%d tygodnie" +msgstr[2] "%d tygodni" +msgstr[3] "%d tygodni" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dzień" -msgstr[1] "%(num)d dni" -msgstr[2] "%(num)d dni" -msgstr[3] "%(num)d dnia" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dzień" +msgstr[1] "%d dni" +msgstr[2] "%d dni" +msgstr[3] "%d dni" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d godzina" -msgstr[1] "%(num)d godziny" -msgstr[2] "%(num)d godzin" -msgstr[3] "%(num)d godziny" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d godzina" +msgstr[1] "%d godziny" +msgstr[2] "%d godzin" +msgstr[3] "%d godzin" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuty" -msgstr[2] "%(num)d minut" -msgstr[3] "%(num)d minut" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minuty" +msgstr[2] "%d minut" +msgstr[3] "%d minut" msgid "Forbidden" msgstr "Dostęp zabroniony" @@ -1227,14 +1224,14 @@ msgstr "Weryfikacja CSRF nie powiodła się. Żądanie zostało przerwane." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" "Widzisz tę wiadomość, ponieważ ta witryna HTTPS wymaga, aby przeglądarka " -"wysłała „nagłówek Referer”, a żaden nie został wysłany. Nagłówek ten jest " -"wymagany ze względów bezpieczeństwa, aby upewnić się, że twoja przeglądarka " -"nie została przechwycona przez osoby trzecie." +"wysłała nagłówek „Referer header”, a żaden nie został wysłany. Nagłówek ten " +"jest wymagany ze względów bezpieczeństwa, aby upewnić się, że Twoja " +"przeglądarka nie została przechwycona przez osoby trzecie." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/pl/formats.py b/venv/Lib/site-packages/django/conf/locale/pl/formats.py index 2ad1bfe..e666544 100644 --- a/venv/Lib/site-packages/django/conf/locale/pl/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/pl/formats.py @@ -2,29 +2,27 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j E Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j E Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j E" -SHORT_DATE_FORMAT = "d-m-Y" -SHORT_DATETIME_FORMAT = "d-m-Y H:i" +DATE_FORMAT = 'j E Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j E Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j E' +SHORT_DATE_FORMAT = 'd-m-Y' +SHORT_DATETIME_FORMAT = 'd-m-Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - "%y-%m-%d", # '06-10-25' - # "%d. %B %Y", # '25. października 2006' - # "%d. %b. %Y", # '25. paź. 2006' + '%d.%m.%Y', '%d.%m.%y', # '25.10.2006', '25.10.06' + '%y-%m-%d', # '06-10-25' + # '%d. %B %Y', '%d. %b. %Y', # '25. October 2006', '25. Oct. 2006' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = " " +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = ' ' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/pt/formats.py b/venv/Lib/site-packages/django/conf/locale/pt/formats.py index bb4b3f5..b0fbbad 100644 --- a/venv/Lib/site-packages/django/conf/locale/pt/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/pt/formats.py @@ -2,38 +2,34 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y à\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y à\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 0 # Sunday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - # "%d de %b de %Y", # '25 de Out de 2006' - # "%d de %b, %Y", # '25 Out, 2006' - # "%d de %B de %Y", # '25 de Outubro de 2006' - # "%d de %B, %Y", # '25 de Outubro, 2006' + '%Y-%m-%d', '%d/%m/%Y', '%d/%m/%y', # '2006-10-25', '25/10/2006', '25/10/06' + # '%d de %b de %Y', '%d de %b, %Y', # '25 de Out de 2006', '25 Out, 2006' + # '%d de %B de %Y', '%d de %B, %Y', # '25 de Outubro de 2006', '25 de Outubro, 2006' ] 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' - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' + '%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' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.mo index f462741..556d475 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.po index 2815d41..a039f91 100644 --- a/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ # Arthur Silva , 2017 # bruno.devpod , 2014 # Camilo B. Moreira , 2017 -# Carlos Leite , 2020 -# Carlos Leite , 2016,2019 +# Carlos C. Leite , 2020 +# Carlos C. Leite , 2016,2019 # Filipe Cifali Stangler , 2016 # Claudio Rogerio Carvalho Filho , 2020 # dudanogueira , 2012 @@ -25,7 +25,6 @@ # Lucas Infante , 2015 # Luiz Boaretto , 2017 # Marcelo Moro Brondani , 2018 -# Mariusz Felisiak , 2021 # Rafael Fontenelle , 2021 # Samuel Nogueira Bacelar , 2020 # Sandro , 2011 @@ -37,9 +36,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:29+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-17 05:42+0000\n" +"Last-Translator: Rafael Fontenelle \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/django/django/" "language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -237,9 +236,6 @@ msgstr "Mongol" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Birmanês" @@ -536,7 +532,7 @@ msgid "" "format." msgstr "" "O valor \"%(value)s\" tem um formato de data inválido. Deve ser no formato " -"YYYY-MM-DD." +"YYY-MM-DD." #, python-format msgid "" @@ -1134,7 +1130,7 @@ msgstr "Este não é um endereço IPv6 válido." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr " %(truncated_text)s…" +msgstr " %(truncated_text)s..." msgid "or" msgstr "ou" @@ -1144,40 +1140,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d ano" +msgstr[1] "%d anos" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mês" +msgstr[1] "%d meses" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dia" +msgstr[1] "%d dias" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" msgid "Forbidden" msgstr "Proibido" @@ -1187,10 +1183,14 @@ msgstr "Verificação CSRF falhou. Pedido cancelado." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Você está vendo esta mensagem porque este site HTTPS requer que um “Referer " +"header” seja enviado pelo seu Web browser, mas nenhum foi enviado. Este " +"cabeçalho é requierido por razões de segurança, para assegurar que seu " +"browser não foi sequestrado por terceiros." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/pt_BR/formats.py b/venv/Lib/site-packages/django/conf/locale/pt_BR/formats.py index 96a49b4..ed0c09f 100644 --- a/venv/Lib/site-packages/django/conf/locale/pt_BR/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/pt_BR/formats.py @@ -2,33 +2,30 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j \d\e F \d\e Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"j \d\e F \d\e Y à\s H:i" -YEAR_MONTH_FORMAT = r"F \d\e Y" -MONTH_DAY_FORMAT = r"j \d\e F" -SHORT_DATE_FORMAT = "d/m/Y" -SHORT_DATETIME_FORMAT = "d/m/Y H:i" +DATE_FORMAT = r'j \d\e F \d\e Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'j \d\e F \d\e Y à\s H:i' +YEAR_MONTH_FORMAT = r'F \d\e Y' +MONTH_DAY_FORMAT = r'j \d\e F' +SHORT_DATE_FORMAT = 'd/m/Y' +SHORT_DATETIME_FORMAT = 'd/m/Y H:i' FIRST_DAY_OF_WEEK = 0 # Sunday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # '25/10/2006' - "%d/%m/%y", # '25/10/06' - # "%d de %b de %Y", # '24 de Out de 2006' - # "%d de %b, %Y", # '25 Out, 2006' - # "%d de %B de %Y", # '25 de Outubro de 2006' - # "%d de %B, %Y", # '25 de Outubro, 2006' + '%d/%m/%Y', '%d/%m/%y', # '25/10/2006', '25/10/06' + # '%d de %b de %Y', '%d de %b, %Y', # '25 de Out de 2006', '25 Out, 2006' + # '%d de %B de %Y', '%d de %B, %Y', # '25 de Outubro de 2006', '25 de Outubro, 2006' ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59' - "%d/%m/%Y %H:%M:%S.%f", # '25/10/2006 14:30:59.000200' - "%d/%m/%Y %H:%M", # '25/10/2006 14:30' - "%d/%m/%y %H:%M:%S", # '25/10/06 14:30:59' - "%d/%m/%y %H:%M:%S.%f", # '25/10/06 14:30:59.000200' - "%d/%m/%y %H:%M", # '25/10/06 14:30' + '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59' + '%d/%m/%Y %H:%M:%S.%f', # '25/10/2006 14:30:59.000200' + '%d/%m/%Y %H:%M', # '25/10/2006 14:30' + '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59' + '%d/%m/%y %H:%M:%S.%f', # '25/10/06 14:30:59.000200' + '%d/%m/%y %H:%M', # '25/10/06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ro/formats.py b/venv/Lib/site-packages/django/conf/locale/ro/formats.py index 5a0c173..8cefeb8 100644 --- a/venv/Lib/site-packages/django/conf/locale/ro/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ro/formats.py @@ -2,34 +2,34 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j F Y, H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y, H:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j F Y, H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y, H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", - "%d.%b.%Y", - "%d %B %Y", - "%A, %d %B %Y", + '%d.%m.%Y', + '%d.%b.%Y', + '%d %B %Y', + '%A, %d %B %Y', ] TIME_INPUT_FORMATS = [ - "%H:%M", - "%H:%M:%S", - "%H:%M:%S.%f", + '%H:%M', + '%H:%M:%S', + '%H:%M:%S.%f', ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y, %H:%M", - "%d.%m.%Y, %H:%M:%S", - "%d.%B.%Y, %H:%M", - "%d.%B.%Y, %H:%M:%S", + '%d.%m.%Y, %H:%M', + '%d.%m.%Y, %H:%M:%S', + '%d.%B.%Y, %H:%M', + '%d.%B.%Y, %H:%M:%S', ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.mo index 742b2ce..75c5572 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.po index 7c202fe..efc6812 100644 --- a/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,6 @@ # Dimmus , 2011 # eigrad , 2012 # Eugene , 2013 -# Eugene Morozov , 2021 # eXtractor , 2015 # crazyzubr , 2020 # Igor Melnyk, 2014 @@ -15,7 +14,7 @@ # Jannis Leidel , 2011 # lilo.panic, 2016 # Mikhail Zholobov , 2013 -# Nikolay Korotkiy , 2018 +# Nikolay Korotkiy , 2018 # Panasoft, 2021 # Вася Аникин , 2017 # SeryiMysh , 2020 @@ -25,9 +24,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-16 22:50+0000\n" +"Last-Translator: Panasoft\n" "Language-Team: Russian (http://www.transifex.com/django/django/language/" "ru/)\n" "MIME-Version: 1.0\n" @@ -227,9 +226,6 @@ msgstr "Монгольский" msgid "Marathi" msgstr "Маратхи" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Бирманский" @@ -712,7 +708,7 @@ msgstr "Связь %(from)s-%(to)s" #, python-format msgid "%(from)s-%(to)s relationships" -msgstr "Связи %(from)s-%(to)s" +msgstr "Связьи %(from)s-%(to)s" msgid "Many-to-many relationship" msgstr "Связь \"многие ко многим\"" @@ -1172,52 +1168,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d год" +msgstr[1] "%d года" +msgstr[2] "%d лет" +msgstr[3] "%d лет" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месяц" +msgstr[1] "%d месяца" +msgstr[2] "%d месяцев" +msgstr[3] "%d месяцев" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d неделя" +msgstr[1] "%d недели" +msgstr[2] "%d недель" +msgstr[3] "%d недель" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d день" +msgstr[1] "%d дня" +msgstr[2] "%d дней" +msgstr[3] "%d дней" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d час" +msgstr[1] "%d часа" +msgstr[2] "%d часов" +msgstr[3] "%d часов" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d минута" +msgstr[1] "%d минуты" +msgstr[2] "%d минут" +msgstr[3] "%d минут" msgid "Forbidden" msgstr "Ошибка доступа" @@ -1227,10 +1223,15 @@ msgstr "Ошибка проверки CSRF. Запрос отклонён." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Вы видите это сообщение, потому что данный сайт использует защищённое " +"соединение и требует, чтобы заголовок “Referer” был передан вашим браузером, " +"но он не был им передан. Данный заголовок необходим по соображениям " +"безопасности, чтобы убедиться, что ваш браузер не был взломан, а запрос к " +"серверу не был перехвачен или подменён." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/ru/formats.py b/venv/Lib/site-packages/django/conf/locale/ru/formats.py index 212e526..c601c3e 100644 --- a/venv/Lib/site-packages/django/conf/locale/ru/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ru/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j E Y г." -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j E Y г. G:i" -YEAR_MONTH_FORMAT = "F Y г." -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j E Y г.' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j E Y г. G:i' +YEAR_MONTH_FORMAT = 'F Y г.' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y', # '25.10.06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.mo index 464b66d..f27fa2a 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.po index f95abc0..01edca7 100644 --- a/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/sk/LC_MESSAGES/django.po @@ -6,14 +6,13 @@ # Marian Andre , 2013,2015,2017-2018 # Martin Kosír, 2011 # Martin Tóth , 2017 -# Peter Kuma, 2021 # Peter Stríž , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 12:25+0000\n" "Last-Translator: Transifex Bot <>\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" @@ -30,7 +29,7 @@ msgid "Arabic" msgstr "arabský" msgid "Algerian Arabic" -msgstr "alžírsky arabsky" +msgstr "" msgid "Asturian" msgstr "astúrsky" @@ -147,7 +146,7 @@ msgid "Hungarian" msgstr "maďarsky" msgid "Armenian" -msgstr "arménsky" +msgstr "Arménsky" msgid "Interlingua" msgstr "interlinguánsky" @@ -156,7 +155,7 @@ msgid "Indonesian" msgstr "indonézsky" msgid "Igbo" -msgstr "igbožsky" +msgstr "" msgid "Ido" msgstr "ido" @@ -189,7 +188,7 @@ msgid "Korean" msgstr "kórejsky" msgid "Kyrgyz" -msgstr "kirgizsky" +msgstr "" msgid "Luxembourgish" msgstr "luxembursky" @@ -212,9 +211,6 @@ msgstr "mongolsky" msgid "Marathi" msgstr "maráthsky" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "barmsky" @@ -279,13 +275,13 @@ msgid "Telugu" msgstr "telugsky" msgid "Tajik" -msgstr "tadžiksky" +msgstr "" msgid "Thai" msgstr "thajsky" msgid "Turkmen" -msgstr "turkménsky" +msgstr "" msgid "Turkish" msgstr "turecky" @@ -303,7 +299,7 @@ msgid "Urdu" msgstr "urdsky" msgid "Uzbek" -msgstr "uzbecky" +msgstr "" msgid "Vietnamese" msgstr "vietnamsky" @@ -329,7 +325,7 @@ msgstr "Syndikácia" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "…" +msgstr "" msgid "That page number is not an integer" msgstr "Číslo stránky nie je celé číslo" @@ -356,15 +352,11 @@ msgstr "Zadajte platnú e-mailovú adresu." msgid "" "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." msgstr "" -"Zadajte platnú skratku pozostávajúcu z písmen, čísel, podčiarkovníkov alebo " -"pomlčiek." msgid "" "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " "hyphens." msgstr "" -"Zadajte platnú skratku pozostávajúcu z písmen Unicode, čísel, " -"podčiarkovníkov alebo pomlčiek." msgid "Enter a valid IPv4 address." msgstr "Zadajte platnú IPv4 adresu." @@ -550,16 +542,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare RRRR-MM-DD HH:MM[:" -"ss[.uuuuuu]][ČZ]." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" "[TZ]) but it is an invalid date/time." msgstr "" -"Hodnota “%(value)s” má byť v tvare (RRRR-MM-DD HH:MM[:ss[.uuuuuu]][ČZ]), ale " -"toto je neplatný dátum/čas." msgid "Date (with time)" msgstr "Dátum (a čas)" @@ -576,8 +564,6 @@ msgid "" "“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." "uuuuuu] format." msgstr "" -"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare [DD] [[HH:]MM:]ss[." -"uuuuuu]." msgid "Duration" msgstr "Doba trvania" @@ -590,7 +576,7 @@ msgstr "Cesta k súboru" #, python-format msgid "“%(value)s” value must be a float." -msgstr "Hodnota “%(value)s” musí byť desatinné číslo." +msgstr "" msgid "Floating point number" msgstr "Číslo s plávajúcou desatinnou čiarkou" @@ -616,13 +602,13 @@ msgstr "IP adresa" #, python-format msgid "“%(value)s” value must be either None, True or False." -msgstr "Hodnota “%(value)s” musí byť buď None, True alebo False." +msgstr "" msgid "Boolean (Either True, False or None)" msgstr "Logická hodnota (buď True, False alebo None)" msgid "Positive big integer" -msgstr "Kladné veľké celé číslo" +msgstr "" msgid "Positive integer" msgstr "Kladné celé číslo" @@ -642,15 +628,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare HH:MM[:ss[.uuuuuu]]." #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"Hodnota “%(value)s” má mať správny tvar (HH:MM[:ss[.uuuuuu]]), ale toto je " -"neplatný čas." msgid "Time" msgstr "Čas" @@ -663,7 +646,7 @@ msgstr "Binárne údaje" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "“%(value)s” nie je platné UUID." +msgstr "" msgid "Universally unique identifier" msgstr "Úplne všade jedinečný identifikátor" @@ -792,25 +775,22 @@ msgid "" "ManagementForm data is missing or has been tampered with. Missing fields: " "%(field_names)s. You may need to file a bug report if the issue persists." msgstr "" -"Dáta ManagementForm chýbajú alebo boli zmanipulované. Chýbajúce polia: " -"%(field_names)s. Možno budete musieť túto chybu nahlásiť, ak sa bude naďalej " -"vyskytovať." #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Prosím odošlite najviac %d formulár." -msgstr[1] "Prosím odošlite najviac %d formulárov." -msgstr[2] "Prosím odošlite najviac %d formulárov." -msgstr[3] "Prosím odošlite najviac %d formulárov." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Prosím odošlite aspoň %d formulár." -msgstr[1] "Prosím odošlite aspoň %d formulárov." -msgstr[2] "Prosím odošlite aspoň %d formulárov." -msgstr[3] "Prosím odošlite aspoň %d formulárov." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" msgid "Order" msgstr "Poradie" @@ -846,15 +826,13 @@ msgstr "" #, python-format msgid "“%(pk)s” is not a valid value." -msgstr "\"%(pk)s\" nie je platná hodnota." +msgstr "" #, python-format msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " "may be ambiguous or it may not exist." msgstr "" -"Hodnota %(datetime)s v časovej zóne %(current_timezone)s sa nedá " -"interpretovať; môže byť nejednoznačná alebo nemusí existovať." msgid "Clear" msgstr "Vymazať" @@ -1140,7 +1118,7 @@ msgstr "Toto nieje platná IPv6 adresa." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s..." +msgstr "" msgid "or" msgstr "alebo" @@ -1150,52 +1128,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d roky" +msgstr[2] "%d rokov" +msgstr[3] "%d rokov" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mesiac" +msgstr[1] "%d mesiace" +msgstr[2] "%d mesiacov" +msgstr[3] "%d mesiacov" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d týždeň" +msgstr[1] "%d týždne" +msgstr[2] "%d týždňov" +msgstr[3] "%d týždňov" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d deň" +msgstr[1] "%d dni" +msgstr[2] "%d dní" +msgstr[3] "%d dní" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodina" +msgstr[1] "%d hodiny" +msgstr[2] "%d hodín" +msgstr[3] "%d hodín" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minúta" +msgstr[1] "%d minúty" +msgstr[2] "%d minút" +msgstr[3] "%d minút" msgid "Forbidden" msgstr "Zakázané (Forbidden)" @@ -1205,7 +1183,7 @@ msgstr "CSRF verifikázia zlyhala. Požiadavka bola prerušená." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1215,9 +1193,6 @@ msgid "" "enable them, at least for this site, or for HTTPS connections, or for “same-" "origin” requests." msgstr "" -"Ak ste vo vašom prehliadači vypli hlavičky “Referer”, tak ich prosím " -"zapnite, alebo aspoň pre túto stránku, alebo pre HTTPS pripojenia, alebo pre " -"požiadavky “same-origin”." msgid "" "If you are using the tag or " @@ -1226,11 +1201,6 @@ msgid "" "If you’re concerned about privacy, use alternatives like for links to third-party sites." msgstr "" -"Ak používate tag , alebo " -"vkladáte hlavičku 'Referrer-Policy: no-referrer', prosím odstránte ich. " -"Ochrana CSRF vyžaduje hlavičku “Referer” na striktnú kontrolu. Ak máte obavy " -"o súkromie, použite alternatívy ako pre linky na " -"iné stránky." msgid "" "You are seeing this message because this site requires a CSRF cookie when " @@ -1245,8 +1215,6 @@ msgid "" "If you have configured your browser to disable cookies, please re-enable " "them, at least for this site, or for “same-origin” requests." msgstr "" -"Ak ste vypli cookies vo vašom prehliadači, tak ich prosím zapnite, aspoň pre " -"túto stránku, alebo pre požiadavky “same-origin”." msgid "More information is available with DEBUG=True." msgstr "Viac informácií bude dostupných s DEBUG=True." @@ -1280,7 +1248,7 @@ msgstr "" #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "Neplatný dátumový reťazec “%(datestr)s” pre formát “%(format)s”" +msgstr "" #, python-format msgid "No %(verbose_name)s found matching the query" @@ -1289,8 +1257,6 @@ msgstr "" msgid "Page is not “last”, nor can it be converted to an int." msgstr "" -"Stránka nemá hodnotu “last” a taktiež nie je možné prekonvertovať hodnotu na " -"celé číslo." #, python-format msgid "Invalid page (%(page_number)s): %(message)s" @@ -1298,14 +1264,14 @@ msgstr "Nesprávna stránka (%(page_number)s): %(message)s" #, python-format msgid "Empty list and “%(class_name)s.allow_empty” is False." -msgstr "Zoznam je prázdny a hodnota “%(class_name)s.allow_empty” je False." +msgstr "" msgid "Directory indexes are not allowed here." msgstr "Výpis adresárov tu nieje povolený." #, python-format msgid "“%(path)s” does not exist" -msgstr "\"%(path)s\" neexistuje" +msgstr "" #, python-format msgid "Index of %(directory)s" @@ -1339,7 +1305,7 @@ msgid "Django Documentation" msgstr "Dokumentácia Django" msgid "Topics, references, & how-to’s" -msgstr "Témy, referencie a návody" +msgstr "" msgid "Tutorial: A Polling App" msgstr "Tutoriál: Aplikácia \"Hlasovania\"" diff --git a/venv/Lib/site-packages/django/conf/locale/sk/formats.py b/venv/Lib/site-packages/django/conf/locale/sk/formats.py index 31d4912..2052641 100644 --- a/venv/Lib/site-packages/django/conf/locale/sk/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sk/formats.py @@ -2,29 +2,27 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j. F Y G:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y G:i" +DATE_FORMAT = 'j. F Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j. F Y G:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y G:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - "%y-%m-%d", # '06-10-25' - # "%d. %B %Y", # '25. October 2006' - # "%d. %b. %Y", # '25. Oct. 2006' + '%d.%m.%Y', '%d.%m.%y', # '25.10.2006', '25.10.06' + '%y-%m-%d', # '06-10-25' + # '%d. %B %Y', '%d. %b. %Y', # '25. October 2006', '25. Oct. 2006' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/sl/formats.py b/venv/Lib/site-packages/django/conf/locale/sl/formats.py index c3e96bb..35de5ad 100644 --- a/venv/Lib/site-packages/django/conf/locale/sl/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sl/formats.py @@ -2,43 +2,41 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d. F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y. H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "j. M. Y" -SHORT_DATETIME_FORMAT = "j.n.Y. H:i" +DATE_FORMAT = 'd. F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y. H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'j. M. Y' +SHORT_DATETIME_FORMAT = 'j.n.Y. H:i' FIRST_DAY_OF_WEEK = 0 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' - "%d-%m-%Y", # '25-10-2006' - "%d. %m. %Y", # '25. 10. 2006' - "%d. %m. %y", # '25. 10. 06' + '%d.%m.%Y', '%d.%m.%y', # '25.10.2006', '25.10.06' + '%d-%m-%Y', # '25-10-2006' + '%d. %m. %Y', '%d. %m. %y', # '25. 10. 2006', '25. 10. 06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d-%m-%Y %H:%M:%S", # '25-10-2006 14:30:59' - "%d-%m-%Y %H:%M:%S.%f", # '25-10-2006 14:30:59.000200' - "%d-%m-%Y %H:%M", # '25-10-2006 14:30' - "%d. %m. %Y %H:%M:%S", # '25. 10. 2006 14:30:59' - "%d. %m. %Y %H:%M:%S.%f", # '25. 10. 2006 14:30:59.000200' - "%d. %m. %Y %H:%M", # '25. 10. 2006 14:30' - "%d. %m. %y %H:%M:%S", # '25. 10. 06 14:30:59' - "%d. %m. %y %H:%M:%S.%f", # '25. 10. 06 14:30:59.000200' - "%d. %m. %y %H:%M", # '25. 10. 06 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d-%m-%Y %H:%M:%S', # '25-10-2006 14:30:59' + '%d-%m-%Y %H:%M:%S.%f', # '25-10-2006 14:30:59.000200' + '%d-%m-%Y %H:%M', # '25-10-2006 14:30' + '%d. %m. %Y %H:%M:%S', # '25. 10. 2006 14:30:59' + '%d. %m. %Y %H:%M:%S.%f', # '25. 10. 2006 14:30:59.000200' + '%d. %m. %Y %H:%M', # '25. 10. 2006 14:30' + '%d. %m. %y %H:%M:%S', # '25. 10. 06 14:30:59' + '%d. %m. %y %H:%M:%S.%f', # '25. 10. 06 14:30:59.000200' + '%d. %m. %y %H:%M', # '25. 10. 06 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.mo index b2cb851..0a1e5e2 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.po index bc706f4..82ac0d0 100644 --- a/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/sq/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 09:58+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 13:59+0000\n" "Last-Translator: Besnik Bleta \n" "Language-Team: Albanian (http://www.transifex.com/django/django/language/" "sq/)\n" @@ -209,9 +209,6 @@ msgstr "Mongoliane" msgid "Marathi" msgstr "Marati" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Burmeze" @@ -1125,40 +1122,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d vit" -msgstr[1] "%(num)d vjet" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d vit" +msgstr[1] "%d vjet" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d muaj" -msgstr[1] "%(num)d muaj" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d muaj" +msgstr[1] "%d muaj" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d javë" -msgstr[1] "%(num)d javë" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d javë" +msgstr[1] "%d javë" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d ditë" -msgstr[1] "%(num)d ditë" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d ditë" +msgstr[1] "%d ditë" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d orë" -msgstr[1] "%(num)d orë" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d orë" +msgstr[1] "%d orë" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutë" -msgstr[1] "%(num)d minuta" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutë" +msgstr[1] "%d minuta" msgid "Forbidden" msgstr "E ndaluar" @@ -1168,7 +1165,7 @@ msgstr "Verifikimi CSRF dështoi. Kërkesa u ndërpre." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/sq/formats.py b/venv/Lib/site-packages/django/conf/locale/sq/formats.py index c7ed92e..2f0da0d 100644 --- a/venv/Lib/site-packages/django/conf/locale/sq/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sq/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d F Y" -TIME_FORMAT = "g.i.A" +DATE_FORMAT = 'd F Y' +TIME_FORMAT = 'g.i.A' # DATETIME_FORMAT = -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "Y-m-d" +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'Y-m-d' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = @@ -16,6 +16,6 @@ SHORT_DATE_FORMAT = "Y-m-d" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.mo index 6743f6b..9e02bf0 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.po index 5ba534d..de472f4 100644 --- a/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/sr/LC_MESSAGES/django.po @@ -5,14 +5,13 @@ # Igor Jerosimić, 2019-2021 # Jannis Leidel , 2011 # Janos Guljas , 2011-2012 -# Mariusz Felisiak , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:29+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 17:16+0000\n" +"Last-Translator: Igor Jerosimić\n" "Language-Team: Serbian (http://www.transifex.com/django/django/language/" "sr/)\n" "MIME-Version: 1.0\n" @@ -211,9 +210,6 @@ msgstr "монголски" msgid "Marathi" msgstr "маратхи" -msgid "Malay" -msgstr "малајски" - msgid "Burmese" msgstr "бурмански" @@ -1122,7 +1118,7 @@ msgstr "Ово није валидна IPv6 адреса." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "или" @@ -1132,46 +1128,46 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d година" -msgstr[1] "%(num)d године" -msgstr[2] "%(num)d година" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d година" +msgstr[1] "%d године" +msgstr[2] "%d година" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месец" -msgstr[1] "%(num)d месеца" -msgstr[2] "%(num)d месеци" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месец" +msgstr[1] "%d месеца" +msgstr[2] "%d месеци" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d недеља" -msgstr[1] "%(num)d недеље" -msgstr[2] "%(num)d недеља" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d недеља" +msgstr[1] "%d недеље" +msgstr[2] "%d недеља" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d дан" -msgstr[1] "%(num)d дана" -msgstr[2] "%(num)d дана" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d дан" +msgstr[1] "%d дана" +msgstr[2] "%d дана" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d сат" -msgstr[1] "%(num)d сата" -msgstr[2] "%(num)d сати" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d час" +msgstr[1] "%d часа" +msgstr[2] "%d часова" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d минут" -msgstr[1] "%(num)d минута" -msgstr[2] "%(num)d минута" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d минут" +msgstr[1] "%d минута" +msgstr[2] "%d минута" msgid "Forbidden" msgstr "Забрањено" @@ -1181,7 +1177,7 @@ msgstr "CSRF верификација није прошла. Захтев одб msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" diff --git a/venv/Lib/site-packages/django/conf/locale/sr/formats.py b/venv/Lib/site-packages/django/conf/locale/sr/formats.py index 423f86d..937a409 100644 --- a/venv/Lib/site-packages/django/conf/locale/sr/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sr/formats.py @@ -2,43 +2,38 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y." -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y. H:i" -YEAR_MONTH_FORMAT = "F Y." -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "j.m.Y." -SHORT_DATETIME_FORMAT = "j.m.Y. H:i" +DATE_FORMAT = 'j. F Y.' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y. H:i' +YEAR_MONTH_FORMAT = 'F Y.' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'j.m.Y.' +SHORT_DATETIME_FORMAT = 'j.m.Y. H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y.", # '25.10.2006.' - "%d.%m.%y.", # '25.10.06.' - "%d. %m. %Y.", # '25. 10. 2006.' - "%d. %m. %y.", # '25. 10. 06.' - # "%d. %b %y.", # '25. Oct 06.' - # "%d. %B %y.", # '25. October 06.' - # "%d. %b '%y.", # '25. Oct '06.' - # "%d. %B '%y.", # '25. October '06.' - # "%d. %b %Y.", # '25. Oct 2006.' - # "%d. %B %Y.", # '25. October 2006.' + '%d.%m.%Y.', '%d.%m.%y.', # '25.10.2006.', '25.10.06.' + '%d. %m. %Y.', '%d. %m. %y.', # '25. 10. 2006.', '25. 10. 06.' + # '%d. %b %y.', '%d. %B %y.', # '25. Oct 06.', '25. October 06.' + # '%d. %b \'%y.', '%d. %B \'%y.', # '25. Oct '06.', '25. October '06.' + # '%d. %b %Y.', '%d. %B %Y.', # '25. Oct 2006.', '25. October 2006.' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y. %H:%M:%S", # '25.10.2006. 14:30:59' - "%d.%m.%Y. %H:%M:%S.%f", # '25.10.2006. 14:30:59.000200' - "%d.%m.%Y. %H:%M", # '25.10.2006. 14:30' - "%d.%m.%y. %H:%M:%S", # '25.10.06. 14:30:59' - "%d.%m.%y. %H:%M:%S.%f", # '25.10.06. 14:30:59.000200' - "%d.%m.%y. %H:%M", # '25.10.06. 14:30' - "%d. %m. %Y. %H:%M:%S", # '25. 10. 2006. 14:30:59' - "%d. %m. %Y. %H:%M:%S.%f", # '25. 10. 2006. 14:30:59.000200' - "%d. %m. %Y. %H:%M", # '25. 10. 2006. 14:30' - "%d. %m. %y. %H:%M:%S", # '25. 10. 06. 14:30:59' - "%d. %m. %y. %H:%M:%S.%f", # '25. 10. 06. 14:30:59.000200' - "%d. %m. %y. %H:%M", # '25. 10. 06. 14:30' + '%d.%m.%Y. %H:%M:%S', # '25.10.2006. 14:30:59' + '%d.%m.%Y. %H:%M:%S.%f', # '25.10.2006. 14:30:59.000200' + '%d.%m.%Y. %H:%M', # '25.10.2006. 14:30' + '%d.%m.%y. %H:%M:%S', # '25.10.06. 14:30:59' + '%d.%m.%y. %H:%M:%S.%f', # '25.10.06. 14:30:59.000200' + '%d.%m.%y. %H:%M', # '25.10.06. 14:30' + '%d. %m. %Y. %H:%M:%S', # '25. 10. 2006. 14:30:59' + '%d. %m. %Y. %H:%M:%S.%f', # '25. 10. 2006. 14:30:59.000200' + '%d. %m. %Y. %H:%M', # '25. 10. 2006. 14:30' + '%d. %m. %y. %H:%M:%S', # '25. 10. 06. 14:30:59' + '%d. %m. %y. %H:%M:%S.%f', # '25. 10. 06. 14:30:59.000200' + '%d. %m. %y. %H:%M', # '25. 10. 06. 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo index 2072e7a..e686f26 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.po index 2ca3c7c..a30442b 100644 --- a/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/sr_Latn/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 23:38+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 17:37+0000\n" "Last-Translator: Igor Jerosimić\n" "Language-Team: Serbian (Latin) (http://www.transifex.com/django/django/" "language/sr@latin/)\n" @@ -210,9 +210,6 @@ msgstr "mongolski" msgid "Marathi" msgstr "marathi" -msgid "Malay" -msgstr "malajski" - msgid "Burmese" msgstr "burmanski" @@ -444,17 +441,15 @@ msgid "" "Ensure that there are no more than %(max)s digit before the decimal point." msgid_plural "" "Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "Ne može biti više od %(max)s cifre pre decimalnog zapisa." -msgstr[1] "Ne može biti više od %(max)s cifre pre decimalnog zapisa." -msgstr[2] "Ne može biti više od %(max)s cifara pre decimalnog zapisa." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" #, python-format msgid "" "File extension “%(extension)s” is not allowed. Allowed extensions are: " "%(allowed_extensions)s." msgstr "" -"Ekstenzija datoteke \"%(extension)s\" nije dozvoljena. Dozvoljene su sledeće " -"ekstenzije: %(allowed_extensions)s." msgid "Null characters are not allowed." msgstr "'Null' karakteri nisu dozvoljeni." @@ -486,8 +481,6 @@ msgstr "%(model_name)s sa ovom vrednošću %(field_label)s već postoji." msgid "" "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." msgstr "" -"%(field_label)s mora biti jedinstven(a) za %(date_field_label)s " -"%(lookup_type)s." #, python-format msgid "Field of type: %(field_type)s" @@ -495,11 +488,11 @@ msgstr "Polje tipa: %(field_type)s" #, python-format msgid "“%(value)s” value must be either True or False." -msgstr "Vrednost \"%(value)s\" mora biti True ili False." +msgstr "" #, python-format msgid "“%(value)s” value must be either True, False, or None." -msgstr "\"%(value)s\" vrednost mora biti True, False ili None." +msgstr "" msgid "Boolean (Either True or False)" msgstr "Bulova vrednost (True ili False)" @@ -516,8 +509,6 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"Vrednost \"%(value)s\" nema ispravan format datuma. Mora biti u formatu GGGG-" -"MM-DD." #, python-format msgid "" @@ -1103,7 +1094,7 @@ msgstr "Ovo nije ispravna IPv6 adresa." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s..." +msgstr "" msgid "or" msgstr "ili" @@ -1113,46 +1104,46 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d godina" -msgstr[1] "%(num)d godine" -msgstr[2] "%(num)d godina" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d godina" +msgstr[1] "%d godine" +msgstr[2] "%d godina" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mesec" +msgstr[1] "%d meseca" +msgstr[2] "%d meseci" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nedelja" +msgstr[1] "%d nedelje" +msgstr[2] "%d nedelja" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dan" +msgstr[1] "%d dana" +msgstr[2] "%d dana" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d čas" +msgstr[1] "%d časa" +msgstr[2] "%d časova" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minute" +msgstr[2] "%d minuta" msgid "Forbidden" msgstr "Zabranjeno" @@ -1162,10 +1153,14 @@ msgstr "CSRF verifikacija nije prošla. Zahtev odbijen." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Ova poruka je prikazana jer ovaj HTTPS sajt zahteva da \"Referer header\" " +"bude poslat od strane vašeg internet pregledača, što trenutno nije slučaj. " +"Pomenuto zaglavlje je potrebno iz bezbedonosnih razloga, da bi se osiguralo " +"da vaš pregledač nije pod kontrolom trećih lica." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1232,7 +1227,7 @@ msgstr "" #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "Neispravan datum \"%(datestr)s\" za format \"%(format)s\"" +msgstr "" #, python-format msgid "No %(verbose_name)s found matching the query" @@ -1243,7 +1238,7 @@ msgstr "Stranica nije poslednja, niti može biti konvertovana u tip \"int\"." #, python-format msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "Neispravna strana (%(page_number)s): %(message)s" +msgstr "" #, python-format msgid "Empty list and “%(class_name)s.allow_empty” is False." diff --git a/venv/Lib/site-packages/django/conf/locale/sr_Latn/formats.py b/venv/Lib/site-packages/django/conf/locale/sr_Latn/formats.py index 0078895..937a409 100644 --- a/venv/Lib/site-packages/django/conf/locale/sr_Latn/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sr_Latn/formats.py @@ -2,43 +2,38 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j. F Y." -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j. F Y. H:i" -YEAR_MONTH_FORMAT = "F Y." -MONTH_DAY_FORMAT = "j. F" -SHORT_DATE_FORMAT = "j.m.Y." -SHORT_DATETIME_FORMAT = "j.m.Y. H:i" +DATE_FORMAT = 'j. F Y.' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j. F Y. H:i' +YEAR_MONTH_FORMAT = 'F Y.' +MONTH_DAY_FORMAT = 'j. F' +SHORT_DATE_FORMAT = 'j.m.Y.' +SHORT_DATETIME_FORMAT = 'j.m.Y. H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y.", # '25.10.2006.' - "%d.%m.%y.", # '25.10.06.' - "%d. %m. %Y.", # '25. 10. 2006.' - "%d. %m. %y.", # '25. 10. 06.' - # "%d. %b %y.", # '25. Oct 06.' - # "%d. %B %y.", # '25. October 06.' - # "%d. %b '%y.", # '25. Oct '06.' - # "%d. %B '%y.", #'25. October '06.' - # "%d. %b %Y.", # '25. Oct 2006.' - # "%d. %B %Y.", # '25. October 2006.' + '%d.%m.%Y.', '%d.%m.%y.', # '25.10.2006.', '25.10.06.' + '%d. %m. %Y.', '%d. %m. %y.', # '25. 10. 2006.', '25. 10. 06.' + # '%d. %b %y.', '%d. %B %y.', # '25. Oct 06.', '25. October 06.' + # '%d. %b \'%y.', '%d. %B \'%y.', # '25. Oct '06.', '25. October '06.' + # '%d. %b %Y.', '%d. %B %Y.', # '25. Oct 2006.', '25. October 2006.' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y. %H:%M:%S", # '25.10.2006. 14:30:59' - "%d.%m.%Y. %H:%M:%S.%f", # '25.10.2006. 14:30:59.000200' - "%d.%m.%Y. %H:%M", # '25.10.2006. 14:30' - "%d.%m.%y. %H:%M:%S", # '25.10.06. 14:30:59' - "%d.%m.%y. %H:%M:%S.%f", # '25.10.06. 14:30:59.000200' - "%d.%m.%y. %H:%M", # '25.10.06. 14:30' - "%d. %m. %Y. %H:%M:%S", # '25. 10. 2006. 14:30:59' - "%d. %m. %Y. %H:%M:%S.%f", # '25. 10. 2006. 14:30:59.000200' - "%d. %m. %Y. %H:%M", # '25. 10. 2006. 14:30' - "%d. %m. %y. %H:%M:%S", # '25. 10. 06. 14:30:59' - "%d. %m. %y. %H:%M:%S.%f", # '25. 10. 06. 14:30:59.000200' - "%d. %m. %y. %H:%M", # '25. 10. 06. 14:30' + '%d.%m.%Y. %H:%M:%S', # '25.10.2006. 14:30:59' + '%d.%m.%Y. %H:%M:%S.%f', # '25.10.2006. 14:30:59.000200' + '%d.%m.%Y. %H:%M', # '25.10.2006. 14:30' + '%d.%m.%y. %H:%M:%S', # '25.10.06. 14:30:59' + '%d.%m.%y. %H:%M:%S.%f', # '25.10.06. 14:30:59.000200' + '%d.%m.%y. %H:%M', # '25.10.06. 14:30' + '%d. %m. %Y. %H:%M:%S', # '25. 10. 2006. 14:30:59' + '%d. %m. %Y. %H:%M:%S.%f', # '25. 10. 2006. 14:30:59.000200' + '%d. %m. %Y. %H:%M', # '25. 10. 2006. 14:30' + '%d. %m. %y. %H:%M:%S', # '25. 10. 06. 14:30:59' + '%d. %m. %y. %H:%M:%S.%f', # '25. 10. 06. 14:30:59.000200' + '%d. %m. %y. %H:%M', # '25. 10. 06. 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.mo index 761268d..b66581b 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.po index c648d91..8139b42 100644 --- a/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/sv/LC_MESSAGES/django.po @@ -18,8 +18,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-23 10:11+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-05-06 06:05+0000\n" "Last-Translator: Tomas Lööw \n" "Language-Team: Swedish (http://www.transifex.com/django/django/language/" "sv/)\n" @@ -218,9 +218,6 @@ msgstr "Mongoliska" msgid "Marathi" msgstr "Marathi" -msgid "Malay" -msgstr "Malajiska" - msgid "Burmese" msgstr "Burmesiska" @@ -335,7 +332,7 @@ msgstr "Syndikering" #. Translators: String used to replace omitted page numbers in elided page #. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. msgid "…" -msgstr "..." +msgstr "" msgid "That page number is not an integer" msgstr "Sidnumret är inte ett heltal" @@ -519,16 +516,12 @@ msgid "" "“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " "format." msgstr "" -"“%(value)s” värde har ett ogiltigt datumformat. Det måste vara i formatet " -"YYYY-MM-DD." #, python-format msgid "" "“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " "date." msgstr "" -"Värdet “%(value)s” har det giltiga formatet (YYYY-MM-DD) men det är ett " -"ogiltigt datum." msgid "Date (without time)" msgstr "Datum (utan tid)" @@ -538,8 +531,6 @@ msgid "" "“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." "uuuuuu]][TZ] format." msgstr "" -"“%(value)s” har ett ogiltigt format. Det måste vara i formatet YYYY-MM-DD HH:" -"MM[:ss[.uuuuuu]][TZ]." #, python-format msgid "" @@ -626,16 +617,12 @@ msgid "" "“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " "format." msgstr "" -"“%(value)s” har ett ogiltigt format. Det måste vara i formatet HH:MM[:ss[." -"uuuuuu]]." #, python-format msgid "" "“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " "invalid time." msgstr "" -"Värdet “%(value)s” har det giltiga formatet (HH:MM[:ss[.uuuuuu]]) men det är " -"en ogiltig tid." msgid "Time" msgstr "Tid" @@ -648,7 +635,7 @@ msgstr "Rå binärdata" #, python-format msgid "“%(value)s” is not a valid UUID." -msgstr "“%(value)s” är inget giltigt UUID." +msgstr "" msgid "Universally unique identifier" msgstr "Globalt unik identifierare" @@ -760,7 +747,7 @@ msgid "Enter a valid UUID." msgstr "Fyll i ett giltigt UUID." msgid "Enter a valid JSON." -msgstr "Fyll i ett giltigt JSON." +msgstr "" #. Translators: This is the default suffix added to form field labels msgid ":" @@ -779,14 +766,14 @@ msgstr "" #, python-format msgid "Please submit at most %d form." msgid_plural "Please submit at most %d forms." -msgstr[0] "Vänligen skicka som mest %d formulär." -msgstr[1] "Vänligen skicka som mest %d formulär." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "Please submit at least %d form." msgid_plural "Please submit at least %d forms." -msgstr[0] "Vänligen skicka minst %d formulär." -msgstr[1] "Vänligen skicka minst %d formulär." +msgstr[0] "" +msgstr[1] "" msgid "Order" msgstr "Sortering" @@ -830,8 +817,6 @@ msgid "" "%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " "may be ambiguous or it may not exist." msgstr "" -"%(datetime)s kunde inte tolkas i tidszonen %(current_timezone)s; det kan " -"vara en ogiltig eller tvetydigt tidpunkt." msgid "Clear" msgstr "Rensa" @@ -1125,40 +1110,40 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d år" +msgstr[1] "%d år" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d månad" -msgstr[1] "%(num)d månader" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d månad" +msgstr[1] "%d månader" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d vecka" -msgstr[1] "%(num)d veckor" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d vecka" +msgstr[1] "%d veckor" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dagar" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dag" +msgstr[1] "%d dagar" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d timme" -msgstr[1] "%(num)d timmar" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d timme" +msgstr[1] "%d timmar" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minuter" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minuter" msgid "Forbidden" msgstr "Ottillåtet" @@ -1168,13 +1153,10 @@ msgstr "CSRF-verifikation misslyckades. Förfrågan avbröts." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"Du ser detta meddelande eftersom denna HTTPS-sida kräver att en “Referer " -"header” skickas från din webbläsare, men ingen skickades. Denna header krävs " -"av säkerhetsskäl, för att säkerställa att din webbläsare inte kapats." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1203,8 +1185,6 @@ msgid "" "If you have configured your browser to disable cookies, please re-enable " "them, at least for this site, or for “same-origin” requests." msgstr "" -"Om cookies är inaktiverade i din webbläsare, vänligen återaktivera dem, " -"åtminstone för denna sida eller för “same-origin”-förfrågningar." msgid "More information is available with DEBUG=True." msgstr "Mer information är tillgänglig med DEBUG=True." @@ -1238,7 +1218,7 @@ msgstr "" #, python-format msgid "Invalid date string “%(datestr)s” given format “%(format)s”" -msgstr "Ogiltig datumsträng “%(datestr)s” med givet format “%(format)s”" +msgstr "" #, python-format msgid "No %(verbose_name)s found matching the query" @@ -1260,7 +1240,7 @@ msgstr "Kataloglistningar är inte tillåtna här." #, python-format msgid "“%(path)s” does not exist" -msgstr "\"%(path)s\" finns inte" +msgstr "" #, python-format msgid "Index of %(directory)s" @@ -1293,7 +1273,7 @@ msgid "Django Documentation" msgstr "Djangodokumentation" msgid "Topics, references, & how-to’s" -msgstr "Ämnen, referenser och how-to’s" +msgstr "" msgid "Tutorial: A Polling App" msgstr "Tutorial: En undersöknings-app" diff --git a/venv/Lib/site-packages/django/conf/locale/sv/formats.py b/venv/Lib/site-packages/django/conf/locale/sv/formats.py index 29e6317..9467526 100644 --- a/venv/Lib/site-packages/django/conf/locale/sv/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/sv/formats.py @@ -2,34 +2,34 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "j F Y H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "Y-m-d" -SHORT_DATETIME_FORMAT = "Y-m-d H:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'j F Y H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'Y-m-d' +SHORT_DATETIME_FORMAT = 'Y-m-d H:i' FIRST_DAY_OF_WEEK = 1 # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior # Kept ISO formats as they are in first position DATE_INPUT_FORMATS = [ - "%Y-%m-%d", # '2006-10-25' - "%m/%d/%Y", # '10/25/2006' - "%m/%d/%y", # '10/25/06' + '%Y-%m-%d', # '2006-10-25' + '%m/%d/%Y', # '10/25/2006' + '%m/%d/%y', # '10/25/06' ] 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' - "%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 %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' + '%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' + '%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 %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' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/ta/formats.py b/venv/Lib/site-packages/django/conf/locale/ta/formats.py index d023608..61810e3 100644 --- a/venv/Lib/site-packages/django/conf/locale/ta/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/ta/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F, Y" -TIME_FORMAT = "g:i A" +DATE_FORMAT = 'j F, Y' +TIME_FORMAT = 'g:i A' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M, Y" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M, Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = diff --git a/venv/Lib/site-packages/django/conf/locale/te/formats.py b/venv/Lib/site-packages/django/conf/locale/te/formats.py index bb7f2d1..8fb98cf 100644 --- a/venv/Lib/site-packages/django/conf/locale/te/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/te/formats.py @@ -2,12 +2,12 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "g:i A" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'g:i A' # DATETIME_FORMAT = # YEAR_MONTH_FORMAT = -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' # SHORT_DATETIME_FORMAT = # FIRST_DAY_OF_WEEK = diff --git a/venv/Lib/site-packages/django/conf/locale/tg/formats.py b/venv/Lib/site-packages/django/conf/locale/tg/formats.py index 0ab7d49..3e7651d 100644 --- a/venv/Lib/site-packages/django/conf/locale/tg/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/tg/formats.py @@ -2,31 +2,31 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j E Y г." -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j E Y г. G:i" -YEAR_MONTH_FORMAT = "F Y г." -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'j E Y г.' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j E Y г. G:i' +YEAR_MONTH_FORMAT = 'F Y г.' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y', # '25.10.06' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d.%m.%Y", # '25.10.2006' - "%d.%m.%y %H:%M:%S", # '25.10.06 14:30:59' - "%d.%m.%y %H:%M:%S.%f", # '25.10.06 14:30:59.000200' - "%d.%m.%y %H:%M", # '25.10.06 14:30' - "%d.%m.%y", # '25.10.06' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d.%m.%Y', # '25.10.2006' + '%d.%m.%y %H:%M:%S', # '25.10.06 14:30:59' + '%d.%m.%y %H:%M:%S.%f', # '25.10.06 14:30:59.000200' + '%d.%m.%y %H:%M', # '25.10.06 14:30' + '%d.%m.%y', # '25.10.06' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/th/formats.py b/venv/Lib/site-packages/django/conf/locale/th/formats.py index 190e6d1..d7394eb 100644 --- a/venv/Lib/site-packages/django/conf/locale/th/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/th/formats.py @@ -2,32 +2,32 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "j F Y" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = "j F Y, G:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "j M Y" -SHORT_DATETIME_FORMAT = "j M Y, G:i" +DATE_FORMAT = 'j F Y' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = 'j F Y, G:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'j M Y' +SHORT_DATETIME_FORMAT = 'j M Y, G:i' FIRST_DAY_OF_WEEK = 0 # Sunday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d/%m/%Y", # 25/10/2006 - "%d %b %Y", # 25 ต.ค. 2006 - "%d %B %Y", # 25 ตุลาคม 2006 + '%d/%m/%Y', # 25/10/2006 + '%d %b %Y', # 25 ต.ค. 2006 + '%d %B %Y', # 25 ตุลาคม 2006 ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # 14:30:59 - "%H:%M:%S.%f", # 14:30:59.000200 - "%H:%M", # 14:30 + '%H:%M:%S', # 14:30:59 + '%H:%M:%S.%f', # 14:30:59.000200 + '%H:%M', # 14:30 ] DATETIME_INPUT_FORMATS = [ - "%d/%m/%Y %H:%M:%S", # 25/10/2006 14:30:59 - "%d/%m/%Y %H:%M:%S.%f", # 25/10/2006 14:30:59.000200 - "%d/%m/%Y %H:%M", # 25/10/2006 14:30 + '%d/%m/%Y %H:%M:%S', # 25/10/2006 14:30:59 + '%d/%m/%Y %H:%M:%S.%f', # 25/10/2006 14:30:59.000200 + '%d/%m/%Y %H:%M', # 25/10/2006 14:30 ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "," +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.mo index 4647125..2c98ebf 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.po index 5f4cc18..9992a74 100644 --- a/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/tk/LC_MESSAGES/django.po @@ -1,16 +1,16 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Mariusz Felisiak , 2020-2021 +# Mariusz Felisiak , 2020 # Resulkary , 2020 # Welbeck Garli , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-24 16:30+0000\n" -"Last-Translator: Mariusz Felisiak \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-08-24 20:32+0000\n" +"Last-Translator: Resulkary \n" "Language-Team: Turkmen (http://www.transifex.com/django/django/language/" "tk/)\n" "MIME-Version: 1.0\n" @@ -208,9 +208,6 @@ msgstr "Mongolça" msgid "Marathi" msgstr "Marasi" -msgid "Malay" -msgstr "" - msgid "Burmese" msgstr "Birma" @@ -322,11 +319,6 @@ msgstr "Statik Faýllar" msgid "Syndication" msgstr "Syndikasiýa" -#. Translators: String used to replace omitted page numbers in elided page -#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. -msgid "…" -msgstr "" - msgid "That page number is not an integer" msgstr "Ol sahypanyň sany bitewi san däl" @@ -581,9 +573,6 @@ msgstr "Bitewi san" msgid "Big (8 byte) integer" msgstr "Uly (8 baýt) bitewi san" -msgid "Small integer" -msgstr "Kiçi bitewi san" - msgid "IPv4 address" msgstr "IPv4 salgy" @@ -610,6 +599,9 @@ msgstr "Pozitiw kiçi bitewi san" msgid "Slug (up to %(max_length)s)" msgstr "Slug (iň köp %(max_length)s)" +msgid "Small integer" +msgstr "Kiçi bitewi san" + msgid "Text" msgstr "Tekst" @@ -759,23 +751,20 @@ msgstr ":" msgid "(Hidden field %(name)s) %(error)s" msgstr "(Gizlin meýdan %(name)s) %(error)s" -#, python-format -msgid "" -"ManagementForm data is missing or has been tampered with. Missing fields: " -"%(field_names)s. You may need to file a bug report if the issue persists." -msgstr "" +msgid "ManagementForm data is missing or has been tampered with" +msgstr "ManagementForm maglumatlary ýok ýa-da bozulandyr" #, python-format -msgid "Please submit at most %d form." -msgid_plural "Please submit at most %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "%dýa-da ondan azyrak forma tabşyryň" +msgstr[1] "%d ýa-da ondan azyrak forma tabşyryň." #, python-format -msgid "Please submit at least %d form." -msgid_plural "Please submit at least %d forms." -msgstr[0] "" -msgstr[1] "" +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "%d ýa-da ondan köp forma tabşyryň." +msgstr[1] "%d ýa-da ondan köp forma tabşyryň." msgid "Order" msgstr "Tertip" @@ -1102,7 +1091,7 @@ msgstr "Bu dogry IPv6 salgy däl." #, python-format msgctxt "String to return when truncating text" msgid "%(truncated_text)s…" -msgstr "%(truncated_text)s…" +msgstr "%(truncated_text)s..." msgid "or" msgstr "ýa" @@ -1112,40 +1101,40 @@ msgid ", " msgstr "\"" #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d ýyl" +msgstr[1] "%d ýyl" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d aý" +msgstr[1] "%d aý" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d hepde" +msgstr[1] "%d hepde" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d gün" +msgstr[1] "%d gün" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d sagat" +msgstr[1] "%d sagat" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minut" msgid "Forbidden" msgstr "Gadagan " @@ -1155,10 +1144,14 @@ msgstr "CSRF dogrylamak şowsuz. Talap ýatyryldy." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" +"Bu habary görýärsiňiz, sebäbi bu HTTPS sahypasy web brauzeriňiz tarapyndan " +"iberilmegi üçin \"Referer sözbaşy\" talap edýär, ýöne hiç biri iberilmedi. " +"Bu sözbaşy, brauzeriňiziň üçünji taraplar tarapyndan ogurlanmazlygy üçin " +"howpsuzlyk sebäpli talap edilýär." msgid "" "If you have configured your browser to disable “Referer” headers, please re-" @@ -1258,8 +1251,9 @@ msgstr "\"%(path)s\" beýle ýol ýok" msgid "Index of %(directory)s" msgstr "%(directory)s indeksi" -msgid "The install worked successfully! Congratulations!" -msgstr "Üstünlikli guruldy! Gutlaýarys!" +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" +"Django: möhletleri bolan we kämillik talap edýänler üçin web freýmworky." #, python-format msgid "" @@ -1270,6 +1264,9 @@ msgstr "" "%(version)s/releases/\" target=\"_blank\" rel=\"noopener\">goýberiş " "belliklerini görüň" +msgid "The install worked successfully! Congratulations!" +msgstr "Üstünlikli guruldy! Gutlaýarys!" + #, python-format msgid "" "You are seeing this page because \n" "Language-Team: Ukrainian (http://www.transifex.com/django/django/language/" "uk/)\n" @@ -224,9 +224,6 @@ msgstr "Монгольська" msgid "Marathi" msgstr "Маратхі" -msgid "Malay" -msgstr "Малайська" - msgid "Burmese" msgstr "Бірманська" @@ -624,7 +621,7 @@ msgid "Boolean (Either True, False or None)" msgstr "Булеве значення (включаючи True, False або None)" msgid "Positive big integer" -msgstr "Додатнє велике ціле число" +msgstr "" msgid "Positive integer" msgstr "Додатнє ціле число" @@ -1149,52 +1146,52 @@ msgid ", " msgstr ", " #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d рік" +msgstr[1] "%d роки" +msgstr[2] "%d років" +msgstr[3] "%d років" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d місяць" +msgstr[1] "%d місяці" +msgstr[2] "%d місяців" +msgstr[3] "%d місяців" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d тиждень" +msgstr[1] "%d тижні" +msgstr[2] "%d тижнів" +msgstr[3] "%d тижнів" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d день" +msgstr[1] "%d дня" +msgstr[2] "%d днів" +msgstr[3] "%d днів" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d година" +msgstr[1] "%d години" +msgstr[2] "%d годин" +msgstr[3] "%d годин" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d хвилина" +msgstr[1] "%d хвилини" +msgstr[2] "%d хвилин" +msgstr[3] "%d хвилин" msgid "Forbidden" msgstr "Заборонено" @@ -1204,7 +1201,7 @@ msgstr "Помилка перевірки CSRF. Запит відхилений. msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" @@ -1324,7 +1321,7 @@ msgid "Django Documentation" msgstr "Документація Django" msgid "Topics, references, & how-to’s" -msgstr "Статті, довідки та інструкції" +msgstr "" msgid "Tutorial: A Polling App" msgstr "Посібник: програма голосування" diff --git a/venv/Lib/site-packages/django/conf/locale/uk/formats.py b/venv/Lib/site-packages/django/conf/locale/uk/formats.py index 0f28831..ca2593b 100644 --- a/venv/Lib/site-packages/django/conf/locale/uk/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/uk/formats.py @@ -2,34 +2,34 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "d E Y р." -TIME_FORMAT = "H:i" -DATETIME_FORMAT = "d E Y р. H:i" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "d F" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = 'd E Y р.' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = 'd E Y р. H:i' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'd F' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d %B %Y", # '25 October 2006' + '%d.%m.%Y', # '25.10.2006' + '%d %B %Y', # '25 October 2006' ] TIME_INPUT_FORMATS = [ - "%H:%M:%S", # '14:30:59' - "%H:%M:%S.%f", # '14:30:59.000200' - "%H:%M", # '14:30' + '%H:%M:%S', # '14:30:59' + '%H:%M:%S.%f', # '14:30:59.000200' + '%H:%M', # '14:30' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d %B %Y %H:%M:%S", # '25 October 2006 14:30:59' - "%d %B %Y %H:%M:%S.%f", # '25 October 2006 14:30:59.000200' - "%d %B %Y %H:%M", # '25 October 2006 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d %B %Y %H:%M:%S', # '25 October 2006 14:30:59' + '%d %B %Y %H:%M:%S.%f', # '25 October 2006 14:30:59.000200' + '%d %B %Y %H:%M', # '25 October 2006 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/uz/formats.py b/venv/Lib/site-packages/django/conf/locale/uz/formats.py index 2c7ee73..14af096 100644 --- a/venv/Lib/site-packages/django/conf/locale/uz/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/uz/formats.py @@ -2,29 +2,29 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"j-E, Y-\y\i\l" -TIME_FORMAT = "G:i" -DATETIME_FORMAT = r"j-E, Y-\y\i\l G:i" -YEAR_MONTH_FORMAT = r"F Y-\y\i\l" -MONTH_DAY_FORMAT = "j-E" -SHORT_DATE_FORMAT = "d.m.Y" -SHORT_DATETIME_FORMAT = "d.m.Y H:i" +DATE_FORMAT = r'j-E, Y-\y\i\l' +TIME_FORMAT = 'G:i' +DATETIME_FORMAT = r'j-E, Y-\y\i\l G:i' +YEAR_MONTH_FORMAT = r'F Y-\y\i\l' +MONTH_DAY_FORMAT = 'j-E' +SHORT_DATE_FORMAT = 'd.m.Y' +SHORT_DATETIME_FORMAT = 'd.m.Y H:i' FIRST_DAY_OF_WEEK = 1 # Monday # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%d.%m.%Y", # '25.10.2006' - "%d-%B, %Y-yil", # '25-Oktabr, 2006-yil' + '%d.%m.%Y', # '25.10.2006' + '%d-%B, %Y-yil', # '25-Oktabr, 2006-yil' ] DATETIME_INPUT_FORMATS = [ - "%d.%m.%Y %H:%M:%S", # '25.10.2006 14:30:59' - "%d.%m.%Y %H:%M:%S.%f", # '25.10.2006 14:30:59.000200' - "%d.%m.%Y %H:%M", # '25.10.2006 14:30' - "%d-%B, %Y-yil %H:%M:%S", # '25-Oktabr, 2006-yil 14:30:59' - "%d-%B, %Y-yil %H:%M:%S.%f", # '25-Oktabr, 2006-yil 14:30:59.000200' - "%d-%B, %Y-yil %H:%M", # '25-Oktabr, 2006-yil 14:30' + '%d.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59' + '%d.%m.%Y %H:%M:%S.%f', # '25.10.2006 14:30:59.000200' + '%d.%m.%Y %H:%M', # '25.10.2006 14:30' + '%d-%B, %Y-yil %H:%M:%S', # '25-Oktabr, 2006-yil 14:30:59' + '%d-%B, %Y-yil %H:%M:%S.%f', # '25-Oktabr, 2006-yil 14:30:59.000200' + '%d-%B, %Y-yil %H:%M', # '25-Oktabr, 2006-yil 14:30' ] -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "\xa0" # non-breaking space +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '\xa0' # non-breaking space NUMBER_GROUPING = 3 diff --git a/venv/Lib/site-packages/django/conf/locale/vi/formats.py b/venv/Lib/site-packages/django/conf/locale/vi/formats.py index 7b76020..495b6f7 100644 --- a/venv/Lib/site-packages/django/conf/locale/vi/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/vi/formats.py @@ -2,13 +2,13 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = r"\N\gà\y d \t\há\n\g n \nă\m Y" -TIME_FORMAT = "H:i" -DATETIME_FORMAT = r"H:i \N\gà\y d \t\há\n\g n \nă\m Y" -YEAR_MONTH_FORMAT = "F Y" -MONTH_DAY_FORMAT = "j F" -SHORT_DATE_FORMAT = "d-m-Y" -SHORT_DATETIME_FORMAT = "H:i d-m-Y" +DATE_FORMAT = r'\N\gà\y d \t\há\n\g n \nă\m Y' +TIME_FORMAT = 'H:i' +DATETIME_FORMAT = r'H:i \N\gà\y d \t\há\n\g n \nă\m Y' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'j F' +SHORT_DATE_FORMAT = 'd-m-Y' +SHORT_DATETIME_FORMAT = 'H:i d-m-Y' # FIRST_DAY_OF_WEEK = # The *_INPUT_FORMATS strings use the Python strftime format syntax, @@ -16,6 +16,6 @@ SHORT_DATETIME_FORMAT = "H:i d-m-Y" # DATE_INPUT_FORMATS = # TIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS = -DECIMAL_SEPARATOR = "," -THOUSAND_SEPARATOR = "." +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' # NUMBER_GROUPING = diff --git a/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.mo index c61602f..9755f5f 100644 Binary files a/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.po index 8f44682..e867840 100644 --- a/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/conf/locale/zh_Hans/LC_MESSAGES/django.po @@ -2,7 +2,6 @@ # # Translators: # HuanCheng Bai白宦成 , 2017-2018 -# lanbla , 2021 # Daniel Duan , 2013 # jamin M , 2019 # Jannis Leidel , 2011 @@ -14,7 +13,7 @@ # matthew Yip , 2020 # mozillazg , 2016 # Ronald White , 2014 -# Lemon Li , 2013 +# pylemon , 2013 # Ray Wang , 2017 # slene , 2011 # Sun Liwen , 2014 @@ -35,9 +34,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 03:05+0000\n" -"Last-Translator: lanbla \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-02-07 04:20+0000\n" +"Last-Translator: Veoco \n" "Language-Team: Chinese (China) (http://www.transifex.com/django/django/" "language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -235,9 +234,6 @@ msgstr "蒙古语" msgid "Marathi" msgstr "马拉地语" -msgid "Malay" -msgstr "马来语" - msgid "Burmese" msgstr "缅甸语" @@ -1105,34 +1101,34 @@ msgid ", " msgstr "," #, python-format -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d 年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d 月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d 月" #, python-format -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d 周" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 周" #, python-format -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d 日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d 日" #, python-format -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d 小时" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 小时" #, python-format -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d 分钟" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分钟" msgid "Forbidden" msgstr "禁止访问" @@ -1142,12 +1138,12 @@ msgstr "CSRF验证失败. 请求被中断." msgid "" "You are seeing this message because this HTTPS site requires a “Referer " -"header” to be sent by your web browser, but none was sent. This header is " +"header” to be sent by your Web browser, but none was sent. This header is " "required for security reasons, to ensure that your browser is not being " "hijacked by third parties." msgstr "" -"您看到此消息是由于HTTPS站点需要您的浏览器发送 'Referer header',但是该信息并" -"未被发送。出于安全原因,此HTTP头是必需的,以确保您的浏览器不会被第三方劫持。" +"您看到此消息是因为此HTTPS站点要求Web浏览器发送的“Referer头”没被发送。出于安全" +"原因,此HTTP头是必需的,以确保您的浏览器不会被第三方劫持。" msgid "" "If you have configured your browser to disable “Referer” headers, please re-" diff --git a/venv/Lib/site-packages/django/conf/locale/zh_Hans/formats.py b/venv/Lib/site-packages/django/conf/locale/zh_Hans/formats.py index 79936f8..018b9b1 100644 --- a/venv/Lib/site-packages/django/conf/locale/zh_Hans/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/zh_Hans/formats.py @@ -2,41 +2,41 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "Y年n月j日" # 2016年9月5日 -TIME_FORMAT = "H:i" # 20:45 -DATETIME_FORMAT = "Y年n月j日 H:i" # 2016年9月5日 20:45 -YEAR_MONTH_FORMAT = "Y年n月" # 2016年9月 -MONTH_DAY_FORMAT = "m月j日" # 9月5日 -SHORT_DATE_FORMAT = "Y年n月j日" # 2016年9月5日 -SHORT_DATETIME_FORMAT = "Y年n月j日 H:i" # 2016年9月5日 20:45 -FIRST_DAY_OF_WEEK = 1 # 星期一 (Monday) +DATE_FORMAT = 'Y年n月j日' # 2016年9月5日 +TIME_FORMAT = 'H:i' # 20:45 +DATETIME_FORMAT = 'Y年n月j日 H:i' # 2016年9月5日 20:45 +YEAR_MONTH_FORMAT = 'Y年n月' # 2016年9月 +MONTH_DAY_FORMAT = 'm月j日' # 9月5日 +SHORT_DATE_FORMAT = 'Y年n月j日' # 2016年9月5日 +SHORT_DATETIME_FORMAT = 'Y年n月j日 H:i' # 2016年9月5日 20:45 +FIRST_DAY_OF_WEEK = 1 # 星期一 (Monday) # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%Y/%m/%d", # '2016/09/05' - "%Y-%m-%d", # '2016-09-05' - "%Y年%n月%j日", # '2016年9月5日' + '%Y/%m/%d', # '2016/09/05' + '%Y-%m-%d', # '2016-09-05' + '%Y年%n月%j日', # '2016年9月5日' ] TIME_INPUT_FORMATS = [ - "%H:%M", # '20:45' - "%H:%M:%S", # '20:45:29' - "%H:%M:%S.%f", # '20:45:29.000200' + '%H:%M', # '20:45' + '%H:%M:%S', # '20:45:29' + '%H:%M:%S.%f', # '20:45:29.000200' ] DATETIME_INPUT_FORMATS = [ - "%Y/%m/%d %H:%M", # '2016/09/05 20:45' - "%Y-%m-%d %H:%M", # '2016-09-05 20:45' - "%Y年%n月%j日 %H:%M", # '2016年9月5日 14:45' - "%Y/%m/%d %H:%M:%S", # '2016/09/05 20:45:29' - "%Y-%m-%d %H:%M:%S", # '2016-09-05 20:45:29' - "%Y年%n月%j日 %H:%M:%S", # '2016年9月5日 20:45:29' - "%Y/%m/%d %H:%M:%S.%f", # '2016/09/05 20:45:29.000200' - "%Y-%m-%d %H:%M:%S.%f", # '2016-09-05 20:45:29.000200' - "%Y年%n月%j日 %H:%n:%S.%f", # '2016年9月5日 20:45:29.000200' + '%Y/%m/%d %H:%M', # '2016/09/05 20:45' + '%Y-%m-%d %H:%M', # '2016-09-05 20:45' + '%Y年%n月%j日 %H:%M', # '2016年9月5日 14:45' + '%Y/%m/%d %H:%M:%S', # '2016/09/05 20:45:29' + '%Y-%m-%d %H:%M:%S', # '2016-09-05 20:45:29' + '%Y年%n月%j日 %H:%M:%S', # '2016年9月5日 20:45:29' + '%Y/%m/%d %H:%M:%S.%f', # '2016/09/05 20:45:29.000200' + '%Y-%m-%d %H:%M:%S.%f', # '2016-09-05 20:45:29.000200' + '%Y年%n月%j日 %H:%n:%S.%f', # '2016年9月5日 20:45:29.000200' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "" +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = '' NUMBER_GROUPING = 4 diff --git a/venv/Lib/site-packages/django/conf/locale/zh_Hant/formats.py b/venv/Lib/site-packages/django/conf/locale/zh_Hant/formats.py index 79936f8..018b9b1 100644 --- a/venv/Lib/site-packages/django/conf/locale/zh_Hant/formats.py +++ b/venv/Lib/site-packages/django/conf/locale/zh_Hant/formats.py @@ -2,41 +2,41 @@ # # The *_FORMAT strings use the Django date format syntax, # see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date -DATE_FORMAT = "Y年n月j日" # 2016年9月5日 -TIME_FORMAT = "H:i" # 20:45 -DATETIME_FORMAT = "Y年n月j日 H:i" # 2016年9月5日 20:45 -YEAR_MONTH_FORMAT = "Y年n月" # 2016年9月 -MONTH_DAY_FORMAT = "m月j日" # 9月5日 -SHORT_DATE_FORMAT = "Y年n月j日" # 2016年9月5日 -SHORT_DATETIME_FORMAT = "Y年n月j日 H:i" # 2016年9月5日 20:45 -FIRST_DAY_OF_WEEK = 1 # 星期一 (Monday) +DATE_FORMAT = 'Y年n月j日' # 2016年9月5日 +TIME_FORMAT = 'H:i' # 20:45 +DATETIME_FORMAT = 'Y年n月j日 H:i' # 2016年9月5日 20:45 +YEAR_MONTH_FORMAT = 'Y年n月' # 2016年9月 +MONTH_DAY_FORMAT = 'm月j日' # 9月5日 +SHORT_DATE_FORMAT = 'Y年n月j日' # 2016年9月5日 +SHORT_DATETIME_FORMAT = 'Y年n月j日 H:i' # 2016年9月5日 20:45 +FIRST_DAY_OF_WEEK = 1 # 星期一 (Monday) # The *_INPUT_FORMATS strings use the Python strftime format syntax, # see https://docs.python.org/library/datetime.html#strftime-strptime-behavior DATE_INPUT_FORMATS = [ - "%Y/%m/%d", # '2016/09/05' - "%Y-%m-%d", # '2016-09-05' - "%Y年%n月%j日", # '2016年9月5日' + '%Y/%m/%d', # '2016/09/05' + '%Y-%m-%d', # '2016-09-05' + '%Y年%n月%j日', # '2016年9月5日' ] TIME_INPUT_FORMATS = [ - "%H:%M", # '20:45' - "%H:%M:%S", # '20:45:29' - "%H:%M:%S.%f", # '20:45:29.000200' + '%H:%M', # '20:45' + '%H:%M:%S', # '20:45:29' + '%H:%M:%S.%f', # '20:45:29.000200' ] DATETIME_INPUT_FORMATS = [ - "%Y/%m/%d %H:%M", # '2016/09/05 20:45' - "%Y-%m-%d %H:%M", # '2016-09-05 20:45' - "%Y年%n月%j日 %H:%M", # '2016年9月5日 14:45' - "%Y/%m/%d %H:%M:%S", # '2016/09/05 20:45:29' - "%Y-%m-%d %H:%M:%S", # '2016-09-05 20:45:29' - "%Y年%n月%j日 %H:%M:%S", # '2016年9月5日 20:45:29' - "%Y/%m/%d %H:%M:%S.%f", # '2016/09/05 20:45:29.000200' - "%Y-%m-%d %H:%M:%S.%f", # '2016-09-05 20:45:29.000200' - "%Y年%n月%j日 %H:%n:%S.%f", # '2016年9月5日 20:45:29.000200' + '%Y/%m/%d %H:%M', # '2016/09/05 20:45' + '%Y-%m-%d %H:%M', # '2016-09-05 20:45' + '%Y年%n月%j日 %H:%M', # '2016年9月5日 14:45' + '%Y/%m/%d %H:%M:%S', # '2016/09/05 20:45:29' + '%Y-%m-%d %H:%M:%S', # '2016-09-05 20:45:29' + '%Y年%n月%j日 %H:%M:%S', # '2016年9月5日 20:45:29' + '%Y/%m/%d %H:%M:%S.%f', # '2016/09/05 20:45:29.000200' + '%Y-%m-%d %H:%M:%S.%f', # '2016-09-05 20:45:29.000200' + '%Y年%n月%j日 %H:%n:%S.%f', # '2016年9月5日 20:45:29.000200' ] -DECIMAL_SEPARATOR = "." -THOUSAND_SEPARATOR = "" +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = '' NUMBER_GROUPING = 4 diff --git a/venv/Lib/site-packages/django/conf/project_template/project_name/settings.py-tpl b/venv/Lib/site-packages/django/conf/project_template/project_name/settings.py-tpl index 3b6caab..7830fb2 100644 --- a/venv/Lib/site-packages/django/conf/project_template/project_name/settings.py-tpl +++ b/venv/Lib/site-packages/django/conf/project_template/project_name/settings.py-tpl @@ -109,13 +109,15 @@ TIME_ZONE = 'UTC' USE_I18N = True +USE_L10N = True + USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = '/static/' # Default primary key field type # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#default-auto-field diff --git a/venv/Lib/site-packages/django/conf/urls/__init__.py b/venv/Lib/site-packages/django/conf/urls/__init__.py index 302f68d..c58e581 100644 --- a/venv/Lib/site-packages/django/conf/urls/__init__.py +++ b/venv/Lib/site-packages/django/conf/urls/__init__.py @@ -1,9 +1,22 @@ -from django.urls import include +import warnings + +from django.urls import include, re_path +from django.utils.deprecation import RemovedInDjango40Warning from django.views import defaults -__all__ = ["handler400", "handler403", "handler404", "handler500", "include"] +__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url'] handler400 = defaults.bad_request handler403 = defaults.permission_denied handler404 = defaults.page_not_found handler500 = defaults.server_error + + +def url(regex, view, kwargs=None, name=None): + warnings.warn( + 'django.conf.urls.url() is deprecated in favor of ' + 'django.urls.re_path().', + RemovedInDjango40Warning, + stacklevel=2, + ) + return re_path(regex, view, kwargs, name) diff --git a/venv/Lib/site-packages/django/conf/urls/i18n.py b/venv/Lib/site-packages/django/conf/urls/i18n.py index ebe5d51..256c247 100644 --- a/venv/Lib/site-packages/django/conf/urls/i18n.py +++ b/venv/Lib/site-packages/django/conf/urls/i18n.py @@ -35,5 +35,5 @@ def is_language_prefix_patterns_used(urlconf): urlpatterns = [ - path("setlang/", set_language, name="set_language"), + path('setlang/', set_language, name='set_language'), ] diff --git a/venv/Lib/site-packages/django/conf/urls/static.py b/venv/Lib/site-packages/django/conf/urls/static.py index 8e7816e..fa83645 100644 --- a/venv/Lib/site-packages/django/conf/urls/static.py +++ b/venv/Lib/site-packages/django/conf/urls/static.py @@ -24,7 +24,5 @@ def static(prefix, view=serve, **kwargs): # No-op if not in debug mode or a non-local prefix. return [] return [ - re_path( - r"^%s(?P.*)$" % re.escape(prefix.lstrip("/")), view, kwargs=kwargs - ), + re_path(r'^%s(?P.*)$' % re.escape(prefix.lstrip('/')), view, kwargs=kwargs), ] diff --git a/venv/Lib/site-packages/django/contrib/admin/__init__.py b/venv/Lib/site-packages/django/contrib/admin/__init__.py index ef5c64f..975cf05 100644 --- a/venv/Lib/site-packages/django/contrib/admin/__init__.py +++ b/venv/Lib/site-packages/django/contrib/admin/__init__.py @@ -1,50 +1,24 @@ from django.contrib.admin.decorators import action, display, register from django.contrib.admin.filters import ( - AllValuesFieldListFilter, - BooleanFieldListFilter, - ChoicesFieldListFilter, - DateFieldListFilter, - EmptyFieldListFilter, - FieldListFilter, - ListFilter, - RelatedFieldListFilter, - RelatedOnlyFieldListFilter, - SimpleListFilter, + AllValuesFieldListFilter, BooleanFieldListFilter, ChoicesFieldListFilter, + DateFieldListFilter, EmptyFieldListFilter, FieldListFilter, ListFilter, + RelatedFieldListFilter, RelatedOnlyFieldListFilter, SimpleListFilter, ) from django.contrib.admin.options import ( - HORIZONTAL, - VERTICAL, - ModelAdmin, - StackedInline, - TabularInline, + HORIZONTAL, VERTICAL, ModelAdmin, StackedInline, TabularInline, ) from django.contrib.admin.sites import AdminSite, site from django.utils.module_loading import autodiscover_modules __all__ = [ - "action", - "display", - "register", - "ModelAdmin", - "HORIZONTAL", - "VERTICAL", - "StackedInline", - "TabularInline", - "AdminSite", - "site", - "ListFilter", - "SimpleListFilter", - "FieldListFilter", - "BooleanFieldListFilter", - "RelatedFieldListFilter", - "ChoicesFieldListFilter", - "DateFieldListFilter", - "AllValuesFieldListFilter", - "EmptyFieldListFilter", - "RelatedOnlyFieldListFilter", - "autodiscover", + "action", "display", "register", "ModelAdmin", "HORIZONTAL", "VERTICAL", + "StackedInline", "TabularInline", "AdminSite", "site", "ListFilter", + "SimpleListFilter", "FieldListFilter", "BooleanFieldListFilter", + "RelatedFieldListFilter", "ChoicesFieldListFilter", "DateFieldListFilter", + "AllValuesFieldListFilter", "EmptyFieldListFilter", + "RelatedOnlyFieldListFilter", "autodiscover", ] def autodiscover(): - autodiscover_modules("admin", register_to=site) + autodiscover_modules('admin', register_to=site) diff --git a/venv/Lib/site-packages/django/contrib/admin/actions.py b/venv/Lib/site-packages/django/contrib/admin/actions.py index 60dc848..665d83c 100644 --- a/venv/Lib/site-packages/django/contrib/admin/actions.py +++ b/venv/Lib/site-packages/django/contrib/admin/actions.py @@ -8,13 +8,12 @@ from django.contrib.admin.decorators import action from django.contrib.admin.utils import model_ngettext from django.core.exceptions import PermissionDenied from django.template.response import TemplateResponse -from django.utils.translation import gettext as _ -from django.utils.translation import gettext_lazy +from django.utils.translation import gettext as _, gettext_lazy @action( - permissions=["delete"], - description=gettext_lazy("Delete selected %(verbose_name_plural)s"), + permissions=['delete'], + description=gettext_lazy('Delete selected %(verbose_name_plural)s'), ) def delete_selected(modeladmin, request, queryset): """ @@ -31,16 +30,11 @@ def delete_selected(modeladmin, request, queryset): # Populate deletable_objects, a data structure of all related objects that # will also be deleted. - ( - deletable_objects, - model_count, - perms_needed, - protected, - ) = modeladmin.get_deleted_objects(queryset, request) + deletable_objects, model_count, perms_needed, protected = modeladmin.get_deleted_objects(queryset, request) # The user has already confirmed the deletion. # Do the deletion and return None to display the change list view again. - if request.POST.get("post") and not protected: + if request.POST.get('post') and not protected: if perms_needed: raise PermissionDenied n = queryset.count() @@ -49,12 +43,9 @@ def delete_selected(modeladmin, request, queryset): obj_display = str(obj) modeladmin.log_deletion(request, obj, obj_display) modeladmin.delete_queryset(request, queryset) - modeladmin.message_user( - request, - _("Successfully deleted %(count)d %(items)s.") - % {"count": n, "items": model_ngettext(modeladmin.opts, n)}, - messages.SUCCESS, - ) + modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % { + "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) # Return None to display the change list page again. return None @@ -67,29 +58,23 @@ def delete_selected(modeladmin, request, queryset): context = { **modeladmin.admin_site.each_context(request), - "title": title, - "objects_name": str(objects_name), - "deletable_objects": [deletable_objects], - "model_count": dict(model_count).items(), - "queryset": queryset, - "perms_lacking": perms_needed, - "protected": protected, - "opts": opts, - "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME, - "media": modeladmin.media, + 'title': title, + 'objects_name': str(objects_name), + 'deletable_objects': [deletable_objects], + 'model_count': dict(model_count).items(), + 'queryset': queryset, + 'perms_lacking': perms_needed, + 'protected': protected, + 'opts': opts, + 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, + 'media': modeladmin.media, } request.current_app = modeladmin.admin_site.name # Display the confirmation page - return TemplateResponse( - request, - modeladmin.delete_selected_confirmation_template - or [ - "admin/%s/%s/delete_selected_confirmation.html" - % (app_label, opts.model_name), - "admin/%s/delete_selected_confirmation.html" % app_label, - "admin/delete_selected_confirmation.html", - ], - context, - ) + return TemplateResponse(request, modeladmin.delete_selected_confirmation_template or [ + "admin/%s/%s/delete_selected_confirmation.html" % (app_label, opts.model_name), + "admin/%s/delete_selected_confirmation.html" % app_label, + "admin/delete_selected_confirmation.html" + ], context) diff --git a/venv/Lib/site-packages/django/contrib/admin/apps.py b/venv/Lib/site-packages/django/contrib/admin/apps.py index 08a9e0d..c4fba88 100644 --- a/venv/Lib/site-packages/django/contrib/admin/apps.py +++ b/venv/Lib/site-packages/django/contrib/admin/apps.py @@ -7,9 +7,9 @@ from django.utils.translation import gettext_lazy as _ class SimpleAdminConfig(AppConfig): """Simple AppConfig which does not do automatic discovery.""" - default_auto_field = "django.db.models.AutoField" - default_site = "django.contrib.admin.sites.AdminSite" - name = "django.contrib.admin" + default_auto_field = 'django.db.models.AutoField' + default_site = 'django.contrib.admin.sites.AdminSite' + name = 'django.contrib.admin' verbose_name = _("Administration") def ready(self): diff --git a/venv/Lib/site-packages/django/contrib/admin/checks.py b/venv/Lib/site-packages/django/contrib/admin/checks.py index 10d33b0..fea5e24 100644 --- a/venv/Lib/site-packages/django/contrib/admin/checks.py +++ b/venv/Lib/site-packages/django/contrib/admin/checks.py @@ -3,13 +3,17 @@ from itertools import chain from django.apps import apps from django.conf import settings -from django.contrib.admin.utils import NotRelationField, flatten, get_fields_from_path +from django.contrib.admin.utils import ( + NotRelationField, flatten, get_fields_from_path, +) from django.core import checks from django.core.exceptions import FieldDoesNotExist from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.expressions import Combinable -from django.forms.models import BaseModelForm, BaseModelFormSet, _get_foreign_key +from django.forms.models import ( + BaseModelForm, BaseModelFormSet, _get_foreign_key, +) from django.template import engines from django.template.backends.django import DjangoTemplates from django.utils.module_loading import import_string @@ -45,7 +49,6 @@ def _contains_subclass(class_path, candidate_paths): def check_admin_app(app_configs, **kwargs): from django.contrib.admin.sites import all_sites - errors = [] for site in all_sites: errors.extend(site.check(app_configs)) @@ -57,24 +60,21 @@ def check_dependencies(**kwargs): Check that the admin's dependencies are correctly installed. """ from django.contrib.admin.sites import all_sites - - if not apps.is_installed("django.contrib.admin"): + if not apps.is_installed('django.contrib.admin'): return [] errors = [] app_dependencies = ( - ("django.contrib.contenttypes", 401), - ("django.contrib.auth", 405), - ("django.contrib.messages", 406), + ('django.contrib.contenttypes', 401), + ('django.contrib.auth', 405), + ('django.contrib.messages', 406), ) for app_name, error_code in app_dependencies: if not apps.is_installed(app_name): - errors.append( - checks.Error( - "'%s' must be in INSTALLED_APPS in order to use the admin " - "application." % app_name, - id="admin.E%d" % error_code, - ) - ) + errors.append(checks.Error( + "'%s' must be in INSTALLED_APPS in order to use the admin " + "application." % app_name, + id='admin.E%d' % error_code, + )) for engine in engines.all(): if isinstance(engine, DjangoTemplates): django_templates_instance = engine.engine @@ -82,98 +82,69 @@ def check_dependencies(**kwargs): else: django_templates_instance = None if not django_templates_instance: - errors.append( - checks.Error( - "A 'django.template.backends.django.DjangoTemplates' instance " - "must be configured in TEMPLATES in order to use the admin " - "application.", - id="admin.E403", - ) - ) + errors.append(checks.Error( + "A 'django.template.backends.django.DjangoTemplates' instance " + "must be configured in TEMPLATES in order to use the admin " + "application.", + id='admin.E403', + )) else: - if ( - "django.contrib.auth.context_processors.auth" - not in django_templates_instance.context_processors - and _contains_subclass( - "django.contrib.auth.backends.ModelBackend", - settings.AUTHENTICATION_BACKENDS, - ) - ): - errors.append( - checks.Error( - "'django.contrib.auth.context_processors.auth' must be " - "enabled in DjangoTemplates (TEMPLATES) if using the default " - "auth backend in order to use the admin application.", - id="admin.E402", - ) - ) - if ( - "django.contrib.messages.context_processors.messages" - not in django_templates_instance.context_processors - ): - errors.append( - checks.Error( - "'django.contrib.messages.context_processors.messages' must " - "be enabled in DjangoTemplates (TEMPLATES) in order to use " - "the admin application.", - id="admin.E404", - ) - ) + if ('django.contrib.auth.context_processors.auth' + not in django_templates_instance.context_processors and + _contains_subclass('django.contrib.auth.backends.ModelBackend', settings.AUTHENTICATION_BACKENDS)): + errors.append(checks.Error( + "'django.contrib.auth.context_processors.auth' must be " + "enabled in DjangoTemplates (TEMPLATES) if using the default " + "auth backend in order to use the admin application.", + id='admin.E402', + )) + if ('django.contrib.messages.context_processors.messages' + not in django_templates_instance.context_processors): + errors.append(checks.Error( + "'django.contrib.messages.context_processors.messages' must " + "be enabled in DjangoTemplates (TEMPLATES) in order to use " + "the admin application.", + id='admin.E404', + )) sidebar_enabled = any(site.enable_nav_sidebar for site in all_sites) - if ( - sidebar_enabled - and "django.template.context_processors.request" - not in django_templates_instance.context_processors - ): - errors.append( - checks.Warning( - "'django.template.context_processors.request' must be enabled " - "in DjangoTemplates (TEMPLATES) in order to use the admin " - "navigation sidebar.", - id="admin.W411", - ) - ) + if (sidebar_enabled and 'django.template.context_processors.request' + not in django_templates_instance.context_processors): + errors.append(checks.Warning( + "'django.template.context_processors.request' must be enabled " + "in DjangoTemplates (TEMPLATES) in order to use the admin " + "navigation sidebar.", + id='admin.W411', + )) - if not _contains_subclass( - "django.contrib.auth.middleware.AuthenticationMiddleware", settings.MIDDLEWARE - ): - errors.append( - checks.Error( - "'django.contrib.auth.middleware.AuthenticationMiddleware' must " - "be in MIDDLEWARE in order to use the admin application.", - id="admin.E408", - ) - ) - if not _contains_subclass( - "django.contrib.messages.middleware.MessageMiddleware", settings.MIDDLEWARE - ): - errors.append( - checks.Error( - "'django.contrib.messages.middleware.MessageMiddleware' must " - "be in MIDDLEWARE in order to use the admin application.", - id="admin.E409", - ) - ) - if not _contains_subclass( - "django.contrib.sessions.middleware.SessionMiddleware", settings.MIDDLEWARE - ): - errors.append( - checks.Error( - "'django.contrib.sessions.middleware.SessionMiddleware' must " - "be in MIDDLEWARE in order to use the admin application.", - hint=( - "Insert " - "'django.contrib.sessions.middleware.SessionMiddleware' " - "before " - "'django.contrib.auth.middleware.AuthenticationMiddleware'." - ), - id="admin.E410", - ) - ) + if not _contains_subclass('django.contrib.auth.middleware.AuthenticationMiddleware', settings.MIDDLEWARE): + errors.append(checks.Error( + "'django.contrib.auth.middleware.AuthenticationMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + id='admin.E408', + )) + if not _contains_subclass('django.contrib.messages.middleware.MessageMiddleware', settings.MIDDLEWARE): + errors.append(checks.Error( + "'django.contrib.messages.middleware.MessageMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + id='admin.E409', + )) + if not _contains_subclass('django.contrib.sessions.middleware.SessionMiddleware', settings.MIDDLEWARE): + errors.append(checks.Error( + "'django.contrib.sessions.middleware.SessionMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + hint=( + "Insert " + "'django.contrib.sessions.middleware.SessionMiddleware' " + "before " + "'django.contrib.auth.middleware.AuthenticationMiddleware'." + ), + id='admin.E410', + )) return errors class BaseModelAdminChecks: + def check(self, admin_obj, **kwargs): return [ *self._check_autocomplete_fields(admin_obj), @@ -196,23 +167,12 @@ class BaseModelAdminChecks: Check that `autocomplete_fields` is a list or tuple of model fields. """ if not isinstance(obj.autocomplete_fields, (list, tuple)): - return must_be( - "a list or tuple", - option="autocomplete_fields", - obj=obj, - id="admin.E036", - ) + return must_be('a list or tuple', option='autocomplete_fields', obj=obj, id='admin.E036') else: - return list( - chain.from_iterable( - [ - self._check_autocomplete_fields_item( - obj, field_name, "autocomplete_fields[%d]" % index - ) - for index, field_name in enumerate(obj.autocomplete_fields) - ] - ) - ) + return list(chain.from_iterable([ + self._check_autocomplete_fields_item(obj, field_name, 'autocomplete_fields[%d]' % index) + for index, field_name in enumerate(obj.autocomplete_fields) + ])) def _check_autocomplete_fields_item(self, obj, field_name, label): """ @@ -223,109 +183,82 @@ class BaseModelAdminChecks: try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E037" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E037') else: if not field.many_to_many and not isinstance(field, models.ForeignKey): return must_be( - "a foreign key or a many-to-many field", - option=label, - obj=obj, - id="admin.E038", + 'a foreign key or a many-to-many field', + option=label, obj=obj, id='admin.E038' ) related_admin = obj.admin_site._registry.get(field.remote_field.model) if related_admin is None: return [ checks.Error( 'An admin for model "%s" has to be registered ' - "to be referenced by %s.autocomplete_fields." - % ( + 'to be referenced by %s.autocomplete_fields.' % ( field.remote_field.model.__name__, type(obj).__name__, ), obj=obj.__class__, - id="admin.E039", + id='admin.E039', ) ] elif not related_admin.search_fields: return [ checks.Error( '%s must define "search_fields", because it\'s ' - "referenced by %s.autocomplete_fields." - % ( + 'referenced by %s.autocomplete_fields.' % ( related_admin.__class__.__name__, type(obj).__name__, ), obj=obj.__class__, - id="admin.E040", + id='admin.E040', ) ] return [] def _check_raw_id_fields(self, obj): - """Check that `raw_id_fields` only contains field names that are listed - on the model.""" + """ Check that `raw_id_fields` only contains field names that are listed + on the model. """ if not isinstance(obj.raw_id_fields, (list, tuple)): - return must_be( - "a list or tuple", option="raw_id_fields", obj=obj, id="admin.E001" - ) + return must_be('a list or tuple', option='raw_id_fields', obj=obj, id='admin.E001') else: - return list( - chain.from_iterable( - self._check_raw_id_fields_item( - obj, field_name, "raw_id_fields[%d]" % index - ) - for index, field_name in enumerate(obj.raw_id_fields) - ) - ) + return list(chain.from_iterable( + self._check_raw_id_fields_item(obj, field_name, 'raw_id_fields[%d]' % index) + for index, field_name in enumerate(obj.raw_id_fields) + )) def _check_raw_id_fields_item(self, obj, field_name, label): - """Check an item of `raw_id_fields`, i.e. check that field named + """ Check an item of `raw_id_fields`, i.e. check that field named `field_name` exists in model `model` and is a ForeignKey or a - ManyToManyField.""" + ManyToManyField. """ try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E002" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E002') else: - # Using attname is not supported. - if field.name != field_name: - return refer_to_missing_field( - field=field_name, - option=label, - obj=obj, - id="admin.E002", - ) if not field.many_to_many and not isinstance(field, models.ForeignKey): - return must_be( - "a foreign key or a many-to-many field", - option=label, - obj=obj, - id="admin.E003", - ) + return must_be('a foreign key or a many-to-many field', option=label, obj=obj, id='admin.E003') else: return [] def _check_fields(self, obj): - """Check that `fields` only refer to existing fields, doesn't contain + """ Check that `fields` only refer to existing fields, doesn't contain duplicates. Check if at most one of `fields` and `fieldsets` is defined. """ if obj.fields is None: return [] elif not isinstance(obj.fields, (list, tuple)): - return must_be("a list or tuple", option="fields", obj=obj, id="admin.E004") + return must_be('a list or tuple', option='fields', obj=obj, id='admin.E004') elif obj.fieldsets: return [ checks.Error( "Both 'fieldsets' and 'fields' are specified.", obj=obj.__class__, - id="admin.E005", + id='admin.E005', ) ] fields = flatten(obj.fields) @@ -334,96 +267,75 @@ class BaseModelAdminChecks: checks.Error( "The value of 'fields' contains duplicate field(s).", obj=obj.__class__, - id="admin.E006", + id='admin.E006', ) ] - return list( - chain.from_iterable( - self._check_field_spec(obj, field_name, "fields") - for field_name in obj.fields - ) - ) + return list(chain.from_iterable( + self._check_field_spec(obj, field_name, 'fields') + for field_name in obj.fields + )) def _check_fieldsets(self, obj): - """Check that fieldsets is properly formatted and doesn't contain - duplicates.""" + """ Check that fieldsets is properly formatted and doesn't contain + duplicates. """ if obj.fieldsets is None: return [] elif not isinstance(obj.fieldsets, (list, tuple)): - return must_be( - "a list or tuple", option="fieldsets", obj=obj, id="admin.E007" - ) + return must_be('a list or tuple', option='fieldsets', obj=obj, id='admin.E007') else: seen_fields = [] - return list( - chain.from_iterable( - self._check_fieldsets_item( - obj, fieldset, "fieldsets[%d]" % index, seen_fields - ) - for index, fieldset in enumerate(obj.fieldsets) - ) - ) + return list(chain.from_iterable( + self._check_fieldsets_item(obj, fieldset, 'fieldsets[%d]' % index, seen_fields) + for index, fieldset in enumerate(obj.fieldsets) + )) def _check_fieldsets_item(self, obj, fieldset, label, seen_fields): - """Check an item of `fieldsets`, i.e. check that this is a pair of a - set name and a dictionary containing "fields" key.""" + """ Check an item of `fieldsets`, i.e. check that this is a pair of a + set name and a dictionary containing "fields" key. """ if not isinstance(fieldset, (list, tuple)): - return must_be("a list or tuple", option=label, obj=obj, id="admin.E008") + return must_be('a list or tuple', option=label, obj=obj, id='admin.E008') elif len(fieldset) != 2: - return must_be("of length 2", option=label, obj=obj, id="admin.E009") + return must_be('of length 2', option=label, obj=obj, id='admin.E009') elif not isinstance(fieldset[1], dict): - return must_be( - "a dictionary", option="%s[1]" % label, obj=obj, id="admin.E010" - ) - elif "fields" not in fieldset[1]: + return must_be('a dictionary', option='%s[1]' % label, obj=obj, id='admin.E010') + elif 'fields' not in fieldset[1]: return [ checks.Error( "The value of '%s[1]' must contain the key 'fields'." % label, obj=obj.__class__, - id="admin.E011", + id='admin.E011', ) ] - elif not isinstance(fieldset[1]["fields"], (list, tuple)): - return must_be( - "a list or tuple", - option="%s[1]['fields']" % label, - obj=obj, - id="admin.E008", - ) + elif not isinstance(fieldset[1]['fields'], (list, tuple)): + return must_be('a list or tuple', option="%s[1]['fields']" % label, obj=obj, id='admin.E008') - seen_fields.extend(flatten(fieldset[1]["fields"])) + seen_fields.extend(flatten(fieldset[1]['fields'])) if len(seen_fields) != len(set(seen_fields)): return [ checks.Error( "There are duplicate field(s) in '%s[1]'." % label, obj=obj.__class__, - id="admin.E012", + id='admin.E012', ) ] - return list( - chain.from_iterable( - self._check_field_spec(obj, fieldset_fields, '%s[1]["fields"]' % label) - for fieldset_fields in fieldset[1]["fields"] - ) - ) + return list(chain.from_iterable( + self._check_field_spec(obj, fieldset_fields, '%s[1]["fields"]' % label) + for fieldset_fields in fieldset[1]['fields'] + )) def _check_field_spec(self, obj, fields, label): - """`fields` should be an item of `fields` or an item of + """ `fields` should be an item of `fields` or an item of fieldset[1]['fields'] for any `fieldset` in `fieldsets`. It should be a - field name or a tuple of field names.""" + field name or a tuple of field names. """ if isinstance(fields, tuple): - return list( - chain.from_iterable( - self._check_field_spec_item( - obj, field_name, "%s[%d]" % (label, index) - ) - for index, field_name in enumerate(fields) - ) - ) + return list(chain.from_iterable( + self._check_field_spec_item(obj, field_name, "%s[%d]" % (label, index)) + for index, field_name in enumerate(fields) + )) else: return self._check_field_spec_item(obj, fields, label) @@ -441,154 +353,125 @@ class BaseModelAdminChecks: # be an extra field on the form. return [] else: - if ( - isinstance(field, models.ManyToManyField) - and not field.remote_field.through._meta.auto_created - ): + if (isinstance(field, models.ManyToManyField) and + not field.remote_field.through._meta.auto_created): return [ checks.Error( - "The value of '%s' cannot include the ManyToManyField " - "'%s', because that field manually specifies a " - "relationship model." % (label, field_name), + "The value of '%s' cannot include the ManyToManyField '%s', " + "because that field manually specifies a relationship model." + % (label, field_name), obj=obj.__class__, - id="admin.E013", + id='admin.E013', ) ] else: return [] def _check_exclude(self, obj): - """Check that exclude is a sequence without duplicates.""" + """ Check that exclude is a sequence without duplicates. """ if obj.exclude is None: # default value is None return [] elif not isinstance(obj.exclude, (list, tuple)): - return must_be( - "a list or tuple", option="exclude", obj=obj, id="admin.E014" - ) + return must_be('a list or tuple', option='exclude', obj=obj, id='admin.E014') elif len(obj.exclude) > len(set(obj.exclude)): return [ checks.Error( "The value of 'exclude' contains duplicate field(s).", obj=obj.__class__, - id="admin.E015", + id='admin.E015', ) ] else: return [] def _check_form(self, obj): - """Check that form subclasses BaseModelForm.""" + """ Check that form subclasses BaseModelForm. """ if not _issubclass(obj.form, BaseModelForm): - return must_inherit_from( - parent="BaseModelForm", option="form", obj=obj, id="admin.E016" - ) + return must_inherit_from(parent='BaseModelForm', option='form', + obj=obj, id='admin.E016') else: return [] def _check_filter_vertical(self, obj): - """Check that filter_vertical is a sequence of field names.""" + """ Check that filter_vertical is a sequence of field names. """ if not isinstance(obj.filter_vertical, (list, tuple)): - return must_be( - "a list or tuple", option="filter_vertical", obj=obj, id="admin.E017" - ) + return must_be('a list or tuple', option='filter_vertical', obj=obj, id='admin.E017') else: - return list( - chain.from_iterable( - self._check_filter_item( - obj, field_name, "filter_vertical[%d]" % index - ) - for index, field_name in enumerate(obj.filter_vertical) - ) - ) + return list(chain.from_iterable( + self._check_filter_item(obj, field_name, "filter_vertical[%d]" % index) + for index, field_name in enumerate(obj.filter_vertical) + )) def _check_filter_horizontal(self, obj): - """Check that filter_horizontal is a sequence of field names.""" + """ Check that filter_horizontal is a sequence of field names. """ if not isinstance(obj.filter_horizontal, (list, tuple)): - return must_be( - "a list or tuple", option="filter_horizontal", obj=obj, id="admin.E018" - ) + return must_be('a list or tuple', option='filter_horizontal', obj=obj, id='admin.E018') else: - return list( - chain.from_iterable( - self._check_filter_item( - obj, field_name, "filter_horizontal[%d]" % index - ) - for index, field_name in enumerate(obj.filter_horizontal) - ) - ) + return list(chain.from_iterable( + self._check_filter_item(obj, field_name, "filter_horizontal[%d]" % index) + for index, field_name in enumerate(obj.filter_horizontal) + )) def _check_filter_item(self, obj, field_name, label): - """Check one item of `filter_vertical` or `filter_horizontal`, i.e. - check that given field exists and is a ManyToManyField.""" + """ Check one item of `filter_vertical` or `filter_horizontal`, i.e. + check that given field exists and is a ManyToManyField. """ try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E019" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E019') else: if not field.many_to_many: - return must_be( - "a many-to-many field", option=label, obj=obj, id="admin.E020" - ) + return must_be('a many-to-many field', option=label, obj=obj, id='admin.E020') else: return [] def _check_radio_fields(self, obj): - """Check that `radio_fields` is a dictionary.""" + """ Check that `radio_fields` is a dictionary. """ if not isinstance(obj.radio_fields, dict): - return must_be( - "a dictionary", option="radio_fields", obj=obj, id="admin.E021" - ) + return must_be('a dictionary', option='radio_fields', obj=obj, id='admin.E021') else: - return list( - chain.from_iterable( - self._check_radio_fields_key(obj, field_name, "radio_fields") - + self._check_radio_fields_value( - obj, val, 'radio_fields["%s"]' % field_name - ) - for field_name, val in obj.radio_fields.items() - ) - ) + return list(chain.from_iterable( + self._check_radio_fields_key(obj, field_name, 'radio_fields') + + self._check_radio_fields_value(obj, val, 'radio_fields["%s"]' % field_name) + for field_name, val in obj.radio_fields.items() + )) def _check_radio_fields_key(self, obj, field_name, label): - """Check that a key of `radio_fields` dictionary is name of existing - field and that the field is a ForeignKey or has `choices` defined.""" + """ Check that a key of `radio_fields` dictionary is name of existing + field and that the field is a ForeignKey or has `choices` defined. """ try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E022" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E022') else: if not (isinstance(field, models.ForeignKey) or field.choices): return [ checks.Error( "The value of '%s' refers to '%s', which is not an " - "instance of ForeignKey, and does not have a 'choices' " - "definition." % (label, field_name), + "instance of ForeignKey, and does not have a 'choices' definition." % ( + label, field_name + ), obj=obj.__class__, - id="admin.E023", + id='admin.E023', ) ] else: return [] def _check_radio_fields_value(self, obj, val, label): - """Check type of a value of `radio_fields` dictionary.""" + """ Check type of a value of `radio_fields` dictionary. """ from django.contrib.admin.options import HORIZONTAL, VERTICAL if val not in (HORIZONTAL, VERTICAL): return [ checks.Error( - "The value of '%s' must be either admin.HORIZONTAL or " - "admin.VERTICAL." % label, + "The value of '%s' must be either admin.HORIZONTAL or admin.VERTICAL." % label, obj=obj.__class__, - id="admin.E024", + id='admin.E024', ) ] else: @@ -598,111 +481,87 @@ class BaseModelAdminChecks: if not callable(obj.view_on_site) and not isinstance(obj.view_on_site, bool): return [ checks.Error( - "The value of 'view_on_site' must be a callable or a boolean " - "value.", + "The value of 'view_on_site' must be a callable or a boolean value.", obj=obj.__class__, - id="admin.E025", + id='admin.E025', ) ] else: return [] def _check_prepopulated_fields(self, obj): - """Check that `prepopulated_fields` is a dictionary containing allowed - field types.""" + """ Check that `prepopulated_fields` is a dictionary containing allowed + field types. """ if not isinstance(obj.prepopulated_fields, dict): - return must_be( - "a dictionary", option="prepopulated_fields", obj=obj, id="admin.E026" - ) + return must_be('a dictionary', option='prepopulated_fields', obj=obj, id='admin.E026') else: - return list( - chain.from_iterable( - self._check_prepopulated_fields_key( - obj, field_name, "prepopulated_fields" - ) - + self._check_prepopulated_fields_value( - obj, val, 'prepopulated_fields["%s"]' % field_name - ) - for field_name, val in obj.prepopulated_fields.items() - ) - ) + return list(chain.from_iterable( + self._check_prepopulated_fields_key(obj, field_name, 'prepopulated_fields') + + self._check_prepopulated_fields_value(obj, val, 'prepopulated_fields["%s"]' % field_name) + for field_name, val in obj.prepopulated_fields.items() + )) def _check_prepopulated_fields_key(self, obj, field_name, label): - """Check a key of `prepopulated_fields` dictionary, i.e. check that it + """ Check a key of `prepopulated_fields` dictionary, i.e. check that it is a name of existing field and the field is one of the allowed types. """ try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E027" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E027') else: - if isinstance( - field, (models.DateTimeField, models.ForeignKey, models.ManyToManyField) - ): + if isinstance(field, (models.DateTimeField, models.ForeignKey, models.ManyToManyField)): return [ checks.Error( - "The value of '%s' refers to '%s', which must not be a " - "DateTimeField, a ForeignKey, a OneToOneField, or a " - "ManyToManyField." % (label, field_name), + "The value of '%s' refers to '%s', which must not be a DateTimeField, " + "a ForeignKey, a OneToOneField, or a ManyToManyField." % (label, field_name), obj=obj.__class__, - id="admin.E028", + id='admin.E028', ) ] else: return [] def _check_prepopulated_fields_value(self, obj, val, label): - """Check a value of `prepopulated_fields` dictionary, i.e. it's an - iterable of existing fields.""" + """ Check a value of `prepopulated_fields` dictionary, i.e. it's an + iterable of existing fields. """ if not isinstance(val, (list, tuple)): - return must_be("a list or tuple", option=label, obj=obj, id="admin.E029") + return must_be('a list or tuple', option=label, obj=obj, id='admin.E029') else: - return list( - chain.from_iterable( - self._check_prepopulated_fields_value_item( - obj, subfield_name, "%s[%r]" % (label, index) - ) - for index, subfield_name in enumerate(val) - ) - ) + return list(chain.from_iterable( + self._check_prepopulated_fields_value_item(obj, subfield_name, "%s[%r]" % (label, index)) + for index, subfield_name in enumerate(val) + )) def _check_prepopulated_fields_value_item(self, obj, field_name, label): - """For `prepopulated_fields` equal to {"slug": ("title",)}, - `field_name` is "title".""" + """ For `prepopulated_fields` equal to {"slug": ("title",)}, + `field_name` is "title". """ try: obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E030" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E030') else: return [] def _check_ordering(self, obj): - """Check that ordering refers to existing fields or is random.""" + """ Check that ordering refers to existing fields or is random. """ # ordering = None if obj.ordering is None: # The default value is None return [] elif not isinstance(obj.ordering, (list, tuple)): - return must_be( - "a list or tuple", option="ordering", obj=obj, id="admin.E031" - ) + return must_be('a list or tuple', option='ordering', obj=obj, id='admin.E031') else: - return list( - chain.from_iterable( - self._check_ordering_item(obj, field_name, "ordering[%d]" % index) - for index, field_name in enumerate(obj.ordering) - ) - ) + return list(chain.from_iterable( + self._check_ordering_item(obj, field_name, 'ordering[%d]' % index) + for index, field_name in enumerate(obj.ordering) + )) def _check_ordering_item(self, obj, field_name, label): - """Check that `ordering` refers to existing fields.""" + """ Check that `ordering` refers to existing fields. """ if isinstance(field_name, (Combinable, models.OrderBy)): if not isinstance(field_name, models.OrderBy): field_name = field_name.asc() @@ -710,54 +569,46 @@ class BaseModelAdminChecks: field_name = field_name.expression.name else: return [] - if field_name == "?" and len(obj.ordering) != 1: + if field_name == '?' and len(obj.ordering) != 1: return [ checks.Error( "The value of 'ordering' has the random ordering marker '?', " "but contains other fields as well.", hint='Either remove the "?", or remove the other fields.', obj=obj.__class__, - id="admin.E032", + id='admin.E032', ) ] - elif field_name == "?": + elif field_name == '?': return [] elif LOOKUP_SEP in field_name: # Skip ordering in the format field1__field2 (FIXME: checking # this format would be nice, but it's a little fiddly). return [] else: - if field_name.startswith("-"): + if field_name.startswith('-'): field_name = field_name[1:] - if field_name == "pk": + if field_name == 'pk': return [] try: obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E033" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E033') else: return [] def _check_readonly_fields(self, obj): - """Check that readonly_fields refers to proper attribute or field.""" + """ Check that readonly_fields refers to proper attribute or field. """ if obj.readonly_fields == (): return [] elif not isinstance(obj.readonly_fields, (list, tuple)): - return must_be( - "a list or tuple", option="readonly_fields", obj=obj, id="admin.E034" - ) + return must_be('a list or tuple', option='readonly_fields', obj=obj, id='admin.E034') else: - return list( - chain.from_iterable( - self._check_readonly_fields_item( - obj, field_name, "readonly_fields[%d]" % index - ) - for index, field_name in enumerate(obj.readonly_fields) - ) - ) + return list(chain.from_iterable( + self._check_readonly_fields_item(obj, field_name, "readonly_fields[%d]" % index) + for index, field_name in enumerate(obj.readonly_fields) + )) def _check_readonly_fields_item(self, obj, field_name, label): if callable(field_name): @@ -773,14 +624,11 @@ class BaseModelAdminChecks: return [ checks.Error( "The value of '%s' is not a callable, an attribute of " - "'%s', or an attribute of '%s'." - % ( - label, - obj.__class__.__name__, - obj.model._meta.label, + "'%s', or an attribute of '%s'." % ( + label, obj.__class__.__name__, obj.model._meta.label, ), obj=obj.__class__, - id="admin.E035", + id='admin.E035', ) ] else: @@ -788,6 +636,7 @@ class BaseModelAdminChecks: class ModelAdminChecks(BaseModelAdminChecks): + def check(self, admin_obj, **kwargs): return [ *super().check(admin_obj), @@ -808,46 +657,44 @@ class ModelAdminChecks(BaseModelAdminChecks): ] def _check_save_as(self, obj): - """Check save_as is a boolean.""" + """ Check save_as is a boolean. """ if not isinstance(obj.save_as, bool): - return must_be("a boolean", option="save_as", obj=obj, id="admin.E101") + return must_be('a boolean', option='save_as', + obj=obj, id='admin.E101') else: return [] def _check_save_on_top(self, obj): - """Check save_on_top is a boolean.""" + """ Check save_on_top is a boolean. """ if not isinstance(obj.save_on_top, bool): - return must_be("a boolean", option="save_on_top", obj=obj, id="admin.E102") + return must_be('a boolean', option='save_on_top', + obj=obj, id='admin.E102') else: return [] def _check_inlines(self, obj): - """Check all inline model admin classes.""" + """ Check all inline model admin classes. """ if not isinstance(obj.inlines, (list, tuple)): - return must_be( - "a list or tuple", option="inlines", obj=obj, id="admin.E103" - ) + return must_be('a list or tuple', option='inlines', obj=obj, id='admin.E103') else: - return list( - chain.from_iterable( - self._check_inlines_item(obj, item, "inlines[%d]" % index) - for index, item in enumerate(obj.inlines) - ) - ) + return list(chain.from_iterable( + self._check_inlines_item(obj, item, "inlines[%d]" % index) + for index, item in enumerate(obj.inlines) + )) def _check_inlines_item(self, obj, inline, label): - """Check one inline model admin.""" + """ Check one inline model admin. """ try: - inline_label = inline.__module__ + "." + inline.__name__ + inline_label = inline.__module__ + '.' + inline.__name__ except AttributeError: return [ checks.Error( "'%s' must inherit from 'InlineModelAdmin'." % obj, obj=obj.__class__, - id="admin.E104", + id='admin.E104', ) ] @@ -858,7 +705,7 @@ class ModelAdminChecks(BaseModelAdminChecks): checks.Error( "'%s' must inherit from 'InlineModelAdmin'." % inline_label, obj=obj.__class__, - id="admin.E104", + id='admin.E104', ) ] elif not inline.model: @@ -866,30 +713,25 @@ class ModelAdminChecks(BaseModelAdminChecks): checks.Error( "'%s' must have a 'model' attribute." % inline_label, obj=obj.__class__, - id="admin.E105", + id='admin.E105', ) ] elif not _issubclass(inline.model, models.Model): - return must_be( - "a Model", option="%s.model" % inline_label, obj=obj, id="admin.E106" - ) + return must_be('a Model', option='%s.model' % inline_label, obj=obj, id='admin.E106') else: return inline(obj.model, obj.admin_site).check() def _check_list_display(self, obj): - """Check that list_display only contains fields or usable attributes.""" + """ Check that list_display only contains fields or usable attributes. + """ if not isinstance(obj.list_display, (list, tuple)): - return must_be( - "a list or tuple", option="list_display", obj=obj, id="admin.E107" - ) + return must_be('a list or tuple', option='list_display', obj=obj, id='admin.E107') else: - return list( - chain.from_iterable( - self._check_list_display_item(obj, item, "list_display[%d]" % index) - for index, item in enumerate(obj.list_display) - ) - ) + return list(chain.from_iterable( + self._check_list_display_item(obj, item, "list_display[%d]" % index) + for index, item in enumerate(obj.list_display) + )) def _check_list_display_item(self, obj, item, label): if callable(item): @@ -906,15 +748,12 @@ class ModelAdminChecks(BaseModelAdminChecks): checks.Error( "The value of '%s' refers to '%s', which is not a " "callable, an attribute of '%s', or an attribute or " - "method on '%s'." - % ( - label, - item, - obj.__class__.__name__, + "method on '%s'." % ( + label, item, obj.__class__.__name__, obj.model._meta.label, ), obj=obj.__class__, - id="admin.E108", + id='admin.E108', ) ] if isinstance(field, models.ManyToManyField): @@ -922,44 +761,37 @@ class ModelAdminChecks(BaseModelAdminChecks): checks.Error( "The value of '%s' must not be a ManyToManyField." % label, obj=obj.__class__, - id="admin.E109", + id='admin.E109', ) ] return [] def _check_list_display_links(self, obj): - """Check that list_display_links is a unique subset of list_display.""" + """ Check that list_display_links is a unique subset of list_display. + """ from django.contrib.admin.options import ModelAdmin if obj.list_display_links is None: return [] elif not isinstance(obj.list_display_links, (list, tuple)): - return must_be( - "a list, a tuple, or None", - option="list_display_links", - obj=obj, - id="admin.E110", - ) + return must_be('a list, a tuple, or None', option='list_display_links', obj=obj, id='admin.E110') # Check only if ModelAdmin.get_list_display() isn't overridden. elif obj.get_list_display.__func__ is ModelAdmin.get_list_display: - return list( - chain.from_iterable( - self._check_list_display_links_item( - obj, field_name, "list_display_links[%d]" % index - ) - for index, field_name in enumerate(obj.list_display_links) - ) - ) + return list(chain.from_iterable( + self._check_list_display_links_item(obj, field_name, "list_display_links[%d]" % index) + for index, field_name in enumerate(obj.list_display_links) + )) return [] def _check_list_display_links_item(self, obj, field_name, label): if field_name not in obj.list_display: return [ checks.Error( - "The value of '%s' refers to '%s', which is not defined in " - "'list_display'." % (label, field_name), + "The value of '%s' refers to '%s', which is not defined in 'list_display'." % ( + label, field_name + ), obj=obj.__class__, - id="admin.E111", + id='admin.E111', ) ] else: @@ -967,16 +799,12 @@ class ModelAdminChecks(BaseModelAdminChecks): def _check_list_filter(self, obj): if not isinstance(obj.list_filter, (list, tuple)): - return must_be( - "a list or tuple", option="list_filter", obj=obj, id="admin.E112" - ) + return must_be('a list or tuple', option='list_filter', obj=obj, id='admin.E112') else: - return list( - chain.from_iterable( - self._check_list_filter_item(obj, item, "list_filter[%d]" % index) - for index, item in enumerate(obj.list_filter) - ) - ) + return list(chain.from_iterable( + self._check_list_filter_item(obj, item, "list_filter[%d]" % index) + for index, item in enumerate(obj.list_filter) + )) def _check_list_filter_item(self, obj, item, label): """ @@ -991,17 +819,15 @@ class ModelAdminChecks(BaseModelAdminChecks): if callable(item) and not isinstance(item, models.Field): # If item is option 3, it should be a ListFilter... if not _issubclass(item, ListFilter): - return must_inherit_from( - parent="ListFilter", option=label, obj=obj, id="admin.E113" - ) + return must_inherit_from(parent='ListFilter', option=label, + obj=obj, id='admin.E113') # ... but not a FieldListFilter. elif issubclass(item, FieldListFilter): return [ checks.Error( - "The value of '%s' must not inherit from 'FieldListFilter'." - % label, + "The value of '%s' must not inherit from 'FieldListFilter'." % label, obj=obj.__class__, - id="admin.E114", + id='admin.E114', ) ] else: @@ -1010,12 +836,7 @@ class ModelAdminChecks(BaseModelAdminChecks): # item is option #2 field, list_filter_class = item if not _issubclass(list_filter_class, FieldListFilter): - return must_inherit_from( - parent="FieldListFilter", - option="%s[1]" % label, - obj=obj, - id="admin.E115", - ) + return must_inherit_from(parent='FieldListFilter', option='%s[1]' % label, obj=obj, id='admin.E115') else: return [] else: @@ -1028,73 +849,55 @@ class ModelAdminChecks(BaseModelAdminChecks): except (NotRelationField, FieldDoesNotExist): return [ checks.Error( - "The value of '%s' refers to '%s', which does not refer to a " - "Field." % (label, field), + "The value of '%s' refers to '%s', which does not refer to a Field." % (label, field), obj=obj.__class__, - id="admin.E116", + id='admin.E116', ) ] else: return [] def _check_list_select_related(self, obj): - """Check that list_select_related is a boolean, a list or a tuple.""" + """ Check that list_select_related is a boolean, a list or a tuple. """ if not isinstance(obj.list_select_related, (bool, list, tuple)): - return must_be( - "a boolean, tuple or list", - option="list_select_related", - obj=obj, - id="admin.E117", - ) + return must_be('a boolean, tuple or list', option='list_select_related', obj=obj, id='admin.E117') else: return [] def _check_list_per_page(self, obj): - """Check that list_per_page is an integer.""" + """ Check that list_per_page is an integer. """ if not isinstance(obj.list_per_page, int): - return must_be( - "an integer", option="list_per_page", obj=obj, id="admin.E118" - ) + return must_be('an integer', option='list_per_page', obj=obj, id='admin.E118') else: return [] def _check_list_max_show_all(self, obj): - """Check that list_max_show_all is an integer.""" + """ Check that list_max_show_all is an integer. """ if not isinstance(obj.list_max_show_all, int): - return must_be( - "an integer", option="list_max_show_all", obj=obj, id="admin.E119" - ) + return must_be('an integer', option='list_max_show_all', obj=obj, id='admin.E119') else: return [] def _check_list_editable(self, obj): - """Check that list_editable is a sequence of editable fields from - list_display without first element.""" + """ Check that list_editable is a sequence of editable fields from + list_display without first element. """ if not isinstance(obj.list_editable, (list, tuple)): - return must_be( - "a list or tuple", option="list_editable", obj=obj, id="admin.E120" - ) + return must_be('a list or tuple', option='list_editable', obj=obj, id='admin.E120') else: - return list( - chain.from_iterable( - self._check_list_editable_item( - obj, item, "list_editable[%d]" % index - ) - for index, item in enumerate(obj.list_editable) - ) - ) + return list(chain.from_iterable( + self._check_list_editable_item(obj, item, "list_editable[%d]" % index) + for index, item in enumerate(obj.list_editable) + )) def _check_list_editable_item(self, obj, field_name, label): try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: - return refer_to_missing_field( - field=field_name, option=label, obj=obj, id="admin.E121" - ) + return refer_to_missing_field(field=field_name, option=label, obj=obj, id='admin.E121') else: if field_name not in obj.list_display: return [ @@ -1102,58 +905,54 @@ class ModelAdminChecks(BaseModelAdminChecks): "The value of '%s' refers to '%s', which is not " "contained in 'list_display'." % (label, field_name), obj=obj.__class__, - id="admin.E122", + id='admin.E122', ) ] elif obj.list_display_links and field_name in obj.list_display_links: return [ checks.Error( - "The value of '%s' cannot be in both 'list_editable' and " - "'list_display_links'." % field_name, + "The value of '%s' cannot be in both 'list_editable' and 'list_display_links'." % field_name, obj=obj.__class__, - id="admin.E123", + id='admin.E123', ) ] # If list_display[0] is in list_editable, check that # list_display_links is set. See #22792 and #26229 for use cases. - elif ( - obj.list_display[0] == field_name - and not obj.list_display_links - and obj.list_display_links is not None - ): + elif (obj.list_display[0] == field_name and not obj.list_display_links and + obj.list_display_links is not None): return [ checks.Error( - "The value of '%s' refers to the first field in 'list_display' " - "('%s'), which cannot be used unless 'list_display_links' is " - "set." % (label, obj.list_display[0]), + "The value of '%s' refers to the first field in 'list_display' ('%s'), " + "which cannot be used unless 'list_display_links' is set." % ( + label, obj.list_display[0] + ), obj=obj.__class__, - id="admin.E124", + id='admin.E124', ) ] elif not field.editable: return [ checks.Error( - "The value of '%s' refers to '%s', which is not editable " - "through the admin." % (label, field_name), + "The value of '%s' refers to '%s', which is not editable through the admin." % ( + label, field_name + ), obj=obj.__class__, - id="admin.E125", + id='admin.E125', ) ] else: return [] def _check_search_fields(self, obj): - """Check search_fields is a sequence.""" + """ Check search_fields is a sequence. """ if not isinstance(obj.search_fields, (list, tuple)): - return must_be( - "a list or tuple", option="search_fields", obj=obj, id="admin.E126" - ) + return must_be('a list or tuple', option='search_fields', obj=obj, id='admin.E126') else: return [] def _check_date_hierarchy(self, obj): - """Check that date_hierarchy refers to DateField or DateTimeField.""" + """ Check that date_hierarchy refers to DateField or DateTimeField. """ if obj.date_hierarchy is None: return [] @@ -1166,17 +965,12 @@ class ModelAdminChecks(BaseModelAdminChecks): "The value of 'date_hierarchy' refers to '%s', which " "does not refer to a Field." % obj.date_hierarchy, obj=obj.__class__, - id="admin.E127", + id='admin.E127', ) ] else: if not isinstance(field, (models.DateField, models.DateTimeField)): - return must_be( - "a DateField or DateTimeField", - option="date_hierarchy", - obj=obj, - id="admin.E128", - ) + return must_be('a DateField or DateTimeField', option='date_hierarchy', obj=obj, id='admin.E128') else: return [] @@ -1188,21 +982,20 @@ class ModelAdminChecks(BaseModelAdminChecks): actions = obj._get_base_actions() errors = [] for func, name, _ in actions: - if not hasattr(func, "allowed_permissions"): + if not hasattr(func, 'allowed_permissions'): continue for permission in func.allowed_permissions: - method_name = "has_%s_permission" % permission + method_name = 'has_%s_permission' % permission if not hasattr(obj, method_name): errors.append( checks.Error( - "%s must define a %s() method for the %s action." - % ( + '%s must define a %s() method for the %s action.' % ( obj.__class__.__name__, method_name, func.__name__, ), obj=obj.__class__, - id="admin.E129", + id='admin.E129', ) ) return errors @@ -1213,22 +1006,20 @@ class ModelAdminChecks(BaseModelAdminChecks): names = collections.Counter(name for _, name, _ in obj._get_base_actions()) for name, count in names.items(): if count > 1: - errors.append( - checks.Error( - "__name__ attributes of actions defined in %s must be " - "unique. Name %r is not unique." - % ( - obj.__class__.__name__, - name, - ), - obj=obj.__class__, - id="admin.E130", - ) - ) + errors.append(checks.Error( + '__name__ attributes of actions defined in %s must be ' + 'unique. Name %r is not unique.' % ( + obj.__class__.__name__, + name, + ), + obj=obj.__class__, + id='admin.E130', + )) return errors class InlineModelAdminChecks(BaseModelAdminChecks): + def check(self, inline_obj, **kwargs): parent_model = inline_obj.parent_model return [ @@ -1260,13 +1051,11 @@ class InlineModelAdminChecks(BaseModelAdminChecks): return [ checks.Error( "Cannot exclude the field '%s', because it is the foreign key " - "to the parent model '%s'." - % ( - fk.name, - parent_model._meta.label, + "to the parent model '%s'." % ( + fk.name, parent_model._meta.label, ), obj=obj.__class__, - id="admin.E201", + id='admin.E201', ) ] else: @@ -1276,45 +1065,43 @@ class InlineModelAdminChecks(BaseModelAdminChecks): try: _get_foreign_key(parent_model, obj.model, fk_name=obj.fk_name) except ValueError as e: - return [checks.Error(e.args[0], obj=obj.__class__, id="admin.E202")] + return [checks.Error(e.args[0], obj=obj.__class__, id='admin.E202')] else: return [] def _check_extra(self, obj): - """Check that extra is an integer.""" + """ Check that extra is an integer. """ if not isinstance(obj.extra, int): - return must_be("an integer", option="extra", obj=obj, id="admin.E203") + return must_be('an integer', option='extra', obj=obj, id='admin.E203') else: return [] def _check_max_num(self, obj): - """Check that max_num is an integer.""" + """ Check that max_num is an integer. """ if obj.max_num is None: return [] elif not isinstance(obj.max_num, int): - return must_be("an integer", option="max_num", obj=obj, id="admin.E204") + return must_be('an integer', option='max_num', obj=obj, id='admin.E204') else: return [] def _check_min_num(self, obj): - """Check that min_num is an integer.""" + """ Check that min_num is an integer. """ if obj.min_num is None: return [] elif not isinstance(obj.min_num, int): - return must_be("an integer", option="min_num", obj=obj, id="admin.E205") + return must_be('an integer', option='min_num', obj=obj, id='admin.E205') else: return [] def _check_formset(self, obj): - """Check formset is a subclass of BaseModelFormSet.""" + """ Check formset is a subclass of BaseModelFormSet. """ if not _issubclass(obj.formset, BaseModelFormSet): - return must_inherit_from( - parent="BaseModelFormSet", option="formset", obj=obj, id="admin.E206" - ) + return must_inherit_from(parent='BaseModelFormSet', option='formset', obj=obj, id='admin.E206') else: return [] @@ -1342,8 +1129,8 @@ def must_inherit_from(parent, option, obj, id): def refer_to_missing_field(field, option, obj, id): return [ checks.Error( - "The value of '%s' refers to '%s', which is not a field of '%s'." - % (option, field, obj.model._meta.label), + "The value of '%s' refers to '%s', which is not an attribute of " + "'%s'." % (option, field, obj.model._meta.label), obj=obj.__class__, id=id, ), diff --git a/venv/Lib/site-packages/django/contrib/admin/decorators.py b/venv/Lib/site-packages/django/contrib/admin/decorators.py index d3ff56a..4de9958 100644 --- a/venv/Lib/site-packages/django/contrib/admin/decorators.py +++ b/venv/Lib/site-packages/django/contrib/admin/decorators.py @@ -17,23 +17,19 @@ def action(function=None, *, permissions=None, description=None): make_published.allowed_permissions = ['publish'] make_published.short_description = 'Mark selected stories as published' """ - def decorator(func): if permissions is not None: func.allowed_permissions = permissions if description is not None: func.short_description = description return func - if function is None: return decorator else: return decorator(function) -def display( - function=None, *, boolean=None, ordering=None, description=None, empty_value=None -): +def display(function=None, *, boolean=None, ordering=None, description=None, empty_value=None): """ Conveniently add attributes to a display function:: @@ -54,12 +50,11 @@ def display( is_published.admin_order_field = '-publish_date' is_published.short_description = 'Is Published?' """ - def decorator(func): if boolean is not None and empty_value is not None: raise ValueError( - "The boolean and empty_value arguments to the @display " - "decorator are mutually exclusive." + 'The boolean and empty_value arguments to the @display ' + 'decorator are mutually exclusive.' ) if boolean is not None: func.boolean = boolean @@ -70,7 +65,6 @@ def display( if empty_value is not None: func.empty_value_display = empty_value return func - if function is None: return decorator else: @@ -89,23 +83,21 @@ def register(*models, site=None): The `site` kwarg is an admin site to use instead of the default admin site. """ from django.contrib.admin import ModelAdmin - from django.contrib.admin.sites import AdminSite - from django.contrib.admin.sites import site as default_site + from django.contrib.admin.sites import AdminSite, site as default_site def _model_admin_wrapper(admin_class): if not models: - raise ValueError("At least one model must be passed to register.") + raise ValueError('At least one model must be passed to register.') admin_site = site or default_site if not isinstance(admin_site, AdminSite): - raise ValueError("site must subclass AdminSite") + raise ValueError('site must subclass AdminSite') if not issubclass(admin_class, ModelAdmin): - raise ValueError("Wrapped class must subclass ModelAdmin.") + raise ValueError('Wrapped class must subclass ModelAdmin.') admin_site.register(models, admin_class=admin_class) return admin_class - return _model_admin_wrapper diff --git a/venv/Lib/site-packages/django/contrib/admin/exceptions.py b/venv/Lib/site-packages/django/contrib/admin/exceptions.py index 2ee8f62..f619bc2 100644 --- a/venv/Lib/site-packages/django/contrib/admin/exceptions.py +++ b/venv/Lib/site-packages/django/contrib/admin/exceptions.py @@ -3,11 +3,9 @@ from django.core.exceptions import SuspiciousOperation class DisallowedModelAdminLookup(SuspiciousOperation): """Invalid filter was passed to admin view via URL querystring""" - pass class DisallowedModelAdminToField(SuspiciousOperation): """Invalid to_field was passed to admin view via URL query string""" - pass diff --git a/venv/Lib/site-packages/django/contrib/admin/filters.py b/venv/Lib/site-packages/django/contrib/admin/filters.py index 0113464..7f46908 100644 --- a/venv/Lib/site-packages/django/contrib/admin/filters.py +++ b/venv/Lib/site-packages/django/contrib/admin/filters.py @@ -9,9 +9,7 @@ import datetime from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.utils import ( - get_model_from_relation, - prepare_lookup_value, - reverse_field_path, + get_model_from_relation, prepare_lookup_value, reverse_field_path, ) from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models @@ -21,7 +19,7 @@ from django.utils.translation import gettext_lazy as _ class ListFilter: title = None # Human-readable title to appear in the right sidebar. - template = "admin/filter.html" + template = 'admin/filter.html' def __init__(self, request, params, model, model_admin): # This dictionary will eventually contain the request's query string @@ -37,9 +35,7 @@ class ListFilter: """ Return True if some choices would be output for this filter. """ - raise NotImplementedError( - "subclasses of ListFilter must provide a has_output() method" - ) + raise NotImplementedError('subclasses of ListFilter must provide a has_output() method') def choices(self, changelist): """ @@ -47,26 +43,20 @@ class ListFilter: `changelist` is the ChangeList to be displayed. """ - raise NotImplementedError( - "subclasses of ListFilter must provide a choices() method" - ) + raise NotImplementedError('subclasses of ListFilter must provide a choices() method') def queryset(self, request, queryset): """ Return the filtered queryset. """ - raise NotImplementedError( - "subclasses of ListFilter must provide a queryset() method" - ) + raise NotImplementedError('subclasses of ListFilter must provide a queryset() method') def expected_parameters(self): """ Return the list of parameter names that are expected from the request's query string and that will be used by this filter. """ - raise NotImplementedError( - "subclasses of ListFilter must provide an expected_parameters() method" - ) + raise NotImplementedError('subclasses of ListFilter must provide an expected_parameters() method') class SimpleListFilter(ListFilter): @@ -104,8 +94,8 @@ class SimpleListFilter(ListFilter): Must be overridden to return a list of tuples (value, verbose value) """ raise NotImplementedError( - "The SimpleListFilter.lookups() method must be overridden to " - "return a list of tuples (value, verbose value)." + 'The SimpleListFilter.lookups() method must be overridden to ' + 'return a list of tuples (value, verbose value).' ) def expected_parameters(self): @@ -113,17 +103,15 @@ class SimpleListFilter(ListFilter): def choices(self, changelist): yield { - "selected": self.value() is None, - "query_string": changelist.get_query_string(remove=[self.parameter_name]), - "display": _("All"), + 'selected': self.value() is None, + 'query_string': changelist.get_query_string(remove=[self.parameter_name]), + 'display': _('All'), } for lookup, title in self.lookup_choices: yield { - "selected": self.value() == str(lookup), - "query_string": changelist.get_query_string( - {self.parameter_name: lookup} - ), - "display": title, + 'selected': self.value() == str(lookup), + 'query_string': changelist.get_query_string({self.parameter_name: lookup}), + 'display': title, } @@ -134,7 +122,7 @@ class FieldListFilter(ListFilter): def __init__(self, field, request, params, model, model_admin, field_path): self.field = field self.field_path = field_path - self.title = getattr(field, "verbose_name", field_path) + self.title = getattr(field, 'verbose_name', field_path) super().__init__(request, params, model, model_admin) for p in self.expected_parameters(): if p in params: @@ -159,8 +147,7 @@ class FieldListFilter(ListFilter): # of fields with some custom filters. The first found in the list # is used in priority. cls._field_list_filters.insert( - cls._take_priority_index, (test, list_filter_class) - ) + cls._take_priority_index, (test, list_filter_class)) cls._take_priority_index += 1 else: cls._field_list_filters.append((test, list_filter_class)) @@ -169,21 +156,19 @@ class FieldListFilter(ListFilter): def create(cls, field, request, params, model, model_admin, field_path): for test, list_filter_class in cls._field_list_filters: if test(field): - return list_filter_class( - field, request, params, model, model_admin, field_path=field_path - ) + return list_filter_class(field, request, params, model, model_admin, field_path=field_path) class RelatedFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): other_model = get_model_from_relation(field) - self.lookup_kwarg = "%s__%s__exact" % (field_path, field.target_field.name) - self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_kwarg = '%s__%s__exact' % (field_path, field.target_field.name) + self.lookup_kwarg_isnull = '%s__isnull' % field_path self.lookup_val = params.get(self.lookup_kwarg) self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) super().__init__(field, request, params, model, model_admin, field_path) self.lookup_choices = self.field_choices(field, request, model_admin) - if hasattr(field, "verbose_name"): + if hasattr(field, 'verbose_name'): self.lookup_title = field.verbose_name else: self.lookup_title = other_model._meta.verbose_name @@ -223,27 +208,21 @@ class RelatedFieldListFilter(FieldListFilter): def choices(self, changelist): yield { - "selected": self.lookup_val is None and not self.lookup_val_isnull, - "query_string": changelist.get_query_string( - remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] - ), - "display": _("All"), + 'selected': self.lookup_val is None and not self.lookup_val_isnull, + 'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]), + 'display': _('All'), } for pk_val, val in self.lookup_choices: yield { - "selected": self.lookup_val == str(pk_val), - "query_string": changelist.get_query_string( - {self.lookup_kwarg: pk_val}, [self.lookup_kwarg_isnull] - ), - "display": val, + 'selected': self.lookup_val == str(pk_val), + 'query_string': changelist.get_query_string({self.lookup_kwarg: pk_val}, [self.lookup_kwarg_isnull]), + 'display': val, } if self.include_empty_choice: yield { - "selected": bool(self.lookup_val_isnull), - "query_string": changelist.get_query_string( - {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] - ), - "display": self.empty_value_display, + 'selected': bool(self.lookup_val_isnull), + 'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]), + 'display': self.empty_value_display, } @@ -252,19 +231,14 @@ FieldListFilter.register(lambda f: f.remote_field, RelatedFieldListFilter) class BooleanFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): - self.lookup_kwarg = "%s__exact" % field_path - self.lookup_kwarg2 = "%s__isnull" % field_path + self.lookup_kwarg = '%s__exact' % field_path + self.lookup_kwarg2 = '%s__isnull' % field_path self.lookup_val = params.get(self.lookup_kwarg) self.lookup_val2 = params.get(self.lookup_kwarg2) super().__init__(field, request, params, model, model_admin, field_path) - if ( - self.used_parameters - and self.lookup_kwarg in self.used_parameters - and self.used_parameters[self.lookup_kwarg] in ("1", "0") - ): - self.used_parameters[self.lookup_kwarg] = bool( - int(self.used_parameters[self.lookup_kwarg]) - ) + if (self.used_parameters and self.lookup_kwarg in self.used_parameters and + self.used_parameters[self.lookup_kwarg] in ('1', '0')): + self.used_parameters[self.lookup_kwarg] = bool(int(self.used_parameters[self.lookup_kwarg])) def expected_parameters(self): return [self.lookup_kwarg, self.lookup_kwarg2] @@ -272,36 +246,30 @@ class BooleanFieldListFilter(FieldListFilter): def choices(self, changelist): field_choices = dict(self.field.flatchoices) for lookup, title in ( - (None, _("All")), - ("1", field_choices.get(True, _("Yes"))), - ("0", field_choices.get(False, _("No"))), + (None, _('All')), + ('1', field_choices.get(True, _('Yes'))), + ('0', field_choices.get(False, _('No'))), ): yield { - "selected": self.lookup_val == lookup and not self.lookup_val2, - "query_string": changelist.get_query_string( - {self.lookup_kwarg: lookup}, [self.lookup_kwarg2] - ), - "display": title, + 'selected': self.lookup_val == lookup and not self.lookup_val2, + 'query_string': changelist.get_query_string({self.lookup_kwarg: lookup}, [self.lookup_kwarg2]), + 'display': title, } if self.field.null: yield { - "selected": self.lookup_val2 == "True", - "query_string": changelist.get_query_string( - {self.lookup_kwarg2: "True"}, [self.lookup_kwarg] - ), - "display": field_choices.get(None, _("Unknown")), + 'selected': self.lookup_val2 == 'True', + 'query_string': changelist.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), + 'display': field_choices.get(None, _('Unknown')), } -FieldListFilter.register( - lambda f: isinstance(f, models.BooleanField), BooleanFieldListFilter -) +FieldListFilter.register(lambda f: isinstance(f, models.BooleanField), BooleanFieldListFilter) class ChoicesFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): - self.lookup_kwarg = "%s__exact" % field_path - self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_kwarg = '%s__exact' % field_path + self.lookup_kwarg_isnull = '%s__isnull' % field_path self.lookup_val = params.get(self.lookup_kwarg) self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) super().__init__(field, request, params, model, model_admin, field_path) @@ -311,31 +279,25 @@ class ChoicesFieldListFilter(FieldListFilter): def choices(self, changelist): yield { - "selected": self.lookup_val is None, - "query_string": changelist.get_query_string( - remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] - ), - "display": _("All"), + 'selected': self.lookup_val is None, + 'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]), + 'display': _('All') } - none_title = "" + none_title = '' for lookup, title in self.field.flatchoices: if lookup is None: none_title = title continue yield { - "selected": str(lookup) == self.lookup_val, - "query_string": changelist.get_query_string( - {self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull] - ), - "display": title, + 'selected': str(lookup) == self.lookup_val, + 'query_string': changelist.get_query_string({self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull]), + 'display': title, } if none_title: yield { - "selected": bool(self.lookup_val_isnull), - "query_string": changelist.get_query_string( - {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] - ), - "display": none_title, + 'selected': bool(self.lookup_val_isnull), + 'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]), + 'display': none_title, } @@ -344,10 +306,8 @@ FieldListFilter.register(lambda f: bool(f.choices), ChoicesFieldListFilter) class DateFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): - self.field_generic = "%s__" % field_path - self.date_params = { - k: v for k, v in params.items() if k.startswith(self.field_generic) - } + self.field_generic = '%s__' % field_path + self.date_params = {k: v for k, v in params.items() if k.startswith(self.field_generic)} now = timezone.now() # When time zone support is enabled, convert "now" to the user's time @@ -357,7 +317,7 @@ class DateFieldListFilter(FieldListFilter): if isinstance(field, models.DateTimeField): today = now.replace(hour=0, minute=0, second=0, microsecond=0) - else: # field is a models.DateField + else: # field is a models.DateField today = now.date() tomorrow = today + datetime.timedelta(days=1) if today.month == 12: @@ -366,44 +326,32 @@ class DateFieldListFilter(FieldListFilter): next_month = today.replace(month=today.month + 1, day=1) next_year = today.replace(year=today.year + 1, month=1, day=1) - self.lookup_kwarg_since = "%s__gte" % field_path - self.lookup_kwarg_until = "%s__lt" % field_path + self.lookup_kwarg_since = '%s__gte' % field_path + self.lookup_kwarg_until = '%s__lt' % field_path self.links = ( - (_("Any date"), {}), - ( - _("Today"), - { - self.lookup_kwarg_since: str(today), - self.lookup_kwarg_until: str(tomorrow), - }, - ), - ( - _("Past 7 days"), - { - self.lookup_kwarg_since: str(today - datetime.timedelta(days=7)), - self.lookup_kwarg_until: str(tomorrow), - }, - ), - ( - _("This month"), - { - self.lookup_kwarg_since: str(today.replace(day=1)), - self.lookup_kwarg_until: str(next_month), - }, - ), - ( - _("This year"), - { - self.lookup_kwarg_since: str(today.replace(month=1, day=1)), - self.lookup_kwarg_until: str(next_year), - }, - ), + (_('Any date'), {}), + (_('Today'), { + self.lookup_kwarg_since: str(today), + self.lookup_kwarg_until: str(tomorrow), + }), + (_('Past 7 days'), { + self.lookup_kwarg_since: str(today - datetime.timedelta(days=7)), + self.lookup_kwarg_until: str(tomorrow), + }), + (_('This month'), { + self.lookup_kwarg_since: str(today.replace(day=1)), + self.lookup_kwarg_until: str(next_month), + }), + (_('This year'), { + self.lookup_kwarg_since: str(today.replace(month=1, day=1)), + self.lookup_kwarg_until: str(next_year), + }), ) if field.null: - self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_kwarg_isnull = '%s__isnull' % field_path self.links += ( - (_("No date"), {self.field_generic + "isnull": "True"}), - (_("Has date"), {self.field_generic + "isnull": "False"}), + (_('No date'), {self.field_generic + 'isnull': 'True'}), + (_('Has date'), {self.field_generic + 'isnull': 'False'}), ) super().__init__(field, request, params, model, model_admin, field_path) @@ -416,15 +364,14 @@ class DateFieldListFilter(FieldListFilter): def choices(self, changelist): for title, param_dict in self.links: yield { - "selected": self.date_params == param_dict, - "query_string": changelist.get_query_string( - param_dict, [self.field_generic] - ), - "display": title, + 'selected': self.date_params == param_dict, + 'query_string': changelist.get_query_string(param_dict, [self.field_generic]), + 'display': title, } -FieldListFilter.register(lambda f: isinstance(f, models.DateField), DateFieldListFilter) +FieldListFilter.register( + lambda f: isinstance(f, models.DateField), DateFieldListFilter) # This should be registered last, because it's a last resort. For example, @@ -433,7 +380,7 @@ FieldListFilter.register(lambda f: isinstance(f, models.DateField), DateFieldLis class AllValuesFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): self.lookup_kwarg = field_path - self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_kwarg_isnull = '%s__isnull' % field_path self.lookup_val = params.get(self.lookup_kwarg) self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) self.empty_value_display = model_admin.get_empty_value_display() @@ -443,9 +390,7 @@ class AllValuesFieldListFilter(FieldListFilter): queryset = model_admin.get_queryset(request) else: queryset = parent_model._default_manager.all() - self.lookup_choices = ( - queryset.distinct().order_by(field.name).values_list(field.name, flat=True) - ) + self.lookup_choices = queryset.distinct().order_by(field.name).values_list(field.name, flat=True) super().__init__(field, request, params, model, model_admin, field_path) def expected_parameters(self): @@ -453,11 +398,9 @@ class AllValuesFieldListFilter(FieldListFilter): def choices(self, changelist): yield { - "selected": self.lookup_val is None and self.lookup_val_isnull is None, - "query_string": changelist.get_query_string( - remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] - ), - "display": _("All"), + 'selected': self.lookup_val is None and self.lookup_val_isnull is None, + 'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]), + 'display': _('All'), } include_none = False for val in self.lookup_choices: @@ -466,19 +409,15 @@ class AllValuesFieldListFilter(FieldListFilter): continue val = str(val) yield { - "selected": self.lookup_val == val, - "query_string": changelist.get_query_string( - {self.lookup_kwarg: val}, [self.lookup_kwarg_isnull] - ), - "display": val, + 'selected': self.lookup_val == val, + 'query_string': changelist.get_query_string({self.lookup_kwarg: val}, [self.lookup_kwarg_isnull]), + 'display': val, } if include_none: yield { - "selected": bool(self.lookup_val_isnull), - "query_string": changelist.get_query_string( - {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] - ), - "display": self.empty_value_display, + 'selected': bool(self.lookup_val_isnull), + 'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]), + 'display': self.empty_value_display, } @@ -487,15 +426,9 @@ FieldListFilter.register(lambda f: True, AllValuesFieldListFilter) class RelatedOnlyFieldListFilter(RelatedFieldListFilter): def field_choices(self, field, request, model_admin): - pk_qs = ( - model_admin.get_queryset(request) - .distinct() - .values_list("%s__pk" % self.field_path, flat=True) - ) + pk_qs = model_admin.get_queryset(request).distinct().values_list('%s__pk' % self.field_path, flat=True) ordering = self.field_admin_ordering(field, request, model_admin) - return field.get_choices( - include_blank=False, limit_choices_to={"pk__in": pk_qs}, ordering=ordering - ) + return field.get_choices(include_blank=False, limit_choices_to={'pk__in': pk_qs}, ordering=ordering) class EmptyFieldListFilter(FieldListFilter): @@ -503,29 +436,27 @@ class EmptyFieldListFilter(FieldListFilter): if not field.empty_strings_allowed and not field.null: raise ImproperlyConfigured( "The list filter '%s' cannot be used with field '%s' which " - "doesn't allow empty strings and nulls." - % ( + "doesn't allow empty strings and nulls." % ( self.__class__.__name__, field.name, ) ) - self.lookup_kwarg = "%s__isempty" % field_path + self.lookup_kwarg = '%s__isempty' % field_path self.lookup_val = params.get(self.lookup_kwarg) super().__init__(field, request, params, model, model_admin, field_path) def queryset(self, request, queryset): if self.lookup_kwarg not in self.used_parameters: return queryset - if self.lookup_val not in ("0", "1"): + if self.lookup_val not in ('0', '1'): raise IncorrectLookupParameters - lookup_conditions = [] + lookup_condition = models.Q() if self.field.empty_strings_allowed: - lookup_conditions.append((self.field_path, "")) + lookup_condition |= models.Q(**{self.field_path: ''}) if self.field.null: - lookup_conditions.append((f"{self.field_path}__isnull", True)) - lookup_condition = models.Q(*lookup_conditions, _connector=models.Q.OR) - if self.lookup_val == "1": + lookup_condition |= models.Q(**{'%s__isnull' % self.field_path: True}) + if self.lookup_val == '1': return queryset.filter(lookup_condition) return queryset.exclude(lookup_condition) @@ -534,14 +465,12 @@ class EmptyFieldListFilter(FieldListFilter): def choices(self, changelist): for lookup, title in ( - (None, _("All")), - ("1", _("Empty")), - ("0", _("Not empty")), + (None, _('All')), + ('1', _('Empty')), + ('0', _('Not empty')), ): yield { - "selected": self.lookup_val == lookup, - "query_string": changelist.get_query_string( - {self.lookup_kwarg: lookup} - ), - "display": title, + 'selected': self.lookup_val == lookup, + 'query_string': changelist.get_query_string({self.lookup_kwarg: lookup}), + 'display': title, } diff --git a/venv/Lib/site-packages/django/contrib/admin/forms.py b/venv/Lib/site-packages/django/contrib/admin/forms.py index bbb072b..ee27509 100644 --- a/venv/Lib/site-packages/django/contrib/admin/forms.py +++ b/venv/Lib/site-packages/django/contrib/admin/forms.py @@ -7,25 +7,24 @@ class AdminAuthenticationForm(AuthenticationForm): """ A custom authentication form used in the admin app. """ - error_messages = { **AuthenticationForm.error_messages, - "invalid_login": _( + 'invalid_login': _( "Please enter the correct %(username)s and password for a staff " "account. Note that both fields may be case-sensitive." ), } - required_css_class = "required" + required_css_class = 'required' def confirm_login_allowed(self, user): super().confirm_login_allowed(user) if not user.is_staff: raise ValidationError( - self.error_messages["invalid_login"], - code="invalid_login", - params={"username": self.username_field.verbose_name}, + self.error_messages['invalid_login'], + code='invalid_login', + params={'username': self.username_field.verbose_name} ) class AdminPasswordChangeForm(PasswordChangeForm): - required_css_class = "required" + required_css_class = 'required' diff --git a/venv/Lib/site-packages/django/contrib/admin/helpers.py b/venv/Lib/site-packages/django/contrib/admin/helpers.py index 2e7a20a..6f0be6b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/helpers.py +++ b/venv/Lib/site-packages/django/contrib/admin/helpers.py @@ -2,77 +2,55 @@ import json from django import forms from django.contrib.admin.utils import ( - display_for_field, - flatten_fieldsets, - help_text_for_field, - label_for_field, - lookup_field, - quote, + display_for_field, flatten_fieldsets, help_text_for_field, label_for_field, + lookup_field, quote, ) from django.core.exceptions import ObjectDoesNotExist from django.db.models.fields.related import ( - ForeignObjectRel, - ManyToManyRel, - OneToOneField, + ForeignObjectRel, ManyToManyRel, OneToOneField, ) from django.forms.utils import flatatt from django.template.defaultfilters import capfirst, linebreaksbr from django.urls import NoReverseMatch, reverse from django.utils.html import conditional_escape, format_html from django.utils.safestring import mark_safe -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ -ACTION_CHECKBOX_NAME = "_selected_action" +ACTION_CHECKBOX_NAME = '_selected_action' class ActionForm(forms.Form): - action = forms.ChoiceField(label=_("Action:")) + action = forms.ChoiceField(label=_('Action:')) select_across = forms.BooleanField( - label="", + label='', required=False, initial=0, - widget=forms.HiddenInput({"class": "select-across"}), + widget=forms.HiddenInput({'class': 'select-across'}), ) -checkbox = forms.CheckboxInput({"class": "action-select"}, lambda value: False) +checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False) class AdminForm: - def __init__( - self, - form, - fieldsets, - prepopulated_fields, - readonly_fields=None, - model_admin=None, - ): + def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, model_admin=None): self.form, self.fieldsets = form, fieldsets - self.prepopulated_fields = [ - {"field": form[field_name], "dependencies": [form[f] for f in dependencies]} - for field_name, dependencies in prepopulated_fields.items() - ] + self.prepopulated_fields = [{ + 'field': form[field_name], + 'dependencies': [form[f] for f in dependencies] + } for field_name, dependencies in prepopulated_fields.items()] self.model_admin = model_admin if readonly_fields is None: readonly_fields = () self.readonly_fields = readonly_fields - def __repr__(self): - return ( - f"<{self.__class__.__qualname__}: " - f"form={self.form.__class__.__qualname__} " - f"fieldsets={self.fieldsets!r}>" - ) - def __iter__(self): for name, options in self.fieldsets: yield Fieldset( - self.form, - name, + self.form, name, readonly_fields=self.readonly_fields, model_admin=self.model_admin, - **options, + **options ) @property @@ -92,34 +70,24 @@ class AdminForm: class Fieldset: - def __init__( - self, - form, - name=None, - readonly_fields=(), - fields=(), - classes=(), - description=None, - model_admin=None, - ): + def __init__(self, form, name=None, readonly_fields=(), fields=(), classes=(), + description=None, model_admin=None): self.form = form self.name, self.fields = name, fields - self.classes = " ".join(classes) + self.classes = ' '.join(classes) self.description = description self.model_admin = model_admin self.readonly_fields = readonly_fields @property def media(self): - if "collapse" in self.classes: - return forms.Media(js=["admin/js/collapse.js"]) + if 'collapse' in self.classes: + return forms.Media(js=['admin/js/collapse.js']) return forms.Media() def __iter__(self): for field in self.fields: - yield Fieldline( - self.form, field, self.readonly_fields, model_admin=self.model_admin - ) + yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin) class Fieldline: @@ -141,19 +109,15 @@ class Fieldline: def __iter__(self): for i, field in enumerate(self.fields): if field in self.readonly_fields: - yield AdminReadonlyField( - self.form, field, is_first=(i == 0), model_admin=self.model_admin - ) + yield AdminReadonlyField(self.form, field, is_first=(i == 0), model_admin=self.model_admin) else: yield AdminField(self.form, field, is_first=(i == 0)) def errors(self): return mark_safe( - "\n".join( - self.form[f].errors.as_ul() - for f in self.fields - if f not in self.readonly_fields - ).strip("\n") + '\n'.join( + self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields + ).strip('\n') ) @@ -168,19 +132,18 @@ class AdminField: classes = [] contents = conditional_escape(self.field.label) if self.is_checkbox: - classes.append("vCheckboxLabel") + classes.append('vCheckboxLabel') if self.field.field.required: - classes.append("required") + classes.append('required') if not self.is_first: - classes.append("inline") - attrs = {"class": " ".join(classes)} if classes else {} + classes.append('inline') + attrs = {'class': ' '.join(classes)} if classes else {} # checkboxes should not have a label suffix as the checkbox appears # to the left of the label. return self.field.label_tag( - contents=mark_safe(contents), - attrs=attrs, - label_suffix="" if self.is_checkbox else None, + contents=mark_safe(contents), attrs=attrs, + label_suffix='' if self.is_checkbox else None, ) def errors(self): @@ -193,7 +156,7 @@ class AdminReadonlyField: # {{ field.name }} must be a useful class name to identify the field. # For convenience, store other field-related data here too. if callable(field): - class_name = field.__name__ if field.__name__ != "" else "" + class_name = field.__name__ if field.__name__ != '' else '' else: class_name = field @@ -207,17 +170,11 @@ class AdminReadonlyField: else: help_text = help_text_for_field(class_name, form._meta.model) - if field in form.fields: - is_hidden = form.fields[field].widget.is_hidden - else: - is_hidden = False - self.field = { - "name": class_name, - "label": label, - "help_text": help_text, - "field": field, - "is_hidden": is_hidden, + 'name': class_name, + 'label': label, + 'help_text': help_text, + 'field': field, } self.form = form self.model_admin = model_admin @@ -230,37 +187,23 @@ class AdminReadonlyField: attrs = {} if not self.is_first: attrs["class"] = "inline" - label = self.field["label"] - return format_html( - "{}{}", - flatatt(attrs), - capfirst(label), - self.form.label_suffix, - ) + label = self.field['label'] + return format_html('{}{}', flatatt(attrs), capfirst(label), self.form.label_suffix) def get_admin_url(self, remote_field, remote_obj): - url_name = "admin:%s_%s_change" % ( + url_name = 'admin:%s_%s_change' % ( remote_field.model._meta.app_label, remote_field.model._meta.model_name, ) try: - url = reverse( - url_name, - args=[quote(remote_obj.pk)], - current_app=self.model_admin.admin_site.name, - ) + url = reverse(url_name, args=[quote(remote_obj.pk)]) return format_html('{}', url, remote_obj) except NoReverseMatch: return str(remote_obj) def contents(self): from django.contrib.admin.templatetags.admin_list import _boolean_icon - - field, obj, model_admin = ( - self.field["field"], - self.form.instance, - self.model_admin, - ) + field, obj, model_admin = self.field['field'], self.form.instance, self.model_admin try: f, attr, value = lookup_field(field, obj, model_admin) except (AttributeError, ValueError, ObjectDoesNotExist): @@ -270,10 +213,10 @@ class AdminReadonlyField: widget = self.form[field].field.widget # This isn't elegant but suffices for contrib.auth's # ReadOnlyPasswordHashWidget. - if getattr(widget, "read_only", False): + if getattr(widget, 'read_only', False): return widget.render(field, value) if f is None: - if getattr(attr, "boolean", False): + if getattr(attr, 'boolean', False): result_repr = _boolean_icon(value) else: if hasattr(value, "__html__"): @@ -284,8 +227,8 @@ class AdminReadonlyField: if isinstance(f.remote_field, ManyToManyRel) and value is not None: result_repr = ", ".join(map(str, value.all())) elif ( - isinstance(f.remote_field, (ForeignObjectRel, OneToOneField)) - and value is not None + isinstance(f.remote_field, (ForeignObjectRel, OneToOneField)) and + value is not None ): result_repr = self.get_admin_url(f.remote_field, value) else: @@ -298,20 +241,10 @@ class InlineAdminFormSet: """ A wrapper around an inline formset for use in the admin system. """ - - def __init__( - self, - inline, - formset, - fieldsets, - prepopulated_fields=None, - readonly_fields=None, - model_admin=None, - has_add_permission=True, - has_change_permission=True, - has_delete_permission=True, - has_view_permission=True, - ): + def __init__(self, inline, formset, fieldsets, prepopulated_fields=None, + readonly_fields=None, model_admin=None, has_add_permission=True, + has_change_permission=True, has_delete_permission=True, + has_view_permission=True): self.opts = inline self.formset = formset self.fieldsets = fieldsets @@ -322,7 +255,7 @@ class InlineAdminFormSet: if prepopulated_fields is None: prepopulated_fields = {} self.prepopulated_fields = prepopulated_fields - self.classes = " ".join(inline.classes) if inline.classes else "" + self.classes = ' '.join(inline.classes) if inline.classes else '' self.has_add_permission = has_add_permission self.has_change_permission = has_change_permission self.has_delete_permission = has_delete_permission @@ -332,43 +265,25 @@ class InlineAdminFormSet: if self.has_change_permission: readonly_fields_for_editing = self.readonly_fields else: - readonly_fields_for_editing = self.readonly_fields + flatten_fieldsets( - self.fieldsets - ) + readonly_fields_for_editing = self.readonly_fields + flatten_fieldsets(self.fieldsets) - for form, original in zip( - self.formset.initial_forms, self.formset.get_queryset() - ): + for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): view_on_site_url = self.opts.get_view_on_site_url(original) yield InlineAdminForm( - self.formset, - form, - self.fieldsets, - self.prepopulated_fields, - original, - readonly_fields_for_editing, - model_admin=self.opts, + self.formset, form, self.fieldsets, self.prepopulated_fields, + original, readonly_fields_for_editing, model_admin=self.opts, view_on_site_url=view_on_site_url, ) for form in self.formset.extra_forms: yield InlineAdminForm( - self.formset, - form, - self.fieldsets, - self.prepopulated_fields, - None, - self.readonly_fields, - model_admin=self.opts, + self.formset, form, self.fieldsets, self.prepopulated_fields, + None, self.readonly_fields, model_admin=self.opts, ) if self.has_add_permission: yield InlineAdminForm( - self.formset, - self.formset.empty_form, - self.fieldsets, - self.prepopulated_fields, - None, - self.readonly_fields, - model_admin=self.opts, + self.formset, self.formset.empty_form, + self.fieldsets, self.prepopulated_fields, None, + self.readonly_fields, model_admin=self.opts, ) def fields(self): @@ -380,54 +295,43 @@ class InlineAdminFormSet: if fk and fk.name == field_name: continue if not self.has_change_permission or field_name in self.readonly_fields: - form_field = empty_form.fields.get(field_name) - widget_is_hidden = False - if form_field is not None: - widget_is_hidden = form_field.widget.is_hidden yield { - "name": field_name, - "label": meta_labels.get(field_name) - or label_for_field( + 'name': field_name, + 'label': meta_labels.get(field_name) or label_for_field( field_name, self.opts.model, self.opts, form=empty_form, ), - "widget": {"is_hidden": widget_is_hidden}, - "required": False, - "help_text": meta_help_texts.get(field_name) - or help_text_for_field(field_name, self.opts.model), + 'widget': {'is_hidden': False}, + 'required': False, + 'help_text': meta_help_texts.get(field_name) or help_text_for_field(field_name, self.opts.model), } else: form_field = empty_form.fields[field_name] label = form_field.label if label is None: - label = label_for_field( - field_name, self.opts.model, self.opts, form=empty_form - ) + label = label_for_field(field_name, self.opts.model, self.opts, form=empty_form) yield { - "name": field_name, - "label": label, - "widget": form_field.widget, - "required": form_field.required, - "help_text": form_field.help_text, + 'name': field_name, + 'label': label, + 'widget': form_field.widget, + 'required': form_field.required, + 'help_text': form_field.help_text, } def inline_formset_data(self): verbose_name = self.opts.verbose_name - return json.dumps( - { - "name": "#%s" % self.formset.prefix, - "options": { - "prefix": self.formset.prefix, - "addText": gettext("Add another %(verbose_name)s") - % { - "verbose_name": capfirst(verbose_name), - }, - "deleteText": gettext("Remove"), + return json.dumps({ + 'name': '#%s' % self.formset.prefix, + 'options': { + 'prefix': self.formset.prefix, + 'addText': gettext('Add another %(verbose_name)s') % { + 'verbose_name': capfirst(verbose_name), }, + 'deleteText': gettext('Remove'), } - ) + }) @property def forms(self): @@ -449,51 +353,31 @@ class InlineAdminForm(AdminForm): """ A wrapper around an inline form for use in the admin system. """ - - def __init__( - self, - formset, - form, - fieldsets, - prepopulated_fields, - original, - readonly_fields=None, - model_admin=None, - view_on_site_url=None, - ): + def __init__(self, formset, form, fieldsets, prepopulated_fields, original, + readonly_fields=None, model_admin=None, view_on_site_url=None): self.formset = formset self.model_admin = model_admin self.original = original self.show_url = original and view_on_site_url is not None self.absolute_url = view_on_site_url - super().__init__( - form, fieldsets, prepopulated_fields, readonly_fields, model_admin - ) + super().__init__(form, fieldsets, prepopulated_fields, readonly_fields, model_admin) def __iter__(self): for name, options in self.fieldsets: yield InlineFieldset( - self.formset, - self.form, - name, - self.readonly_fields, - model_admin=self.model_admin, - **options, + self.formset, self.form, name, self.readonly_fields, + model_admin=self.model_admin, **options ) def needs_explicit_pk_field(self): return ( # Auto fields are editable, so check for auto or non-editable pk. - self.form._meta.model._meta.auto_field - or not self.form._meta.model._meta.pk.editable - or + self.form._meta.model._meta.auto_field or not self.form._meta.model._meta.pk.editable or # Also search any parents for an auto field. (The pk info is # propagated to child models so that does not need to be checked # in parents.) - any( - parent._meta.auto_field or not parent._meta.model._meta.pk.editable - for parent in self.form._meta.model._meta.get_parent_list() - ) + any(parent._meta.auto_field or not parent._meta.model._meta.pk.editable + for parent in self.form._meta.model._meta.get_parent_list()) ) def pk_field(self): @@ -508,12 +392,10 @@ class InlineAdminForm(AdminForm): def deletion_field(self): from django.forms.formsets import DELETION_FIELD_NAME - return AdminField(self.form, DELETION_FIELD_NAME, False) def ordering_field(self): from django.forms.formsets import ORDERING_FIELD_NAME - return AdminField(self.form, ORDERING_FIELD_NAME, False) @@ -526,14 +408,11 @@ class InlineFieldset(Fieldset): fk = getattr(self.formset, "fk", None) for field in self.fields: if not fk or fk.name != field: - yield Fieldline( - self.form, field, self.readonly_fields, model_admin=self.model_admin - ) + yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin) class AdminErrorList(forms.utils.ErrorList): """Store errors for the form/formsets in an add/change view.""" - def __init__(self, form, inline_formsets): super().__init__() diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.mo index 1e0d680..064ada0 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.po index f1b1725..eaa04f2 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2015-2016,2018,2020-2021 +# Bashar Al-Abdulhadi, 2015-2016,2018,2020 # Bashar Al-Abdulhadi, 2014 # Eyad Toma , 2013 # Jannis Leidel , 2011 @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-15 21:11+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-15 00:40+0000\n" "Last-Translator: Bashar Al-Abdulhadi\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" @@ -23,10 +23,6 @@ msgstr "" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "احذف %(verbose_name_plural)s المحدّدة" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "نجح حذف %(count)d من %(items)s." @@ -38,6 +34,10 @@ msgstr "تعذّر حذف %(name)s" msgid "Are you sure?" msgstr "هل أنت متأكد؟" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "احذف %(verbose_name_plural)s المحدّدة" + msgid "Administration" msgstr "الإدارة" @@ -527,12 +527,6 @@ msgstr "نسيت كلمة المرور أو اسم المستخدم الخاص msgid "Toggle navigation" msgstr "تغيير التصفّح" -msgid "Start typing to filter…" -msgstr "ابدأ الكتابة للتصفية ..." - -msgid "Filter navigation items" -msgstr "تصفية عناصر التصفح" - msgid "Date/time" msgstr "التاريخ/الوقت" @@ -602,8 +596,8 @@ msgstr "أضف %(model)s آخر" msgid "Delete selected %(model)s" msgstr "حذف %(model)s المختارة" -msgid "Thanks for spending some quality time with the web site today." -msgstr "شكرا لقضاء بعض الوقت الجيد في الموقع اليوم." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "شكراً لك على قضائك بعض الوقت مع الموقع اليوم." msgid "Log in again" msgstr "ادخل مجدداً" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo index 0060573..f25264b 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po index f7570b5..f22d6e6 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ar/LC_MESSAGES/djangojs.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2015,2020-2021 +# Bashar Al-Abdulhadi, 2015,2020 # Bashar Al-Abdulhadi, 2014 # Jannis Leidel , 2011 # Omar Lajam, 2020 @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-15 21:27+0000\n" +"POT-Creation-Date: 2020-05-11 20:56+0200\n" +"PO-Revision-Date: 2020-07-06 09:56+0000\n" "Last-Translator: Bashar Al-Abdulhadi\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" @@ -37,7 +37,7 @@ msgid "Type into this box to filter down the list of available %s." msgstr "اكتب في هذا الصندوق لتصفية قائمة %s المتوفرة." msgid "Filter" -msgstr "تصفية" +msgstr "انتقاء" msgid "Choose all" msgstr "اختر الكل" @@ -195,54 +195,6 @@ msgstr "نوفمبر" msgid "December" msgstr "ديسمبر" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "يناير" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "فبراير" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "مارس" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "إبريل" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "مايو" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "يونيو" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "يوليو" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "أغسطس" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "سبتمبر" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "أكتوبر" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "نوفمبر" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "ديسمبر" - msgctxt "one letter Sunday" msgid "S" msgstr "أحد" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.mo index 16720dc..18713a0 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.po index 472f7e9..5baf1e0 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/be/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # # Translators: # Viktar Palstsiuk , 2015 -# znotdead , 2016-2017,2019-2021 +# znotdead , 2016-2017,2019-2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 16:42+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-15 01:22+0000\n" "Last-Translator: znotdead \n" "Language-Team: Belarusian (http://www.transifex.com/django/django/language/" "be/)\n" @@ -20,10 +20,6 @@ msgstr "" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%100>=11 && n%100<=14)? 2 : 3);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Выдаліць абраныя %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Выдалілі %(count)d %(items)s." @@ -35,6 +31,10 @@ msgstr "Не ўдаецца выдаліць %(name)s" msgid "Are you sure?" msgstr "Ці ўпэўненыя вы?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Выдаліць абраныя %(verbose_name_plural)s" + msgid "Administration" msgstr "Адміністрацыя" @@ -524,12 +524,6 @@ msgstr "Забыліся на імя ці пароль?" msgid "Toggle navigation" msgstr "Пераключыць навігацыю" -msgid "Start typing to filter…" -msgstr "Пачніце ўводзіць, каб адфільтраваць..." - -msgid "Filter navigation items" -msgstr "Фільтраваць элементы навігацыі" - msgid "Date/time" msgstr "Час, дата" @@ -597,7 +591,7 @@ msgstr "Дадаць яшчэ %(model)s" msgid "Delete selected %(model)s" msgstr "Выдаліць абраныя %(model)s" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "Дзякуем за час, які вы сёньня правялі на гэтай пляцоўцы." msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.mo index 340a880..4a04342 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.po index 111d709..767c727 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec , 2022 # Boris Chervenkov , 2012 # Claude Paroz , 2014 # Jannis Leidel , 2011 @@ -13,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-14 09:41+0000\n" -"Last-Translator: arneatec \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-06 05:54+0000\n" +"Last-Translator: Todor Lubenov \n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -79,7 +78,7 @@ msgid "Empty" msgstr "Празно" msgid "Not empty" -msgstr "Не е празно" +msgstr "" #, python-format msgid "" @@ -87,7 +86,7 @@ msgid "" "that both fields may be case-sensitive." msgstr "" "Моля въведете правилния %(username)s и парола за администраторски акаунт. " -"Моля забележете, че и двете полета могат да са с главни и малки букви." +"Моля забележете, че и двете полета са с главни и малки букви." msgid "Action:" msgstr "Действие:" @@ -100,7 +99,7 @@ msgid "Remove" msgstr "Премахване" msgid "Addition" -msgstr "Добавка" +msgstr "" msgid "Change" msgstr "Промени" @@ -132,29 +131,29 @@ msgid "change message" msgstr "промени съобщение" msgid "log entry" -msgstr "записка в журнала" +msgstr "записка" msgid "log entries" -msgstr "записки в журнала" +msgstr "записки" #, python-format msgid "Added “%(object)s”." -msgstr "Добавен “%(object)s”." +msgstr "" #, python-format msgid "Changed “%(object)s” — %(changes)s" -msgstr "Променени “%(object)s” — %(changes)s" +msgstr "" #, python-format msgid "Deleted “%(object)s.”" -msgstr "Изтрити “%(object)s.”" +msgstr "" msgid "LogEntry Object" msgstr "LogEntry обект" #, python-brace-format msgid "Added {name} “{object}”." -msgstr "Добавен {name} “{object}”." +msgstr "" msgid "Added." msgstr "Добавено." @@ -164,7 +163,7 @@ msgstr "и" #, python-brace-format msgid "Changed {fields} for {name} “{object}”." -msgstr "Променени {fields} за {name} “{object}”." +msgstr "" #, python-brace-format msgid "Changed {fields}." @@ -172,7 +171,7 @@ msgstr "Променени {fields}." #, python-brace-format msgid "Deleted {name} “{object}”." -msgstr "Изтрит {name} “{object}”." +msgstr "" msgid "No fields changed." msgstr "Няма променени полета." @@ -182,46 +181,37 @@ msgstr "Празно" msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." msgstr "" -"Задръжте “Control”, или “Command” на Mac, за да изберете повече от едно." #, python-brace-format msgid "The {name} “{obj}” was added successfully." -msgstr "Обектът {name} “{obj}” бе успешно добавен." +msgstr "" msgid "You may edit it again below." -msgstr "Можете отново да го промените по-долу." +msgstr "" #, python-brace-format msgid "" "The {name} “{obj}” was added successfully. You may add another {name} below." msgstr "" -"Обектът {name} “{obj}” бе успешно добавен. Можете да добавите друг {name} по-" -"долу." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may edit it again below." msgstr "" -"Обектът {name} “{obj}” бе успешно променен. Можете да го промените отново по-" -"долу." #, python-brace-format msgid "The {name} “{obj}” was added successfully. You may edit it again below." msgstr "" -"Обектът {name} “{obj}” бе успешно добавен. Можете да го промените отново по-" -"долу." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may add another {name} " "below." msgstr "" -"Обектът {name} “{obj}” бе успешно променен. Можете да добавите друг {name} " -"по-долу." #, python-brace-format msgid "The {name} “{obj}” was changed successfully." -msgstr "Обектът {name} “{obj}” бе успешно променен." +msgstr "" msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -231,15 +221,15 @@ msgstr "" "променени елементи." msgid "No action selected." -msgstr "Няма избрано действие." +msgstr "Няма избрани действия." #, python-format msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "%(name)s “%(obj)s” беше успешно изтрит." +msgstr "" #, python-format msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "%(name)s с ID “%(key)s” не съществува. Може би е изтрит?" +msgstr "" #, python-format msgid "Add %s" @@ -260,17 +250,17 @@ msgstr "Грешка в базата данни" msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s беше променено успешно." -msgstr[1] "%(count)s %(name)s бяха успешно променени." +msgstr[1] "%(count)s %(name)s бяха променени успешно." #, python-format msgid "%(total_count)s selected" msgid_plural "All %(total_count)s selected" msgstr[0] "%(total_count)s е избран" -msgstr[1] "Избрани са всички %(total_count)s" +msgstr[1] "Всички %(total_count)s са избрани" #, python-format msgid "0 of %(cnt)s selected" -msgstr "Избрани са 0 от %(cnt)s" +msgstr "0 от %(cnt)s са избрани" #, python-format msgid "Change history: %s" @@ -291,10 +281,10 @@ msgstr "" "на следните защитени и свързани обекти: %(related_objects)s" msgid "Django site admin" -msgstr "Django административен сайт" +msgstr "Административен панел" msgid "Django administration" -msgstr "Администрация на Django" +msgstr "Административен панел" msgid "Site administration" msgstr "Администрация на сайта" @@ -328,12 +318,9 @@ msgid "" "There’s been an error. It’s been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" -"Получи се грешка. Администраторите на сайта са уведомени за това чрез " -"електронна поща и грешката трябва да бъде поправена скоро. Благодарим ви за " -"търпението." msgid "Run the selected action" -msgstr "Изпълни избраното действие" +msgstr "Стартирай избраните действия" msgid "Go" msgstr "Напред" @@ -346,11 +333,11 @@ msgid "Select all %(total_count)s %(module_name)s" msgstr "Избери всички %(total_count)s %(module_name)s" msgid "Clear selection" -msgstr "Изчисти избраното" +msgstr "Изтрий избраното" #, python-format msgid "Models in the %(name)s application" -msgstr "Модели в приложението %(name)s " +msgstr "Моделите в %(name)s приложение" msgid "Add" msgstr "Добави" @@ -359,14 +346,12 @@ msgid "View" msgstr "Изглед" msgid "You don’t have permission to view or edit anything." -msgstr "Нямате права да разглеждате или редактирате каквото и да е." +msgstr "Вие нямате права да разглеждате или редактирате нищо." msgid "" "First, enter a username and password. Then, you’ll be able to edit more user " "options." msgstr "" -"Първо въведете потребител и парола. След това ще можете да редактирате " -"повече детайли. " msgid "Enter a username and password." msgstr "Въведете потребителско име и парола." @@ -375,7 +360,7 @@ msgid "Change password" msgstr "Промени парола" msgid "Please correct the error below." -msgstr "Моля, поправете грешката по-долу" +msgstr "Моля коригирайте грешката долу" msgid "Please correct the errors below." msgstr "Моля поправете грешките по-долу." @@ -420,7 +405,7 @@ msgid "Sorting priority: %(priority_number)s" msgstr "Ред на подреждане: %(priority_number)s" msgid "Toggle sorting" -msgstr "Превключи подреждането" +msgstr "Обърни подреждането" msgid "Delete" msgstr "Изтрий" @@ -431,31 +416,31 @@ msgid "" "related objects, but your account doesn't have permission to delete the " "following types of objects:" msgstr "" -"Изтриването на %(object_name)s '%(escaped_object)s' би причинило изтриване " -"на свързани обекти, но вашият потребител няма право да изтрива следните " -"видове обекти:" +"Изтриването на обекта %(object_name)s '%(escaped_object)s' не може да бъде " +"извършено без да се изтрият и някои свързани обекти, върху които обаче " +"нямате права: " #, python-format msgid "" "Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " "following protected related objects:" msgstr "" -"Изтриването на %(object_name)s '%(escaped_object)s' изисква изтриването на " -"следните защитени свързани обекти:" +"Изтриването на %(object_name)s '%(escaped_object)s' ще доведе до " +"заличаването на следните защитени свързани обекти:" #, python-format msgid "" "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " "All of the following related items will be deleted:" msgstr "" -"Наистина ли искате да изтриете %(object_name)s \"%(escaped_object)s\"? " -"Следните свързани елементи също ще бъдат изтрити:" +"Наистина ли искате да изтриете обектите %(object_name)s \"%(escaped_object)s" +"\"? Следните свързани елементи също ще бъдат изтрити:" msgid "Objects" msgstr "Обекти" msgid "Yes, I’m sure" -msgstr "Да, сигурен съм" +msgstr "Да сигурен съм" msgid "No, take me back" msgstr "Не, върни ме обратно" @@ -470,15 +455,15 @@ msgid "" "types of objects:" msgstr "" "Изтриването на избраните %(objects_name)s ще доведе до изтриване на свързани " -"обекти, но вашият потребител няма право да изтрива следните типове обекти:" +"обекти. Вашият профил няма права за изтриване на следните типове обекти:" #, python-format msgid "" "Deleting the selected %(objects_name)s would require deleting the following " "protected related objects:" msgstr "" -"Изтриването на избраните %(objects_name)s изисква изтриването на следните " -"защитени свързани обекти:" +"Изтриването на избраните %(objects_name)s ще доведе до заличаването на " +"следните защитени свързани обекти:" #, python-format msgid "" @@ -515,16 +500,14 @@ msgid "" "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Проблем с вашата база данни. Убедете се, че необходимите таблици в базата са " -"създадени и че съответния потребител има необходимите права за достъп. " #, python-format msgid "" "You are authenticated as %(username)s, but are not authorized to access this " "page. Would you like to login to a different account?" msgstr "" -"Вие сте се удостоверен като %(username)s, но не сте оторизиран да достъпите " -"тази страница. Бихте ли желали да влезе с друг профил?" +"Вие сте се автентикиран като %(username)s, но не сте оторизиран да достъпите " +"тази страница. Бихте ли желали да влезе с друг профил." msgid "Forgotten your password or username?" msgstr "Забравена парола или потребителско име?" @@ -532,12 +515,6 @@ msgstr "Забравена парола или потребителско име msgid "Toggle navigation" msgstr "Превключи навигацията" -msgid "Start typing to filter…" -msgstr "Започнете да пишете за филтър..." - -msgid "Filter navigation items" -msgstr "Филтриране на навигационните елементи" - msgid "Date/time" msgstr "Дата/час" @@ -551,8 +528,6 @@ msgid "" "This object doesn’t have a change history. It probably wasn’t added via this " "admin site." msgstr "" -"Този обект няма история на промените. Вероятно не е бил добавен чрез този " -"административен сайт." msgid "Show all" msgstr "Покажи всички" @@ -561,7 +536,7 @@ msgid "Save" msgstr "Запис" msgid "Popup closing…" -msgstr "Изскачащият прозорец се затваря..." +msgstr "" msgid "Search" msgstr "Търсене" @@ -577,13 +552,13 @@ msgid "%(full_result_count)s total" msgstr "%(full_result_count)s общо" msgid "Save as new" -msgstr "Запиши като нов" +msgstr "Запис като нов" msgid "Save and add another" -msgstr "Запиши и добави нов" +msgstr "Запис и нов" msgid "Save and continue editing" -msgstr "Запиши и продължи" +msgstr "Запис и продължение" msgid "Save and view" msgstr "Запиши и прегледай" @@ -603,8 +578,8 @@ msgstr "Добавяне на друг %(model)s" msgid "Delete selected %(model)s" msgstr "Изтриване на избрания %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Благодарим ви за добре прекараното време с този сайт днес." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Благодарим Ви, че използвахте този сайт днес." msgid "Log in again" msgstr "Влез пак" @@ -619,27 +594,25 @@ msgid "" "Please enter your old password, for security’s sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" -"Въведете старата си парола /от съображения за сигурност/. След това въведете " -"желаната нова парола два пъти, за да сверим дали е написана правилно." msgid "Change my password" -msgstr "Промяна на паролата ми" +msgstr "Промяна на парола" msgid "Password reset" msgstr "Нова парола" msgid "Your password has been set. You may go ahead and log in now." -msgstr "Паролата е променена. Вече можете да се впишете." +msgstr "Паролата е променена. Вече можете да се впишете" msgid "Password reset confirmation" -msgstr "Потвърждение за смяна на паролата" +msgstr "Парола за потвърждение" msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" -"Моля, въведете новата парола два пъти, за да се уверим, че сте я написали " -"правилно." +"Моля, въведете новата парола два пъти, за да може да се потвърди, че сте я " +"написали правилно." msgid "New password:" msgstr "Нова парола:" @@ -658,29 +631,25 @@ msgid "" "We’ve emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" -"По имейл изпратихме инструкции за смяна на паролата, ако съществува профил с " -"въведения от вас адрес. Би трябвало скоро да ги получите. " msgid "" "If you don’t receive an email, please make sure you’ve entered the address " "you registered with, and check your spam folder." msgstr "" -"Ако не получите имейл, моля уверете се, че сте попълнили правилно адреса, с " -"който сте се регистрирали, също проверете спам папката във вашата поща." #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"Вие получавати този имейл, защото сте поискали да промените паролата за " +"Вие сте получили този имейл, защото сте поискали да промените паролата за " "вашия потребителски акаунт в %(site_name)s." msgid "Please go to the following page and choose a new password:" msgstr "Моля, отидете на следната страница и изберете нова парола:" msgid "Your username, in case you’ve forgotten:" -msgstr "Вашето потребителско име, в случай че сте го забравили:" +msgstr "Вашето потребителско име в случай че сте го забравили:" msgid "Thanks for using our site!" msgstr "Благодарим, че ползвате сайта ни!" @@ -693,14 +662,12 @@ msgid "" "Forgotten your password? Enter your email address below, and we’ll email " "instructions for setting a new one." msgstr "" -"Забравили сте си паролата? Въведете своя имейл адрес по-долу, и ние ще ви " -"изпратим инструкции как да я смените с нова." msgid "Email address:" -msgstr "Имейл адреси:" +msgstr "E-mail адреси:" msgid "Reset my password" -msgstr "Задай новата ми парола" +msgstr "Нова парола" msgid "All dates" msgstr "Всички дати" @@ -730,4 +697,4 @@ msgid "Currently:" msgstr "Сега:" msgid "Change:" -msgstr "Промяна:" +msgstr "Промени" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo index 2a86863..4940bb9 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po index 84a6141..ded64ac 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/djangojs.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec , 2022 # Jannis Leidel , 2011 # Venelin Stoykov , 2015-2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2022-01-14 09:49+0000\n" -"Last-Translator: arneatec \n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" +"Last-Translator: Venelin Stoykov \n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -29,8 +28,8 @@ msgid "" "the box below and then clicking the \"Choose\" arrow between the two boxes." msgstr "" "Това е списък на наличните %s . Можете да изберете някои, като ги изберете в " -"полето по-долу и след това кликнете върху стрелката \"Избери\" между двете " -"полета." +"полето по-долу и след това кликнете върху \"Избор\" стрелка между двете " +"кутии." #, javascript-format msgid "Type into this box to filter down the list of available %s." @@ -47,7 +46,7 @@ msgid "Click to choose all %s at once." msgstr "Кликнете, за да изберете всички %s наведнъж." msgid "Choose" -msgstr "Избери" +msgstr "Избирам" msgid "Remove" msgstr "Премахни" @@ -61,9 +60,9 @@ msgid "" "This is the list of chosen %s. You may remove some by selecting them in the " "box below and then clicking the \"Remove\" arrow between the two boxes." msgstr "" -"Това е списък на избраните %s. Можете да премахнете някои, като ги изберете " -"в полето по-долу и след това щракнете върху стрелката \"Премахни\" между " -"двете полета." +"Това е списък на избрания %s. Можете да премахнете някои, като ги изберете в " +"полето по-долу и след това щракнете върху \"Премахни\" стрелка между двете " +"кутии." msgid "Remove all" msgstr "Премахване на всички" @@ -81,33 +80,51 @@ msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." msgstr "" -"Имате незапазени промени по отделни полета за редактиране. Ако изпълните " -"действие, незаписаните промени ще бъдат загубени." +"Имате незапазени промени по отделни полета за редактиране. Ако започнете " +"друго, незаписаните промени ще бъдат загубени." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" "Вие сте избрали действие, но не сте записали промените по полета. Моля, " -"кликнете ОК, за да се запишат. Трябва отново да изпълните действието." +"кликнете ОК, за да се запишат. Трябва отново да започнете действие." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" -"Вие сте избрали действие, но не сте направили промени по полетата. Вероятно " -"търсите Изпълни бутона, а не бутона Запис." +"Вие сте избрали дадена дейност, а не сте направили някакви промени по " +"полетата. Вероятно търсите Go бутон, а не бутона Save." + +#, javascript-format +msgid "Note: You are %s hour ahead of server time." +msgid_plural "Note: You are %s hours ahead of server time." +msgstr[0] "Бележка: Вие сте %s час напред от времето на сървъра." +msgstr[1] "Бележка: Вие сте %s часа напред от времето на сървъра" + +#, javascript-format +msgid "Note: You are %s hour behind server time." +msgid_plural "Note: You are %s hours behind server time." +msgstr[0] "Внимание: Вие сте %s час назад от времето на сървъра." +msgstr[1] "Внимание: Вие сте %s часа назад от времето на сървъра." msgid "Now" msgstr "Сега" +msgid "Choose a Time" +msgstr "Изберете време" + +msgid "Choose a time" +msgstr "Избери време" + msgid "Midnight" msgstr "Полунощ" msgid "6 a.m." -msgstr "6 сутринта" +msgstr "6 a.m." msgid "Noon" msgstr "По обяд" @@ -115,24 +132,6 @@ msgstr "По обяд" msgid "6 p.m." msgstr "6 след обяд" -#, javascript-format -msgid "Note: You are %s hour ahead of server time." -msgid_plural "Note: You are %s hours ahead of server time." -msgstr[0] "Бележка: Вие сте %s час напред от времето на сървъра." -msgstr[1] "Бележка: Вие сте с %s часа напред от времето на сървъра" - -#, javascript-format -msgid "Note: You are %s hour behind server time." -msgid_plural "Note: You are %s hours behind server time." -msgstr[0] "Внимание: Вие сте %s час назад от времето на сървъра." -msgstr[1] "Внимание: Вие сте с %s часа назад от времето на сървъра." - -msgid "Choose a Time" -msgstr "Изберете време" - -msgid "Choose a time" -msgstr "Изберете време" - msgid "Cancel" msgstr "Отказ" @@ -184,54 +183,6 @@ msgstr "Ноември" msgid "December" msgstr "Декември" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "ян." - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "февр." - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "март" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "апр." - -msgctxt "abbrev. month May" -msgid "May" -msgstr "май" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "юни" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "юли" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "авг." - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "септ." - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "окт." - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "ноем." - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "дек." - msgctxt "one letter Sunday" msgid "S" msgstr "Н" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.mo index ff5ca15..72873a1 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.po index 642be31..2322313 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy , 2014-2015,2017,2021 +# Antoni Aloy , 2014-2015,2017 # Carles Barrobés , 2011-2012,2014 # duub qnnp, 2015 # GerardoGa , 2018 @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-27 08:45+0000\n" -"Last-Translator: Antoni Aloy \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-04 21:09+0000\n" +"Last-Translator: Marc Compte \n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" "MIME-Version: 1.0\n" @@ -535,12 +535,6 @@ msgstr "Heu oblidat la vostra contrasenya o nom d'usuari?" msgid "Toggle navigation" msgstr "Canviar mode de navegació" -msgid "Start typing to filter…" -msgstr "Comença a teclejar per filtrar ..." - -msgid "Filter navigation items" -msgstr "Filtrar els items de navegació" - msgid "Date/time" msgstr "Data/hora" @@ -606,8 +600,8 @@ msgstr "Afegeix un altre %(model)s" msgid "Delete selected %(model)s" msgstr "Esborra el %(model)s seleccionat" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Gràcies per dedicar temps de qualitat avui a aquesta web." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Gràcies per passar una estona de qualitat al web durant el dia d'avui." msgid "Log in again" msgstr "Iniciar sessió de nou" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo index 4381bff..7b6e4de 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po index b06cf6c..5fbbab8 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/djangojs.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy , 2017,2021 +# Antoni Aloy , 2017 # Carles Barrobés , 2011-2012,2014 # Jannis Leidel , 2011 # Roger Pons , 2015 @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-27 08:48+0000\n" +"POT-Creation-Date: 2018-05-17 11:50+0200\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" "Last-Translator: Antoni Aloy \n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" @@ -86,21 +86,21 @@ msgstr "" "acció, es perdran aquests canvis no desats." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" -"Has seleccionat una acció, però encara no l'has desat els canvis dels camps " -"individuals. Si us plau clica OK per desar. Necessitaràs tornar a executar " -"l'acció." +"Heu seleccionat una acció, però encara no heu desat els vostres canvis a " +"camps individuals. Si us plau premeu OK per desar. Haureu de tornar a " +"executar l'acció." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" -"Has seleccionat una acció i no has fet cap canvi als camps individuals. " -"Probablement estàs cercant el botó Anar enlloc del botó de Desar." +"Heu seleccionat una acció i no heu fet cap canvi a camps individuals. " +"Probablement esteu cercant el botó 'Anar' enlloc de 'Desar'." msgid "Now" msgstr "Ara" @@ -186,54 +186,6 @@ msgstr "Novembre" msgid "December" msgstr "Desembre" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Gen" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mar" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Abr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Mai" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jun" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Jul" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Ago" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Oct" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nov" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Des" - msgctxt "one letter Sunday" msgid "S" msgstr "D" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.mo index 0dd0b0c..3329fe2 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.po index ce5d6f8..be87f4a 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/cs/LC_MESSAGES/django.po @@ -5,14 +5,14 @@ # Jirka Vejrazka , 2011 # Tomáš Ehrlich , 2015 # Vláďa Macek , 2013-2014 -# Vláďa Macek , 2015-2020,2022 +# Vláďa Macek , 2015-2020 # yedpodtrzitko , 2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-04 18:54+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-20 09:24+0000\n" "Last-Translator: Vláďa Macek \n" "Language-Team: Czech (http://www.transifex.com/django/django/language/cs/)\n" "MIME-Version: 1.0\n" @@ -22,10 +22,6 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Odstranit vybrané položky typu %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Úspěšně odstraněno: %(count)d %(items)s." @@ -37,6 +33,10 @@ msgstr "Nelze smazat %(name)s" msgid "Are you sure?" msgstr "Jste si jisti?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Odstranit vybrané položky typu %(verbose_name_plural)s" + msgid "Administration" msgstr "Správa" @@ -536,12 +536,6 @@ msgstr "Zapomněli jste heslo nebo uživatelské jméno?" msgid "Toggle navigation" msgstr "Přehodit navigaci" -msgid "Start typing to filter…" -msgstr "Filtrovat začnete vepsáním textu..." - -msgid "Filter navigation items" -msgstr "Filtrace položek navigace" - msgid "Date/time" msgstr "Datum a čas" @@ -609,8 +603,8 @@ msgstr "Přidat další %(model)s" msgid "Delete selected %(model)s" msgstr "Odstranit vybrané položky typu %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Děkujeme za dnešní čas strávený s tímto neobyčejným webem." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Děkujeme za čas strávený s tímto webem." msgid "Log in again" msgstr "Přihlaste se znovu" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.mo index 7b6bae0..8c59438 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.po index a1425c5..d628530 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ # André Hagenbruch, 2012 # Florian Apolloner , 2011 # Dimitris Glezos , 2012 -# Florian Apolloner , 2020-2022 +# Florian Apolloner , 2020 # Jannis Vajen, 2013 # Jannis Leidel , 2013-2018,2020 # Jannis Vajen, 2016 @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-15 11:22+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-17 07:47+0000\n" "Last-Translator: Florian Apolloner \n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" @@ -24,10 +24,6 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Ausgewählte %(verbose_name_plural)s löschen" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Erfolgreich %(count)d %(items)s gelöscht." @@ -39,6 +35,10 @@ msgstr "Kann %(name)s nicht löschen" msgid "Are you sure?" msgstr "Sind Sie sicher?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Ausgewählte %(verbose_name_plural)s löschen" + msgid "Administration" msgstr "Administration" @@ -392,7 +392,7 @@ msgid "Welcome," msgstr "Willkommen," msgid "View site" -msgstr "Website anzeigen" +msgstr "Auf der Website anzeigen" msgid "Documentation" msgstr "Dokumentation" @@ -538,12 +538,6 @@ msgstr "Benutzername oder Passwort vergessen?" msgid "Toggle navigation" msgstr "Navigation ein-/ausblenden" -msgid "Start typing to filter…" -msgstr "Eingabe beginnen um zu filtern…" - -msgid "Filter navigation items" -msgstr "Navigationselemente filtern" - msgid "Date/time" msgstr "Datum/Zeit" @@ -609,10 +603,8 @@ msgstr "%(model)s hinzufügen" msgid "Delete selected %(model)s" msgstr "Ausgewählte %(model)s löschen" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" -"Vielen Dank, dass Sie heute ein paar nette Minuten auf dieser Webseite " -"verbracht haben." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Vielen Dank, dass Sie hier ein paar nette Minuten verbracht haben." msgid "Log in again" msgstr "Erneut anmelden" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo index 871b205..1ae1cce 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po index 6ae2da3..7d009f3 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/de/LC_MESSAGES/djangojs.po @@ -2,7 +2,7 @@ # # Translators: # André Hagenbruch, 2011-2012 -# Florian Apolloner , 2020-2021 +# Florian Apolloner , 2020 # Jannis Leidel , 2011,2013-2016 # Jannis Vajen, 2016 # Markus Holtermann , 2020 @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-28 17:14+0000\n" -"Last-Translator: Raphael Michel \n" +"POT-Creation-Date: 2020-05-11 20:56+0200\n" +"PO-Revision-Date: 2020-06-16 13:07+0000\n" +"Last-Translator: Florian Apolloner \n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -188,54 +188,6 @@ msgstr "November" msgid "December" msgstr "Dezember" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Jan" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mrz" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Apr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Mai" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jun" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Jul" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Aug" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Okt" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nov" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Dez" - msgctxt "one letter Sunday" msgid "S" msgstr "So" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo index 5548ab0..5da3e88 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po index 1ffee5d..223eccb 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/el/LC_MESSAGES/djangojs.po @@ -2,7 +2,6 @@ # # Translators: # Dimitris Glezos , 2011 -# Fotis Athineos , 2021 # glogiotatidis , 2011 # Jannis Leidel , 2011 # Nikolas Demiridis , 2014 @@ -13,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-08-04 06:47+0000\n" -"Last-Translator: Fotis Athineos \n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-23 19:47+0000\n" +"Last-Translator: Nick Mavrakis \n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,8 +88,8 @@ msgstr "" "εκτελέσετε μια ενέργεια, οι μη αποθηκευμένες αλλάγες θα χαθούν" msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" "Έχετε επιλέξει μια ενέργεια, αλλά δεν έχετε αποθηκεύσει τις αλλαγές στα " @@ -98,28 +97,13 @@ msgstr "" "χρειαστεί να εκτελέσετε ξανά την ενέργεια." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" "Έχετε επιλέξει μια ενέργεια, και δεν έχετε κάνει καμία αλλαγή στα εκάστοτε " "πεδία. Πιθανών θέλετε το κουμπί Go αντί του κουμπιού Αποθήκευσης." -msgid "Now" -msgstr "Τώρα" - -msgid "Midnight" -msgstr "Μεσάνυχτα" - -msgid "6 a.m." -msgstr "6 π.μ." - -msgid "Noon" -msgstr "Μεσημέρι" - -msgid "6 p.m." -msgstr "6 μ.μ." - #, javascript-format msgid "Note: You are %s hour ahead of server time." msgid_plural "Note: You are %s hours ahead of server time." @@ -132,12 +116,27 @@ msgid_plural "Note: You are %s hours behind server time." msgstr[0] "Σημείωση: Είστε %s ώρα πίσω από την ώρα του εξυπηρετητή" msgstr[1] "Σημείωση: Είστε %s ώρες πίσω από την ώρα του εξυπηρετητή." +msgid "Now" +msgstr "Τώρα" + msgid "Choose a Time" msgstr "Επιλέξτε Χρόνο" msgid "Choose a time" msgstr "Επιλέξτε χρόνο" +msgid "Midnight" +msgstr "Μεσάνυχτα" + +msgid "6 a.m." +msgstr "6 π.μ." + +msgid "Noon" +msgstr "Μεσημέρι" + +msgid "6 p.m." +msgstr "6 μ.μ." + msgid "Cancel" msgstr "Ακύρωση" @@ -189,54 +188,6 @@ msgstr "Νοέμβριος" msgid "December" msgstr "Δεκέμβριος" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Ιαν" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Φεβ" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Μάρ" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Απρ" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Μάι" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Ιούν" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Ιούλ" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Αύγ" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Σεπ" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Οκτ" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Νοέ" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Δεκ" - msgctxt "one letter Sunday" msgid "S" msgstr "Κ" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/en/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/en/LC_MESSAGES/django.po index ee8c3cf..d5dc5e5 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/en/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English \n" @@ -24,12 +24,12 @@ msgstr "" msgid "Successfully deleted %(count)d %(items)s." msgstr "" -#: contrib/admin/actions.py:55 contrib/admin/options.py:1897 +#: contrib/admin/actions.py:55 contrib/admin/options.py:1886 #, python-format msgid "Cannot delete %(name)s" msgstr "" -#: contrib/admin/actions.py:57 contrib/admin/options.py:1899 +#: contrib/admin/actions.py:57 contrib/admin/options.py:1888 msgid "Are you sure?" msgstr "" @@ -39,7 +39,7 @@ msgstr "" #: contrib/admin/filters.py:108 contrib/admin/filters.py:213 #: contrib/admin/filters.py:249 contrib/admin/filters.py:284 -#: contrib/admin/filters.py:403 contrib/admin/filters.py:469 +#: contrib/admin/filters.py:403 contrib/admin/filters.py:468 msgid "All" msgstr "" @@ -83,11 +83,11 @@ msgstr "" msgid "Has date" msgstr "" -#: contrib/admin/filters.py:470 +#: contrib/admin/filters.py:469 msgid "Empty" msgstr "" -#: contrib/admin/filters.py:471 +#: contrib/admin/filters.py:470 msgid "Not empty" msgstr "" @@ -102,12 +102,12 @@ msgstr "" msgid "Action:" msgstr "" -#: contrib/admin/helpers.py:350 +#: contrib/admin/helpers.py:329 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: contrib/admin/helpers.py:353 +#: contrib/admin/helpers.py:332 msgid "Remove" msgstr "" @@ -116,8 +116,8 @@ msgid "Addition" msgstr "" #: contrib/admin/models.py:18 contrib/admin/templates/admin/app_list.html:28 -#: contrib/admin/templates/admin/edit_inline/stacked.html:16 -#: contrib/admin/templates/admin/edit_inline/tabular.html:36 +#: contrib/admin/templates/admin/edit_inline/stacked.html:12 +#: contrib/admin/templates/admin/edit_inline/tabular.html:34 #: contrib/admin/templates/admin/widgets/related_widget_wrapper.html:11 msgid "Change" msgstr "" @@ -191,7 +191,7 @@ msgstr "" msgid "Added." msgstr "" -#: contrib/admin/models.py:117 contrib/admin/options.py:2132 +#: contrib/admin/models.py:117 contrib/admin/options.py:2112 msgid "and" msgstr "" @@ -214,148 +214,148 @@ msgstr "" msgid "No fields changed." msgstr "" -#: contrib/admin/options.py:202 contrib/admin/options.py:234 +#: contrib/admin/options.py:203 contrib/admin/options.py:235 msgid "None" msgstr "" -#: contrib/admin/options.py:280 +#: contrib/admin/options.py:281 msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." msgstr "" -#: contrib/admin/options.py:1232 contrib/admin/options.py:1256 +#: contrib/admin/options.py:1221 contrib/admin/options.py:1245 #, python-brace-format msgid "The {name} “{obj}” was added successfully." msgstr "" -#: contrib/admin/options.py:1234 +#: contrib/admin/options.py:1223 msgid "You may edit it again below." msgstr "" -#: contrib/admin/options.py:1246 +#: contrib/admin/options.py:1235 #, python-brace-format msgid "" "The {name} “{obj}” was added successfully. You may add another {name} below." msgstr "" -#: contrib/admin/options.py:1296 +#: contrib/admin/options.py:1285 #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may edit it again below." msgstr "" -#: contrib/admin/options.py:1306 +#: contrib/admin/options.py:1295 #, python-brace-format msgid "The {name} “{obj}” was added successfully. You may edit it again below." msgstr "" -#: contrib/admin/options.py:1319 +#: contrib/admin/options.py:1308 #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may add another {name} " "below." msgstr "" -#: contrib/admin/options.py:1331 +#: contrib/admin/options.py:1320 #, python-brace-format msgid "The {name} “{obj}” was changed successfully." msgstr "" -#: contrib/admin/options.py:1408 contrib/admin/options.py:1738 +#: contrib/admin/options.py:1397 contrib/admin/options.py:1727 msgid "" "Items must be selected in order to perform actions on them. No items have " "been changed." msgstr "" -#: contrib/admin/options.py:1427 +#: contrib/admin/options.py:1416 msgid "No action selected." msgstr "" -#: contrib/admin/options.py:1452 +#: contrib/admin/options.py:1441 #, python-format msgid "The %(name)s “%(obj)s” was deleted successfully." msgstr "" -#: contrib/admin/options.py:1538 +#: contrib/admin/options.py:1527 #, python-format msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" msgstr "" -#: contrib/admin/options.py:1633 +#: contrib/admin/options.py:1622 #, python-format msgid "Add %s" msgstr "" -#: contrib/admin/options.py:1635 +#: contrib/admin/options.py:1624 #, python-format msgid "Change %s" msgstr "" -#: contrib/admin/options.py:1637 +#: contrib/admin/options.py:1626 #, python-format msgid "View %s" msgstr "" -#: contrib/admin/options.py:1716 +#: contrib/admin/options.py:1705 msgid "Database error" msgstr "" -#: contrib/admin/options.py:1785 +#: contrib/admin/options.py:1774 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "" msgstr[1] "" -#: contrib/admin/options.py:1816 +#: contrib/admin/options.py:1805 #, python-format msgid "%(total_count)s selected" msgid_plural "All %(total_count)s selected" msgstr[0] "" msgstr[1] "" -#: contrib/admin/options.py:1824 +#: contrib/admin/options.py:1813 #, python-format msgid "0 of %(cnt)s selected" msgstr "" -#: contrib/admin/options.py:1944 +#: contrib/admin/options.py:1932 #, python-format msgid "Change history: %s" msgstr "" #. Translators: Model verbose name and instance representation, #. suitable to be an item in a list. -#: contrib/admin/options.py:2125 +#: contrib/admin/options.py:2105 #, python-format msgid "%(class_name)s %(instance)s" msgstr "" -#: contrib/admin/options.py:2134 +#: contrib/admin/options.py:2114 #, python-format msgid "" "Deleting %(class_name)s %(instance)s would require deleting the following " "protected related objects: %(related_objects)s" msgstr "" -#: contrib/admin/sites.py:48 contrib/admin/templates/admin/base_site.html:3 +#: contrib/admin/sites.py:47 contrib/admin/templates/admin/base_site.html:3 msgid "Django site admin" msgstr "" -#: contrib/admin/sites.py:51 contrib/admin/templates/admin/base_site.html:6 +#: contrib/admin/sites.py:50 contrib/admin/templates/admin/base_site.html:6 msgid "Django administration" msgstr "" -#: contrib/admin/sites.py:54 +#: contrib/admin/sites.py:53 msgid "Site administration" msgstr "" -#: contrib/admin/sites.py:402 contrib/admin/templates/admin/login.html:63 +#: contrib/admin/sites.py:399 contrib/admin/templates/admin/login.html:63 #: contrib/admin/templates/registration/password_reset_complete.html:15 #: contrib/admin/tests.py:135 msgid "Log in" msgstr "" -#: contrib/admin/sites.py:547 +#: contrib/admin/sites.py:544 #, python-format msgid "%(app)s administration" msgstr "" @@ -372,7 +372,7 @@ msgstr "" #: contrib/admin/templates/admin/500.html:6 #: contrib/admin/templates/admin/app_index.html:9 #: contrib/admin/templates/admin/auth/user/change_password.html:10 -#: contrib/admin/templates/admin/base.html:66 +#: contrib/admin/templates/admin/base.html:65 #: contrib/admin/templates/admin/change_form.html:18 #: contrib/admin/templates/admin/change_list.html:31 #: contrib/admin/templates/admin/delete_confirmation.html:14 @@ -439,8 +439,8 @@ msgid "Add" msgstr "" #: contrib/admin/templates/admin/app_list.html:26 -#: contrib/admin/templates/admin/edit_inline/stacked.html:16 -#: contrib/admin/templates/admin/edit_inline/tabular.html:36 +#: contrib/admin/templates/admin/edit_inline/stacked.html:12 +#: contrib/admin/templates/admin/edit_inline/tabular.html:34 msgid "View" msgstr "" @@ -519,8 +519,8 @@ msgid "History" msgstr "" #: contrib/admin/templates/admin/change_form_object_tools.html:7 -#: contrib/admin/templates/admin/edit_inline/stacked.html:18 -#: contrib/admin/templates/admin/edit_inline/tabular.html:38 +#: contrib/admin/templates/admin/edit_inline/stacked.html:14 +#: contrib/admin/templates/admin/edit_inline/tabular.html:36 msgid "View on site" msgstr "" @@ -614,7 +614,7 @@ msgid "" "following objects and their related items will be deleted:" msgstr "" -#: contrib/admin/templates/admin/edit_inline/tabular.html:22 +#: contrib/admin/templates/admin/edit_inline/tabular.html:20 msgid "Delete?" msgstr "" @@ -665,14 +665,6 @@ msgstr "" msgid "Toggle navigation" msgstr "" -#: contrib/admin/templates/admin/nav_sidebar.html:5 -msgid "Start typing to filter…" -msgstr "" - -#: contrib/admin/templates/admin/nav_sidebar.html:6 -msgid "Filter navigation items" -msgstr "" - #: contrib/admin/templates/admin/object_history.html:22 msgid "Date/time" msgstr "" @@ -757,7 +749,7 @@ msgid "Delete selected %(model)s" msgstr "" #: contrib/admin/templates/registration/logged_out.html:10 -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "" #: contrib/admin/templates/registration/logged_out.html:12 @@ -868,21 +860,21 @@ msgstr "" msgid "Reset my password" msgstr "" -#: contrib/admin/templatetags/admin_list.py:391 +#: contrib/admin/templatetags/admin_list.py:390 msgid "All dates" msgstr "" -#: contrib/admin/views/main.py:103 +#: contrib/admin/views/main.py:102 #, python-format msgid "Select %s" msgstr "" -#: contrib/admin/views/main.py:105 +#: contrib/admin/views/main.py:104 #, python-format msgid "Select %s to change" msgstr "" -#: contrib/admin/views/main.py:107 +#: contrib/admin/views/main.py:106 #, python-format msgid "Select %s to view" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo index c86ec5d..a19397e 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po index dfe62a0..111eb38 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield , 2014 -# Tom Fifield , 2021 +# Tom Fifield , 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 07:21+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 21:09+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: English (Australia) (http://www.transifex.com/django/django/" "language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -18,10 +17,6 @@ msgstr "" "Language: en_AU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Delete selected %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Successfully deleted %(count)d %(items)s." @@ -33,14 +28,18 @@ msgstr "Cannot delete %(name)s" msgid "Are you sure?" msgstr "Are you sure?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Delete selected %(verbose_name_plural)s" + msgid "Administration" -msgstr "Administration" +msgstr "" msgid "All" msgstr "All" msgid "Yes" -msgstr "Yes" +msgstr "" msgid "No" msgstr "No" @@ -64,16 +63,10 @@ msgid "This year" msgstr "This year" msgid "No date" -msgstr "No date" +msgstr "" msgid "Has date" -msgstr "Has date" - -msgid "Empty" -msgstr "Empty" - -msgid "Not empty" -msgstr "Not empty" +msgstr "" #, python-format msgid "" @@ -88,34 +81,25 @@ msgstr "Action:" #, python-format msgid "Add another %(verbose_name)s" -msgstr "Add another %(verbose_name)s" +msgstr "" msgid "Remove" -msgstr "Remove" - -msgid "Addition" -msgstr "Addition" - -msgid "Change" -msgstr "Change" - -msgid "Deletion" -msgstr "Deletion" +msgstr "" msgid "action time" msgstr "action time" msgid "user" -msgstr "user" +msgstr "" msgid "content type" -msgstr "content type" +msgstr "" msgid "object id" msgstr "object id" #. Translators: 'repr' means representation -#. (https://docs.python.org/library/functions.html#repr) +#. (https://docs.python.org/3/library/functions.html#repr) msgid "object repr" msgstr "object repr" @@ -132,41 +116,41 @@ msgid "log entries" msgstr "log entries" #, python-format -msgid "Added “%(object)s”." -msgstr "Added “%(object)s”." +msgid "Added \"%(object)s\"." +msgstr "Added \"%(object)s\"." #, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "Changed “%(object)s” — %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Changed \"%(object)s\" - %(changes)s" #, python-format -msgid "Deleted “%(object)s.”" -msgstr "Deleted “%(object)s.”" +msgid "Deleted \"%(object)s.\"" +msgstr "Deleted \"%(object)s.\"" msgid "LogEntry Object" msgstr "LogEntry Object" #, python-brace-format -msgid "Added {name} “{object}”." -msgstr "Added {name} “{object}”." +msgid "Added {name} \"{object}\"." +msgstr "" msgid "Added." -msgstr "Added." +msgstr "" msgid "and" msgstr "and" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "Changed {fields} for {name} “{object}”." +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" #, python-brace-format msgid "Changed {fields}." -msgstr "Changed {fields}." +msgstr "" #, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "Deleted {name} “{object}”." +msgid "Deleted {name} \"{object}\"." +msgstr "" msgid "No fields changed." msgstr "No fields changed." @@ -174,44 +158,39 @@ msgstr "No fields changed." msgid "None" msgstr "None" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." -msgstr "Hold down “Control”, or “Command” on a Mac, to select more than one." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "The {name} “{obj}” was added successfully." - -msgid "You may edit it again below." -msgstr "You may edit it again below." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." +"The {name} \"{obj}\" was added successfully. You may edit it again below." msgstr "" -"The {name} “{obj}” was added successfully. You may add another {name} below." #, python-brace-format msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" -"The {name} “{obj}” was changed successfully. You may edit it again below." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" -"The {name} “{obj}” was added successfully. You may edit it again below." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" -"The {name} “{obj}” was changed successfully. You may add another {name} " -"below." #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." -msgstr "The {name} “{obj}” was changed successfully." +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -224,12 +203,12 @@ msgid "No action selected." msgstr "No action selected." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "The %(name)s “%(obj)s” was deleted successfully." +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "" #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" #, python-format msgid "Add %s" @@ -239,10 +218,6 @@ msgstr "Add %s" msgid "Change %s" msgstr "Change %s" -#, python-format -msgid "View %s" -msgstr "View %s" - msgid "Database error" msgstr "Database error" @@ -264,155 +239,133 @@ msgstr "0 of %(cnt)s selected" #, python-format msgid "Change history: %s" -msgstr "Change history: %s" +msgstr "" #. Translators: Model verbose name and instance representation, #. suitable to be an item in a list. #, python-format msgid "%(class_name)s %(instance)s" -msgstr "%(class_name)s %(instance)s" +msgstr "" #, python-format msgid "" "Deleting %(class_name)s %(instance)s would require deleting the following " "protected related objects: %(related_objects)s" msgstr "" -"Deleting %(class_name)s %(instance)s would require deleting the following " -"protected related objects: %(related_objects)s" msgid "Django site admin" -msgstr "Django site admin" +msgstr "" msgid "Django administration" -msgstr "Django administration" +msgstr "" msgid "Site administration" -msgstr "Site administration" +msgstr "" msgid "Log in" -msgstr "Log in" +msgstr "" #, python-format msgid "%(app)s administration" -msgstr "%(app)s administration" +msgstr "" msgid "Page not found" -msgstr "Page not found" +msgstr "" -msgid "We’re sorry, but the requested page could not be found." -msgstr "We’re sorry, but the requested page could not be found." +msgid "We're sorry, but the requested page could not be found." +msgstr "" msgid "Home" -msgstr "Home" +msgstr "" msgid "Server error" -msgstr "Server error" +msgstr "" msgid "Server error (500)" -msgstr "Server error (500)" +msgstr "" msgid "Server Error (500)" -msgstr "Server Error (500)" +msgstr "" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" -"There’s been an error. It’s been reported to the site administrators via " -"email and should be fixed shortly. Thanks for your patience." msgid "Run the selected action" -msgstr "Run the selected action" +msgstr "" msgid "Go" -msgstr "Go" +msgstr "" msgid "Click here to select the objects across all pages" -msgstr "Click here to select the objects across all pages" +msgstr "" #, python-format msgid "Select all %(total_count)s %(module_name)s" -msgstr "Select all %(total_count)s %(module_name)s" +msgstr "" msgid "Clear selection" -msgstr "Clear selection" - -#, python-format -msgid "Models in the %(name)s application" -msgstr "Models in the %(name)s application" - -msgid "Add" -msgstr "Add" - -msgid "View" -msgstr "View" - -msgid "You don’t have permission to view or edit anything." -msgstr "You don’t have permission to view or edit anything." +msgstr "" msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" -"First, enter a username and password. Then, you’ll be able to edit more user " -"options." msgid "Enter a username and password." -msgstr "Enter a username and password." +msgstr "" msgid "Change password" -msgstr "Change password" +msgstr "" msgid "Please correct the error below." -msgstr "Please correct the error below." +msgstr "" msgid "Please correct the errors below." -msgstr "Please correct the errors below." +msgstr "" #, python-format msgid "Enter a new password for the user %(username)s." -msgstr "Enter a new password for the user %(username)s." +msgstr "" msgid "Welcome," -msgstr "Welcome," +msgstr "" msgid "View site" -msgstr "View site" +msgstr "" msgid "Documentation" -msgstr "Documentation" +msgstr "" msgid "Log out" -msgstr "Log out" +msgstr "" #, python-format msgid "Add %(name)s" -msgstr "Add %(name)s" +msgstr "" msgid "History" -msgstr "History" +msgstr "" msgid "View on site" -msgstr "View on site" +msgstr "" msgid "Filter" -msgstr "Filter" - -msgid "Clear all filters" -msgstr "Clear all filters" +msgstr "" msgid "Remove from sorting" -msgstr "Remove from sorting" +msgstr "" #, python-format msgid "Sorting priority: %(priority_number)s" -msgstr "Sorting priority: %(priority_number)s" +msgstr "" msgid "Toggle sorting" -msgstr "Toggle sorting" +msgstr "" msgid "Delete" -msgstr "Delete" +msgstr "" #, python-format msgid "" @@ -420,37 +373,30 @@ msgid "" "related objects, but your account doesn't have permission to delete the " "following types of objects:" msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" #, python-format msgid "" "Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " "following protected related objects:" msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " -"following protected related objects:" #, python-format msgid "" "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " "All of the following related items will be deleted:" msgstr "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" msgid "Objects" -msgstr "Objects" +msgstr "" -msgid "Yes, I’m sure" -msgstr "Yes, I’m sure" +msgid "Yes, I'm sure" +msgstr "" msgid "No, take me back" -msgstr "No, take me back" +msgstr "" msgid "Delete multiple objects" -msgstr "Delete multiple objects" +msgstr "" #, python-format msgid "" @@ -458,267 +404,233 @@ msgid "" "objects, but your account doesn't have permission to delete the following " "types of objects:" msgstr "" -"Deleting the selected %(objects_name)s would result in deleting related " -"objects, but your account doesn't have permission to delete the following " -"types of objects:" #, python-format msgid "" "Deleting the selected %(objects_name)s would require deleting the following " "protected related objects:" msgstr "" -"Deleting the selected %(objects_name)s would require deleting the following " -"protected related objects:" #, python-format msgid "" "Are you sure you want to delete the selected %(objects_name)s? All of the " "following objects and their related items will be deleted:" msgstr "" -"Are you sure you want to delete the selected %(objects_name)s? All of the " -"following objects and their related items will be deleted:" + +msgid "Change" +msgstr "" msgid "Delete?" -msgstr "Delete?" +msgstr "" #, python-format msgid " By %(filter_title)s " -msgstr " By %(filter_title)s " +msgstr "" msgid "Summary" -msgstr "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "" + +msgid "You don't have permission to edit anything." +msgstr "" msgid "Recent actions" -msgstr "Recent actions" +msgstr "" msgid "My actions" -msgstr "My actions" +msgstr "" msgid "None available" -msgstr "None available" +msgstr "" msgid "Unknown content" -msgstr "Unknown content" +msgstr "" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Something’s wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." #, python-format msgid "" "You are authenticated as %(username)s, but are not authorized to access this " "page. Would you like to login to a different account?" msgstr "" -"You are authenticated as %(username)s, but are not authorised to access this " -"page. Would you like to login to a different account?" msgid "Forgotten your password or username?" -msgstr "Forgotten your password or username?" - -msgid "Toggle navigation" -msgstr "Toggle navigation" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" msgstr "" msgid "Date/time" -msgstr "Date/time" +msgstr "" msgid "User" -msgstr "User" +msgstr "" msgid "Action" -msgstr "Action" +msgstr "" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" -"This object doesn’t have a change history. It probably wasn’t added via this " -"admin site." msgid "Show all" -msgstr "Show all" +msgstr "" msgid "Save" -msgstr "Save" +msgstr "" -msgid "Popup closing…" -msgstr "Popup closing…" +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" msgid "Search" -msgstr "Search" +msgstr "" #, python-format msgid "%(counter)s result" msgid_plural "%(counter)s results" -msgstr[0] "%(counter)s result" -msgstr[1] "%(counter)s results" +msgstr[0] "" +msgstr[1] "" #, python-format msgid "%(full_result_count)s total" -msgstr "%(full_result_count)s total" +msgstr "" msgid "Save as new" -msgstr "Save as new" +msgstr "" msgid "Save and add another" -msgstr "Save and add another" +msgstr "" msgid "Save and continue editing" -msgstr "Save and continue editing" +msgstr "" -msgid "Save and view" -msgstr "Save and view" - -msgid "Close" -msgstr "Close" - -#, python-format -msgid "Change selected %(model)s" -msgstr "Change selected %(model)s" - -#, python-format -msgid "Add another %(model)s" -msgstr "Add another %(model)s" - -#, python-format -msgid "Delete selected %(model)s" -msgstr "Delete selected %(model)s" - -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "" msgid "Log in again" -msgstr "Log in again" +msgstr "" msgid "Password change" -msgstr "Password change" +msgstr "" msgid "Your password was changed." -msgstr "Your password was changed." +msgstr "" msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" -"Please enter your old password, for security’s sake, and then enter your new " -"password twice so we can verify you typed it in correctly." msgid "Change my password" -msgstr "Change my password" +msgstr "" msgid "Password reset" -msgstr "Password reset" +msgstr "" msgid "Your password has been set. You may go ahead and log in now." -msgstr "Your password has been set. You may go ahead and log in now." +msgstr "" msgid "Password reset confirmation" -msgstr "Password reset confirmation" +msgstr "" msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." msgid "New password:" -msgstr "New password:" +msgstr "" msgid "Confirm password:" -msgstr "Confirm password:" +msgstr "" msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" -"We’ve emailed you instructions for setting your password, if an account " -"exists with the email you entered. You should receive them shortly." msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" -"If you don’t receive an email, please make sure you’ve entered the address " -"you registered with, and check your spam folder." #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"You're receiving this email because you requested a password reset for your " -"user account at %(site_name)s." msgid "Please go to the following page and choose a new password:" -msgstr "Please go to the following page and choose a new password:" +msgstr "" -msgid "Your username, in case you’ve forgotten:" -msgstr "Your username, in case you’ve forgotten:" +msgid "Your username, in case you've forgotten:" +msgstr "" msgid "Thanks for using our site!" -msgstr "Thanks for using our site!" +msgstr "" #, python-format msgid "The %(site_name)s team" -msgstr "The %(site_name)s team" +msgstr "" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" -"Forgotten your password? Enter your email address below, and we’ll email " -"instructions for setting a new one." msgid "Email address:" -msgstr "Email address:" +msgstr "" msgid "Reset my password" -msgstr "Reset my password" +msgstr "" msgid "All dates" -msgstr "All dates" +msgstr "" #, python-format msgid "Select %s" -msgstr "Select %s" +msgstr "" #, python-format msgid "Select %s to change" -msgstr "Select %s to change" - -#, python-format -msgid "Select %s to view" -msgstr "Select %s to view" +msgstr "" msgid "Date:" -msgstr "Date:" +msgstr "" msgid "Time:" -msgstr "Time:" +msgstr "" msgid "Lookup" -msgstr "Lookup" +msgstr "" msgid "Currently:" -msgstr "Currently:" +msgstr "" msgid "Change:" -msgstr "Change:" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo index 077e784..775077f 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po index c4e52eb..fe991ff 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/en_AU/LC_MESSAGES/djangojs.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield , 2014 -# Tom Fifield , 2021 +# Tom Fifield , 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-04-11 13:13+0000\n" -"Last-Translator: Tom Fifield \n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-19 21:09+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: English (Australia) (http://www.transifex.com/django/django/" "language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -71,196 +70,140 @@ msgstr "Click to remove all chosen %s at once." msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" -msgstr[0] "%(sel)s of %(cnt)s selected" -msgstr[1] "%(sel)s of %(cnt)s selected" +msgstr[0] "" +msgstr[1] "" msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." msgstr "" -"You have unsaved changes on individual editable fields. If you run an " -"action, your unsaved changes will be lost." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " -"action." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " -"button." - -msgid "Now" -msgstr "Now" - -msgid "Midnight" -msgstr "Midnight" - -msgid "6 a.m." -msgstr "6 a.m." - -msgid "Noon" -msgstr "Noon" - -msgid "6 p.m." -msgstr "6 p.m." #, javascript-format msgid "Note: You are %s hour ahead of server time." msgid_plural "Note: You are %s hours ahead of server time." -msgstr[0] "Note: You are %s hour ahead of server time." -msgstr[1] "Note: You are %s hours ahead of server time." +msgstr[0] "" +msgstr[1] "" #, javascript-format msgid "Note: You are %s hour behind server time." msgid_plural "Note: You are %s hours behind server time." -msgstr[0] "Note: You are %s hour behind server time." -msgstr[1] "Note: You are %s hours behind server time." +msgstr[0] "" +msgstr[1] "" + +msgid "Now" +msgstr "" msgid "Choose a Time" -msgstr "Choose a Time" +msgstr "" msgid "Choose a time" -msgstr "Choose a time" +msgstr "" + +msgid "Midnight" +msgstr "" + +msgid "6 a.m." +msgstr "" + +msgid "Noon" +msgstr "" + +msgid "6 p.m." +msgstr "" msgid "Cancel" -msgstr "Cancel" +msgstr "" msgid "Today" -msgstr "Today" +msgstr "" msgid "Choose a Date" -msgstr "Choose a Date" +msgstr "" msgid "Yesterday" -msgstr "Yesterday" +msgstr "" msgid "Tomorrow" -msgstr "Tomorrow" +msgstr "" msgid "January" -msgstr "January" +msgstr "" msgid "February" -msgstr "February" +msgstr "" msgid "March" -msgstr "March" +msgstr "" msgid "April" -msgstr "April" +msgstr "" msgid "May" -msgstr "May" +msgstr "" msgid "June" -msgstr "June" +msgstr "" msgid "July" -msgstr "July" +msgstr "" msgid "August" -msgstr "August" +msgstr "" msgid "September" -msgstr "September" +msgstr "" msgid "October" -msgstr "October" +msgstr "" msgid "November" -msgstr "November" +msgstr "" msgid "December" -msgstr "December" - -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Jan" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mar" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Apr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "May" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jun" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Jul" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Aug" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Oct" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nov" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Dec" +msgstr "" msgctxt "one letter Sunday" msgid "S" -msgstr "S" +msgstr "" msgctxt "one letter Monday" msgid "M" -msgstr "M" +msgstr "" msgctxt "one letter Tuesday" msgid "T" -msgstr "T" +msgstr "" msgctxt "one letter Wednesday" msgid "W" -msgstr "W" +msgstr "" msgctxt "one letter Thursday" msgid "T" -msgstr "T" +msgstr "" msgctxt "one letter Friday" msgid "F" -msgstr "F" +msgstr "" msgctxt "one letter Saturday" msgid "S" -msgstr "S" +msgstr "" msgid "Show" -msgstr "Show" +msgstr "" msgid "Hide" -msgstr "Hide" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.mo index 72401f3..3e5dbb4 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.po index 65f3e0c..2931189 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/es/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ # abraham.martin , 2014 # Antoni Aloy , 2011-2014 # Claude Paroz , 2014 -# e4db27214f7e7544f2022c647b585925_bb0e321, 2015-2016 +# Ernesto Avilés, 2015-2016 # 8cb2d5a716c3c9a99b6d20472609a4d5_6d03802 , 2011 # guillem , 2012 # Ignacio José Lizarán Rus , 2019 @@ -17,14 +17,14 @@ # Marc Garcia , 2011 # Miguel Angel Tribaldos , 2017 # Pablo, 2015 -# Uriel Medina , 2020-2021 +# Uriel Medina , 2020 # Veronicabh , 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-10 03:53+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-09-25 17:35+0000\n" "Last-Translator: Uriel Medina \n" "Language-Team: Spanish (http://www.transifex.com/django/django/language/" "es/)\n" @@ -34,10 +34,6 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Eliminar %(verbose_name_plural)s seleccionado/s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Eliminado/s %(count)d %(items)s satisfactoriamente." @@ -49,6 +45,10 @@ msgstr "No se puede eliminar %(name)s" msgid "Are you sure?" msgstr "¿Está seguro?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Eliminar %(verbose_name_plural)s seleccionado/s" + msgid "Administration" msgstr "Administración" @@ -546,12 +546,6 @@ msgstr "¿Ha olvidado la contraseña o el nombre de usuario?" msgid "Toggle navigation" msgstr "Activar navegación" -msgid "Start typing to filter…" -msgstr "Empiece a escribir para filtrar…" - -msgid "Filter navigation items" -msgstr "Filtrar elementos de navegación" - msgid "Date/time" msgstr "Fecha/hora" @@ -617,8 +611,8 @@ msgstr "Añadir otro %(model)s" msgid "Delete selected %(model)s" msgstr "Eliminar %(model)s seleccionada/o" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Gracias por pasar un buen rato con el sitio web hoy." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Gracias por el tiempo que ha dedicado hoy al sitio web." msgid "Log in again" msgstr "Iniciar sesión de nuevo" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo index 808d192..862aaa6 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po index c3327e7..189f5b5 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 14:47+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-21 12:55+0000\n" "Last-Translator: Ramiro Morales\n" "Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/" "language/es_AR/)\n" @@ -528,13 +528,7 @@ msgid "Forgotten your password or username?" msgstr "¿Olvidó su contraseña o nombre de usuario?" msgid "Toggle navigation" -msgstr "(des)activar navegación" - -msgid "Start typing to filter…" -msgstr "Empiece a escribir para filtrar…" - -msgid "Filter navigation items" -msgstr "Filtrar elementos de navegación" +msgstr "(des)activar ordenamiento" msgid "Date/time" msgstr "Fecha/hora" @@ -601,7 +595,7 @@ msgstr "Agregar otro/a %(model)s" msgid "Delete selected %(model)s" msgstr "Eliminar %(model)s seleccionados/as" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "Gracias por el tiempo que ha dedicado al sitio web hoy." msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.mo index 2cc581a..7ebef69 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.po index ae3144b..70431e4 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django.po @@ -6,21 +6,19 @@ # Ali Vakilzade , 2015 # Amir Ajorloo , 2020 # Arash Fazeli , 2012 -# Farshad Asadpour, 2021 # Jannis Leidel , 2011 # MJafar Mashhadi , 2018 # Mohammad Hossein Mojtahedi , 2017,2019 # Pouya Abbassi, 2016 # rahim agh , 2021 # Reza Mohammadi , 2013-2014 -# Sajad Rahimi , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-23 18:10+0000\n" -"Last-Translator: Farshad Asadpour\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-04-03 06:12+0000\n" +"Last-Translator: rahim agh \n" "Language-Team: Persian (http://www.transifex.com/django/django/language/" "fa/)\n" "MIME-Version: 1.0\n" @@ -214,8 +212,8 @@ msgstr "" #, python-brace-format msgid "The {name} “{obj}” was added successfully. You may edit it again below." msgstr "" -" {name} \"{obj}\" به موفقیت اضافه شد. شما میتوانید در قسمت پایین، دوباره آن " -"را ویرایش کنید." +" {name} \"{obj}\" به موفقیت اضافه شد. شما میتوانید در قسمت پایین، آنرا " +"ویرایش کنید." #, python-brace-format msgid "" @@ -537,12 +535,6 @@ msgstr "گذرواژه یا نام کاربری خود را فراموش کرد msgid "Toggle navigation" msgstr "تعویض جهت یابی" -msgid "Start typing to filter…" -msgstr "آغار به کار نوشتن برای فیلترکردن ..." - -msgid "Filter navigation items" -msgstr "فیلتر کردن آیتم های مسیریابی" - msgid "Date/time" msgstr "تاریخ/ساعت" @@ -608,9 +600,8 @@ msgstr "افزدون %(model)s دیگر" msgid "Delete selected %(model)s" msgstr "حذف کردن %(model)s انتخاب شده" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" -"از شما ممنون هستیم که زمان با ارزش خود را برای این تارنما امروز صرف کرده اید" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "متشکر از اینکه مدتی از وقت خود را به ما اختصاص دادید." msgid "Log in again" msgstr "ورود دوباره" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.mo index 3eaee9b..b9134b2 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.po index f6e7a65..6417007 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/django.po @@ -1,19 +1,18 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Aarni Koskela, 2015,2017,2020-2021 +# Aarni Koskela, 2015,2017,2020 # Antti Kaihola , 2011 # Jannis Leidel , 2011 -# Jiri Grönroos , 2021 # Klaus Dahlén , 2012 -# Nikolay Korotkiy , 2018 +# Nikolay Korotkiy , 2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 15:46+0000\n" -"Last-Translator: Jiri Grönroos \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-09 06:47+0000\n" +"Last-Translator: Aarni Koskela\n" "Language-Team: Finnish (http://www.transifex.com/django/django/language/" "fi/)\n" "MIME-Version: 1.0\n" @@ -365,10 +364,10 @@ msgid "Change password" msgstr "Vaihda salasana" msgid "Please correct the error below." -msgstr "Korjaa alla oleva virhe." +msgstr "Korjaa allaoleva virhe." msgid "Please correct the errors below." -msgstr "Korjaa alla olevat virheet." +msgstr "Korjaa allaolevat virheet." #, python-format msgid "Enter a new password for the user %(username)s." @@ -400,7 +399,7 @@ msgid "Filter" msgstr "Suodatin" msgid "Clear all filters" -msgstr "Tyhjennä kaikki suodattimet" +msgstr "Tyhjennä kaikki suotimet" msgid "Remove from sorting" msgstr "Poista järjestämisestä" @@ -476,7 +475,7 @@ msgid "" "Are you sure you want to delete the selected %(objects_name)s? All of the " "following objects and their related items will be deleted:" msgstr "" -"Haluatko varmasti poistaa valitut %(objects_name)s? Samalla poistetaan " +"Haluatki varmasti poistaa valitut %(objects_name)s? Samalla poistetaan " "kaikki alla mainitut ja niihin liittyvät kohteet:" msgid "Delete?" @@ -523,12 +522,6 @@ msgstr "Unohditko salasanasi tai käyttäjätunnuksesi?" msgid "Toggle navigation" msgstr "Kytke navigaatio" -msgid "Start typing to filter…" -msgstr "Kirjoita suodattaaksesi..." - -msgid "Filter navigation items" -msgstr "Suodata navigaatiovaihtoehtoja" - msgid "Date/time" msgstr "Pvm/klo" @@ -594,7 +587,7 @@ msgstr "Lisää toinen %(model)s" msgid "Delete selected %(model)s" msgstr "Poista valitut %(model)s" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "Kiitos sivuillamme viettämästäsi ajasta." msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo index 8d6a1db..7ff9be7 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po index 15fddfe..05b1602 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/fi/LC_MESSAGES/djangojs.po @@ -4,14 +4,13 @@ # Aarni Koskela, 2015,2017,2020-2021 # Antti Kaihola , 2011 # Jannis Leidel , 2011 -# Jiri Grönroos , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-22 15:49+0000\n" -"Last-Translator: Jiri Grönroos \n" +"PO-Revision-Date: 2021-04-14 12:20+0000\n" +"Last-Translator: Aarni Koskela\n" "Language-Team: Finnish (http://www.transifex.com/django/django/language/" "fi/)\n" "MIME-Version: 1.0\n" @@ -29,7 +28,7 @@ msgid "" "This is the list of available %s. You may choose some by selecting them in " "the box below and then clicking the \"Choose\" arrow between the two boxes." msgstr "" -"Tämä on lista saatavilla olevista %s. Valitse alla olevasta laatikosta " +"Tämä on lista saatavillaolevista %s. Valitse allaolevasta laatikosta " "haluamasi ja siirrä ne valittuihin klikkamalla \"Valitse\"-nuolta " "laatikoiden välillä." diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.mo index c701c72..e24a337 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.po index 2234710..86d0b2b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/fr/LC_MESSAGES/django.po @@ -2,16 +2,16 @@ # # Translators: # Bruno Brouard , 2021 -# Claude Paroz , 2013-2021 +# Claude Paroz , 2013-2020 # Claude Paroz , 2011,2013 # Jannis Leidel , 2011 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-09 07:39+0000\n" -"Last-Translator: Claude Paroz \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-04 20:53+0000\n" +"Last-Translator: Bruno Brouard \n" "Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -93,7 +93,7 @@ msgid "Add another %(verbose_name)s" msgstr "Ajouter un objet %(verbose_name)s supplémentaire" msgid "Remove" -msgstr "Enlever" +msgstr "Supprimer" msgid "Addition" msgstr "Ajout" @@ -537,12 +537,6 @@ msgstr "Mot de passe ou nom d’utilisateur oublié ?" msgid "Toggle navigation" msgstr "Basculer la navigation" -msgid "Start typing to filter…" -msgstr "Écrivez ici pour filtrer…" - -msgid "Filter navigation items" -msgstr "Filtrer les éléments de navigation" - msgid "Date/time" msgstr "Date/heure" @@ -608,7 +602,7 @@ msgstr "Ajouter un autre objet %(model)s" msgid "Delete selected %(model)s" msgstr "Supprimer l’objet %(model)s sélectionné" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "Merci pour le temps que vous avez accordé à ce site aujourd’hui." msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.mo index 3807caa..d1e1690 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.po index 030f4b6..28cc9e9 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django.po @@ -1,15 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# GunChleoc, 2015-2017,2021 +# GunChleoc, 2015-2017 # GunChleoc, 2015 # GunChleoc, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-27 12:57+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-16 08:31+0000\n" "Last-Translator: GunChleoc\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" @@ -542,12 +542,6 @@ msgstr "" msgid "Toggle navigation" msgstr "Toglaich an t-seòladaireachd" -msgid "Start typing to filter…" -msgstr "Tòisich air sgrìobhadh airson criathradh…" - -msgid "Filter navigation items" -msgstr "Criathraich nithean na seòladaireachd" - msgid "Date/time" msgstr "Ceann-là ’s àm" @@ -615,7 +609,7 @@ msgstr "Cuir %(model)s eile ris" msgid "Delete selected %(model)s" msgstr "Sguab às a’ %(model)s a thagh thu" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "" "Mòran taing gun do chuir thu seachad deagh-àm air an làrach-lìn an-diugh." diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo index 661e42e..db338a4 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po index f8b6c1f..17b7a76 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/djangojs.po @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-15 10:43+0000\n" -"Last-Translator: GunChleoc\n" +"PO-Revision-Date: 2021-01-15 11:28+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" "MIME-Version: 1.0\n" @@ -205,51 +205,51 @@ msgstr "An Dùbhlachd" msgctxt "abbrev. month January" msgid "Jan" -msgstr "Faoi" +msgstr "" msgctxt "abbrev. month February" msgid "Feb" -msgstr "Gearr" +msgstr "" msgctxt "abbrev. month March" msgid "Mar" -msgstr "Màrt" +msgstr "" msgctxt "abbrev. month April" msgid "Apr" -msgstr "Gibl" +msgstr "" msgctxt "abbrev. month May" msgid "May" -msgstr "Cèit" +msgstr "" msgctxt "abbrev. month June" msgid "Jun" -msgstr "Ògmh" +msgstr "" msgctxt "abbrev. month July" msgid "Jul" -msgstr "Iuch" +msgstr "" msgctxt "abbrev. month August" msgid "Aug" -msgstr "Lùna" +msgstr "" msgctxt "abbrev. month September" msgid "Sep" -msgstr "Sult" +msgstr "" msgctxt "abbrev. month October" msgid "Oct" -msgstr "Dàmh" +msgstr "" msgctxt "abbrev. month November" msgid "Nov" -msgstr "Samh" +msgstr "" msgctxt "abbrev. month December" msgid "Dec" -msgstr "Dùbh" +msgstr "" msgctxt "one letter Sunday" msgid "S" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.mo index 99e403a..7cf4d84 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.po index aec9f28..47f1115 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/gl/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Denís Bermúdez Delgado , 2021 # fasouto , 2011-2012 # fonso , 2011,2013 # fasouto , 2017 @@ -13,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 07:21+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: fasouto \n" "Language-Team: Galician (http://www.transifex.com/django/django/language/" "gl/)\n" "MIME-Version: 1.0\n" @@ -24,10 +23,6 @@ msgstr "" "Language: gl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Borrar %(verbose_name_plural)s seleccionados." - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Borrado exitosamente %(count)d %(items)s" @@ -39,6 +34,10 @@ msgstr "Non foi posíbel eliminar %(name)s" msgid "Are you sure?" msgstr "¿Está seguro?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Borrar %(verbose_name_plural)s seleccionados." + msgid "Administration" msgstr "Administración" @@ -75,12 +74,6 @@ msgstr "Sen data" msgid "Has date" msgstr "Ten data" -msgid "Empty" -msgstr "Baleiro" - -msgid "Not empty" -msgstr "Non baleiro" - #, python-format msgid "" "Please enter the correct %(username)s and password for a staff account. Note " @@ -99,15 +92,6 @@ msgstr "Engadir outro %(verbose_name)s" msgid "Remove" msgstr "Retirar" -msgid "Addition" -msgstr "" - -msgid "Change" -msgstr "Modificar" - -msgid "Deletion" -msgstr "" - msgid "action time" msgstr "hora da acción" @@ -115,13 +99,13 @@ msgid "user" msgstr "usuario" msgid "content type" -msgstr "tipo de contido" +msgstr "" msgid "object id" msgstr "id do obxecto" #. Translators: 'repr' means representation -#. (https://docs.python.org/library/functions.html#repr) +#. (https://docs.python.org/3/library/functions.html#repr) msgid "object repr" msgstr "repr do obxecto" @@ -138,22 +122,22 @@ msgid "log entries" msgstr "entradas de rexistro" #, python-format -msgid "Added “%(object)s”." -msgstr "Engadido %(object)s" +msgid "Added \"%(object)s\"." +msgstr "Engadido \"%(object)s\"." #, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "" +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Modificados \"%(object)s\" - %(changes)s" #, python-format -msgid "Deleted “%(object)s.”" -msgstr "" +msgid "Deleted \"%(object)s.\"" +msgstr "Borrados \"%(object)s.\"" msgid "LogEntry Object" msgstr "Obxecto LogEntry" #, python-brace-format -msgid "Added {name} “{object}”." +msgid "Added {name} \"{object}\"." msgstr "" msgid "Added." @@ -163,7 +147,7 @@ msgid "and" msgstr "e" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." +msgid "Changed {fields} for {name} \"{object}\"." msgstr "" #, python-brace-format @@ -171,7 +155,7 @@ msgid "Changed {fields}." msgstr "" #, python-brace-format -msgid "Deleted {name} “{object}”." +msgid "Deleted {name} \"{object}\"." msgstr "" msgid "No fields changed." @@ -180,38 +164,38 @@ msgstr "Non se modificou ningún campo." msgid "None" msgstr "Ningún" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." -msgstr "" - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "" - -msgid "You may edit it again below." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." +"The {name} \"{obj}\" was added successfully. You may edit it again below." msgstr "" #, python-brace-format msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." msgstr "" msgid "" @@ -225,11 +209,11 @@ msgid "No action selected." msgstr "Non se elixiu ningunha acción." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "" +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "Eliminouse correctamente o/a %(name)s \"%(obj)s\"." #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" msgstr "" #, python-format @@ -240,10 +224,6 @@ msgstr "Engadir %s" msgid "Change %s" msgstr "Modificar %s" -#, python-format -msgid "View %s" -msgstr "" - msgid "Database error" msgstr "Erro da base de datos" @@ -298,8 +278,8 @@ msgstr "administración de %(app)s " msgid "Page not found" msgstr "Páxina non atopada" -msgid "We’re sorry, but the requested page could not be found." -msgstr "" +msgid "We're sorry, but the requested page could not be found." +msgstr "Sentímolo, pero non se atopou a páxina solicitada." msgid "Home" msgstr "Inicio" @@ -314,9 +294,11 @@ msgid "Server Error (500)" msgstr "Erro no servidor (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" +"Ocorreu un erro. Os administradores do sitio foron informados por email e " +"debería ser arranxado pronto. Grazas pola súa paciencia." msgid "Run the selected action" msgstr "Executar a acción seleccionada" @@ -334,23 +316,12 @@ msgstr "Seleccionar todos os %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Limpar selección" -#, python-format -msgid "Models in the %(name)s application" -msgstr "Modelos na aplicación %(name)s" - -msgid "Add" -msgstr "Engadir" - -msgid "View" -msgstr "" - -msgid "You don’t have permission to view or edit anything." -msgstr "" - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" +"Primeiro insira un nome de usuario e un contrasinal. Despois poderá editar " +"máis opcións de usuario." msgid "Enter a username and password." msgstr "Introduza un nome de usuario e contrasinal." @@ -359,7 +330,7 @@ msgid "Change password" msgstr "Cambiar contrasinal" msgid "Please correct the error below." -msgstr "" +msgstr "Corrixa os erros de embaixo." msgid "Please correct the errors below." msgstr "Por favor, corrixa os erros de embaixo" @@ -394,9 +365,6 @@ msgstr "Ver no sitio" msgid "Filter" msgstr "Filtro" -msgid "Clear all filters" -msgstr "" - msgid "Remove from sorting" msgstr "Eliminar da clasificación" @@ -439,8 +407,8 @@ msgstr "" msgid "Objects" msgstr "Obxectos" -msgid "Yes, I’m sure" -msgstr "" +msgid "Yes, I'm sure" +msgstr "Si, estou seguro" msgid "No, take me back" msgstr "" @@ -475,6 +443,9 @@ msgstr "" "Serán eliminados todos os seguintes obxectos e elementos relacionados on " "eles:" +msgid "Change" +msgstr "Modificar" + msgid "Delete?" msgstr "¿Eliminar?" @@ -485,6 +456,16 @@ msgstr " Por %(filter_title)s " msgid "Summary" msgstr "" +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modelos na aplicación %(name)s" + +msgid "Add" +msgstr "Engadir" + +msgid "You don't have permission to edit anything." +msgstr "Non ten permiso para editar nada." + msgid "Recent actions" msgstr "Accións recentes" @@ -498,10 +479,13 @@ msgid "Unknown content" msgstr "Contido descoñecido" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" +"Hai un problema coa súa instalación de base de datos. Asegúrese de que se " +"creasen as táboas axeitadas na base de datos, e de que o usuario apropiado " +"teña permisos para lela." #, python-format msgid "" @@ -512,15 +496,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "¿Olvidou o usuario ou contrasinal?" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "Data/hora" @@ -531,9 +506,11 @@ msgid "Action" msgstr "Acción" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" +"Este obxecto non ten histórico de cambios. Posibelmente non se creou usando " +"este sitio de administración." msgid "Show all" msgstr "Amosar todo" @@ -541,7 +518,19 @@ msgstr "Amosar todo" msgid "Save" msgstr "Gardar" -msgid "Popup closing…" +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "Engadir outro %(model)s" + +#, python-format +msgid "Delete selected %(model)s" msgstr "" msgid "Search" @@ -566,26 +555,8 @@ msgstr "Gardar e engadir outro" msgid "Save and continue editing" msgstr "Gardar e seguir modificando" -msgid "Save and view" -msgstr "" - -msgid "Close" -msgstr "" - -#, python-format -msgid "Change selected %(model)s" -msgstr "" - -#, python-format -msgid "Add another %(model)s" -msgstr "Engadir outro %(model)s" - -#, python-format -msgid "Delete selected %(model)s" -msgstr "" - -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Grazas polo tempo que dedicou ao sitio web." msgid "Log in again" msgstr "Entrar de novo" @@ -597,9 +568,11 @@ msgid "Your password was changed." msgstr "Cambiouse o seu contrasinal." msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" +"Por razóns de seguridade, introduza o contrasinal actual. Despois introduza " +"dúas veces o contrasinal para verificarmos que o escribiu correctamente." msgid "Change my password" msgstr "Cambiar o contrasinal" @@ -636,12 +609,12 @@ msgstr "" "usada. Por favor pida un novo reseteo da contrasinal." msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" @@ -656,8 +629,8 @@ msgstr "" msgid "Please go to the following page and choose a new password:" msgstr "Por favor vaia á seguinte páxina e elixa una nova contrasinal:" -msgid "Your username, in case you’ve forgotten:" -msgstr "" +msgid "Your username, in case you've forgotten:" +msgstr "No caso de que o esquecese, o seu nome de usuario é:" msgid "Thanks for using our site!" msgstr "Grazas por usar o noso sitio web!" @@ -667,9 +640,11 @@ msgid "The %(site_name)s team" msgstr "O equipo de %(site_name)s" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" +"Esqueceu o contrasinal? Insira o seu enderezo de email embaixo e " +"enviarémoslle as instrucións para configurar un novo." msgid "Email address:" msgstr "Enderezo de correo electrónico:" @@ -688,10 +663,6 @@ msgstr "Seleccione un/unha %s" msgid "Select %s to change" msgstr "Seleccione %s que modificar" -#, python-format -msgid "Select %s to view" -msgstr "" - msgid "Date:" msgstr "Data:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.mo index 74a428d..49935c8 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.po index ac68336..f7f8889 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django.po @@ -4,15 +4,14 @@ # 534b44a19bf18d20b71ecc4eb77c572f_db336e9 , 2011 # Jannis Leidel , 2011 # Meir Kriheli , 2011-2015,2017,2019-2020 -# Menachem G., 2021 -# Yaron Shahrabani , 2020-2021 +# Yaron Shahrabani , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-02 07:48+0000\n" -"Last-Translator: Menachem G.\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-11-12 12:52+0000\n" +"Last-Translator: Yaron Shahrabani \n" "Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -34,13 +33,13 @@ msgid "Cannot delete %(name)s" msgstr "לא ניתן למחוק %(name)s" msgid "Are you sure?" -msgstr "להמשיך?" +msgstr "האם את/ה בטוח/ה ?" msgid "Administration" msgstr "ניהול" msgid "All" -msgstr "הכול" +msgstr "הכל" msgid "Yes" msgstr "כן" @@ -144,7 +143,7 @@ msgstr "" #, python-format msgid "Deleted “%(object)s.”" -msgstr "„%(object)s” נמחקו." +msgstr "" msgid "LogEntry Object" msgstr "אובייקט LogEntry" @@ -518,12 +517,6 @@ msgstr "שכחת את שם המשתמש והסיסמה שלך ?" msgid "Toggle navigation" msgstr "" -msgid "Start typing to filter…" -msgstr "התחל להקליד כדי לסנן..." - -msgid "Filter navigation items" -msgstr "סנן פריטי ניווט" - msgid "Date/time" msgstr "תאריך/שעה" @@ -589,8 +582,8 @@ msgstr "הוספת %(model)s נוסף." msgid "Delete selected %(model)s" msgstr "מחיקת %(model)s הנבחר." -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "תודה על בילוי זמן איכות עם האתר." msgid "Log in again" msgstr "התחבר/י שוב" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo index 852d04a..56ec238 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po index 73788d5..3d94448 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/djangojs.po @@ -4,14 +4,13 @@ # 534b44a19bf18d20b71ecc4eb77c572f_db336e9 , 2012 # Jannis Leidel , 2011 # Meir Kriheli , 2011-2012,2014-2015,2017,2020 -# Yaron Shahrabani , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-05-17 12:28+0000\n" -"Last-Translator: Yaron Shahrabani \n" +"POT-Creation-Date: 2020-05-11 20:56+0200\n" +"PO-Revision-Date: 2020-08-01 18:00+0000\n" +"Last-Translator: Meir Kriheli \n" "Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -189,54 +188,6 @@ msgstr "נובמבר" msgid "December" msgstr "דצמבר" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "ינו׳" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "פבר׳" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "מרץ" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "אפר׳" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "מאי" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "יונ׳" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "יול׳" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "אוג׳" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "ספט׳" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "אוק׳" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "נוב׳" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "דצמ׳" - msgctxt "one letter Sunday" msgid "S" msgstr "ר" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo index 3569e8c..3cb66b6 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.po index efbbd58..31c4a76 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django.po @@ -1,13 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Michael Wolf , 2016-2021 +# Michael Wolf , 2016-2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-28 18:16+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-21 12:57+0000\n" "Last-Translator: Michael Wolf \n" "Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" "language/hsb/)\n" @@ -18,10 +18,6 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" "%100==4 ? 2 : 3);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Wubrane %(verbose_name_plural)s zhašeć" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "%(count)d %(items)s je so wuspěšnje zhašało." @@ -33,6 +29,10 @@ msgstr "%(name)s njeda so zhašeć." msgid "Are you sure?" msgstr "Sće wěsty?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Wubrane %(verbose_name_plural)s zhašeć" + msgid "Administration" msgstr "Administracija" @@ -523,12 +523,6 @@ msgstr "Sće swoje hesło abo wužiwarske mjeno zabył?" msgid "Toggle navigation" msgstr "Nawigaciju přepinać" -msgid "Start typing to filter…" -msgstr "Pisajće, zo byšće filtrował …" - -msgid "Filter navigation items" -msgstr "Nawigaciske zapiski fitrować" - msgid "Date/time" msgstr "Datum/čas" @@ -596,10 +590,8 @@ msgstr "Druhi %(model)s přidać" msgid "Delete selected %(model)s" msgstr "Wubrane %(model)s zhašeć" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" -"Wulki dźak, zo sće sej čas brał, zo byšće kwalitu websydła dźensa " -"přepruwował." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Wulki dźak, zo sće dźensa rjane chwile z websydłom přebywali." msgid "Log in again" msgstr "Znowa přizjewić" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.mo index 8f97aeb..7d56210 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.po index 244df77..3fe3a15 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/django.po @@ -2,21 +2,21 @@ # # Translators: # Claude Paroz , 2014 -# Fery Setiawan , 2015-2019,2021 +# Fery Setiawan , 2015-2019 # Jannis Leidel , 2011 # M Asep Indrayana , 2015 # oon arfiandwi , 2016,2020 # rodin , 2011-2013 # rodin , 2013-2017 -# sag᠎e , 2019 +# sage , 2019 # Sutrisno Efendi , 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-06 09:12+0000\n" -"Last-Translator: Fery Setiawan \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-24 09:53+0000\n" +"Last-Translator: oon arfiandwi \n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" "MIME-Version: 1.0\n" @@ -527,12 +527,6 @@ msgstr "Lupa nama pengguna atau sandi?" msgid "Toggle navigation" msgstr "Alihkan navigasi" -msgid "Start typing to filter…" -msgstr "Mulai mengetik untuk menyaring..." - -msgid "Filter navigation items" -msgstr "Navigasi pencarian barang" - msgid "Date/time" msgstr "Tanggal/waktu" @@ -597,10 +591,8 @@ msgstr "Tambahkan %(model)s yang lain" msgid "Delete selected %(model)s" msgstr "Hapus %(model)s yang dipilih" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" -"Terima kasih untuk meluangkan waktu berkualitas dengan jaringan situs hari " -"ini." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Terima kasih telah menggunakan situs ini hari ini." msgid "Log in again" msgstr "Masuk kembali" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo index a6cfffc..9aa68a7 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po index d59dcd4..125897b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/id/LC_MESSAGES/djangojs.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Fery Setiawan , 2015-2016,2021 +# Fery Setiawan , 2015-2016 # Jannis Leidel , 2011 # oon arfiandwi , 2020 # rodin , 2011-2012 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-09-14 00:31+0000\n" -"Last-Translator: Fery Setiawan \n" +"PO-Revision-Date: 2021-01-15 11:28+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" "MIME-Version: 1.0\n" @@ -186,51 +186,51 @@ msgstr "Desember" msgctxt "abbrev. month January" msgid "Jan" -msgstr "Jan" +msgstr "" msgctxt "abbrev. month February" msgid "Feb" -msgstr "Feb" +msgstr "" msgctxt "abbrev. month March" msgid "Mar" -msgstr "Mar" +msgstr "" msgctxt "abbrev. month April" msgid "Apr" -msgstr "Apr" +msgstr "" msgctxt "abbrev. month May" msgid "May" -msgstr "Mei" +msgstr "" msgctxt "abbrev. month June" msgid "Jun" -msgstr "Jun" +msgstr "" msgctxt "abbrev. month July" msgid "Jul" -msgstr "Jul" +msgstr "" msgctxt "abbrev. month August" msgid "Aug" -msgstr "Agu" +msgstr "" msgctxt "abbrev. month September" msgid "Sep" -msgstr "Sep" +msgstr "" msgctxt "abbrev. month October" msgid "Oct" -msgstr "Okt" +msgstr "" msgctxt "abbrev. month November" msgid "Nov" -msgstr "Nov" +msgstr "" msgctxt "abbrev. month December" msgid "Dec" -msgstr "Des" +msgstr "" msgctxt "one letter Sunday" msgid "S" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo index 29c2bc1..5b06183 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po index 5ddb17c..480c550 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/is/LC_MESSAGES/djangojs.po @@ -5,13 +5,13 @@ # Hafsteinn Einarsson , 2011-2012 # Jannis Leidel , 2011 # Matt R, 2018 -# Thordur Sigurdsson , 2016-2017,2020-2021 +# Thordur Sigurdsson , 2016-2017,2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-04-06 17:37+0000\n" +"POT-Creation-Date: 2020-05-11 20:56+0200\n" +"PO-Revision-Date: 2020-07-07 22:53+0000\n" "Last-Translator: Thordur Sigurdsson \n" "Language-Team: Icelandic (http://www.transifex.com/django/django/language/" "is/)\n" @@ -184,54 +184,6 @@ msgstr "nóvember" msgid "December" msgstr "desember" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Jan" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mar" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Apr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Maí" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jún" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Júl" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Ágú" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Okt" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nóv" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Des" - msgctxt "one letter Sunday" msgid "S" msgstr "S" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.mo index 45e9039..099be53 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.po index e380766..98e90bc 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/it/LC_MESSAGES/django.po @@ -2,8 +2,7 @@ # # Translators: # 0d21a39e384d88c2313b89b5042c04cb, 2017 -# Carlo Miron , 2018-2019 -# Davide Targa , 2021 +# Carlo Miron , 2018-2019 # Denis Darii , 2011 # Flavio Curella , 2013 # Jannis Leidel , 2011 @@ -18,9 +17,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-12 11:54+0000\n" -"Last-Translator: Davide Targa \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 15:53+0000\n" +"Last-Translator: palmux \n" "Language-Team: Italian (http://www.transifex.com/django/django/language/" "it/)\n" "MIME-Version: 1.0\n" @@ -541,12 +540,6 @@ msgstr "Hai dimenticato la password o lo username?" msgid "Toggle navigation" msgstr "Abilita/disabilita navigazione" -msgid "Start typing to filter…" -msgstr "Inizia a scrivere per filtrare..." - -msgid "Filter navigation items" -msgstr "Filtra gli oggetti di navigazione" - msgid "Date/time" msgstr "Data/ora" @@ -612,8 +605,8 @@ msgstr "Aggiungi un altro %(model)s" msgid "Delete selected %(model)s" msgstr "Elimina la selezione %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Grazie per avere trascorso del tempo di qualità sul sito oggi." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Grazie per aver speso il tuo tempo prezioso su questo sito oggi." msgid "Log in again" msgstr "Accedi di nuovo" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.mo index b769e84..ceaede5 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.po index 5fbaba9..0775826 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django.po @@ -3,10 +3,10 @@ # Translators: # akiyoko , 2020 # Claude Paroz , 2016 -# Goto Hayato , 2019 +# GOTO Hayato , 2019 # Jannis Leidel , 2011 # Shinichi Katsumata , 2019 -# Shinya Okano , 2012-2018,2021 +# Shinya Okano , 2012-2018 # Takuro Onoue , 2020 # Takuya N , 2020 # Tetsuya Morimoto , 2011 @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-13 11:41+0000\n" -"Last-Translator: Shinya Okano \n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-08 04:30+0000\n" +"Last-Translator: Takuro Onoue \n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" "MIME-Version: 1.0\n" @@ -525,12 +525,6 @@ msgstr "パスワードまたはユーザー名を忘れましたか?" msgid "Toggle navigation" msgstr "ナビゲーションを切り替えます" -msgid "Start typing to filter…" -msgstr "絞り込みの入力..." - -msgid "Filter navigation items" -msgstr "ナビゲーション項目の絞り込み" - msgid "Date/time" msgstr "日付/時刻" @@ -595,7 +589,7 @@ msgstr "%(model)s の追加" msgid "Delete selected %(model)s" msgstr "選択された %(model)s を削除" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "ご利用ありがとうございました。" msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo index 700feaf..3cd4e36 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po index e3ce222..65a0872 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/djangojs.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Goto Hayato , 2021 # Jannis Leidel , 2011 # Shinya Okano , 2012,2014-2016 # Takuro Onoue , 2020 @@ -10,8 +9,8 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-02 13:55+0000\n" -"Last-Translator: Goto Hayato \n" +"PO-Revision-Date: 2021-01-15 11:28+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" "MIME-Version: 1.0\n" @@ -182,51 +181,51 @@ msgstr "12月" msgctxt "abbrev. month January" msgid "Jan" -msgstr "1月" +msgstr "" msgctxt "abbrev. month February" msgid "Feb" -msgstr "2月" +msgstr "" msgctxt "abbrev. month March" msgid "Mar" -msgstr "3月" +msgstr "" msgctxt "abbrev. month April" msgid "Apr" -msgstr "4月" +msgstr "" msgctxt "abbrev. month May" msgid "May" -msgstr "5月" +msgstr "" msgctxt "abbrev. month June" msgid "Jun" -msgstr "6月" +msgstr "" msgctxt "abbrev. month July" msgid "Jul" -msgstr "7月" +msgstr "" msgctxt "abbrev. month August" msgid "Aug" -msgstr "8月" +msgstr "" msgctxt "abbrev. month September" msgid "Sep" -msgstr "9月" +msgstr "" msgctxt "abbrev. month October" msgid "Oct" -msgstr "10月" +msgstr "" msgctxt "abbrev. month November" msgid "Nov" -msgstr "11月" +msgstr "" msgctxt "abbrev. month December" msgid "Dec" -msgstr "12月" +msgstr "" msgctxt "one letter Sunday" msgid "S" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/kn/LC_MESSAGES/django.po index 06e63dc..3ae96cf 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/kn/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" #, python-format msgid "Successfully deleted %(count)d %(items)s." diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.mo index 61fe9bb..bbfe573 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.po index e41f878..c968af1 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django.po @@ -3,7 +3,6 @@ # Translators: # Jiyoon, Ha , 2016 # DONGHO JEONG , 2020 -# 코딩 영, 2021 # Geonho Kim / Leo Kim , 2019 # Gihun Ham , 2018 # Hang Park , 2019 @@ -21,9 +20,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-04 03:40+0000\n" -"Last-Translator: 코딩 영\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-09-05 05:57+0000\n" +"Last-Translator: DONGHO JEONG \n" "Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,10 +30,6 @@ msgstr "" "Language: ko\n" "Plural-Forms: nplurals=1; plural=0;\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "선택된 %(verbose_name_plural)s 을/를 삭제합니다." - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "%(count)d개의 %(items)s 을/를 성공적으로 삭제하였습니다." @@ -46,6 +41,10 @@ msgstr "%(name)s를 삭제할 수 없습니다." msgid "Are you sure?" msgstr "확실합니까?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "선택된 %(verbose_name_plural)s 을/를 삭제합니다." + msgid "Administration" msgstr "관리" @@ -540,12 +539,6 @@ msgstr "아이디 또는 비밀번호를 분실하였습니까?" msgid "Toggle navigation" msgstr "토글 메뉴" -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "날짜/시간" @@ -610,7 +603,7 @@ msgstr "%(model)s 추가" msgid "Delete selected %(model)s" msgstr "선택된 %(model)s 제거" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "사이트를 이용해 주셔서 고맙습니다." msgid "Log in again" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo index d7ab7dd..9d8de15 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po index b7d12da..9bfafc8 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/djangojs.po @@ -8,14 +8,13 @@ # Jay Oh , 2020 # Le Tartuffe , 2014 # minsung kang, 2015 -# Yang Chan Woo , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-29 08:54+0000\n" -"Last-Translator: Yang Chan Woo \n" +"POT-Creation-Date: 2020-05-11 20:56+0200\n" +"PO-Revision-Date: 2020-07-04 14:16+0000\n" +"Last-Translator: Jay Oh \n" "Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -183,54 +182,6 @@ msgstr "11월" msgid "December" msgstr "12월" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "1월" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "2월" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "3월" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "4월" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "5월" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "6월" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "7월" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "8월" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "9월" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "10월" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "11월" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "12월" - msgctxt "one letter Sunday" msgid "S" msgstr "일" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.mo index 71d5e5b..35be3e9 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.po index 683480c..c26d68e 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ky/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # Translators: # Belek , 2016 # Chyngyz Monokbaev , 2016 -# Soyuzbek Orozbek uulu , 2020-2021 +# Soyuzbek Orozbek uulu , 2020 # Soyuzbek Orozbek uulu , 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-27 14:12+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-11-23 00:50+0000\n" "Last-Translator: Soyuzbek Orozbek uulu \n" "Language-Team: Kyrgyz (http://www.transifex.com/django/django/language/ky/)\n" "MIME-Version: 1.0\n" @@ -478,7 +478,7 @@ msgstr "Өчүрөлүбү?" #, python-format msgid " By %(filter_title)s " -msgstr "%(filter_title)s боюнча" +msgstr "%(filter_title)s карап" msgid "Summary" msgstr "Жалпысынан" @@ -517,12 +517,6 @@ msgstr "Колдонуучу атыңыз же сырсөздү унутуп к msgid "Toggle navigation" msgstr "Навигацияны алмаштыруу" -msgid "Start typing to filter…" -msgstr "чыпкалоо үчүн жазып башта" - -msgid "Filter navigation items" -msgstr "Навигация элементтерин чыпкалоо" - msgid "Date/time" msgstr "Күн/убакыт" @@ -585,8 +579,8 @@ msgstr "Башка %(model)s кошуу" msgid "Delete selected %(model)s" msgstr "Тандалган %(model)s обеттерин өчүрүү" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Сайтта бираз убакыт өткөргөн үчүн ыраазычылык." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Бүгүнкү баалуу убактыңызды Сайт үчүн бөлгөнүңүзгө рахмат." msgid "Log in again" msgstr "Кайрадан кирүү" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.mo index f2a081a..bc907fc 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.po index 73a1518..e865dd1 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/lv/LC_MESSAGES/django.po @@ -6,14 +6,14 @@ # NullIsNot0 , 2018 # Jannis Leidel , 2011 # Māris Nartišs , 2016 -# NullIsNot0 , 2019-2021 +# NullIsNot0 , 2019-2020 # peterisb , 2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-06 05:10+0000\n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-22 17:27+0000\n" "Last-Translator: NullIsNot0 \n" "Language-Team: Latvian (http://www.transifex.com/django/django/language/" "lv/)\n" @@ -24,10 +24,6 @@ msgstr "" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " "2);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Izdzēst izvēlēto %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Veiksmīgi izdzēsti %(count)d %(items)s." @@ -39,6 +35,10 @@ msgstr "Nevar izdzēst %(name)s" msgid "Are you sure?" msgstr "Vai esat pārliecināts?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Izdzēst izvēlēto %(verbose_name_plural)s" + msgid "Administration" msgstr "Administrācija" @@ -523,12 +523,6 @@ msgstr "Aizmirsi paroli vai lietotājvārdu?" msgid "Toggle navigation" msgstr "Pārslēgt navigāciju" -msgid "Start typing to filter…" -msgstr "Sāciet rakstīt, lai atlasītu…" - -msgid "Filter navigation items" -msgstr "Atlasīt navigācijas vienības" - msgid "Date/time" msgstr "Datums/laiks" @@ -595,8 +589,8 @@ msgstr "Pievienot citu %(model)s" msgid "Delete selected %(model)s" msgstr "Dzēst izvēlēto %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Paldies, ka šodien vietnei veltījāt kvalitatīvu laiku." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Paldies par pavadīto laiku mājas lapā." msgid "Log in again" msgstr "Pieslēgties vēlreiz" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.mo index 0acf0fe..dfbc8b1 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.po index 861cde4..2efa5d3 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Dimce Grozdanoski , 2021 # dekomote , 2015 # Jannis Leidel , 2011 # Vasil Vangelovski , 2016-2017,2019,2021 @@ -11,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 07:21+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-06-11 17:28+0000\n" +"Last-Translator: Vasil Vangelovski \n" "Language-Team: Macedonian (http://www.transifex.com/django/django/language/" "mk/)\n" "MIME-Version: 1.0\n" @@ -74,7 +73,7 @@ msgid "Has date" msgstr "Има датум" msgid "Empty" -msgstr "Празно" +msgstr "" msgid "Not empty" msgstr "" @@ -137,7 +136,7 @@ msgstr "ставки во записникот" #, python-format msgid "Added “%(object)s”." -msgstr "Додадено “%(object)s”." +msgstr "" #, python-format msgid "Changed “%(object)s” — %(changes)s" @@ -145,7 +144,7 @@ msgstr "" #, python-format msgid "Deleted “%(object)s.”" -msgstr "Избришано “%(object)s.”" +msgstr "" msgid "LogEntry Object" msgstr "Запис во дневник" @@ -514,12 +513,6 @@ msgstr "Ја заборавивте вашата лозинка или кори msgid "Toggle navigation" msgstr "" -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "Датум/час" @@ -583,8 +576,9 @@ msgstr "Додади уште %(model)s" msgid "Delete selected %(model)s" msgstr "Избриши ги избраните %(model)s" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "" +"Ви благодариме што денеска поминавте квалитетно време со интернет страницава." msgid "Log in again" msgstr "Најавете се повторно" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo index c87ddf6..5b11c78 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po index bd72010..04e9dcb 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/djangojs.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-01-15 11:28+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Vasil Vangelovski \n" "Language-Team: Macedonian (http://www.transifex.com/django/django/language/" "mk/)\n" "MIME-Version: 1.0\n" @@ -84,31 +84,21 @@ msgstr "" "незачувани промени ќе бидат изгубени." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" +"Избравте акција, но сеуште ги немате зачувано вашите промени на поединечни " +"полиња. Кликнете ОК за да ги зачувате. Ќе треба повторно да ја извршите " +"акцијата." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" - -msgid "Now" -msgstr "Сега" - -msgid "Midnight" -msgstr "Полноќ" - -msgid "6 a.m." -msgstr "6 наутро" - -msgid "Noon" -msgstr "Пладне" - -msgid "6 p.m." -msgstr "6 попладне" +"Избравте акција и немате направено промени на поединечни полиња. Веројатно " +"го барате копчето Оди наместо Зачувај." #, javascript-format msgid "Note: You are %s hour ahead of server time." @@ -122,12 +112,27 @@ msgid_plural "Note: You are %s hours behind server time." msgstr[0] "Забелешка: Вие сте %s час поназад од времето на серверот." msgstr[1] "Забелешка: Вие сте %s часа поназад од времето на серверот." +msgid "Now" +msgstr "Сега" + msgid "Choose a Time" msgstr "Одбери време" msgid "Choose a time" msgstr "Одбери време" +msgid "Midnight" +msgstr "Полноќ" + +msgid "6 a.m." +msgstr "6 наутро" + +msgid "Noon" +msgstr "Пладне" + +msgid "6 p.m." +msgstr "6 попладне" + msgid "Cancel" msgstr "Откажи" @@ -179,54 +184,6 @@ msgstr "Ноември" msgid "December" msgstr "Декември" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "" - msgctxt "one letter Sunday" msgid "S" msgstr "Н" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.mo index 82cb0bb..57a9d75 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.po index 48c2e13..8137103 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/mn/LC_MESSAGES/django.po @@ -5,14 +5,14 @@ # Jannis Leidel , 2011 # jargalan , 2011 # Zorig, 2016 -# Анхбаяр Анхаа , 2013-2016,2018-2019,2021 +# Анхбаяр Анхаа , 2013-2016,2018-2019 # Баясгалан Цэвлээ , 2011,2017 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-16 17:18+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-02-13 09:17+0000\n" "Last-Translator: Анхбаяр Анхаа \n" "Language-Team: Mongolian (http://www.transifex.com/django/django/language/" "mn/)\n" @@ -22,10 +22,6 @@ msgstr "" "Language: mn\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Сонгосон %(verbose_name_plural)s-ийг устга" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "%(items)s ээс %(count)d-ийг амжилттай устгалаа." @@ -37,6 +33,10 @@ msgstr "%(name)s устгаж чадахгүй." msgid "Are you sure?" msgstr "Итгэлтэй байна уу?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Сонгосон %(verbose_name_plural)s-ийг устга" + msgid "Administration" msgstr "Удирдлага" @@ -73,12 +73,6 @@ msgstr "Огноогүй" msgid "Has date" msgstr "Огноотой" -msgid "Empty" -msgstr "" - -msgid "Not empty" -msgstr "" - #, python-format msgid "" "Please enter the correct %(username)s and password for a staff account. Note " @@ -136,23 +130,23 @@ msgid "log entries" msgstr "лог өгөгдөлүүд" #, python-format -msgid "Added “%(object)s”." -msgstr "Нэмэгдсэн \"%(object)s\"." +msgid "Added \"%(object)s\"." +msgstr "\"%(object)s\" нэмсэн." #, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "Өөрчлөгдсөн \"%(object)s\"— %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "\"%(object)s\"-ийг %(changes)s өөрчилсөн." #, python-format -msgid "Deleted “%(object)s.”" -msgstr "Устгагдсан \"%(object)s\"." +msgid "Deleted \"%(object)s.\"" +msgstr "\"%(object)s\" устгасан." msgid "LogEntry Object" msgstr "Лог бүртгэлийн обект" #, python-brace-format -msgid "Added {name} “{object}”." -msgstr "" +msgid "Added {name} \"{object}\"." +msgstr "Нэмэгдсэн {name} \"{object}\"." msgid "Added." msgstr "Нэмэгдсэн." @@ -161,16 +155,16 @@ msgid "and" msgstr "ба" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "" +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "{name} \"{object}\"-ны {fields} өөрчилөгдсөн." #, python-brace-format msgid "Changed {fields}." msgstr "Өөрчлөгдсөн {fields}." #, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "" +msgid "Deleted {name} \"{object}\"." +msgstr "Устгасан {name} \"{object}\"." msgid "No fields changed." msgstr "Өөрчилсөн талбар алга байна." @@ -178,39 +172,48 @@ msgstr "Өөрчилсөн талбар алга байна." msgid "None" msgstr "Хоосон" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" +"Олон утга сонгохын тулд \"Control\", эсвэл Mac дээр \"Command\" товчыг дарж " +"байгаад сонгоно." #, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "" +msgid "The {name} \"{obj}\" was added successfully." +msgstr " {name} \"{obj}\" амжилттай нэмэгдлээ." msgid "You may edit it again below." msgstr "Та дараахийг дахин засах боломжтой" #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" +"{name} \"{obj}\" амжилттай нэмэгдлээ. Доорх хэсгээс {name} өөрийн нэмэх " +"боломжтой." #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "{name} \"{obj}\" амжилттай өөрчилөгдлөө. Та дахин засах боломжтой." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "{name} \"{obj}\" амжилттай нэмэгдлээ. Та дахин засах боломжтой." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." msgstr "" +"{name} \"{obj}\" амжилттай өөрчилөгдлөө. Доорх хэсгээс {name} өөрийн нэмэх " +"боломжтой." + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "{name} \"{obj}\" амжилттай засагдлаа." msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -222,12 +225,13 @@ msgid "No action selected." msgstr "Үйлдэл сонгоогүй." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "" +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr " %(name)s \"%(obj)s\" амжилттай устгагдлаа." #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" msgstr "" +"\"%(key)s\" дугаартай %(name)s байхгүй байна. Устсан байсан юм болов уу?" #, python-format msgid "Add %s" @@ -297,8 +301,8 @@ msgstr "%(app)s удирдлага" msgid "Page not found" msgstr "Хуудас олдсонгүй." -msgid "We’re sorry, but the requested page could not be found." -msgstr "" +msgid "We're sorry, but the requested page could not be found." +msgstr "Уучлаарай, хандахыг хүссэн хуудас тань олдсонгүй." msgid "Home" msgstr "Нүүр" @@ -313,9 +317,11 @@ msgid "Server Error (500)" msgstr "Серверийн алдаа (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" +"Алдаа гарсан байна. Энэ алдааг сайт хариуцагчид имэйлээр мэдэгдсэн бөгөөд " +"тэд нэн даруй засах хэрэгтэй. Хүлээцтэй хандсанд баярлалаа." msgid "Run the selected action" msgstr "Сонгосон үйлдэлийг ажилуулах" @@ -333,23 +339,12 @@ msgstr "Бүгдийг сонгох %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Сонгосонг цэвэрлэх" -#, python-format -msgid "Models in the %(name)s application" -msgstr "%(name)s хэрэглүүр дэх моделууд." - -msgid "Add" -msgstr "Нэмэх" - -msgid "View" -msgstr "Харах" - -msgid "You don’t have permission to view or edit anything." -msgstr "" - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" +"Эхлээд хэрэглэгчийн нэр нууц үгээ оруулна уу. Ингэснээр та хэрэглэгчийн " +"сонголтыг нэмж засварлах боломжтой болно. " msgid "Enter a username and password." msgstr "Хэрэглэгчийн нэр ба нууц үгээ оруулна." @@ -392,9 +387,6 @@ msgstr "Сайтаас харах" msgid "Filter" msgstr "Шүүлтүүр" -msgid "Clear all filters" -msgstr "" - msgid "Remove from sorting" msgstr "Эрэмблэлтээс хасах" @@ -436,8 +428,8 @@ msgstr "" msgid "Objects" msgstr "Бичлэгүүд" -msgid "Yes, I’m sure" -msgstr "" +msgid "Yes, I'm sure" +msgstr "Тийм, итгэлтэй байна." msgid "No, take me back" msgstr "Үгүй, намайг буцаа" @@ -470,6 +462,9 @@ msgstr "" "Та %(objects_name)s ийг устгах гэж байна итгэлтэй байна? Дараах обектууд " "болон холбоотой зүйлс хамт устагдах болно:" +msgid "View" +msgstr "Харах" + msgid "Delete?" msgstr "Устгах уу?" @@ -480,6 +475,16 @@ msgstr " %(filter_title)s -ээр" msgid "Summary" msgstr "Нийт" +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s хэрэглүүр дэх моделууд." + +msgid "Add" +msgstr "Нэмэх" + +msgid "You don't have permission to view or edit anything." +msgstr "Танд харах болон засах эрх алга." + msgid "Recent actions" msgstr "Сүүлд хийсэн үйлдлүүд" @@ -493,10 +498,13 @@ msgid "Unknown content" msgstr "Тодорхойгүй агуулга" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" +"Өгөгдлийн сангийн ямар нэг зүйл буруу суугдсан байна. Өгөгдлийн сангийн " +"зохих хүснэгт үүсгэгдсэн эсэх, өгөгдлийн санг зохих хэрэглэгч унших " +"боломжтой байгаа эсэхийг шалгаарай." #, python-format msgid "" @@ -509,15 +517,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "Таны мартсан нууц үг эсвэл нэрвтэр нэр?" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "Огноо/цаг" @@ -528,9 +527,11 @@ msgid "Action" msgstr "Үйлдэл" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" +"Уг объектэд өөрчлөлтийн түүх байхгүй байна. Магадгүй үүнийг уг удирдлагын " +"сайтаар дамжуулан нэмээгүй байх." msgid "Show all" msgstr "Бүгдийг харуулах" @@ -581,8 +582,8 @@ msgstr "Өөр %(model)s нэмэх" msgid "Delete selected %(model)s" msgstr "Сонгосон %(model)s устгах" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Манай вэб сайтыг ашигласанд баярлалаа." msgid "Log in again" msgstr "Ахин нэвтрэх " @@ -594,9 +595,11 @@ msgid "Your password was changed." msgstr "Нууц үг тань өөрчлөгдлөө." msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" +"Аюулгүй байдлын үүднээс хуучин нууц үгээ оруулаад шинэ нууц үгээ хоёр удаа " +"хийнэ үү. Ингэснээр нууц үгээ зөв бичиж байгаа эсэхийг тань шалгах юм." msgid "Change my password" msgstr "Нууц үгээ солих" @@ -631,14 +634,18 @@ msgstr "" "байж болзошгүй. Шинэ нууц үг авахаар хүсэлт гаргана уу. " msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" +"Таны оруулсан имайл хаяг бүртгэлтэй бол таны имайл хаягруу нууц үг " +"тохируулах зааварыг удахгүй очих болно. Та удахгүй имайл хүлээж авах болно. " msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" +"Хэрвээ та имайл хүлээж аваагүй бол оруулсан имайл хаягаараа бүртгүүлсэн " +"эсхээ шалгаад мөн имайлийнхаа Spam фолдер ийг шалгана уу." #, python-format msgid "" @@ -651,8 +658,8 @@ msgstr "" msgid "Please go to the following page and choose a new password:" msgstr "Дараах хуудас руу орон шинэ нууц үг сонгоно уу:" -msgid "Your username, in case you’ve forgotten:" -msgstr "" +msgid "Your username, in case you've forgotten:" +msgstr "Хэрэглэгчийн нэрээ мартсан бол :" msgid "Thanks for using our site!" msgstr "Манай сайтыг хэрэглэсэнд баярлалаа!" @@ -662,9 +669,11 @@ msgid "The %(site_name)s team" msgstr "%(site_name)s баг" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" +"Нууц үгээ мартсан уу? Доорх хэсэгт имайл хаягаа оруулвал бид хаягаар тань " +"нууц үг сэргэх зааварчилгаа явуулах болно." msgid "Email address:" msgstr "Имэйл хаяг:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index f558c1b..0000000 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index e69439e..0000000 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,721 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -# Mariusz Felisiak , 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-06 07:41+0000\n" -"Last-Translator: Mariusz Felisiak \n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Padam pilihan %(verbose_name_plural)s" - -#, python-format -msgid "Successfully deleted %(count)d %(items)s." -msgstr "%(count)d %(items)s berjaya dipadamkan" - -#, python-format -msgid "Cannot delete %(name)s" -msgstr "%(name)s tidak boleh dipadamkan" - -msgid "Are you sure?" -msgstr "Adakah anda pasti?" - -msgid "Administration" -msgstr "Pentadbiran" - -msgid "All" -msgstr "Semua" - -msgid "Yes" -msgstr "Ya" - -msgid "No" -msgstr "Tidak" - -msgid "Unknown" -msgstr "Tidak diketahui" - -msgid "Any date" -msgstr "Sebarang tarikh" - -msgid "Today" -msgstr "Hari ini" - -msgid "Past 7 days" -msgstr "7 hari lalu" - -msgid "This month" -msgstr "Bulan ini" - -msgid "This year" -msgstr "Tahun ini" - -msgid "No date" -msgstr "Tiada tarikh" - -msgid "Has date" -msgstr "Mempunyai tarikh" - -msgid "Empty" -msgstr "Kosong" - -msgid "Not empty" -msgstr "Tidak kosong" - -#, python-format -msgid "" -"Please enter the correct %(username)s and password for a staff account. Note " -"that both fields may be case-sensitive." -msgstr "" -"Sila masukkan %(username)s dan kata-laluan bagi akaun staf. Kedua-dua medan " -"berkemungkinan kes-sensitif." - -msgid "Action:" -msgstr "Tindakan" - -#, python-format -msgid "Add another %(verbose_name)s" -msgstr "Tambah %(verbose_name)s" - -msgid "Remove" -msgstr "Buang" - -msgid "Addition" -msgstr "Tambahan" - -msgid "Change" -msgstr "Tukar" - -msgid "Deletion" -msgstr "Pemadaman" - -msgid "action time" -msgstr "masa tindakan" - -msgid "user" -msgstr "pengguna" - -msgid "content type" -msgstr "jenis kandungan" - -msgid "object id" -msgstr "id objek" - -#. Translators: 'repr' means representation -#. (https://docs.python.org/library/functions.html#repr) -msgid "object repr" -msgstr "repr objek" - -msgid "action flag" -msgstr "bendera tindakan" - -msgid "change message" -msgstr "tukar mesej" - -msgid "log entry" -msgstr "entri log" - -msgid "log entries" -msgstr "entri log" - -#, python-format -msgid "Added “%(object)s”." -msgstr "\"%(object)s\" ditambah" - -#, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "\"%(object)s\" ditukar - %(changes)s" - -#, python-format -msgid "Deleted “%(object)s.”" -msgstr "\"%(object)s\" dipadam." - -msgid "LogEntry Object" -msgstr "Objek EntriLog" - -#, python-brace-format -msgid "Added {name} “{object}”." -msgstr "{name} “{object}” ditambah." - -msgid "Added." -msgstr "Ditambah." - -msgid "and" -msgstr "dan" - -#, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "“{object}” {name} untuk {fields} telah ditukar." - -#, python-brace-format -msgid "Changed {fields}." -msgstr "{fields} telah ditukar." - -#, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "“{object}” {name} telah dipadamkan" - -msgid "No fields changed." -msgstr "Tiada medan diubah." - -msgid "None" -msgstr "Tiada" - -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." -msgstr "" -"Tekan \"Control\", atau \"Command pada Mac untuk memilih lebih daripada satu." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "{name} \"{obj}\" telah berjaya ditambah." - -msgid "You may edit it again below." -msgstr "Anda boleh edit semula dibawah." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." -msgstr "" -"{name} \"{obj}\" telah berjaya ditambah. Anda boleh menambah {name} lain " -"dibawah." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "{name} \"{obj}\" berjaya diubah. Anda boleh edit semula dibawah." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "{name} \"{obj}\" berjaya ditambah. Anda boleh edit semula dibawah." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " -"below." -msgstr "{name} \"{obj}\" berjaya diubah. Anda boleh tambah {name} lain dibawah" - -#, python-brace-format -msgid "The {name} “{obj}” was changed successfully." -msgstr "{name} \"{obj}\" berjaya diubah." - -msgid "" -"Items must be selected in order to perform actions on them. No items have " -"been changed." -msgstr "" -"Item-item perlu dipilih mengikut turutan untuk tindakan lanjut. Tiada item-" -"item yang diubah." - -msgid "No action selected." -msgstr "Tiada tindakan dipilih." - -#, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "%(name)s \"%(obj)s\" berjaya dipadam." - -#, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "" -"%(name)s dengan ID \"%(key)s\" tidak wujud. Mungkin ia telah dipadamkan?" - -#, python-format -msgid "Add %s" -msgstr "Tambah %s" - -#, python-format -msgid "Change %s" -msgstr "Tukar %s" - -#, python-format -msgid "View %s" -msgstr "Lihat %s" - -msgid "Database error" -msgstr "Masalah pangkalan data" - -#, python-format -msgid "%(count)s %(name)s was changed successfully." -msgid_plural "%(count)s %(name)s were changed successfully." -msgstr[0] "%(count)s %(name)s berjaya ditukar." - -#, python-format -msgid "%(total_count)s selected" -msgid_plural "All %(total_count)s selected" -msgstr[0] "Kesemua %(total_count)s dipilih" - -#, python-format -msgid "0 of %(cnt)s selected" -msgstr "0 daripada %(cnt)s dipilih" - -#, python-format -msgid "Change history: %s" -msgstr "Sejarah penukaran: %s" - -#. Translators: Model verbose name and instance representation, -#. suitable to be an item in a list. -#, python-format -msgid "%(class_name)s %(instance)s" -msgstr "%(class_name)s %(instance)s" - -#, python-format -msgid "" -"Deleting %(class_name)s %(instance)s would require deleting the following " -"protected related objects: %(related_objects)s" -msgstr "" -"Memadam %(class_name)s %(instance)s memerlukan pemadaman objek berkaitan " -"yang dilindungi: %(related_objects)s" - -msgid "Django site admin" -msgstr "Pentadbiran laman Django" - -msgid "Django administration" -msgstr "Pentadbiran Django" - -msgid "Site administration" -msgstr "Pentadbiran laman" - -msgid "Log in" -msgstr "Log masuk" - -#, python-format -msgid "%(app)s administration" -msgstr "Pentadbiran %(app)s" - -msgid "Page not found" -msgstr "Laman tidak dijumpai" - -msgid "We’re sorry, but the requested page could not be found." -msgstr "Maaf, tetapi laman yang diminta tidak dijumpai." - -msgid "Home" -msgstr "Utama" - -msgid "Server error" -msgstr "Masalah pelayan" - -msgid "Server error (500)" -msgstr "Masalah pelayan (500)" - -msgid "Server Error (500)" -msgstr "Masalah pelayan (500)" - -msgid "" -"There’s been an error. It’s been reported to the site administrators via " -"email and should be fixed shortly. Thanks for your patience." -msgstr "" -"Terdapat masalah. Ia telah dilaporkan kepada pentadbir laman melalui emel " -"dan sepatutnya dibaiki sebentar lagi. Kesabaran anda amat dihargai." - -msgid "Run the selected action" -msgstr "Jalankan tindakan yang dipilih" - -msgid "Go" -msgstr "Teruskan" - -msgid "Click here to select the objects across all pages" -msgstr "Klik disini untuk memilih objek-objek disemua laman" - -#, python-format -msgid "Select all %(total_count)s %(module_name)s" -msgstr "Pilih kesemua %(total_count)s%(module_name)s" - -msgid "Clear selection" -msgstr "Padam pilihan" - -#, python-format -msgid "Models in the %(name)s application" -msgstr "Model didalam aplikasi %(name)s" - -msgid "Add" -msgstr "Tambah" - -msgid "View" -msgstr "Lihat" - -msgid "You don’t have permission to view or edit anything." -msgstr "Anda tidak mempunyai kebenaran untuk melihat atau edit apa-apa." - -msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " -"options." -msgstr "" -"Pertama sekali, masukkan nama pengguna dan kata laluan. Selepas itu, anda " -"boleh edit pilihan pengguna yang lain" - -msgid "Enter a username and password." -msgstr "Masukkan nama pengguna dan kata laluan." - -msgid "Change password" -msgstr "Tukar kata laluan" - -msgid "Please correct the error below." -msgstr "Sila betulkan ralat di bawah." - -msgid "Please correct the errors below." -msgstr "Sila betulkan ralat-ralat di bawah." - -#, python-format -msgid "Enter a new password for the user %(username)s." -msgstr "Masukkan kata lalauan bagi pengguna %(username)s" - -msgid "Welcome," -msgstr "Selamat datang," - -msgid "View site" -msgstr "Lihat laman" - -msgid "Documentation" -msgstr "Dokumentasi" - -msgid "Log out" -msgstr "Log keluar" - -#, python-format -msgid "Add %(name)s" -msgstr "Tambah %(name)s" - -msgid "History" -msgstr "Sejarah" - -msgid "View on site" -msgstr "Lihat di laman" - -msgid "Filter" -msgstr "Tapis" - -msgid "Clear all filters" -msgstr "Kosongkan kesemua tapisan" - -msgid "Remove from sorting" -msgstr "Buang daripada penyusunan" - -#, python-format -msgid "Sorting priority: %(priority_number)s" -msgstr "Keutamaan susunan: %(priority_number)s" - -msgid "Toggle sorting" -msgstr "Togol penyusunan" - -msgid "Delete" -msgstr "Buang" - -#, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" -msgstr "" -"Memadam %(object_name)s '%(escaped_object)s' akan menyebabkan pembuangan " -"objek-objek yang berkaitan, tetapi akaun anda tidak mempunyai kebenaran " -"untuk memadam jenis-jenis objek-objek berikut:" - -#, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " -"following protected related objects:" -msgstr "" -"Membuang %(object_name)s '%(escaped_object)s' memerlukan pembuangan objek-" -"objek berkaitan yang dilindungi:" - -#, python-format -msgid "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" -msgstr "" -"Adakah anda pasti anda ingin membuang %(object_name)s \"%(escaped_object)s" -"\"? Semua item-item berkaitan berikut akan turut dibuang:" - -msgid "Objects" -msgstr "Objek-objek" - -msgid "Yes, I’m sure" -msgstr "Ya, saya pasti" - -msgid "No, take me back" -msgstr "Tidak, bawa saya kembali" - -msgid "Delete multiple objects" -msgstr "Buang pelbagai objek" - -#, python-format -msgid "" -"Deleting the selected %(objects_name)s would result in deleting related " -"objects, but your account doesn't have permission to delete the following " -"types of objects:" -msgstr "" -"Membuang %(objects_name)s akan menyebabkan pembuangan objek-objek yang " -"berkaitan, tetapi akaun anda tidak mempunyai kebenaran to membuang jenis " -"objek-objek berikut:" - -#, python-format -msgid "" -"Deleting the selected %(objects_name)s would require deleting the following " -"protected related objects:" -msgstr "" -"Membuang %(objects_name)s memerlukan pembuangan objek-objek berkaitan yang " -"dilindungi:" - -#, python-format -msgid "" -"Are you sure you want to delete the selected %(objects_name)s? All of the " -"following objects and their related items will be deleted:" -msgstr "" -"Adakah anda pasti untuk membuang %(objects_name)s yang dipilih? Segala objek-" -"objek berikut dan item-item yang berkaitan akan turut dibuang:" - -msgid "Delete?" -msgstr "Buang?" - -#, python-format -msgid " By %(filter_title)s " -msgstr "Daripada %(filter_title)s" - -msgid "Summary" -msgstr "Rumusan" - -msgid "Recent actions" -msgstr "Tindakan terkini" - -msgid "My actions" -msgstr "Tindakan saya" - -msgid "None available" -msgstr "Tiada yang tersedia" - -msgid "Unknown content" -msgstr "Kandungan tidak diketahui" - -msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." -msgstr "" -"Nampaknya ada masalah dengan pemasangan pangkalan data anda. Pastikan jadual " -"pangkalan yang bersesuaian telah di cipta, dan pastikan pangkalan data " -"tersebut boleh dibaca oleh pengguna yang bersesuaian." - -#, python-format -msgid "" -"You are authenticated as %(username)s, but are not authorized to access this " -"page. Would you like to login to a different account?" -msgstr "" -"Anda telah disahkan sebagai %(username)s, tetapi anda tidak dibenarkan untuk " -"mengakses ruangan ini. Adakah anda ingin log masuk menggunakan akaun lain?" - -msgid "Forgotten your password or username?" -msgstr "Terlupa kata laluan atau nama pengguna anda?" - -msgid "Toggle navigation" -msgstr "Togol navigasi" - -msgid "Start typing to filter…" -msgstr "Mulakan menaip untuk menapis..." - -msgid "Filter navigation items" -msgstr "Tapis item-item navigasi" - -msgid "Date/time" -msgstr "Tarikh/masa" - -msgid "User" -msgstr "Pengguna" - -msgid "Action" -msgstr "Tindakan" - -msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " -"admin site." -msgstr "" -"Objek ini tidak mempunyai sejarah penukaran. Ini mungkin bermaksud ia tidak " -"ditambah menggunakan laman admin ini." - -msgid "Show all" -msgstr "Tunjuk semua" - -msgid "Save" -msgstr "Simpan" - -msgid "Popup closing…" -msgstr "Popup sedang ditutup..." - -msgid "Search" -msgstr "Cari" - -#, python-format -msgid "%(counter)s result" -msgid_plural "%(counter)s results" -msgstr[0] "%(counter)s keputusan" - -#, python-format -msgid "%(full_result_count)s total" -msgstr "%(full_result_count)s jumlah" - -msgid "Save as new" -msgstr "Simpan sebagai baru" - -msgid "Save and add another" -msgstr "Simpan dan tambah lagi" - -msgid "Save and continue editing" -msgstr "Simpan dan teruskan mengedit" - -msgid "Save and view" -msgstr "Simpan dan lihat" - -msgid "Close" -msgstr "Tutup" - -#, python-format -msgid "Change selected %(model)s" -msgstr "Tukar %(model)s yang dipilih" - -#, python-format -msgid "Add another %(model)s" -msgstr "Tambah %(model)s" - -#, python-format -msgid "Delete selected %(model)s" -msgstr "Buang %(model)s pilihan" - -msgid "Thanks for spending some quality time with the web site today." -msgstr "Terima kasih kerana meluangkan masa di laman sesawang ini hari ini." - -msgid "Log in again" -msgstr "Log masuk semula" - -msgid "Password change" -msgstr "Pertukaran kata laluan" - -msgid "Your password was changed." -msgstr "Kata laluan anda telah ditukarkan" - -msgid "" -"Please enter your old password, for security’s sake, and then enter your new " -"password twice so we can verify you typed it in correctly." -msgstr "" -"Untuk tujuan keselamatan, sila masukkan kata laluan lama, kemudian masukkan " -"kata laluan baru dua kali supaya kami dapat memastikan anda memasukkannya " -"dengan betul." - -msgid "Change my password" -msgstr "Tukar kata laluan saya" - -msgid "Password reset" -msgstr "Tetap semula kata laluan" - -msgid "Your password has been set. You may go ahead and log in now." -msgstr "Kata laluan anda telah ditetapkan. Sila log masuk." - -msgid "Password reset confirmation" -msgstr "Pengesahan tetapan semula kata laluan" - -msgid "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." -msgstr "" -"Sila masukkan kata laluan baru anda dua kali supaya kami adpat memastikan " -"anda memasukkannya dengan betul." - -msgid "New password:" -msgstr "Kata laluan baru:" - -msgid "Confirm password:" -msgstr "Sahkan kata laluan:" - -msgid "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." -msgstr "" -"Pautan tetapan semula kata laluan tidak sah, mungkin kerana ia telah " -"digunakan. Sila minta tetapan semula kata laluan yang baru." - -msgid "" -"We’ve emailed you instructions for setting your password, if an account " -"exists with the email you entered. You should receive them shortly." -msgstr "" -"Kami telah menghantar panduan untuk menetapkan kata laluan anda melalui " -"emel, sekiranya emel yang anda masukkan itu wujud. Anda sepatutnya " -"menerimanya sebentar lagi." - -msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " -"you registered with, and check your spam folder." -msgstr "" -"Jika anda tidak menerima emel, sila pastikan anda telah memasukkan alamat " -"emel yang telah didaftarkan, dan semak folder spam anda." - -#, python-format -msgid "" -"You're receiving this email because you requested a password reset for your " -"user account at %(site_name)s." -msgstr "" -"Anda menerima emel ini kerana anda telah memohon untuk menetapkan semula " -"kata laluan bagi akaun pengguna di %(site_name)s" - -msgid "Please go to the following page and choose a new password:" -msgstr "Sila ke ruangan berikut dan pilih kata laluan baru:" - -msgid "Your username, in case you’ve forgotten:" -msgstr "Nama pengguna anda, sekiranya anda terlupa:" - -msgid "Thanks for using our site!" -msgstr "Terima kasih kerana menggunakan laman kami!" - -#, python-format -msgid "The %(site_name)s team" -msgstr "Pasukan %(site_name)s" - -msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " -"instructions for setting a new one." -msgstr "" -"Lupa kata laluan anda? Masukkan alamat emel anda dibawah, dan kami akan " -"menghantar cara untuk menetapkan kata laluan baru." - -msgid "Email address:" -msgstr "Alamat emel:" - -msgid "Reset my password" -msgstr "Tetap semula kata laluan saya" - -msgid "All dates" -msgstr "Semua tarikh" - -#, python-format -msgid "Select %s" -msgstr "Pilih %s" - -#, python-format -msgid "Select %s to change" -msgstr "Pilih %s untuk diubah" - -#, python-format -msgid "Select %s to view" -msgstr "Pilih %s untuk lihat" - -msgid "Date:" -msgstr "Tarikh:" - -msgid "Time:" -msgstr "Masa:" - -msgid "Lookup" -msgstr "Carian" - -msgid "Currently:" -msgstr "Kini:" - -msgid "Change:" -msgstr "Tukar:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.mo deleted file mode 100644 index 65e0050..0000000 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.po deleted file mode 100644 index 6d86502..0000000 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ms/LC_MESSAGES/djangojs.po +++ /dev/null @@ -1,264 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-16 13:42+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -#, javascript-format -msgid "Available %s" -msgstr "%s tersedia" - -#, javascript-format -msgid "" -"This is the list of available %s. You may choose some by selecting them in " -"the box below and then clicking the \"Choose\" arrow between the two boxes." -msgstr "" -"Ini adalah senarai %s yang tersedia. Anda boleh memilih beberapa dengan " -"memilihnya di dalam kotak dibawah dan kemudian klik pada anak panah \"Pilih" -"\" diantara dua kotak itu." - -#, javascript-format -msgid "Type into this box to filter down the list of available %s." -msgstr "Taip didalam kotak untuk menapis senarai %s yang tersedia." - -msgid "Filter" -msgstr "Tapis" - -msgid "Choose all" -msgstr "Pilih semua" - -#, javascript-format -msgid "Click to choose all %s at once." -msgstr "Klik untuk memlih semua %s serentak." - -msgid "Choose" -msgstr "Pilih" - -msgid "Remove" -msgstr "Buang" - -#, javascript-format -msgid "Chosen %s" -msgstr "%s dipilh" - -#, javascript-format -msgid "" -"This is the list of chosen %s. You may remove some by selecting them in the " -"box below and then clicking the \"Remove\" arrow between the two boxes." -msgstr "" -"Ini adalah senarai %s yang dipilih. Anda boleh membuangnya dengan memilihnya " -"pada kotak dibawah dan kemudian klik pada anak panah \"Buang\" diantara dua " -"kotak itu." - -msgid "Remove all" -msgstr "Buang semua" - -#, javascript-format -msgid "Click to remove all chosen %s at once." -msgstr "Klik untuk membuang serentak semua %s yang dipilih." - -msgid "%(sel)s of %(cnt)s selected" -msgid_plural "%(sel)s of %(cnt)s selected" -msgstr[0] "%(sel)s daripada %(cnt)s dipilih" - -msgid "" -"You have unsaved changes on individual editable fields. If you run an " -"action, your unsaved changes will be lost." -msgstr "" -"Anda mempunyai perubahan yang belum disimpan pada medan-medan individu yang " -"boleh di-edit. Sekiranya anda melakukan sebarang tindakan, penukaran yang " -"tidak disimpan akan hilang." - -msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " -"action." -msgstr "" -"Anda telah memlih tindakan, tetapi anda belum menyimpan perubahan yang " -"dilakukan pada medan-medan individu. Sila klik OK to untuk simpan. Anda " -"perlu melakukan semula tindakan tersebut." - -msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " -"button." -msgstr "" -"Anda telah memilih sesuatu tindakan, dan belum membuat perubahan pada medan-" -"medan individu. Anda mungkin sedang mencari butang Pergi dan bukannya butang " -"Simpan." - -msgid "Now" -msgstr "Sekarang" - -msgid "Midnight" -msgstr "Tengah malam" - -msgid "6 a.m." -msgstr "6 pagi" - -msgid "Noon" -msgstr "Tengahari" - -msgid "6 p.m." -msgstr "6 malam" - -#, javascript-format -msgid "Note: You are %s hour ahead of server time." -msgid_plural "Note: You are %s hours ahead of server time." -msgstr[0] "Nota: Anda %s jam ke depan daripada masa pelayan." - -#, javascript-format -msgid "Note: You are %s hour behind server time." -msgid_plural "Note: You are %s hours behind server time." -msgstr[0] "Nota: Anda %s jam ke belakang daripada masa pelayan." - -msgid "Choose a Time" -msgstr "Pilih Masa" - -msgid "Choose a time" -msgstr "Pilih masa" - -msgid "Cancel" -msgstr "Batal" - -msgid "Today" -msgstr "Hari ini" - -msgid "Choose a Date" -msgstr "Pilih Tarikh" - -msgid "Yesterday" -msgstr "Semalam" - -msgid "Tomorrow" -msgstr "Esok" - -msgid "January" -msgstr "Januari" - -msgid "February" -msgstr "Februari" - -msgid "March" -msgstr "Mac" - -msgid "April" -msgstr "Arpil" - -msgid "May" -msgstr "Mei" - -msgid "June" -msgstr "Jun" - -msgid "July" -msgstr "Julai" - -msgid "August" -msgstr "Ogos" - -msgid "September" -msgstr "September" - -msgid "October" -msgstr "Oktober" - -msgid "November" -msgstr "November" - -msgid "December" -msgstr "Disember" - -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Jan" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mar" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Apr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Mei" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jun" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Jul" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Ogo" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Okt" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nov" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Dis" - -msgctxt "one letter Sunday" -msgid "S" -msgstr "A" - -msgctxt "one letter Monday" -msgid "M" -msgstr "I" - -msgctxt "one letter Tuesday" -msgid "T" -msgstr "Se" - -msgctxt "one letter Wednesday" -msgid "W" -msgstr "R" - -msgctxt "one letter Thursday" -msgid "T" -msgstr "K" - -msgctxt "one letter Friday" -msgid "F" -msgstr "J" - -msgctxt "one letter Saturday" -msgid "S" -msgstr "Sa" - -msgid "Show" -msgstr "Tunjuk" - -msgid "Hide" -msgstr "Sorok" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.mo index d104714..903f979 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.po index ab9e55a..a328113 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # Sagar Chalise , 2011 # Santosh Purbey , 2020 -# Shrawan Poudel , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 06:50+0000\n" -"Last-Translator: Shrawan Poudel \n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-21 09:52+0000\n" +"Last-Translator: Santosh Purbey \n" "Language-Team: Nepali (http://www.transifex.com/django/django/language/ne/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,10 +17,6 @@ msgstr "" "Language: ne\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "%(verbose_name_plural)s छानिएको मेट्नुहोस" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "सफलतापूर्वक मेटियो %(count)d %(items)s ।" @@ -33,6 +28,10 @@ msgstr "%(name)s मेट्न सकिएन " msgid "Are you sure?" msgstr "के तपाई पक्का हुनुहुन्छ ?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "%(verbose_name_plural)s छानिएको मेट्नुहोस" + msgid "Administration" msgstr "प्रशासन " @@ -69,12 +68,6 @@ msgstr "मिति छैन" msgid "Has date" msgstr "मिति छ" -msgid "Empty" -msgstr "खाली" - -msgid "Not empty" -msgstr "खाली छैन" - #, python-format msgid "" "Please enter the correct %(username)s and password for a staff account. Note " @@ -328,19 +321,6 @@ msgstr "%(total_count)s %(module_name)s सबै छान्नुहोस " msgid "Clear selection" msgstr "चुनेको कुरा हटाउनुहोस ।" -#, python-format -msgid "Models in the %(name)s application" -msgstr "%(name)s एप्लिकेसनमा भएको मोडेलहरु" - -msgid "Add" -msgstr "थप्नुहोस " - -msgid "View" -msgstr "" - -msgid "You don’t have permission to view or edit anything." -msgstr "तपाईंसँग केहि पनि हेर्न वा सम्पादन गर्न अनुमति छैन।" - msgid "" "First, enter a username and password. Then, you’ll be able to edit more user " "options." @@ -389,9 +369,6 @@ msgstr "साइटमा हेर्नुहोस" msgid "Filter" msgstr "छान्नुहोस" -msgid "Clear all filters" -msgstr "" - msgid "Remove from sorting" msgstr "" @@ -455,6 +432,9 @@ msgid "" "following objects and their related items will be deleted:" msgstr "%(objects_name)s " +msgid "View" +msgstr "" + msgid "Delete?" msgstr "मेट्नुहुन्छ ?" @@ -465,6 +445,16 @@ msgstr " %(filter_title)s द्वारा" msgid "Summary" msgstr "" +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s एप्लिकेसनमा भएको मोडेलहरु" + +msgid "Add" +msgstr "थप्नुहोस " + +msgid "You don’t have permission to view or edit anything." +msgstr "तपाईंसँग केहि पनि हेर्न वा सम्पादन गर्न अनुमति छैन।" + msgid "Recent actions" msgstr "भर्खरका कार्यहरू" @@ -497,15 +487,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "पासवर्ड अथवा प्रयोगकर्ता नाम भुल्नुभयो ।" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "मिति/समय" @@ -569,8 +550,8 @@ msgstr "" msgid "Delete selected %(model)s" msgstr "" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "वेब साइटमा समय बिताउनु भएकोमा धन्यवाद ।" msgid "Log in again" msgstr "पुन: लगिन गर्नुहोस" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.mo index 315dffc..78170f0 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.po index 37eb0cb..a85f011 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/django.po @@ -5,16 +5,14 @@ # Jannis Leidel , 2011 # jensadne , 2013 # Sigurd Gartmann , 2012 -# Sivert Olstad, 2021 # velmont , 2012 -# Vibeke Uthaug, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-18 12:44+0000\n" -"Last-Translator: Vibeke Uthaug\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -23,10 +21,6 @@ msgstr "" "Language: nn\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Slett valgte %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Sletta %(count)d %(items)s." @@ -38,8 +32,12 @@ msgstr "Kan ikkje slette %(name)s" msgid "Are you sure?" msgstr "Er du sikker?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Slett valgte %(verbose_name_plural)s" + msgid "Administration" -msgstr "Administrasjon" +msgstr "" msgid "All" msgstr "Alle" @@ -69,58 +67,41 @@ msgid "This year" msgstr "I år" msgid "No date" -msgstr "Ingen dato" +msgstr "" msgid "Has date" -msgstr "Har dato" - -msgid "Empty" -msgstr "Tom" - -msgid "Not empty" -msgstr "Ikkje tom" +msgstr "" #, python-format msgid "" "Please enter the correct %(username)s and password for a staff account. Note " "that both fields may be case-sensitive." msgstr "" -"Oppgje korrekt %(username)s og passord for ein administrasjonsbrukarkonto. " -"Merk at det er skilnad på små og store bokstavar." msgid "Action:" msgstr "Handling:" #, python-format msgid "Add another %(verbose_name)s" -msgstr "Opprett ny %(verbose_name)s." +msgstr "Legg til ny %(verbose_name)s." msgid "Remove" msgstr "Fjern" -msgid "Addition" -msgstr "Tillegg" - -msgid "Change" -msgstr "Endre" - -msgid "Deletion" -msgstr "Sletting" - msgid "action time" msgstr "tid for handling" msgid "user" -msgstr "brukar" +msgstr "" msgid "content type" -msgstr "innhaldstype" +msgstr "" msgid "object id" msgstr "objekt-ID" #. Translators: 'repr' means representation -#. (https://docs.python.org/library/functions.html#repr) +#. (https://docs.python.org/3/library/functions.html#repr) msgid "object repr" msgstr "objekt repr" @@ -137,41 +118,41 @@ msgid "log entries" msgstr "logginnlegg" #, python-format -msgid "Added “%(object)s”." -msgstr "Oppretta “%(object)s”." +msgid "Added \"%(object)s\"." +msgstr "La til «%(object)s»." #, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "Endra “%(object)s” — %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Endra «%(object)s» - %(changes)s" #, python-format -msgid "Deleted “%(object)s.”" -msgstr "Sletta “%(object)s”." +msgid "Deleted \"%(object)s.\"" +msgstr "Sletta «%(object)s»." msgid "LogEntry Object" msgstr "LogEntry-objekt" #, python-brace-format -msgid "Added {name} “{object}”." -msgstr "Oppretta {name} “{object}”." +msgid "Added {name} \"{object}\"." +msgstr "" msgid "Added." -msgstr "Oppretta." +msgstr "" msgid "and" msgstr "og" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "Endra {fields} for {name} “{object}”." +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" #, python-brace-format msgid "Changed {fields}." -msgstr "Endra {fields}." +msgstr "" #, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "Sletta {name} “{object}”." +msgid "Deleted {name} \"{object}\"." +msgstr "" msgid "No fields changed." msgstr "Ingen felt endra." @@ -179,41 +160,39 @@ msgstr "Ingen felt endra." msgid "None" msgstr "Ingen" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" -"Hald nede “Control”, eller “Command” på ein Mac, for å velge meir enn éin." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "{name} “{obj}” vart oppretta." - -msgid "You may edit it again below." -msgstr "Du kan endre det att nedanfor." #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." +"The {name} \"{obj}\" was added successfully. You may edit it again below." msgstr "" -"{name} “{obj}” vart oppretta. Du kan opprette enda ein {name} nedanfor." #, python-brace-format msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "{name} “{obj}” vart endra. Du kan redigere vidare nedanfor." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "{name} “{obj}” vart oppretta. Du kan redigere vidare nedanfor." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." -msgstr "{name} “{obj}” vart endra. Du kan opprette enda ein {name} nedanfor." +msgstr "" #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." -msgstr "{name} “{obj}” vart endra." +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -226,13 +205,12 @@ msgid "No action selected." msgstr "Inga valt handling." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "%(name)s “%(obj)s” vart sletta." +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" vart sletta." #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" msgstr "" -"%(name)s med ID “%(key)s” eksisterer ikkje. Kanskje den har vorte sletta?" #, python-format msgid "Add %s" @@ -242,10 +220,6 @@ msgstr "Opprett %s" msgid "Change %s" msgstr "Rediger %s" -#, python-format -msgid "View %s" -msgstr "Sjå %s" - msgid "Database error" msgstr "Databasefeil" @@ -273,7 +247,7 @@ msgstr "Endringshistorikk: %s" #. suitable to be an item in a list. #, python-format msgid "%(class_name)s %(instance)s" -msgstr "%(class_name)s %(instance)s" +msgstr "" #, python-format msgid "" @@ -297,12 +271,12 @@ msgstr "Logg inn" #, python-format msgid "%(app)s administration" -msgstr "%(app)s-administrasjon" +msgstr "" msgid "Page not found" msgstr "Fann ikkje sida" -msgid "We’re sorry, but the requested page could not be found." +msgid "We're sorry, but the requested page could not be found." msgstr "Sida du spør etter finst ikkje." msgid "Home" @@ -318,11 +292,9 @@ msgid "Server Error (500)" msgstr "Tenarfeil (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" -"Det har oppstått ein feil. Det er rapportert til dei som administrerer " -"nettsida med e-mail og burde bli fiksa snarast. Takk for tolmodigheita." msgid "Run the selected action" msgstr "Utfør den valde handlinga" @@ -340,21 +312,8 @@ msgstr "Velg alle %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Nullstill utval" -#, python-format -msgid "Models in the %(name)s application" -msgstr "Modellar i %(name)s-applikasjonen" - -msgid "Add" -msgstr "Opprett" - -msgid "View" -msgstr "Sjå" - -msgid "You don’t have permission to view or edit anything." -msgstr "Du har ikkje løyve til å sjå eller redigere noko." - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" "Skriv først inn brukernamn og passord. Deretter vil du få høve til å endre " @@ -367,10 +326,10 @@ msgid "Change password" msgstr "Endre passord" msgid "Please correct the error below." -msgstr "Korriger feilen under." +msgstr "Korriger feila under." msgid "Please correct the errors below." -msgstr "Korriger feila under." +msgstr "" #, python-format msgid "Enter a new password for the user %(username)s." @@ -380,7 +339,7 @@ msgid "Welcome," msgstr "Velkommen," msgid "View site" -msgstr "Vis nettstad" +msgstr "" msgid "Documentation" msgstr "Dokumentasjon" @@ -401,9 +360,6 @@ msgstr "Vis på nettstad" msgid "Filter" msgstr "Filtrering" -msgid "Clear all filters" -msgstr "Fjern alle filter" - msgid "Remove from sorting" msgstr "Fjern frå sortering" @@ -443,13 +399,13 @@ msgstr "" "Alle dei følgjande relaterte objekta vil bli sletta:" msgid "Objects" -msgstr "Objekt" +msgstr "" -msgid "Yes, I’m sure" +msgid "Yes, I'm sure" msgstr "Ja, eg er sikker" msgid "No, take me back" -msgstr "Nei, ta meg attende" +msgstr "" msgid "Delete multiple objects" msgstr "Slett fleire objekt" @@ -479,6 +435,9 @@ msgstr "" "Er du sikker på at du vil slette dei valgte objekta %(objects_name)s? " "Følgjande objekt og deira relaterte objekt vil bli sletta:" +msgid "Change" +msgstr "Endre" + msgid "Delete?" msgstr "Slette?" @@ -487,13 +446,23 @@ msgid " By %(filter_title)s " msgstr "Etter %(filter_title)s " msgid "Summary" -msgstr "Oppsummering" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "Opprett" + +msgid "You don't have permission to edit anything." +msgstr "Du har ikkje løyve til å redigere noko." msgid "Recent actions" -msgstr "Siste handlingar" +msgstr "" msgid "My actions" -msgstr "Mine handlingar" +msgstr "" msgid "None available" msgstr "Ingen tilgjengelege" @@ -502,11 +471,10 @@ msgid "Unknown content" msgstr "Ukjent innhald" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -" match \n" "Noko er gale med databaseinstallasjonen din. Syt for at databasetabellane er " "oppretta og at brukaren har dei naudsynte løyve." @@ -515,21 +483,10 @@ msgid "" "You are authenticated as %(username)s, but are not authorized to access this " "page. Would you like to login to a different account?" msgstr "" -"Du er stadfesta som %(username)s, men er ikkje autentisert til å få tilgang " -"til denne sida . Ynskjer du å logge inn med ein annan konto?" msgid "Forgotten your password or username?" msgstr "Gløymd brukarnamn eller passord?" -msgid "Toggle navigation" -msgstr "Veksl navigasjon" - -msgid "Start typing to filter…" -msgstr "Begynn å skrive for å filtrere..." - -msgid "Filter navigation items" -msgstr "Filtrer navigasjonselement" - msgid "Date/time" msgstr "Dato/tid" @@ -540,11 +497,11 @@ msgid "Action" msgstr "Handling" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" -"Dette objektet har ingen endringshistorikk. Det blei sannsynlegvis ikkje " -"oppretta av denne administratoren. " +"Dette objektet har ingen endringshistorikk. Det var sannsynlegvis ikkje " +"oppretta med administrasjonssida." msgid "Show all" msgstr "Vis alle" @@ -552,8 +509,20 @@ msgstr "Vis alle" msgid "Save" msgstr "Lagre" -msgid "Popup closing…" -msgstr "Lukkar popup…" +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" msgid "Search" msgstr "Søk" @@ -577,26 +546,8 @@ msgstr "Lagre og opprett ny" msgid "Save and continue editing" msgstr "Lagre og hald fram å redigere" -msgid "Save and view" -msgstr "Lagre og sjå" - -msgid "Close" -msgstr "Lukk" - -#, python-format -msgid "Change selected %(model)s" -msgstr "Endre valt %(model)s" - -#, python-format -msgid "Add another %(model)s" -msgstr "Opprett ny %(model)s" - -#, python-format -msgid "Delete selected %(model)s" -msgstr "Slett valde %(model)s" - -msgid "Thanks for spending some quality time with the web site today." -msgstr "Takk for at du brukte litt kvalitetstid på nettsida i dag. " +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Takk for at du brukte kvalitetstid på nettstaden i dag." msgid "Log in again" msgstr "Logg inn att" @@ -608,11 +559,11 @@ msgid "Your password was changed." msgstr "Passordet ditt vart endret." msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" "Av sikkerheitsgrunnar må du oppgje det gamle passordet ditt. Oppgje så det " -"nye passordet ditt to gongar, sånn at vi kan kontrollere at det er korrekt." +"nye passordet ditt to gonger, slik at vi kan kontrollere at det er korrekt." msgid "Change my password" msgstr "Endre passord" @@ -647,32 +598,25 @@ msgstr "" "Nullstill passordet ditt på nytt." msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" -"Dersom det eksisterer ein brukarkonto med e-postadressa du skreiv inn vil " -"det bli sendt ein e-post med instruksjonar for å nullstille passordet til " -"den e-postadressa. Du burde motta den snart. " msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" -"Om du ikkje mottar ein e-post, ver vennleg og sørg for at du skreiv inn e-" -"postadressa du er registrert med og sjekk spam-filteret. " #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"Du får denne e-posten fordi du har bedt om å nullstille passordet for " -"brukarkontoen din på %(site_name)s." msgid "Please go to the following page and choose a new password:" msgstr "Gå til følgjande side og velg eit nytt passord:" -msgid "Your username, in case you’ve forgotten:" +msgid "Your username, in case you've forgotten:" msgstr "Brukarnamnet ditt, i tilfelle du har gløymt det:" msgid "Thanks for using our site!" @@ -683,14 +627,12 @@ msgid "The %(site_name)s team" msgstr "Helsing %(site_name)s" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" -"Gløymt passordet ditt? Oppgje e-postadressa di under, så sender me deg ein e-" -"post med instruksjonar for nullstilling av passord." msgid "Email address:" -msgstr "E-postadresse:" +msgstr "" msgid "Reset my password" msgstr "Nullstill passordet" @@ -706,10 +648,6 @@ msgstr "Velg %s" msgid "Select %s to change" msgstr "Velg %s du ønskar å redigere" -#, python-format -msgid "Select %s to view" -msgstr "Velg %s du ønskar å sjå" - msgid "Date:" msgstr "Dato:" @@ -720,7 +658,7 @@ msgid "Lookup" msgstr "Oppslag" msgid "Currently:" -msgstr "Noverande:" +msgstr "" msgid "Change:" -msgstr "Endre:" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo index d94421c..c4c8241 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po index 8d4f648..07ba2f6 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/nn/LC_MESSAGES/djangojs.po @@ -3,15 +3,14 @@ # Translators: # hgrimelid , 2011 # Jannis Leidel , 2011 -# Sivert Olstad, 2021 # velmont , 2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-10 23:27+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -84,24 +83,42 @@ msgstr "" "Endringar som ikkje er lagra vil gå tapt." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" "Du har vald ei handling, men du har framleis ikkje lagra endringar for " "individuelle felt. Klikk OK for å lagre. Du må gjere handlinga på nytt." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" "Du har vald ei handling og du har ikkje gjort endringar i individuelle felt. " "Du ser sannsynlegvis etter Gå vidare-knappen - ikkje Lagre-knappen." +#, javascript-format +msgid "Note: You are %s hour ahead of server time." +msgid_plural "Note: You are %s hours ahead of server time." +msgstr[0] "" +msgstr[1] "" + +#, javascript-format +msgid "Note: You are %s hour behind server time." +msgid_plural "Note: You are %s hours behind server time." +msgstr[0] "" +msgstr[1] "" + msgid "Now" msgstr "No" +msgid "Choose a Time" +msgstr "" + +msgid "Choose a time" +msgstr "Velg eit klokkeslett" + msgid "Midnight" msgstr "Midnatt" @@ -112,25 +129,7 @@ msgid "Noon" msgstr "12:00" msgid "6 p.m." -msgstr "18:00" - -#, javascript-format -msgid "Note: You are %s hour ahead of server time." -msgid_plural "Note: You are %s hours ahead of server time." -msgstr[0] "Merk: Du er %s time framanfor tjenar-tid." -msgstr[1] "Merk: Du er %s timar framanfor tjenar-tid." - -#, javascript-format -msgid "Note: You are %s hour behind server time." -msgid_plural "Note: You are %s hours behind server time." -msgstr[0] "Merk: Du er %s time bak tjenar-tid." -msgstr[1] "Merk: Du er %s timar bak tjenar-tid." - -msgid "Choose a Time" -msgstr "Velg eit klokkeslett" - -msgid "Choose a time" -msgstr "Velg eit klokkeslett" +msgstr "" msgid "Cancel" msgstr "Avbryt" @@ -139,7 +138,7 @@ msgid "Today" msgstr "I dag" msgid "Choose a Date" -msgstr "Velg ein dato" +msgstr "" msgid "Yesterday" msgstr "I går" @@ -148,116 +147,68 @@ msgid "Tomorrow" msgstr "I morgon" msgid "January" -msgstr "Januar" +msgstr "" msgid "February" -msgstr "Februar" +msgstr "" msgid "March" -msgstr "Mars" +msgstr "" msgid "April" -msgstr "April" +msgstr "" msgid "May" -msgstr "Mai" +msgstr "" msgid "June" -msgstr "Juni" +msgstr "" msgid "July" -msgstr "Juli" +msgstr "" msgid "August" -msgstr "August" +msgstr "" msgid "September" -msgstr "September" +msgstr "" msgid "October" -msgstr "Oktober" +msgstr "" msgid "November" -msgstr "November" +msgstr "" msgid "December" -msgstr "Desember" - -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Jan" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Feb" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Mar" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Apr" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Mai" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Jun" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Jul" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Aug" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Sep" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Okt" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Nov" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Des" +msgstr "" msgctxt "one letter Sunday" msgid "S" -msgstr "S" +msgstr "" msgctxt "one letter Monday" msgid "M" -msgstr "M" +msgstr "" msgctxt "one letter Tuesday" msgid "T" -msgstr "T" +msgstr "" msgctxt "one letter Wednesday" msgid "W" -msgstr "O" +msgstr "" msgctxt "one letter Thursday" msgid "T" -msgstr "T" +msgstr "" msgctxt "one letter Friday" msgid "F" -msgstr "F" +msgstr "" msgctxt "one letter Saturday" msgid "S" -msgstr "L" +msgstr "" msgid "Show" msgstr "Vis" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.mo index 710af1d..7f97615 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.po index bcdec1a..14b83e8 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # A S Alam , 2018 # Jannis Leidel , 2011 -# Satnam S Virdi , 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 07:21+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2018-05-21 14:16-0300\n" +"PO-Revision-Date: 2018-05-28 01:29+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: Panjabi (Punjabi) (http://www.transifex.com/django/django/" "language/pa/)\n" "MIME-Version: 1.0\n" @@ -19,10 +18,6 @@ msgstr "" "Language: pa\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "ਚੁਣੇ %(verbose_name_plural)s ਹਟਾਓ" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "%(count)d %(items)s ਠੀਕ ਤਰ੍ਹਾਂ ਹਟਾਈਆਂ ਗਈਆਂ।" @@ -34,8 +29,12 @@ msgstr "%(name)s ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ" msgid "Are you sure?" msgstr "ਕੀ ਤੁਸੀਂ ਇਹ ਚਾਹੁੰਦੇ ਹੋ?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "ਚੁਣੇ %(verbose_name_plural)s ਹਟਾਓ" + msgid "Administration" -msgstr "ਪ੍ਰਸ਼ਾਸਨ" +msgstr "ਪਰਸ਼ਾਸ਼ਨ" msgid "All" msgstr "ਸਭ" @@ -65,15 +64,9 @@ msgid "This year" msgstr "ਇਹ ਸਾਲ" msgid "No date" -msgstr "ਕੋਈ ਮਿਤੀ ਨਹੀਂ" - -msgid "Has date" -msgstr "ਮਿਤੀ ਹੈ" - -msgid "Empty" msgstr "" -msgid "Not empty" +msgid "Has date" msgstr "" #, python-format @@ -114,7 +107,7 @@ msgid "object id" msgstr "ਆਬਜੈਕਟ id" #. Translators: 'repr' means representation -#. (https://docs.python.org/library/functions.html#repr) +#. (https://docs.python.org/3/library/functions.html#repr) msgid "object repr" msgstr "ਆਬਜੈਕਟ repr" @@ -131,22 +124,22 @@ msgid "log entries" msgstr "ਲਾਗ ਐਂਟਰੀਆਂ" #, python-format -msgid "Added “%(object)s”." +msgid "Added \"%(object)s\"." msgstr "" #, python-format -msgid "Changed “%(object)s” — %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" msgstr "" #, python-format -msgid "Deleted “%(object)s.”" +msgid "Deleted \"%(object)s.\"" msgstr "" msgid "LogEntry Object" msgstr "" #, python-brace-format -msgid "Added {name} “{object}”." +msgid "Added {name} \"{object}\"." msgstr "" msgid "Added." @@ -156,7 +149,7 @@ msgid "and" msgstr "ਅਤੇ" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." +msgid "Changed {fields} for {name} \"{object}\"." msgstr "" #, python-brace-format @@ -164,7 +157,7 @@ msgid "Changed {fields}." msgstr "" #, python-brace-format -msgid "Deleted {name} “{object}”." +msgid "Deleted {name} \"{object}\"." msgstr "" msgid "No fields changed." @@ -173,11 +166,12 @@ msgstr "ਕੋਈ ਖੇਤਰ ਨਹੀਂ ਬਦਲਿਆ।" msgid "None" msgstr "ਕੋਈ ਨਹੀਂ" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" #, python-brace-format -msgid "The {name} “{obj}” was added successfully." +msgid "The {name} \"{obj}\" was added successfully." msgstr "" msgid "You may edit it again below." @@ -185,26 +179,28 @@ msgstr "" #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." msgstr "" msgid "" @@ -216,11 +212,11 @@ msgid "No action selected." msgstr "ਕੋਈ ਕਾਰਵਾਈ ਨਹੀਂ ਚੁਣੀ ਗਈ।" #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "" +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" ਠੀਕ ਤਰ੍ਹਾਂ ਹਟਾਇਆ ਗਿਆ ਹੈ।" #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" msgstr "" #, python-format @@ -289,8 +285,8 @@ msgstr "" msgid "Page not found" msgstr "ਸਫ਼ਾ ਨਹੀਂ ਲੱਭਿਆ" -msgid "We’re sorry, but the requested page could not be found." -msgstr "" +msgid "We're sorry, but the requested page could not be found." +msgstr "ਸਾਨੂੰ ਅਫਸੋਸ ਹੈ, ਪਰ ਅਸੀਂ ਮੰਗਿਆ ਗਿਆ ਸਫ਼ਾ ਨਹੀਂ ਲੱਭ ਸਕੇ।" msgid "Home" msgstr "ਘਰ" @@ -305,7 +301,7 @@ msgid "Server Error (500)" msgstr "ਸਰਵਰ ਗਲਤੀ (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" @@ -325,23 +321,10 @@ msgstr "ਸਭ %(total_count)s %(module_name)s ਚੁਣੋ" msgid "Clear selection" msgstr "ਚੋਣ ਸਾਫ਼ ਕਰੋ" -#, python-format -msgid "Models in the %(name)s application" -msgstr "" - -msgid "Add" -msgstr "ਸ਼ਾਮਲ" - -msgid "View" -msgstr "" - -msgid "You don’t have permission to view or edit anything." -msgstr "" - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." -msgstr "" +msgstr "ਪਹਿਲਾਂ ਆਪਣਾ ਯੂਜ਼ਰ ਨਾਂ ਤੇ ਪਾਸਵਰਡ ਦਿਉ। ਫੇਰ ਤੁਸੀਂ ਹੋਰ ਯੂਜ਼ਰ ਚੋਣਾਂ ਨੂੰ ਸੋਧ ਸਕਦੇ ਹੋ।" msgid "Enter a username and password." msgstr "" @@ -384,9 +367,6 @@ msgstr "ਸਾਈਟ ਉੱਤੇ ਜਾਓ" msgid "Filter" msgstr "ਫਿਲਟਰ" -msgid "Clear all filters" -msgstr "" - msgid "Remove from sorting" msgstr "" @@ -422,8 +402,8 @@ msgstr "" msgid "Objects" msgstr "" -msgid "Yes, I’m sure" -msgstr "" +msgid "Yes, I'm sure" +msgstr "ਹਾਂ, ਮੈਂ ਚਾਹੁੰਦਾ ਹਾਂ" msgid "No, take me back" msgstr "" @@ -450,6 +430,9 @@ msgid "" "following objects and their related items will be deleted:" msgstr "" +msgid "View" +msgstr "" + msgid "Delete?" msgstr "ਹਟਾਉਣਾ?" @@ -460,6 +443,16 @@ msgstr " %(filter_title)s ਵਲੋਂ " msgid "Summary" msgstr "" +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "ਸ਼ਾਮਲ" + +msgid "You don't have permission to view or edit anything." +msgstr "" + msgid "Recent actions" msgstr "" @@ -473,7 +466,7 @@ msgid "Unknown content" msgstr "ਅਣਜਾਣ ਸਮੱਗਰੀ" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" @@ -487,15 +480,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "ਮਿਤੀ/ਸਮਾਂ" @@ -506,7 +490,7 @@ msgid "Action" msgstr "ਕਾਰਵਾਈ" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" @@ -516,7 +500,23 @@ msgstr "ਸਭ ਵੇਖੋ" msgid "Save" msgstr "ਸੰਭਾਲੋ" -msgid "Popup closing…" +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" msgstr "" msgid "Search" @@ -547,20 +547,8 @@ msgstr "" msgid "Close" msgstr "" -#, python-format -msgid "Change selected %(model)s" -msgstr "" - -#, python-format -msgid "Add another %(model)s" -msgstr "" - -#, python-format -msgid "Delete selected %(model)s" -msgstr "" - -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "ਅੱਜ ਵੈੱਬਸਾਈਟ ਨੂੰ ਕੁਝ ਚੰਗਾ ਸਮਾਂ ਦੇਣ ਲਈ ਧੰਨਵਾਦ ਹੈ।" msgid "Log in again" msgstr "ਫੇਰ ਲਾਗਇਨ ਕਰੋ" @@ -572,9 +560,11 @@ msgid "Your password was changed." msgstr "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਬਦਲਿਆ ਗਿਆ ਹੈ।" msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" +"ਸੁਰੱਖਿਆ ਲਈ ਪਹਿਲਾਂ ਆਪਣਾ ਪੁਰਾਣਾ ਪਾਸਵਰਡ ਦਿਉ, ਅਤੇ ਫੇਰ ਆਪਣਾ ਨਵਾਂ ਪਾਸਵਰਡ ਦੋ ਵਰਾ ਦਿਉ ਤਾਂ ਕਿ " +"ਅਸੀਂ ਜਾਂਚ ਸਕੀਏ ਕਿ ਤੁਸੀਂ ਇਹ ਠੀਕ ਤਰ੍ਹਾਂ ਲਿਖਿਆ ਹੈ।" msgid "Change my password" msgstr "ਮੇਰਾ ਪਾਸਵਰਡ ਬਦਲੋ" @@ -608,12 +598,12 @@ msgstr "" "ਸੈੱਟ ਲਈ ਬੇਨਤੀ ਭੇਜੋ ਜੀ।" msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" @@ -626,8 +616,8 @@ msgstr "" msgid "Please go to the following page and choose a new password:" msgstr "ਅੱਗੇ ਦਿੱਤੇ ਸਫ਼ੇ ਉੱਤੇ ਜਾਉ ਤੇ ਨਵਾਂ ਪਾਸਵਰਡ ਚੁਣੋ:" -msgid "Your username, in case you’ve forgotten:" -msgstr "" +msgid "Your username, in case you've forgotten:" +msgstr "ਤੁਹਾਡਾ ਯੂਜ਼ਰ ਨਾਂ, ਜੇ ਕਿਤੇ ਗਲਤੀ ਨਾਲ ਭੁੱਲ ਗਏ ਹੋਵੋ:" msgid "Thanks for using our site!" msgstr "ਸਾਡੀ ਸਾਈਟ ਵਰਤਣ ਲਈ ਧੰਨਵਾਦ ਜੀ!" @@ -637,7 +627,7 @@ msgid "The %(site_name)s team" msgstr "%(site_name)s ਟੀਮ" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo index 08925e4..57cc79f 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po index ed55c46..2a36046 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/pa/LC_MESSAGES/djangojs.po @@ -6,9 +6,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-01-15 11:28+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2018-05-17 11:50+0200\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: Panjabi (Punjabi) (http://www.transifex.com/django/django/" "language/pa/)\n" "MIME-Version: 1.0\n" @@ -75,14 +75,14 @@ msgid "" msgstr "" msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" @@ -170,54 +170,6 @@ msgstr "" msgid "December" msgstr "" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "" - msgctxt "one letter Sunday" msgid "S" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.mo index bd17033..20d6375 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.po index f41d11a..3601c18 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django.po @@ -19,8 +19,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 11:13+0000\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-06-20 11:10+0000\n" "Last-Translator: m_aciek \n" "Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" "MIME-Version: 1.0\n" @@ -101,7 +101,7 @@ msgstr "Akcja:" #, python-format msgid "Add another %(verbose_name)s" -msgstr "Dodaj kolejne(go)(-ną)(-ny) %(verbose_name)s" +msgstr "Dodaj kolejne(go)(-ną) %(verbose_name)s" msgid "Remove" msgstr "Usuń" @@ -194,7 +194,7 @@ msgstr "" #, python-brace-format msgid "The {name} “{obj}” was added successfully." -msgstr "{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie." +msgstr "{name} „{obj}” został dodany pomyślnie." msgid "You may edit it again below." msgstr "Poniżej możesz ponownie edytować." @@ -203,33 +203,31 @@ msgstr "Poniżej możesz ponownie edytować." msgid "" "The {name} “{obj}” was added successfully. You may add another {name} below." msgstr "" -"{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie. Można dodać " -"kolejne(go)(-ną)(-ny) {name} poniżej." +"{name} „{obj}” został dodany pomyślnie. Można dodać kolejny {name} poniżej." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may edit it again below." msgstr "" -"{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne). Można edytować " -"go/ją/je ponownie poniżej." +"{name} „{obj}” został pomyślnie zmieniony. Można edytować go ponownie " +"poniżej." #, python-brace-format msgid "The {name} “{obj}” was added successfully. You may edit it again below." msgstr "" -"{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie. Można edytować go/" -"ją/je ponownie poniżej." +"{name} „{obj}” został dodany pomyślnie. Można edytować go ponownie poniżej." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may add another {name} " "below." msgstr "" -"{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne). Można dodać " -"kolejny(-nego)(-ną)(-ne) {name} poniżej." +"{name} „{obj}” został pomyślnie zmieniony. Można dodać kolejny {name} " +"poniżej." #, python-brace-format msgid "The {name} “{obj}” was changed successfully." -msgstr "{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne)." +msgstr "{name} „{obj}” został pomyślnie zmieniony." msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -242,12 +240,11 @@ msgstr "Nie wybrano akcji." #, python-format msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "%(name)s „%(obj)s” usunięty(-ta)(-te) pomyślnie." +msgstr "%(name)s „%(obj)s” usunięty pomyślnie." #, python-format msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "" -"%(name)s z ID „%(key)s” nie istnieje. Może został(a)(-ło) usunięty(-ta)(-te)?" +msgstr "%(name)s z ID „%(key)s” nie istnieje. Może został usunięty?" #, python-format msgid "Add %s" @@ -268,8 +265,7 @@ msgstr "Błąd bazy danych" msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s został(a)(-ło) pomyślnie zmieniony(-na)(-ne)." -msgstr[1] "" -"%(count)s %(name)s zostały(-ło)(-li) pomyślnie zmienione(-nych)(-nieni)." +msgstr[1] "%(count)s %(name)s zostały(-li) pomyślnie zmienione(-nieni)." msgstr[2] "%(count)s %(name)s zostało pomyślnie zmienionych." msgstr[3] "%(count)s %(name)s zostało pomyślnie zmienione." @@ -355,7 +351,7 @@ msgstr "Kliknij by wybrać obiekty na wszystkich stronach" #, python-format msgid "Select all %(total_count)s %(module_name)s" -msgstr "Wybierz wszystkie(-kich) %(total_count)s %(module_name)s" +msgstr "Wybierz wszystkie %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Wyczyść wybór" @@ -384,7 +380,7 @@ msgid "Enter a username and password." msgstr "Podaj nazwę użytkownika i hasło." msgid "Change password" -msgstr "Zmień hasło" +msgstr "Zmiana hasła" msgid "Please correct the error below." msgstr "Prosimy poprawić poniższy błąd." @@ -444,7 +440,7 @@ msgid "" "following types of objects:" msgstr "" "Usunięcie %(object_name)s „%(escaped_object)s” wiązałoby się z usunięciem " -"obiektów z nim/nią powiązanych, ale niestety nie posiadasz uprawnień do " +"obiektów z nim powiązanych, ale niestety nie posiadasz uprawnień do " "usunięcia obiektów następujących typów:" #, python-format @@ -453,7 +449,7 @@ msgid "" "following protected related objects:" msgstr "" "Usunięcie %(object_name)s „%(escaped_object)s” wymagałoby skasowania " -"następujących chronionych obiektów, które są z nim/nią powiązane:" +"następujących chronionych obiektów, które są z nim powiązane:" #, python-format msgid "" @@ -498,8 +494,8 @@ msgid "" "Are you sure you want to delete the selected %(objects_name)s? All of the " "following objects and their related items will be deleted:" msgstr "" -"Czy chcesz skasować zaznaczone(go)(-ną)(-ny)(-nych) %(objects_name)s? " -"Następujące obiekty oraz obiekty od nich zależne zostaną skasowane:" +"Czy chcesz skasować zaznaczone %(objects_name)s? Następujące obiekty oraz " +"obiekty od nich zależne zostaną skasowane:" msgid "Delete?" msgstr "Usunąć?" @@ -546,12 +542,6 @@ msgstr "Nie pamiętasz swojego hasła lub nazwy użytkownika?" msgid "Toggle navigation" msgstr "Przełącz nawigację" -msgid "Start typing to filter…" -msgstr "Zacznij pisać, aby odfiltrować…" - -msgid "Filter navigation items" -msgstr "Filtruj elementy nawigacji" - msgid "Date/time" msgstr "Data/czas" @@ -609,18 +599,18 @@ msgstr "Zamknij" #, python-format msgid "Change selected %(model)s" -msgstr "Zmień wybraną(-ne)(-nego)(-ny) %(model)s" +msgstr "Zmień wybrane %(model)s" #, python-format msgid "Add another %(model)s" -msgstr "Dodaj kolejne(go)(-ną)(-ny) %(model)s" +msgstr "Dodaj kolejny %(model)s" #, python-format msgid "Delete selected %(model)s" -msgstr "Usuń wybraną(-ne)(-nego)(-ny) %(model)s" +msgstr "Usuń wybrane %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Dzięki za spędzenie cennego czasu ze stroną." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Dziękujemy za spędzenie cennego czasu na stronie." msgid "Log in again" msgstr "Zaloguj się ponownie" @@ -692,7 +682,7 @@ msgid "" "user account at %(site_name)s." msgstr "" "Otrzymujesz tę wiadomość, gdyż skorzystano z opcji resetu hasła dla Twojego " -"konta na stronie %(site_name)s." +"konta na stronie %(site_name)s." msgid "Please go to the following page and choose a new password:" msgstr "" @@ -703,7 +693,7 @@ msgid "Your username, in case you’ve forgotten:" msgstr "Twoja nazwa użytkownika, na wypadek, gdybyś zapomniał(a):" msgid "Thanks for using our site!" -msgstr "Dzięki za korzystanie z naszej strony!" +msgstr "Dziękujemy za korzystanie naszej strony." #, python-format msgid "The %(site_name)s team" @@ -717,7 +707,7 @@ msgstr "" "wyślemy ci instrukcję opisującą sposób ustawienia nowego hasła." msgid "Email address:" -msgstr "Adres e-mail:" +msgstr "Adres email:" msgid "Reset my password" msgstr "Zresetuj moje hasło" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.mo index 2d9fe82..f0244f3 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.po index 6e40593..a0d0831 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/django.po @@ -6,16 +6,15 @@ # Marian Andre , 2013-2015,2017 # Martin Kosír, 2011 # Martin Tóth , 2017 -# Peter Kuma, 2021 # Peter Stríž , 2020 # Zbynek Drlik , 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 07:21+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2020-12-14 18:32+0000\n" +"Last-Translator: Peter Stríž \n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -196,33 +195,25 @@ msgstr "Nižšie môžete začať znovu editovať " msgid "" "The {name} “{obj}” was added successfully. You may add another {name} below." msgstr "" -"Objekt {name} \"{obj}\" bol úspešne pridaný. Môžete pridať ďaľší {name} " -"nižšie." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may edit it again below." msgstr "" -"Objekt {name} \"{obj}\" bol úspešne zmenený. Ďalšie zmeny môžete urobiť " -"nižšie." #, python-brace-format msgid "The {name} “{obj}” was added successfully. You may edit it again below." msgstr "" -"Objekt {name} \"{obj}\" bol úspešne pridaný. Ďalšie zmeny môžete urobiť " -"nižšie." #, python-brace-format msgid "" "The {name} “{obj}” was changed successfully. You may add another {name} " "below." msgstr "" -"Objekt {name} \"{obj}\" bol úspešne zmenený. Môžete pridať ďaľší {name} " -"nižšie." #, python-brace-format msgid "The {name} “{obj}” was changed successfully." -msgstr "Objekt {name} \"{obj}\" bol úspešne zmenený." +msgstr "" msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -236,11 +227,11 @@ msgstr "Nebola vybraná žiadna akcia." #, python-format msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "Objekt %(name)s \"%(obj)s\" bol úspešne vymazaný." +msgstr "" #, python-format msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "Objekt %(name)s s ID \"%(key)s\" neexistuje. Možno bol vymazaný?" +msgstr "" #, python-format msgid "Add %s" @@ -315,7 +306,7 @@ msgid "Page not found" msgstr "Stránka nenájdená" msgid "We’re sorry, but the requested page could not be found." -msgstr "Ľutujeme, ale požadovaná stránka nebola nájdená." +msgstr "" msgid "Home" msgstr "Domov" @@ -333,8 +324,6 @@ msgid "" "There’s been an error. It’s been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" -"Došlo k chybe. Chyba bola nahlásená správcovi webu prostredníctvom e-mailu a " -"zanedlho by mala byť odstránená. Ďakujeme za vašu trpezlivosť." msgid "Run the selected action" msgstr "Vykonať vybranú akciu" @@ -369,8 +358,6 @@ msgid "" "First, enter a username and password. Then, you’ll be able to edit more user " "options." msgstr "" -"Najskôr zadajte používateľské meno a heslo. Potom budete môcť upraviť viac " -"používateľských nastavení." msgid "Enter a username and password." msgstr "Zadajte používateľské meno a heslo." @@ -379,7 +366,7 @@ msgid "Change password" msgstr "Zmeniť heslo" msgid "Please correct the error below." -msgstr "Prosím, opravte chybu uvedenú nižšie." +msgstr "" msgid "Please correct the errors below." msgstr "Prosím, opravte chyby uvedené nižšie." @@ -520,9 +507,6 @@ msgid "" "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Niečo nie je v poriadku s vašou inštaláciou databázy. Zabezpečte, aby boli " -"vytvorené potrebné databázové tabuľky a taktiež zabezpečte, aby príslušný " -"používateľ mohol čítať databázu." #, python-format msgid "" @@ -536,12 +520,6 @@ msgid "Forgotten your password or username?" msgstr "Zabudli ste heslo alebo používateľské meno?" msgid "Toggle navigation" -msgstr "Zameniť navigáciu" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" msgstr "" msgid "Date/time" @@ -557,8 +535,6 @@ msgid "" "This object doesn’t have a change history. It probably wasn’t added via this " "admin site." msgstr "" -"Tento objekt nemá zoznam zmien. Pravdepodobne nebol pridaný prostredníctvom " -"tejto správcovskej stránky." msgid "Show all" msgstr "Zobraziť všetky" @@ -567,7 +543,7 @@ msgid "Save" msgstr "Uložiť" msgid "Popup closing…" -msgstr "Vyskakovacie okno sa zatvára..." +msgstr "" msgid "Search" msgstr "Vyhľadávanie" @@ -594,7 +570,7 @@ msgid "Save and continue editing" msgstr "Uložiť a pokračovať v úpravách" msgid "Save and view" -msgstr "Uložiť a zobraziť" +msgstr "" msgid "Close" msgstr "Zatvoriť" @@ -611,8 +587,8 @@ msgstr "Pridať ďalší %(model)s" msgid "Delete selected %(model)s" msgstr "Zmazať vybrané %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Ďakujeme za čas strávený na našich stránkach." msgid "Log in again" msgstr "Znova sa prihlásiť" @@ -627,8 +603,6 @@ msgid "" "Please enter your old password, for security’s sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" -"Z bezpečnostných dôvodov zadajte staré heslo a potom nové heslo dvakrát, aby " -"sme mohli overiť, že ste ho zadali správne." msgid "Change my password" msgstr "Zmeniť moje heslo" @@ -665,15 +639,11 @@ msgid "" "We’ve emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" -"Poslali sme vám e-mailom inštrukcie pre nastavenie hesla, ak existuje konto " -"so zadanou emailovou adresou. Čoskoro by ste ich mali dostať." msgid "" "If you don’t receive an email, please make sure you’ve entered the address " "you registered with, and check your spam folder." msgstr "" -"Ak vám nepríde e-mail, uistite sa, že ste zadali adresu, s ktorou ste sa " -"registrovali a skontrolujte svoj spamový priečinok." #, python-format msgid "" @@ -700,8 +670,6 @@ msgid "" "Forgotten your password? Enter your email address below, and we’ll email " "instructions for setting a new one." msgstr "" -"Zabudli ste heslo? Zadajte vašu e-mailovú adresu nižšie a my vám pošleme " -"inštrukcie pre nastavenie nového hesla." msgid "Email address:" msgstr "E-mailová adresa:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo index 9880b3a..798ad96 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po index 866afea..d703330 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/sk/LC_MESSAGES/djangojs.po @@ -3,18 +3,17 @@ # Translators: # Dimitris Glezos , 2012 # Jannis Leidel , 2011 -# 18f25ad6fa9930fc67cb11aca9d16a27, 2012 +# Juraj Bubniak , 2012 # Marian Andre , 2012,2015 # Martin Kosír, 2011 # Martin Tóth , 2017 -# Peter Kuma, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-24 21:14+0000\n" -"Last-Translator: Peter Kuma\n" +"POT-Creation-Date: 2018-05-17 11:50+0200\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Marian Andre \n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,20 +88,20 @@ msgstr "" "akciu, vaše zmeny budú stratené." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" "Vybrali ste akciu, ale neuložili ste jednotlivé polia. Prosím, uložte zmeny " "kliknutím na OK. Akciu budete musieť vykonať znova." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" "Vybrali ste akciu, ale neurobili ste žiadne zmeny v jednotlivých poliach. " -"Pravdepodobne ste chceli použiť tlačidlo Vykonať namiesto Uložiť." +"Pravdepodobne ste chceli použiť tlačidlo vykonať namiesto uložiť." msgid "Now" msgstr "Teraz" @@ -192,54 +191,6 @@ msgstr "november" msgid "December" msgstr "december" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "jan." - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "feb." - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "mar." - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "apr." - -msgctxt "abbrev. month May" -msgid "May" -msgstr "máj" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "jún" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "júl" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "aug." - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "sep." - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "okt." - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "nov." - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "dec." - msgctxt "one letter Sunday" msgid "S" msgstr "N" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.mo index 86babad..731bd86 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.po index a654e40..593ccc3 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/django.po @@ -1,13 +1,11 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Abbl Kto , 2021 # Oleksandr Chernihov , 2014 # Andriy Sokolovskiy , 2015 # Boryslav Larin , 2011 -# Denis Podlesniy , 2016 +# Денис Подлесный , 2016 # Igor Melnyk, 2014,2017 -# Illia Volochii , 2021 # Ivan Dmytrenko , 2019 # Jannis Leidel , 2011 # Kirill Gagarski , 2015 @@ -21,9 +19,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-02 22:21+0000\n" -"Last-Translator: Illia Volochii \n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-02-18 21:37+0000\n" +"Last-Translator: Ivan Dmytrenko \n" "Language-Team: Ukrainian (http://www.transifex.com/django/django/language/" "uk/)\n" "MIME-Version: 1.0\n" @@ -35,10 +33,6 @@ msgstr "" "100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " "(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Видалити обрані %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Успішно видалено %(count)d %(items)s." @@ -50,6 +44,10 @@ msgstr "Не вдається видалити %(name)s" msgid "Are you sure?" msgstr "Ви впевнені?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Видалити обрані %(verbose_name_plural)s" + msgid "Administration" msgstr "Адміністрування" @@ -86,12 +84,6 @@ msgstr "Без дати" msgid "Has date" msgstr "Має дату" -msgid "Empty" -msgstr "Порожні" - -msgid "Not empty" -msgstr "Непорожні" - #, python-format msgid "" "Please enter the correct %(username)s and password for a staff account. Note " @@ -149,23 +141,23 @@ msgid "log entries" msgstr "записи в журналі" #, python-format -msgid "Added “%(object)s”." +msgid "Added \"%(object)s\"." msgstr "Додано \"%(object)s\"." #, python-format -msgid "Changed “%(object)s” — %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" msgstr "Змінено \"%(object)s\" - %(changes)s" #, python-format -msgid "Deleted “%(object)s.”" -msgstr "" +msgid "Deleted \"%(object)s.\"" +msgstr "Видалено \"%(object)s.\"" msgid "LogEntry Object" msgstr "Об'єкт журнального запису" #, python-brace-format -msgid "Added {name} “{object}”." -msgstr "" +msgid "Added {name} \"{object}\"." +msgstr "Додано {name} \"{object}\"." msgid "Added." msgstr "Додано." @@ -174,16 +166,16 @@ msgid "and" msgstr "та" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "" +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "Змінені {fields} для {name} \"{object}\"." #, python-brace-format msgid "Changed {fields}." msgstr "Змінені {fields}." #, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "" +msgid "Deleted {name} \"{object}\"." +msgstr "Видалено {name} \"{object}\"." msgid "No fields changed." msgstr "Поля не змінені." @@ -191,39 +183,48 @@ msgstr "Поля не змінені." msgid "None" msgstr "Ніщо" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" +"Затисніть клавішу \"Control\", або \"Command\" на Mac, щоб обрати більше " +"однієї опції." #, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "" +msgid "The {name} \"{obj}\" was added successfully." +msgstr "{name} \"{obj}\" було додано успішно." msgid "You may edit it again below." msgstr "Ви можете відредагувати це знову." #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" +"{name} \"{obj}\" було додано успішно. Нижче Ви можете додати інше {name}." #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." msgstr "" +"{name} \"{obj}\" було змінено успішно. Нижче Ви можете редагувати його знову." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\" було додано успішно. Нижче Ви можете редагувати його знову." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} \"{obj}\" було змінено успішно. Нижче Ви можете додати інше {name}." + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "{name} \"{obj}\" було змінено успішно." msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -235,12 +236,12 @@ msgid "No action selected." msgstr "Дія не обрана." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "" +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" був видалений успішно." #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "%(name)s з ID \"%(key)s\" не існує. Можливо воно було видалене?" #, python-format msgid "Add %s" @@ -314,8 +315,8 @@ msgstr "Адміністрування %(app)s" msgid "Page not found" msgstr "Сторінка не знайдена" -msgid "We’re sorry, but the requested page could not be found." -msgstr "На жаль, запрошену сторінку не знайдено." +msgid "We're sorry, but the requested page could not be found." +msgstr "Нам шкода, але сторінка яку ви запросили, не знайдена." msgid "Home" msgstr "Домівка" @@ -330,9 +331,11 @@ msgid "Server Error (500)" msgstr "Помилка сервера (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" +"Виникла помилка. Адміністратора сайту повідомлено електронною поштою. " +"Помилка буде виправлена ​​найближчим часом. Дякуємо за ваше терпіння." msgid "Run the selected action" msgstr "Виконати обрану дію" @@ -350,23 +353,12 @@ msgstr "Обрати всі %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Скинути вибір" -#, python-format -msgid "Models in the %(name)s application" -msgstr "Моделі у %(name)s додатку" - -msgid "Add" -msgstr "Додати" - -msgid "View" -msgstr "Переглянути" - -msgid "You don’t have permission to view or edit anything." -msgstr "" - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" +"Спочатку введіть ім'я користувача і пароль. Після цього ви зможете " +"редагувати більше опцій користувача." msgid "Enter a username and password." msgstr "Введіть ім'я користувача і пароль." @@ -409,9 +401,6 @@ msgstr "Дивитися на сайті" msgid "Filter" msgstr "Відфільтрувати" -msgid "Clear all filters" -msgstr "Очистити всі фільтри" - msgid "Remove from sorting" msgstr "Видалити з сортування" @@ -454,7 +443,7 @@ msgstr "" msgid "Objects" msgstr "Об'єкти" -msgid "Yes, I’m sure" +msgid "Yes, I'm sure" msgstr "Так, я впевнений" msgid "No, take me back" @@ -488,6 +477,9 @@ msgstr "" "Ви впевнені, що хочете видалити вибрані %(objects_name)s? Всі вказані " "об'єкти та пов'язані з ними елементи будуть видалені:" +msgid "View" +msgstr "Переглянути" + msgid "Delete?" msgstr "Видалити?" @@ -498,6 +490,16 @@ msgstr "За %(filter_title)s" msgid "Summary" msgstr "Резюме" +#, python-format +msgid "Models in the %(name)s application" +msgstr "Моделі у %(name)s додатку" + +msgid "Add" +msgstr "Додати" + +msgid "You don't have permission to view or edit anything." +msgstr "У вас немає дозволу на перегляд чи редагування чого-небудь." + msgid "Recent actions" msgstr "Недавні дії" @@ -511,10 +513,12 @@ msgid "Unknown content" msgstr "Невідомий зміст" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" +"Щось не так з інсталяцією бази даних. Перевірте, що відповідні таблиці бази " +"даних створені та база даних може бути прочитана відповідним користувачем." #, python-format msgid "" @@ -528,15 +532,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "Забули пароль або ім'я користувача?" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "Дата/час" @@ -547,9 +542,11 @@ msgid "Action" msgstr "Дія" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" +"Цей об'єкт не має історії змін. Напевно, він був доданий не через цей сайт " +"адміністрування." msgid "Show all" msgstr "Показати всі" @@ -602,8 +599,8 @@ msgstr "Додати ще одну %(model)s" msgid "Delete selected %(model)s" msgstr "Видалити обрану %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Дякуємо за час, проведений сьогодні на сайті." msgid "Log in again" msgstr "Увійти знову" @@ -615,9 +612,11 @@ msgid "Your password was changed." msgstr "Ваш пароль було змінено." msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" +"Будь ласка введіть ваш старий пароль, задля безпеки, потім введіть ваш новий " +"пароль двічі для перевірки." msgid "Change my password" msgstr "Змінити мій пароль" @@ -652,14 +651,19 @@ msgstr "" "було вже використано. Будь ласка, замовте нове перевстановлення паролю." msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" +"На електронну адресу, яку ви ввели, надіслано ліста з інструкціями щодо " +"встановлення пароля, якщо обліковий запис з введеною адресою існує. Ви маєте " +"отримати його найближчим часом." msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" +"Якщо Ви не отримали електронного листа, будь ласка переконайтеся, що ввели " +"адресу яку вказували при реєстрації та перевірте папку зі спамом." #, python-format msgid "" @@ -672,8 +676,8 @@ msgstr "" msgid "Please go to the following page and choose a new password:" msgstr "Будь ласка, перейдіть на цю сторінку, та оберіть новий пароль:" -msgid "Your username, in case you’ve forgotten:" -msgstr "" +msgid "Your username, in case you've forgotten:" +msgstr "У разі, якщо ви забули, ваше ім'я користувача:" msgid "Thanks for using our site!" msgstr "Дякуємо за користування нашим сайтом!" @@ -683,9 +687,11 @@ msgid "The %(site_name)s team" msgstr "Команда сайту %(site_name)s " msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" +"Забули пароль? Введіть свою email-адресу нижче і ми вишлемо інструкції по " +"встановленню нового." msgid "Email address:" msgstr "Email адреса:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo index 20523ff..f70d010 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po index 40e60de..502c548 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/uk/LC_MESSAGES/djangojs.po @@ -3,18 +3,17 @@ # Translators: # Oleksandr Chernihov , 2014 # Boryslav Larin , 2011 -# Denis Podlesniy , 2016 -# Illia Volochii , 2021 +# Денис Подлесный , 2016 # Jannis Leidel , 2011 -# Panasoft, 2016 +# panasoft , 2016 # Sergey Lysach , 2011-2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-21 18:50+0000\n" -"Last-Translator: Illia Volochii \n" +"POT-Creation-Date: 2018-05-17 11:50+0200\n" +"PO-Revision-Date: 2017-09-19 16:41+0000\n" +"Last-Translator: Денис Подлесный \n" "Language-Team: Ukrainian (http://www.transifex.com/django/django/language/" "uk/)\n" "MIME-Version: 1.0\n" @@ -93,16 +92,20 @@ msgstr "" "незбережені зміни буде втрачено." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" +"Ви обрали дію, але не зберегли зміни в окремих полях. Будь ласка, натисніть " +"ОК, щоб зберегти. Вам доведеться повторно запустити дію." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" +"Ви обрали дію і не зробили жодних змін у полях. Ви, напевно, шукаєте кнопку " +"\"Виконати\", а не \"Зберегти\"." msgid "Now" msgstr "Зараз" @@ -192,54 +195,6 @@ msgstr "листопада" msgid "December" msgstr "грудня" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Січ." - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Лют." - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Берез." - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Квіт." - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Трав." - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Черв." - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Лип." - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Серп." - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Верес." - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Жовт." - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Листоп." - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Груд." - msgctxt "one letter Sunday" msgid "S" msgstr "Н" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.mo index 67c6d07..6fa3374 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.po index 80dd797..b5cdb16 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/django.po @@ -1,11 +1,9 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Alex Ibragimov, 2021 # Anvar Ulugov , 2020 # Bedilbek Khamidov , 2019 # Claude Paroz , 2019 -# Nuriddin Islamov, 2021 # Shukrullo Turgunov , 2021 # Sukhrobbek Ismatov , 2019 # Yet Sum , 2019 @@ -13,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-16 12:06+0000\n" -"Last-Translator: Alex Ibragimov\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-03-23 11:13+0000\n" +"Last-Translator: Shukrullo Turgunov \n" "Language-Team: Uzbek (http://www.transifex.com/django/django/language/uz/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -93,7 +91,7 @@ msgstr "Harakat:" #, python-format msgid "Add another %(verbose_name)s" -msgstr "Boshqa %(verbose_name)s qo‘shish" +msgstr "Boshqa%(verbose_name)s qo‘shish" msgid "Remove" msgstr "Olib tashlash" @@ -131,10 +129,10 @@ msgid "change message" msgstr "xabarni o'zgartirish" msgid "log entry" -msgstr "jurnal yozuvi" +msgstr "" msgid "log entries" -msgstr "jurnal yozuvlari" +msgstr "" #, python-format msgid "Added “%(object)s”." @@ -159,7 +157,7 @@ msgid "Added." msgstr "" msgid "and" -msgstr "va" +msgstr "" #, python-brace-format msgid "Changed {fields} for {name} “{object}”." @@ -235,14 +233,14 @@ msgstr "Qo'shish %s" #, python-format msgid "Change %s" -msgstr "%sni o'zgartirish" +msgstr "O'zgartirish %s" #, python-format msgid "View %s" msgstr "Ko'rish %s" msgid "Database error" -msgstr "Ma'lumotlar bazasi xatoligi yuz berdi" +msgstr "" #, python-format msgid "%(count)s %(name)s was changed successfully." @@ -377,7 +375,7 @@ msgstr "Chiqish" #, python-format msgid "Add %(name)s" -msgstr "%(name)sqo'shish" +msgstr "" msgid "History" msgstr "" @@ -492,12 +490,6 @@ msgid "Forgotten your password or username?" msgstr "" msgid "Toggle navigation" -msgstr "Navigatsiyani almashtirish" - -msgid "Start typing to filter…" -msgstr "" - -msgid "Filter navigation items" msgstr "" msgid "Date/time" @@ -562,7 +554,7 @@ msgstr "" msgid "Delete selected %(model)s" msgstr "" -msgid "Thanks for spending some quality time with the web site today." +msgid "Thanks for spending some quality time with the Web site today." msgstr "" msgid "Log in again" @@ -645,14 +637,14 @@ msgid "Email address:" msgstr "" msgid "Reset my password" -msgstr "Parolimni tiklash" +msgstr "" msgid "All dates" -msgstr "Barcha sanalar" +msgstr "" #, python-format msgid "Select %s" -msgstr "%sni tanlash" +msgstr "" #, python-format msgid "Select %s to change" @@ -663,16 +655,16 @@ msgid "Select %s to view" msgstr "" msgid "Date:" -msgstr "Sana:" +msgstr "" msgid "Time:" -msgstr "Vaqt:" +msgstr "" msgid "Lookup" -msgstr "Izlash" +msgstr "" msgid "Currently:" -msgstr "Hozirda:" +msgstr "" msgid "Change:" -msgstr "O'zgartirish:" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo index 7c922f6..914da08 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po index d731b22..05e4641 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/uz/LC_MESSAGES/djangojs.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Nuriddin Islamov, 2021 # Otabek Umurzakov , 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-12-15 16:52+0000\n" -"Last-Translator: Nuriddin Islamov\n" +"POT-Creation-Date: 2018-05-17 11:50+0200\n" +"PO-Revision-Date: 2019-12-13 21:48+0000\n" +"Last-Translator: Otabek Umurzakov \n" "Language-Team: Uzbek (http://www.transifex.com/django/django/language/uz/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -85,16 +84,22 @@ msgstr "" "o'zgarishlaringiz yo'qotiladi." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" +"Siz harakatni tanladingiz, lekin hali ham o'zgartirishlaringizni alohida " +"maydonlarga saqlamadingiz. Iltimos saqlash uchun OK ni bosing. Harakatni " +"qayta ishga tushurishingiz kerak bo'ladi." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" +"Siz harakatni tanladingiz va alohida maydonlarda hech qanday o'zgartirishlar " +"kiritmadingiz. Ehtimol siz Saqlash tugmasini emas, balki O'tish tugmasini " +"qidirmoqdasiz." msgid "Now" msgstr "Hozir" @@ -178,84 +183,36 @@ msgstr "Noyabr" msgid "December" msgstr "Dekabr" -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "" - msgctxt "one letter Sunday" msgid "S" -msgstr "Ya" +msgstr "S" msgctxt "one letter Monday" msgid "M" -msgstr "Du" +msgstr "M" msgctxt "one letter Tuesday" msgid "T" -msgstr "Se" +msgstr "T" msgctxt "one letter Wednesday" msgid "W" -msgstr "Ch" +msgstr "W" msgctxt "one letter Thursday" msgid "T" -msgstr "Pa" +msgstr "T" msgctxt "one letter Friday" msgid "F" -msgstr "Ju" +msgstr "F" msgctxt "one letter Saturday" msgid "S" -msgstr "Sh" +msgstr "S" msgid "Show" msgstr "Ko'rsatish" msgid "Hide" -msgstr "Bekitish" +msgstr "Yashirish" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.mo index 1091b6f..298498a 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.po index 60fe2ce..68fd78c 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django.po @@ -6,16 +6,15 @@ # Thanh Le Viet , 2013 # Tran , 2011 # Tran Van , 2011-2013,2016,2018 -# tinnguyen121221, 2021 # Vuong Nguyen , 2011 # xgenvn , 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-23 17:57+0000\n" -"Last-Translator: tinnguyen121221\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-01-18 00:36+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: Vietnamese (http://www.transifex.com/django/django/language/" "vi/)\n" "MIME-Version: 1.0\n" @@ -24,10 +23,6 @@ msgstr "" "Language: vi\n" "Plural-Forms: nplurals=1; plural=0;\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "Xóa các %(verbose_name_plural)s đã chọn" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Đã xóa thành công %(count)d %(items)s ." @@ -39,6 +34,10 @@ msgstr "Không thể xóa %(name)s" msgid "Are you sure?" msgstr "Bạn có chắc chắn không?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Xóa các %(verbose_name_plural)s đã chọn" + msgid "Administration" msgstr "Quản trị website" @@ -70,16 +69,10 @@ msgid "This year" msgstr "Năm nay" msgid "No date" -msgstr "Không có ngày" +msgstr "" msgid "Has date" -msgstr "Có ngày" - -msgid "Empty" -msgstr "Rỗng" - -msgid "Not empty" -msgstr "Không rỗng" +msgstr "" #, python-format msgid "" @@ -99,19 +92,19 @@ msgid "Remove" msgstr "Gỡ bỏ" msgid "Addition" -msgstr "Thêm" +msgstr "" msgid "Change" msgstr "Thay đổi" msgid "Deletion" -msgstr "Xóa" +msgstr "" msgid "action time" msgstr "Thời gian tác động" msgid "user" -msgstr "người dùng" +msgstr "" msgid "content type" msgstr "kiểu nội dung" @@ -137,23 +130,23 @@ msgid "log entries" msgstr "mục đăng nhập" #, python-format -msgid "Added “%(object)s”." -msgstr "Đã thêm “%(object)s”." +msgid "Added \"%(object)s\"." +msgstr "Thêm \"%(object)s\"." #, python-format -msgid "Changed “%(object)s” — %(changes)s" -msgstr "Đã thay đổi “%(object)s” — %(changes)s" +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Đã thay đổi \"%(object)s\" - %(changes)s" #, python-format -msgid "Deleted “%(object)s.”" -msgstr "Đã xóa “%(object)s.”" +msgid "Deleted \"%(object)s.\"" +msgstr "Đối tượng \"%(object)s.\" đã được xoá." msgid "LogEntry Object" msgstr "LogEntry Object" #, python-brace-format -msgid "Added {name} “{object}”." -msgstr "Đã thêm {name} “{object}”." +msgid "Added {name} \"{object}\"." +msgstr "{name} \"{object}\" đã được thêm vào." msgid "Added." msgstr "Được thêm." @@ -162,16 +155,16 @@ msgid "and" msgstr "và" #, python-brace-format -msgid "Changed {fields} for {name} “{object}”." -msgstr "Đã thay đổi {fields} cho {name} “{object}”." +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" #, python-brace-format msgid "Changed {fields}." -msgstr "Đã thay đổi {fields}." +msgstr "" #, python-brace-format -msgid "Deleted {name} “{object}”." -msgstr "Đã xóa {name} “{object}”." +msgid "Deleted {name} \"{object}\"." +msgstr "" msgid "No fields changed." msgstr "Không có trường nào thay đổi" @@ -179,46 +172,43 @@ msgstr "Không có trường nào thay đổi" msgid "None" msgstr "Không" -msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" -"Nhấn giữ phím “Control”, hoặc “Command” trên máy Mac, để chọn nhiều hơn một." +"Giữ phím \"Control\", hoặc \"Command\" trên Mac, để chọn nhiều hơn một." #, python-brace-format -msgid "The {name} “{obj}” was added successfully." -msgstr "{name} “{obj}” được thêm vào thành công." +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" msgid "You may edit it again below." -msgstr "Bạn có thể chỉnh sửa lại bên dưới." +msgstr "" #, python-brace-format msgid "" -"The {name} “{obj}” was added successfully. You may add another {name} below." -msgstr "" -"{name} “{obj}” được thêm vào thành công. Bạn có thể thêm một {name} khác bên " -"dưới." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may edit it again below." -msgstr "" -"{name} “{obj}” được chỉnh sửa thành công. Bạn có thể chỉnh sửa lại bên dưới." - -#, python-brace-format -msgid "The {name} “{obj}” was added successfully. You may edit it again below." -msgstr "" -"{name} “{obj}” được thêm vào thành công. Bạn có thể chỉnh sửa lại bên dưới." - -#, python-brace-format -msgid "" -"The {name} “{obj}” was changed successfully. You may add another {name} " +"The {name} \"{obj}\" was added successfully. You may add another {name} " "below." msgstr "" -"{name} “{obj}” được chỉnh sửa thành công. Bạn có thể thêm một {name} khác " -"bên dưới." #, python-brace-format -msgid "The {name} “{obj}” was changed successfully." -msgstr "{name} “{obj}” đã được thay đổi thành công." +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" msgid "" "Items must be selected in order to perform actions on them. No items have " @@ -231,12 +221,12 @@ msgid "No action selected." msgstr "Không có hoạt động nào được lựa chọn." #, python-format -msgid "The %(name)s “%(obj)s” was deleted successfully." -msgstr "%(name)s “%(obj)s” đã được xóa thành công." +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" đã được xóa thành công." #, python-format -msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" -msgstr "%(name)s với ID “%(key)s” không tồn tại. Có lẽ nó đã bị xóa?" +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" #, python-format msgid "Add %s" @@ -248,7 +238,7 @@ msgstr "Thay đổi %s" #, python-format msgid "View %s" -msgstr "Xem %s" +msgstr "" msgid "Database error" msgstr "Cơ sở dữ liệu bị lỗi" @@ -304,8 +294,8 @@ msgstr "Quản lý %(app)s" msgid "Page not found" msgstr "Không tìm thấy trang nào" -msgid "We’re sorry, but the requested page could not be found." -msgstr "Rất tiếc, không thể tìm thấy trang được yêu cầu." +msgid "We're sorry, but the requested page could not be found." +msgstr "Xin lỗi bạn! Trang mà bạn yêu cầu không tìm thấy." msgid "Home" msgstr "Trang chủ" @@ -320,7 +310,7 @@ msgid "Server Error (500)" msgstr "Lỗi máy chủ (500)" msgid "" -"There’s been an error. It’s been reported to the site administrators via " +"There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" "Có lỗi xảy ra. Lỗi sẽ được gửi đến quản trị website qua email và sẽ được " @@ -342,24 +332,11 @@ msgstr "Hãy chọn tất cả %(total_count)s %(module_name)s" msgid "Clear selection" msgstr "Xóa lựa chọn" -#, python-format -msgid "Models in the %(name)s application" -msgstr "Các mô models trong %(name)s" - -msgid "Add" -msgstr "Thêm vào" - -msgid "View" -msgstr "Xem" - -msgid "You don’t have permission to view or edit anything." -msgstr "Bạn không có quyền xem hoặc chỉnh sửa bất cứ gì." - msgid "" -"First, enter a username and password. Then, you’ll be able to edit more user " +"First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" -"Đầu tiên, điền tên đăng nhập và mật khẩu. Sau đó, bạn mới có thể chỉnh sửa " +"Đầu tiên, điền tên đăng nhập và mật khẩu. Sau đó bạn mới có thể chỉnh sửa " "nhiều hơn lựa chọn của người dùng." msgid "Enter a username and password." @@ -382,7 +359,7 @@ msgid "Welcome," msgstr "Chào mừng bạn," msgid "View site" -msgstr "Xem trang web" +msgstr "" msgid "Documentation" msgstr "Tài liệu" @@ -403,9 +380,6 @@ msgstr "Xem trên trang web" msgid "Filter" msgstr "Bộ lọc" -msgid "Clear all filters" -msgstr "Xóa tất cả bộ lọc" - msgid "Remove from sorting" msgstr "Bỏ khỏi sắp xếp" @@ -447,11 +421,11 @@ msgstr "" msgid "Objects" msgstr "Đối tượng" -msgid "Yes, I’m sure" -msgstr "Có, tôi chắc chắn" +msgid "Yes, I'm sure" +msgstr "Có, tôi chắc chắn." msgid "No, take me back" -msgstr "Không, đưa tôi trở lại" +msgstr "" msgid "Delete multiple objects" msgstr "Xóa nhiều đối tượng" @@ -481,6 +455,9 @@ msgstr "" "Bạn chắc chắn muốn xóa những lựa chọn %(objects_name)s? Tất cả những đối " "tượng sau và những đối tượng liên quan sẽ được xóa:" +msgid "View" +msgstr "" + msgid "Delete?" msgstr "Bạn muốn xóa?" @@ -489,13 +466,23 @@ msgid " By %(filter_title)s " msgstr "Bởi %(filter_title)s " msgid "Summary" -msgstr "Tóm tắt" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Các mô models trong %(name)s" + +msgid "Add" +msgstr "Thêm vào" + +msgid "You don't have permission to view or edit anything." +msgstr "" msgid "Recent actions" -msgstr "Hoạt động gần đây" +msgstr "" msgid "My actions" -msgstr "Hoạt động của tôi" +msgstr "" msgid "None available" msgstr "Không có sẵn" @@ -504,7 +491,7 @@ msgid "Unknown content" msgstr "Không biết nội dung" msgid "" -"Something’s wrong with your database installation. Make sure the appropriate " +"Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" @@ -523,15 +510,6 @@ msgstr "" msgid "Forgotten your password or username?" msgstr "Bạn quên mật khẩu hoặc tài khoản?" -msgid "Toggle navigation" -msgstr "" - -msgid "Start typing to filter…" -msgstr "Nhập để lọc..." - -msgid "Filter navigation items" -msgstr "" - msgid "Date/time" msgstr "Ngày/giờ" @@ -542,7 +520,7 @@ msgid "Action" msgstr "Hành động" msgid "" -"This object doesn’t have a change history. It probably wasn’t added via this " +"This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" "Đối tượng này không có một lịch sử thay đổi. Nó có lẽ đã không được thêm vào " @@ -555,7 +533,7 @@ msgid "Save" msgstr "Lưu lại" msgid "Popup closing…" -msgstr "Đang đóng cửa sổ popup ..." +msgstr "" msgid "Search" msgstr "Tìm kiếm" @@ -579,14 +557,14 @@ msgid "Save and continue editing" msgstr "Lưu và tiếp tục chỉnh sửa" msgid "Save and view" -msgstr "Lưu lại và xem" +msgstr "" msgid "Close" -msgstr "Đóng" +msgstr "" #, python-format msgid "Change selected %(model)s" -msgstr "Thay đổi %(model)s đã chọn" +msgstr "" #, python-format msgid "Add another %(model)s" @@ -596,8 +574,8 @@ msgstr "Thêm %(model)s khác" msgid "Delete selected %(model)s" msgstr "Xóa %(model)s đã chọn" -msgid "Thanks for spending some quality time with the web site today." -msgstr "Cảm ơn bạn đã dành thời gian với trang web." +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Cảm ơn bạn đã dành thời gian với website này" msgid "Log in again" msgstr "Đăng nhập lại" @@ -609,7 +587,7 @@ msgid "Your password was changed." msgstr "Mật khẩu của bạn đã được thay đổi" msgid "" -"Please enter your old password, for security’s sake, and then enter your new " +"Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" "Hãy nhập lại mật khẩu cũ và sau đó nhập mật khẩu mới hai lần để chúng tôi có " @@ -648,18 +626,16 @@ msgstr "" "vui lòng yêu cầu đặt lại mật khẩu mới." msgid "" -"We’ve emailed you instructions for setting your password, if an account " +"We've emailed you instructions for setting your password, if an account " "exists with the email you entered. You should receive them shortly." msgstr "" -"Chúng tôi đã gửi cho bạn hướng dẫn thiết lập mật khẩu của bạn qua email, nếu " -"tài khoản tồn tại với email bạn đã nhập. Bạn sẽ nhận được chúng sớm." msgid "" -"If you don’t receive an email, please make sure you’ve entered the address " +"If you don't receive an email, please make sure you've entered the address " "you registered with, and check your spam folder." msgstr "" -"Nếu bạn không nhận được email, hãy đảm bảo rằng bạn đã nhập địa chỉ mà bạn " -"đã đăng ký và kiểm tra thư mục spam của mình." +"Nếu bạn không nhận được email, hãy kiểm tra lại địa chỉ email mà bạn dùng để " +"đăng kí hoặc kiểm tra trong thư mục spam/rác" #, python-format msgid "" @@ -672,7 +648,7 @@ msgstr "" msgid "Please go to the following page and choose a new password:" msgstr "Hãy vào đường link dưới đây và chọn một mật khẩu mới" -msgid "Your username, in case you’ve forgotten:" +msgid "Your username, in case you've forgotten:" msgstr "Tên đăng nhập của bạn, trường hợp bạn quên nó:" msgid "Thanks for using our site!" @@ -683,7 +659,7 @@ msgid "The %(site_name)s team" msgstr "Đội của %(site_name)s" msgid "" -"Forgotten your password? Enter your email address below, and we’ll email " +"Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" "Quên mật khẩu? Nhập địa chỉ email vào ô dưới đây. Chúng tôi sẽ email cho bạn " @@ -708,7 +684,7 @@ msgstr "Chọn %s để thay đổi" #, python-format msgid "Select %s to view" -msgstr "Chọn %s để xem" +msgstr "" msgid "Date:" msgstr "Ngày:" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo index c9d57cd..7588ed6 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po index a3faf74..d2155ca 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/djangojs.po @@ -4,16 +4,15 @@ # Jannis Leidel , 2011 # Tran , 2011 # Tran Van , 2013 -# tinnguyen121221, 2021 # Vuong Nguyen , 2011 # xgenvn , 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-12-23 17:25+0000\n" -"Last-Translator: tinnguyen121221\n" +"POT-Creation-Date: 2016-05-17 23:12+0200\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Jannis Leidel \n" "Language-Team: Vietnamese (http://www.transifex.com/django/django/language/" "vi/)\n" "MIME-Version: 1.0\n" @@ -85,35 +84,21 @@ msgstr "" "chỉnh sửa chưa được lưu sẽ bị mất." msgid "" -"You have selected an action, but you haven’t saved your changes to " -"individual fields yet. Please click OK to save. You’ll need to re-run the " +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" -"Bạn đã chọn một hành động, nhưng bạn chưa lưu các thay đổi trên các trường. " -"Vui lòng bấm OK để lưu lại. Bạn sẽ cần chạy lại hành dộng." +"Bạn đã lựa chọn một hành động, nhưng bạn không lưu thay đổi của bạn đến các " +"lĩnh vực cá nhân được nêu ra. Xin vui lòng click OK để lưu lại. Bạn sẽ cần " +"phải chạy lại các hành động." msgid "" -"You have selected an action, and you haven’t made any changes on individual " -"fields. You’re probably looking for the Go button rather than the Save " +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" -"Bạn đã chọn một hành động và bạn đã không thực hiện bất kỳ thay đổi nào trên " -"các trường. Có lẽ bạn nên bấm nút Đi đến hơn là nút Lưu lại." - -msgid "Now" -msgstr "Bây giờ" - -msgid "Midnight" -msgstr "Nửa đêm" - -msgid "6 a.m." -msgstr "6 giờ sáng" - -msgid "Noon" -msgstr "Buổi trưa" - -msgid "6 p.m." -msgstr "6 giờ chiều" +"Bạn đã lựa chọn một hành động, và bạn đã không thực hiện bất kỳ thay đổi nào " +"trên các trường. Có lẽ bạn đang tìm kiếm nút bấm Go thay vì nút bấm Save." #, javascript-format msgid "Note: You are %s hour ahead of server time." @@ -128,12 +113,27 @@ msgid_plural "Note: You are %s hours behind server time." msgstr[0] "" "Lưu ý: Hiện tại bạn đang thấy thời gian sau %s giờ so với thời gian máy chủ." +msgid "Now" +msgstr "Bây giờ" + msgid "Choose a Time" -msgstr "Chọn Thời gian" +msgstr "" msgid "Choose a time" msgstr "Chọn giờ" +msgid "Midnight" +msgstr "Nửa đêm" + +msgid "6 a.m." +msgstr "6 giờ sáng" + +msgid "Noon" +msgstr "Buổi trưa" + +msgid "6 p.m." +msgstr "" + msgid "Cancel" msgstr "Hủy bỏ" @@ -141,7 +141,7 @@ msgid "Today" msgstr "Hôm nay" msgid "Choose a Date" -msgstr "Chọn Ngày" +msgstr "" msgid "Yesterday" msgstr "Hôm qua" @@ -150,116 +150,68 @@ msgid "Tomorrow" msgstr "Ngày mai" msgid "January" -msgstr "Tháng Một" +msgstr "" msgid "February" -msgstr "Tháng Hai" +msgstr "" msgid "March" -msgstr "Tháng Ba" +msgstr "" msgid "April" -msgstr "Tháng Tư" +msgstr "" msgid "May" -msgstr "Tháng Năm" +msgstr "" msgid "June" -msgstr "Tháng Sáu" +msgstr "" msgid "July" -msgstr "Tháng Bảy" +msgstr "" msgid "August" -msgstr "Tháng Tám" +msgstr "" msgid "September" -msgstr "Tháng Chín" +msgstr "" msgid "October" -msgstr "Tháng Mười" +msgstr "" msgid "November" -msgstr "Tháng Mười Một" +msgstr "" msgid "December" -msgstr "Tháng Mười Hai" - -msgctxt "abbrev. month January" -msgid "Jan" -msgstr "Tháng Một" - -msgctxt "abbrev. month February" -msgid "Feb" -msgstr "Tháng Hai" - -msgctxt "abbrev. month March" -msgid "Mar" -msgstr "Tháng Ba" - -msgctxt "abbrev. month April" -msgid "Apr" -msgstr "Tháng Tư" - -msgctxt "abbrev. month May" -msgid "May" -msgstr "Tháng Năm" - -msgctxt "abbrev. month June" -msgid "Jun" -msgstr "Tháng Sáu" - -msgctxt "abbrev. month July" -msgid "Jul" -msgstr "Tháng Bảy" - -msgctxt "abbrev. month August" -msgid "Aug" -msgstr "Tháng Tám" - -msgctxt "abbrev. month September" -msgid "Sep" -msgstr "Tháng Chín" - -msgctxt "abbrev. month October" -msgid "Oct" -msgstr "Tháng Mười" - -msgctxt "abbrev. month November" -msgid "Nov" -msgstr "Tháng Mười Một" - -msgctxt "abbrev. month December" -msgid "Dec" -msgstr "Tháng Mười Hai" +msgstr "" msgctxt "one letter Sunday" msgid "S" -msgstr "CN" +msgstr "" msgctxt "one letter Monday" msgid "M" -msgstr "2" +msgstr "" msgctxt "one letter Tuesday" msgid "T" -msgstr "3" +msgstr "" msgctxt "one letter Wednesday" msgid "W" -msgstr "4" +msgstr "" msgctxt "one letter Thursday" msgid "T" -msgstr "5" +msgstr "" msgctxt "one letter Friday" msgid "F" -msgstr "6" +msgstr "" msgctxt "one letter Saturday" msgid "S" -msgstr "7" +msgstr "" msgid "Show" msgstr "Hiện ra" diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo index 98b5a7f..1d6d75a 100644 Binary files a/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po index ad7fd6c..773847b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# lanbla , 2021 # Brian Wang , 2018 # Fulong Sun , 2016 # Jannis Leidel , 2011 @@ -21,7 +20,7 @@ # yf zhan , 2018 # dykai , 2019 # ced773123cfad7b4e8b79ca80f736af9, 2012 -# fangjiaqi77 <370358679@qq.com>, 2020 +# 嘉琪 方 <370358679@qq.com>, 2020 # Kevin Sze , 2012 # 考证 李 , 2020 # 雨翌 , 2016 @@ -30,9 +29,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 03:10+0000\n" -"Last-Translator: lanbla \n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-09-11 01:47+0000\n" +"Last-Translator: 嘉琪 方 <370358679@qq.com>\n" "Language-Team: Chinese (China) (http://www.transifex.com/django/django/" "language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -41,10 +40,6 @@ msgstr "" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" -#, python-format -msgid "Delete selected %(verbose_name_plural)s" -msgstr "删除所选的 %(verbose_name_plural)s" - #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "成功删除了 %(count)d 个 %(items)s" @@ -56,6 +51,10 @@ msgstr "无法删除 %(name)s" msgid "Are you sure?" msgstr "你确定吗?" +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "删除所选的 %(verbose_name_plural)s" + msgid "Administration" msgstr "管理" @@ -527,12 +526,6 @@ msgstr "忘记了您的密码或用户名?" msgid "Toggle navigation" msgstr "切换导航" -msgid "Start typing to filter…" -msgstr "开始输入以筛选..." - -msgid "Filter navigation items" -msgstr "筛选导航项目" - msgid "Date/time" msgstr "日期/时间" @@ -595,8 +588,8 @@ msgstr "增加另一个 %(model)s" msgid "Delete selected %(model)s" msgstr "取消选中 %(model)s" -msgid "Thanks for spending some quality time with the web site today." -msgstr "感谢您今天与本网站共度一段高品质时光。" +msgid "Thanks for spending some quality time with the Web site today." +msgstr "感谢您今天在本站花费了一些宝贵时间。" msgid "Log in again" msgstr "重新登录" diff --git a/venv/Lib/site-packages/django/contrib/admin/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/admin/migrations/0001_initial.py index d02e128..78cd7a7 100644 --- a/venv/Lib/site-packages/django/contrib/admin/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/admin/migrations/0001_initial.py @@ -7,70 +7,41 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("contenttypes", "__first__"), + ('contenttypes', '__first__'), ] operations = [ migrations.CreateModel( - name="LogEntry", + name='LogEntry', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ( - "action_time", - models.DateTimeField(auto_now=True, verbose_name="action time"), - ), - ( - "object_id", - models.TextField(null=True, verbose_name="object id", blank=True), - ), - ( - "object_repr", - models.CharField(max_length=200, verbose_name="object repr"), - ), - ( - "action_flag", - models.PositiveSmallIntegerField(verbose_name="action flag"), - ), - ( - "change_message", - models.TextField(verbose_name="change message", blank=True), - ), - ( - "content_type", - models.ForeignKey( - on_delete=models.SET_NULL, - blank=True, - null=True, - to="contenttypes.ContentType", - verbose_name="content type", - ), - ), - ( - "user", - models.ForeignKey( - to=settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - verbose_name="user", - ), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('action_time', models.DateTimeField(auto_now=True, verbose_name='action time')), + ('object_id', models.TextField(null=True, verbose_name='object id', blank=True)), + ('object_repr', models.CharField(max_length=200, verbose_name='object repr')), + ('action_flag', models.PositiveSmallIntegerField(verbose_name='action flag')), + ('change_message', models.TextField(verbose_name='change message', blank=True)), + ('content_type', models.ForeignKey( + to_field='id', + on_delete=models.SET_NULL, + blank=True, null=True, + to='contenttypes.ContentType', + verbose_name='content type', + )), + ('user', models.ForeignKey( + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + verbose_name='user', + )), ], options={ - "ordering": ["-action_time"], - "db_table": "django_admin_log", - "verbose_name": "log entry", - "verbose_name_plural": "log entries", + 'ordering': ['-action_time'], + 'db_table': 'django_admin_log', + 'verbose_name': 'log entry', + 'verbose_name_plural': 'log entries', }, bases=(models.Model,), managers=[ - ("objects", django.contrib.admin.models.LogEntryManager()), + ('objects', django.contrib.admin.models.LogEntryManager()), ], ), ] diff --git a/venv/Lib/site-packages/django/contrib/admin/migrations/0002_logentry_remove_auto_add.py b/venv/Lib/site-packages/django/contrib/admin/migrations/0002_logentry_remove_auto_add.py index 4e83978..a2b1916 100644 --- a/venv/Lib/site-packages/django/contrib/admin/migrations/0002_logentry_remove_auto_add.py +++ b/venv/Lib/site-packages/django/contrib/admin/migrations/0002_logentry_remove_auto_add.py @@ -5,16 +5,16 @@ from django.utils import timezone class Migration(migrations.Migration): dependencies = [ - ("admin", "0001_initial"), + ('admin', '0001_initial'), ] # No database changes; removes auto_add and adds default/editable. operations = [ migrations.AlterField( - model_name="logentry", - name="action_time", + model_name='logentry', + name='action_time', field=models.DateTimeField( - verbose_name="action time", + verbose_name='action time', default=timezone.now, editable=False, ), diff --git a/venv/Lib/site-packages/django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py b/venv/Lib/site-packages/django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py index 59b2231..a041a9d 100644 --- a/venv/Lib/site-packages/django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py +++ b/venv/Lib/site-packages/django/contrib/admin/migrations/0003_logentry_add_action_flag_choices.py @@ -4,17 +4,17 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("admin", "0002_logentry_remove_auto_add"), + ('admin', '0002_logentry_remove_auto_add'), ] # No database changes; adds choices to action_flag. operations = [ migrations.AlterField( - model_name="logentry", - name="action_flag", + model_name='logentry', + name='action_flag', field=models.PositiveSmallIntegerField( - choices=[(1, "Addition"), (2, "Change"), (3, "Deletion")], - verbose_name="action flag", + choices=[(1, 'Addition'), (2, 'Change'), (3, 'Deletion')], + verbose_name='action flag', ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/admin/models.py b/venv/Lib/site-packages/django/contrib/admin/models.py index 7ff0410..a0fbb02 100644 --- a/venv/Lib/site-packages/django/contrib/admin/models.py +++ b/venv/Lib/site-packages/django/contrib/admin/models.py @@ -7,32 +7,23 @@ from django.db import models from django.urls import NoReverseMatch, reverse from django.utils import timezone from django.utils.text import get_text_list -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ ADDITION = 1 CHANGE = 2 DELETION = 3 ACTION_FLAG_CHOICES = ( - (ADDITION, _("Addition")), - (CHANGE, _("Change")), - (DELETION, _("Deletion")), + (ADDITION, _('Addition')), + (CHANGE, _('Change')), + (DELETION, _('Deletion')), ) class LogEntryManager(models.Manager): use_in_migrations = True - def log_action( - self, - user_id, - content_type_id, - object_id, - object_repr, - action_flag, - change_message="", - ): + def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): if isinstance(change_message, list): change_message = json.dumps(change_message) return self.model.objects.create( @@ -47,55 +38,51 @@ class LogEntryManager(models.Manager): class LogEntry(models.Model): action_time = models.DateTimeField( - _("action time"), + _('action time'), default=timezone.now, editable=False, ) user = models.ForeignKey( settings.AUTH_USER_MODEL, models.CASCADE, - verbose_name=_("user"), + verbose_name=_('user'), ) content_type = models.ForeignKey( ContentType, models.SET_NULL, - verbose_name=_("content type"), - blank=True, - null=True, - ) - object_id = models.TextField(_("object id"), blank=True, null=True) - # Translators: 'repr' means representation - # (https://docs.python.org/library/functions.html#repr) - object_repr = models.CharField(_("object repr"), max_length=200) - action_flag = models.PositiveSmallIntegerField( - _("action flag"), choices=ACTION_FLAG_CHOICES + verbose_name=_('content type'), + blank=True, null=True, ) + object_id = models.TextField(_('object id'), blank=True, null=True) + # Translators: 'repr' means representation (https://docs.python.org/library/functions.html#repr) + object_repr = models.CharField(_('object repr'), max_length=200) + action_flag = models.PositiveSmallIntegerField(_('action flag'), choices=ACTION_FLAG_CHOICES) # change_message is either a string or a JSON structure - change_message = models.TextField(_("change message"), blank=True) + change_message = models.TextField(_('change message'), blank=True) objects = LogEntryManager() class Meta: - verbose_name = _("log entry") - verbose_name_plural = _("log entries") - db_table = "django_admin_log" - ordering = ["-action_time"] + verbose_name = _('log entry') + verbose_name_plural = _('log entries') + db_table = 'django_admin_log' + ordering = ['-action_time'] def __repr__(self): return str(self.action_time) def __str__(self): if self.is_addition(): - return gettext("Added “%(object)s”.") % {"object": self.object_repr} + return gettext('Added “%(object)s”.') % {'object': self.object_repr} elif self.is_change(): - return gettext("Changed “%(object)s” — %(changes)s") % { - "object": self.object_repr, - "changes": self.get_change_message(), + return gettext('Changed “%(object)s” — %(changes)s') % { + 'object': self.object_repr, + 'changes': self.get_change_message(), } elif self.is_deletion(): - return gettext("Deleted “%(object)s.”") % {"object": self.object_repr} + return gettext('Deleted “%(object)s.”') % {'object': self.object_repr} - return gettext("LogEntry Object") + return gettext('LogEntry Object') def is_addition(self): return self.action_flag == ADDITION @@ -111,62 +98,38 @@ class LogEntry(models.Model): If self.change_message is a JSON structure, interpret it as a change string, properly translated. """ - if self.change_message and self.change_message[0] == "[": + if self.change_message and self.change_message[0] == '[': try: change_message = json.loads(self.change_message) except json.JSONDecodeError: return self.change_message messages = [] for sub_message in change_message: - if "added" in sub_message: - if sub_message["added"]: - sub_message["added"]["name"] = gettext( - sub_message["added"]["name"] - ) - messages.append( - gettext("Added {name} “{object}”.").format( - **sub_message["added"] - ) - ) + if 'added' in sub_message: + if sub_message['added']: + sub_message['added']['name'] = gettext(sub_message['added']['name']) + messages.append(gettext('Added {name} “{object}”.').format(**sub_message['added'])) else: - messages.append(gettext("Added.")) + messages.append(gettext('Added.')) - elif "changed" in sub_message: - sub_message["changed"]["fields"] = get_text_list( - [ - gettext(field_name) - for field_name in sub_message["changed"]["fields"] - ], - gettext("and"), + elif 'changed' in sub_message: + sub_message['changed']['fields'] = get_text_list( + [gettext(field_name) for field_name in sub_message['changed']['fields']], gettext('and') ) - if "name" in sub_message["changed"]: - sub_message["changed"]["name"] = gettext( - sub_message["changed"]["name"] - ) - messages.append( - gettext("Changed {fields} for {name} “{object}”.").format( - **sub_message["changed"] - ) - ) + if 'name' in sub_message['changed']: + sub_message['changed']['name'] = gettext(sub_message['changed']['name']) + messages.append(gettext('Changed {fields} for {name} “{object}”.').format( + **sub_message['changed'] + )) else: - messages.append( - gettext("Changed {fields}.").format( - **sub_message["changed"] - ) - ) + messages.append(gettext('Changed {fields}.').format(**sub_message['changed'])) - elif "deleted" in sub_message: - sub_message["deleted"]["name"] = gettext( - sub_message["deleted"]["name"] - ) - messages.append( - gettext("Deleted {name} “{object}”.").format( - **sub_message["deleted"] - ) - ) + elif 'deleted' in sub_message: + sub_message['deleted']['name'] = gettext(sub_message['deleted']['name']) + messages.append(gettext('Deleted {name} “{object}”.').format(**sub_message['deleted'])) - change_message = " ".join(msg[0].upper() + msg[1:] for msg in messages) - return change_message or gettext("No fields changed.") + change_message = ' '.join(msg[0].upper() + msg[1:] for msg in messages) + return change_message or gettext('No fields changed.') else: return self.change_message @@ -179,10 +142,7 @@ class LogEntry(models.Model): Return the admin URL to edit the object represented by this log entry. """ if self.content_type and self.object_id: - url_name = "admin:%s_%s_change" % ( - self.content_type.app_label, - self.content_type.model, - ) + url_name = 'admin:%s_%s_change' % (self.content_type.app_label, self.content_type.model) try: return reverse(url_name, args=(quote(self.object_id),)) except NoReverseMatch: diff --git a/venv/Lib/site-packages/django/contrib/admin/options.py b/venv/Lib/site-packages/django/contrib/admin/options.py index 20e6b3a..6eba48f 100644 --- a/venv/Lib/site-packages/django/contrib/admin/options.py +++ b/venv/Lib/site-packages/django/contrib/admin/options.py @@ -1,7 +1,8 @@ import copy import json +import operator import re -from functools import partial, update_wrapper +from functools import partial, reduce, update_wrapper from urllib.parse import quote as urlquote from django import forms @@ -9,42 +10,30 @@ from django.conf import settings from django.contrib import messages from django.contrib.admin import helpers, widgets from django.contrib.admin.checks import ( - BaseModelAdminChecks, - InlineModelAdminChecks, - ModelAdminChecks, + BaseModelAdminChecks, InlineModelAdminChecks, ModelAdminChecks, ) from django.contrib.admin.decorators import display from django.contrib.admin.exceptions import DisallowedModelAdminToField from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.utils import ( - NestedObjects, - construct_change_message, - flatten_fieldsets, - get_deleted_objects, - lookup_spawns_duplicates, - model_format_dict, - model_ngettext, - quote, - unquote, + NestedObjects, construct_change_message, flatten_fieldsets, + get_deleted_objects, lookup_needs_distinct, model_format_dict, + model_ngettext, quote, unquote, +) +from django.contrib.admin.widgets import ( + AutocompleteSelect, AutocompleteSelectMultiple, ) -from django.contrib.admin.widgets import AutocompleteSelect, AutocompleteSelectMultiple from django.contrib.auth import get_permission_codename from django.core.exceptions import ( - FieldDoesNotExist, - FieldError, - PermissionDenied, - ValidationError, + FieldDoesNotExist, FieldError, PermissionDenied, ValidationError, ) from django.core.paginator import Paginator from django.db import models, router, transaction from django.db.models.constants import LOOKUP_SEP from django.forms.formsets import DELETION_FIELD_NAME, all_valid from django.forms.models import ( - BaseInlineFormSet, - inlineformset_factory, - modelform_defines_fields, - modelform_factory, - modelformset_factory, + BaseInlineFormSet, inlineformset_factory, modelform_defines_fields, + modelform_factory, modelformset_factory, ) from django.forms.widgets import CheckboxSelectMultiple, SelectMultiple from django.http import HttpResponseRedirect @@ -56,19 +45,14 @@ from django.utils.html import format_html from django.utils.http import urlencode from django.utils.safestring import mark_safe from django.utils.text import ( - capfirst, - format_lazy, - get_text_list, - smart_split, - unescape_string_literal, + capfirst, format_lazy, get_text_list, smart_split, unescape_string_literal, ) -from django.utils.translation import gettext as _ -from django.utils.translation import ngettext +from django.utils.translation import gettext as _, ngettext from django.views.decorators.csrf import csrf_protect from django.views.generic import RedirectView -IS_POPUP_VAR = "_popup" -TO_FIELD_VAR = "_to_field" +IS_POPUP_VAR = '_popup' +TO_FIELD_VAR = '_to_field' HORIZONTAL, VERTICAL = 1, 2 @@ -78,12 +62,11 @@ def get_content_type_for_model(obj): # Since this module gets imported in the application's root package, # it cannot import models from other applications at the module level. from django.contrib.contenttypes.models import ContentType - return ContentType.objects.get_for_model(obj, for_concrete_model=False) def get_ul_class(radio_style): - return "radiolist" if radio_style == VERTICAL else "radiolist inline" + return 'radiolist' if radio_style == VERTICAL else 'radiolist inline' class IncorrectLookupParameters(Exception): @@ -95,20 +78,20 @@ class IncorrectLookupParameters(Exception): FORMFIELD_FOR_DBFIELD_DEFAULTS = { models.DateTimeField: { - "form_class": forms.SplitDateTimeField, - "widget": widgets.AdminSplitDateTime, + 'form_class': forms.SplitDateTimeField, + 'widget': widgets.AdminSplitDateTime }, - models.DateField: {"widget": widgets.AdminDateWidget}, - models.TimeField: {"widget": widgets.AdminTimeWidget}, - models.TextField: {"widget": widgets.AdminTextareaWidget}, - models.URLField: {"widget": widgets.AdminURLFieldWidget}, - models.IntegerField: {"widget": widgets.AdminIntegerFieldWidget}, - models.BigIntegerField: {"widget": widgets.AdminBigIntegerFieldWidget}, - models.CharField: {"widget": widgets.AdminTextInputWidget}, - models.ImageField: {"widget": widgets.AdminFileWidget}, - models.FileField: {"widget": widgets.AdminFileWidget}, - models.EmailField: {"widget": widgets.AdminEmailInputWidget}, - models.UUIDField: {"widget": widgets.AdminUUIDInputWidget}, + models.DateField: {'widget': widgets.AdminDateWidget}, + models.TimeField: {'widget': widgets.AdminTimeWidget}, + models.TextField: {'widget': widgets.AdminTextareaWidget}, + models.URLField: {'widget': widgets.AdminURLFieldWidget}, + models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget}, + models.BigIntegerField: {'widget': widgets.AdminBigIntegerFieldWidget}, + models.CharField: {'widget': widgets.AdminTextInputWidget}, + models.ImageField: {'widget': widgets.AdminFileWidget}, + models.FileField: {'widget': widgets.AdminFileWidget}, + models.EmailField: {'widget': widgets.AdminEmailInputWidget}, + models.UUIDField: {'widget': widgets.AdminUUIDInputWidget}, } csrf_protect_m = method_decorator(csrf_protect) @@ -178,28 +161,17 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # rendered output. formfield can be None if it came from a # OneToOneField with parent_link=True or a M2M intermediary. if formfield and db_field.name not in self.raw_id_fields: - related_modeladmin = self.admin_site._registry.get( - db_field.remote_field.model - ) + related_modeladmin = self.admin_site._registry.get(db_field.remote_field.model) wrapper_kwargs = {} if related_modeladmin: wrapper_kwargs.update( can_add_related=related_modeladmin.has_add_permission(request), - can_change_related=related_modeladmin.has_change_permission( - request - ), - can_delete_related=related_modeladmin.has_delete_permission( - request - ), - can_view_related=related_modeladmin.has_view_permission( - request - ), + can_change_related=related_modeladmin.has_change_permission(request), + can_delete_related=related_modeladmin.has_delete_permission(request), + can_view_related=related_modeladmin.has_view_permission(request), ) formfield.widget = widgets.RelatedFieldWidgetWrapper( - formfield.widget, - db_field.remote_field, - self.admin_site, - **wrapper_kwargs, + formfield.widget, db_field.remote_field, self.admin_site, **wrapper_kwargs ) return formfield @@ -221,15 +193,14 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # If the field is named as a radio_field, use a RadioSelect if db_field.name in self.radio_fields: # Avoid stomping on custom widget/choices arguments. - if "widget" not in kwargs: - kwargs["widget"] = widgets.AdminRadioSelect( - attrs={ - "class": get_ul_class(self.radio_fields[db_field.name]), - } - ) - if "choices" not in kwargs: - kwargs["choices"] = db_field.get_choices( - include_blank=db_field.blank, blank_choice=[("", _("None"))] + if 'widget' not in kwargs: + kwargs['widget'] = widgets.AdminRadioSelect(attrs={ + 'class': get_ul_class(self.radio_fields[db_field.name]), + }) + if 'choices' not in kwargs: + kwargs['choices'] = db_field.get_choices( + include_blank=db_field.blank, + blank_choice=[('', _('None'))] ) return db_field.formfield(**kwargs) @@ -243,38 +214,30 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): if related_admin is not None: ordering = related_admin.get_ordering(request) if ordering is not None and ordering != (): - return db_field.remote_field.model._default_manager.using(db).order_by( - *ordering - ) + return db_field.remote_field.model._default_manager.using(db).order_by(*ordering) return None def formfield_for_foreignkey(self, db_field, request, **kwargs): """ Get a form Field for a ForeignKey. """ - db = kwargs.get("using") + db = kwargs.get('using') - if "widget" not in kwargs: + if 'widget' not in kwargs: if db_field.name in self.get_autocomplete_fields(request): - kwargs["widget"] = AutocompleteSelect( - db_field, self.admin_site, using=db - ) + kwargs['widget'] = AutocompleteSelect(db_field, self.admin_site, using=db) elif db_field.name in self.raw_id_fields: - kwargs["widget"] = widgets.ForeignKeyRawIdWidget( - db_field.remote_field, self.admin_site, using=db - ) + kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.remote_field, self.admin_site, using=db) elif db_field.name in self.radio_fields: - kwargs["widget"] = widgets.AdminRadioSelect( - attrs={ - "class": get_ul_class(self.radio_fields[db_field.name]), - } - ) - kwargs["empty_label"] = _("None") if db_field.blank else None + kwargs['widget'] = widgets.AdminRadioSelect(attrs={ + 'class': get_ul_class(self.radio_fields[db_field.name]), + }) + kwargs['empty_label'] = _('None') if db_field.blank else None - if "queryset" not in kwargs: + if 'queryset' not in kwargs: queryset = self.get_field_queryset(db, db_field, request) if queryset is not None: - kwargs["queryset"] = queryset + kwargs['queryset'] = queryset return db_field.formfield(**kwargs) @@ -286,42 +249,38 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # a field in admin. if not db_field.remote_field.through._meta.auto_created: return None - db = kwargs.get("using") + db = kwargs.get('using') - if "widget" not in kwargs: + if 'widget' not in kwargs: autocomplete_fields = self.get_autocomplete_fields(request) if db_field.name in autocomplete_fields: - kwargs["widget"] = AutocompleteSelectMultiple( + kwargs['widget'] = AutocompleteSelectMultiple( db_field, self.admin_site, using=db, ) elif db_field.name in self.raw_id_fields: - kwargs["widget"] = widgets.ManyToManyRawIdWidget( + kwargs['widget'] = widgets.ManyToManyRawIdWidget( db_field.remote_field, self.admin_site, using=db, ) elif db_field.name in [*self.filter_vertical, *self.filter_horizontal]: - kwargs["widget"] = widgets.FilteredSelectMultiple( - db_field.verbose_name, db_field.name in self.filter_vertical + kwargs['widget'] = widgets.FilteredSelectMultiple( + db_field.verbose_name, + db_field.name in self.filter_vertical ) - if "queryset" not in kwargs: + if 'queryset' not in kwargs: queryset = self.get_field_queryset(db, db_field, request) if queryset is not None: - kwargs["queryset"] = queryset + kwargs['queryset'] = queryset form_field = db_field.formfield(**kwargs) - if isinstance(form_field.widget, SelectMultiple) and not isinstance( - form_field.widget, (CheckboxSelectMultiple, AutocompleteSelectMultiple) - ): - msg = _( - "Hold down “Control”, or “Command” on a Mac, to select more than one." - ) + if (isinstance(form_field.widget, SelectMultiple) and + not isinstance(form_field.widget, (CheckboxSelectMultiple, AutocompleteSelectMultiple))): + msg = _('Hold down “Control”, or “Command” on a Mac, to select more than one.') help_text = form_field.help_text - form_field.help_text = ( - format_lazy("{} {}", help_text, msg) if help_text else msg - ) + form_field.help_text = format_lazy('{} {}', help_text, msg) if help_text else msg return form_field def get_autocomplete_fields(self, request): @@ -337,15 +296,12 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): if callable(self.view_on_site): return self.view_on_site(obj) - elif hasattr(obj, "get_absolute_url"): + elif hasattr(obj, 'get_absolute_url'): # use the ContentType lookup if view_on_site is True - return reverse( - "admin:view_on_site", - kwargs={ - "content_type_id": get_content_type_for_model(obj).pk, - "object_id": obj.pk, - }, - ) + return reverse('admin:view_on_site', kwargs={ + 'content_type_id': get_content_type_for_model(obj).pk, + 'object_id': obj.pk + }) def get_empty_value_display(self): """ @@ -378,7 +334,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): """ if self.fieldsets: return self.fieldsets - return [(None, {"fields": self.get_fields(request, obj)})] + return [(None, {'fields': self.get_fields(request, obj)})] def get_inlines(self, request, obj): """Hook for specifying custom inlines.""" @@ -416,11 +372,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): def get_sortable_by(self, request): """Hook for specifying which fields can be sorted in the changelist.""" - return ( - self.sortable_by - if self.sortable_by is not None - else self.get_list_display(request) - ) + return self.sortable_by if self.sortable_by is not None else self.get_list_display(request) def lookup_allowed(self, lookup, value): from django.contrib.admin.filters import SimpleListFilter @@ -433,9 +385,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # As ``limit_choices_to`` can be a callable, invoke it here. if callable(fk_lookup): fk_lookup = fk_lookup() - if (lookup, value) in widgets.url_params_from_lookup_dict( - fk_lookup - ).items(): + if (lookup, value) in widgets.url_params_from_lookup_dict(fk_lookup).items(): return True relation_parts = [] @@ -450,12 +400,10 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # It is allowed to filter on values that would be found from local # model anyways. For example, if you filter on employee__department__id, # then the id value would be found already from employee__department_id. - if not prev_field or ( - prev_field.is_relation - and field not in prev_field.get_path_info()[-1].target_fields - ): + if not prev_field or (prev_field.is_relation and + field not in prev_field.get_path_info()[-1].target_fields): relation_parts.append(part) - if not getattr(field, "get_path_info", None): + if not getattr(field, 'get_path_info', None): # This is not a relational field, so further parts # must be transforms. break @@ -467,9 +415,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): return True valid_lookups = {self.date_hierarchy} for filter_item in self.list_filter: - if isinstance(filter_item, type) and issubclass( - filter_item, SimpleListFilter - ): + if isinstance(filter_item, type) and issubclass(filter_item, SimpleListFilter): valid_lookups.add(filter_item.parameter_name) elif isinstance(filter_item, (list, tuple)): valid_lookups.add(filter_item[0]) @@ -479,7 +425,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): # Is it a valid relational lookup? return not { LOOKUP_SEP.join(relation_parts), - LOOKUP_SEP.join(relation_parts + [part]), + LOOKUP_SEP.join(relation_parts + [part]) }.isdisjoint(valid_lookups) def to_field_allowed(self, request, to_field): @@ -514,18 +460,15 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): registered_models.add(inline.model) related_objects = ( - f - for f in opts.get_fields(include_hidden=True) + f for f in opts.get_fields(include_hidden=True) if (f.auto_created and not f.concrete) ) for related_object in related_objects: related_model = related_object.related_model remote_field = related_object.field.remote_field - if ( - any(issubclass(model, related_model) for model in registered_models) - and hasattr(remote_field, "get_related_field") - and remote_field.get_related_field() == field - ): + if (any(issubclass(model, related_model) for model in registered_models) and + hasattr(remote_field, 'get_related_field') and + remote_field.get_related_field() == field): return True return False @@ -536,7 +479,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): Can be overridden by the user in subclasses. """ opts = self.opts - codename = get_permission_codename("add", opts) + codename = get_permission_codename('add', opts) return request.user.has_perm("%s.%s" % (opts.app_label, codename)) def has_change_permission(self, request, obj=None): @@ -551,7 +494,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): request has permission to change *any* object of the given type. """ opts = self.opts - codename = get_permission_codename("change", opts) + codename = get_permission_codename('change', opts) return request.user.has_perm("%s.%s" % (opts.app_label, codename)) def has_delete_permission(self, request, obj=None): @@ -566,7 +509,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): request has permission to delete *any* object of the given type. """ opts = self.opts - codename = get_permission_codename("delete", opts) + codename = get_permission_codename('delete', opts) return request.user.has_perm("%s.%s" % (opts.app_label, codename)) def has_view_permission(self, request, obj=None): @@ -581,16 +524,15 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): any object of the given type. """ opts = self.opts - codename_view = get_permission_codename("view", opts) - codename_change = get_permission_codename("change", opts) - return request.user.has_perm( - "%s.%s" % (opts.app_label, codename_view) - ) or request.user.has_perm("%s.%s" % (opts.app_label, codename_change)) + codename_view = get_permission_codename('view', opts) + codename_change = get_permission_codename('change', opts) + return ( + request.user.has_perm('%s.%s' % (opts.app_label, codename_view)) or + request.user.has_perm('%s.%s' % (opts.app_label, codename_change)) + ) def has_view_or_change_permission(self, request, obj=None): - return self.has_view_permission(request, obj) or self.has_change_permission( - request, obj - ) + return self.has_view_permission(request, obj) or self.has_change_permission(request, obj) def has_module_permission(self, request): """ @@ -609,7 +551,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): class ModelAdmin(BaseModelAdmin): """Encapsulate all admin options and functionality for a given model.""" - list_display = ("__str__",) + list_display = ('__str__',) list_display_links = () list_filter = () list_select_related = False @@ -617,7 +559,6 @@ class ModelAdmin(BaseModelAdmin): list_max_show_all = 200 list_editable = () search_fields = () - search_help_text = None date_hierarchy = None save_as = False save_as_continue = True @@ -652,22 +593,14 @@ class ModelAdmin(BaseModelAdmin): def __str__(self): return "%s.%s" % (self.model._meta.app_label, self.__class__.__name__) - def __repr__(self): - return ( - f"<{self.__class__.__qualname__}: model={self.model.__qualname__} " - f"site={self.admin_site!r}>" - ) - def get_inline_instances(self, request, obj=None): inline_instances = [] for inline_class in self.get_inlines(request, obj): inline = inline_class(self.model, self.admin_site) if request: - if not ( - inline.has_view_or_change_permission(request, obj) - or inline.has_add_permission(request, obj) - or inline.has_delete_permission(request, obj) - ): + if not (inline.has_view_or_change_permission(request, obj) or + inline.has_add_permission(request, obj) or + inline.has_delete_permission(request, obj)): continue if not inline.has_add_permission(request, obj): inline.max_num = 0 @@ -681,40 +614,21 @@ class ModelAdmin(BaseModelAdmin): def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) - wrapper.model_admin = self return update_wrapper(wrapper, view) info = self.model._meta.app_label, self.model._meta.model_name return [ - path("", wrap(self.changelist_view), name="%s_%s_changelist" % info), - path("add/", wrap(self.add_view), name="%s_%s_add" % info), - path( - "/history/", - wrap(self.history_view), - name="%s_%s_history" % info, - ), - path( - "/delete/", - wrap(self.delete_view), - name="%s_%s_delete" % info, - ), - path( - "/change/", - wrap(self.change_view), - name="%s_%s_change" % info, - ), + path('', wrap(self.changelist_view), name='%s_%s_changelist' % info), + path('add/', wrap(self.add_view), name='%s_%s_add' % info), + path('/history/', wrap(self.history_view), name='%s_%s_history' % info), + path('/delete/', wrap(self.delete_view), name='%s_%s_delete' % info), + path('/change/', wrap(self.change_view), name='%s_%s_change' % info), # For backwards compatibility (was the change url before 1.9) - path( - "/", - wrap( - RedirectView.as_view( - pattern_name="%s:%s_%s_change" - % ((self.admin_site.name,) + info) - ) - ), - ), + path('/', wrap(RedirectView.as_view( + pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info) + ))), ] @property @@ -723,18 +637,18 @@ class ModelAdmin(BaseModelAdmin): @property def media(self): - extra = "" if settings.DEBUG else ".min" + extra = '' if settings.DEBUG else '.min' js = [ - "vendor/jquery/jquery%s.js" % extra, - "jquery.init.js", - "core.js", - "admin/RelatedObjectLookups.js", - "actions.js", - "urlify.js", - "prepopulate.js", - "vendor/xregexp/xregexp%s.js" % extra, + 'vendor/jquery/jquery%s.js' % extra, + 'jquery.init.js', + 'core.js', + 'admin/RelatedObjectLookups.js', + 'actions.js', + 'urlify.js', + 'prepopulate.js', + 'vendor/xregexp/xregexp%s.js' % extra, ] - return forms.Media(js=["admin/js/%s" % url for url in js]) + return forms.Media(js=['admin/js/%s' % url for url in js]) def get_model_perms(self, request): """ @@ -743,10 +657,10 @@ class ModelAdmin(BaseModelAdmin): for each of those actions. """ return { - "add": self.has_add_permission(request), - "change": self.has_change_permission(request), - "delete": self.has_delete_permission(request), - "view": self.has_view_permission(request), + 'add': self.has_add_permission(request), + 'change': self.has_change_permission(request), + 'delete': self.has_delete_permission(request), + 'view': self.has_view_permission(request), } def _get_form_for_get_fields(self, request, obj): @@ -757,8 +671,8 @@ class ModelAdmin(BaseModelAdmin): Return a Form class for use in the admin add view. This is used by add_view and change_view. """ - if "fields" in kwargs: - fields = kwargs.pop("fields") + if 'fields' in kwargs: + fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) excluded = self.get_exclude(request, obj) @@ -767,13 +681,9 @@ class ModelAdmin(BaseModelAdmin): exclude.extend(readonly_fields) # Exclude all fields if it's a change form and the user doesn't have # the change permission. - if ( - change - and hasattr(request, "user") - and not self.has_change_permission(request, obj) - ): + if change and hasattr(request, 'user') and not self.has_change_permission(request, obj): exclude.extend(fields) - if excluded is None and hasattr(self.form, "_meta") and self.form._meta.exclude: + if excluded is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # ModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) @@ -782,29 +692,25 @@ class ModelAdmin(BaseModelAdmin): exclude = exclude or None # Remove declared form fields which are in readonly_fields. - new_attrs = dict.fromkeys( - f for f in readonly_fields if f in self.form.declared_fields - ) + new_attrs = dict.fromkeys(f for f in readonly_fields if f in self.form.declared_fields) form = type(self.form.__name__, (self.form,), new_attrs) defaults = { - "form": form, - "fields": fields, - "exclude": exclude, - "formfield_callback": partial(self.formfield_for_dbfield, request=request), + 'form': form, + 'fields': fields, + 'exclude': exclude, + 'formfield_callback': partial(self.formfield_for_dbfield, request=request), **kwargs, } - if defaults["fields"] is None and not modelform_defines_fields( - defaults["form"] - ): - defaults["fields"] = forms.ALL_FIELDS + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = forms.ALL_FIELDS try: return modelform_factory(self.model, **defaults) except FieldError as e: raise FieldError( - "%s. Check fields/fieldsets/exclude attributes of class %s." + '%s. Check fields/fieldsets/exclude attributes of class %s.' % (e, self.__class__.__name__) ) @@ -813,7 +719,6 @@ class ModelAdmin(BaseModelAdmin): Return the ChangeList class for use on the changelist page. """ from django.contrib.admin.views.main import ChangeList - return ChangeList def get_changelist_instance(self, request): @@ -825,7 +730,7 @@ class ModelAdmin(BaseModelAdmin): list_display_links = self.get_list_display_links(request, list_display) # Add the action checkboxes if any actions are available. if self.get_actions(request): - list_display = ["action_checkbox", *list_display] + list_display = ['action_checkbox', *list_display] sortable_by = self.get_sortable_by(request) ChangeList = self.get_changelist(request) return ChangeList( @@ -842,7 +747,6 @@ class ModelAdmin(BaseModelAdmin): self.list_editable, self, sortable_by, - self.search_help_text, ) def get_object(self, request, object_id, from_field=None): @@ -853,9 +757,7 @@ class ModelAdmin(BaseModelAdmin): """ queryset = self.get_queryset(request) model = queryset.model - field = ( - model._meta.pk if from_field is None else model._meta.get_field(from_field) - ) + field = model._meta.pk if from_field is None else model._meta.get_field(from_field) try: object_id = field.to_python(object_id) return queryset.get(**{field.name: object_id}) @@ -867,13 +769,11 @@ class ModelAdmin(BaseModelAdmin): Return a Form class for use in the Formset on the changelist page. """ defaults = { - "formfield_callback": partial(self.formfield_for_dbfield, request=request), + 'formfield_callback': partial(self.formfield_for_dbfield, request=request), **kwargs, } - if defaults.get("fields") is None and not modelform_defines_fields( - defaults.get("form") - ): - defaults["fields"] = forms.ALL_FIELDS + if defaults.get('fields') is None and not modelform_defines_fields(defaults.get('form')): + defaults['fields'] = forms.ALL_FIELDS return modelform_factory(self.model, **defaults) @@ -883,15 +783,12 @@ class ModelAdmin(BaseModelAdmin): is used. """ defaults = { - "formfield_callback": partial(self.formfield_for_dbfield, request=request), + 'formfield_callback': partial(self.formfield_for_dbfield, request=request), **kwargs, } return modelformset_factory( - self.model, - self.get_changelist_form(request), - extra=0, - fields=self.list_editable, - **defaults, + self.model, self.get_changelist_form(request), extra=0, + fields=self.list_editable, **defaults ) def get_formsets_with_inlines(self, request, obj=None): @@ -901,46 +798,42 @@ class ModelAdmin(BaseModelAdmin): for inline in self.get_inline_instances(request, obj): yield inline.get_formset(request, obj), inline - def get_paginator( - self, request, queryset, per_page, orphans=0, allow_empty_first_page=True - ): + def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True): return self.paginator(queryset, per_page, orphans, allow_empty_first_page) - def log_addition(self, request, obj, message): + def log_addition(self, request, object, message): """ Log that an object has been successfully added. The default implementation creates an admin LogEntry object. """ from django.contrib.admin.models import ADDITION, LogEntry - return LogEntry.objects.log_action( user_id=request.user.pk, - content_type_id=get_content_type_for_model(obj).pk, - object_id=obj.pk, - object_repr=str(obj), + content_type_id=get_content_type_for_model(object).pk, + object_id=object.pk, + object_repr=str(object), action_flag=ADDITION, change_message=message, ) - def log_change(self, request, obj, message): + def log_change(self, request, object, message): """ Log that an object has been successfully changed. The default implementation creates an admin LogEntry object. """ from django.contrib.admin.models import CHANGE, LogEntry - return LogEntry.objects.log_action( user_id=request.user.pk, - content_type_id=get_content_type_for_model(obj).pk, - object_id=obj.pk, - object_repr=str(obj), + content_type_id=get_content_type_for_model(object).pk, + object_id=object.pk, + object_repr=str(object), action_flag=CHANGE, change_message=message, ) - def log_deletion(self, request, obj, object_repr): + def log_deletion(self, request, object, object_repr): """ Log that an object will be deleted. Note that this method must be called before the deletion. @@ -948,11 +841,10 @@ class ModelAdmin(BaseModelAdmin): The default implementation creates an admin LogEntry object. """ from django.contrib.admin.models import DELETION, LogEntry - return LogEntry.objects.log_action( user_id=request.user.pk, - content_type_id=get_content_type_for_model(obj).pk, - object_id=obj.pk, + content_type_id=get_content_type_for_model(object).pk, + object_id=object.pk, object_repr=object_repr, action_flag=DELETION, ) @@ -966,7 +858,7 @@ class ModelAdmin(BaseModelAdmin): @staticmethod def _get_action_description(func, name): - return getattr(func, "short_description", capfirst(name.replace("_", " "))) + return getattr(func, 'short_description', capfirst(name.replace('_', ' '))) def _get_base_actions(self): """Return the list of actions, prior to any request-based filtering.""" @@ -991,11 +883,11 @@ class ModelAdmin(BaseModelAdmin): filtered_actions = [] for action in actions: callable = action[0] - if not hasattr(callable, "allowed_permissions"): + if not hasattr(callable, 'allowed_permissions'): filtered_actions.append(action) continue permission_checks = ( - getattr(self, "has_%s_permission" % permission) + getattr(self, 'has_%s_permission' % permission) for permission in callable.allowed_permissions ) if any(has_permission(request) for has_permission in permission_checks): @@ -1065,11 +957,7 @@ class ModelAdmin(BaseModelAdmin): on the changelist. The list_display parameter is the list of fields returned by get_list_display(). """ - if ( - self.list_display_links - or self.list_display_links is None - or not list_display - ): + if self.list_display_links or self.list_display_links is None or not list_display: return self.list_display_links else: # Use only the first item in list_display as link @@ -1103,11 +991,11 @@ class ModelAdmin(BaseModelAdmin): """ # Apply keyword searches. def construct_search(field_name): - if field_name.startswith("^"): + if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] - elif field_name.startswith("="): + elif field_name.startswith('='): return "%s__iexact" % field_name[1:] - elif field_name.startswith("@"): + elif field_name.startswith('@'): return "%s__search" % field_name[1:] # Use field_name if it includes a lookup. opts = queryset.model._meta @@ -1115,7 +1003,7 @@ class ModelAdmin(BaseModelAdmin): # Go through the fields, following all relations. prev_field = None for path_part in lookup_fields: - if path_part == "pk": + if path_part == 'pk': path_part = opts.pk.name try: field = opts.get_field(path_part) @@ -1125,7 +1013,7 @@ class ModelAdmin(BaseModelAdmin): return field_name else: prev_field = field - if hasattr(field, "get_path_info"): + if hasattr(field, 'get_path_info'): # Update opts to follow the relation. opts = field.get_path_info()[-1].to_opts # Otherwise, use the field with icontains. @@ -1134,19 +1022,16 @@ class ModelAdmin(BaseModelAdmin): may_have_duplicates = False search_fields = self.get_search_fields(request) if search_fields and search_term: - orm_lookups = [ - construct_search(str(search_field)) for search_field in search_fields - ] + orm_lookups = [construct_search(str(search_field)) + for search_field in search_fields] for bit in smart_split(search_term): if bit.startswith(('"', "'")) and bit[0] == bit[-1]: bit = unescape_string_literal(bit) - or_queries = models.Q( - *((orm_lookup, bit) for orm_lookup in orm_lookups), - _connector=models.Q.OR, - ) - queryset = queryset.filter(or_queries) + or_queries = [models.Q(**{orm_lookup: bit}) + for orm_lookup in orm_lookups] + queryset = queryset.filter(reduce(operator.or_, or_queries)) may_have_duplicates |= any( - lookup_spawns_duplicates(self.opts, search_spec) + lookup_needs_distinct(self.opts, search_spec) for search_spec in orm_lookups ) return queryset, may_have_duplicates @@ -1158,19 +1043,16 @@ class ModelAdmin(BaseModelAdmin): match = request.resolver_match if self.preserve_filters and match: opts = self.model._meta - current_url = "%s:%s" % (match.app_name, match.url_name) - changelist_url = "admin:%s_%s_changelist" % ( - opts.app_label, - opts.model_name, - ) + current_url = '%s:%s' % (match.app_name, match.url_name) + changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name) if current_url == changelist_url: preserved_filters = request.GET.urlencode() else: - preserved_filters = request.GET.get("_changelist_filters") + preserved_filters = request.GET.get('_changelist_filters') if preserved_filters: - return urlencode({"_changelist_filters": preserved_filters}) - return "" + return urlencode({'_changelist_filters': preserved_filters}) + return '' def construct_change_message(self, request, form, formsets, add=False): """ @@ -1178,9 +1060,8 @@ class ModelAdmin(BaseModelAdmin): """ return construct_change_message(form, formsets, add) - def message_user( - self, request, message, level=messages.INFO, extra_tags="", fail_silently=False - ): + def message_user(self, request, message, level=messages.INFO, extra_tags='', + fail_silently=False): """ Send a message to the user. The default implementation posts a message using the django.contrib.messages backend. @@ -1196,15 +1077,13 @@ class ModelAdmin(BaseModelAdmin): level = getattr(messages.constants, level.upper()) except AttributeError: levels = messages.constants.DEFAULT_TAGS.values() - levels_repr = ", ".join("`%s`" % level for level in levels) + levels_repr = ', '.join('`%s`' % level for level in levels) raise ValueError( - "Bad message level string: `%s`. Possible values are: %s" + 'Bad message level string: `%s`. Possible values are: %s' % (level, levels_repr) ) - messages.add_message( - request, level, message, extra_tags=extra_tags, fail_silently=fail_silently - ) + messages.add_message(request, level, message, extra_tags=extra_tags, fail_silently=fail_silently) def save_form(self, request, form, change): """ @@ -1247,53 +1126,40 @@ class ModelAdmin(BaseModelAdmin): for formset in formsets: self.save_formset(request, form, formset, change=change) - def render_change_form( - self, request, context, add=False, change=False, form_url="", obj=None - ): + def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): opts = self.model._meta app_label = opts.app_label preserved_filters = self.get_preserved_filters(request) - form_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, form_url - ) + form_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, form_url) view_on_site_url = self.get_view_on_site_url(obj) has_editable_inline_admin_formsets = False - for inline in context["inline_admin_formsets"]: - if ( - inline.has_add_permission - or inline.has_change_permission - or inline.has_delete_permission - ): + for inline in context['inline_admin_formsets']: + if inline.has_add_permission or inline.has_change_permission or inline.has_delete_permission: has_editable_inline_admin_formsets = True break - context.update( - { - "add": add, - "change": change, - "has_view_permission": self.has_view_permission(request, obj), - "has_add_permission": self.has_add_permission(request), - "has_change_permission": self.has_change_permission(request, obj), - "has_delete_permission": self.has_delete_permission(request, obj), - "has_editable_inline_admin_formsets": ( - has_editable_inline_admin_formsets - ), - "has_file_field": context["adminform"].form.is_multipart() - or any( - admin_formset.formset.is_multipart() - for admin_formset in context["inline_admin_formsets"] - ), - "has_absolute_url": view_on_site_url is not None, - "absolute_url": view_on_site_url, - "form_url": form_url, - "opts": opts, - "content_type_id": get_content_type_for_model(self.model).pk, - "save_as": self.save_as, - "save_on_top": self.save_on_top, - "to_field_var": TO_FIELD_VAR, - "is_popup_var": IS_POPUP_VAR, - "app_label": app_label, - } - ) + context.update({ + 'add': add, + 'change': change, + 'has_view_permission': self.has_view_permission(request, obj), + 'has_add_permission': self.has_add_permission(request), + 'has_change_permission': self.has_change_permission(request, obj), + 'has_delete_permission': self.has_delete_permission(request, obj), + 'has_editable_inline_admin_formsets': has_editable_inline_admin_formsets, + 'has_file_field': context['adminform'].form.is_multipart() or any( + admin_formset.formset.is_multipart() + for admin_formset in context['inline_admin_formsets'] + ), + 'has_absolute_url': view_on_site_url is not None, + 'absolute_url': view_on_site_url, + 'form_url': form_url, + 'opts': opts, + 'content_type_id': get_content_type_for_model(self.model).pk, + 'save_as': self.save_as, + 'save_on_top': self.save_on_top, + 'to_field_var': TO_FIELD_VAR, + 'is_popup_var': IS_POPUP_VAR, + 'app_label': app_label, + }) if add and self.add_form_template is not None: form_template = self.add_form_template else: @@ -1301,16 +1167,11 @@ class ModelAdmin(BaseModelAdmin): request.current_app = self.admin_site.name - return TemplateResponse( - request, - form_template - or [ - "admin/%s/%s/change_form.html" % (app_label, opts.model_name), - "admin/%s/change_form.html" % app_label, - "admin/change_form.html", - ], - context, - ) + return TemplateResponse(request, form_template or [ + "admin/%s/%s/change_form.html" % (app_label, opts.model_name), + "admin/%s/change_form.html" % app_label, + "admin/change_form.html" + ], context) def response_add(self, request, obj, post_url_continue=None): """ @@ -1319,7 +1180,7 @@ class ModelAdmin(BaseModelAdmin): opts = obj._meta preserved_filters = self.get_preserved_filters(request) obj_url = reverse( - "admin:%s_%s_change" % (opts.app_label, opts.model_name), + 'admin:%s_%s_change' % (opts.app_label, opts.model_name), args=(quote(obj.pk),), current_app=self.admin_site.name, ) @@ -1329,8 +1190,8 @@ class ModelAdmin(BaseModelAdmin): else: obj_repr = str(obj) msg_dict = { - "name": opts.verbose_name, - "obj": obj_repr, + 'name': opts.verbose_name, + 'obj': obj_repr, } # Here, we distinguish between different save types by checking for # the presence of keys in request.POST. @@ -1342,62 +1203,49 @@ class ModelAdmin(BaseModelAdmin): else: attr = obj._meta.pk.attname value = obj.serializable_value(attr) - popup_response_data = json.dumps( - { - "value": str(value), - "obj": str(obj), - } - ) - return TemplateResponse( - request, - self.popup_response_template - or [ - "admin/%s/%s/popup_response.html" - % (opts.app_label, opts.model_name), - "admin/%s/popup_response.html" % opts.app_label, - "admin/popup_response.html", - ], - { - "popup_response_data": popup_response_data, - }, - ) + popup_response_data = json.dumps({ + 'value': str(value), + 'obj': str(obj), + }) + return TemplateResponse(request, self.popup_response_template or [ + 'admin/%s/%s/popup_response.html' % (opts.app_label, opts.model_name), + 'admin/%s/popup_response.html' % opts.app_label, + 'admin/popup_response.html', + ], { + 'popup_response_data': popup_response_data, + }) elif "_continue" in request.POST or ( - # Redirecting after "Save as new". - "_saveasnew" in request.POST - and self.save_as_continue - and self.has_change_permission(request, obj) + # Redirecting after "Save as new". + "_saveasnew" in request.POST and self.save_as_continue and + self.has_change_permission(request, obj) ): - msg = _("The {name} “{obj}” was added successfully.") + msg = _('The {name} “{obj}” was added successfully.') if self.has_change_permission(request, obj): - msg += " " + _("You may edit it again below.") + msg += ' ' + _('You may edit it again below.') self.message_user(request, format_html(msg, **msg_dict), messages.SUCCESS) if post_url_continue is None: post_url_continue = obj_url post_url_continue = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, - post_url_continue, + {'preserved_filters': preserved_filters, 'opts': opts}, + post_url_continue ) return HttpResponseRedirect(post_url_continue) elif "_addanother" in request.POST: msg = format_html( - _( - "The {name} “{obj}” was added successfully. You may add another " - "{name} below." - ), - **msg_dict, + _('The {name} “{obj}” was added successfully. You may add another {name} below.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) redirect_url = request.path - redirect_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, redirect_url - ) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) return HttpResponseRedirect(redirect_url) else: msg = format_html( - _("The {name} “{obj}” was added successfully."), **msg_dict + _('The {name} “{obj}” was added successfully.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) return self.response_post_save_add(request, obj) @@ -1411,92 +1259,68 @@ class ModelAdmin(BaseModelAdmin): opts = obj._meta to_field = request.POST.get(TO_FIELD_VAR) attr = str(to_field) if to_field else opts.pk.attname - value = request.resolver_match.kwargs["object_id"] + value = request.resolver_match.kwargs['object_id'] new_value = obj.serializable_value(attr) - popup_response_data = json.dumps( - { - "action": "change", - "value": str(value), - "obj": str(obj), - "new_value": str(new_value), - } - ) - return TemplateResponse( - request, - self.popup_response_template - or [ - "admin/%s/%s/popup_response.html" - % (opts.app_label, opts.model_name), - "admin/%s/popup_response.html" % opts.app_label, - "admin/popup_response.html", - ], - { - "popup_response_data": popup_response_data, - }, - ) + popup_response_data = json.dumps({ + 'action': 'change', + 'value': str(value), + 'obj': str(obj), + 'new_value': str(new_value), + }) + return TemplateResponse(request, self.popup_response_template or [ + 'admin/%s/%s/popup_response.html' % (opts.app_label, opts.model_name), + 'admin/%s/popup_response.html' % opts.app_label, + 'admin/popup_response.html', + ], { + 'popup_response_data': popup_response_data, + }) opts = self.model._meta preserved_filters = self.get_preserved_filters(request) msg_dict = { - "name": opts.verbose_name, - "obj": format_html('{}', urlquote(request.path), obj), + 'name': opts.verbose_name, + 'obj': format_html('{}', urlquote(request.path), obj), } if "_continue" in request.POST: msg = format_html( - _( - "The {name} “{obj}” was changed successfully. You may edit it " - "again below." - ), - **msg_dict, + _('The {name} “{obj}” was changed successfully. You may edit it again below.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) redirect_url = request.path - redirect_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, redirect_url - ) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) return HttpResponseRedirect(redirect_url) elif "_saveasnew" in request.POST: msg = format_html( - _( - "The {name} “{obj}” was added successfully. You may edit it again " - "below." - ), - **msg_dict, + _('The {name} “{obj}” was added successfully. You may edit it again below.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) - redirect_url = reverse( - "admin:%s_%s_change" % (opts.app_label, opts.model_name), - args=(obj.pk,), - current_app=self.admin_site.name, - ) - redirect_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, redirect_url - ) + redirect_url = reverse('admin:%s_%s_change' % + (opts.app_label, opts.model_name), + args=(obj.pk,), + current_app=self.admin_site.name) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) return HttpResponseRedirect(redirect_url) elif "_addanother" in request.POST: msg = format_html( - _( - "The {name} “{obj}” was changed successfully. You may add another " - "{name} below." - ), - **msg_dict, + _('The {name} “{obj}” was changed successfully. You may add another {name} below.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) - redirect_url = reverse( - "admin:%s_%s_add" % (opts.app_label, opts.model_name), - current_app=self.admin_site.name, - ) - redirect_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, redirect_url - ) + redirect_url = reverse('admin:%s_%s_add' % + (opts.app_label, opts.model_name), + current_app=self.admin_site.name) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) return HttpResponseRedirect(redirect_url) else: msg = format_html( - _("The {name} “{obj}” was changed successfully."), **msg_dict + _('The {name} “{obj}” was changed successfully.'), + **msg_dict ) self.message_user(request, msg, messages.SUCCESS) return self.response_post_save_change(request, obj) @@ -1504,16 +1328,14 @@ class ModelAdmin(BaseModelAdmin): def _response_post_save(self, request, obj): opts = self.model._meta if self.has_view_or_change_permission(request): - post_url = reverse( - "admin:%s_%s_changelist" % (opts.app_label, opts.model_name), - current_app=self.admin_site.name, - ) + post_url = reverse('admin:%s_%s_changelist' % + (opts.app_label, opts.model_name), + current_app=self.admin_site.name) preserved_filters = self.get_preserved_filters(request) - post_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, post_url - ) + post_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url) else: - post_url = reverse("admin:index", current_app=self.admin_site.name) + post_url = reverse('admin:index', + current_app=self.admin_site.name) return HttpResponseRedirect(post_url) def response_post_save_add(self, request, obj): @@ -1541,7 +1363,7 @@ class ModelAdmin(BaseModelAdmin): # and bottom of the change list, for example). Get the action # whose button was pushed. try: - action_index = int(request.POST.get("index", 0)) + action_index = int(request.POST.get('index', 0)) except ValueError: action_index = 0 @@ -1552,7 +1374,7 @@ class ModelAdmin(BaseModelAdmin): # Use the action whose button was pushed try: - data.update({"action": data.getlist("action")[action_index]}) + data.update({'action': data.getlist('action')[action_index]}) except IndexError: # If we didn't get an action from the chosen form that's invalid # POST data, so by deleting action it'll fail the validation check @@ -1560,12 +1382,12 @@ class ModelAdmin(BaseModelAdmin): pass action_form = self.action_form(data, auto_id=None) - action_form.fields["action"].choices = self.get_action_choices(request) + action_form.fields['action'].choices = self.get_action_choices(request) # If the form's valid we can handle the action. if action_form.is_valid(): - action = action_form.cleaned_data["action"] - select_across = action_form.cleaned_data["select_across"] + action = action_form.cleaned_data['action'] + select_across = action_form.cleaned_data['select_across'] func = self.get_actions(request)[action][0] # Get the list of selected PKs. If nothing's selected, we can't @@ -1574,10 +1396,8 @@ class ModelAdmin(BaseModelAdmin): selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) if not selected and not select_across: # Reminder that something needs to be selected or nothing will happen - msg = _( - "Items must be selected in order to perform " - "actions on them. No items have been changed." - ) + msg = _("Items must be selected in order to perform " + "actions on them. No items have been changed.") self.message_user(request, msg, messages.WARNING) return None @@ -1606,47 +1426,38 @@ class ModelAdmin(BaseModelAdmin): opts = self.model._meta if IS_POPUP_VAR in request.POST: - popup_response_data = json.dumps( - { - "action": "delete", - "value": str(obj_id), - } - ) - return TemplateResponse( - request, - self.popup_response_template - or [ - "admin/%s/%s/popup_response.html" - % (opts.app_label, opts.model_name), - "admin/%s/popup_response.html" % opts.app_label, - "admin/popup_response.html", - ], - { - "popup_response_data": popup_response_data, - }, - ) + popup_response_data = json.dumps({ + 'action': 'delete', + 'value': str(obj_id), + }) + return TemplateResponse(request, self.popup_response_template or [ + 'admin/%s/%s/popup_response.html' % (opts.app_label, opts.model_name), + 'admin/%s/popup_response.html' % opts.app_label, + 'admin/popup_response.html', + ], { + 'popup_response_data': popup_response_data, + }) self.message_user( request, - _("The %(name)s “%(obj)s” was deleted successfully.") - % { - "name": opts.verbose_name, - "obj": obj_display, + _('The %(name)s “%(obj)s” was deleted successfully.') % { + 'name': opts.verbose_name, + 'obj': obj_display, }, messages.SUCCESS, ) if self.has_change_permission(request, None): post_url = reverse( - "admin:%s_%s_changelist" % (opts.app_label, opts.model_name), + 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name), current_app=self.admin_site.name, ) preserved_filters = self.get_preserved_filters(request) post_url = add_preserved_filters( - {"preserved_filters": preserved_filters, "opts": opts}, post_url + {'preserved_filters': preserved_filters, 'opts': opts}, post_url ) else: - post_url = reverse("admin:index", current_app=self.admin_site.name) + post_url = reverse('admin:index', current_app=self.admin_site.name) return HttpResponseRedirect(post_url) def render_delete_form(self, request, context): @@ -1662,11 +1473,8 @@ class ModelAdmin(BaseModelAdmin): return TemplateResponse( request, - self.delete_confirmation_template - or [ - "admin/{}/{}/delete_confirmation.html".format( - app_label, opts.model_name - ), + self.delete_confirmation_template or [ + "admin/{}/{}/delete_confirmation.html".format(app_label, opts.model_name), "admin/{}/delete_confirmation.html".format(app_label), "admin/delete_confirmation.html", ], @@ -1675,11 +1483,7 @@ class ModelAdmin(BaseModelAdmin): def get_inline_formsets(self, request, formsets, inline_instances, obj=None): # Edit permissions on parent model are required for editable inlines. - can_edit_parent = ( - self.has_change_permission(request, obj) - if obj - else self.has_add_permission(request) - ) + can_edit_parent = self.has_change_permission(request, obj) if obj else self.has_add_permission(request) inline_admin_formsets = [] for inline, formset in zip(inline_instances, formsets): fieldsets = list(inline.get_fieldsets(request, obj)) @@ -1690,23 +1494,14 @@ class ModelAdmin(BaseModelAdmin): has_delete_permission = inline.has_delete_permission(request, obj) else: # Disable all edit-permissions, and overide formset settings. - has_add_permission = ( - has_change_permission - ) = has_delete_permission = False + has_add_permission = has_change_permission = has_delete_permission = False formset.extra = formset.max_num = 0 has_view_permission = inline.has_view_permission(request, obj) prepopulated = dict(inline.get_prepopulated_fields(request, obj)) inline_admin_formset = helpers.InlineAdminFormSet( - inline, - formset, - fieldsets, - prepopulated, - readonly, - model_admin=self, - has_add_permission=has_add_permission, - has_change_permission=has_change_permission, - has_delete_permission=has_delete_permission, - has_view_permission=has_view_permission, + inline, formset, fieldsets, prepopulated, readonly, model_admin=self, + has_add_permission=has_add_permission, has_change_permission=has_change_permission, + has_delete_permission=has_delete_permission, has_view_permission=has_view_permission, ) inline_admin_formsets.append(inline_admin_formset) return inline_admin_formsets @@ -1731,30 +1526,28 @@ class ModelAdmin(BaseModelAdmin): Create a message informing the user that the object doesn't exist and return a redirect to the admin index page. """ - msg = _("%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?") % { - "name": opts.verbose_name, - "key": unquote(object_id), + msg = _('%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?') % { + 'name': opts.verbose_name, + 'key': unquote(object_id), } self.message_user(request, msg, messages.WARNING) - url = reverse("admin:index", current_app=self.admin_site.name) + url = reverse('admin:index', current_app=self.admin_site.name) return HttpResponseRedirect(url) @csrf_protect_m - def changeform_view(self, request, object_id=None, form_url="", extra_context=None): + def changeform_view(self, request, object_id=None, form_url='', extra_context=None): with transaction.atomic(using=router.db_for_write(self.model)): return self._changeform_view(request, object_id, form_url, extra_context) def _changeform_view(self, request, object_id, form_url, extra_context): to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)) if to_field and not self.to_field_allowed(request, to_field): - raise DisallowedModelAdminToField( - "The field %s cannot be referenced." % to_field - ) + raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field) model = self.model opts = model._meta - if request.method == "POST" and "_saveasnew" in request.POST: + if request.method == 'POST' and '_saveasnew' in request.POST: object_id = None add = object_id is None @@ -1767,7 +1560,7 @@ class ModelAdmin(BaseModelAdmin): else: obj = self.get_object(request, unquote(object_id), to_field) - if request.method == "POST": + if request.method == 'POST': if not self.has_change_permission(request, obj): raise PermissionDenied else: @@ -1781,24 +1574,18 @@ class ModelAdmin(BaseModelAdmin): ModelForm = self.get_form( request, obj, change=not add, fields=flatten_fieldsets(fieldsets) ) - if request.method == "POST": + if request.method == 'POST': form = ModelForm(request.POST, request.FILES, instance=obj) - formsets, inline_instances = self._create_formsets( - request, - form.instance if add else obj, - change=not add, - ) form_validated = form.is_valid() if form_validated: new_object = self.save_form(request, form, change=not add) else: new_object = form.instance + formsets, inline_instances = self._create_formsets(request, new_object, change=not add) if all_valid(formsets) and form_validated: self.save_model(request, new_object, form, not add) self.save_related(request, form, formsets, not add) - change_message = self.construct_change_message( - request, form, formsets, add - ) + change_message = self.construct_change_message(request, form, formsets, add) if add: self.log_addition(request, new_object, change_message) return self.response_add(request, new_object) @@ -1811,14 +1598,10 @@ class ModelAdmin(BaseModelAdmin): if add: initial = self.get_changeform_initial_data(request) form = ModelForm(initial=initial) - formsets, inline_instances = self._create_formsets( - request, form.instance, change=False - ) + formsets, inline_instances = self._create_formsets(request, form.instance, change=False) else: form = ModelForm(instance=obj) - formsets, inline_instances = self._create_formsets( - request, obj, change=True - ) + formsets, inline_instances = self._create_formsets(request, obj, change=True) if not add and not self.has_change_permission(request, obj): readonly_fields = flatten_fieldsets(fieldsets) @@ -1828,69 +1611,58 @@ class ModelAdmin(BaseModelAdmin): form, list(fieldsets), # Clear prepopulated fields on a view-only form to avoid a crash. - self.get_prepopulated_fields(request, obj) - if add or self.has_change_permission(request, obj) - else {}, + self.get_prepopulated_fields(request, obj) if add or self.has_change_permission(request, obj) else {}, readonly_fields, - model_admin=self, - ) + model_admin=self) media = self.media + adminForm.media - inline_formsets = self.get_inline_formsets( - request, formsets, inline_instances, obj - ) + inline_formsets = self.get_inline_formsets(request, formsets, inline_instances, obj) for inline_formset in inline_formsets: media = media + inline_formset.media if add: - title = _("Add %s") + title = _('Add %s') elif self.has_change_permission(request, obj): - title = _("Change %s") + title = _('Change %s') else: - title = _("View %s") + title = _('View %s') context = { **self.admin_site.each_context(request), - "title": title % opts.verbose_name, - "subtitle": str(obj) if obj else None, - "adminform": adminForm, - "object_id": object_id, - "original": obj, - "is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, - "to_field": to_field, - "media": media, - "inline_admin_formsets": inline_formsets, - "errors": helpers.AdminErrorList(form, formsets), - "preserved_filters": self.get_preserved_filters(request), + 'title': title % opts.verbose_name, + 'subtitle': str(obj) if obj else None, + 'adminform': adminForm, + 'object_id': object_id, + 'original': obj, + 'is_popup': IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, + 'to_field': to_field, + 'media': media, + 'inline_admin_formsets': inline_formsets, + 'errors': helpers.AdminErrorList(form, formsets), + 'preserved_filters': self.get_preserved_filters(request), } # Hide the "Save" and "Save and continue" buttons if "Save as New" was # previously chosen to prevent the interface from getting confusing. - if ( - request.method == "POST" - and not form_validated - and "_saveasnew" in request.POST - ): - context["show_save"] = False - context["show_save_and_continue"] = False + if request.method == 'POST' and not form_validated and "_saveasnew" in request.POST: + context['show_save'] = False + context['show_save_and_continue'] = False # Use the change template instead of the add template. add = False context.update(extra_context or {}) - return self.render_change_form( - request, context, add=add, change=not add, obj=obj, form_url=form_url - ) + return self.render_change_form(request, context, add=add, change=not add, obj=obj, form_url=form_url) - def add_view(self, request, form_url="", extra_context=None): + def add_view(self, request, form_url='', extra_context=None): return self.changeform_view(request, None, form_url, extra_context) - def change_view(self, request, object_id, form_url="", extra_context=None): + def change_view(self, request, object_id, form_url='', extra_context=None): return self.changeform_view(request, object_id, form_url, extra_context) def _get_edited_object_pks(self, request, prefix): """Return POST data values of list_editable primary keys.""" pk_pattern = re.compile( - r"{}-\d+-{}$".format(re.escape(prefix), self.model._meta.pk.name) + r'{}-\d+-{}$'.format(re.escape(prefix), self.model._meta.pk.name) ) return [value for key, value in request.POST.items() if pk_pattern.match(key)] @@ -1916,7 +1688,6 @@ class ModelAdmin(BaseModelAdmin): The 'change list' admin view for this model. """ from django.contrib.admin.views.main import ERROR_FLAG - opts = self.model._meta app_label = opts.app_label if not self.has_view_or_change_permission(request): @@ -1932,13 +1703,10 @@ class ModelAdmin(BaseModelAdmin): # something is screwed up with the database, so display an error # page. if ERROR_FLAG in request.GET: - return SimpleTemplateResponse( - "admin/invalid_setup.html", - { - "title": _("Database error"), - }, - ) - return HttpResponseRedirect(request.path + "?" + ERROR_FLAG + "=1") + return SimpleTemplateResponse('admin/invalid_setup.html', { + 'title': _('Database error'), + }) + return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') # If the request was POSTed, this might be a bulk action or a bulk # edit. Try to look up an action or confirmation first, but if this @@ -1949,40 +1717,26 @@ class ModelAdmin(BaseModelAdmin): actions = self.get_actions(request) # Actions with no confirmation - if ( - actions - and request.method == "POST" - and "index" in request.POST - and "_save" not in request.POST - ): + if (actions and request.method == 'POST' and + 'index' in request.POST and '_save' not in request.POST): if selected: - response = self.response_action( - request, queryset=cl.get_queryset(request) - ) + response = self.response_action(request, queryset=cl.get_queryset(request)) if response: return response else: action_failed = True else: - msg = _( - "Items must be selected in order to perform " - "actions on them. No items have been changed." - ) + msg = _("Items must be selected in order to perform " + "actions on them. No items have been changed.") self.message_user(request, msg, messages.WARNING) action_failed = True # Actions with confirmation - if ( - actions - and request.method == "POST" - and helpers.ACTION_CHECKBOX_NAME in request.POST - and "index" not in request.POST - and "_save" not in request.POST - ): + if (actions and request.method == 'POST' and + helpers.ACTION_CHECKBOX_NAME in request.POST and + 'index' not in request.POST and '_save' not in request.POST): if selected: - response = self.response_action( - request, queryset=cl.get_queryset(request) - ) + response = self.response_action(request, queryset=cl.get_queryset(request)) if response: return response else: @@ -2000,16 +1754,12 @@ class ModelAdmin(BaseModelAdmin): formset = cl.formset = None # Handle POSTed bulk-edit data. - if request.method == "POST" and cl.list_editable and "_save" in request.POST: + if request.method == 'POST' and cl.list_editable and '_save' in request.POST: if not self.has_change_permission(request): raise PermissionDenied FormSet = self.get_changelist_formset(request) - modified_objects = self._get_list_editable_queryset( - request, FormSet.get_default_prefix() - ) - formset = cl.formset = FormSet( - request.POST, request.FILES, queryset=modified_objects - ) + modified_objects = self._get_list_editable_queryset(request, FormSet.get_default_prefix()) + formset = cl.formset = FormSet(request.POST, request.FILES, queryset=modified_objects) if formset.is_valid(): changecount = 0 for form in formset.forms: @@ -2025,10 +1775,10 @@ class ModelAdmin(BaseModelAdmin): msg = ngettext( "%(count)s %(name)s was changed successfully.", "%(count)s %(name)s were changed successfully.", - changecount, + changecount ) % { - "count": changecount, - "name": model_ngettext(opts, changecount), + 'count': changecount, + 'name': model_ngettext(opts, changecount), } self.message_user(request, msg, messages.SUCCESS) @@ -2048,48 +1798,45 @@ class ModelAdmin(BaseModelAdmin): # Build the action form and populate it with available actions. if actions: action_form = self.action_form(auto_id=None) - action_form.fields["action"].choices = self.get_action_choices(request) + action_form.fields['action'].choices = self.get_action_choices(request) media += action_form.media else: action_form = None selection_note_all = ngettext( - "%(total_count)s selected", "All %(total_count)s selected", cl.result_count + '%(total_count)s selected', + 'All %(total_count)s selected', + cl.result_count ) context = { **self.admin_site.each_context(request), - "module_name": str(opts.verbose_name_plural), - "selection_note": _("0 of %(cnt)s selected") % {"cnt": len(cl.result_list)}, - "selection_note_all": selection_note_all % {"total_count": cl.result_count}, - "title": cl.title, - "subtitle": None, - "is_popup": cl.is_popup, - "to_field": cl.to_field, - "cl": cl, - "media": media, - "has_add_permission": self.has_add_permission(request), - "opts": cl.opts, - "action_form": action_form, - "actions_on_top": self.actions_on_top, - "actions_on_bottom": self.actions_on_bottom, - "actions_selection_counter": self.actions_selection_counter, - "preserved_filters": self.get_preserved_filters(request), + 'module_name': str(opts.verbose_name_plural), + 'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)}, + 'selection_note_all': selection_note_all % {'total_count': cl.result_count}, + 'title': cl.title, + 'subtitle': None, + 'is_popup': cl.is_popup, + 'to_field': cl.to_field, + 'cl': cl, + 'media': media, + 'has_add_permission': self.has_add_permission(request), + 'opts': cl.opts, + 'action_form': action_form, + 'actions_on_top': self.actions_on_top, + 'actions_on_bottom': self.actions_on_bottom, + 'actions_selection_counter': self.actions_selection_counter, + 'preserved_filters': self.get_preserved_filters(request), **(extra_context or {}), } request.current_app = self.admin_site.name - return TemplateResponse( - request, - self.change_list_template - or [ - "admin/%s/%s/change_list.html" % (app_label, opts.model_name), - "admin/%s/change_list.html" % app_label, - "admin/change_list.html", - ], - context, - ) + return TemplateResponse(request, self.change_list_template or [ + 'admin/%s/%s/change_list.html' % (app_label, opts.model_name), + 'admin/%s/change_list.html' % app_label, + 'admin/change_list.html' + ], context) def get_deleted_objects(self, objs, request): """ @@ -2110,9 +1857,7 @@ class ModelAdmin(BaseModelAdmin): to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)) if to_field and not self.to_field_allowed(request, to_field): - raise DisallowedModelAdminToField( - "The field %s cannot be referenced." % to_field - ) + raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field) obj = self.get_object(request, unquote(object_id), to_field) @@ -2124,12 +1869,7 @@ class ModelAdmin(BaseModelAdmin): # Populate deleted_objects, a data structure of all related objects that # will also be deleted. - ( - deleted_objects, - model_count, - perms_needed, - protected, - ) = self.get_deleted_objects([obj], request) + deleted_objects, model_count, perms_needed, protected = self.get_deleted_objects([obj], request) if request.POST and not protected: # The user has confirmed the deletion. if perms_needed: @@ -2151,19 +1891,19 @@ class ModelAdmin(BaseModelAdmin): context = { **self.admin_site.each_context(request), - "title": title, - "subtitle": None, - "object_name": object_name, - "object": obj, - "deleted_objects": deleted_objects, - "model_count": dict(model_count).items(), - "perms_lacking": perms_needed, - "protected": protected, - "opts": opts, - "app_label": app_label, - "preserved_filters": self.get_preserved_filters(request), - "is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, - "to_field": to_field, + 'title': title, + 'subtitle': None, + 'object_name': object_name, + 'object': obj, + 'deleted_objects': deleted_objects, + 'model_count': dict(model_count).items(), + 'perms_lacking': perms_needed, + 'protected': protected, + 'opts': opts, + 'app_label': app_label, + 'preserved_filters': self.get_preserved_filters(request), + 'is_popup': IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, + 'to_field': to_field, **(extra_context or {}), } @@ -2177,9 +1917,7 @@ class ModelAdmin(BaseModelAdmin): model = self.model obj = self.get_object(request, unquote(object_id)) if obj is None: - return self._get_obj_does_not_exist_redirect( - request, model._meta, object_id - ) + return self._get_obj_does_not_exist_redirect(request, model._meta, object_id) if not self.has_view_or_change_permission(request, obj): raise PermissionDenied @@ -2187,55 +1925,30 @@ class ModelAdmin(BaseModelAdmin): # Then get the history for this object. opts = model._meta app_label = opts.app_label - action_list = ( - LogEntry.objects.filter( - object_id=unquote(object_id), - content_type=get_content_type_for_model(model), - ) - .select_related() - .order_by("action_time") - ) + action_list = LogEntry.objects.filter( + object_id=unquote(object_id), + content_type=get_content_type_for_model(model) + ).select_related().order_by('action_time') context = { **self.admin_site.each_context(request), - "title": _("Change history: %s") % obj, - "subtitle": None, - "action_list": action_list, - "module_name": str(capfirst(opts.verbose_name_plural)), - "object": obj, - "opts": opts, - "preserved_filters": self.get_preserved_filters(request), + 'title': _('Change history: %s') % obj, + 'subtitle': None, + 'action_list': action_list, + 'module_name': str(capfirst(opts.verbose_name_plural)), + 'object': obj, + 'opts': opts, + 'preserved_filters': self.get_preserved_filters(request), **(extra_context or {}), } request.current_app = self.admin_site.name - return TemplateResponse( - request, - self.object_history_template - or [ - "admin/%s/%s/object_history.html" % (app_label, opts.model_name), - "admin/%s/object_history.html" % app_label, - "admin/object_history.html", - ], - context, - ) - - def get_formset_kwargs(self, request, obj, inline, prefix): - formset_params = { - "instance": obj, - "prefix": prefix, - "queryset": inline.get_queryset(request), - } - if request.method == "POST": - formset_params.update( - { - "data": request.POST.copy(), - "files": request.FILES, - "save_as_new": "_saveasnew" in request.POST, - } - ) - return formset_params + return TemplateResponse(request, self.object_history_template or [ + "admin/%s/%s/object_history.html" % (app_label, opts.model_name), + "admin/%s/object_history.html" % app_label, + "admin/object_history.html" + ], context) def _create_formsets(self, request, obj, change): "Helper function to generate formsets for add/change_view." @@ -2250,14 +1963,24 @@ class ModelAdmin(BaseModelAdmin): prefixes[prefix] = prefixes.get(prefix, 0) + 1 if prefixes[prefix] != 1 or not prefix: prefix = "%s-%s" % (prefix, prefixes[prefix]) - formset_params = self.get_formset_kwargs(request, obj, inline, prefix) + formset_params = { + 'instance': obj, + 'prefix': prefix, + 'queryset': inline.get_queryset(request), + } + if request.method == 'POST': + formset_params.update({ + 'data': request.POST.copy(), + 'files': request.FILES, + 'save_as_new': '_saveasnew' in request.POST + }) formset = FormSet(**formset_params) def user_deleted_form(request, obj, formset, index): """Return whether or not the user deleted the form.""" return ( - inline.has_delete_permission(request, obj) - and "{}-{}-DELETE".format(formset.prefix, index) in request.POST + inline.has_delete_permission(request, obj) and + '{}-{}-DELETE'.format(formset.prefix, index) in request.POST ) # Bypass validation of each view-only inline form (since the form's @@ -2281,7 +2004,6 @@ class InlineModelAdmin(BaseModelAdmin): from ``model`` to its parent. This is required if ``model`` has more than one ``ForeignKey`` to its parent. """ - model = None fk_name = None formset = BaseInlineFormSet @@ -2302,23 +2024,20 @@ class InlineModelAdmin(BaseModelAdmin): self.opts = self.model._meta self.has_registered_model = admin_site.is_registered(self.model) super().__init__() - if self.verbose_name_plural is None: - if self.verbose_name is None: - self.verbose_name_plural = self.model._meta.verbose_name_plural - else: - self.verbose_name_plural = format_lazy("{}s", self.verbose_name) if self.verbose_name is None: self.verbose_name = self.model._meta.verbose_name + if self.verbose_name_plural is None: + self.verbose_name_plural = self.model._meta.verbose_name_plural @property def media(self): - extra = "" if settings.DEBUG else ".min" - js = ["vendor/jquery/jquery%s.js" % extra, "jquery.init.js", "inlines.js"] + extra = '' if settings.DEBUG else '.min' + js = ['vendor/jquery/jquery%s.js' % extra, 'jquery.init.js', 'inlines.js'] if self.filter_vertical or self.filter_horizontal: - js.extend(["SelectBox.js", "SelectFilter2.js"]) - if self.classes and "collapse" in self.classes: - js.append("collapse.js") - return forms.Media(js=["admin/js/%s" % url for url in js]) + js.extend(['SelectBox.js', 'SelectFilter2.js']) + if self.classes and 'collapse' in self.classes: + js.append('collapse.js') + return forms.Media(js=['admin/js/%s' % url for url in js]) def get_extra(self, request, obj=None, **kwargs): """Hook for customizing the number of extra inline forms.""" @@ -2334,14 +2053,14 @@ class InlineModelAdmin(BaseModelAdmin): def get_formset(self, request, obj=None, **kwargs): """Return a BaseInlineFormSet class for use in admin add/change views.""" - if "fields" in kwargs: - fields = kwargs.pop("fields") + if 'fields' in kwargs: + fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) excluded = self.get_exclude(request, obj) exclude = [] if excluded is None else list(excluded) exclude.extend(self.get_readonly_fields(request, obj)) - if excluded is None and hasattr(self.form, "_meta") and self.form._meta.exclude: + if excluded is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # InlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) @@ -2350,24 +2069,25 @@ class InlineModelAdmin(BaseModelAdmin): exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) defaults = { - "form": self.form, - "formset": self.formset, - "fk_name": self.fk_name, - "fields": fields, - "exclude": exclude, - "formfield_callback": partial(self.formfield_for_dbfield, request=request), - "extra": self.get_extra(request, obj, **kwargs), - "min_num": self.get_min_num(request, obj, **kwargs), - "max_num": self.get_max_num(request, obj, **kwargs), - "can_delete": can_delete, + 'form': self.form, + 'formset': self.formset, + 'fk_name': self.fk_name, + 'fields': fields, + 'exclude': exclude, + 'formfield_callback': partial(self.formfield_for_dbfield, request=request), + 'extra': self.get_extra(request, obj, **kwargs), + 'min_num': self.get_min_num(request, obj, **kwargs), + 'max_num': self.get_max_num(request, obj, **kwargs), + 'can_delete': can_delete, **kwargs, } - base_model_form = defaults["form"] + base_model_form = defaults['form'] can_change = self.has_change_permission(request, obj) if request else True can_add = self.has_add_permission(request, obj) if request else True class DeleteProtectedModelForm(base_model_form): + def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on @@ -2384,25 +2104,21 @@ class InlineModelAdmin(BaseModelAdmin): objs = [] for p in collector.protected: objs.append( - # Translators: Model verbose name and instance - # representation, suitable to be an item in a - # list. - _("%(class_name)s %(instance)s") - % {"class_name": p._meta.verbose_name, "instance": p} + # Translators: Model verbose name and instance representation, + # suitable to be an item in a list. + _('%(class_name)s %(instance)s') % { + 'class_name': p._meta.verbose_name, + 'instance': p} ) params = { - "class_name": self._meta.model._meta.verbose_name, - "instance": self.instance, - "related_objects": get_text_list(objs, _("and")), + 'class_name': self._meta.model._meta.verbose_name, + 'instance': self.instance, + 'related_objects': get_text_list(objs, _('and')), } - msg = _( - "Deleting %(class_name)s %(instance)s would require " - "deleting the following protected related objects: " - "%(related_objects)s" - ) - raise ValidationError( - msg, code="deleting_protected", params=params - ) + msg = _("Deleting %(class_name)s %(instance)s would require " + "deleting the following protected related objects: " + "%(related_objects)s") + raise ValidationError(msg, code='deleting_protected', params=params) def is_valid(self): result = super().is_valid() @@ -2417,12 +2133,10 @@ class InlineModelAdmin(BaseModelAdmin): return False return super().has_changed() - defaults["form"] = DeleteProtectedModelForm + defaults['form'] = DeleteProtectedModelForm - if defaults["fields"] is None and not modelform_defines_fields( - defaults["form"] - ): - defaults["fields"] = forms.ALL_FIELDS + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = forms.ALL_FIELDS return inlineformset_factory(self.parent_model, self.model, **defaults) @@ -2449,9 +2163,7 @@ class InlineModelAdmin(BaseModelAdmin): opts = field.remote_field.model._meta break return any( - request.user.has_perm( - "%s.%s" % (opts.app_label, get_permission_codename(perm, opts)) - ) + request.user.has_perm('%s.%s' % (opts.app_label, get_permission_codename(perm, opts))) for perm in perms ) @@ -2461,32 +2173,32 @@ class InlineModelAdmin(BaseModelAdmin): # permissions. The user needs to have the change permission for the # related model in order to be able to do anything with the # intermediate model. - return self._has_any_perms_for_target_model(request, ["change"]) + return self._has_any_perms_for_target_model(request, ['change']) return super().has_add_permission(request) def has_change_permission(self, request, obj=None): if self.opts.auto_created: # Same comment as has_add_permission(). - return self._has_any_perms_for_target_model(request, ["change"]) + return self._has_any_perms_for_target_model(request, ['change']) return super().has_change_permission(request) def has_delete_permission(self, request, obj=None): if self.opts.auto_created: # Same comment as has_add_permission(). - return self._has_any_perms_for_target_model(request, ["change"]) + return self._has_any_perms_for_target_model(request, ['change']) return super().has_delete_permission(request, obj) def has_view_permission(self, request, obj=None): if self.opts.auto_created: # Same comment as has_add_permission(). The 'change' permission # also implies the 'view' permission. - return self._has_any_perms_for_target_model(request, ["view", "change"]) + return self._has_any_perms_for_target_model(request, ['view', 'change']) return super().has_view_permission(request) class StackedInline(InlineModelAdmin): - template = "admin/edit_inline/stacked.html" + template = 'admin/edit_inline/stacked.html' class TabularInline(InlineModelAdmin): - template = "admin/edit_inline/tabular.html" + template = 'admin/edit_inline/tabular.html' diff --git a/venv/Lib/site-packages/django/contrib/admin/sites.py b/venv/Lib/site-packages/django/contrib/admin/sites.py index ae166ce..3d9492e 100644 --- a/venv/Lib/site-packages/django/contrib/admin/sites.py +++ b/venv/Lib/site-packages/django/contrib/admin/sites.py @@ -9,15 +9,15 @@ from django.contrib.admin.views.autocomplete import AutocompleteJsonView from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured from django.db.models.base import ModelBase -from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect +from django.http import ( + Http404, HttpResponsePermanentRedirect, HttpResponseRedirect, +) from django.template.response import TemplateResponse from django.urls import NoReverseMatch, Resolver404, resolve, reverse -from django.utils.decorators import method_decorator from django.utils.functional import LazyObject from django.utils.module_loading import import_string from django.utils.text import capfirst -from django.utils.translation import gettext as _ -from django.utils.translation import gettext_lazy +from django.utils.translation import gettext as _, gettext_lazy from django.views.decorators.cache import never_cache from django.views.decorators.common import no_append_slash from django.views.decorators.csrf import csrf_protect @@ -44,20 +44,20 @@ class AdminSite: """ # Text to put at the end of each page's . - site_title = gettext_lazy("Django site admin") + site_title = gettext_lazy('Django site admin') # Text to put in each page's <h1>. - site_header = gettext_lazy("Django administration") + site_header = gettext_lazy('Django administration') # Text to put at the top of the admin index page. - index_title = gettext_lazy("Site administration") + index_title = gettext_lazy('Site administration') # URL for the "View site" link at the top of each admin page. - site_url = "/" + site_url = '/' enable_nav_sidebar = True - empty_value_display = "-" + empty_value_display = '-' login_form = None index_template = None @@ -69,16 +69,13 @@ class AdminSite: final_catch_all_view = True - def __init__(self, name="admin"): + def __init__(self, name='admin'): self._registry = {} # model_class class -> admin_class instance self.name = name - self._actions = {"delete_selected": actions.delete_selected} + self._actions = {'delete_selected': actions.delete_selected} self._global_actions = self._actions.copy() all_sites.add(self) - def __repr__(self): - return f"{self.__class__.__name__}(name={self.name!r})" - def check(self, app_configs): """ Run the system checks on all ModelAdmins, except if they aren't @@ -89,9 +86,7 @@ class AdminSite: app_configs = set(app_configs) # Speed up lookups below errors = [] - modeladmins = ( - o for o in self._registry.values() if o.__class__ is not ModelAdmin - ) + modeladmins = (o for o in self._registry.values() if o.__class__ is not ModelAdmin) for modeladmin in modeladmins: if modeladmin.model._meta.app_config in app_configs: errors.extend(modeladmin.check()) @@ -117,18 +112,17 @@ class AdminSite: for model in model_or_iterable: if model._meta.abstract: raise ImproperlyConfigured( - "The model %s is abstract, so it cannot be registered with admin." - % model.__name__ + 'The model %s is abstract, so it cannot be registered with admin.' % model.__name__ ) if model in self._registry: registered_admin = str(self._registry[model]) - msg = "The model %s is already registered " % model.__name__ - if registered_admin.endswith(".ModelAdmin"): + msg = 'The model %s is already registered ' % model.__name__ + if registered_admin.endswith('.ModelAdmin'): # Most likely registered without a ModelAdmin subclass. - msg += "in app %r." % re.sub(r"\.ModelAdmin$", "", registered_admin) + msg += 'in app %r.' % re.sub(r'\.ModelAdmin$', '', registered_admin) else: - msg += "with %r." % registered_admin + msg += 'with %r.' % registered_admin raise AlreadyRegistered(msg) # Ignore the registration if the model has been @@ -140,10 +134,8 @@ class AdminSite: # For reasons I don't quite understand, without a __module__ # the created class appears to "live" in the wrong place, # which causes issues later on. - options["__module__"] = __name__ - admin_class = type( - "%sAdmin" % model.__name__, (admin_class,), options - ) + options['__module__'] = __name__ + admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) # Instantiate the admin class to save in the registry self._registry[model] = admin_class(model, self) @@ -158,7 +150,7 @@ class AdminSite: model_or_iterable = [model_or_iterable] for model in model_or_iterable: if model not in self._registry: - raise NotRegistered("The model %s is not registered" % model.__name__) + raise NotRegistered('The model %s is not registered' % model.__name__) del self._registry[model] def is_registered(self, model): @@ -225,27 +217,24 @@ class AdminSite: ``never_cache`` decorator. If the view can be safely cached, set cacheable=True. """ - def inner(request, *args, **kwargs): if not self.has_permission(request): - if request.path == reverse("admin:logout", current_app=self.name): - index_path = reverse("admin:index", current_app=self.name) + if request.path == reverse('admin:logout', current_app=self.name): + index_path = reverse('admin:index', current_app=self.name) return HttpResponseRedirect(index_path) # Inner import to prevent django.contrib.admin (app) from # importing django.contrib.auth.models.User (unrelated model). from django.contrib.auth.views import redirect_to_login - return redirect_to_login( request.get_full_path(), - reverse("admin:login", current_app=self.name), + reverse('admin:login', current_app=self.name) ) return view(request, *args, **kwargs) - if not cacheable: inner = never_cache(inner) # We add csrf_protect here so this function can be used as a utility # function for any view, without having to repeat 'csrf_protect'. - if not getattr(view, "csrf_exempt", False): + if not getattr(view, 'csrf_exempt', False): inner = csrf_protect(inner) return update_wrapper(inner, view) @@ -259,31 +248,26 @@ class AdminSite: def wrap(view, cacheable=False): def wrapper(*args, **kwargs): return self.admin_view(view, cacheable)(*args, **kwargs) - wrapper.admin_site = self return update_wrapper(wrapper, view) # Admin-site-wide views. urlpatterns = [ - path("", wrap(self.index), name="index"), - path("login/", self.login, name="login"), - path("logout/", wrap(self.logout), name="logout"), + path('', wrap(self.index), name='index'), + path('login/', self.login, name='login'), + path('logout/', wrap(self.logout), name='logout'), + path('password_change/', wrap(self.password_change, cacheable=True), name='password_change'), path( - "password_change/", - wrap(self.password_change, cacheable=True), - name="password_change", - ), - path( - "password_change/done/", + 'password_change/done/', wrap(self.password_change_done, cacheable=True), - name="password_change_done", + name='password_change_done', ), - path("autocomplete/", wrap(self.autocomplete_view), name="autocomplete"), - path("jsi18n/", wrap(self.i18n_javascript, cacheable=True), name="jsi18n"), + path('autocomplete/', wrap(self.autocomplete_view), name='autocomplete'), + path('jsi18n/', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'), path( - "r/<int:content_type_id>/<path:object_id>/", + 'r/<int:content_type_id>/<path:object_id>/', wrap(contenttype_views.shortcut), - name="view_on_site", + name='view_on_site', ), ] @@ -292,10 +276,7 @@ class AdminSite: valid_app_labels = [] for model, model_admin in self._registry.items(): urlpatterns += [ - path( - "%s/%s/" % (model._meta.app_label, model._meta.model_name), - include(model_admin.urls), - ), + path('%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), ] if model._meta.app_label not in valid_app_labels: valid_app_labels.append(model._meta.app_label) @@ -303,19 +284,19 @@ class AdminSite: # If there were ModelAdmins registered, we should have a list of app # labels for which we need to allow access to the app_index view, if valid_app_labels: - regex = r"^(?P<app_label>" + "|".join(valid_app_labels) + ")/$" + regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$' urlpatterns += [ - re_path(regex, wrap(self.app_index), name="app_list"), + re_path(regex, wrap(self.app_index), name='app_list'), ] if self.final_catch_all_view: - urlpatterns.append(re_path(r"(?P<url>.*)$", wrap(self.catch_all_view))) + urlpatterns.append(re_path(r'(?P<url>.*)$', wrap(self.catch_all_view))) return urlpatterns @property def urls(self): - return self.get_urls(), "admin", self.name + return self.get_urls(), 'admin', self.name def each_context(self, request): """ @@ -325,18 +306,16 @@ class AdminSite: For sites running on a subpath, use the SCRIPT_NAME value if site_url hasn't been customized. """ - script_name = request.META["SCRIPT_NAME"] - site_url = ( - script_name if self.site_url == "/" and script_name else self.site_url - ) + script_name = request.META['SCRIPT_NAME'] + site_url = script_name if self.site_url == '/' and script_name else self.site_url return { - "site_title": self.site_title, - "site_header": self.site_header, - "site_url": site_url, - "has_permission": self.has_permission(request), - "available_apps": self.get_app_list(request), - "is_popup": False, - "is_nav_sidebar_enabled": self.enable_nav_sidebar, + 'site_title': self.site_title, + 'site_header': self.site_header, + 'site_url': site_url, + 'has_permission': self.has_permission(request), + 'available_apps': self.get_app_list(request), + 'is_popup': False, + 'is_nav_sidebar_enabled': self.enable_nav_sidebar, } def password_change(self, request, extra_context=None): @@ -345,15 +324,14 @@ class AdminSite: """ from django.contrib.admin.forms import AdminPasswordChangeForm from django.contrib.auth.views import PasswordChangeView - - url = reverse("admin:password_change_done", current_app=self.name) + url = reverse('admin:password_change_done', current_app=self.name) defaults = { - "form_class": AdminPasswordChangeForm, - "success_url": url, - "extra_context": {**self.each_context(request), **(extra_context or {})}, + 'form_class': AdminPasswordChangeForm, + 'success_url': url, + 'extra_context': {**self.each_context(request), **(extra_context or {})}, } if self.password_change_template is not None: - defaults["template_name"] = self.password_change_template + defaults['template_name'] = self.password_change_template request.current_app = self.name return PasswordChangeView.as_view(**defaults)(request) @@ -362,12 +340,11 @@ class AdminSite: Display the "success" page after a password change. """ from django.contrib.auth.views import PasswordChangeDoneView - defaults = { - "extra_context": {**self.each_context(request), **(extra_context or {})}, + 'extra_context': {**self.each_context(request), **(extra_context or {})}, } if self.password_change_done_template is not None: - defaults["template_name"] = self.password_change_done_template + defaults['template_name'] = self.password_change_done_template request.current_app = self.name return PasswordChangeDoneView.as_view(**defaults)(request) @@ -378,8 +355,9 @@ class AdminSite: `extra_context` is unused but present for consistency with the other admin views. """ - return JavaScriptCatalog.as_view(packages=["django.contrib.admin"])(request) + return JavaScriptCatalog.as_view(packages=['django.contrib.admin'])(request) + @never_cache def logout(self, request, extra_context=None): """ Log out the user for the given HttpRequest. @@ -387,29 +365,28 @@ class AdminSite: This should *not* assume the user is already logged in. """ from django.contrib.auth.views import LogoutView - defaults = { - "extra_context": { + 'extra_context': { **self.each_context(request), # Since the user isn't logged out at this point, the value of # has_permission must be overridden. - "has_permission": False, - **(extra_context or {}), + 'has_permission': False, + **(extra_context or {}) }, } if self.logout_template is not None: - defaults["template_name"] = self.logout_template + defaults['template_name'] = self.logout_template request.current_app = self.name return LogoutView.as_view(**defaults)(request) - @method_decorator(never_cache) + @never_cache def login(self, request, extra_context=None): """ Display the login form for the given HttpRequest. """ - if request.method == "GET" and self.has_permission(request): + if request.method == 'GET' and self.has_permission(request): # Already logged-in, redirect to admin index - index_path = reverse("admin:index", current_app=self.name) + index_path = reverse('admin:index', current_app=self.name) return HttpResponseRedirect(index_path) # Since this module gets imported in the application's root package, @@ -417,24 +394,21 @@ class AdminSite: # and django.contrib.admin.forms eventually imports User. from django.contrib.admin.forms import AdminAuthenticationForm from django.contrib.auth.views import LoginView - context = { **self.each_context(request), - "title": _("Log in"), - "app_path": request.get_full_path(), - "username": request.user.get_username(), + 'title': _('Log in'), + 'app_path': request.get_full_path(), + 'username': request.user.get_username(), } - if ( - REDIRECT_FIELD_NAME not in request.GET - and REDIRECT_FIELD_NAME not in request.POST - ): - context[REDIRECT_FIELD_NAME] = reverse("admin:index", current_app=self.name) + if (REDIRECT_FIELD_NAME not in request.GET and + REDIRECT_FIELD_NAME not in request.POST): + context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name) context.update(extra_context or {}) defaults = { - "extra_context": context, - "authentication_form": self.login_form or AdminAuthenticationForm, - "template_name": self.login_template or "admin/login.html", + 'extra_context': context, + 'authentication_form': self.login_form or AdminAuthenticationForm, + 'template_name': self.login_template or 'admin/login.html', } request.current_app = self.name return LoginView.as_view(**defaults)(request) @@ -444,15 +418,15 @@ class AdminSite: @no_append_slash def catch_all_view(self, request, url): - if settings.APPEND_SLASH and not url.endswith("/"): - urlconf = getattr(request, "urlconf", None) + if settings.APPEND_SLASH and not url.endswith('/'): + urlconf = getattr(request, 'urlconf', None) try: - match = resolve("%s/" % request.path_info, urlconf) + match = resolve('%s/' % request.path_info, urlconf) except Resolver404: pass else: - if getattr(match.func, "should_append_slash", True): - return HttpResponsePermanentRedirect("%s/" % request.path) + if getattr(match.func, 'should_append_slash', True): + return HttpResponsePermanentRedirect('%s/' % request.path) raise Http404 def _build_app_dict(self, request, label=None): @@ -464,8 +438,7 @@ class AdminSite: if label: models = { - m: m_a - for m, m_a in self._registry.items() + m: m_a for m, m_a in self._registry.items() if m._meta.app_label == label } else: @@ -487,42 +460,37 @@ class AdminSite: info = (app_label, model._meta.model_name) model_dict = { - "model": model, - "name": capfirst(model._meta.verbose_name_plural), - "object_name": model._meta.object_name, - "perms": perms, - "admin_url": None, - "add_url": None, + 'name': capfirst(model._meta.verbose_name_plural), + 'object_name': model._meta.object_name, + 'perms': perms, + 'admin_url': None, + 'add_url': None, } - if perms.get("change") or perms.get("view"): - model_dict["view_only"] = not perms.get("change") + if perms.get('change') or perms.get('view'): + model_dict['view_only'] = not perms.get('change') try: - model_dict["admin_url"] = reverse( - "admin:%s_%s_changelist" % info, current_app=self.name - ) + model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name) except NoReverseMatch: pass - if perms.get("add"): + if perms.get('add'): try: - model_dict["add_url"] = reverse( - "admin:%s_%s_add" % info, current_app=self.name - ) + model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name) except NoReverseMatch: pass if app_label in app_dict: - app_dict[app_label]["models"].append(model_dict) + app_dict[app_label]['models'].append(model_dict) else: app_dict[app_label] = { - "name": apps.get_app_config(app_label).verbose_name, - "app_label": app_label, - "app_url": reverse( - "admin:app_list", - kwargs={"app_label": app_label}, + 'name': apps.get_app_config(app_label).verbose_name, + 'app_label': app_label, + 'app_url': reverse( + 'admin:app_list', + kwargs={'app_label': app_label}, current_app=self.name, ), - "has_module_perms": has_module_perms, - "models": [model_dict], + 'has_module_perms': has_module_perms, + 'models': [model_dict], } if label: @@ -537,14 +505,15 @@ class AdminSite: app_dict = self._build_app_dict(request) # Sort the apps alphabetically. - app_list = sorted(app_dict.values(), key=lambda x: x["name"].lower()) + app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower()) # Sort the models alphabetically within each app. for app in app_list: - app["models"].sort(key=lambda x: x["name"]) + app['models'].sort(key=lambda x: x['name']) return app_list + @never_cache def index(self, request, extra_context=None): """ Display the main admin index page, which lists all of the installed @@ -554,51 +523,44 @@ class AdminSite: context = { **self.each_context(request), - "title": self.index_title, - "subtitle": None, - "app_list": app_list, + 'title': self.index_title, + 'subtitle': None, + 'app_list': app_list, **(extra_context or {}), } request.current_app = self.name - return TemplateResponse( - request, self.index_template or "admin/index.html", context - ) + return TemplateResponse(request, self.index_template or 'admin/index.html', context) def app_index(self, request, app_label, extra_context=None): app_dict = self._build_app_dict(request, app_label) if not app_dict: - raise Http404("The requested admin page does not exist.") + raise Http404('The requested admin page does not exist.') # Sort the models alphabetically within each app. - app_dict["models"].sort(key=lambda x: x["name"]) + app_dict['models'].sort(key=lambda x: x['name']) context = { **self.each_context(request), - "title": _("%(app)s administration") % {"app": app_dict["name"]}, - "subtitle": None, - "app_list": [app_dict], - "app_label": app_label, + 'title': _('%(app)s administration') % {'app': app_dict['name']}, + 'subtitle': None, + 'app_list': [app_dict], + 'app_label': app_label, **(extra_context or {}), } request.current_app = self.name - return TemplateResponse( - request, - self.app_index_template - or ["admin/%s/app_index.html" % app_label, "admin/app_index.html"], - context, - ) + return TemplateResponse(request, self.app_index_template or [ + 'admin/%s/app_index.html' % app_label, + 'admin/app_index.html' + ], context) class DefaultAdminSite(LazyObject): def _setup(self): - AdminSiteClass = import_string(apps.get_app_config("admin").default_site) + AdminSiteClass = import_string(apps.get_app_config('admin').default_site) self._wrapped = AdminSiteClass() - def __repr__(self): - return repr(self._wrapped) - # This global object represents the default admin site, for the common case. # You can provide your own AdminSite using the (Simple)AdminConfig.default_site diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/changelists.css b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/changelists.css index a4baf32..b4a1557 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/changelists.css +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/changelists.css @@ -125,10 +125,6 @@ margin-right: 4px; } -#changelist-search .help { - word-break: break-word; -} - /* FILTER COLUMN */ #changelist-filter { diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/forms.css b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/forms.css index bb44be7..89b2270 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/forms.css +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/forms.css @@ -37,19 +37,16 @@ label { /* RADIO BUTTONS */ -form div.radiolist div { - padding-right: 7px; +form ul.radiolist li { + list-style-type: none; } -form div.radiolist.inline div { - display: inline-block; +form ul.radiolist label { + float: none; + display: inline; } -form div.radiolist label { - width: auto; -} - -form div.radiolist input[type="radio"] { +form ul.radiolist input[type="radio"] { margin: -2px 4px 0 0; padding: 0; } @@ -87,7 +84,6 @@ form ul.inline li { margin-top: 0; margin-bottom: 0; margin-left: 170px; - overflow-wrap: break-word; } .aligned ul label { @@ -109,7 +105,7 @@ form .aligned ul { padding-left: 10px; } -form .aligned div.radiolist { +form .aligned ul.radiolist { display: inline-block; margin: 0; padding: 0; diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/login.css b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/login.css index bf4ba8d..10d9d22 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/login.css +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/login.css @@ -13,7 +13,6 @@ .login #header h1 { font-size: 18px; - margin: 0; } .login #header h1 a { diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/nav_sidebar.css b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/nav_sidebar.css index 0c590ff..f3c2fd8 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/nav_sidebar.css +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/nav_sidebar.css @@ -60,10 +60,13 @@ } .main.shifted > #nav-sidebar { + left: 24px; margin-left: 0; } [dir="rtl"] .main.shifted > #nav-sidebar { + left: 0; + right: 24px; margin-right: 0; } @@ -115,25 +118,3 @@ max-width: 100%; } } - -#nav-filter { - width: 100%; - box-sizing: border-box; - padding: 2px 5px; - margin: 5px 0; - border: 1px solid var(--border-color); - background-color: var(--darkened-bg); - color: var(--body-fg); -} - -#nav-filter:focus { - border-color: var(--body-quiet-color); -} - -#nav-filter.no-results { - background: var(--message-error-bg); -} - -#nav-sidebar table { - width: 100%; -} diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/responsive.css b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/responsive.css index 5779c5a..8c6dd81 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/css/responsive.css +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/css/responsive.css @@ -232,7 +232,7 @@ input[type="submit"], button { margin-left: 15px; } - form .aligned div.radiolist { + form .aligned ul.radiolist { margin-left: 2px; } @@ -650,13 +650,12 @@ input[type="submit"], button { padding-left: 0; } - form .aligned div.radiolist { - margin-top: 5px; + form .aligned ul.radiolist { margin-right: 15px; margin-bottom: -3px; } - form .aligned div.radiolist:not(.inline) div + div { + form .aligned ul.radiolist:not(.inline) li + li { margin-top: 5px; } diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/actions.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/actions.js index 20a5c14..da1c310 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/actions.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/actions.js @@ -36,10 +36,7 @@ function clearAcross(options) { reset(options); - const acrossInputs = document.querySelectorAll(options.acrossInput); - acrossInputs.forEach(function(acrossInput) { - acrossInput.value = 0; - }); + document.querySelector(options.acrossInput).value = 0; document.querySelector(options.actionContainer).classList.remove(options.selectedClass); } @@ -110,10 +107,8 @@ document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) { el.addEventListener('click', function(event) { event.preventDefault(); - const acrossInputs = document.querySelectorAll(options.acrossInput); - acrossInputs.forEach(function(acrossInput) { - acrossInput.value = 1; - }); + const acrossInput = document.querySelector(options.acrossInput); + acrossInput.value = 1; showClear(options); }); }); @@ -156,7 +151,7 @@ }); }); - document.querySelector('#changelist-form button[name=index]').addEventListener('click', function(event) { + document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() { if (list_editable_changed) { const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); if (!confirmed) { diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/autocomplete.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/autocomplete.js index 6095abe..c55eee1 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/autocomplete.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/autocomplete.js @@ -1,22 +1,28 @@ 'use strict'; { const $ = django.jQuery; - - $.fn.djangoAdminSelect2 = function() { - $.each(this, function(i, element) { - $(element).select2({ - ajax: { - data: (params) => { - return { - term: params.term, - page: params.page, - app_label: element.dataset.appLabel, - model_name: element.dataset.modelName, - field_name: element.dataset.fieldName - }; - } + const init = function($element, options) { + const settings = $.extend({ + ajax: { + data: function(params) { + return { + term: params.term, + page: params.page, + app_label: $element.data('app-label'), + model_name: $element.data('model-name'), + field_name: $element.data('field-name') + }; } - }); + } + }, options); + $element.select2(settings); + }; + + $.fn.djangoAdminSelect2 = function(options) { + const settings = $.extend({}, options); + $.each(this, function(i, element) { + const $element = $(element); + init($element, settings); }); return this; }; diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/core.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/core.js index afdae28..3a2e4aa 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/core.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/core.js @@ -1,4 +1,4 @@ -// Core JavaScript helper functions +// Core javascript helper functions 'use strict'; // quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]); diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/inlines.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/inlines.js index d9a9032..82ec027 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/inlines.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/inlines.js @@ -218,10 +218,12 @@ // instantiate a new SelectFilter instance for it. if (typeof SelectFilter !== 'undefined') { $('.selectfilter').each(function(index, value) { - SelectFilter.init(value.id, this.dataset.fieldName, false); + const namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], false); }); $('.selectfilterstacked').each(function(index, value) { - SelectFilter.init(value.id, this.dataset.fieldName, true); + const namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], true); }); } }; @@ -281,10 +283,12 @@ // If any SelectFilter widgets were added, instantiate a new instance. if (typeof SelectFilter !== "undefined") { $(".selectfilter").each(function(index, value) { - SelectFilter.init(value.id, this.dataset.fieldName, false); + const namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], false); }); $(".selectfilterstacked").each(function(index, value) { - SelectFilter.init(value.id, this.dataset.fieldName, true); + const namearr = value.name.split('-'); + SelectFilter.init(value.id, namearr[namearr.length - 1], true); }); } }; diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/nav_sidebar.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/nav_sidebar.js index 86cb1cf..efaa721 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/nav_sidebar.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/nav_sidebar.js @@ -36,58 +36,4 @@ main.classList.toggle('shifted'); }); } - - function initSidebarQuickFilter() { - const options = []; - const navSidebar = document.getElementById('nav-sidebar'); - if (!navSidebar) { - return; - } - navSidebar.querySelectorAll('th[scope=row] a').forEach((container) => { - options.push({title: container.innerHTML, node: container}); - }); - - function checkValue(event) { - let filterValue = event.target.value; - if (filterValue) { - filterValue = filterValue.toLowerCase(); - } - if (event.key === 'Escape') { - filterValue = ''; - event.target.value = ''; // clear input - } - let matches = false; - for (const o of options) { - let displayValue = ''; - if (filterValue) { - if (o.title.toLowerCase().indexOf(filterValue) === -1) { - displayValue = 'none'; - } else { - matches = true; - } - } - // show/hide parent <TR> - o.node.parentNode.parentNode.style.display = displayValue; - } - if (!filterValue || matches) { - event.target.classList.remove('no-results'); - } else { - event.target.classList.add('no-results'); - } - sessionStorage.setItem('django.admin.navSidebarFilterValue', filterValue); - } - - const nav = document.getElementById('nav-filter'); - nav.addEventListener('change', checkValue, false); - nav.addEventListener('input', checkValue, false); - nav.addEventListener('keyup', checkValue, false); - - const storedValue = sessionStorage.getItem('django.admin.navSidebarFilterValue'); - if (storedValue) { - nav.value = storedValue; - checkValue({target: nav, key: ''}); - } - } - window.initSidebarQuickFilter = initSidebarQuickFilter; - initSidebarQuickFilter(); } diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt index f642c3f..e3dbacb 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright OpenJS Foundation and other contributors, https://openjsf.org/ +Copyright JS Foundation and other contributors, https://js.foundation/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.js index fc6c299..5093733 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.js @@ -1,15 +1,15 @@ /*! - * jQuery JavaScript Library v3.6.0 + * jQuery JavaScript Library v3.5.1 * https://jquery.com/ * * Includes Sizzle.js * https://sizzlejs.com/ * - * Copyright OpenJS Foundation and other contributors + * Copyright JS Foundation and other contributors * Released under the MIT license * https://jquery.org/license * - * Date: 2021-03-02T17:08Z + * Date: 2020-05-04T22:49Z */ ( function( global, factory ) { @@ -76,16 +76,12 @@ var support = {}; var isFunction = function isFunction( obj ) { - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML <object> elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 - // Plus for old WebKit, typeof returns "function" for HTML collections - // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) - return typeof obj === "function" && typeof obj.nodeType !== "number" && - typeof obj.item !== "function"; - }; + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML <object> elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; var isWindow = function isWindow( obj ) { @@ -151,7 +147,7 @@ function toType( obj ) { var - version = "3.6.0", + version = "3.5.1", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -405,7 +401,7 @@ jQuery.extend( { if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? - [ arr ] : arr + [ arr ] : arr ); } else { push.call( ret, arr ); @@ -500,9 +496,9 @@ if ( typeof Symbol === "function" ) { // Populate the class2type map jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), - function( _i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); - } ); +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); function isArrayLike( obj ) { @@ -522,14 +518,14 @@ function isArrayLike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.3.6 + * Sizzle CSS Selector Engine v2.3.5 * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://js.foundation/ * - * Date: 2021-02-16 + * Date: 2020-03-14 */ ( function( window ) { var i, @@ -1112,8 +1108,8 @@ support = Sizzle.support = {}; * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { - var namespace = elem && elem.namespaceURI, - docElem = elem && ( elem.ownerDocument || elem ).documentElement; + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; // Support: IE <=8 // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes @@ -3028,9 +3024,9 @@ var rneedsContext = jQuery.expr.match.needsContext; function nodeName( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); -} +}; var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); @@ -4001,8 +3997,8 @@ jQuery.extend( { resolveContexts = Array( i ), resolveValues = slice.call( arguments ), - // the primary Deferred - primary = jQuery.Deferred(), + // the master Deferred + master = jQuery.Deferred(), // subordinate callback factory updateFunc = function( i ) { @@ -4010,30 +4006,30 @@ jQuery.extend( { resolveContexts[ i ] = this; resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { - primary.resolveWith( resolveContexts, resolveValues ); + master.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve if ( remaining <= 1 ) { - adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, !remaining ); // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( primary.state() === "pending" || + if ( master.state() === "pending" || isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - return primary.then(); + return master.then(); } } // Multiple arguments are aggregated like Promise.all array elements while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } - return primary.promise(); + return master.promise(); } } ); @@ -4184,8 +4180,8 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } @@ -5093,7 +5089,10 @@ function buildFragment( elems, context, scripts, selection, ignored ) { } -var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; @@ -5388,8 +5387,8 @@ jQuery.event = { event = jQuery.event.fix( nativeEvent ), handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event @@ -5513,12 +5512,12 @@ jQuery.event = { get: isFunction( hook ) ? function() { if ( this.originalEvent ) { - return hook( this.originalEvent ); + return hook( this.originalEvent ); } } : function() { if ( this.originalEvent ) { - return this.originalEvent[ name ]; + return this.originalEvent[ name ]; } }, @@ -5657,13 +5656,7 @@ function leverageNative( el, type, expectSync ) { // Cancel the outer synthetic event event.stopImmediatePropagation(); event.preventDefault(); - - // Support: Chrome 86+ - // In Chrome, if an element having a focusout handler is blurred by - // clicking outside of it, it invokes the handler synchronously. If - // that handler calls `.remove()` on the element, the data is cleared, - // leaving `result` undefined. We need to guard against this. - return result && result.value; + return result.value; } // If this is an inner synthetic event for an event with a bubbling surrogate @@ -5828,7 +5821,34 @@ jQuery.each( { targetTouches: true, toElement: true, touches: true, - which: true + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } }, jQuery.event.addProp ); jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { @@ -5854,12 +5874,6 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp return true; }, - // Suppress native focus or blur as it's already being fired - // in leverageNative. - _default: function() { - return true; - }, - delegateType: delegateType }; } ); @@ -6527,10 +6541,6 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); // set in CSS while `offset*` properties report correct values. // Behavior in IE 9 is more subtle than in newer versions & it passes // some versions of this test; make sure not to make it pass there! - // - // Support: Firefox 70+ - // Only Firefox includes border widths - // in computed dimensions. (gh-4529) reliableTrDimensions: function() { var table, tr, trChild, trStyle; if ( reliableTrDimensionsVal == null ) { @@ -6538,32 +6548,17 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); tr = document.createElement( "tr" ); trChild = document.createElement( "div" ); - table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "border:1px solid"; - - // Support: Chrome 86+ - // Height set through cssText does not get applied. - // Computed height then comes back as 0. + table.style.cssText = "position:absolute;left:-11111px"; tr.style.height = "1px"; trChild.style.height = "9px"; - // Support: Android 8 Chrome 86+ - // In our bodyBackground.html iframe, - // display for all div elements is set to "inline", - // which causes a problem only in Android 8 Chrome 86. - // Ensuring the div is display: block - // gets around this issue. - trChild.style.display = "block"; - documentElement .appendChild( table ) .appendChild( tr ) .appendChild( trChild ); trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + - parseInt( trStyle.borderTopWidth, 10 ) + - parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; documentElement.removeChild( table ); } @@ -7027,10 +7022,10 @@ jQuery.each( [ "height", "width" ], function( _i, dimension ) { // Running getBoundingClientRect on a disconnected node // in IE throws an error. ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); } }, @@ -7089,7 +7084,7 @@ jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, swap( elem, { marginLeft: 0 }, function() { return elem.getBoundingClientRect().left; } ) - ) + "px"; + ) + "px"; } } ); @@ -7228,7 +7223,7 @@ Tween.propHooks = { if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || + jQuery.cssHooks[ tween.prop ] || tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { @@ -7473,7 +7468,7 @@ function defaultPrefilter( elem, props, opts ) { anim.done( function() { - /* eslint-enable no-loop-func */ + /* eslint-enable no-loop-func */ // The final step of a "hide" animation is actually hiding the element if ( !hidden ) { @@ -7593,7 +7588,7 @@ function Animation( elem, properties, options ) { tweens: [], createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); return tween; }, @@ -7766,8 +7761,7 @@ jQuery.fn.extend( { anim.stop( true ); } }; - - doAnimation.finish = doAnimation; + doAnimation.finish = doAnimation; return empty || optall.queue === false ? this.each( doAnimation ) : @@ -8407,8 +8401,8 @@ jQuery.fn.extend( { if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" + "" : + dataPriv.get( this, "__className__" ) || "" ); } } @@ -8423,7 +8417,7 @@ jQuery.fn.extend( { while ( ( elem = this[ i++ ] ) ) { if ( elem.nodeType === 1 && ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; + return true; } } @@ -8713,7 +8707,9 @@ jQuery.extend( jQuery.event, { special.bindType || type; // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && dataPriv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); @@ -8860,7 +8856,7 @@ var rquery = ( /\?/ ); // Cross-browser xml parsing jQuery.parseXML = function( data ) { - var xml, parserErrorElem; + var xml; if ( !data || typeof data !== "string" ) { return null; } @@ -8869,17 +8865,12 @@ jQuery.parseXML = function( data ) { // IE throws on parseFromString with invalid input. try { xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} + } catch ( e ) { + xml = undefined; + } - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); } return xml; }; @@ -8980,14 +8971,16 @@ jQuery.fn.extend( { // Can add propHook for "elements" to filter or add form elements var elements = jQuery.prop( this, "elements" ); return elements ? jQuery.makeArray( elements ) : this; - } ).filter( function() { + } ) + .filter( function() { var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works return this.name && !jQuery( this ).is( ":disabled" ) && rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && ( this.checked || !rcheckableType.test( type ) ); - } ).map( function( _i, elem ) { + } ) + .map( function( _i, elem ) { var val = jQuery( this ).val(); if ( val == null ) { @@ -9040,8 +9033,7 @@ var // Anchor tag for parsing the document origin originAnchor = document.createElement( "a" ); - -originAnchor.href = location.href; + originAnchor.href = location.href; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { @@ -9422,8 +9414,8 @@ jQuery.extend( { // Context for global events is callbackContext if it is a DOM node or jQuery collection globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, + jQuery( callbackContext ) : + jQuery.event, // Deferreds deferred = jQuery.Deferred(), @@ -9735,10 +9727,8 @@ jQuery.extend( { response = ajaxHandleResponses( s, jqXHR, responses ); } - // Use a noop converter for missing script but not if jsonp - if ( !isSuccess && - jQuery.inArray( "script", s.dataTypes ) > -1 && - jQuery.inArray( "json", s.dataTypes ) < 0 ) { + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { s.converters[ "text script" ] = function() {}; } @@ -10476,6 +10466,12 @@ jQuery.offset = { options.using.call( elem, props ); } else { + if ( typeof props.top === "number" ) { + props.top += "px"; + } + if ( typeof props.left === "number" ) { + props.left += "px"; + } curElem.css( props ); } } @@ -10644,11 +10640,8 @@ jQuery.each( [ "top", "left" ], function( _i, prop ) { // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { - jQuery.each( { - padding: "inner" + name, - content: type, - "": "outer" + name - }, function( defaultExtra, funcName ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, + function( defaultExtra, funcName ) { // Margin is only for outerHeight, outerWidth jQuery.fn[ funcName ] = function( margin, value ) { @@ -10733,8 +10726,7 @@ jQuery.fn.extend( { } } ); -jQuery.each( - ( "blur focus focusin focusout resize scroll click dblclick " + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu" ).split( " " ), function( _i, name ) { @@ -10745,8 +10737,7 @@ jQuery.each( this.on( name, null, data, fn ) : this.trigger( name ); }; - } -); + } ); diff --git a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js index c4c6022..b061403 100644 --- a/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js +++ b/venv/Lib/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js @@ -1,2 +1,2 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&D.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(j),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(j).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var D,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function Te(){return!1}function Ce(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ee(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,we)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=be.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=be.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click",we),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?we:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=we,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=we,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=we,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Se(this,e,Ce),!1},trigger:function(){return Se(this,e),!0},_default:function(){return!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return Ee(this,e,t,n,r)},one:function(e,t,n,r){return Ee(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){S.event.remove(this,e,n,t)})}});var ke=/<script|<style|<link/i,Ae=/checked\s*(?:[^=]|=\s*.checked.)/i,Ne=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function He(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Ae.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),He(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),De)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,qe),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(Ne,""),u,l))}return n}function Oe(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Le(o[r],a[r]);else Le(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Oe(this,e,!0)},remove:function(e){return Oe(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return He(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||je(this,e).appendChild(e)})},prepend:function(){return He(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=je(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ke.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return He(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Pe=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Re=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Me=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Ie=new RegExp(ne.join("|"),"i");function We(e,t,n){var r,i,o,a,s=e.style;return(n=n||Re(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Pe.test(a)&&Ie.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function Fe(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,re.removeChild(e)),a}}))}();var Be=["Webkit","Moz","ms"],$e=E.createElement("div").style,_e={};function ze(e){var t=S.cssProps[e]||_e[e];return t||(e in $e?e:_e[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Be.length;while(n--)if((e=Be[n]+t)in $e)return e}(e)||e)}var Ue=/^(none|table(?!-c[ea]).+)/,Xe=/^--/,Ve={position:"absolute",visibility:"hidden",display:"block"},Ge={letterSpacing:"0",fontWeight:"400"};function Ye(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Qe(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Je(e,t,n){var r=Re(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=We(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Pe.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Qe(e,t,n||(i?"border":"content"),o,r,a)+"px"}function Ke(e,t,n,r,i){return new Ke.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=We(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Xe.test(t),l=e.style;if(u||(t=ze(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Xe.test(t)||(t=ze(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=We(e,t,r)),"normal"===i&&t in Ge&&(i=Ge[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ue.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Je(e,u,n):Me(e,Ve,function(){return Je(e,u,n)})},set:function(e,t,n){var r,i=Re(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Qe(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Qe(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Ye(0,t,s)}}}),S.cssHooks.marginLeft=Fe(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(We(e,"marginLeft"))||e.getBoundingClientRect().left-Me(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Ye)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Re(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=Ke).prototype={constructor:Ke,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=Ke.propHooks[this.prop];return e&&e.get?e.get(this):Ke.propHooks._default.get(this)},run:function(e){var t,n=Ke.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ke.propHooks._default.set(this),this}}).init.prototype=Ke.prototype,(Ke.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[ze(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=Ke.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=Ke.prototype.init,S.fx.step={};var Ze,et,tt,nt,rt=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function ot(){et&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(ot):C.setTimeout(ot,S.fx.interval),S.fx.tick())}function at(){return C.setTimeout(function(){Ze=void 0}),Ze=Date.now()}function st(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ut(e,t,n){for(var r,i=(lt.tweeners[t]||[]).concat(lt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function lt(o,e,t){var n,a,r=0,i=lt.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=Ze||at(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:Ze||at(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=lt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ut,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(lt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],lt.tweeners[n]=lt.tweeners[n]||[],lt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],rt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ut(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?lt.prefilters.unshift(e):lt.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=lt(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&it.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(st(r,!0),e,t,n)}}),S.each({slideDown:st("show"),slideUp:st("hide"),slideToggle:st("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(Ze=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),Ze=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){et||(et=!0,ot())},S.fx.stop=function(){et=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},tt=E.createElement("input"),nt=E.createElement("select").appendChild(E.createElement("option")),tt.type="checkbox",y.checkOn=""!==tt.value,y.optSelected=nt.selected,(tt=E.createElement("input")).value="t",tt.type="radio",y.radioValue="t"===tt.value;var ct,ft=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?ct:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ct={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=ft[t]||S.find.attr;ft[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=ft[o],ft[o]=r,r=null!=a(e,t,n)?o:null,ft[o]=i),r}});var pt=/^(?:input|select|textarea|button)$/i,dt=/^(?:a|area)$/i;function ht(e){return(e.match(P)||[]).join(" ")}function gt(e){return e.getAttribute&&e.getAttribute("class")||""}function vt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):pt.test(e.nodeName)||dt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,gt(this)))});if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,gt(this)))});if(!arguments.length)return this.attr("class","");if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,gt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=vt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=gt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+ht(gt(n))+" ").indexOf(t))return!0;return!1}});var yt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(yt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:ht(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var mt=/^(?:focusinfocus|focusoutblur)$/,xt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!mt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,mt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,xt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,xt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var bt=C.location,wt={guid:Date.now()},Tt=/\?/;S.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||S.error("Invalid XML: "+(n?S.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Ct=/\[\]$/,Et=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,kt=/^(?:input|select|textarea|keygen)/i;function At(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||Ct.test(n)?i(n,t):At(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)At(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)At(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&kt.test(this.nodeName)&&!St.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(Et,"\r\n")}}):{name:t.name,value:n.replace(Et,"\r\n")}}).get()}});var Nt=/%20/g,jt=/#.*$/,Dt=/([?&])_=[^&]*/,qt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Lt=/^(?:GET|HEAD)$/,Ht=/^\/\//,Ot={},Pt={},Rt="*/".concat("*"),Mt=E.createElement("a");function It(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Wt(t,i,o,a){var s={},u=t===Pt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Ft(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Mt.href=bt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:bt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(bt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Rt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Ft(Ft(e,S.ajaxSettings),t):Ft(S.ajaxSettings,e)},ajaxPrefilter:It(Ot),ajaxTransport:It(Pt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=qt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||bt.href)+"").replace(Ht,bt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Mt.protocol+"//"+Mt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Wt(Ot,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Lt.test(v.type),f=v.url.replace(jt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Nt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Tt.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Dt,"$1"),o=(Tt.test(f)?"&":"?")+"_="+wt.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+Rt+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Wt(Pt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&S.inArray("json",v.dataTypes)<0&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Bt={0:200,1223:204},$t=S.ajaxSettings.xhr();y.cors=!!$t&&"withCredentials"in $t,y.ajax=$t=!!$t,S.ajaxTransport(function(i){var o,a;if(y.cors||$t&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Bt[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=ht(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Xt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Xt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Vt=C.jQuery,Gt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Gt),e&&C.jQuery===S&&(C.jQuery=Vt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/app_list.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/app_list.html index 00c4178..ea4a85b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/app_list.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/app_list.html @@ -2,15 +2,15 @@ {% if app_list %} {% for app in app_list %} - <div class="app-{{ app.app_label }} module{% if app.app_url in request.path|urlencode %} current-app{% endif %}"> + <div class="app-{{ app.app_label }} module{% if app.app_url in request.path %} current-app{% endif %}"> <table> <caption> <a href="{{ app.app_url }}" class="section" title="{% blocktranslate with name=app.name %}Models in the {{ name }} application{% endblocktranslate %}">{{ app.name }}</a> </caption> {% for model in app.models %} - <tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path|urlencode %} current-model{% endif %}"> + <tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path %} current-model{% endif %}"> {% if model.admin_url %} - <th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path|urlencode %} aria-current="page"{% endif %}>{{ model.name }}</a></th> + <th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path %} aria-current="page"{% endif %}>{{ model.name }}</a></th> {% else %} <th scope="row">{{ model.name }}</th> {% endif %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/auth/user/change_password.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/auth/user/change_password.html index 730e4c5..c107161 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/auth/user/change_password.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/auth/user/change_password.html @@ -19,7 +19,7 @@ <form{% if form_url %} action="{{ form_url }}"{% endif %} method="post" id="{{ opts.model_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %} <input type="text" name="username" value="{{ original.get_username }}" class="hidden"> <div> -{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %} +{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} {% if form.errors %} <p class="errornote"> {% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/base.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/base.html index 271f774..e3788b9 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/base.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/base.html @@ -18,6 +18,7 @@ {% endblock %} {% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %} </head> +{% load i18n %} <body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}" data-admin-utc-offset="{% now "Z" %}"> @@ -27,7 +28,6 @@ {% if not is_popup %} <!-- Header --> - {% block header %} <div id="header"> <div id="branding"> {% block branding %}{% endblock %} @@ -59,7 +59,6 @@ {% endblock %} {% block nav-global %}{% endblock %} </div> - {% endblock %} <!-- END Header --> {% block breadcrumbs %} <div class="breadcrumbs"> @@ -69,7 +68,7 @@ {% endblock %} {% endif %} - <div class="main" id="main"> + <div class="main shifted" id="main"> {% if not is_popup and is_nav_sidebar_enabled %} {% block nav-sidebar %} {% include "admin/nav_sidebar.html" %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/change_list_results.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/change_list_results.html index 399d1d3..f8099ce 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/change_list_results.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/change_list_results.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n static %} {% if result_hidden_fields %} <div class="hiddenfields">{# DIV for HTML validation #} {% for item in result_hidden_fields %}{{ item }}{% endfor %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html index ccc7134..25b7b58 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html @@ -1,4 +1,4 @@ -{% load i18n admin_urls %} +{% load i18n admin_urls static %} <div class="js-inline-admin-formset inline-group" id="{{ inline_admin_formset.formset.prefix }}-group" data-inline-type="stacked" diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html index 4c0a08c..7a4e7cb 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html @@ -15,9 +15,11 @@ <thead><tr> <th class="original"></th> {% for field in inline_admin_formset.fields %} - <th class="column-{{ field.name }}{% if field.required %} required{% endif %}{% if field.widget.is_hidden %} hidden{% endif %}">{{ field.label|capfirst }} - {% if field.help_text %}<img src="{% static "admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}">{% endif %} - </th> + {% if not field.widget.is_hidden %} + <th class="column-{{ field.name }}{% if field.required %} required{% endif %}">{{ field.label|capfirst }} + {% if field.help_text %}<img src="{% static "admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}">{% endif %} + </th> + {% endif %} {% endfor %} {% if inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission %}<th>{% translate "Delete?" %}</th>{% endif %} </tr></thead> @@ -39,11 +41,21 @@ </p>{% endif %} {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %} {% if inline_admin_form.fk_field %}{{ inline_admin_form.fk_field.field }}{% endif %} + {% spaceless %} + {% for fieldset in inline_admin_form %} + {% for line in fieldset %} + {% for field in line %} + {% if not field.is_readonly and field.field.is_hidden %}{{ field.field }}{% endif %} + {% endfor %} + {% endfor %} + {% endfor %} + {% endspaceless %} </td> {% for fieldset in inline_admin_form %} {% for line in fieldset %} {% for field in line %} - <td class="{% if field.field.name %}field-{{ field.field.name }}{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"> + {% if field.is_readonly or not field.field.is_hidden %} + <td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}> {% if field.is_readonly %} <p>{{ field.contents }}</p> {% else %} @@ -51,6 +63,7 @@ {{ field.field }} {% endif %} </td> + {% endif %} {% endfor %} {% endfor %} {% endfor %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/nav_sidebar.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/nav_sidebar.html index 6ddff01..32c5b8f 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/nav_sidebar.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/nav_sidebar.html @@ -1,8 +1,5 @@ {% load i18n %} <button class="sticky toggle-nav-sidebar" id="toggle-nav-sidebar" aria-label="{% translate 'Toggle navigation' %}"></button> <nav class="sticky" id="nav-sidebar"> - <input type="search" id="nav-filter" - placeholder="{% translate 'Start typing to filter…' %}" - aria-label="{% translate 'Filter navigation items' %}"> {% include 'admin/app_list.html' with app_list=available_apps show_changelinks=False %} </nav> diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html index dd6e561..bbe1916 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html @@ -1,4 +1,4 @@ -{% load static %} +{% load l10n static %} <script id="django-admin-prepopulated-fields-constants" src="{% static "admin/js/prepopulate_init.js" %}" data-prepopulated-fields="{{ prepopulated_fields_json }}"> diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/admin/search_form.html b/venv/Lib/site-packages/django/contrib/admin/templates/admin/search_form.html index fd7818a..3ec37f2 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/admin/search_form.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/admin/search_form.html @@ -6,15 +6,11 @@ <input type="text" size="40" name="{{ search_var }}" value="{{ cl.query }}" id="searchbar" autofocus> <input type="submit" value="{% translate 'Search' %}"> {% if show_result_count %} - <span class="small quiet">{% blocktranslate count counter=cl.result_count %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktranslate %} (<a href="?{% if cl.is_popup %}{{ is_popup_var }}=1{% endif %}">{% if cl.show_full_result_count %}{% blocktranslate with full_result_count=cl.full_result_count %}{{ full_result_count }} total{% endblocktranslate %}{% else %}{% translate "Show all" %}{% endif %}</a>)</span> + <span class="small quiet">{% blocktranslate count counter=cl.result_count %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktranslate %} (<a href="?{% if cl.is_popup %}_popup=1{% endif %}">{% if cl.show_full_result_count %}{% blocktranslate with full_result_count=cl.full_result_count %}{{ full_result_count }} total{% endblocktranslate %}{% else %}{% translate "Show all" %}{% endif %}</a>)</span> {% endif %} {% for pair in cl.params.items %} {% if pair.0 != search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}">{% endif %} {% endfor %} </div> -{% if cl.search_help_text %} -<br class="clear"> -<div class="help">{{ cl.search_help_text }}</div> -{% endif %} </form></div> {% endif %} diff --git a/venv/Lib/site-packages/django/contrib/admin/templates/registration/logged_out.html b/venv/Lib/site-packages/django/contrib/admin/templates/registration/logged_out.html index e9a5545..460e17e 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templates/registration/logged_out.html +++ b/venv/Lib/site-packages/django/contrib/admin/templates/registration/logged_out.html @@ -7,7 +7,7 @@ {% block content %} -<p>{% translate "Thanks for spending some quality time with the web site today." %}</p> +<p>{% translate "Thanks for spending some quality time with the Web site today." %}</p> <p><a href="{% url 'admin:index' %}">{% translate 'Log in again' %}</a></p> diff --git a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_list.py b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_list.py index 5865843..f4b6d8d 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_list.py +++ b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_list.py @@ -1,20 +1,12 @@ import datetime -from django.conf import settings from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.utils import ( - display_for_field, - display_for_value, - get_fields_from_path, - label_for_field, - lookup_field, + display_for_field, display_for_value, get_fields_from_path, + label_for_field, lookup_field, ) from django.contrib.admin.views.main import ( - ALL_VAR, - IS_POPUP_VAR, - ORDER_VAR, - PAGE_VAR, - SEARCH_VAR, + ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR, ) from django.core.exceptions import ObjectDoesNotExist from django.db import models @@ -39,14 +31,14 @@ def paginator_number(cl, i): Generate an individual page index link in a paginated list. """ if i == cl.paginator.ELLIPSIS: - return format_html("{} ", cl.paginator.ELLIPSIS) + return format_html('{} ', cl.paginator.ELLIPSIS) elif i == cl.page_num: return format_html('<span class="this-page">{}</span> ', i) else: return format_html( '<a href="{}"{}>{}</a> ', cl.get_query_string({PAGE_VAR: i}), - mark_safe(' class="end"' if i == cl.paginator.num_pages else ""), + mark_safe(' class="end"' if i == cl.paginator.num_pages else ''), i, ) @@ -56,27 +48,24 @@ def pagination(cl): Generate the series of links to the pages in a paginated list. """ pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page - page_range = ( - cl.paginator.get_elided_page_range(cl.page_num) if pagination_required else [] - ) + page_range = cl.paginator.get_elided_page_range(cl.page_num) if pagination_required else [] need_show_all_link = cl.can_show_all and not cl.show_all and cl.multi_page return { - "cl": cl, - "pagination_required": pagination_required, - "show_all_url": need_show_all_link and cl.get_query_string({ALL_VAR: ""}), - "page_range": page_range, - "ALL_VAR": ALL_VAR, - "1": 1, + 'cl': cl, + 'pagination_required': pagination_required, + 'show_all_url': need_show_all_link and cl.get_query_string({ALL_VAR: ''}), + 'page_range': page_range, + 'ALL_VAR': ALL_VAR, + '1': 1, } -@register.tag(name="pagination") +@register.tag(name='pagination') def pagination_tag(parser, token): return InclusionAdminNode( - parser, - token, + parser, token, func=pagination, - template_name="pagination.html", + template_name='pagination.html', takes_context=False, ) @@ -88,7 +77,9 @@ def result_headers(cl): ordering_field_columns = cl.get_ordering_field_columns() for i, field_name in enumerate(cl.list_display): text, attr = label_for_field( - field_name, cl.model, model_admin=cl.model_admin, return_attr=True + field_name, cl.model, + model_admin=cl.model_admin, + return_attr=True ) is_field_sortable = cl.sortable_by is None or field_name in cl.sortable_by if attr: @@ -96,7 +87,7 @@ def result_headers(cl): # Potentially not sortable # if the field is the action checkbox: no sorting and special class - if field_name == "action_checkbox": + if field_name == 'action_checkbox': yield { "text": text, "class_attrib": mark_safe(' class="action-checkbox-column"'), @@ -106,32 +97,32 @@ def result_headers(cl): admin_order_field = getattr(attr, "admin_order_field", None) # Set ordering for attr that is a property, if defined. - if isinstance(attr, property) and hasattr(attr, "fget"): - admin_order_field = getattr(attr.fget, "admin_order_field", None) + if isinstance(attr, property) and hasattr(attr, 'fget'): + admin_order_field = getattr(attr.fget, 'admin_order_field', None) if not admin_order_field: is_field_sortable = False if not is_field_sortable: # Not sortable yield { - "text": text, - "class_attrib": format_html(' class="column-{}"', field_name), - "sortable": False, + 'text': text, + 'class_attrib': format_html(' class="column-{}"', field_name), + 'sortable': False, } continue # OK, it is sortable if we got this far - th_classes = ["sortable", "column-{}".format(field_name)] - order_type = "" - new_order_type = "asc" + th_classes = ['sortable', 'column-{}'.format(field_name)] + order_type = '' + new_order_type = 'asc' sort_priority = 0 # Is it currently being sorted on? is_sorted = i in ordering_field_columns if is_sorted: order_type = ordering_field_columns.get(i).lower() sort_priority = list(ordering_field_columns).index(i) + 1 - th_classes.append("sorted %sending" % order_type) - new_order_type = {"asc": "desc", "desc": "asc"}[order_type] + th_classes.append('sorted %sending' % order_type) + new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type] # build new ordering param o_list_primary = [] # URL for making this field the primary sort @@ -139,7 +130,7 @@ def result_headers(cl): o_list_toggle = [] # URL for toggling order type for this field def make_qs_param(t, n): - return ("-" if t == "desc" else "") + str(n) + return ('-' if t == 'desc' else '') + str(n) for j, ot in ordering_field_columns.items(): if j == i: # Same column @@ -164,19 +155,15 @@ def result_headers(cl): "sorted": is_sorted, "ascending": order_type == "asc", "sort_priority": sort_priority, - "url_primary": cl.get_query_string({ORDER_VAR: ".".join(o_list_primary)}), - "url_remove": cl.get_query_string({ORDER_VAR: ".".join(o_list_remove)}), - "url_toggle": cl.get_query_string({ORDER_VAR: ".".join(o_list_toggle)}), - "class_attrib": format_html(' class="{}"', " ".join(th_classes)) - if th_classes - else "", + "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}), + "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}), + "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}), + "class_attrib": format_html(' class="{}"', ' '.join(th_classes)) if th_classes else '', } def _boolean_icon(field_val): - icon_url = static( - "admin/img/icon-%s.svg" % {True: "yes", False: "no", None: "unknown"}[field_val] - ) + icon_url = static('admin/img/icon-%s.svg' % {True: 'yes', False: 'no', None: 'unknown'}[field_val]) return format_html('<img src="{}" alt="{}">', icon_url, field_val) @@ -185,8 +172,8 @@ def _coerce_field_name(field_name, field_index): Coerce a field_name (which may be a callable) to a string. """ if callable(field_name): - if field_name.__name__ == "<lambda>": - return "lambda" + str(field_index) + if field_name.__name__ == '<lambda>': + return 'lambda' + str(field_index) else: return field_name.__name__ return field_name @@ -208,22 +195,20 @@ def items_for_result(cl, result, form): pk = cl.lookup_opts.pk.attname for field_index, field_name in enumerate(cl.list_display): empty_value_display = cl.model_admin.get_empty_value_display() - row_classes = ["field-%s" % _coerce_field_name(field_name, field_index)] + row_classes = ['field-%s' % _coerce_field_name(field_name, field_index)] try: f, attr, value = lookup_field(field_name, result, cl.model_admin) except ObjectDoesNotExist: result_repr = empty_value_display else: - empty_value_display = getattr( - attr, "empty_value_display", empty_value_display - ) + empty_value_display = getattr(attr, 'empty_value_display', empty_value_display) if f is None or f.auto_created: - if field_name == "action_checkbox": - row_classes = ["action-checkbox"] - boolean = getattr(attr, "boolean", False) + if field_name == 'action_checkbox': + row_classes = ['action-checkbox'] + boolean = getattr(attr, 'boolean', False) result_repr = display_for_value(value, empty_value_display, boolean) if isinstance(value, (datetime.date, datetime.time)): - row_classes.append("nowrap") + row_classes.append('nowrap') else: if isinstance(f.remote_field, models.ManyToOneRel): field_val = getattr(result, f.name) @@ -233,14 +218,12 @@ def items_for_result(cl, result, form): result_repr = field_val else: result_repr = display_for_field(value, f, empty_value_display) - if isinstance( - f, (models.DateField, models.TimeField, models.ForeignKey) - ): - row_classes.append("nowrap") - row_class = mark_safe(' class="%s"' % " ".join(row_classes)) + if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): + row_classes.append('nowrap') + row_class = mark_safe(' class="%s"' % ' '.join(row_classes)) # If list_display_links not defined, add the link tag to the first field if link_in_col(first, field_name, cl): - table_tag = "th" if first else "td" + table_tag = 'th' if first else 'td' first = False # Display link to the result's change_view if the url exists, else @@ -250,10 +233,8 @@ def items_for_result(cl, result, form): except NoReverseMatch: link_or_text = result_repr else: - url = add_preserved_filters( - {"preserved_filters": cl.preserved_filters, "opts": cl.opts}, url - ) - # Convert the pk to something that can be used in JavaScript. + url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url) + # Convert the pk to something that can be used in Javascript. # Problem cases are non-ASCII strings. if cl.to_field: attr = str(cl.to_field) @@ -263,32 +244,24 @@ def items_for_result(cl, result, form): link_or_text = format_html( '<a href="{}"{}>{}</a>', url, - format_html(' data-popup-opener="{}"', value) - if cl.is_popup - else "", - result_repr, - ) + format_html( + ' data-popup-opener="{}"', value + ) if cl.is_popup else '', + result_repr) - yield format_html( - "<{}{}>{}</{}>", table_tag, row_class, link_or_text, table_tag - ) + yield format_html('<{}{}>{}</{}>', table_tag, row_class, link_or_text, table_tag) else: # By default the fields come from ModelAdmin.list_editable, but if we pull # the fields out of the form instead of list_editable custom admins # can provide fields on a per request basis - if ( - form - and field_name in form.fields - and not ( - field_name == cl.model._meta.pk.name - and form[cl.model._meta.pk.name].is_hidden - ) - ): + if (form and field_name in form.fields and not ( + field_name == cl.model._meta.pk.name and + form[cl.model._meta.pk.name].is_hidden)): bf = form[field_name] result_repr = mark_safe(str(bf.errors) + str(bf)) - yield format_html("<td{}>{}</td>", row_class, result_repr) + yield format_html('<td{}>{}</td>', row_class, result_repr) if form and not form[cl.model._meta.pk.name].is_hidden: - yield format_html("<td>{}</td>", form[cl.model._meta.pk.name]) + yield format_html('<td>{}</td>', form[cl.model._meta.pk.name]) class ResultList(list): @@ -297,7 +270,6 @@ class ResultList(list): with the form object for error reporting purposes. Needed to maintain backwards compatibility with existing admin templates. """ - def __init__(self, form, *items): self.form = form super().__init__(*items) @@ -326,24 +298,23 @@ def result_list(cl): headers = list(result_headers(cl)) num_sorted_fields = 0 for h in headers: - if h["sortable"] and h["sorted"]: + if h['sortable'] and h['sorted']: num_sorted_fields += 1 return { - "cl": cl, - "result_hidden_fields": list(result_hidden_fields(cl)), - "result_headers": headers, - "num_sorted_fields": num_sorted_fields, - "results": list(results(cl)), + 'cl': cl, + 'result_hidden_fields': list(result_hidden_fields(cl)), + 'result_headers': headers, + 'num_sorted_fields': num_sorted_fields, + 'results': list(results(cl)), } -@register.tag(name="result_list") +@register.tag(name='result_list') def result_list_tag(parser, token): return InclusionAdminNode( - parser, - token, + parser, token, func=result_list, - template_name="change_list_results.html", + template_name='change_list_results.html', takes_context=False, ) @@ -356,15 +327,15 @@ def date_hierarchy(cl): field_name = cl.date_hierarchy field = get_fields_from_path(cl.model, field_name)[-1] if isinstance(field, models.DateTimeField): - dates_or_datetimes = "datetimes" - qs_kwargs = {"is_dst": True} if settings.USE_DEPRECATED_PYTZ else {} + dates_or_datetimes = 'datetimes' + qs_kwargs = {'is_dst': True} else: - dates_or_datetimes = "dates" + dates_or_datetimes = 'dates' qs_kwargs = {} - year_field = "%s__year" % field_name - month_field = "%s__month" % field_name - day_field = "%s__day" % field_name - field_generic = "%s__" % field_name + year_field = '%s__year' % field_name + month_field = '%s__month' % field_name + day_field = '%s__day' % field_name + field_generic = '%s__' % field_name year_lookup = cl.params.get(year_field) month_lookup = cl.params.get(month_field) day_lookup = cl.params.get(day_field) @@ -374,99 +345,73 @@ def date_hierarchy(cl): if not (year_lookup or month_lookup or day_lookup): # select appropriate start level - date_range = cl.queryset.aggregate( - first=models.Min(field_name), last=models.Max(field_name) - ) - if date_range["first"] and date_range["last"]: - if dates_or_datetimes == "datetimes": + date_range = cl.queryset.aggregate(first=models.Min(field_name), + last=models.Max(field_name)) + if date_range['first'] and date_range['last']: + if dates_or_datetimes == 'datetimes': date_range = { k: timezone.localtime(v) if timezone.is_aware(v) else v for k, v in date_range.items() } - if date_range["first"].year == date_range["last"].year: - year_lookup = date_range["first"].year - if date_range["first"].month == date_range["last"].month: - month_lookup = date_range["first"].month + if date_range['first'].year == date_range['last'].year: + year_lookup = date_range['first'].year + if date_range['first'].month == date_range['last'].month: + month_lookup = date_range['first'].month if year_lookup and month_lookup and day_lookup: day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) return { - "show": True, - "back": { - "link": link({year_field: year_lookup, month_field: month_lookup}), - "title": capfirst(formats.date_format(day, "YEAR_MONTH_FORMAT")), + 'show': True, + 'back': { + 'link': link({year_field: year_lookup, month_field: month_lookup}), + 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT')) }, - "choices": [ - {"title": capfirst(formats.date_format(day, "MONTH_DAY_FORMAT"))} - ], + 'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}] } elif year_lookup and month_lookup: - days = getattr(cl.queryset, dates_or_datetimes)( - field_name, "day", **qs_kwargs - ) + days = getattr(cl.queryset, dates_or_datetimes)(field_name, 'day', **qs_kwargs) return { - "show": True, - "back": { - "link": link({year_field: year_lookup}), - "title": str(year_lookup), + 'show': True, + 'back': { + 'link': link({year_field: year_lookup}), + 'title': str(year_lookup) }, - "choices": [ - { - "link": link( - { - year_field: year_lookup, - month_field: month_lookup, - day_field: day.day, - } - ), - "title": capfirst(formats.date_format(day, "MONTH_DAY_FORMAT")), - } - for day in days - ], + 'choices': [{ + 'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}), + 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) + } for day in days] } elif year_lookup: - months = getattr(cl.queryset, dates_or_datetimes)( - field_name, "month", **qs_kwargs - ) + months = getattr(cl.queryset, dates_or_datetimes)(field_name, 'month', **qs_kwargs) return { - "show": True, - "back": {"link": link({}), "title": _("All dates")}, - "choices": [ - { - "link": link( - {year_field: year_lookup, month_field: month.month} - ), - "title": capfirst( - formats.date_format(month, "YEAR_MONTH_FORMAT") - ), - } - for month in months - ], + 'show': True, + 'back': { + 'link': link({}), + 'title': _('All dates') + }, + 'choices': [{ + 'link': link({year_field: year_lookup, month_field: month.month}), + 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT')) + } for month in months] } else: - years = getattr(cl.queryset, dates_or_datetimes)( - field_name, "year", **qs_kwargs - ) + years = getattr(cl.queryset, dates_or_datetimes)(field_name, 'year', **qs_kwargs) return { - "show": True, - "back": None, - "choices": [ - { - "link": link({year_field: str(year.year)}), - "title": str(year.year), - } - for year in years - ], + 'show': True, + 'back': None, + 'choices': [{ + 'link': link({year_field: str(year.year)}), + 'title': str(year.year), + } for year in years] } -@register.tag(name="date_hierarchy") +@register.tag(name='date_hierarchy') def date_hierarchy_tag(parser, token): return InclusionAdminNode( - parser, - token, + parser, token, func=date_hierarchy, - template_name="date_hierarchy.html", + template_name='date_hierarchy.html', takes_context=False, ) @@ -476,34 +421,25 @@ def search_form(cl): Display a search form for searching the list. """ return { - "cl": cl, - "show_result_count": cl.result_count != cl.full_result_count, - "search_var": SEARCH_VAR, - "is_popup_var": IS_POPUP_VAR, + 'cl': cl, + 'show_result_count': cl.result_count != cl.full_result_count, + 'search_var': SEARCH_VAR } -@register.tag(name="search_form") +@register.tag(name='search_form') def search_form_tag(parser, token): - return InclusionAdminNode( - parser, - token, - func=search_form, - template_name="search_form.html", - takes_context=False, - ) + return InclusionAdminNode(parser, token, func=search_form, template_name='search_form.html', takes_context=False) @register.simple_tag def admin_list_filter(cl, spec): tpl = get_template(spec.template) - return tpl.render( - { - "title": spec.title, - "choices": list(spec.choices(cl)), - "spec": spec, - } - ) + return tpl.render({ + 'title': spec.title, + 'choices': list(spec.choices(cl)), + 'spec': spec, + }) def admin_actions(context): @@ -511,23 +447,20 @@ def admin_actions(context): Track the number of times the action field has been rendered on the page, so we know which value to use. """ - context["action_index"] = context.get("action_index", -1) + 1 + context['action_index'] = context.get('action_index', -1) + 1 return context -@register.tag(name="admin_actions") +@register.tag(name='admin_actions') def admin_actions_tag(parser, token): - return InclusionAdminNode( - parser, token, func=admin_actions, template_name="actions.html" - ) + return InclusionAdminNode(parser, token, func=admin_actions, template_name='actions.html') -@register.tag(name="change_list_object_tools") +@register.tag(name='change_list_object_tools') def change_list_object_tools_tag(parser, token): """Display the row of change list object tools.""" return InclusionAdminNode( - parser, - token, + parser, token, func=lambda context: context, - template_name="change_list_object_tools.html", + template_name='change_list_object_tools.html', ) diff --git a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_modify.py b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_modify.py index 9df4b7a..ee5f23b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_modify.py +++ b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_modify.py @@ -10,123 +10,94 @@ register = template.Library() def prepopulated_fields_js(context): """ - Create a list of prepopulated_fields that should render JavaScript for + Create a list of prepopulated_fields that should render Javascript for the prepopulated fields for both the admin form and inlines. """ prepopulated_fields = [] - if "adminform" in context: - prepopulated_fields.extend(context["adminform"].prepopulated_fields) - if "inline_admin_formsets" in context: - for inline_admin_formset in context["inline_admin_formsets"]: + if 'adminform' in context: + prepopulated_fields.extend(context['adminform'].prepopulated_fields) + if 'inline_admin_formsets' in context: + for inline_admin_formset in context['inline_admin_formsets']: for inline_admin_form in inline_admin_formset: if inline_admin_form.original is None: prepopulated_fields.extend(inline_admin_form.prepopulated_fields) prepopulated_fields_json = [] for field in prepopulated_fields: - prepopulated_fields_json.append( - { - "id": "#%s" % field["field"].auto_id, - "name": field["field"].name, - "dependency_ids": [ - "#%s" % dependency.auto_id for dependency in field["dependencies"] - ], - "dependency_list": [ - dependency.name for dependency in field["dependencies"] - ], - "maxLength": field["field"].field.max_length or 50, - "allowUnicode": getattr(field["field"].field, "allow_unicode", False), - } - ) + prepopulated_fields_json.append({ + "id": "#%s" % field["field"].auto_id, + "name": field["field"].name, + "dependency_ids": ["#%s" % dependency.auto_id for dependency in field["dependencies"]], + "dependency_list": [dependency.name for dependency in field["dependencies"]], + "maxLength": field["field"].field.max_length or 50, + "allowUnicode": getattr(field["field"].field, "allow_unicode", False) + }) - context.update( - { - "prepopulated_fields": prepopulated_fields, - "prepopulated_fields_json": json.dumps(prepopulated_fields_json), - } - ) + context.update({ + 'prepopulated_fields': prepopulated_fields, + 'prepopulated_fields_json': json.dumps(prepopulated_fields_json), + }) return context -@register.tag(name="prepopulated_fields_js") +@register.tag(name='prepopulated_fields_js') def prepopulated_fields_js_tag(parser, token): - return InclusionAdminNode( - parser, - token, - func=prepopulated_fields_js, - template_name="prepopulated_fields_js.html", - ) + return InclusionAdminNode(parser, token, func=prepopulated_fields_js, template_name="prepopulated_fields_js.html") def submit_row(context): """ Display the row of buttons for delete and save. """ - add = context["add"] - change = context["change"] - is_popup = context["is_popup"] - save_as = context["save_as"] - show_save = context.get("show_save", True) - show_save_and_add_another = context.get("show_save_and_add_another", True) - show_save_and_continue = context.get("show_save_and_continue", True) - has_add_permission = context["has_add_permission"] - has_change_permission = context["has_change_permission"] - has_view_permission = context["has_view_permission"] - has_editable_inline_admin_formsets = context["has_editable_inline_admin_formsets"] - can_save = ( - (has_change_permission and change) - or (has_add_permission and add) - or has_editable_inline_admin_formsets - ) + add = context['add'] + change = context['change'] + is_popup = context['is_popup'] + save_as = context['save_as'] + show_save = context.get('show_save', True) + show_save_and_add_another = context.get('show_save_and_add_another', True) + show_save_and_continue = context.get('show_save_and_continue', True) + has_add_permission = context['has_add_permission'] + has_change_permission = context['has_change_permission'] + has_view_permission = context['has_view_permission'] + has_editable_inline_admin_formsets = context['has_editable_inline_admin_formsets'] + can_save = (has_change_permission and change) or (has_add_permission and add) or has_editable_inline_admin_formsets can_save_and_add_another = ( - has_add_permission - and not is_popup - and (not save_as or add) - and can_save - and show_save_and_add_another - ) - can_save_and_continue = ( - not is_popup and can_save and has_view_permission and show_save_and_continue + has_add_permission and + not is_popup and + (not save_as or add) and + can_save and + show_save_and_add_another ) + can_save_and_continue = not is_popup and can_save and has_view_permission and show_save_and_continue can_change = has_change_permission or has_editable_inline_admin_formsets ctx = Context(context) - ctx.update( - { - "can_change": can_change, - "show_delete_link": ( - not is_popup - and context["has_delete_permission"] - and change - and context.get("show_delete", True) - ), - "show_save_as_new": not is_popup - and has_change_permission - and change - and save_as, - "show_save_and_add_another": can_save_and_add_another, - "show_save_and_continue": can_save_and_continue, - "show_save": show_save and can_save, - "show_close": not (show_save and can_save), - } - ) + ctx.update({ + 'can_change': can_change, + 'show_delete_link': ( + not is_popup and context['has_delete_permission'] and + change and context.get('show_delete', True) + ), + 'show_save_as_new': not is_popup and has_change_permission and change and save_as, + 'show_save_and_add_another': can_save_and_add_another, + 'show_save_and_continue': can_save_and_continue, + 'show_save': show_save and can_save, + 'show_close': not(show_save and can_save) + }) return ctx -@register.tag(name="submit_row") +@register.tag(name='submit_row') def submit_row_tag(parser, token): - return InclusionAdminNode( - parser, token, func=submit_row, template_name="submit_line.html" - ) + return InclusionAdminNode(parser, token, func=submit_row, template_name='submit_line.html') -@register.tag(name="change_form_object_tools") +@register.tag(name='change_form_object_tools') def change_form_object_tools_tag(parser, token): """Display the row of change form object tools.""" return InclusionAdminNode( - parser, - token, + parser, token, func=lambda context: context, - template_name="change_form_object_tools.html", + template_name='change_form_object_tools.html', ) @@ -135,15 +106,10 @@ def cell_count(inline_admin_form): """Return the number of cells used in a tabular inline.""" count = 1 # Hidden cell with hidden 'id' field for fieldset in inline_admin_form: - # Count all visible fields. + # Loop through all the fields (one per cell) for line in fieldset: for field in line: - try: - is_hidden = field.field.is_hidden - except AttributeError: - is_hidden = field.field["is_hidden"] - if not is_hidden: - count += 1 + count += 1 if inline_admin_form.formset.can_delete: # Delete checkbox count += 1 diff --git a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_urls.py b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_urls.py index 13ded03..f817c25 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_urls.py +++ b/venv/Lib/site-packages/django/contrib/admin/templatetags/admin_urls.py @@ -10,7 +10,7 @@ register = template.Library() @register.filter def admin_urlname(value, arg): - return "admin:%s_%s_%s" % (value.app_label, value.model_name, arg) + return 'admin:%s_%s_%s' % (value.app_label, value.model_name, arg) @register.filter @@ -20,8 +20,8 @@ def admin_urlquote(value): @register.simple_tag(takes_context=True) def add_preserved_filters(context, url, popup=False, to_field=None): - opts = context.get("opts") - preserved_filters = context.get("preserved_filters") + opts = context.get('opts') + preserved_filters = context.get('preserved_filters') parsed_url = list(urlparse(url)) parsed_qs = dict(parse_qsl(parsed_url[4])) @@ -30,34 +30,24 @@ def add_preserved_filters(context, url, popup=False, to_field=None): if opts and preserved_filters: preserved_filters = dict(parse_qsl(preserved_filters)) - match_url = "/%s" % unquote(url).partition(get_script_prefix())[2] + match_url = '/%s' % unquote(url).partition(get_script_prefix())[2] try: match = resolve(match_url) except Resolver404: pass else: - current_url = "%s:%s" % (match.app_name, match.url_name) - changelist_url = "admin:%s_%s_changelist" % ( - opts.app_label, - opts.model_name, - ) - if ( - changelist_url == current_url - and "_changelist_filters" in preserved_filters - ): - preserved_filters = dict( - parse_qsl(preserved_filters["_changelist_filters"]) - ) + current_url = '%s:%s' % (match.app_name, match.url_name) + changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name) + if changelist_url == current_url and '_changelist_filters' in preserved_filters: + preserved_filters = dict(parse_qsl(preserved_filters['_changelist_filters'])) merged_qs.update(preserved_filters) if popup: from django.contrib.admin.options import IS_POPUP_VAR - merged_qs[IS_POPUP_VAR] = 1 if to_field: from django.contrib.admin.options import TO_FIELD_VAR - merged_qs[TO_FIELD_VAR] = to_field merged_qs.update(parsed_qs) diff --git a/venv/Lib/site-packages/django/contrib/admin/templatetags/base.py b/venv/Lib/site-packages/django/contrib/admin/templatetags/base.py index 23e4cfb..e98604a 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templatetags/base.py +++ b/venv/Lib/site-packages/django/contrib/admin/templatetags/base.py @@ -11,35 +11,23 @@ class InclusionAdminNode(InclusionNode): def __init__(self, parser, token, func, template_name, takes_context=True): self.template_name = template_name - params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec( - func - ) + params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func) bits = token.split_contents() args, kwargs = parse_bits( - parser, - bits[1:], - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - takes_context, - bits[0], + parser, bits[1:], params, varargs, varkw, defaults, kwonly, + kwonly_defaults, takes_context, bits[0], ) super().__init__(func, takes_context, args, kwargs, filename=None) def render(self, context): - opts = context["opts"] + opts = context['opts'] app_label = opts.app_label.lower() object_name = opts.object_name.lower() # Load template for this render call. (Setting self.filename isn't # thread-safe.) - context.render_context[self] = context.template.engine.select_template( - [ - "admin/%s/%s/%s" % (app_label, object_name, self.template_name), - "admin/%s/%s" % (app_label, self.template_name), - "admin/%s" % self.template_name, - ] - ) + context.render_context[self] = context.template.engine.select_template([ + 'admin/%s/%s/%s' % (app_label, object_name, self.template_name), + 'admin/%s/%s' % (app_label, self.template_name), + 'admin/%s' % self.template_name, + ]) return super().render(context) diff --git a/venv/Lib/site-packages/django/contrib/admin/templatetags/log.py b/venv/Lib/site-packages/django/contrib/admin/templatetags/log.py index 098aa64..08c2345 100644 --- a/venv/Lib/site-packages/django/contrib/admin/templatetags/log.py +++ b/venv/Lib/site-packages/django/contrib/admin/templatetags/log.py @@ -19,10 +19,8 @@ class AdminLogNode(template.Node): if not user_id.isdigit(): user_id = context[self.user].pk entries = LogEntry.objects.filter(user__pk=user_id) - context[self.varname] = entries.select_related("content_type", "user")[ - : int(self.limit) - ] - return "" + context[self.varname] = entries.select_related('content_type', 'user')[:int(self.limit)] + return '' @register.tag @@ -32,7 +30,7 @@ def get_admin_log(parser, token): Usage:: - {% get_admin_log [limit] as [varname] for_user [context_var_with_user_obj] %} + {% get_admin_log [limit] as [varname] for_user [context_var_containing_user_obj] %} Examples:: @@ -47,23 +45,15 @@ def get_admin_log(parser, token): tokens = token.contents.split() if len(tokens) < 4: raise template.TemplateSyntaxError( - "'get_admin_log' statements require two arguments" - ) + "'get_admin_log' statements require two arguments") if not tokens[1].isdigit(): raise template.TemplateSyntaxError( - "First argument to 'get_admin_log' must be an integer" - ) - if tokens[2] != "as": + "First argument to 'get_admin_log' must be an integer") + if tokens[2] != 'as': raise template.TemplateSyntaxError( - "Second argument to 'get_admin_log' must be 'as'" - ) + "Second argument to 'get_admin_log' must be 'as'") if len(tokens) > 4: - if tokens[4] != "for_user": + if tokens[4] != 'for_user': raise template.TemplateSyntaxError( - "Fourth argument to 'get_admin_log' must be 'for_user'" - ) - return AdminLogNode( - limit=tokens[1], - varname=tokens[3], - user=(tokens[5] if len(tokens) > 5 else None), - ) + "Fourth argument to 'get_admin_log' must be 'for_user'") + return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(tokens[5] if len(tokens) > 5 else None)) diff --git a/venv/Lib/site-packages/django/contrib/admin/tests.py b/venv/Lib/site-packages/django/contrib/admin/tests.py index e3def6e..482027b 100644 --- a/venv/Lib/site-packages/django/contrib/admin/tests.py +++ b/venv/Lib/site-packages/django/contrib/admin/tests.py @@ -9,21 +9,20 @@ from django.utils.translation import gettext as _ class CSPMiddleware(MiddlewareMixin): """The admin's JavaScript should be compatible with CSP.""" - def process_response(self, request, response): - response.headers["Content-Security-Policy"] = "default-src 'self'" + response.headers['Content-Security-Policy'] = "default-src 'self'" return response -@modify_settings(MIDDLEWARE={"append": "django.contrib.admin.tests.CSPMiddleware"}) +@modify_settings(MIDDLEWARE={'append': 'django.contrib.admin.tests.CSPMiddleware'}) class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): available_apps = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.sites", + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', ] def wait_until(self, callback, timeout=10): @@ -34,7 +33,6 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): call this function for more details. """ from selenium.webdriver.support.wait import WebDriverWait - WebDriverWait(self.selenium, timeout).until(callback) def wait_for_and_switch_to_popup(self, num_windows=2, timeout=10): @@ -53,9 +51,9 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec - self.wait_until( - ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)), timeout + ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout ) def wait_for_text(self, css_selector, text, timeout=10): @@ -64,10 +62,10 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec - self.wait_until( - ec.text_to_be_present_in_element((By.CSS_SELECTOR, css_selector), text), - timeout, + ec.text_to_be_present_in_element( + (By.CSS_SELECTOR, css_selector), text), + timeout ) def wait_for_value(self, css_selector, text, timeout=10): @@ -76,12 +74,10 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec - self.wait_until( ec.text_to_be_present_in_element_value( - (By.CSS_SELECTOR, css_selector), text - ), - timeout, + (By.CSS_SELECTOR, css_selector), text), + timeout ) def wait_until_visible(self, css_selector, timeout=10): @@ -90,9 +86,9 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec - self.wait_until( - ec.visibility_of_element_located((By.CSS_SELECTOR, css_selector)), timeout + ec.visibility_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout ) def wait_until_invisible(self, css_selector, timeout=10): @@ -101,9 +97,9 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec - self.wait_until( - ec.invisibility_of_element_located((By.CSS_SELECTOR, css_selector)), timeout + ec.invisibility_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout ) def wait_page_ready(self, timeout=10): @@ -111,8 +107,7 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): Block until the page is ready. """ self.wait_until( - lambda driver: driver.execute_script("return document.readyState;") - == "complete", + lambda driver: driver.execute_script('return document.readyState;') == 'complete', timeout, ) @@ -122,27 +117,24 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): Block until a new page has loaded and is ready. """ from selenium.webdriver.support import expected_conditions as ec - - old_page = self.selenium.find_element_by_tag_name("html") + old_page = self.selenium.find_element_by_tag_name('html') yield # Wait for the next page to be loaded self.wait_until(ec.staleness_of(old_page), timeout=timeout) self.wait_page_ready(timeout=timeout) - def admin_login(self, username, password, login_url="/admin/"): + def admin_login(self, username, password, login_url='/admin/'): """ Log in to the admin. """ - self.selenium.get("%s%s" % (self.live_server_url, login_url)) - username_input = self.selenium.find_element_by_name("username") + self.selenium.get('%s%s' % (self.live_server_url, login_url)) + username_input = self.selenium.find_element_by_name('username') username_input.send_keys(username) - password_input = self.selenium.find_element_by_name("password") + password_input = self.selenium.find_element_by_name('password') password_input.send_keys(password) - login_text = _("Log in") + login_text = _('Log in') with self.wait_page_loaded(): - self.selenium.find_element_by_xpath( - '//input[@value="%s"]' % login_text - ).click() + self.selenium.find_element_by_xpath('//input[@value="%s"]' % login_text).click() def select_option(self, selector, value): """ @@ -150,7 +142,6 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): identified by the CSS selector `selector`. """ from selenium.webdriver.support.ui import Select - select = Select(self.selenium.find_element_by_css_selector(selector)) select.select_by_value(value) @@ -160,7 +151,6 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): identified by the CSS selector `selector`. """ from selenium.webdriver.support.ui import Select - select = Select(self.selenium.find_element_by_css_selector(selector)) select.deselect_by_value(value) @@ -169,7 +159,7 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): options = self.selenium.find_elements_by_css_selector(options_selector) actual_values = [] for option in options: - actual_values.append(option.get_attribute("value")) + actual_values.append(option.get_attribute('value')) self.assertEqual(values, actual_values) else: # Prevent the `find_elements_by_css_selector` call from blocking @@ -177,9 +167,7 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): # to be the case. with self.disable_implicit_wait(): self.wait_until( - lambda driver: not driver.find_elements_by_css_selector( - options_selector - ) + lambda driver: not driver.find_elements_by_css_selector(options_selector) ) def assertSelectOptions(self, selector, values): @@ -201,9 +189,5 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): Return True if the element identified by `selector` has the CSS class `klass`. """ - return ( - self.selenium.find_element_by_css_selector(selector) - .get_attribute("class") - .find(klass) - != -1 - ) + return (self.selenium.find_element_by_css_selector(selector) + .get_attribute('class').find(klass) != -1) diff --git a/venv/Lib/site-packages/django/contrib/admin/utils.py b/venv/Lib/site-packages/django/contrib/admin/utils.py index 5b875ae..ba06da5 100644 --- a/venv/Lib/site-packages/django/contrib/admin/utils.py +++ b/venv/Lib/site-packages/django/contrib/admin/utils.py @@ -13,28 +13,26 @@ from django.utils import formats, timezone from django.utils.html import format_html from django.utils.regex_helper import _lazy_re_compile from django.utils.text import capfirst -from django.utils.translation import ngettext -from django.utils.translation import override as translation_override +from django.utils.translation import ngettext, override as translation_override -QUOTE_MAP = {i: "_%02X" % i for i in b'":/_#?;@&=+$,"[]<>%\n\\'} +QUOTE_MAP = {i: '_%02X' % i for i in b'":/_#?;@&=+$,"[]<>%\n\\'} UNQUOTE_MAP = {v: chr(k) for k, v in QUOTE_MAP.items()} -UNQUOTE_RE = _lazy_re_compile("_(?:%s)" % "|".join([x[1:] for x in UNQUOTE_MAP])) +UNQUOTE_RE = _lazy_re_compile('_(?:%s)' % '|'.join([x[1:] for x in UNQUOTE_MAP])) class FieldIsAForeignKeyColumnName(Exception): """A field is a foreign key attname, i.e. <FK>_id.""" - pass -def lookup_spawns_duplicates(opts, lookup_path): +def lookup_needs_distinct(opts, lookup_path): """ - Return True if the given lookup path spawns duplicates. + Return True if 'distinct()' should be used to query the given lookup path. """ lookup_fields = lookup_path.split(LOOKUP_SEP) # Go through the fields (following all relations) and look for an m2m. for field_name in lookup_fields: - if field_name == "pk": + if field_name == 'pk': field_name = opts.pk.name try: field = opts.get_field(field_name) @@ -42,13 +40,12 @@ def lookup_spawns_duplicates(opts, lookup_path): # Ignore query lookups. continue else: - if hasattr(field, "get_path_info"): + if hasattr(field, 'get_path_info'): # This field is a relation; update opts to follow the relation. path_info = field.get_path_info() opts = path_info[-1].to_opts if any(path.m2m for path in path_info): - # This field is a m2m relation so duplicates must be - # handled. + # This field is a m2m relation so distinct must be called. return True return False @@ -58,11 +55,11 @@ def prepare_lookup_value(key, value): Return a lookup value prepared to be used in queryset filtering. """ # if key ends with __in, split parameter into separate values - if key.endswith("__in"): - value = value.split(",") + if key.endswith('__in'): + value = value.split(',') # if key ends with __isnull, special case '' and the string literals 'false' and '0' - elif key.endswith("__isnull"): - value = value.lower() not in ("", "false", "0") + elif key.endswith('__isnull'): + value = value.lower() not in ('', 'false', '0') return value @@ -71,7 +68,7 @@ def quote(s): Ensure that primary key values do not confuse the admin URLs by escaping any '/', '_' and ':' and similarly problematic characters. Similar to urllib.parse.quote(), except that the quoting is slightly - different so that it doesn't get automatically unquoted by the web browser. + different so that it doesn't get automatically unquoted by the Web browser. """ return s.translate(QUOTE_MAP) if isinstance(s, str) else s @@ -98,7 +95,9 @@ def flatten_fieldsets(fieldsets): """Return a list of field names from an admin fieldsets structure.""" field_names = [] for name, opts in fieldsets: - field_names.extend(flatten(opts["fields"])) + field_names.extend( + flatten(opts['fields']) + ) return field_names @@ -125,26 +124,26 @@ def get_deleted_objects(objs, request, admin_site): has_admin = model in admin_site._registry opts = obj._meta - no_edit_link = "%s: %s" % (capfirst(opts.verbose_name), obj) + no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj) if has_admin: if not admin_site._registry[model].has_delete_permission(request, obj): perms_needed.add(opts.verbose_name) try: - admin_url = reverse( - "%s:%s_%s_change" - % (admin_site.name, opts.app_label, opts.model_name), - None, - (quote(obj.pk),), - ) + admin_url = reverse('%s:%s_%s_change' + % (admin_site.name, + opts.app_label, + opts.model_name), + None, (quote(obj.pk),)) except NoReverseMatch: # Change url doesn't exist -- don't display link to edit return no_edit_link # Display a link to the admin page. - return format_html( - '{}: <a href="{}">{}</a>', capfirst(opts.verbose_name), admin_url, obj - ) + return format_html('{}: <a href="{}">{}</a>', + capfirst(opts.verbose_name), + admin_url, + obj) else: # Don't display link to edit, because it either has no # admin or is edited inline. @@ -153,10 +152,7 @@ def get_deleted_objects(objs, request, admin_site): to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] - model_count = { - model._meta.verbose_name_plural: len(objs) - for model, objs in collector.model_objs.items() - } + model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()} return to_delete, model_count, perms_needed, protected @@ -173,10 +169,10 @@ class NestedObjects(Collector): def collect(self, objs, source=None, source_attr=None, **kwargs): for obj in objs: - if source_attr and not source_attr.endswith("+"): + if source_attr and not source_attr.endswith('+'): related_name = source_attr % { - "class": source._meta.model_name, - "app_label": source._meta.app_label, + 'class': source._meta.model_name, + 'app_label': source._meta.app_label, } self.add_edge(getattr(obj, related_name), obj) else: @@ -191,9 +187,7 @@ class NestedObjects(Collector): def related_objects(self, related_model, related_fields, objs): qs = super().related_objects(related_model, related_fields, objs) - return qs.select_related( - *[related_field.name for related_field in related_fields] - ) + return qs.select_related(*[related_field.name for related_field in related_fields]) def _nested(self, obj, seen, format_callback): if obj in seen: @@ -242,8 +236,8 @@ def model_format_dict(obj): else: opts = obj return { - "verbose_name": opts.verbose_name, - "verbose_name_plural": opts.verbose_name_plural, + 'verbose_name': opts.verbose_name, + 'verbose_name_plural': opts.verbose_name_plural, } @@ -275,7 +269,7 @@ def lookup_field(name, obj, model_admin=None): if callable(name): attr = name value = attr(obj) - elif hasattr(model_admin, name) and name != "__str__": + elif hasattr(model_admin, name) and name != '__str__': attr = getattr(model_admin, name) value = attr(obj) else: @@ -300,21 +294,13 @@ def _get_non_gfk_field(opts, name): model (rather something like `foo_set`). """ field = opts.get_field(name) - if ( - field.is_relation - and - # Generic foreign keys OR reverse relations - ((field.many_to_one and not field.related_model) or field.one_to_many) - ): + if (field.is_relation and + # Generic foreign keys OR reverse relations + ((field.many_to_one and not field.related_model) or field.one_to_many)): raise FieldDoesNotExist() # Avoid coercing <FK>_id fields to FK - if ( - field.is_relation - and not field.many_to_many - and hasattr(field, "attname") - and field.attname == name - ): + if field.is_relation and not field.many_to_many and hasattr(field, 'attname') and field.attname == name: raise FieldIsAForeignKeyColumnName() return field @@ -350,10 +336,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False, form=None) elif form and name in form.fields: attr = form.fields[name] else: - message = "Unable to lookup '%s' on %s" % ( - name, - model._meta.object_name, - ) + message = "Unable to lookup '%s' on %s" % (name, model._meta.object_name) if model_admin: message += " or %s" % model_admin.__class__.__name__ if form: @@ -362,11 +345,9 @@ def label_for_field(name, model, model_admin=None, return_attr=False, form=None) if hasattr(attr, "short_description"): label = attr.short_description - elif ( - isinstance(attr, property) - and hasattr(attr, "fget") - and hasattr(attr.fget, "short_description") - ): + elif (isinstance(attr, property) and + hasattr(attr, "fget") and + hasattr(attr.fget, "short_description")): label = attr.fget.short_description elif callable(attr): if attr.__name__ == "<lambda>": @@ -392,7 +373,7 @@ def help_text_for_field(name, model): except (FieldDoesNotExist, FieldIsAForeignKeyColumnName): pass else: - if hasattr(field, "help_text"): + if hasattr(field, 'help_text'): help_text = field.help_text return help_text @@ -400,7 +381,7 @@ def help_text_for_field(name, model): def display_for_field(value, field, empty_value_display): from django.contrib.admin.templatetags.admin_list import _boolean_icon - if getattr(field, "flatchoices", None): + if getattr(field, 'flatchoices', None): return dict(field.flatchoices).get(value, empty_value_display) # BooleanField needs special-case null-handling, so it comes before the # general null test. @@ -443,7 +424,7 @@ def display_for_value(value, empty_value_display, boolean=False): elif isinstance(value, (int, decimal.Decimal, float)): return formats.number_format(value) elif isinstance(value, (list, tuple)): - return ", ".join(str(v) for v in value) + return ', '.join(str(v) for v in value) else: return str(value) @@ -453,14 +434,14 @@ class NotRelationField(Exception): def get_model_from_relation(field): - if hasattr(field, "get_path_info"): + if hasattr(field, 'get_path_info'): return field.get_path_info()[-1].to_opts.model else: raise NotRelationField def reverse_field_path(model, path): - """Create a reversed field path. + """ Create a reversed field path. E.g. Given (Order, "user__groups"), return (Group, "user__order"). @@ -491,7 +472,7 @@ def reverse_field_path(model, path): def get_fields_from_path(model, path): - """Return list of Fields given path relative to model. + """ Return list of Fields given path relative to model. e.g. (ModelX, "user__groups__name") -> [ <django.db.models.fields.related.ForeignKey object at 0x...>, @@ -528,42 +509,34 @@ def construct_change_message(form, formsets, add): change_message = [] if add: - change_message.append({"added": {}}) + change_message.append({'added': {}}) elif form.changed_data: - change_message.append({"changed": {"fields": changed_field_labels}}) + change_message.append({'changed': {'fields': changed_field_labels}}) if formsets: with translation_override(None): for formset in formsets: for added_object in formset.new_objects: - change_message.append( - { - "added": { - "name": str(added_object._meta.verbose_name), - "object": str(added_object), - } + change_message.append({ + 'added': { + 'name': str(added_object._meta.verbose_name), + 'object': str(added_object), } - ) + }) for changed_object, changed_fields in formset.changed_objects: - change_message.append( - { - "changed": { - "name": str(changed_object._meta.verbose_name), - "object": str(changed_object), - "fields": _get_changed_field_labels_from_form( - formset.forms[0], changed_fields - ), - } + change_message.append({ + 'changed': { + 'name': str(changed_object._meta.verbose_name), + 'object': str(changed_object), + 'fields': _get_changed_field_labels_from_form(formset.forms[0], changed_fields), } - ) + }) for deleted_object in formset.deleted_objects: - change_message.append( - { - "deleted": { - "name": str(deleted_object._meta.verbose_name), - "object": str(deleted_object), - } + change_message.append({ + 'deleted': { + 'name': str(deleted_object._meta.verbose_name), + 'object': str(deleted_object), } - ) + }) return change_message diff --git a/venv/Lib/site-packages/django/contrib/admin/views/autocomplete.py b/venv/Lib/site-packages/django/contrib/admin/views/autocomplete.py index 130848b..3903e4c 100644 --- a/venv/Lib/site-packages/django/contrib/admin/views/autocomplete.py +++ b/venv/Lib/site-packages/django/contrib/admin/views/autocomplete.py @@ -6,47 +6,31 @@ from django.views.generic.list import BaseListView class AutocompleteJsonView(BaseListView): """Handle AutocompleteWidget's AJAX requests for data.""" - paginate_by = 20 admin_site = None def get(self, request, *args, **kwargs): """ - Return a JsonResponse with search results as defined in - serialize_result(), by default: + Return a JsonResponse with search results of the form: { results: [{id: "123" text: "foo"}], pagination: {more: true} } """ - ( - self.term, - self.model_admin, - self.source_field, - to_field_name, - ) = self.process_request(request) + self.term, self.model_admin, self.source_field, to_field_name = self.process_request(request) if not self.has_perm(request): raise PermissionDenied self.object_list = self.get_queryset() context = self.get_context_data() - return JsonResponse( - { - "results": [ - self.serialize_result(obj, to_field_name) - for obj in context["object_list"] - ], - "pagination": {"more": context["page_obj"].has_next()}, - } - ) - - def serialize_result(self, obj, to_field_name): - """ - Convert the provided model object to a dictionary that is added to the - results list. - """ - return {"id": str(getattr(obj, to_field_name)), "text": str(obj)} + return JsonResponse({ + 'results': [ + {'id': str(getattr(obj, to_field_name)), 'text': str(obj)} + for obj in context['object_list'] + ], + 'pagination': {'more': context['page_obj'].has_next()}, + }) def get_paginator(self, *args, **kwargs): """Use the ModelAdmin's paginator.""" @@ -56,9 +40,7 @@ class AutocompleteJsonView(BaseListView): """Return queryset based on ModelAdmin.get_search_results().""" qs = self.model_admin.get_queryset(self.request) qs = qs.complex_filter(self.source_field.get_limit_choices_to()) - qs, search_use_distinct = self.model_admin.get_search_results( - self.request, qs, self.term - ) + qs, search_use_distinct = self.model_admin.get_search_results(self.request, qs, self.term) if search_use_distinct: qs = qs.distinct() return qs @@ -74,11 +56,11 @@ class AutocompleteJsonView(BaseListView): Raise Http404 if the target model admin is not configured properly with search_fields. """ - term = request.GET.get("term", "") + term = request.GET.get('term', '') try: - app_label = request.GET["app_label"] - model_name = request.GET["model_name"] - field_name = request.GET["field_name"] + app_label = request.GET['app_label'] + model_name = request.GET['model_name'] + field_name = request.GET['field_name'] except KeyError as e: raise PermissionDenied from e @@ -104,13 +86,11 @@ class AutocompleteJsonView(BaseListView): # Validate suitability of objects. if not model_admin.get_search_fields(request): raise Http404( - "%s must have search_fields for the autocomplete_view." - % type(model_admin).__qualname__ + '%s must have search_fields for the autocomplete_view.' % + type(model_admin).__qualname__ ) - to_field_name = getattr( - source_field.remote_field, "field_name", remote_model._meta.pk.attname - ) + to_field_name = getattr(source_field.remote_field, 'field_name', remote_model._meta.pk.attname) to_field_name = remote_model._meta.get_field(to_field_name).attname if not model_admin.to_field_allowed(request, to_field_name): raise PermissionDenied diff --git a/venv/Lib/site-packages/django/contrib/admin/views/decorators.py b/venv/Lib/site-packages/django/contrib/admin/views/decorators.py index c1b63ba..f14570c 100644 --- a/venv/Lib/site-packages/django/contrib/admin/views/decorators.py +++ b/venv/Lib/site-packages/django/contrib/admin/views/decorators.py @@ -2,9 +2,8 @@ from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.decorators import user_passes_test -def staff_member_required( - view_func=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url="admin:login" -): +def staff_member_required(view_func=None, redirect_field_name=REDIRECT_FIELD_NAME, + login_url='admin:login'): """ Decorator for views that checks that the user is logged in and is a staff member, redirecting to the login page if necessary. @@ -12,7 +11,7 @@ def staff_member_required( actual_decorator = user_passes_test( lambda u: u.is_active and u.is_staff, login_url=login_url, - redirect_field_name=redirect_field_name, + redirect_field_name=redirect_field_name ) if view_func: return actual_decorator(view_func) diff --git a/venv/Lib/site-packages/django/contrib/admin/views/main.py b/venv/Lib/site-packages/django/contrib/admin/views/main.py index ace4b34..a54ef25 100644 --- a/venv/Lib/site-packages/django/contrib/admin/views/main.py +++ b/venv/Lib/site-packages/django/contrib/admin/views/main.py @@ -5,24 +5,16 @@ from django.conf import settings from django.contrib import messages from django.contrib.admin import FieldListFilter from django.contrib.admin.exceptions import ( - DisallowedModelAdminLookup, - DisallowedModelAdminToField, + DisallowedModelAdminLookup, DisallowedModelAdminToField, ) from django.contrib.admin.options import ( - IS_POPUP_VAR, - TO_FIELD_VAR, - IncorrectLookupParameters, + IS_POPUP_VAR, TO_FIELD_VAR, IncorrectLookupParameters, ) from django.contrib.admin.utils import ( - get_fields_from_path, - lookup_spawns_duplicates, - prepare_lookup_value, - quote, + get_fields_from_path, lookup_needs_distinct, prepare_lookup_value, quote, ) from django.core.exceptions import ( - FieldDoesNotExist, - ImproperlyConfigured, - SuspiciousOperation, + FieldDoesNotExist, ImproperlyConfigured, SuspiciousOperation, ) from django.core.paginator import InvalidPage from django.db.models import Exists, F, Field, ManyToOneRel, OrderBy, OuterRef @@ -33,13 +25,15 @@ from django.utils.timezone import make_aware from django.utils.translation import gettext # Changelist settings -ALL_VAR = "all" -ORDER_VAR = "o" -PAGE_VAR = "p" -SEARCH_VAR = "q" -ERROR_FLAG = "e" +ALL_VAR = 'all' +ORDER_VAR = 'o' +ORDER_TYPE_VAR = 'ot' +PAGE_VAR = 'p' +SEARCH_VAR = 'q' +ERROR_FLAG = 'e' -IGNORED_PARAMS = (ALL_VAR, ORDER_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR) +IGNORED_PARAMS = ( + ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR) class ChangeListSearchForm(forms.Form): @@ -54,23 +48,9 @@ class ChangeListSearchForm(forms.Form): class ChangeList: search_form_class = ChangeListSearchForm - def __init__( - self, - request, - model, - list_display, - list_display_links, - list_filter, - date_hierarchy, - search_fields, - list_select_related, - list_per_page, - list_max_show_all, - list_editable, - model_admin, - sortable_by, - search_help_text, - ): + def __init__(self, request, model, list_display, list_display_links, + list_filter, date_hierarchy, search_fields, list_select_related, + list_per_page, list_max_show_all, list_editable, model_admin, sortable_by): self.model = model self.opts = model._meta self.lookup_opts = self.opts @@ -89,14 +69,13 @@ class ChangeList: self.model_admin = model_admin self.preserved_filters = model_admin.get_preserved_filters(request) self.sortable_by = sortable_by - self.search_help_text = search_help_text # Get search parameters from the query string. _search_form = self.search_form_class(request.GET) if not _search_form.is_valid(): for error in _search_form.errors.values(): - messages.error(request, ", ".join(error)) - self.query = _search_form.cleaned_data.get(SEARCH_VAR) or "" + messages.error(request, ', '.join(error)) + self.query = _search_form.cleaned_data.get(SEARCH_VAR) or '' try: self.page_num = int(request.GET.get(PAGE_VAR, 1)) except ValueError: @@ -105,9 +84,7 @@ class ChangeList: self.is_popup = IS_POPUP_VAR in request.GET to_field = request.GET.get(TO_FIELD_VAR) if to_field and not model_admin.to_field_allowed(request, to_field): - raise DisallowedModelAdminToField( - "The field %s cannot be referenced." % to_field - ) + raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field) self.to_field = to_field self.params = dict(request.GET.items()) if PAGE_VAR in self.params: @@ -122,21 +99,14 @@ class ChangeList: self.queryset = self.get_queryset(request) self.get_results(request) if self.is_popup: - title = gettext("Select %s") + title = gettext('Select %s') elif self.model_admin.has_change_permission(request): - title = gettext("Select %s to change") + title = gettext('Select %s to change') else: - title = gettext("Select %s to view") + title = gettext('Select %s to view') self.title = title % self.opts.verbose_name self.pk_attname = self.lookup_opts.pk.attname - def __repr__(self): - return "<%s: model=%s model_admin=%s>" % ( - self.__class__.__qualname__, - self.model.__qualname__, - self.model_admin.__class__.__qualname__, - ) - def get_filters_params(self, params=None): """ Return all params except IGNORED_PARAMS. @@ -180,21 +150,14 @@ class ChangeList: field = get_fields_from_path(self.model, field_path)[-1] spec = field_list_filter_class( - field, - request, - lookup_params, - self.model, - self.model_admin, - field_path=field_path, + field, request, lookup_params, + self.model, self.model_admin, field_path=field_path, ) # field_list_filter_class removes any lookup_params it - # processes. If that happened, check if duplicates should be - # removed. + # processes. If that happened, check if distinct() is needed to + # remove duplicate results. if lookup_params_count > len(lookup_params): - may_have_duplicates |= lookup_spawns_duplicates( - self.lookup_opts, - field_path, - ) + may_have_duplicates |= lookup_needs_distinct(self.lookup_opts, field_path) if spec and spec.has_output(): filter_specs.append(spec) if lookup_params_count > len(lookup_params): @@ -203,10 +166,10 @@ class ChangeList: if self.date_hierarchy: # Create bounded lookup parameters so that the query is more # efficient. - year = lookup_params.pop("%s__year" % self.date_hierarchy, None) + year = lookup_params.pop('%s__year' % self.date_hierarchy, None) if year is not None: - month = lookup_params.pop("%s__month" % self.date_hierarchy, None) - day = lookup_params.pop("%s__day" % self.date_hierarchy, None) + month = lookup_params.pop('%s__month' % self.date_hierarchy, None) + day = lookup_params.pop('%s__day' % self.date_hierarchy, None) try: from_date = datetime( int(year), @@ -226,28 +189,23 @@ class ChangeList: if settings.USE_TZ: from_date = make_aware(from_date) to_date = make_aware(to_date) - lookup_params.update( - { - "%s__gte" % self.date_hierarchy: from_date, - "%s__lt" % self.date_hierarchy: to_date, - } - ) + lookup_params.update({ + '%s__gte' % self.date_hierarchy: from_date, + '%s__lt' % self.date_hierarchy: to_date, + }) # At this point, all the parameters used by the various ListFilters # have been removed from lookup_params, which now only contains other # parameters passed via the query string. We now loop through the # remaining parameters both to ensure that all the parameters are valid - # fields and to determine if at least one of them spawns duplicates. If + # fields and to determine if at least one of them needs distinct(). If # the lookup parameters aren't real fields, then bail out. try: for key, value in lookup_params.items(): lookup_params[key] = prepare_lookup_value(key, value) - may_have_duplicates |= lookup_spawns_duplicates(self.lookup_opts, key) + may_have_duplicates |= lookup_needs_distinct(self.lookup_opts, key) return ( - filter_specs, - bool(filter_specs), - lookup_params, - may_have_duplicates, + filter_specs, bool(filter_specs), lookup_params, may_have_duplicates, has_active_filters, ) except FieldDoesNotExist as e: @@ -269,12 +227,10 @@ class ChangeList: del p[k] else: p[k] = v - return "?%s" % urlencode(sorted(p.items())) + return '?%s' % urlencode(sorted(p.items())) def get_results(self, request): - paginator = self.model_admin.get_paginator( - request, self.queryset, self.list_per_page - ) + paginator = self.model_admin.get_paginator(request, self.queryset, self.list_per_page) # Get the number of objects, with admin filters applied. result_count = paginator.count @@ -299,9 +255,7 @@ class ChangeList: self.show_full_result_count = self.model_admin.show_full_result_count # Admin actions are shown if there is at least one entry # or if entries are not counted because show_full_result_count is disabled - self.show_admin_actions = not self.show_full_result_count or bool( - full_result_count - ) + self.show_admin_actions = not self.show_full_result_count or bool(full_result_count) self.full_result_count = full_result_count self.result_list = result_list self.can_show_all = can_show_all @@ -336,9 +290,9 @@ class ChangeList: attr = getattr(self.model_admin, field_name) else: attr = getattr(self.model, field_name) - if isinstance(attr, property) and hasattr(attr, "fget"): + if isinstance(attr, property) and hasattr(attr, 'fget'): attr = attr.fget - return getattr(attr, "admin_order_field", None) + return getattr(attr, 'admin_order_field', None) def get_ordering(self, request, queryset): """ @@ -350,32 +304,28 @@ class ChangeList: constructed ordering. """ params = self.params - ordering = list( - self.model_admin.get_ordering(request) or self._get_default_ordering() - ) + ordering = list(self.model_admin.get_ordering(request) or self._get_default_ordering()) if ORDER_VAR in params: # Clear ordering and used params ordering = [] - order_params = params[ORDER_VAR].split(".") + order_params = params[ORDER_VAR].split('.') for p in order_params: try: - none, pfx, idx = p.rpartition("-") + none, pfx, idx = p.rpartition('-') field_name = self.list_display[int(idx)] order_field = self.get_ordering_field(field_name) if not order_field: continue # No 'admin_order_field', skip it if isinstance(order_field, OrderBy): - if pfx == "-": + if pfx == '-': order_field = order_field.copy() order_field.reverse_ordering() ordering.append(order_field) - elif hasattr(order_field, "resolve_expression"): + elif hasattr(order_field, 'resolve_expression'): # order_field is an expression. - ordering.append( - order_field.desc() if pfx == "-" else order_field.asc() - ) + ordering.append(order_field.desc() if pfx == '-' else order_field.asc()) # reverse order if order_field has already "-" as prefix - elif order_field.startswith("-") and pfx == "-": + elif order_field.startswith('-') and pfx == '-': ordering.append(order_field[1:]) else: ordering.append(pfx + order_field) @@ -396,16 +346,15 @@ class ChangeList: """ ordering = list(ordering) ordering_fields = set() - total_ordering_fields = {"pk"} | { - field.attname - for field in self.lookup_opts.fields + total_ordering_fields = {'pk'} | { + field.attname for field in self.lookup_opts.fields if field.unique and not field.null } for part in ordering: # Search for single field providing a total ordering. field_name = None if isinstance(part, str): - field_name = part.lstrip("-") + field_name = part.lstrip('-') elif isinstance(part, F): field_name = part.name elif isinstance(part, OrderBy) and isinstance(part.expression, F): @@ -437,9 +386,7 @@ class ChangeList: ) for field_names in constraint_field_names: # Normalize attname references by using get_field(). - fields = [ - self.lookup_opts.get_field(field_name) for field_name in field_names - ] + fields = [self.lookup_opts.get_field(field_name) for field_name in field_names] # Composite unique constraints containing a nullable column # cannot ensure total ordering. if any(field.null for field in fields): @@ -449,7 +396,7 @@ class ChangeList: else: # If no set of unique fields is present in the ordering, rely # on the primary key to provide total ordering. - ordering.append("-pk") + ordering.append('-pk') return ordering def get_ordering_field_columns(self): @@ -469,27 +416,27 @@ class ChangeList: if not isinstance(field, OrderBy): field = field.asc() if isinstance(field.expression, F): - order_type = "desc" if field.descending else "asc" + order_type = 'desc' if field.descending else 'asc' field = field.expression.name else: continue - elif field.startswith("-"): + elif field.startswith('-'): field = field[1:] - order_type = "desc" + order_type = 'desc' else: - order_type = "asc" + order_type = 'asc' for index, attr in enumerate(self.list_display): if self.get_ordering_field(attr) == field: ordering_fields[index] = order_type break else: - for p in self.params[ORDER_VAR].split("."): - none, pfx, idx = p.rpartition("-") + for p in self.params[ORDER_VAR].split('.'): + none, pfx, idx = p.rpartition('-') try: idx = int(idx) except ValueError: continue # skip it - ordering_fields[idx] = "desc" if pfx == "-" else "asc" + ordering_fields[idx] = 'desc' if pfx == '-' else 'asc' return ordering_fields def get_queryset(self, request): @@ -527,9 +474,7 @@ class ChangeList: # Apply search results qs, search_may_have_duplicates = self.model_admin.get_search_results( - request, - qs, - self.query, + request, qs, self.query, ) # Set query string for clearing all filters. @@ -539,7 +484,7 @@ class ChangeList: ) # Remove duplicates from results, if necessary if filters_may_have_duplicates | search_may_have_duplicates: - qs = qs.filter(pk=OuterRef("pk")) + qs = qs.filter(pk=OuterRef('pk')) qs = self.root_queryset.filter(Exists(qs)) # Set ordering. @@ -578,8 +523,7 @@ class ChangeList: def url_for_result(self, result): pk = getattr(result, self.pk_attname) - return reverse( - "admin:%s_%s_change" % (self.opts.app_label, self.opts.model_name), - args=(quote(pk),), - current_app=self.model_admin.admin_site.name, - ) + return reverse('admin:%s_%s_change' % (self.opts.app_label, + self.opts.model_name), + args=(quote(pk),), + current_app=self.model_admin.admin_site.name) diff --git a/venv/Lib/site-packages/django/contrib/admin/widgets.py b/venv/Lib/site-packages/django/contrib/admin/widgets.py index 8ee72fb..aeb7477 100644 --- a/venv/Lib/site-packages/django/contrib/admin/widgets.py +++ b/venv/Lib/site-packages/django/contrib/admin/widgets.py @@ -14,8 +14,7 @@ from django.urls.exceptions import NoReverseMatch from django.utils.html import smart_urlquote from django.utils.http import urlencode from django.utils.text import Truncator -from django.utils.translation import get_language -from django.utils.translation import gettext as _ +from django.utils.translation import get_language, gettext as _ class FilteredSelectMultiple(forms.SelectMultiple): @@ -25,12 +24,11 @@ class FilteredSelectMultiple(forms.SelectMultiple): Note that the resulting JavaScript assumes that the jsi18n catalog has been loaded in the page """ - class Media: js = [ - "admin/js/core.js", - "admin/js/SelectBox.js", - "admin/js/SelectFilter2.js", + 'admin/js/core.js', + 'admin/js/SelectBox.js', + 'admin/js/SelectFilter2.js', ] def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): @@ -40,35 +38,35 @@ class FilteredSelectMultiple(forms.SelectMultiple): def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["widget"]["attrs"]["class"] = "selectfilter" + context['widget']['attrs']['class'] = 'selectfilter' if self.is_stacked: - context["widget"]["attrs"]["class"] += "stacked" - context["widget"]["attrs"]["data-field-name"] = self.verbose_name - context["widget"]["attrs"]["data-is-stacked"] = int(self.is_stacked) + context['widget']['attrs']['class'] += 'stacked' + context['widget']['attrs']['data-field-name'] = self.verbose_name + context['widget']['attrs']['data-is-stacked'] = int(self.is_stacked) return context class AdminDateWidget(forms.DateInput): class Media: js = [ - "admin/js/calendar.js", - "admin/js/admin/DateTimeShortcuts.js", + 'admin/js/calendar.js', + 'admin/js/admin/DateTimeShortcuts.js', ] def __init__(self, attrs=None, format=None): - attrs = {"class": "vDateField", "size": "10", **(attrs or {})} + attrs = {'class': 'vDateField', 'size': '10', **(attrs or {})} super().__init__(attrs=attrs, format=format) class AdminTimeWidget(forms.TimeInput): class Media: js = [ - "admin/js/calendar.js", - "admin/js/admin/DateTimeShortcuts.js", + 'admin/js/calendar.js', + 'admin/js/admin/DateTimeShortcuts.js', ] def __init__(self, attrs=None, format=None): - attrs = {"class": "vTimeField", "size": "8", **(attrs or {})} + attrs = {'class': 'vTimeField', 'size': '8', **(attrs or {})} super().__init__(attrs=attrs, format=format) @@ -76,8 +74,7 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget): """ A SplitDateTime Widget that has some admin-specific styling. """ - - template_name = "admin/widgets/split_datetime.html" + template_name = 'admin/widgets/split_datetime.html' def __init__(self, attrs=None): widgets = [AdminDateWidget, AdminTimeWidget] @@ -87,17 +84,17 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget): def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["date_label"] = _("Date:") - context["time_label"] = _("Time:") + context['date_label'] = _('Date:') + context['time_label'] = _('Time:') return context class AdminRadioSelect(forms.RadioSelect): - template_name = "admin/widgets/radio.html" + template_name = 'admin/widgets/radio.html' class AdminFileWidget(forms.ClearableFileInput): - template_name = "admin/widgets/clearable_file_input.html" + template_name = 'admin/widgets/clearable_file_input.html' def url_params_from_lookup_dict(lookups): @@ -106,14 +103,14 @@ def url_params_from_lookup_dict(lookups): attribute to a dictionary of query parameters """ params = {} - if lookups and hasattr(lookups, "items"): + if lookups and hasattr(lookups, 'items'): for k, v in lookups.items(): if callable(v): v = v() if isinstance(v, (tuple, list)): - v = ",".join(str(x) for x in v) + v = ','.join(str(x) for x in v) elif isinstance(v, bool): - v = ("0", "1")[v] + v = ('0', '1')[v] else: v = str(v) params[k] = v @@ -125,8 +122,7 @@ class ForeignKeyRawIdWidget(forms.TextInput): A Widget for displaying ForeignKeys in the "raw_id" interface rather than in a <select> box. """ - - template_name = "admin/widgets/foreign_key_raw_id.html" + template_name = 'admin/widgets/foreign_key_raw_id.html' def __init__(self, rel, admin_site, attrs=None, using=None): self.rel = rel @@ -140,8 +136,7 @@ class ForeignKeyRawIdWidget(forms.TextInput): if rel_to in self.admin_site._registry: # The related object is registered with the same AdminSite related_url = reverse( - "admin:%s_%s_changelist" - % ( + 'admin:%s_%s_changelist' % ( rel_to._meta.app_label, rel_to._meta.model_name, ), @@ -150,19 +145,17 @@ class ForeignKeyRawIdWidget(forms.TextInput): params = self.url_parameters() if params: - related_url += "?" + urlencode(params) - context["related_url"] = related_url - context["link_title"] = _("Lookup") + related_url += '?' + urlencode(params) + context['related_url'] = related_url + context['link_title'] = _('Lookup') # The JavaScript code looks for this class. - context["widget"]["attrs"].setdefault("class", "vForeignKeyRawIdAdminField") + context['widget']['attrs'].setdefault('class', 'vForeignKeyRawIdAdminField') else: - context["related_url"] = None - if context["widget"]["value"]: - context["link_label"], context["link_url"] = self.label_and_url_for_value( - value - ) + context['related_url'] = None + if context['widget']['value']: + context['link_label'], context['link_url'] = self.label_and_url_for_value(value) else: - context["link_label"] = None + context['link_label'] = None return context def base_url_parameters(self): @@ -173,7 +166,6 @@ class ForeignKeyRawIdWidget(forms.TextInput): def url_parameters(self): from django.contrib.admin.views.main import TO_FIELD_VAR - params = self.base_url_parameters() params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) return params @@ -183,20 +175,19 @@ class ForeignKeyRawIdWidget(forms.TextInput): try: obj = self.rel.model._default_manager.using(self.db).get(**{key: value}) except (ValueError, self.rel.model.DoesNotExist, ValidationError): - return "", "" + return '', '' try: url = reverse( - "%s:%s_%s_change" - % ( + '%s:%s_%s_change' % ( self.admin_site.name, obj._meta.app_label, obj._meta.object_name.lower(), ), - args=(obj.pk,), + args=(obj.pk,) ) except NoReverseMatch: - url = "" # Admin not registered for target model. + url = '' # Admin not registered for target model. return Truncator(obj).words(14), url @@ -206,29 +197,28 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): A Widget for displaying ManyToMany ids in the "raw_id" interface rather than in a <select multiple> box. """ - - template_name = "admin/widgets/many_to_many_raw_id.html" + template_name = 'admin/widgets/many_to_many_raw_id.html' def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) if self.rel.model in self.admin_site._registry: # The related object is registered with the same AdminSite - context["widget"]["attrs"]["class"] = "vManyToManyRawIdAdminField" + context['widget']['attrs']['class'] = 'vManyToManyRawIdAdminField' return context def url_parameters(self): return self.base_url_parameters() def label_and_url_for_value(self, value): - return "", "" + return '', '' def value_from_datadict(self, data, files, name): value = data.get(name) if value: - return value.split(",") + return value.split(',') def format_value(self, value): - return ",".join(str(v) for v in value) if value else "" + return ','.join(str(v) for v in value) if value else '' class RelatedFieldWidgetWrapper(forms.Widget): @@ -236,19 +226,11 @@ class RelatedFieldWidgetWrapper(forms.Widget): This class is a wrapper to a given widget to add the add icon for the admin interface. """ + template_name = 'admin/widgets/related_widget_wrapper.html' - template_name = "admin/widgets/related_widget_wrapper.html" - - def __init__( - self, - widget, - rel, - admin_site, - can_add_related=None, - can_change_related=False, - can_delete_related=False, - can_view_related=False, - ): + def __init__(self, widget, rel, admin_site, can_add_related=None, + can_change_related=False, can_delete_related=False, + can_view_related=False): self.needs_multipart_form = widget.needs_multipart_form self.attrs = widget.attrs self.choices = widget.choices @@ -260,10 +242,10 @@ class RelatedFieldWidgetWrapper(forms.Widget): can_add_related = rel.model in admin_site._registry self.can_add_related = can_add_related # XXX: The UX does not support multiple selected values. - multiple = getattr(widget, "allow_multiple_selected", False) + multiple = getattr(widget, 'allow_multiple_selected', False) self.can_change_related = not multiple and can_change_related # XXX: The deletion UX can be confusing when dealing with cascading deletion. - cascade = getattr(rel, "on_delete", None) is CASCADE + cascade = getattr(rel, 'on_delete', None) is CASCADE self.can_delete_related = not multiple and not cascade and can_delete_related self.can_view_related = not multiple and can_view_related # so we can check if the related object is registered with this AdminSite @@ -285,46 +267,35 @@ class RelatedFieldWidgetWrapper(forms.Widget): return self.widget.media def get_related_url(self, info, action, *args): - return reverse( - "admin:%s_%s_%s" % (info + (action,)), - current_app=self.admin_site.name, - args=args, - ) + return reverse("admin:%s_%s_%s" % (info + (action,)), + current_app=self.admin_site.name, args=args) def get_context(self, name, value, attrs): from django.contrib.admin.views.main import IS_POPUP_VAR, TO_FIELD_VAR - rel_opts = self.rel.model._meta info = (rel_opts.app_label, rel_opts.model_name) self.widget.choices = self.choices - url_params = "&".join( - "%s=%s" % param - for param in [ - (TO_FIELD_VAR, self.rel.get_related_field().name), - (IS_POPUP_VAR, 1), - ] - ) + url_params = '&'.join("%s=%s" % param for param in [ + (TO_FIELD_VAR, self.rel.get_related_field().name), + (IS_POPUP_VAR, 1), + ]) context = { - "rendered_widget": self.widget.render(name, value, attrs), - "is_hidden": self.is_hidden, - "name": name, - "url_params": url_params, - "model": rel_opts.verbose_name, - "can_add_related": self.can_add_related, - "can_change_related": self.can_change_related, - "can_delete_related": self.can_delete_related, - "can_view_related": self.can_view_related, + 'rendered_widget': self.widget.render(name, value, attrs), + 'is_hidden': self.is_hidden, + 'name': name, + 'url_params': url_params, + 'model': rel_opts.verbose_name, + 'can_add_related': self.can_add_related, + 'can_change_related': self.can_change_related, + 'can_delete_related': self.can_delete_related, + 'can_view_related': self.can_view_related, } if self.can_add_related: - context["add_related_url"] = self.get_related_url(info, "add") + context['add_related_url'] = self.get_related_url(info, 'add') if self.can_delete_related: - context["delete_related_template_url"] = self.get_related_url( - info, "delete", "__fk__" - ) + context['delete_related_template_url'] = self.get_related_url(info, 'delete', '__fk__') if self.can_view_related or self.can_change_related: - context["change_related_template_url"] = self.get_related_url( - info, "change", "__fk__" - ) + context['change_related_template_url'] = self.get_related_url(info, 'change', '__fk__') return context def value_from_datadict(self, data, files, name): @@ -339,112 +310,67 @@ class RelatedFieldWidgetWrapper(forms.Widget): class AdminTextareaWidget(forms.Textarea): def __init__(self, attrs=None): - super().__init__(attrs={"class": "vLargeTextField", **(attrs or {})}) + super().__init__(attrs={'class': 'vLargeTextField', **(attrs or {})}) class AdminTextInputWidget(forms.TextInput): def __init__(self, attrs=None): - super().__init__(attrs={"class": "vTextField", **(attrs or {})}) + super().__init__(attrs={'class': 'vTextField', **(attrs or {})}) class AdminEmailInputWidget(forms.EmailInput): def __init__(self, attrs=None): - super().__init__(attrs={"class": "vTextField", **(attrs or {})}) + super().__init__(attrs={'class': 'vTextField', **(attrs or {})}) class AdminURLFieldWidget(forms.URLInput): - template_name = "admin/widgets/url.html" + template_name = 'admin/widgets/url.html' def __init__(self, attrs=None, validator_class=URLValidator): - super().__init__(attrs={"class": "vURLField", **(attrs or {})}) + super().__init__(attrs={'class': 'vURLField', **(attrs or {})}) self.validator = validator_class() def get_context(self, name, value, attrs): try: - self.validator(value if value else "") + self.validator(value if value else '') url_valid = True except ValidationError: url_valid = False context = super().get_context(name, value, attrs) - context["current_label"] = _("Currently:") - context["change_label"] = _("Change:") - context["widget"]["href"] = ( - smart_urlquote(context["widget"]["value"]) if value else "" - ) - context["url_valid"] = url_valid + context['current_label'] = _('Currently:') + context['change_label'] = _('Change:') + context['widget']['href'] = smart_urlquote(context['widget']['value']) if value else '' + context['url_valid'] = url_valid return context class AdminIntegerFieldWidget(forms.NumberInput): - class_name = "vIntegerField" + class_name = 'vIntegerField' def __init__(self, attrs=None): - super().__init__(attrs={"class": self.class_name, **(attrs or {})}) + super().__init__(attrs={'class': self.class_name, **(attrs or {})}) class AdminBigIntegerFieldWidget(AdminIntegerFieldWidget): - class_name = "vBigIntegerField" + class_name = 'vBigIntegerField' class AdminUUIDInputWidget(forms.TextInput): def __init__(self, attrs=None): - super().__init__(attrs={"class": "vUUIDField", **(attrs or {})}) + super().__init__(attrs={'class': 'vUUIDField', **(attrs or {})}) # Mapping of lowercase language codes [returned by Django's get_language()] to # language codes supported by select2. # See django/contrib/admin/static/admin/js/vendor/select2/i18n/* -SELECT2_TRANSLATIONS = { - x.lower(): x - for x in [ - "ar", - "az", - "bg", - "ca", - "cs", - "da", - "de", - "el", - "en", - "es", - "et", - "eu", - "fa", - "fi", - "fr", - "gl", - "he", - "hi", - "hr", - "hu", - "id", - "is", - "it", - "ja", - "km", - "ko", - "lt", - "lv", - "mk", - "ms", - "nb", - "nl", - "pl", - "pt-BR", - "pt", - "ro", - "ru", - "sk", - "sr-Cyrl", - "sr", - "sv", - "th", - "tr", - "uk", - "vi", - ] -} -SELECT2_TRANSLATIONS.update({"zh-hans": "zh-CN", "zh-hant": "zh-TW"}) +SELECT2_TRANSLATIONS = {x.lower(): x for x in [ + 'ar', 'az', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'et', + 'eu', 'fa', 'fi', 'fr', 'gl', 'he', 'hi', 'hr', 'hu', 'id', 'is', + 'it', 'ja', 'km', 'ko', 'lt', 'lv', 'mk', 'ms', 'nb', 'nl', 'pl', + 'pt-BR', 'pt', 'ro', 'ru', 'sk', 'sr-Cyrl', 'sr', 'sv', 'th', + 'tr', 'uk', 'vi', +]} +SELECT2_TRANSLATIONS.update({'zh-hans': 'zh-CN', 'zh-hant': 'zh-TW'}) class AutocompleteMixin: @@ -454,8 +380,7 @@ class AutocompleteMixin: Renders the necessary data attributes for select2 and adds the static form media. """ - - url_name = "%s:autocomplete" + url_name = '%s:autocomplete' def __init__(self, field, admin_site, attrs=None, choices=(), using=None): self.field = field @@ -463,7 +388,6 @@ class AutocompleteMixin: self.db = using self.choices = choices self.attrs = {} if attrs is None else attrs.copy() - self.i18n_name = SELECT2_TRANSLATIONS.get(get_language()) def get_url(self): return reverse(self.url_name % self.admin_site.name) @@ -477,25 +401,20 @@ class AutocompleteMixin: https://select2.org/configuration/data-attributes#nested-subkey-options """ attrs = super().build_attrs(base_attrs, extra_attrs=extra_attrs) - attrs.setdefault("class", "") - attrs.update( - { - "data-ajax--cache": "true", - "data-ajax--delay": 250, - "data-ajax--type": "GET", - "data-ajax--url": self.get_url(), - "data-app-label": self.field.model._meta.app_label, - "data-model-name": self.field.model._meta.model_name, - "data-field-name": self.field.name, - "data-theme": "admin-autocomplete", - "data-allow-clear": json.dumps(not self.is_required), - "data-placeholder": "", # Allows clearing of the input. - "lang": self.i18n_name, - "class": attrs["class"] - + (" " if attrs["class"] else "") - + "admin-autocomplete", - } - ) + attrs.setdefault('class', '') + attrs.update({ + 'data-ajax--cache': 'true', + 'data-ajax--delay': 250, + 'data-ajax--type': 'GET', + 'data-ajax--url': self.get_url(), + 'data-app-label': self.field.model._meta.app_label, + 'data-model-name': self.field.model._meta.model_name, + 'data-field-name': self.field.name, + 'data-theme': 'admin-autocomplete', + 'data-allow-clear': json.dumps(not self.is_required), + 'data-placeholder': '', # Allows clearing of the input. + 'class': attrs['class'] + (' ' if attrs['class'] else '') + 'admin-autocomplete', + }) return attrs def optgroups(self, name, value, attr=None): @@ -504,57 +423,46 @@ class AutocompleteMixin: groups = [default] has_selected = False selected_choices = { - str(v) for v in value if str(v) not in self.choices.field.empty_values + str(v) for v in value + if str(v) not in self.choices.field.empty_values } if not self.is_required and not self.allow_multiple_selected: - default[1].append(self.create_option(name, "", "", False, 0)) + default[1].append(self.create_option(name, '', '', False, 0)) remote_model_opts = self.field.remote_field.model._meta - to_field_name = getattr( - self.field.remote_field, "field_name", remote_model_opts.pk.attname - ) + to_field_name = getattr(self.field.remote_field, 'field_name', remote_model_opts.pk.attname) to_field_name = remote_model_opts.get_field(to_field_name).attname choices = ( (getattr(obj, to_field_name), self.choices.field.label_from_instance(obj)) - for obj in self.choices.queryset.using(self.db).filter( - **{"%s__in" % to_field_name: selected_choices} - ) + for obj in self.choices.queryset.using(self.db).filter(**{'%s__in' % to_field_name: selected_choices}) ) for option_value, option_label in choices: - selected = str(option_value) in value and ( - has_selected is False or self.allow_multiple_selected + selected = ( + str(option_value) in value and + (has_selected is False or self.allow_multiple_selected) ) has_selected |= selected index = len(default[1]) subgroup = default[1] - subgroup.append( - self.create_option( - name, option_value, option_label, selected_choices, index - ) - ) + subgroup.append(self.create_option(name, option_value, option_label, selected_choices, index)) return groups @property def media(self): - extra = "" if settings.DEBUG else ".min" - i18n_file = ( - ("admin/js/vendor/select2/i18n/%s.js" % self.i18n_name,) - if self.i18n_name - else () - ) + extra = '' if settings.DEBUG else '.min' + i18n_name = SELECT2_TRANSLATIONS.get(get_language()) + i18n_file = ('admin/js/vendor/select2/i18n/%s.js' % i18n_name,) if i18n_name else () return forms.Media( js=( - "admin/js/vendor/jquery/jquery%s.js" % extra, - "admin/js/vendor/select2/select2.full%s.js" % extra, - ) - + i18n_file - + ( - "admin/js/jquery.init.js", - "admin/js/autocomplete.js", + 'admin/js/vendor/jquery/jquery%s.js' % extra, + 'admin/js/vendor/select2/select2.full%s.js' % extra, + ) + i18n_file + ( + 'admin/js/jquery.init.js', + 'admin/js/autocomplete.js', ), css={ - "screen": ( - "admin/css/vendor/select2/select2%s.css" % extra, - "admin/css/autocomplete.css", + 'screen': ( + 'admin/css/vendor/select2/select2%s.css' % extra, + 'admin/css/autocomplete.css', ), }, ) diff --git a/venv/Lib/site-packages/django/contrib/admindocs/apps.py b/venv/Lib/site-packages/django/contrib/admindocs/apps.py index e79dc89..1a50268 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/apps.py +++ b/venv/Lib/site-packages/django/contrib/admindocs/apps.py @@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _ class AdminDocsConfig(AppConfig): - name = "django.contrib.admindocs" + name = 'django.contrib.admindocs' verbose_name = _("Administrative Documentation") diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo index d943e48..11429f2 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.po index 28727f2..37546ea 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/af/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Charl du Plessis <cjdupless@gmail.com>, 2021 # F Wolff <friedel@translate.org.za>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-08-23 10:57+0000\n" -"Last-Translator: Charl du Plessis <cjdupless@gmail.com>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2019-01-07 08:38+0000\n" +"Last-Translator: F Wolff <friedel@translate.org.za>\n" "Language-Team: Afrikaans (http://www.transifex.com/django/django/language/" "af/)\n" "MIME-Version: 1.0\n" @@ -46,14 +45,12 @@ msgid "" "Jumps you from any page to the documentation for the view that generates " "that page." msgstr "" -"Neem jou van enige bladsy na die dokumentasie vir die vertooning wat daardie " -"bladsy genereer." msgid "Tags" msgstr "" msgid "List of all the template tags and their functions." -msgstr "Lys van alle templaat tags en hulle funksies." +msgstr "" msgid "Filters" msgstr "" @@ -80,46 +77,39 @@ msgid "" "template is used to generate the page and which objects are available to " "that template." msgstr "" -"Elke bladsy op die publieke werf word gegenereer deur 'n vertoning. Die " -"vertoning definiëer watter templaat word gebruik om die bladsy te genereer " -"en watter objekte is beskikbaar aan daardie templaat." msgid "Tools for your browser to quickly access admin functionality." msgstr "" msgid "Please install docutils" -msgstr "Installeer asseblief docutils" +msgstr "" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" -"Die admin dokumentasie stelsel benodig Python se <a href=\"%(link)s" -"\">docutils</a> biblioteek." #, python-format msgid "" "Please ask your administrators to install <a href=\"%(link)s\">docutils</a>." msgstr "" -"Vra asseblief jou administrateurs om <a href=\"%(link)s\">docutils</a> te " -"installeer." #, python-format msgid "Model: %(name)s" msgstr "" msgid "Fields" -msgstr "Velde" +msgstr "" msgid "Field" -msgstr "Veld" +msgstr "" msgid "Type" -msgstr "Tipe" +msgstr "" msgid "Description" -msgstr "Beskrywing" +msgstr "" msgid "Methods with arguments" msgstr "" @@ -131,32 +121,32 @@ msgid "Arguments" msgstr "" msgid "Back to Model documentation" -msgstr "Terug na Model dokumentasie" +msgstr "" msgid "Model documentation" -msgstr "Model dokumentasie" +msgstr "" msgid "Model groups" -msgstr "Model groepe" +msgstr "" msgid "Templates" -msgstr "Template" +msgstr "" #, python-format msgid "Template: %(name)s" -msgstr "Templaat: %(name)s" +msgstr "" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Templaat: <q>%(name)s</q>" +msgid "Template: \"%(name)s\"" +msgstr "" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" +msgid "Search path for template \"%(name)s\":" msgstr "" msgid "(does not exist)" -msgstr "(bestaan nie)" +msgstr "" msgid "Back to Documentation" msgstr "" @@ -257,7 +247,7 @@ msgstr "" #, python-format msgid "all %s" -msgstr "alle %s" +msgstr "" #, python-format msgid "number of %s" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo index 7a45097..faba0cc 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po index 05d5370..ca1bd2a 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/ar/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2015-2016,2021 +# Bashar Al-Abdulhadi, 2015-2016 # Bashar Al-Abdulhadi, 2014 # Jannis Leidel <jannis@leidel.info>, 2011 # Muaaz Alsaied, 2020 @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-15 21:25+0000\n" -"Last-Translator: Bashar Al-Abdulhadi\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-04-02 10:32+0000\n" +"Last-Translator: Muaaz Alsaied\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -98,10 +98,9 @@ msgstr "الرجاء تثبيت docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." -msgstr "" -"نظام توثيقات المشرف يتطلب مكتبة بايثون <a href=\"%(link)s\">docutils</a>." +msgstr "نظام توثيقات المشرف مكتبة بايثون <a href=\"%(link)s\">docutils</a>." #, python-format msgid "" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo index 0356778..49fbe0a 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po index 124d843..aa3b1b9 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/bg/LC_MESSAGES/django.po @@ -1,19 +1,18 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Boris Chervenkov <office@sentido.bg>, 2012 # Jannis Leidel <jannis@leidel.info>, 2011 # Lyuboslav Petrov <petrov.lyuboslav@gmail.com>, 2014 -# Todor Lubenov <tlubenov@gmail.com>, 2011,2015 +# Todor Lubenov <tgl.sysdev@gmail.com>, 2011,2015 # Venelin Stoykov <vkstoykov@gmail.com>, 2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2022-01-14 10:04+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -23,7 +22,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Administrative Documentation" -msgstr "Административна документация" +msgstr "Административна Документация" msgid "Home" msgstr "Начало" @@ -58,10 +57,10 @@ msgstr "" "е генерирал." msgid "Tags" -msgstr "Тагове" +msgstr "Етикети" msgid "List of all the template tags and their functions." -msgstr "Списък на всички шаблонни тагове и техните функции." +msgstr "Списък на всички етикети на шаблона и техните функции." msgid "Filters" msgstr "Филтри" @@ -70,7 +69,7 @@ msgid "" "Filters are actions which can be applied to variables in a template to alter " "the output." msgstr "" -"Филтрите са действия, които могат да се използват върху променливи в даден " +"Филтрите са действия, които могат да се използват към променливи в даден " "шаблон, за да променят изхода." msgid "Models" @@ -82,20 +81,20 @@ msgid "" "template variables" msgstr "" "Моделите са описания на всички обекти в системата и свързаните с тях полета. " -"Всеки модел си има списък на полетата, които могат да бъдат достъпени както " +"Всеки модел си има списък на полетата, които могат да бъдат достъпни както " "шаблонни променливи" msgid "Views" -msgstr "Изгледи" +msgstr "Прегледи" msgid "" "Each page on the public site is generated by a view. The view defines which " "template is used to generate the page and which objects are available to " "that template." msgstr "" -"Всяка страница на публичния сайт се генерира от изглед. Изгледът определя " -"кой шаблон се използва за генериране на страницата и кои обекти са на " -"разположение за този шаблон." +"Всяка страница на публичния сайт се генерира от един изглед. Изгледа " +"определя кой шаблон се използва за генериране на страницата, и които обекти " +"са на разположение за този шаблон." msgid "Tools for your browser to quickly access admin functionality." msgstr "" @@ -107,17 +106,17 @@ msgstr "Моля инсталирайте docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" -"Системата за администраторска документация изисква библиотеката за Python <a " +"Системата за администраторска документация изисква библиотеката за python <a " "href=\"%(link)s\">docutils</a>." #, python-format msgid "" "Please ask your administrators to install <a href=\"%(link)s\">docutils</a>." msgstr "" -"Моля, помолете вашите администратори да инсталират <a href=\"%(link)s" +"Моля, попитайте вашите администратори да инсталирате <a href=\"%(link)s" "\">docutils</a> ." #, python-format @@ -137,7 +136,7 @@ msgid "Description" msgstr "Описание" msgid "Methods with arguments" -msgstr "Методи с аргументи" +msgstr "Методи и аргументи" msgid "Method" msgstr "Метод" @@ -146,32 +145,32 @@ msgid "Arguments" msgstr "Аргументи" msgid "Back to Model documentation" -msgstr " Върни се в документацията за модели" +msgstr " Върни се в документацията на модела" msgid "Model documentation" -msgstr "Документация за модели" +msgstr "Модел документация" msgid "Model groups" -msgstr "Групи на модела" +msgstr "Модел групи" msgid "Templates" msgstr "Шаблони" #, python-format msgid "Template: %(name)s" -msgstr "Шаблон: %(name)s" +msgstr "Template: %(name)s" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Шаблон: <q>%(name)s</q>" +msgid "Template: \"%(name)s\"" +msgstr "Шаблон: \"%(name)s\"" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "Път за търсене на шаблон <q>%(name)s</q>:" +msgid "Search path for template \"%(name)s\":" +msgstr "Път за търсене на шаблон \"%(name)s\"" msgid "(does not exist)" -msgstr "(не съществува)" +msgstr "(Не съществува)" msgid "Back to Documentation" msgstr "Назад към Документацията" @@ -180,7 +179,7 @@ msgid "Template filters" msgstr "Шаблонни филтри" msgid "Template filter documentation" -msgstr "Документация за шаблонни филтри" +msgstr "Документация за Шаблонни филтри" msgid "Built-in filters" msgstr "Вградени филтри" @@ -190,25 +189,25 @@ msgid "" "To use these filters, put <code>%(code)s</code> in your template before " "using the filter." msgstr "" -"За да използвате тези филтри, сложете <code>%(code)s</code> във вашия " -"шаблон, преди да използвате филтъра." +"За да използвате тези филтри, сложи <code>%(code)s</code> във вашия шаблон, " +"преди да използвате филтъра." msgid "Template tags" -msgstr "Шаблонни тагове" +msgstr "Шаблон тагове" msgid "Template tag documentation" -msgstr "Документация за Шаблонни тагове" +msgstr "Документация за шаблонни тагове" msgid "Built-in tags" -msgstr "Вградени тагове" +msgstr "Вградени в тагове" #, python-format msgid "" "To use these tags, put <code>%(code)s</code> in your template before using " "the tag." msgstr "" -"За да използвате тези тагове, сложете <code>%(code)s</code> във вашия " -"шаблон, преди да използвате тага." +"За да използвате тези тагове, сложи <code>%(code)s</code> във вашия шаблон, " +"преди да използвате етикет." #, python-format msgid "View: %(name)s" @@ -221,23 +220,23 @@ msgid "Templates:" msgstr "Шаблони:" msgid "Back to View documentation" -msgstr "Обратно към документацията за Изглед" +msgstr "" msgid "View documentation" -msgstr "Документация за Изглед" +msgstr "Документация за Изгледи" msgid "Jump to namespace" -msgstr "Прескочи към именни пространства" +msgstr "Направо към именни пространства" msgid "Empty namespace" -msgstr "Празни именни пространства" +msgstr "Empty именни пространства" #, python-format msgid "Views by namespace %(name)s" -msgstr "Изгледи по именни пространства %(name)s" +msgstr "" msgid "Views by empty namespace" -msgstr "Изгледи по празни именни пространства" +msgstr "Прегледи на празна именни пространства" #, python-format msgid "" @@ -245,12 +244,9 @@ msgid "" " View function: <code>%(full_name)s</code>. Name: <code>%(url_name)s</" "code>.\n" msgstr "" -"\n" -" Изглед функция: <code>%(full_name)s</code>. Име: <code>%(url_name)s</" -"code>.\n" msgid "tag:" -msgstr "таг:" +msgstr "етикет:" msgid "filter:" msgstr "филтър:" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo index 37daf44..76e64a3 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po index 8a0984b..13c2c93 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/ca/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ # # Translators: # abraham.martin <abraham.martin@gmail.com>, 2014 -# Antoni Aloy <aaloy@apsl.net>, 2014,2017,2021 +# Antoni Aloy <aaloy@apsl.net>, 2014,2017 # Carles Barrobés <carles@barrobes.com>, 2012 # Gil Obradors Via <gil.obradors@gmail.com>, 2019 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-27 08:46+0000\n" -"Last-Translator: Antoni Aloy <aaloy@apsl.net>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-11-03 20:47+0000\n" +"Last-Translator: Gil Obradors Via <gil.obradors@gmail.com>\n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" "MIME-Version: 1.0\n" @@ -105,7 +105,7 @@ msgstr "Si us plau instal·leu docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "El sistema d'administració de documentació requereix de la biblioteca de " diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo index ae993f7..4d23fc3 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.po index 0cb229f..b437384 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/de/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Florian Apolloner <florian@apolloner.eu>, 2021 # Jannis Vajen, 2013 # Jannis Leidel <jannis@leidel.info>, 2013-2016,2020 # Jannis Vajen, 2016 @@ -9,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-28 17:15+0000\n" -"Last-Translator: Raphael Michel <mail@raphaelmichel.de>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-17 22:42+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -104,7 +103,7 @@ msgstr "Bitte docutils installieren." #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Das Admin-Dokumentationssystem erfordert die Python-Bibliothek <a href=" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo index 9739b41..e98cc37 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.po index bc4aebb..4a55e69 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/el/LC_MESSAGES/django.po @@ -2,7 +2,6 @@ # # Translators: # Dimitris Glezos <glezos@transifex.com>, 2011 -# Fotis Athineos <fotis@transifex.com>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 # Nick Mavrakis <mavrakis.n@gmail.com>, 2016 # Pãnoș <panos.laganakos@gmail.com>, 2014 @@ -11,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-08-04 06:47+0000\n" -"Last-Translator: Fotis Athineos <fotis@transifex.com>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -106,7 +105,7 @@ msgstr "Παρακαλώ εγκαταστείστε τα docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Το σύστημα διαχειριστικής τεκμηρίωσης απαιτεί την βιβλιοθήκη της Python <a " @@ -161,13 +160,13 @@ msgid "Template: %(name)s" msgstr "Περίγραμμα: %(name)s" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Περίγραμμα: <q>%(name)s</q>" +msgid "Template: \"%(name)s\"" +msgstr "Περίγραμμα: \"%(name)s\"" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "Αναζήτηση μονοπατιού για περίγραμμα <q>%(name)s</q>:" +msgid "Search path for template \"%(name)s\":" +msgstr "Αναζήτηση μονοπατιού για περίγραμμα \"%(name)s\":" msgid "(does not exist)" msgstr "(δεν υπάρχει)" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo index 81898a8..2dab727 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.po index ade7542..06a1242 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/et/LC_MESSAGES/django.po @@ -3,17 +3,16 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # Janno Liivak <jannolii@gmail.com>, 2013,2015 -# Martin <martinpajuste@gmail.com>, 2021 -# Martin <martinpajuste@gmail.com>, 2016 +# Martin Pajuste <martinpajuste@gmail.com>, 2016 # Marti Raudsepp <marti@juffo.org>, 2014 # Ragnar Rebase <rrebase@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-22 11:26+0000\n" -"Last-Translator: Martin <martinpajuste@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-12-28 01:40+0000\n" +"Last-Translator: Ragnar Rebase <rrebase@gmail.com>\n" "Language-Team: Estonian (http://www.transifex.com/django/django/language/" "et/)\n" "MIME-Version: 1.0\n" @@ -105,7 +104,7 @@ msgstr "Palun paigaldage docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Administreerimise dokumentatsioon vajab Python'i <a href=\"%(link)s" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo index 19f0f72..bd4006b 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po index bb8b1b4..b7dc50f 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/gd/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-15 10:42+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-12-13 12:31+0000\n" "Last-Translator: GunChleoc\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" @@ -106,7 +106,7 @@ msgstr "Stàlaich docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Tha siostam docamaideadh na rianachd feumach air an leabharlann <a href=" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo index fd7a12d..1616f4c 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.po index 8e60c4a..3052a37 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/he/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # Meir Kriheli <mkriheli@gmail.com>, 2012-2015,2017,2020 -# Yaron Shahrabani <sh.yaron@gmail.com>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-05-17 12:29+0000\n" -"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-08-02 13:22+0000\n" +"Last-Translator: Meir Kriheli <mkriheli@gmail.com>\n" "Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -57,7 +56,7 @@ msgid "List of all the template tags and their functions." msgstr "רשימת כל ה-template tags והפונקציות שלהן." msgid "Filters" -msgstr "מסננים" +msgstr "Filters" msgid "" "Filters are actions which can be applied to variables in a template to alter " @@ -94,9 +93,11 @@ msgstr "נא להתקין את docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" +"מערכת התיעוד במנשק הניהול דורשת את ספריית הפייתון <a href=\"%(link)s" +"\">docutils</a>." #, python-format msgid "" @@ -254,11 +255,11 @@ msgstr "מודל:" #, python-format msgid "the related `%(app_label)s.%(data_type)s` object" -msgstr "עצמי `%(app_label)s.%(data_type)s` קשורים" +msgstr "אוביקטי `%(app_label)s.%(data_type)s` קשורים" #, python-format msgid "related `%(app_label)s.%(object_name)s` objects" -msgstr "עצמי `%(app_label)s.%(object_name)s` קשורים" +msgstr "אובייקטי `%(app_label)s.%(object_name)s` קשורים" #, python-format msgid "all %s" @@ -270,4 +271,4 @@ msgstr "מספר %s" #, python-format msgid "%s does not appear to be a urlpattern object" -msgstr "לא נראה כי %s הוא עצם urlpattern" +msgstr "לא נראה ש-%s הוא אובייקט urlpattern" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo index d6d934d..600558a 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.po index 92b70c3..539df90 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/id/LC_MESSAGES/django.po @@ -1,18 +1,18 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Fery Setiawan <gembelweb@gmail.com>, 2015-2016,2021 +# Fery Setiawan <gembelweb@gmail.com>, 2015-2016 # Jannis Leidel <jannis@leidel.info>, 2011 # rodin <romihardiyanto@gmail.com>, 2011-2012 # rodin <romihardiyanto@gmail.com>, 2013-2014,2016 -# sag᠎e <laymonage@gmail.com>, 2019 +# sage <laymonage@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-09-14 00:30+0000\n" -"Last-Translator: Fery Setiawan <gembelweb@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-11-18 03:39+0000\n" +"Last-Translator: sage <laymonage@gmail.com>\n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" "MIME-Version: 1.0\n" @@ -104,11 +104,11 @@ msgstr "Instal docutils." #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Sistem dokumentasi admin membutuhkan pustaka <a href=\"%(link)s\">docutils</" -"a> Python." +"a> dari Python." #, python-format msgid "" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po index 7d67436..e1a900d 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/kn/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Administrative Documentation" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo index 3185670..f75d7bb 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po index 7466f0f..04672a7 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/ko/LC_MESSAGES/django.po @@ -2,7 +2,6 @@ # # Translators: # Jiyoon, Ha <cryptography@konkuk.ac.kr>, 2016 -# 코딩 영, 2021 # Geonho Kim / Leo Kim <gh.leokim@gmail.com>, 2019 # Ian Y. Choi <ianyrchoi@gmail.com>, 2015 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -13,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-10 02:10+0000\n" -"Last-Translator: 코딩 영\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-10-05 06:55+0000\n" +"Last-Translator: Geonho Kim / Leo Kim <gh.leokim@gmail.com>\n" "Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -99,11 +98,11 @@ msgstr "docutils를 설치해주세요." #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "관리자 문서 시스템은 파이썬의 <a href=\"%(link)s\">docutils</a> 라이브러리" -"를 필요로 합니다." +"를 필요로합니다." #, python-format msgid "" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 0db0e9c..0000000 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 13fd43e..0000000 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,283 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-16 14:09+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Administrative Documentation" -msgstr "Dokumentasi Pentadbiran" - -msgid "Home" -msgstr "Utama" - -msgid "Documentation" -msgstr "Dokumentasi" - -msgid "Bookmarklets" -msgstr "Penanda" - -msgid "Documentation bookmarklets" -msgstr "Dokumentasi penanda" - -msgid "" -"To install bookmarklets, drag the link to your bookmarks toolbar, or right-" -"click the link and add it to your bookmarks. Now you can select the " -"bookmarklet from any page in the site." -msgstr "" -"Untuk memasang penanda, heret pautan ke palang-alat penanda-buku, atau klik-" -"kanan pada pautan dan tambahkan ke penanda-buku anda. Kini anda boleh " -"memilih penanda itu daripada mana-mana ruangan di laman." - -msgid "Documentation for this page" -msgstr "Dokumentasi untuk ruangan ini" - -msgid "" -"Jumps you from any page to the documentation for the view that generates " -"that page." -msgstr "" -"Membawa anda daripada mana-mana ruangan kepada dokumentasi bagi pemandangan " -"yang menjana ruang itu." - -msgid "Tags" -msgstr "Tag-tag" - -msgid "List of all the template tags and their functions." -msgstr "Senarai kesemua tag templat dan fungsiannya." - -msgid "Filters" -msgstr "Tapisan" - -msgid "" -"Filters are actions which can be applied to variables in a template to alter " -"the output." -msgstr "" -"Tapisan adalah tindakan yang boleh digunakan pada pembolehubah didalam " -"templat untuk mengubah outputnya." - -msgid "Models" -msgstr "Model-model" - -msgid "" -"Models are descriptions of all the objects in the system and their " -"associated fields. Each model has a list of fields which can be accessed as " -"template variables" -msgstr "" -"Model-model adalah penerangan bagi semua objek-objek didalam sistem dan " -"medan-medan yang berkaitan. Setiap model mempunyai senarai medan-medan yang " -"boleh diakses sebagai tempat pembolehubah." - -msgid "Views" -msgstr "Pemandangan" - -msgid "" -"Each page on the public site is generated by a view. The view defines which " -"template is used to generate the page and which objects are available to " -"that template." -msgstr "" -"Setiap ruangan pada laman awan dihasilkan daripada pemandangan. Pemandangan " -"menakrifkan templat mana yang diguna-pakai untuk menghasilkan ruangan itu " -"dan objek mana yang boleh digunakan bersama templat itu." - -msgid "Tools for your browser to quickly access admin functionality." -msgstr "" -"Alat-alat untuk pelayar anda untuk mengakses fungsian pentadbiran secara " -"pantas." - -msgid "Please install docutils" -msgstr "Sila pasang docutils" - -#, python-format -msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" -"\">docutils</a> library." -msgstr "" -"Sistem dokumentasi pentadbiran memerlukan perpustakaan <a href=\"%(link)s" -"\">docutils</a> Python." - -#, python-format -msgid "" -"Please ask your administrators to install <a href=\"%(link)s\">docutils</a>." -msgstr "" -"Sila mohon pada pentadbir-pentadbir anda untuk memasang <a href=\"%(link)s" -"\">docutils</a>." - -#, python-format -msgid "Model: %(name)s" -msgstr "Model: %(name)s" - -msgid "Fields" -msgstr "Medan-medan" - -msgid "Field" -msgstr "Medan" - -msgid "Type" -msgstr "Jenis" - -msgid "Description" -msgstr "Penerangan" - -msgid "Methods with arguments" -msgstr "Kaedah dengan argumen" - -msgid "Method" -msgstr "Kaedah" - -msgid "Arguments" -msgstr "Argumen" - -msgid "Back to Model documentation" -msgstr "Patah balik ke dokumentasi Model" - -msgid "Model documentation" -msgstr "Dokumentasi Model" - -msgid "Model groups" -msgstr "Kumpulan-kumpulan Model" - -msgid "Templates" -msgstr "Templat-templat" - -#, python-format -msgid "Template: %(name)s" -msgstr "Templat: %(name)s" - -#, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Templat: <q>%(name)s</q>" - -#. Translators: Search is not a verb here, it qualifies path (a search path) -#, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "Cari laluan untuk templat <q>%(name)s</q>:" - -msgid "(does not exist)" -msgstr "(tidak wujud)" - -msgid "Back to Documentation" -msgstr "Patah balik ke Dokumentasi" - -msgid "Template filters" -msgstr "Penapisan templat" - -msgid "Template filter documentation" -msgstr "Dokumentasi penapisan templat" - -msgid "Built-in filters" -msgstr "Penapisan terbina-dalam" - -#, python-format -msgid "" -"To use these filters, put <code>%(code)s</code> in your template before " -"using the filter." -msgstr "" -"Untuk menggunakan tapisan-tapisan ini, letakkan <code>%(code)s</code> " -"didalam templat anda sebelum menggunakan tapisan itu." - -msgid "Template tags" -msgstr "Tag-tag templat" - -msgid "Template tag documentation" -msgstr "Dokumentasi tag templat" - -msgid "Built-in tags" -msgstr "Tag-tag terbina-dalam" - -#, python-format -msgid "" -"To use these tags, put <code>%(code)s</code> in your template before using " -"the tag." -msgstr "" -"Untuk menggunakan tag-tag ini, letakkan <code>%(code)s</code> didalam " -"templat anda sebelum menggunakan tag itu." - -#, python-format -msgid "View: %(name)s" -msgstr "Pemandagan: %(name)s" - -msgid "Context:" -msgstr "Konteks:" - -msgid "Templates:" -msgstr "Templat-templat:" - -msgid "Back to View documentation" -msgstr "Patah balik ke Dokumentasi Pemandangan" - -msgid "View documentation" -msgstr "Dokumentasi Pemandangan" - -msgid "Jump to namespace" -msgstr "Lompat ke ruangnama" - -msgid "Empty namespace" -msgstr "Ruangnama koson" - -#, python-format -msgid "Views by namespace %(name)s" -msgstr "Pemandangan berdasakran ruangnama %(name)s" - -msgid "Views by empty namespace" -msgstr "Pemandangan berdasarkan ruangnama kosong" - -#, python-format -msgid "" -"\n" -" View function: <code>%(full_name)s</code>. Name: <code>%(url_name)s</" -"code>.\n" -msgstr "" -"\n" -"Fungsi Pemandangan: <code>%(full_name)s</code>. Name: <code>%(url_name)s</" -"code>.\n" - -msgid "tag:" -msgstr "tag:" - -msgid "filter:" -msgstr "tapis:" - -msgid "view:" -msgstr "lihat:" - -#, python-format -msgid "App %(app_label)r not found" -msgstr "App %(app_label)r tidak dijumpai" - -#, python-format -msgid "Model %(model_name)r not found in app %(app_label)r" -msgstr "Model %(model_name)r tidak dijumpai didalam app %(app_label)r" - -msgid "model:" -msgstr "model:" - -#, python-format -msgid "the related `%(app_label)s.%(data_type)s` object" -msgstr "objek `%(app_label)s.%(data_type)s` berkaitan" - -#, python-format -msgid "related `%(app_label)s.%(object_name)s` objects" -msgstr "objek-objek `%(app_label)s.%(object_name)s` berkaitan" - -#, python-format -msgid "all %s" -msgstr "semua %s" - -#, python-format -msgid "number of %s" -msgstr "bilangan %s" - -#, python-format -msgid "%s does not appear to be a urlpattern object" -msgstr "%s nampaknya bukan objek urlpattern" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo index 72add0c..a63cc2e 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po index 673f796..f0d904e 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/nn/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-16 23:22+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -19,7 +18,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Administrative Documentation" -msgstr "Administrasjons-dokumentasjon" +msgstr "" msgid "Home" msgstr "Heim" @@ -38,9 +37,6 @@ msgid "" "click the link and add it to your bookmarks. Now you can select the " "bookmarklet from any page in the site." msgstr "" -"For å installere bokmerke, dra lenkja til verktøylinja for bokmerke, eller " -"høgreklikk og legg til i dine bokmerke. No kan du velje bokmerket frå kva " -"for helst side på nettstaden." msgid "Documentation for this page" msgstr "Dokumentasjon for denne sida" @@ -53,183 +49,168 @@ msgstr "" "genererte sida." msgid "Tags" -msgstr "Taggar" +msgstr "" msgid "List of all the template tags and their functions." -msgstr "Liste over alle mal-taggar og funksjonane deira." +msgstr "" msgid "Filters" -msgstr "Filter" +msgstr "" msgid "" "Filters are actions which can be applied to variables in a template to alter " "the output." msgstr "" -"Filter er handlingar som kan bli brukt på variablar i ein mal for å endre " -"utdata." msgid "Models" -msgstr "Modellar" +msgstr "" msgid "" "Models are descriptions of all the objects in the system and their " "associated fields. Each model has a list of fields which can be accessed as " "template variables" msgstr "" -"Modellar er beskrivingar av alle objekta i eit system og tilhøyrande felt. " -"Kvar modell har ein liste over felt som kan bli brukt som mal-variablar." msgid "Views" -msgstr "Views" +msgstr "" msgid "" "Each page on the public site is generated by a view. The view defines which " "template is used to generate the page and which objects are available to " "that template." msgstr "" -"Kvar side på den offentlege nettstaden er generert av ein view. Viewen " -"definerer kva for ein mal som blir brukt til å generere sida og kva for " -"nokre objekt som er tilgjengelege for den malen." msgid "Tools for your browser to quickly access admin functionality." msgstr "" -"Verktøy for nettlesaren din for å få rask tilgang til admin-funksjonalitet" msgid "Please install docutils" -msgstr "Installer docutils" +msgstr "" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" -"Admin-verktøyet sitt dokumentasjonssystem behøver Python sitt <a href=" -"\"%(link)s\">docutils</a>-bibliotek." #, python-format msgid "" "Please ask your administrators to install <a href=\"%(link)s\">docutils</a>." msgstr "" -"Spør administratorane dine om å installere <a href=\"%(link)s\">docutils</a>." #, python-format msgid "Model: %(name)s" -msgstr "Modell: %(name)s" +msgstr "" msgid "Fields" -msgstr "Felt" +msgstr "" msgid "Field" -msgstr "Felt" +msgstr "" msgid "Type" -msgstr "Type" +msgstr "" msgid "Description" -msgstr "Beskriving" +msgstr "" msgid "Methods with arguments" -msgstr "Metodar med argument" +msgstr "" msgid "Method" -msgstr "Metode" +msgstr "" msgid "Arguments" -msgstr "Argument" +msgstr "" msgid "Back to Model documentation" -msgstr "Attende til Modelldokumentasjon" +msgstr "" msgid "Model documentation" -msgstr "Modelldokumentasjon" +msgstr "" msgid "Model groups" -msgstr "Modellgrupper" +msgstr "" msgid "Templates" -msgstr "Malar" +msgstr "" #, python-format msgid "Template: %(name)s" -msgstr "Mal: %(name)s" +msgstr "" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Mal: <q>%(name)s</q>" +msgid "Template: \"%(name)s\"" +msgstr "" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "Søkjebane for mal <q>%(name)s</q>:" +msgid "Search path for template \"%(name)s\":" +msgstr "" msgid "(does not exist)" -msgstr "(finnast ikkje)" +msgstr "" msgid "Back to Documentation" -msgstr "Attende til Dokumentasjon" +msgstr "" msgid "Template filters" -msgstr "Malfilter" +msgstr "" msgid "Template filter documentation" -msgstr "Malfilterdokumentasjon" +msgstr "" msgid "Built-in filters" -msgstr "Innbygde filter" +msgstr "" #, python-format msgid "" "To use these filters, put <code>%(code)s</code> in your template before " "using the filter." msgstr "" -"For å bruke desse filtera, set inn <code>%(code)s</code> i malen før du " -"brukar filteret." msgid "Template tags" -msgstr "Maltaggar" +msgstr "" msgid "Template tag documentation" -msgstr "Maltagdokumentasjon" +msgstr "" msgid "Built-in tags" -msgstr "Innebygde taggar" +msgstr "" #, python-format msgid "" "To use these tags, put <code>%(code)s</code> in your template before using " "the tag." msgstr "" -"For å bruke desse taggane, set inn <code>%(code)s</code> i malen før du " -"brukar taggen." #, python-format msgid "View: %(name)s" -msgstr "View: %(name)s" +msgstr "" msgid "Context:" -msgstr "Kontekst:" +msgstr "" msgid "Templates:" -msgstr "Malar:" +msgstr "" msgid "Back to View documentation" -msgstr "Attende til Viewdokumentasjon" +msgstr "" msgid "View documentation" -msgstr "Viewdokumentasjon" +msgstr "" msgid "Jump to namespace" -msgstr "Gå til namnerom" +msgstr "" msgid "Empty namespace" -msgstr "Tomt namnerom" +msgstr "" #, python-format msgid "Views by namespace %(name)s" -msgstr "Views etter namnerom %(name)s" +msgstr "" msgid "Views by empty namespace" -msgstr "Views etter tomt namnerom" +msgstr "" #, python-format msgid "" @@ -237,8 +218,6 @@ msgid "" " View function: <code>%(full_name)s</code>. Name: <code>%(url_name)s</" "code>.\n" msgstr "" -"\n" -" Viewfunksjon: <code>%(full_name)s</code>. Namn: <code>%(url_name)s</code>.\n" msgid "tag:" msgstr "tag:" @@ -251,7 +230,7 @@ msgstr "view:" #, python-format msgid "App %(app_label)r not found" -msgstr "Applikasjon %(app_label)r ikkje funne" +msgstr "" #, python-format msgid "Model %(model_name)r not found in app %(app_label)r" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo index 9af55df..618ce25 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po index 0e41cb8..b7feaee 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/sk/LC_MESSAGES/django.po @@ -2,18 +2,17 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# 18f25ad6fa9930fc67cb11aca9d16a27, 2012-2013 +# Juraj Bubniak <translations@jbub.eu>, 2012-2013 # Marian Andre <marian@andre.sk>, 2012-2013,2015,2017 # Martin Tóth <ezimir@gmail.com>, 2017 -# Peter Kuma, 2021 # supowski <supowski@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-24 21:08+0000\n" -"Last-Translator: Peter Kuma\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Martin Tóth <ezimir@gmail.com>\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -104,10 +103,10 @@ msgstr "Prosím, nainštalujte docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" -"Dokumentačný systém správy stránok vyžaduje Python knižnicu <a href=" +"Dokumentačný systém správy stránok vyžaduje pythonovskú knižnicu <a href=" "\"%(link)s\">docutils</a>." #, python-format @@ -158,13 +157,13 @@ msgid "Template: %(name)s" msgstr "Šablóna: %(name)s" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "Šablóna: <q>%(name)s</q>" +msgid "Template: \"%(name)s\"" +msgstr "Šablóna: \"%(name)s\"" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "Vyhľadávacia cesta pre šablónu <q>%(name)s</q>:" +msgid "Search path for template \"%(name)s\":" +msgstr "Vyhľadávacia cesta pre šablónu \"%(name)s\":" msgid "(does not exist)" msgstr "(neexistuje)" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo index f983cc7..96e620f 100644 Binary files a/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po index 58e38b3..756c128 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/admindocs/locale/sv/LC_MESSAGES/django.po @@ -6,14 +6,13 @@ # Jonathan Lindén, 2014 # Petter Strandmark <petter.strandmark@gmail.com>, 2019 # Thomas Lundqvist, 2013 -# Tomas Lööw <tomas.loow@mailbox.org>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-27 05:33+0000\n" -"Last-Translator: Tomas Lööw <tomas.loow@mailbox.org>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2019-01-28 13:49+0000\n" +"Last-Translator: Petter Strandmark <petter.strandmark@gmail.com>\n" "Language-Team: Swedish (http://www.transifex.com/django/django/language/" "sv/)\n" "MIME-Version: 1.0\n" @@ -106,7 +105,7 @@ msgstr "Vänligen installera docutils" #, python-format msgid "" -"The admin documentation system requires Python’s <a href=\"%(link)s" +"The admin documentation system requires Python's <a href=\"%(link)s" "\">docutils</a> library." msgstr "" "Administrationsdokumentationen kräver Pythons <a href=\"%(link)s\">docutils</" @@ -160,13 +159,13 @@ msgid "Template: %(name)s" msgstr "Mall: %(name)s" #, python-format -msgid "Template: <q>%(name)s</q>" -msgstr "" +msgid "Template: \"%(name)s\"" +msgstr "Mall: \"%(name)s\"" #. Translators: Search is not a verb here, it qualifies path (a search path) #, python-format -msgid "Search path for template <q>%(name)s</q>:" -msgstr "" +msgid "Search path for template \"%(name)s\":" +msgstr "Sökväg för mall \"%(name)s\":" msgid "(does not exist)" msgstr "(finns inte)" diff --git a/venv/Lib/site-packages/django/contrib/admindocs/middleware.py b/venv/Lib/site-packages/django/contrib/admindocs/middleware.py index 5c9f08d..4779db8 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/middleware.py +++ b/venv/Lib/site-packages/django/contrib/admindocs/middleware.py @@ -1,5 +1,4 @@ from django.conf import settings -from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse from django.utils.deprecation import MiddlewareMixin @@ -10,7 +9,6 @@ class XViewMiddleware(MiddlewareMixin): """ Add an X-View header to internal HEAD requests. """ - def process_view(self, request, view_func, view_args, view_kwargs): """ If the request method is HEAD and either the IP is internal or the @@ -18,16 +16,13 @@ class XViewMiddleware(MiddlewareMixin): header indicating the view function. This is used to lookup the view function for an arbitrary page. """ - if not hasattr(request, "user"): - raise ImproperlyConfigured( - "The XView middleware requires authentication middleware to " - "be installed. Edit your MIDDLEWARE setting to insert " - "'django.contrib.auth.middleware.AuthenticationMiddleware'." - ) - if request.method == "HEAD" and ( - request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS - or (request.user.is_active and request.user.is_staff) - ): + assert hasattr(request, 'user'), ( + "The XView middleware requires authentication middleware to be " + "installed. Edit your MIDDLEWARE setting to insert " + "'django.contrib.auth.middleware.AuthenticationMiddleware'." + ) + if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or + (request.user.is_active and request.user.is_staff)): response = HttpResponse() - response.headers["X-View"] = get_view_name(view_func) + response.headers['X-View'] = get_view_name(view_func) return response diff --git a/venv/Lib/site-packages/django/contrib/admindocs/urls.py b/venv/Lib/site-packages/django/contrib/admindocs/urls.py index 6ee14ac..bc9c3df 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/urls.py +++ b/venv/Lib/site-packages/django/contrib/admindocs/urls.py @@ -3,48 +3,48 @@ from django.urls import path, re_path urlpatterns = [ path( - "", - views.BaseAdminDocsView.as_view(template_name="admin_doc/index.html"), - name="django-admindocs-docroot", + '', + views.BaseAdminDocsView.as_view(template_name='admin_doc/index.html'), + name='django-admindocs-docroot', ), path( - "bookmarklets/", + 'bookmarklets/', views.BookmarkletsView.as_view(), - name="django-admindocs-bookmarklets", + name='django-admindocs-bookmarklets', ), path( - "tags/", + 'tags/', views.TemplateTagIndexView.as_view(), - name="django-admindocs-tags", + name='django-admindocs-tags', ), path( - "filters/", + 'filters/', views.TemplateFilterIndexView.as_view(), - name="django-admindocs-filters", + name='django-admindocs-filters', ), path( - "views/", + 'views/', views.ViewIndexView.as_view(), - name="django-admindocs-views-index", + name='django-admindocs-views-index', ), path( - "views/<view>/", + 'views/<view>/', views.ViewDetailView.as_view(), - name="django-admindocs-views-detail", + name='django-admindocs-views-detail', ), path( - "models/", + 'models/', views.ModelIndexView.as_view(), - name="django-admindocs-models-index", + name='django-admindocs-models-index', ), re_path( - r"^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$", + r'^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$', views.ModelDetailView.as_view(), - name="django-admindocs-models-detail", + name='django-admindocs-models-detail', ), path( - "templates/<path:template>/", + 'templates/<path:template>/', views.TemplateDetailView.as_view(), - name="django-admindocs-templates", + name='django-admindocs-templates', ), ] diff --git a/venv/Lib/site-packages/django/contrib/admindocs/utils.py b/venv/Lib/site-packages/django/contrib/admindocs/utils.py index 52bedea..9f66665 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/utils.py +++ b/venv/Lib/site-packages/django/contrib/admindocs/utils.py @@ -20,12 +20,9 @@ else: def get_view_name(view_func): - if hasattr(view_func, "view_class"): - klass = view_func.view_class - return f"{klass.__module__}.{klass.__qualname__}" mod_name = view_func.__module__ - view_name = getattr(view_func, "__qualname__", view_func.__class__.__name__) - return mod_name + "." + view_name + view_name = getattr(view_func, '__qualname__', view_func.__class__.__name__) + return mod_name + '.' + view_name def parse_docstring(docstring): @@ -33,12 +30,12 @@ def parse_docstring(docstring): Parse out the parts of a docstring. Return (title, body, metadata). """ if not docstring: - return "", "", {} + return '', '', {} docstring = cleandoc(docstring) - parts = re.split(r"\n{2,}", docstring) + parts = re.split(r'\n{2,}', docstring) title = parts[0] if len(parts) == 1: - body = "" + body = '' metadata = {} else: parser = HeaderParser() @@ -61,14 +58,14 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None): Convert the string from reST to an XHTML fragment. """ overrides = { - "doctitle_xform": True, - "initial_header_level": 3, + 'doctitle_xform': True, + 'initial_header_level': 3, "default_reference_context": default_reference_context, - "link_base": reverse("django-admindocs-docroot").rstrip("/"), - "raw_enabled": False, - "file_insertion_enabled": False, + "link_base": reverse('django-admindocs-docroot').rstrip('/'), + 'raw_enabled': False, + 'file_insertion_enabled': False, } - thing_being_parsed = thing_being_parsed and "<%s>" % thing_being_parsed + thing_being_parsed = thing_being_parsed and '<%s>' % thing_being_parsed # Wrap ``text`` in some reST that sets the default role to ``cmsreference``, # then restores it. source = """ @@ -80,23 +77,21 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None): """ parts = docutils.core.publish_parts( source % text, - source_path=thing_being_parsed, - destination_path=None, - writer_name="html", - settings_overrides=overrides, + source_path=thing_being_parsed, destination_path=None, + writer_name='html', settings_overrides=overrides, ) - return mark_safe(parts["fragment"]) + return mark_safe(parts['fragment']) # # reST roles # ROLES = { - "model": "%s/models/%s/", - "view": "%s/views/%s/", - "template": "%s/templates/%s/", - "filter": "%s/filters/#%s", - "tag": "%s/tags/#%s", + 'model': '%s/models/%s/', + 'view': '%s/views/%s/', + 'template': '%s/templates/%s/', + 'filter': '%s/filters/#%s', + 'tag': '%s/tags/#%s', } @@ -107,52 +102,41 @@ def create_reference_role(rolename, urlbase): node = docutils.nodes.reference( rawtext, text, - refuri=( - urlbase - % ( - inliner.document.settings.link_base, - text.lower(), - ) - ), - **options, + refuri=(urlbase % ( + inliner.document.settings.link_base, + text.lower(), + )), + **options ) return [node], [] - docutils.parsers.rst.roles.register_canonical_role(rolename, _role) -def default_reference_role( - name, rawtext, text, lineno, inliner, options=None, content=None -): +def default_reference_role(name, rawtext, text, lineno, inliner, options=None, content=None): if options is None: options = {} context = inliner.document.settings.default_reference_context node = docutils.nodes.reference( rawtext, text, - refuri=( - ROLES[context] - % ( - inliner.document.settings.link_base, - text.lower(), - ) - ), - **options, + refuri=(ROLES[context] % ( + inliner.document.settings.link_base, + text.lower(), + )), + **options ) return [node], [] if docutils_is_available: - docutils.parsers.rst.roles.register_canonical_role( - "cmsreference", default_reference_role - ) + docutils.parsers.rst.roles.register_canonical_role('cmsreference', default_reference_role) for name, urlbase in ROLES.items(): create_reference_role(name, urlbase) # Match the beginning of a named or unnamed group. -named_group_matcher = _lazy_re_compile(r"\(\?P(<\w+>)") -unnamed_group_matcher = _lazy_re_compile(r"\(") +named_group_matcher = _lazy_re_compile(r'\(\?P(<\w+>)') +unnamed_group_matcher = _lazy_re_compile(r'\(') def replace_named_groups(pattern): @@ -164,7 +148,8 @@ def replace_named_groups(pattern): 4. ^(?P<a>\w+)/b/(?P<c>\w+) ==> ^<a>/b/<c> """ named_group_indices = [ - (m.start(0), m.end(0), m[1]) for m in named_group_matcher.finditer(pattern) + (m.start(0), m.end(0), m[1]) + for m in named_group_matcher.finditer(pattern) ] # Tuples of (named capture group pattern, group name). group_pattern_and_name = [] @@ -175,17 +160,15 @@ def replace_named_groups(pattern): for idx, val in enumerate(pattern[end:]): # Check for unescaped `(` and `)`. They mark the start and end of a # nested group. - if val == "(" and prev_char != "\\": + if val == '(' and prev_char != '\\': unmatched_open_brackets += 1 - elif val == ")" and prev_char != "\\": + elif val == ')' and prev_char != '\\': unmatched_open_brackets -= 1 prev_char = val # If brackets are balanced, the end of the string for the current # named capture group pattern has been reached. if unmatched_open_brackets == 0: - group_pattern_and_name.append( - (pattern[start : end + idx + 1], group_name) - ) + group_pattern_and_name.append((pattern[start:end + idx + 1], group_name)) break # Replace the string for named capture groups with their group names. @@ -202,21 +185,19 @@ def replace_unnamed_groups(pattern): 3. ^(?P<a>\w+)/b/(\w+) ==> ^(?P<a>\w+)/b/<var> 4. ^(?P<a>\w+)/b/((x|y)\w+) ==> ^(?P<a>\w+)/b/<var> """ - unnamed_group_indices = [ - m.start(0) for m in unnamed_group_matcher.finditer(pattern) - ] + unnamed_group_indices = [m.start(0) for m in unnamed_group_matcher.finditer(pattern)] # Indices of the start of unnamed capture groups. group_indices = [] # Loop over the start indices of the groups. for start in unnamed_group_indices: # Handle nested parentheses, e.g. '^b/((x|y)\w+)$'. unmatched_open_brackets, prev_char = 1, None - for idx, val in enumerate(pattern[start + 1 :]): + for idx, val in enumerate(pattern[start + 1:]): # Check for unescaped `(` and `)`. They mark the start and end of # a nested group. - if val == "(" and prev_char != "\\": + if val == '(' and prev_char != '\\': unmatched_open_brackets += 1 - elif val == ")" and prev_char != "\\": + elif val == ')' and prev_char != '\\': unmatched_open_brackets -= 1 prev_char = val @@ -239,9 +220,9 @@ def replace_unnamed_groups(pattern): for start, end in group_start_end_indices: if prev_end: final_pattern.append(pattern[prev_end:start]) - final_pattern.append(pattern[:start] + "<var>") + final_pattern.append(pattern[:start] + '<var>') prev_end = end final_pattern.append(pattern[prev_end:]) - return "".join(final_pattern) + return ''.join(final_pattern) else: return pattern diff --git a/venv/Lib/site-packages/django/contrib/admindocs/views.py b/venv/Lib/site-packages/django/contrib/admindocs/views.py index e3c53fc..60b357e 100644 --- a/venv/Lib/site-packages/django/contrib/admindocs/views.py +++ b/venv/Lib/site-packages/django/contrib/admindocs/views.py @@ -4,10 +4,13 @@ from inspect import cleandoc from pathlib import Path from django.apps import apps +from django.conf import settings from django.contrib import admin from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admindocs import utils -from django.contrib.admindocs.utils import replace_named_groups, replace_unnamed_groups +from django.contrib.admindocs.utils import ( + replace_named_groups, replace_unnamed_groups, +) from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.db import models from django.http import Http404 @@ -15,11 +18,8 @@ from django.template.engine import Engine from django.urls import get_mod_func, get_resolver, get_urlconf from django.utils._os import safe_join from django.utils.decorators import method_decorator -from django.utils.functional import cached_property from django.utils.inspect import ( - func_accepts_kwargs, - func_accepts_var_args, - get_func_full_args, + func_accepts_kwargs, func_accepts_var_args, get_func_full_args, method_has_no_args, ) from django.utils.translation import gettext as _ @@ -28,37 +28,34 @@ from django.views.generic import TemplateView from .utils import get_view_name # Exclude methods starting with these strings from documentation -MODEL_METHODS_EXCLUDE = ("_", "add_", "delete", "save", "set_") +MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_') class BaseAdminDocsView(TemplateView): """ Base view for admindocs views. """ - @method_decorator(staff_member_required) def dispatch(self, request, *args, **kwargs): if not utils.docutils_is_available: # Display an error message for people without docutils - self.template_name = "admin_doc/missing_docutils.html" + self.template_name = 'admin_doc/missing_docutils.html' return self.render_to_response(admin.site.each_context(request)) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): - return super().get_context_data( - **{ - **kwargs, - **admin.site.each_context(self.request), - } - ) + return super().get_context_data(**{ + **kwargs, + **admin.site.each_context(self.request), + }) class BookmarkletsView(BaseAdminDocsView): - template_name = "admin_doc/bookmarklets.html" + template_name = 'admin_doc/bookmarklets.html' class TemplateTagIndexView(BaseAdminDocsView): - template_name = "admin_doc/template_tag_index.html" + template_name = 'admin_doc/template_tag_index.html' def get_context_data(self, **kwargs): tags = [] @@ -69,33 +66,27 @@ class TemplateTagIndexView(BaseAdminDocsView): pass else: app_libs = sorted(engine.template_libraries.items()) - builtin_libs = [("", lib) for lib in engine.template_builtins] + builtin_libs = [('', lib) for lib in engine.template_builtins] for module_name, library in builtin_libs + app_libs: for tag_name, tag_func in library.tags.items(): title, body, metadata = utils.parse_docstring(tag_func.__doc__) - title = title and utils.parse_rst( - title, "tag", _("tag:") + tag_name - ) - body = body and utils.parse_rst(body, "tag", _("tag:") + tag_name) + title = title and utils.parse_rst(title, 'tag', _('tag:') + tag_name) + body = body and utils.parse_rst(body, 'tag', _('tag:') + tag_name) for key in metadata: - metadata[key] = utils.parse_rst( - metadata[key], "tag", _("tag:") + tag_name - ) - tag_library = module_name.split(".")[-1] - tags.append( - { - "name": tag_name, - "title": title, - "body": body, - "meta": metadata, - "library": tag_library, - } - ) - return super().get_context_data(**{**kwargs, "tags": tags}) + metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name) + tag_library = module_name.split('.')[-1] + tags.append({ + 'name': tag_name, + 'title': title, + 'body': body, + 'meta': metadata, + 'library': tag_library, + }) + return super().get_context_data(**{**kwargs, 'tags': tags}) class TemplateFilterIndexView(BaseAdminDocsView): - template_name = "admin_doc/template_filter_index.html" + template_name = 'admin_doc/template_filter_index.html' def get_context_data(self, **kwargs): filters = [] @@ -106,58 +97,45 @@ class TemplateFilterIndexView(BaseAdminDocsView): pass else: app_libs = sorted(engine.template_libraries.items()) - builtin_libs = [("", lib) for lib in engine.template_builtins] + builtin_libs = [('', lib) for lib in engine.template_builtins] for module_name, library in builtin_libs + app_libs: for filter_name, filter_func in library.filters.items(): title, body, metadata = utils.parse_docstring(filter_func.__doc__) - title = title and utils.parse_rst( - title, "filter", _("filter:") + filter_name - ) - body = body and utils.parse_rst( - body, "filter", _("filter:") + filter_name - ) + title = title and utils.parse_rst(title, 'filter', _('filter:') + filter_name) + body = body and utils.parse_rst(body, 'filter', _('filter:') + filter_name) for key in metadata: - metadata[key] = utils.parse_rst( - metadata[key], "filter", _("filter:") + filter_name - ) - tag_library = module_name.split(".")[-1] - filters.append( - { - "name": filter_name, - "title": title, - "body": body, - "meta": metadata, - "library": tag_library, - } - ) - return super().get_context_data(**{**kwargs, "filters": filters}) + metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name) + tag_library = module_name.split('.')[-1] + filters.append({ + 'name': filter_name, + 'title': title, + 'body': body, + 'meta': metadata, + 'library': tag_library, + }) + return super().get_context_data(**{**kwargs, 'filters': filters}) class ViewIndexView(BaseAdminDocsView): - template_name = "admin_doc/view_index.html" + template_name = 'admin_doc/view_index.html' def get_context_data(self, **kwargs): views = [] - url_resolver = get_resolver(get_urlconf()) - try: - view_functions = extract_views_from_urlpatterns(url_resolver.url_patterns) - except ImproperlyConfigured: - view_functions = [] + urlconf = import_module(settings.ROOT_URLCONF) + view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns) for (func, regex, namespace, name) in view_functions: - views.append( - { - "full_name": get_view_name(func), - "url": simplify_regex(regex), - "url_name": ":".join((namespace or []) + (name and [name] or [])), - "namespace": ":".join(namespace or []), - "name": name, - } - ) - return super().get_context_data(**{**kwargs, "views": views}) + views.append({ + 'full_name': get_view_name(func), + 'url': simplify_regex(regex), + 'url_name': ':'.join((namespace or []) + (name and [name] or [])), + 'namespace': ':'.join(namespace or []), + 'name': name, + }) + return super().get_context_data(**{**kwargs, 'views': views}) class ViewDetailView(BaseAdminDocsView): - template_name = "admin_doc/view_detail.html" + template_name = 'admin_doc/view_detail.html' @staticmethod def _get_view_func(view): @@ -177,56 +155,52 @@ class ViewDetailView(BaseAdminDocsView): return getattr(getattr(import_module(mod), klass), func) def get_context_data(self, **kwargs): - view = self.kwargs["view"] + view = self.kwargs['view'] view_func = self._get_view_func(view) if view_func is None: raise Http404 title, body, metadata = utils.parse_docstring(view_func.__doc__) - title = title and utils.parse_rst(title, "view", _("view:") + view) - body = body and utils.parse_rst(body, "view", _("view:") + view) + title = title and utils.parse_rst(title, 'view', _('view:') + view) + body = body and utils.parse_rst(body, 'view', _('view:') + view) for key in metadata: - metadata[key] = utils.parse_rst(metadata[key], "model", _("view:") + view) - return super().get_context_data( - **{ - **kwargs, - "name": view, - "summary": title, - "body": body, - "meta": metadata, - } - ) + metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view) + return super().get_context_data(**{ + **kwargs, + 'name': view, + 'summary': title, + 'body': body, + 'meta': metadata, + }) class ModelIndexView(BaseAdminDocsView): - template_name = "admin_doc/model_index.html" + template_name = 'admin_doc/model_index.html' def get_context_data(self, **kwargs): m_list = [m._meta for m in apps.get_models()] - return super().get_context_data(**{**kwargs, "models": m_list}) + return super().get_context_data(**{**kwargs, 'models': m_list}) class ModelDetailView(BaseAdminDocsView): - template_name = "admin_doc/model_detail.html" + template_name = 'admin_doc/model_detail.html' def get_context_data(self, **kwargs): - model_name = self.kwargs["model_name"] + model_name = self.kwargs['model_name'] # Get the model class. try: - app_config = apps.get_app_config(self.kwargs["app_label"]) + app_config = apps.get_app_config(self.kwargs['app_label']) except LookupError: raise Http404(_("App %(app_label)r not found") % self.kwargs) try: model = app_config.get_model(model_name) except LookupError: - raise Http404( - _("Model %(model_name)r not found in app %(app_label)r") % self.kwargs - ) + raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs) opts = model._meta title, body, metadata = utils.parse_docstring(model.__doc__) - title = title and utils.parse_rst(title, "model", _("model:") + model_name) - body = body and utils.parse_rst(body, "model", _("model:") + model_name) + title = title and utils.parse_rst(title, 'model', _('model:') + model_name) + body = body and utils.parse_rst(body, 'model', _('model:') + model_name) # Gather fields/field descriptions. fields = [] @@ -237,63 +211,45 @@ class ModelDetailView(BaseAdminDocsView): data_type = field.remote_field.model.__name__ app_label = field.remote_field.model._meta.app_label verbose = utils.parse_rst( - ( - _("the related `%(app_label)s.%(data_type)s` object") - % { - "app_label": app_label, - "data_type": data_type, - } - ), - "model", - _("model:") + data_type, + (_("the related `%(app_label)s.%(data_type)s` object") % { + 'app_label': app_label, 'data_type': data_type, + }), + 'model', + _('model:') + data_type, ) else: data_type = get_readable_field_data_type(field) verbose = field.verbose_name - fields.append( - { - "name": field.name, - "data_type": data_type, - "verbose": verbose or "", - "help_text": field.help_text, - } - ) + fields.append({ + 'name': field.name, + 'data_type': data_type, + 'verbose': verbose or '', + 'help_text': field.help_text, + }) # Gather many-to-many fields. for field in opts.many_to_many: data_type = field.remote_field.model.__name__ app_label = field.remote_field.model._meta.app_label verbose = _("related `%(app_label)s.%(object_name)s` objects") % { - "app_label": app_label, - "object_name": data_type, + 'app_label': app_label, + 'object_name': data_type, } - fields.append( - { - "name": "%s.all" % field.name, - "data_type": "List", - "verbose": utils.parse_rst( - _("all %s") % verbose, "model", _("model:") + opts.model_name - ), - } - ) - fields.append( - { - "name": "%s.count" % field.name, - "data_type": "Integer", - "verbose": utils.parse_rst( - _("number of %s") % verbose, - "model", - _("model:") + opts.model_name, - ), - } - ) + fields.append({ + 'name': "%s.all" % field.name, + "data_type": 'List', + 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name), + }) + fields.append({ + 'name': "%s.count" % field.name, + 'data_type': 'Integer', + 'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name), + }) methods = [] # Gather model methods. for func_name, func in model.__dict__.items(): - if inspect.isfunction(func) or isinstance( - func, (cached_property, property) - ): + if inspect.isfunction(func) or isinstance(func, property): try: for exclude in MODEL_METHODS_EXCLUDE: if func_name.startswith(exclude): @@ -302,96 +258,69 @@ class ModelDetailView(BaseAdminDocsView): continue verbose = func.__doc__ verbose = verbose and ( - utils.parse_rst( - cleandoc(verbose), "model", _("model:") + opts.model_name - ) + utils.parse_rst(cleandoc(verbose), 'model', _('model:') + opts.model_name) ) - # Show properties, cached_properties, and methods without - # arguments as fields. Otherwise, show as a 'method with - # arguments'. - if isinstance(func, (cached_property, property)): - fields.append( - { - "name": func_name, - "data_type": get_return_data_type(func_name), - "verbose": verbose or "", - } - ) - elif ( - method_has_no_args(func) - and not func_accepts_kwargs(func) - and not func_accepts_var_args(func) - ): - fields.append( - { - "name": func_name, - "data_type": get_return_data_type(func_name), - "verbose": verbose or "", - } - ) + # Show properties and methods without arguments as fields. + # Otherwise, show as a 'method with arguments'. + if isinstance(func, property): + fields.append({ + 'name': func_name, + 'data_type': get_return_data_type(func_name), + 'verbose': verbose or '' + }) + elif method_has_no_args(func) and not func_accepts_kwargs(func) and not func_accepts_var_args(func): + fields.append({ + 'name': func_name, + 'data_type': get_return_data_type(func_name), + 'verbose': verbose or '', + }) else: arguments = get_func_full_args(func) # Join arguments with ', ' and in case of default value, # join it with '='. Use repr() so that strings will be # correctly displayed. - print_arguments = ", ".join( - [ - "=".join([arg_el[0], *map(repr, arg_el[1:])]) - for arg_el in arguments - ] - ) - methods.append( - { - "name": func_name, - "arguments": print_arguments, - "verbose": verbose or "", - } - ) + print_arguments = ', '.join([ + '='.join([arg_el[0], *map(repr, arg_el[1:])]) + for arg_el in arguments + ]) + methods.append({ + 'name': func_name, + 'arguments': print_arguments, + 'verbose': verbose or '', + }) # Gather related objects for rel in opts.related_objects: verbose = _("related `%(app_label)s.%(object_name)s` objects") % { - "app_label": rel.related_model._meta.app_label, - "object_name": rel.related_model._meta.object_name, + 'app_label': rel.related_model._meta.app_label, + 'object_name': rel.related_model._meta.object_name, } accessor = rel.get_accessor_name() - fields.append( - { - "name": "%s.all" % accessor, - "data_type": "List", - "verbose": utils.parse_rst( - _("all %s") % verbose, "model", _("model:") + opts.model_name - ), - } - ) - fields.append( - { - "name": "%s.count" % accessor, - "data_type": "Integer", - "verbose": utils.parse_rst( - _("number of %s") % verbose, - "model", - _("model:") + opts.model_name, - ), - } - ) - return super().get_context_data( - **{ - **kwargs, - "name": opts.label, - "summary": title, - "description": body, - "fields": fields, - "methods": methods, - } - ) + fields.append({ + 'name': "%s.all" % accessor, + 'data_type': 'List', + 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name), + }) + fields.append({ + 'name': "%s.count" % accessor, + 'data_type': 'Integer', + 'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name), + }) + return super().get_context_data(**{ + **kwargs, + 'name': opts.label, + 'summary': title, + 'description': body, + 'fields': fields, + 'methods': methods, + }) class TemplateDetailView(BaseAdminDocsView): - template_name = "admin_doc/template_detail.html" + template_name = 'admin_doc/template_detail.html' def get_context_data(self, **kwargs): - template = self.kwargs["template"] + template = self.kwargs['template'] templates = [] try: default_engine = Engine.get_default() @@ -405,22 +334,18 @@ class TemplateDetailView(BaseAdminDocsView): if template_file.exists(): template_contents = template_file.read_text() else: - template_contents = "" - templates.append( - { - "file": template_file, - "exists": template_file.exists(), - "contents": template_contents, - "order": index, - } - ) - return super().get_context_data( - **{ - **kwargs, - "name": template, - "templates": templates, - } - ) + template_contents = '' + templates.append({ + 'file': template_file, + 'exists': template_file.exists(), + 'contents': template_contents, + 'order': index, + }) + return super().get_context_data(**{ + **kwargs, + 'name': template, + 'templates': templates, + }) #################### @@ -430,12 +355,12 @@ class TemplateDetailView(BaseAdminDocsView): def get_return_data_type(func_name): """Return a somewhat-helpful data type given a function name""" - if func_name.startswith("get_"): - if func_name.endswith("_list"): - return "List" - elif func_name.endswith("_count"): - return "Integer" - return "" + if func_name.startswith('get_'): + if func_name.endswith('_list'): + return 'List' + elif func_name.endswith('_count'): + return 'Integer' + return '' def get_readable_field_data_type(field): @@ -447,7 +372,7 @@ def get_readable_field_data_type(field): return field.description % field.__dict__ -def extract_views_from_urlpatterns(urlpatterns, base="", namespace=None): +def extract_views_from_urlpatterns(urlpatterns, base='', namespace=None): """ Return a list of views from a list of urlpatterns. @@ -455,19 +380,17 @@ def extract_views_from_urlpatterns(urlpatterns, base="", namespace=None): """ views = [] for p in urlpatterns: - if hasattr(p, "url_patterns"): + if hasattr(p, 'url_patterns'): try: patterns = p.url_patterns except ImportError: continue - views.extend( - extract_views_from_urlpatterns( - patterns, - base + str(p.pattern), - (namespace or []) + (p.namespace and [p.namespace] or []), - ) - ) - elif hasattr(p, "callback"): + views.extend(extract_views_from_urlpatterns( + patterns, + base + str(p.pattern), + (namespace or []) + (p.namespace and [p.namespace] or []) + )) + elif hasattr(p, 'callback'): try: views.append((p.callback, base + str(p.pattern), namespace, p.name)) except ViewDoesNotExist: @@ -486,7 +409,7 @@ def simplify_regex(pattern): pattern = replace_named_groups(pattern) pattern = replace_unnamed_groups(pattern) # clean up any outstanding regex-y characters. - pattern = pattern.replace("^", "").replace("$", "").replace("?", "") - if not pattern.startswith("/"): - pattern = "/" + pattern + pattern = pattern.replace('^', '').replace('$', '').replace('?', '') + if not pattern.startswith('/'): + pattern = '/' + pattern return pattern diff --git a/venv/Lib/site-packages/django/contrib/auth/__init__.py b/venv/Lib/site-packages/django/contrib/auth/__init__.py index 155330c..cad8eff 100644 --- a/venv/Lib/site-packages/django/contrib/auth/__init__.py +++ b/venv/Lib/site-packages/django/contrib/auth/__init__.py @@ -11,10 +11,10 @@ from django.views.decorators.debug import sensitive_variables from .signals import user_logged_in, user_logged_out, user_login_failed -SESSION_KEY = "_auth_user_id" -BACKEND_SESSION_KEY = "_auth_user_backend" -HASH_SESSION_KEY = "_auth_user_hash" -REDIRECT_FIELD_NAME = "next" +SESSION_KEY = '_auth_user_id' +BACKEND_SESSION_KEY = '_auth_user_backend' +HASH_SESSION_KEY = '_auth_user_hash' +REDIRECT_FIELD_NAME = 'next' def load_backend(path): @@ -28,8 +28,8 @@ def _get_backends(return_tuples=False): backends.append((backend, backend_path) if return_tuples else backend) if not backends: raise ImproperlyConfigured( - "No authentication backends have been defined. Does " - "AUTHENTICATION_BACKENDS contain anything?" + 'No authentication backends have been defined. Does ' + 'AUTHENTICATION_BACKENDS contain anything?' ) return backends @@ -38,7 +38,7 @@ def get_backends(): return _get_backends(return_tuples=False) -@sensitive_variables("credentials") +@sensitive_variables('credentials') def _clean_credentials(credentials): """ Clean a dictionary of credentials of potentially sensitive info before @@ -46,8 +46,8 @@ def _clean_credentials(credentials): Not comprehensive - intended for user_login_failed signal """ - SENSITIVE_CREDENTIALS = re.compile("api|token|key|secret|password|signature", re.I) - CLEANSED_SUBSTITUTE = "********************" + SENSITIVE_CREDENTIALS = re.compile('api|token|key|secret|password|signature', re.I) + CLEANSED_SUBSTITUTE = '********************' for key in credentials: if SENSITIVE_CREDENTIALS.search(key): credentials[key] = CLEANSED_SUBSTITUTE @@ -60,7 +60,7 @@ def _get_user_session_key(request): return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY]) -@sensitive_variables("credentials") +@sensitive_variables('credentials') def authenticate(request=None, **credentials): """ If the given credentials are valid, return a User object. @@ -70,14 +70,12 @@ def authenticate(request=None, **credentials): try: backend_signature.bind(request, **credentials) except TypeError: - # This backend doesn't accept these credentials as arguments. Try - # the next one. + # This backend doesn't accept these credentials as arguments. Try the next one. continue try: user = backend.authenticate(request, **credentials) except PermissionDenied: - # This backend says to stop in our tracks - this user should not be - # allowed in at all. + # This backend says to stop in our tracks - this user should not be allowed in at all. break if user is None: continue @@ -86,9 +84,7 @@ def authenticate(request=None, **credentials): return user # The credentials supplied are invalid to all backends, fire signal - user_login_failed.send( - sender=__name__, credentials=_clean_credentials(credentials), request=request - ) + user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request) def login(request, user, backend=None): @@ -97,19 +93,16 @@ def login(request, user, backend=None): have to reauthenticate on every request. Note that data set during the anonymous session is retained when the user logs in. """ - session_auth_hash = "" + session_auth_hash = '' if user is None: user = request.user - if hasattr(user, "get_session_auth_hash"): + if hasattr(user, 'get_session_auth_hash'): session_auth_hash = user.get_session_auth_hash() if SESSION_KEY in request.session: if _get_user_session_key(request) != user.pk or ( - session_auth_hash - and not constant_time_compare( - request.session.get(HASH_SESSION_KEY, ""), session_auth_hash - ) - ): + session_auth_hash and + not constant_time_compare(request.session.get(HASH_SESSION_KEY, ''), session_auth_hash)): # To avoid reusing another user's session, create a new, empty # session if the existing session corresponds to a different # authenticated user. @@ -125,20 +118,18 @@ def login(request, user, backend=None): _, backend = backends[0] else: raise ValueError( - "You have multiple authentication backends configured and " - "therefore must provide the `backend` argument or set the " - "`backend` attribute on the user." + 'You have multiple authentication backends configured and ' + 'therefore must provide the `backend` argument or set the ' + '`backend` attribute on the user.' ) else: if not isinstance(backend, str): - raise TypeError( - "backend must be a dotted import path string (got %r)." % backend - ) + raise TypeError('backend must be a dotted import path string (got %r).' % backend) request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) request.session[BACKEND_SESSION_KEY] = backend request.session[HASH_SESSION_KEY] = session_auth_hash - if hasattr(request, "user"): + if hasattr(request, 'user'): request.user = user rotate_token(request) user_logged_in.send(sender=user.__class__, request=request, user=user) @@ -151,14 +142,13 @@ def logout(request): """ # Dispatch the signal before the user is logged out so the receivers have a # chance to find out *who* logged out. - user = getattr(request, "user", None) - if not getattr(user, "is_authenticated", True): + user = getattr(request, 'user', None) + if not getattr(user, 'is_authenticated', True): user = None user_logged_out.send(sender=user.__class__, request=request, user=user) request.session.flush() - if hasattr(request, "user"): + if hasattr(request, 'user'): from django.contrib.auth.models import AnonymousUser - request.user = AnonymousUser() @@ -169,13 +159,10 @@ def get_user_model(): try: return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False) except ValueError: - raise ImproperlyConfigured( - "AUTH_USER_MODEL must be of the form 'app_label.model_name'" - ) + raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'") except LookupError: raise ImproperlyConfigured( - "AUTH_USER_MODEL refers to model '%s' that has not been installed" - % settings.AUTH_USER_MODEL + "AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL ) @@ -185,7 +172,6 @@ def get_user(request): If no user is retrieved, return an instance of `AnonymousUser`. """ from .models import AnonymousUser - user = None try: user_id = _get_user_session_key(request) @@ -197,14 +183,20 @@ def get_user(request): backend = load_backend(backend_path) user = backend.get_user(user_id) # Verify the session - if hasattr(user, "get_session_auth_hash"): + if hasattr(user, 'get_session_auth_hash'): session_hash = request.session.get(HASH_SESSION_KEY) session_hash_verified = session_hash and constant_time_compare( - session_hash, user.get_session_auth_hash() + session_hash, + user.get_session_auth_hash() ) if not session_hash_verified: - request.session.flush() - user = None + if not ( + session_hash and + hasattr(user, '_legacy_get_session_auth_hash') and + constant_time_compare(session_hash, user._legacy_get_session_auth_hash()) + ): + request.session.flush() + user = None return user or AnonymousUser() @@ -213,7 +205,7 @@ def get_permission_codename(action, opts): """ Return the codename of the permission for the specified action. """ - return "%s_%s" % (action, opts.model_name) + return '%s_%s' % (action, opts.model_name) def update_session_auth_hash(request, user): @@ -226,5 +218,5 @@ def update_session_auth_hash(request, user): password was changed. """ request.session.cycle_key() - if hasattr(user, "get_session_auth_hash") and request.user == user: + if hasattr(user, 'get_session_auth_hash') and request.user == user: request.session[HASH_SESSION_KEY] = user.get_session_auth_hash() diff --git a/venv/Lib/site-packages/django/contrib/auth/admin.py b/venv/Lib/site-packages/django/contrib/auth/admin.py index 636e620..6709e63 100644 --- a/venv/Lib/site-packages/django/contrib/auth/admin.py +++ b/venv/Lib/site-packages/django/contrib/auth/admin.py @@ -4,9 +4,7 @@ from django.contrib.admin.options import IS_POPUP_VAR from django.contrib.admin.utils import unquote from django.contrib.auth import update_session_auth_hash from django.contrib.auth.forms import ( - AdminPasswordChangeForm, - UserChangeForm, - UserCreationForm, + AdminPasswordChangeForm, UserChangeForm, UserCreationForm, ) from django.contrib.auth.models import Group, User from django.core.exceptions import PermissionDenied @@ -16,8 +14,7 @@ from django.template.response import TemplateResponse from django.urls import path, reverse from django.utils.decorators import method_decorator from django.utils.html import escape -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ from django.views.decorators.csrf import csrf_protect from django.views.decorators.debug import sensitive_post_parameters @@ -27,60 +24,45 @@ sensitive_post_parameters_m = method_decorator(sensitive_post_parameters()) @admin.register(Group) class GroupAdmin(admin.ModelAdmin): - search_fields = ("name",) - ordering = ("name",) - filter_horizontal = ("permissions",) + search_fields = ('name',) + ordering = ('name',) + filter_horizontal = ('permissions',) def formfield_for_manytomany(self, db_field, request=None, **kwargs): - if db_field.name == "permissions": - qs = kwargs.get("queryset", db_field.remote_field.model.objects) + if db_field.name == 'permissions': + qs = kwargs.get('queryset', db_field.remote_field.model.objects) # Avoid a major performance hit resolving permission names which # triggers a content_type load: - kwargs["queryset"] = qs.select_related("content_type") + kwargs['queryset'] = qs.select_related('content_type') return super().formfield_for_manytomany(db_field, request=request, **kwargs) @admin.register(User) class UserAdmin(admin.ModelAdmin): - add_form_template = "admin/auth/user/add_form.html" + add_form_template = 'admin/auth/user/add_form.html' change_user_password_template = None fieldsets = ( - (None, {"fields": ("username", "password")}), - (_("Personal info"), {"fields": ("first_name", "last_name", "email")}), - ( - _("Permissions"), - { - "fields": ( - "is_active", - "is_staff", - "is_superuser", - "groups", - "user_permissions", - ), - }, - ), - (_("Important dates"), {"fields": ("last_login", "date_joined")}), + (None, {'fields': ('username', 'password')}), + (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), + (_('Permissions'), { + 'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'), + }), + (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) add_fieldsets = ( - ( - None, - { - "classes": ("wide",), - "fields": ("username", "password1", "password2"), - }, - ), + (None, { + 'classes': ('wide',), + 'fields': ('username', 'password1', 'password2'), + }), ) form = UserChangeForm add_form = UserCreationForm change_password_form = AdminPasswordChangeForm - list_display = ("username", "email", "first_name", "last_name", "is_staff") - list_filter = ("is_staff", "is_superuser", "is_active", "groups") - search_fields = ("username", "first_name", "last_name", "email") - ordering = ("username",) - filter_horizontal = ( - "groups", - "user_permissions", - ) + list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff') + list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups') + search_fields = ('username', 'first_name', 'last_name', 'email') + ordering = ('username',) + filter_horizontal = ('groups', 'user_permissions',) def get_fieldsets(self, request, obj=None): if not obj: @@ -93,32 +75,30 @@ class UserAdmin(admin.ModelAdmin): """ defaults = {} if obj is None: - defaults["form"] = self.add_form + defaults['form'] = self.add_form defaults.update(kwargs) return super().get_form(request, obj, **defaults) def get_urls(self): return [ path( - "<id>/password/", + '<id>/password/', self.admin_site.admin_view(self.user_change_password), - name="auth_user_password_change", + name='auth_user_password_change', ), ] + super().get_urls() def lookup_allowed(self, lookup, value): # Don't allow lookups involving passwords. - return not lookup.startswith("password") and super().lookup_allowed( - lookup, value - ) + return not lookup.startswith('password') and super().lookup_allowed(lookup, value) @sensitive_post_parameters_m @csrf_protect_m - def add_view(self, request, form_url="", extra_context=None): + def add_view(self, request, form_url='', extra_context=None): with transaction.atomic(using=router.db_for_write(self.model)): return self._add_view(request, form_url, extra_context) - def _add_view(self, request, form_url="", extra_context=None): + def _add_view(self, request, form_url='', extra_context=None): # It's an error for a user to have add permission but NOT change # permission for users. If we allowed such users to add users, they # could create superusers, which would mean they would essentially have @@ -131,47 +111,42 @@ class UserAdmin(admin.ModelAdmin): # error message. raise Http404( 'Your user does not have the "Change user" permission. In ' - "order to add users, Django requires that your user " + 'order to add users, Django requires that your user ' 'account have both the "Add user" and "Change user" ' - "permissions set." - ) + 'permissions set.') raise PermissionDenied if extra_context is None: extra_context = {} username_field = self.model._meta.get_field(self.model.USERNAME_FIELD) defaults = { - "auto_populated_fields": (), - "username_help_text": username_field.help_text, + 'auto_populated_fields': (), + 'username_help_text': username_field.help_text, } extra_context.update(defaults) return super().add_view(request, form_url, extra_context) @sensitive_post_parameters_m - def user_change_password(self, request, id, form_url=""): + def user_change_password(self, request, id, form_url=''): user = self.get_object(request, unquote(id)) if not self.has_change_permission(request, user): raise PermissionDenied if user is None: - raise Http404( - _("%(name)s object with primary key %(key)r does not exist.") - % { - "name": self.model._meta.verbose_name, - "key": escape(id), - } - ) - if request.method == "POST": + raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % { + 'name': self.model._meta.verbose_name, + 'key': escape(id), + }) + if request.method == 'POST': form = self.change_password_form(user, request.POST) if form.is_valid(): form.save() change_message = self.construct_change_message(request, form, None) self.log_change(request, user, change_message) - msg = gettext("Password changed successfully.") + msg = gettext('Password changed successfully.') messages.success(request, msg) update_session_auth_hash(request, form.user) return HttpResponseRedirect( reverse( - "%s:%s_%s_change" - % ( + '%s:%s_%s_change' % ( self.admin_site.name, user._meta.app_label, user._meta.model_name, @@ -182,25 +157,25 @@ class UserAdmin(admin.ModelAdmin): else: form = self.change_password_form(user) - fieldsets = [(None, {"fields": list(form.base_fields)})] + fieldsets = [(None, {'fields': list(form.base_fields)})] adminForm = admin.helpers.AdminForm(form, fieldsets, {}) context = { - "title": _("Change password: %s") % escape(user.get_username()), - "adminForm": adminForm, - "form_url": form_url, - "form": form, - "is_popup": (IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET), - "is_popup_var": IS_POPUP_VAR, - "add": True, - "change": False, - "has_delete_permission": False, - "has_change_permission": True, - "has_absolute_url": False, - "opts": self.model._meta, - "original": user, - "save_as": False, - "show_save": True, + 'title': _('Change password: %s') % escape(user.get_username()), + 'adminForm': adminForm, + 'form_url': form_url, + 'form': form, + 'is_popup': (IS_POPUP_VAR in request.POST or + IS_POPUP_VAR in request.GET), + 'add': True, + 'change': False, + 'has_delete_permission': False, + 'has_change_permission': True, + 'has_absolute_url': False, + 'opts': self.model._meta, + 'original': user, + 'save_as': False, + 'show_save': True, **self.admin_site.each_context(request), } @@ -208,8 +183,8 @@ class UserAdmin(admin.ModelAdmin): return TemplateResponse( request, - self.change_user_password_template - or "admin/auth/user/change_password.html", + self.change_user_password_template or + 'admin/auth/user/change_password.html', context, ) @@ -224,7 +199,7 @@ class UserAdmin(admin.ModelAdmin): # button except in two scenarios: # * The user has pressed the 'Save and add another' button # * We are adding a user in a popup - if "_addanother" not in request.POST and IS_POPUP_VAR not in request.POST: + if '_addanother' not in request.POST and IS_POPUP_VAR not in request.POST: request.POST = request.POST.copy() - request.POST["_continue"] = 1 + request.POST['_continue'] = 1 return super().response_add(request, obj, post_url_continue) diff --git a/venv/Lib/site-packages/django/contrib/auth/apps.py b/venv/Lib/site-packages/django/contrib/auth/apps.py index 4882a27..4e4ef06 100644 --- a/venv/Lib/site-packages/django/contrib/auth/apps.py +++ b/venv/Lib/site-packages/django/contrib/auth/apps.py @@ -11,20 +11,19 @@ from .signals import user_logged_in class AuthConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.auth" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.auth' verbose_name = _("Authentication and Authorization") def ready(self): post_migrate.connect( create_permissions, - dispatch_uid="django.contrib.auth.management.create_permissions", + dispatch_uid="django.contrib.auth.management.create_permissions" ) - last_login_field = getattr(get_user_model(), "last_login", None) + last_login_field = getattr(get_user_model(), 'last_login', None) # Register the handler only if UserModel.last_login is a field. if isinstance(last_login_field, DeferredAttribute): from .models import update_last_login - - user_logged_in.connect(update_last_login, dispatch_uid="update_last_login") + user_logged_in.connect(update_last_login, dispatch_uid='update_last_login') checks.register(check_user_model, checks.Tags.models) checks.register(check_models_permissions, checks.Tags.models) diff --git a/venv/Lib/site-packages/django/contrib/auth/backends.py b/venv/Lib/site-packages/django/contrib/auth/backends.py index 7cf4057..ac86749 100644 --- a/venv/Lib/site-packages/django/contrib/auth/backends.py +++ b/venv/Lib/site-packages/django/contrib/auth/backends.py @@ -53,15 +53,15 @@ class ModelBackend(BaseBackend): Reject users with is_active=False. Custom user models that don't have that attribute are allowed. """ - is_active = getattr(user, "is_active", None) + is_active = getattr(user, 'is_active', None) return is_active or is_active is None def _get_user_permissions(self, user_obj): return user_obj.user_permissions.all() def _get_group_permissions(self, user_obj): - user_groups_field = get_user_model()._meta.get_field("groups") - user_groups_query = "group__%s" % user_groups_field.related_query_name() + user_groups_field = get_user_model()._meta.get_field('groups') + user_groups_query = 'group__%s' % user_groups_field.related_query_name() return Permission.objects.filter(**{user_groups_query: user_obj}) def _get_permissions(self, user_obj, obj, from_name): @@ -73,16 +73,14 @@ class ModelBackend(BaseBackend): if not user_obj.is_active or user_obj.is_anonymous or obj is not None: return set() - perm_cache_name = "_%s_perm_cache" % from_name + perm_cache_name = '_%s_perm_cache' % from_name if not hasattr(user_obj, perm_cache_name): if user_obj.is_superuser: perms = Permission.objects.all() else: - perms = getattr(self, "_get_%s_permissions" % from_name)(user_obj) - perms = perms.values_list("content_type__app_label", "codename").order_by() - setattr( - user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms} - ) + perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj) + perms = perms.values_list('content_type__app_label', 'codename').order_by() + setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms}) return getattr(user_obj, perm_cache_name) def get_user_permissions(self, user_obj, obj=None): @@ -90,19 +88,19 @@ class ModelBackend(BaseBackend): Return a set of permission strings the user `user_obj` has from their `user_permissions`. """ - return self._get_permissions(user_obj, obj, "user") + return self._get_permissions(user_obj, obj, 'user') def get_group_permissions(self, user_obj, obj=None): """ Return a set of permission strings the user `user_obj` has from the groups they belong. """ - return self._get_permissions(user_obj, obj, "group") + return self._get_permissions(user_obj, obj, 'group') def get_all_permissions(self, user_obj, obj=None): if not user_obj.is_active or user_obj.is_anonymous or obj is not None: return set() - if not hasattr(user_obj, "_perm_cache"): + if not hasattr(user_obj, '_perm_cache'): user_obj._perm_cache = super().get_all_permissions(user_obj) return user_obj._perm_cache @@ -114,7 +112,7 @@ class ModelBackend(BaseBackend): Return True if user_obj has any permissions in the given app_label. """ return user_obj.is_active and any( - perm[: perm.index(".")] == app_label + perm[:perm.index('.')] == app_label for perm in self.get_all_permissions(user_obj) ) @@ -125,21 +123,22 @@ class ModelBackend(BaseBackend): """ if isinstance(perm, str): try: - app_label, codename = perm.split(".") + app_label, codename = perm.split('.') except ValueError: raise ValueError( - "Permission name should be in the form " - "app_label.permission_codename." + 'Permission name should be in the form ' + 'app_label.permission_codename.' ) elif not isinstance(perm, Permission): raise TypeError( - "The `perm` argument must be a string or a permission instance." + 'The `perm` argument must be a string or a permission instance.' ) + UserModel = get_user_model() if obj is not None: return UserModel._default_manager.none() - permission_q = Q(group__user=OuterRef("pk")) | Q(user=OuterRef("pk")) + permission_q = Q(group__user=OuterRef('pk')) | Q(user=OuterRef('pk')) if isinstance(perm, Permission): permission_q &= Q(pk=perm.pk) else: @@ -199,9 +198,9 @@ class RemoteUserBackend(ModelBackend): # instead we use get_or_create when creating unknown users since it has # built-in safeguards for multiple threads. if self.create_unknown_user: - user, created = UserModel._default_manager.get_or_create( - **{UserModel.USERNAME_FIELD: username} - ) + user, created = UserModel._default_manager.get_or_create(**{ + UserModel.USERNAME_FIELD: username + }) if created: user = self.configure_user(request, user) else: diff --git a/venv/Lib/site-packages/django/contrib/auth/base_user.py b/venv/Lib/site-packages/django/contrib/auth/base_user.py index f6de3b9..3a4a64e 100644 --- a/venv/Lib/site-packages/django/contrib/auth/base_user.py +++ b/venv/Lib/site-packages/django/contrib/auth/base_user.py @@ -4,11 +4,10 @@ not in INSTALLED_APPS. """ import unicodedata +from django.conf import settings from django.contrib.auth import password_validation from django.contrib.auth.hashers import ( - check_password, - is_password_usable, - make_password, + check_password, is_password_usable, make_password, ) from django.db import models from django.utils.crypto import get_random_string, salted_hmac @@ -16,25 +15,25 @@ from django.utils.translation import gettext_lazy as _ class BaseUserManager(models.Manager): + @classmethod def normalize_email(cls, email): """ Normalize the email address by lowercasing the domain part of it. """ - email = email or "" + email = email or '' try: - email_name, domain_part = email.strip().rsplit("@", 1) + email_name, domain_part = email.strip().rsplit('@', 1) except ValueError: pass else: - email = email_name + "@" + domain_part.lower() + email = email_name + '@' + domain_part.lower() return email - def make_random_password( - self, - length=10, - allowed_chars="abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", - ): + def make_random_password(self, length=10, + allowed_chars='abcdefghjkmnpqrstuvwxyz' + 'ABCDEFGHJKLMNPQRSTUVWXYZ' + '23456789'): """ Generate a random password with the given length and given allowed_chars. The default value of allowed_chars does not have "I" or @@ -47,8 +46,8 @@ class BaseUserManager(models.Manager): class AbstractBaseUser(models.Model): - password = models.CharField(_("password"), max_length=128) - last_login = models.DateTimeField(_("last login"), blank=True, null=True) + password = models.CharField(_('password'), max_length=128) + last_login = models.DateTimeField(_('last login'), blank=True, null=True) is_active = True @@ -105,13 +104,11 @@ class AbstractBaseUser(models.Model): Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ - def setter(raw_password): self.set_password(raw_password) # Password hash upgrades shouldn't be considered password changes. self._password = None self.save(update_fields=["password"]) - return check_password(raw_password, self.password, setter) def set_unusable_password(self): @@ -124,6 +121,11 @@ class AbstractBaseUser(models.Model): """ return is_password_usable(self.password) + def _legacy_get_session_auth_hash(self): + # RemovedInDjango40Warning: pre-Django 3.1 hashes will be invalid. + key_salt = 'django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash' + return salted_hmac(key_salt, self.password, algorithm='sha1').hexdigest() + def get_session_auth_hash(self): """ Return an HMAC of the password field. @@ -132,7 +134,10 @@ class AbstractBaseUser(models.Model): return salted_hmac( key_salt, self.password, - algorithm="sha256", + # RemovedInDjango40Warning: when the deprecation ends, replace + # with: + # algorithm='sha256', + algorithm=settings.DEFAULT_HASHING_ALGORITHM, ).hexdigest() @classmethod @@ -140,12 +145,8 @@ class AbstractBaseUser(models.Model): try: return cls.EMAIL_FIELD except AttributeError: - return "email" + return 'email' @classmethod def normalize_username(cls, username): - return ( - unicodedata.normalize("NFKC", username) - if isinstance(username, str) - else username - ) + return unicodedata.normalize('NFKC', username) if isinstance(username, str) else username diff --git a/venv/Lib/site-packages/django/contrib/auth/checks.py b/venv/Lib/site-packages/django/contrib/auth/checks.py index ee80825..c08ed8a 100644 --- a/venv/Lib/site-packages/django/contrib/auth/checks.py +++ b/venv/Lib/site-packages/django/contrib/auth/checks.py @@ -12,7 +12,7 @@ def check_user_model(app_configs=None, **kwargs): if app_configs is None: cls = apps.get_model(settings.AUTH_USER_MODEL) else: - app_label, model_name = settings.AUTH_USER_MODEL.split(".") + app_label, model_name = settings.AUTH_USER_MODEL.split('.') for app_config in app_configs: if app_config.label == app_label: cls = app_config.get_model(model_name) @@ -31,7 +31,7 @@ def check_user_model(app_configs=None, **kwargs): checks.Error( "'REQUIRED_FIELDS' must be a list or tuple.", obj=cls, - id="auth.E001", + id='auth.E001', ) ) @@ -47,7 +47,7 @@ def check_user_model(app_configs=None, **kwargs): % (cls.USERNAME_FIELD, cls.USERNAME_FIELD) ), obj=cls, - id="auth.E002", + id='auth.E002', ) ) @@ -56,49 +56,47 @@ def check_user_model(app_configs=None, **kwargs): constraint.fields == (cls.USERNAME_FIELD,) for constraint in cls._meta.total_unique_constraints ): - if settings.AUTHENTICATION_BACKENDS == [ - "django.contrib.auth.backends.ModelBackend" - ]: + if (settings.AUTHENTICATION_BACKENDS == + ['django.contrib.auth.backends.ModelBackend']): errors.append( checks.Error( - "'%s.%s' must be unique because it is named as the " - "'USERNAME_FIELD'." % (cls._meta.object_name, cls.USERNAME_FIELD), + "'%s.%s' must be unique because it is named as the 'USERNAME_FIELD'." % ( + cls._meta.object_name, cls.USERNAME_FIELD + ), obj=cls, - id="auth.E003", + id='auth.E003', ) ) else: errors.append( checks.Warning( - "'%s.%s' is named as the 'USERNAME_FIELD', but it is not unique." - % (cls._meta.object_name, cls.USERNAME_FIELD), - hint=( - "Ensure that your authentication backend(s) can handle " - "non-unique usernames." + "'%s.%s' is named as the 'USERNAME_FIELD', but it is not unique." % ( + cls._meta.object_name, cls.USERNAME_FIELD ), + hint='Ensure that your authentication backend(s) can handle non-unique usernames.', obj=cls, - id="auth.W004", + id='auth.W004', ) ) if isinstance(cls().is_anonymous, MethodType): errors.append( checks.Critical( - "%s.is_anonymous must be an attribute or property rather than " - "a method. Ignoring this is a security issue as anonymous " - "users will be treated as authenticated!" % cls, + '%s.is_anonymous must be an attribute or property rather than ' + 'a method. Ignoring this is a security issue as anonymous ' + 'users will be treated as authenticated!' % cls, obj=cls, - id="auth.C009", + id='auth.C009', ) ) if isinstance(cls().is_authenticated, MethodType): errors.append( checks.Critical( - "%s.is_authenticated must be an attribute or property rather " - "than a method. Ignoring this is a security issue as anonymous " - "users will be treated as authenticated!" % cls, + '%s.is_authenticated must be an attribute or property rather ' + 'than a method. Ignoring this is a security issue as anonymous ' + 'users will be treated as authenticated!' % cls, obj=cls, - id="auth.C010", + id='auth.C010', ) ) return errors @@ -108,13 +106,11 @@ def check_models_permissions(app_configs=None, **kwargs): if app_configs is None: models = apps.get_models() else: - models = chain.from_iterable( - app_config.get_models() for app_config in app_configs - ) + models = chain.from_iterable(app_config.get_models() for app_config in app_configs) - Permission = apps.get_model("auth", "Permission") - permission_name_max_length = Permission._meta.get_field("name").max_length - permission_codename_max_length = Permission._meta.get_field("codename").max_length + Permission = apps.get_model('auth', 'Permission') + permission_name_max_length = Permission._meta.get_field('name').max_length + permission_codename_max_length = Permission._meta.get_field('codename').max_length errors = [] for model in models: @@ -123,28 +119,27 @@ def check_models_permissions(app_configs=None, **kwargs): # Check builtin permission name length. max_builtin_permission_name_length = ( max(len(name) for name in builtin_permissions.values()) - if builtin_permissions - else 0 + if builtin_permissions else 0 ) if max_builtin_permission_name_length > permission_name_max_length: - verbose_name_max_length = permission_name_max_length - ( - max_builtin_permission_name_length - len(opts.verbose_name_raw) + verbose_name_max_length = ( + permission_name_max_length - (max_builtin_permission_name_length - len(opts.verbose_name_raw)) ) errors.append( checks.Error( "The verbose_name of model '%s' must be at most %d " "characters for its builtin permission names to be at " - "most %d characters." - % (opts.label, verbose_name_max_length, permission_name_max_length), + "most %d characters." % ( + opts.label, verbose_name_max_length, permission_name_max_length + ), obj=model, - id="auth.E007", + id='auth.E007', ) ) # Check builtin permission codename length. max_builtin_permission_codename_length = ( max(len(codename) for codename in builtin_permissions.keys()) - if builtin_permissions - else 0 + if builtin_permissions else 0 ) if max_builtin_permission_codename_length > permission_codename_max_length: model_name_max_length = permission_codename_max_length - ( @@ -154,14 +149,13 @@ def check_models_permissions(app_configs=None, **kwargs): checks.Error( "The name of model '%s' must be at most %d characters " "for its builtin permission codenames to be at most %d " - "characters." - % ( + "characters." % ( opts.label, model_name_max_length, permission_codename_max_length, ), obj=model, - id="auth.E011", + id='auth.E011', ) ) codenames = set() @@ -171,14 +165,11 @@ def check_models_permissions(app_configs=None, **kwargs): errors.append( checks.Error( "The permission named '%s' of model '%s' is longer " - "than %d characters." - % ( - name, - opts.label, - permission_name_max_length, + "than %d characters." % ( + name, opts.label, permission_name_max_length, ), obj=model, - id="auth.E008", + id='auth.E008', ) ) # Check custom permission codename length. @@ -186,24 +177,23 @@ def check_models_permissions(app_configs=None, **kwargs): errors.append( checks.Error( "The permission codenamed '%s' of model '%s' is " - "longer than %d characters." - % ( + "longer than %d characters." % ( codename, opts.label, permission_codename_max_length, ), obj=model, - id="auth.E012", + id='auth.E012', ) ) # Check custom permissions codename clashing. if codename in builtin_permissions: errors.append( checks.Error( - "The permission codenamed '%s' clashes with a builtin " - "permission for model '%s'." % (codename, opts.label), + "The permission codenamed '%s' clashes with a builtin permission " + "for model '%s'." % (codename, opts.label), obj=model, - id="auth.E005", + id='auth.E005', ) ) elif codename in codenames: @@ -212,7 +202,7 @@ def check_models_permissions(app_configs=None, **kwargs): "The permission codenamed '%s' is duplicated for " "model '%s'." % (codename, opts.label), obj=model, - id="auth.E006", + id='auth.E006', ) ) codenames.add(codename) diff --git a/venv/Lib/site-packages/django/contrib/auth/context_processors.py b/venv/Lib/site-packages/django/contrib/auth/context_processors.py index 0a88199..c223971 100644 --- a/venv/Lib/site-packages/django/contrib/auth/context_processors.py +++ b/venv/Lib/site-packages/django/contrib/auth/context_processors.py @@ -25,9 +25,6 @@ class PermWrapper: def __init__(self, user): self.user = user - def __repr__(self): - return f"{self.__class__.__qualname__}({self.user!r})" - def __getitem__(self, app_label): return PermLookupDict(self.user, app_label) @@ -39,10 +36,10 @@ class PermWrapper: """ Lookup by "someapp" or "someapp.someperm" in perms. """ - if "." not in perm_name: + if '.' not in perm_name: # The name refers to module. return bool(self[perm_name]) - app_label, perm_name = perm_name.split(".", 1) + app_label, perm_name = perm_name.split('.', 1) return self[app_label][perm_name] @@ -54,14 +51,13 @@ def auth(request): If there is no 'user' attribute in the request, use AnonymousUser (from django.contrib.auth). """ - if hasattr(request, "user"): + if hasattr(request, 'user'): user = request.user else: from django.contrib.auth.models import AnonymousUser - user = AnonymousUser() return { - "user": user, - "perms": PermWrapper(user), + 'user': user, + 'perms': PermWrapper(user), } diff --git a/venv/Lib/site-packages/django/contrib/auth/decorators.py b/venv/Lib/site-packages/django/contrib/auth/decorators.py index a419068..53f62e8 100644 --- a/venv/Lib/site-packages/django/contrib/auth/decorators.py +++ b/venv/Lib/site-packages/django/contrib/auth/decorators.py @@ -7,9 +7,7 @@ from django.core.exceptions import PermissionDenied from django.shortcuts import resolve_url -def user_passes_test( - test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME -): +def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): """ Decorator for views that checks that the user passes the given test, redirecting to the log-in page if necessary. The test should be a callable @@ -27,22 +25,17 @@ def user_passes_test( # use the path as the "next" url. login_scheme, login_netloc = urlparse(resolved_login_url)[:2] current_scheme, current_netloc = urlparse(path)[:2] - if (not login_scheme or login_scheme == current_scheme) and ( - not login_netloc or login_netloc == current_netloc - ): + if ((not login_scheme or login_scheme == current_scheme) and + (not login_netloc or login_netloc == current_netloc)): path = request.get_full_path() from django.contrib.auth.views import redirect_to_login - - return redirect_to_login(path, resolved_login_url, redirect_field_name) - + return redirect_to_login( + path, resolved_login_url, redirect_field_name) return _wrapped_view - return decorator -def login_required( - function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None -): +def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. @@ -50,7 +43,7 @@ def login_required( actual_decorator = user_passes_test( lambda u: u.is_authenticated, login_url=login_url, - redirect_field_name=redirect_field_name, + redirect_field_name=redirect_field_name ) if function: return actual_decorator(function) @@ -64,7 +57,6 @@ def permission_required(perm, login_url=None, raise_exception=False): If the raise_exception parameter is given the PermissionDenied exception is raised. """ - def check_perms(user): if isinstance(perm, str): perms = (perm,) @@ -78,5 +70,4 @@ def permission_required(perm, login_url=None, raise_exception=False): raise PermissionDenied # As the last resort, show the login form return False - return user_passes_test(check_perms, login_url=login_url) diff --git a/venv/Lib/site-packages/django/contrib/auth/forms.py b/venv/Lib/site-packages/django/contrib/auth/forms.py index 9d6a991..20d8922 100644 --- a/venv/Lib/site-packages/django/contrib/auth/forms.py +++ b/venv/Lib/site-packages/django/contrib/auth/forms.py @@ -1,8 +1,12 @@ import unicodedata from django import forms -from django.contrib.auth import authenticate, get_user_model, password_validation -from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher +from django.contrib.auth import ( + authenticate, get_user_model, password_validation, +) +from django.contrib.auth.hashers import ( + UNUSABLE_PASSWORD_PREFIX, identify_hasher, +) from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site @@ -12,8 +16,7 @@ from django.template import loader from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.text import capfirst -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ UserModel = get_user_model() @@ -24,60 +27,48 @@ def _unicode_ci_compare(s1, s2): recommended algorithm from Unicode Technical Report 36, section 2.11.2(B)(2). """ - return ( - unicodedata.normalize("NFKC", s1).casefold() - == unicodedata.normalize("NFKC", s2).casefold() - ) + return unicodedata.normalize('NFKC', s1).casefold() == unicodedata.normalize('NFKC', s2).casefold() class ReadOnlyPasswordHashWidget(forms.Widget): - template_name = "auth/widgets/read_only_password_hash.html" + template_name = 'auth/widgets/read_only_password_hash.html' read_only = True def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) summary = [] if not value or value.startswith(UNUSABLE_PASSWORD_PREFIX): - summary.append({"label": gettext("No password set.")}) + summary.append({'label': gettext("No password set.")}) else: try: hasher = identify_hasher(value) except ValueError: - summary.append( - { - "label": gettext( - "Invalid password format or unknown hashing algorithm." - ) - } - ) + summary.append({'label': gettext("Invalid password format or unknown hashing algorithm.")}) else: for key, value_ in hasher.safe_summary(value).items(): - summary.append({"label": gettext(key), "value": value_}) - context["summary"] = summary + summary.append({'label': gettext(key), 'value': value_}) + context['summary'] = summary return context - def id_for_label(self, id_): - return None - class ReadOnlyPasswordHashField(forms.Field): widget = ReadOnlyPasswordHashWidget def __init__(self, *args, **kwargs): kwargs.setdefault("required", False) - kwargs.setdefault("disabled", True) + kwargs.setdefault('disabled', True) super().__init__(*args, **kwargs) class UsernameField(forms.CharField): def to_python(self, value): - return unicodedata.normalize("NFKC", super().to_python(value)) + return unicodedata.normalize('NFKC', super().to_python(value)) def widget_attrs(self, widget): return { **super().widget_attrs(widget), - "autocapitalize": "none", - "autocomplete": "username", + 'autocapitalize': 'none', + 'autocomplete': 'username', } @@ -86,19 +77,18 @@ class UserCreationForm(forms.ModelForm): A form that creates a user, with no privileges, from the given username and password. """ - error_messages = { - "password_mismatch": _("The two password fields didn’t match."), + 'password_mismatch': _('The two password fields didn’t match.'), } password1 = forms.CharField( label=_("Password"), strip=False, - widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}), help_text=password_validation.password_validators_help_text_html(), ) password2 = forms.CharField( label=_("Password confirmation"), - widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}), strip=False, help_text=_("Enter the same password as before, for verification."), ) @@ -106,22 +96,20 @@ class UserCreationForm(forms.ModelForm): class Meta: model = User fields = ("username",) - field_classes = {"username": UsernameField} + field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self._meta.model.USERNAME_FIELD in self.fields: - self.fields[self._meta.model.USERNAME_FIELD].widget.attrs[ - "autofocus" - ] = True + self.fields[self._meta.model.USERNAME_FIELD].widget.attrs['autofocus'] = True def clean_password2(self): password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise ValidationError( - self.error_messages["password_mismatch"], - code="password_mismatch", + self.error_messages['password_mismatch'], + code='password_mismatch', ) return password2 @@ -129,12 +117,12 @@ class UserCreationForm(forms.ModelForm): super()._post_clean() # Validate the password after self.instance is updated with form data # by super(). - password = self.cleaned_data.get("password2") + password = self.cleaned_data.get('password2') if password: try: password_validation.validate_password(password, self.instance) except ValidationError as error: - self.add_error("password2", error) + self.add_error('password2', error) def save(self, commit=True): user = super().save(commit=False) @@ -148,27 +136,25 @@ class UserChangeForm(forms.ModelForm): password = ReadOnlyPasswordHashField( label=_("Password"), help_text=_( - "Raw passwords are not stored, so there is no way to see this " - "user’s password, but you can change the password using " + 'Raw passwords are not stored, so there is no way to see this ' + 'user’s password, but you can change the password using ' '<a href="{}">this form</a>.' ), ) class Meta: model = User - fields = "__all__" - field_classes = {"username": UsernameField} + fields = '__all__' + field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - password = self.fields.get("password") + password = self.fields.get('password') if password: - password.help_text = password.help_text.format("../password/") - user_permissions = self.fields.get("user_permissions") + password.help_text = password.help_text.format('../password/') + user_permissions = self.fields.get('user_permissions') if user_permissions: - user_permissions.queryset = user_permissions.queryset.select_related( - "content_type" - ) + user_permissions.queryset = user_permissions.queryset.select_related('content_type') class AuthenticationForm(forms.Form): @@ -176,20 +162,19 @@ class AuthenticationForm(forms.Form): Base class for authenticating users. Extend this to get a form that accepts username/password logins. """ - - username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True})) + username = UsernameField(widget=forms.TextInput(attrs={'autofocus': True})) password = forms.CharField( label=_("Password"), strip=False, - widget=forms.PasswordInput(attrs={"autocomplete": "current-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'current-password'}), ) error_messages = { - "invalid_login": _( + 'invalid_login': _( "Please enter a correct %(username)s and password. Note that both " "fields may be case-sensitive." ), - "inactive": _("This account is inactive."), + 'inactive': _("This account is inactive."), } def __init__(self, request=None, *args, **kwargs): @@ -204,19 +189,17 @@ class AuthenticationForm(forms.Form): # Set the max length and label for the "username" field. self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD) username_max_length = self.username_field.max_length or 254 - self.fields["username"].max_length = username_max_length - self.fields["username"].widget.attrs["maxlength"] = username_max_length - if self.fields["username"].label is None: - self.fields["username"].label = capfirst(self.username_field.verbose_name) + self.fields['username'].max_length = username_max_length + self.fields['username'].widget.attrs['maxlength'] = username_max_length + if self.fields['username'].label is None: + self.fields['username'].label = capfirst(self.username_field.verbose_name) def clean(self): - username = self.cleaned_data.get("username") - password = self.cleaned_data.get("password") + username = self.cleaned_data.get('username') + password = self.cleaned_data.get('password') if username is not None and password: - self.user_cache = authenticate( - self.request, username=username, password=password - ) + self.user_cache = authenticate(self.request, username=username, password=password) if self.user_cache is None: raise self.get_invalid_login_error() else: @@ -237,8 +220,8 @@ class AuthenticationForm(forms.Form): """ if not user.is_active: raise ValidationError( - self.error_messages["inactive"], - code="inactive", + self.error_messages['inactive'], + code='inactive', ) def get_user(self): @@ -246,9 +229,9 @@ class AuthenticationForm(forms.Form): def get_invalid_login_error(self): return ValidationError( - self.error_messages["invalid_login"], - code="invalid_login", - params={"username": self.username_field.verbose_name}, + self.error_messages['invalid_login'], + code='invalid_login', + params={'username': self.username_field.verbose_name}, ) @@ -256,30 +239,23 @@ class PasswordResetForm(forms.Form): email = forms.EmailField( label=_("Email"), max_length=254, - widget=forms.EmailInput(attrs={"autocomplete": "email"}), + widget=forms.EmailInput(attrs={'autocomplete': 'email'}) ) - def send_mail( - self, - subject_template_name, - email_template_name, - context, - from_email, - to_email, - html_email_template_name=None, - ): + def send_mail(self, subject_template_name, email_template_name, + context, from_email, to_email, html_email_template_name=None): """ Send a django.core.mail.EmailMultiAlternatives to `to_email`. """ subject = loader.render_to_string(subject_template_name, context) # Email subject *must not* contain newlines - subject = "".join(subject.splitlines()) + subject = ''.join(subject.splitlines()) body = loader.render_to_string(email_template_name, context) email_message = EmailMultiAlternatives(subject, body, from_email, [to_email]) if html_email_template_name is not None: html_email = loader.render_to_string(html_email_template_name, context) - email_message.attach_alternative(html_email, "text/html") + email_message.attach_alternative(html_email, 'text/html') email_message.send() @@ -291,31 +267,22 @@ class PasswordResetForm(forms.Form): resetting their password. """ email_field_name = UserModel.get_email_field_name() - active_users = UserModel._default_manager.filter( - **{ - "%s__iexact" % email_field_name: email, - "is_active": True, - } - ) + active_users = UserModel._default_manager.filter(**{ + '%s__iexact' % email_field_name: email, + 'is_active': True, + }) return ( - u - for u in active_users - if u.has_usable_password() - and _unicode_ci_compare(email, getattr(u, email_field_name)) + u for u in active_users + if u.has_usable_password() and + _unicode_ci_compare(email, getattr(u, email_field_name)) ) - def save( - self, - domain_override=None, - subject_template_name="registration/password_reset_subject.txt", - email_template_name="registration/password_reset_email.html", - use_https=False, - token_generator=default_token_generator, - from_email=None, - request=None, - html_email_template_name=None, - extra_email_context=None, - ): + def save(self, domain_override=None, + subject_template_name='registration/password_reset_subject.txt', + email_template_name='registration/password_reset_email.html', + use_https=False, token_generator=default_token_generator, + from_email=None, request=None, html_email_template_name=None, + extra_email_context=None): """ Generate a one-use only link for resetting password and send it to the user. @@ -331,22 +298,18 @@ class PasswordResetForm(forms.Form): for user in self.get_users(email): user_email = getattr(user, email_field_name) context = { - "email": user_email, - "domain": domain, - "site_name": site_name, - "uid": urlsafe_base64_encode(force_bytes(user.pk)), - "user": user, - "token": token_generator.make_token(user), - "protocol": "https" if use_https else "http", + 'email': user_email, + 'domain': domain, + 'site_name': site_name, + 'uid': urlsafe_base64_encode(force_bytes(user.pk)), + 'user': user, + 'token': token_generator.make_token(user), + 'protocol': 'https' if use_https else 'http', **(extra_email_context or {}), } self.send_mail( - subject_template_name, - email_template_name, - context, - from_email, - user_email, - html_email_template_name=html_email_template_name, + subject_template_name, email_template_name, context, from_email, + user_email, html_email_template_name=html_email_template_name, ) @@ -355,20 +318,19 @@ class SetPasswordForm(forms.Form): A form that lets a user change set their password without entering the old password """ - error_messages = { - "password_mismatch": _("The two password fields didn’t match."), + 'password_mismatch': _('The two password fields didn’t match.'), } new_password1 = forms.CharField( label=_("New password"), - widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}), strip=False, help_text=password_validation.password_validators_help_text_html(), ) new_password2 = forms.CharField( label=_("New password confirmation"), strip=False, - widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}), ) def __init__(self, user, *args, **kwargs): @@ -376,13 +338,13 @@ class SetPasswordForm(forms.Form): super().__init__(*args, **kwargs) def clean_new_password2(self): - password1 = self.cleaned_data.get("new_password1") - password2 = self.cleaned_data.get("new_password2") + password1 = self.cleaned_data.get('new_password1') + password2 = self.cleaned_data.get('new_password2') if password1 and password2: if password1 != password2: raise ValidationError( - self.error_messages["password_mismatch"], - code="password_mismatch", + self.error_messages['password_mismatch'], + code='password_mismatch', ) password_validation.validate_password(password2, self.user) return password2 @@ -400,22 +362,17 @@ class PasswordChangeForm(SetPasswordForm): A form that lets a user change their password by entering their old password. """ - error_messages = { **SetPasswordForm.error_messages, - "password_incorrect": _( - "Your old password was entered incorrectly. Please enter it again." - ), + 'password_incorrect': _("Your old password was entered incorrectly. Please enter it again."), } old_password = forms.CharField( label=_("Old password"), strip=False, - widget=forms.PasswordInput( - attrs={"autocomplete": "current-password", "autofocus": True} - ), + widget=forms.PasswordInput(attrs={'autocomplete': 'current-password', 'autofocus': True}), ) - field_order = ["old_password", "new_password1", "new_password2"] + field_order = ['old_password', 'new_password1', 'new_password2'] def clean_old_password(self): """ @@ -424,8 +381,8 @@ class PasswordChangeForm(SetPasswordForm): old_password = self.cleaned_data["old_password"] if not self.user.check_password(old_password): raise ValidationError( - self.error_messages["password_incorrect"], - code="password_incorrect", + self.error_messages['password_incorrect'], + code='password_incorrect', ) return old_password @@ -434,22 +391,19 @@ class AdminPasswordChangeForm(forms.Form): """ A form used to change the password of a user in the admin interface. """ - error_messages = { - "password_mismatch": _("The two password fields didn’t match."), + 'password_mismatch': _('The two password fields didn’t match.'), } - required_css_class = "required" + required_css_class = 'required' password1 = forms.CharField( label=_("Password"), - widget=forms.PasswordInput( - attrs={"autocomplete": "new-password", "autofocus": True} - ), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password', 'autofocus': True}), strip=False, help_text=password_validation.password_validators_help_text_html(), ) password2 = forms.CharField( label=_("Password (again)"), - widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}), strip=False, help_text=_("Enter the same password as before, for verification."), ) @@ -459,12 +413,12 @@ class AdminPasswordChangeForm(forms.Form): super().__init__(*args, **kwargs) def clean_password2(self): - password1 = self.cleaned_data.get("password1") - password2 = self.cleaned_data.get("password2") + password1 = self.cleaned_data.get('password1') + password2 = self.cleaned_data.get('password2') if password1 and password2 and password1 != password2: raise ValidationError( - self.error_messages["password_mismatch"], - code="password_mismatch", + self.error_messages['password_mismatch'], + code='password_mismatch', ) password_validation.validate_password(password2, self.user) return password2 @@ -483,4 +437,4 @@ class AdminPasswordChangeForm(forms.Form): for name in self.fields: if name not in data: return [] - return ["password"] + return ['password'] diff --git a/venv/Lib/site-packages/django/contrib/auth/hashers.py b/venv/Lib/site-packages/django/contrib/auth/hashers.py index 832531e..86ae7f4 100644 --- a/venv/Lib/site-packages/django/contrib/auth/hashers.py +++ b/venv/Lib/site-packages/django/contrib/auth/hashers.py @@ -11,18 +11,13 @@ from django.core.exceptions import ImproperlyConfigured from django.core.signals import setting_changed from django.dispatch import receiver from django.utils.crypto import ( - RANDOM_STRING_CHARS, - constant_time_compare, - get_random_string, - pbkdf2, + RANDOM_STRING_CHARS, constant_time_compare, get_random_string, pbkdf2, ) from django.utils.module_loading import import_string from django.utils.translation import gettext_noop as _ -UNUSABLE_PASSWORD_PREFIX = "!" # This will never be a valid encoded hash -UNUSABLE_PASSWORD_SUFFIX_LENGTH = ( - 40 # number of random chars to add after UNUSABLE_PASSWORD_PREFIX -) +UNUSABLE_PASSWORD_PREFIX = '!' # This will never be a valid encoded hash +UNUSABLE_PASSWORD_SUFFIX_LENGTH = 40 # number of random chars to add after UNUSABLE_PASSWORD_PREFIX def is_password_usable(encoded): @@ -33,7 +28,7 @@ def is_password_usable(encoded): return encoded is None or not encoded.startswith(UNUSABLE_PASSWORD_PREFIX) -def check_password(password, encoded, setter=None, preferred="default"): +def check_password(password, encoded, setter=None, preferred='default'): """ Return a boolean of whether the raw password matches the three part encoded digest. @@ -67,7 +62,7 @@ def check_password(password, encoded, setter=None, preferred="default"): return is_correct -def make_password(password, salt=None, hasher="default"): +def make_password(password, salt=None, hasher='default'): """ Turn a plain-text password into a hash for database storage @@ -77,12 +72,11 @@ def make_password(password, salt=None, hasher="default"): access to staff or superuser accounts. See ticket #20079 for more info. """ if password is None: - return UNUSABLE_PASSWORD_PREFIX + get_random_string( - UNUSABLE_PASSWORD_SUFFIX_LENGTH - ) + return UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH) if not isinstance(password, (bytes, str)): raise TypeError( - "Password must be a string or bytes, got %s." % type(password).__qualname__ + 'Password must be a string or bytes, got %s.' + % type(password).__qualname__ ) hasher = get_hasher(hasher) salt = salt or hasher.salt() @@ -95,10 +89,9 @@ def get_hashers(): for hasher_path in settings.PASSWORD_HASHERS: hasher_cls = import_string(hasher_path) hasher = hasher_cls() - if not getattr(hasher, "algorithm"): - raise ImproperlyConfigured( - "hasher doesn't specify an algorithm name: %s" % hasher_path - ) + if not getattr(hasher, 'algorithm'): + raise ImproperlyConfigured("hasher doesn't specify an " + "algorithm name: %s" % hasher_path) hashers.append(hasher) return hashers @@ -110,22 +103,22 @@ def get_hashers_by_algorithm(): @receiver(setting_changed) def reset_hashers(**kwargs): - if kwargs["setting"] == "PASSWORD_HASHERS": + if kwargs['setting'] == 'PASSWORD_HASHERS': get_hashers.cache_clear() get_hashers_by_algorithm.cache_clear() -def get_hasher(algorithm="default"): +def get_hasher(algorithm='default'): """ Return an instance of a loaded password hasher. If algorithm is 'default', return the default hasher. Lazily import hashers specified in the project's settings file if needed. """ - if hasattr(algorithm, "algorithm"): + if hasattr(algorithm, 'algorithm'): return algorithm - elif algorithm == "default": + elif algorithm == 'default': return get_hashers()[0] else: @@ -133,11 +126,9 @@ def get_hasher(algorithm="default"): try: return hashers[algorithm] except KeyError: - raise ValueError( - "Unknown password hashing algorithm '%s'. " - "Did you specify it in the PASSWORD_HASHERS " - "setting?" % algorithm - ) + raise ValueError("Unknown password hashing algorithm '%s'. " + "Did you specify it in the PASSWORD_HASHERS " + "setting?" % algorithm) def identify_hasher(encoded): @@ -150,15 +141,14 @@ def identify_hasher(encoded): """ # Ancient versions of Django created plain MD5 passwords and accepted # MD5 passwords with an empty salt. - if (len(encoded) == 32 and "$" not in encoded) or ( - len(encoded) == 37 and encoded.startswith("md5$$") - ): - algorithm = "unsalted_md5" + if ((len(encoded) == 32 and '$' not in encoded) or + (len(encoded) == 37 and encoded.startswith('md5$$'))): + algorithm = 'unsalted_md5' # Ancient versions of Django accepted SHA1 passwords with an empty salt. - elif len(encoded) == 46 and encoded.startswith("sha1$$"): - algorithm = "unsalted_sha1" + elif len(encoded) == 46 and encoded.startswith('sha1$$'): + algorithm = 'unsalted_sha1' else: - algorithm = encoded.split("$", 1)[0] + algorithm = encoded.split('$', 1)[0] return get_hasher(algorithm) @@ -186,7 +176,6 @@ class BasePasswordHasher: PasswordHasher objects are immutable. """ - algorithm = None library = None salt_entropy = 128 @@ -200,14 +189,11 @@ class BasePasswordHasher: try: module = importlib.import_module(mod_path) except ImportError as e: - raise ValueError( - "Couldn't load %r algorithm library: %s" - % (self.__class__.__name__, e) - ) + raise ValueError("Couldn't load %r algorithm library: %s" % + (self.__class__.__name__, e)) return module - raise ValueError( - "Hasher %r doesn't specify a library attribute" % self.__class__.__name__ - ) + raise ValueError("Hasher %r doesn't specify a library attribute" % + self.__class__.__name__) def salt(self): """ @@ -221,15 +207,7 @@ class BasePasswordHasher: def verify(self, password, encoded): """Check if the given password is correct.""" - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a verify() method" - ) - - def _check_encode_args(self, password, salt): - if password is None: - raise TypeError("password must be provided.") - if not salt or "$" in salt: - raise ValueError("salt must be provided and cannot contain $.") + raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method') def encode(self, password, salt): """ @@ -238,9 +216,7 @@ class BasePasswordHasher: The result is normally formatted as "algorithm$salt$hash" and must be fewer than 128 characters. """ - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide an encode() method" - ) + raise NotImplementedError('subclasses of BasePasswordHasher must provide an encode() method') def decode(self, encoded): """ @@ -251,7 +227,7 @@ class BasePasswordHasher: `work_factor`. """ raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a decode() method." + 'subclasses of BasePasswordHasher must provide a decode() method.' ) def safe_summary(self, encoded): @@ -261,9 +237,7 @@ class BasePasswordHasher: The result is a dictionary and will be used where the password field must be displayed to construct a safe representation of the password. """ - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a safe_summary() method" - ) + raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method') def must_update(self, encoded): return False @@ -279,9 +253,7 @@ class BasePasswordHasher: for any hasher that has a work factor. If not, this method should be defined as a no-op to silence the warning. """ - warnings.warn( - "subclasses of BasePasswordHasher should provide a harden_runtime() method" - ) + warnings.warn('subclasses of BasePasswordHasher should provide a harden_runtime() method') class PBKDF2PasswordHasher(BasePasswordHasher): @@ -292,52 +264,52 @@ class PBKDF2PasswordHasher(BasePasswordHasher): The result is a 64 byte binary string. Iterations may be changed safely but you must rename the algorithm if you change SHA256. """ - algorithm = "pbkdf2_sha256" - iterations = 320000 + iterations = 260000 digest = hashlib.sha256 def encode(self, password, salt, iterations=None): - self._check_encode_args(password, salt) + assert password is not None + assert salt and '$' not in salt iterations = iterations or self.iterations hash = pbkdf2(password, salt, iterations, digest=self.digest) - hash = base64.b64encode(hash).decode("ascii").strip() + hash = base64.b64encode(hash).decode('ascii').strip() return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash) def decode(self, encoded): - algorithm, iterations, salt, hash = encoded.split("$", 3) + algorithm, iterations, salt, hash = encoded.split('$', 3) assert algorithm == self.algorithm return { - "algorithm": algorithm, - "hash": hash, - "iterations": int(iterations), - "salt": salt, + 'algorithm': algorithm, + 'hash': hash, + 'iterations': int(iterations), + 'salt': salt, } def verify(self, password, encoded): decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"], decoded["iterations"]) + encoded_2 = self.encode(password, decoded['salt'], decoded['iterations']) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("iterations"): decoded["iterations"], - _("salt"): mask_hash(decoded["salt"]), - _("hash"): mask_hash(decoded["hash"]), + _('algorithm'): decoded['algorithm'], + _('iterations'): decoded['iterations'], + _('salt'): mask_hash(decoded['salt']), + _('hash'): mask_hash(decoded['hash']), } def must_update(self, encoded): decoded = self.decode(encoded) - update_salt = must_update_salt(decoded["salt"], self.salt_entropy) - return (decoded["iterations"] != self.iterations) or update_salt + update_salt = must_update_salt(decoded['salt'], self.salt_entropy) + return (decoded['iterations'] != self.iterations) or update_salt def harden_runtime(self, password, encoded): decoded = self.decode(encoded) - extra_iterations = self.iterations - decoded["iterations"] + extra_iterations = self.iterations - decoded['iterations'] if extra_iterations > 0: - self.encode(password, decoded["salt"], extra_iterations) + self.encode(password, decoded['salt'], extra_iterations) class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher): @@ -347,7 +319,6 @@ class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher): implementations of PBKDF2, such as openssl's PKCS5_PBKDF2_HMAC_SHA1(). """ - algorithm = "pbkdf2_sha1" digest = hashlib.sha1 @@ -360,9 +331,8 @@ class Argon2PasswordHasher(BasePasswordHasher): (https://password-hashing.net). It requires the argon2-cffi library which depends on native C code and might cause portability issues. """ - - algorithm = "argon2" - library = "argon2" + algorithm = 'argon2' + library = 'argon2' time_cost = 2 memory_cost = 102400 @@ -380,59 +350,59 @@ class Argon2PasswordHasher(BasePasswordHasher): hash_len=params.hash_len, type=params.type, ) - return self.algorithm + data.decode("ascii") + return self.algorithm + data.decode('ascii') def decode(self, encoded): argon2 = self._load_library() - algorithm, rest = encoded.split("$", 1) + algorithm, rest = encoded.split('$', 1) assert algorithm == self.algorithm - params = argon2.extract_parameters("$" + rest) - variety, *_, b64salt, hash = rest.split("$") + params = argon2.extract_parameters('$' + rest) + variety, *_, b64salt, hash = rest.split('$') # Add padding. - b64salt += "=" * (-len(b64salt) % 4) - salt = base64.b64decode(b64salt).decode("latin1") + b64salt += '=' * (-len(b64salt) % 4) + salt = base64.b64decode(b64salt).decode('latin1') return { - "algorithm": algorithm, - "hash": hash, - "memory_cost": params.memory_cost, - "parallelism": params.parallelism, - "salt": salt, - "time_cost": params.time_cost, - "variety": variety, - "version": params.version, - "params": params, + 'algorithm': algorithm, + 'hash': hash, + 'memory_cost': params.memory_cost, + 'parallelism': params.parallelism, + 'salt': salt, + 'time_cost': params.time_cost, + 'variety': variety, + 'version': params.version, + 'params': params, } def verify(self, password, encoded): argon2 = self._load_library() - algorithm, rest = encoded.split("$", 1) + algorithm, rest = encoded.split('$', 1) assert algorithm == self.algorithm try: - return argon2.PasswordHasher().verify("$" + rest, password) + return argon2.PasswordHasher().verify('$' + rest, password) except argon2.exceptions.VerificationError: return False def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("variety"): decoded["variety"], - _("version"): decoded["version"], - _("memory cost"): decoded["memory_cost"], - _("time cost"): decoded["time_cost"], - _("parallelism"): decoded["parallelism"], - _("salt"): mask_hash(decoded["salt"]), - _("hash"): mask_hash(decoded["hash"]), + _('algorithm'): decoded['algorithm'], + _('variety'): decoded['variety'], + _('version'): decoded['version'], + _('memory cost'): decoded['memory_cost'], + _('time cost'): decoded['time_cost'], + _('parallelism'): decoded['parallelism'], + _('salt'): mask_hash(decoded['salt']), + _('hash'): mask_hash(decoded['hash']), } def must_update(self, encoded): decoded = self.decode(encoded) - current_params = decoded["params"] + current_params = decoded['params'] new_params = self.params() # Set salt_len to the salt_len of the current parameters because salt # is explicitly passed to argon2. new_params.salt_len = current_params.salt_len - update_salt = must_update_salt(decoded["salt"], self.salt_entropy) + update_salt = must_update_salt(decoded['salt'], self.salt_entropy) return (current_params != new_params) or update_salt def harden_runtime(self, password, encoded): @@ -463,7 +433,6 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher): this library depends on native C code and might cause portability issues. """ - algorithm = "bcrypt_sha256" digest = hashlib.sha256 library = ("bcrypt", "bcrypt") @@ -483,46 +452,46 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher): password = binascii.hexlify(self.digest(password).digest()) data = bcrypt.hashpw(password, salt) - return "%s$%s" % (self.algorithm, data.decode("ascii")) + return "%s$%s" % (self.algorithm, data.decode('ascii')) def decode(self, encoded): - algorithm, empty, algostr, work_factor, data = encoded.split("$", 4) + algorithm, empty, algostr, work_factor, data = encoded.split('$', 4) assert algorithm == self.algorithm return { - "algorithm": algorithm, - "algostr": algostr, - "checksum": data[22:], - "salt": data[:22], - "work_factor": int(work_factor), + 'algorithm': algorithm, + 'algostr': algostr, + 'checksum': data[22:], + 'salt': data[:22], + 'work_factor': int(work_factor), } def verify(self, password, encoded): - algorithm, data = encoded.split("$", 1) + algorithm, data = encoded.split('$', 1) assert algorithm == self.algorithm - encoded_2 = self.encode(password, data.encode("ascii")) + encoded_2 = self.encode(password, data.encode('ascii')) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("work factor"): decoded["work_factor"], - _("salt"): mask_hash(decoded["salt"]), - _("checksum"): mask_hash(decoded["checksum"]), + _('algorithm'): decoded['algorithm'], + _('work factor'): decoded['work_factor'], + _('salt'): mask_hash(decoded['salt']), + _('checksum'): mask_hash(decoded['checksum']), } def must_update(self, encoded): decoded = self.decode(encoded) - return decoded["work_factor"] != self.rounds + return decoded['work_factor'] != self.rounds def harden_runtime(self, password, encoded): - _, data = encoded.split("$", 1) + _, data = encoded.split('$', 1) salt = data[:29] # Length of the salt in bcrypt. - rounds = data.split("$")[2] + rounds = data.split('$')[2] # work factor is logarithmic, adding one doubles the load. - diff = 2 ** (self.rounds - int(rounds)) - 1 + diff = 2**(self.rounds - int(rounds)) - 1 while diff > 0: - self.encode(password, salt.encode("ascii")) + self.encode(password, salt.encode('ascii')) diff -= 1 @@ -539,126 +508,47 @@ class BCryptPasswordHasher(BCryptSHA256PasswordHasher): bcrypt's 72 bytes password truncation. Most use cases should prefer the BCryptSHA256PasswordHasher. """ - algorithm = "bcrypt" digest = None -class ScryptPasswordHasher(BasePasswordHasher): - """ - Secure password hashing using the Scrypt algorithm. - """ - - algorithm = "scrypt" - block_size = 8 - maxmem = 0 - parallelism = 1 - work_factor = 2**14 - - def encode(self, password, salt, n=None, r=None, p=None): - self._check_encode_args(password, salt) - n = n or self.work_factor - r = r or self.block_size - p = p or self.parallelism - hash_ = hashlib.scrypt( - password.encode(), - salt=salt.encode(), - n=n, - r=r, - p=p, - maxmem=self.maxmem, - dklen=64, - ) - hash_ = base64.b64encode(hash_).decode("ascii").strip() - return "%s$%d$%s$%d$%d$%s" % (self.algorithm, n, salt, r, p, hash_) - - def decode(self, encoded): - algorithm, work_factor, salt, block_size, parallelism, hash_ = encoded.split( - "$", 6 - ) - assert algorithm == self.algorithm - return { - "algorithm": algorithm, - "work_factor": int(work_factor), - "salt": salt, - "block_size": int(block_size), - "parallelism": int(parallelism), - "hash": hash_, - } - - def verify(self, password, encoded): - decoded = self.decode(encoded) - encoded_2 = self.encode( - password, - decoded["salt"], - decoded["work_factor"], - decoded["block_size"], - decoded["parallelism"], - ) - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - _("algorithm"): decoded["algorithm"], - _("work factor"): decoded["work_factor"], - _("block size"): decoded["block_size"], - _("parallelism"): decoded["parallelism"], - _("salt"): mask_hash(decoded["salt"]), - _("hash"): mask_hash(decoded["hash"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - return ( - decoded["work_factor"] != self.work_factor - or decoded["block_size"] != self.block_size - or decoded["parallelism"] != self.parallelism - ) - - def harden_runtime(self, password, encoded): - # The runtime for Scrypt is too complicated to implement a sensible - # hardening algorithm. - pass - - class SHA1PasswordHasher(BasePasswordHasher): """ The SHA1 password hashing algorithm (not recommended) """ - algorithm = "sha1" def encode(self, password, salt): - self._check_encode_args(password, salt) + assert password is not None + assert salt and '$' not in salt hash = hashlib.sha1((salt + password).encode()).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) + algorithm, salt, hash = encoded.split('$', 2) assert algorithm == self.algorithm return { - "algorithm": algorithm, - "hash": hash, - "salt": salt, + 'algorithm': algorithm, + 'hash': hash, + 'salt': salt, } def verify(self, password, encoded): decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"]) + encoded_2 = self.encode(password, decoded['salt']) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("salt"): mask_hash(decoded["salt"], show=2), - _("hash"): mask_hash(decoded["hash"]), + _('algorithm'): decoded['algorithm'], + _('salt'): mask_hash(decoded['salt'], show=2), + _('hash'): mask_hash(decoded['hash']), } def must_update(self, encoded): decoded = self.decode(encoded) - return must_update_salt(decoded["salt"], self.salt_entropy) + return must_update_salt(decoded['salt'], self.salt_entropy) def harden_runtime(self, password, encoded): pass @@ -668,39 +558,39 @@ class MD5PasswordHasher(BasePasswordHasher): """ The Salted MD5 password hashing algorithm (not recommended) """ - algorithm = "md5" def encode(self, password, salt): - self._check_encode_args(password, salt) + assert password is not None + assert salt and '$' not in salt hash = hashlib.md5((salt + password).encode()).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) + algorithm, salt, hash = encoded.split('$', 2) assert algorithm == self.algorithm return { - "algorithm": algorithm, - "hash": hash, - "salt": salt, + 'algorithm': algorithm, + 'hash': hash, + 'salt': salt, } def verify(self, password, encoded): decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"]) + encoded_2 = self.encode(password, decoded['salt']) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("salt"): mask_hash(decoded["salt"], show=2), - _("hash"): mask_hash(decoded["hash"]), + _('algorithm'): decoded['algorithm'], + _('salt'): mask_hash(decoded['salt'], show=2), + _('hash'): mask_hash(decoded['hash']), } def must_update(self, encoded): decoded = self.decode(encoded) - return must_update_salt(decoded["salt"], self.salt_entropy) + return must_update_salt(decoded['salt'], self.salt_entropy) def harden_runtime(self, password, encoded): pass @@ -715,35 +605,33 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher): hashes. Some older Django installs still have these values lingering around so we need to handle and upgrade them properly. """ - algorithm = "unsalted_sha1" def salt(self): - return "" + return '' def encode(self, password, salt): - if salt != "": - raise ValueError("salt must be empty.") + assert salt == '' hash = hashlib.sha1(password.encode()).hexdigest() - return "sha1$$%s" % hash + return 'sha1$$%s' % hash def decode(self, encoded): - assert encoded.startswith("sha1$$") + assert encoded.startswith('sha1$$') return { - "algorithm": self.algorithm, - "hash": encoded[6:], - "salt": None, + 'algorithm': self.algorithm, + 'hash': encoded[6:], + 'salt': None, } def verify(self, password, encoded): - encoded_2 = self.encode(password, "") + encoded_2 = self.encode(password, '') return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("hash"): mask_hash(decoded["hash"]), + _('algorithm'): decoded['algorithm'], + _('hash'): mask_hash(decoded['hash']), } def harden_runtime(self, password, encoded): @@ -761,35 +649,33 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher): these values lingering around so we need to handle and upgrade them properly. """ - algorithm = "unsalted_md5" def salt(self): - return "" + return '' def encode(self, password, salt): - if salt != "": - raise ValueError("salt must be empty.") + assert salt == '' return hashlib.md5(password.encode()).hexdigest() def decode(self, encoded): return { - "algorithm": self.algorithm, - "hash": encoded, - "salt": None, + 'algorithm': self.algorithm, + 'hash': encoded, + 'salt': None, } def verify(self, password, encoded): - if len(encoded) == 37 and encoded.startswith("md5$$"): + if len(encoded) == 37 and encoded.startswith('md5$$'): encoded = encoded[5:] - encoded_2 = self.encode(password, "") + encoded_2 = self.encode(password, '') return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("hash"): mask_hash(decoded["hash"], show=3), + _('algorithm'): decoded['algorithm'], + _('hash'): mask_hash(decoded['hash'], show=3), } def harden_runtime(self, password, encoded): @@ -802,7 +688,6 @@ class CryptPasswordHasher(BasePasswordHasher): The crypt module is not supported on all platforms. """ - algorithm = "crypt" library = "crypt" @@ -811,35 +696,33 @@ class CryptPasswordHasher(BasePasswordHasher): def encode(self, password, salt): crypt = self._load_library() - if len(salt) != 2: - raise ValueError("salt must be of length 2.") + assert len(salt) == 2 hash = crypt.crypt(password, salt) - if hash is None: # A platform like OpenBSD with a dummy crypt module. - raise TypeError("hash must be provided.") + assert hash is not None # A platform like OpenBSD with a dummy crypt module. # we don't need to store the salt, but Django used to do this - return "%s$%s$%s" % (self.algorithm, "", hash) + return '%s$%s$%s' % (self.algorithm, '', hash) def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) + algorithm, salt, hash = encoded.split('$', 2) assert algorithm == self.algorithm return { - "algorithm": algorithm, - "hash": hash, - "salt": salt, + 'algorithm': algorithm, + 'hash': hash, + 'salt': salt, } def verify(self, password, encoded): crypt = self._load_library() decoded = self.decode(encoded) - data = crypt.crypt(password, decoded["hash"]) - return constant_time_compare(decoded["hash"], data) + data = crypt.crypt(password, decoded['hash']) + return constant_time_compare(decoded['hash'], data) def safe_summary(self, encoded): decoded = self.decode(encoded) return { - _("algorithm"): decoded["algorithm"], - _("salt"): decoded["salt"], - _("hash"): mask_hash(decoded["hash"], show=3), + _('algorithm'): decoded['algorithm'], + _('salt'): decoded['salt'], + _('hash'): mask_hash(decoded['hash'], show=3), } def harden_runtime(self, password, encoded): diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.mo index 970d5e9..f4310c0 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.po index 49ae8c6..2b9853c 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ar/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ # # Translators: # Ahmad Khayyat <akhayyat@gmail.com>, 2013,2021 -# Bashar Al-Abdulhadi, 2015-2016,2021 +# Bashar Al-Abdulhadi, 2015-2016 # Bashar Al-Abdulhadi, 2014 # Eyad Toma <d.eyad.t@gmail.com>, 2013 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-15 21:38+0000\n" -"Last-Translator: Bashar Al-Abdulhadi\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2021-01-03 20:30+0000\n" +"Last-Translator: Ahmad Khayyat <akhayyat@gmail.com>\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -137,9 +137,6 @@ msgstr "عامل العمل" msgid "checksum" msgstr "تدقيق المجموع" -msgid "block size" -msgstr "مقاس الكتله (البلوك)" - msgid "name" msgstr "الاسم" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.mo index 0df2cc8..5fa528f 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.po index ef91333..8114d86 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/be/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # # Translators: # Viktar Palstsiuk <vipals@gmail.com>, 2015 -# znotdead <zhirafchik@gmail.com>, 2016-2017,2019,2021 +# znotdead <zhirafchik@gmail.com>, 2016-2017,2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 16:48+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-10-16 18:29+0000\n" "Last-Translator: znotdead <zhirafchik@gmail.com>\n" "Language-Team: Belarusian (http://www.transifex.com/django/django/language/" "be/)\n" @@ -137,9 +137,6 @@ msgstr "множнік працы" msgid "checksum" msgstr "кантрольная сума" -msgid "block size" -msgstr "памер блока" - msgid "name" msgstr "назва" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.mo index 1d682ad..1418c1f 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.po index 63b556e..8277d9d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/bg/LC_MESSAGES/django.po @@ -1,21 +1,20 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Boris Chervenkov <office@sentido.bg>, 2012 # Georgi Kostadinov <grgkostadinov@gmail.com>, 2012 # Jannis Leidel <jannis@leidel.info>, 2011 # Lyuboslav Petrov <petrov.lyuboslav@gmail.com>, 2014 -# Todor Lubenov <tlubenov@gmail.com>, 2015 +# Todor Lubenov <tgl.sysdev@gmail.com>, 2015 # Venelin Stoykov <vkstoykov@gmail.com>, 2015-2016 # vestimir <vestimir@gmail.com>, 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-14 10:14+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -54,12 +53,12 @@ msgid "last login" msgstr "последно вписване" msgid "No password set." -msgstr "Не е зададена парола." +msgstr "Не е запазена парола." msgid "Invalid password format or unknown hashing algorithm." msgstr "Невалиден формат за парола или неизвестен алгоритъм за хеширане." -msgid "The two password fields didn’t match." +msgid "The two password fields didn't match." msgstr "Двете полета за паролата не съвпадат. " msgid "Password" @@ -72,12 +71,9 @@ msgid "Enter the same password as before, for verification." msgstr "Въведете същата парола като преди, за да потвърдите." msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" -"Паролите не се съхраняват в чист вид, така че е невъзможно да видите " -"паролата на този потребител, но можете да промените паролата чрез <a href=" -"\"{}\">този формуляр</a>." #, python-format msgid "" @@ -85,13 +81,13 @@ msgid "" "be case-sensitive." msgstr "" "Моля, въведете правилните %(username)s и парола. Имайте предвид, че и двете " -"полета могат да бъдат с малки или главни букви." +"полета могат да бъдат малки или главни букви." msgid "This account is inactive." msgstr "Този профил е неактивен." msgid "Email" -msgstr "Имейл" +msgstr "Email" msgid "New password" msgstr "Нова парола" @@ -115,25 +111,25 @@ msgid "iterations" msgstr "повторения" msgid "salt" -msgstr "salt" +msgstr "'salt'" msgid "hash" msgstr "хеш" msgid "variety" -msgstr "разнообразие" +msgstr "" msgid "version" msgstr "версия" msgid "memory cost" -msgstr "разход памет" +msgstr "" msgid "time cost" -msgstr "разход време" +msgstr "" msgid "parallelism" -msgstr "паралелизъм" +msgstr "" msgid "work factor" msgstr "работен фактор" @@ -141,9 +137,6 @@ msgstr "работен фактор" msgid "checksum" msgstr "чексума" -msgid "block size" -msgstr "размер на блока" - msgid "name" msgstr "име" @@ -219,8 +212,8 @@ msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" -"Указва дали този потребител трябва да се третира като активен. Премахнете " -"тази отметката, вместо да изтривате профили." +"Указва дали този потребител трябва да се третира като активен. Премахнете на " +"избора на това, вместо да триете профила." msgid "date joined" msgstr "дата на регистриране" @@ -253,24 +246,24 @@ msgstr[1] "Вашата парола трябва да съдържа поне % msgid "The password is too similar to the %(verbose_name)s." msgstr "Паролата е много подобна на %(verbose_name)s." -msgid "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." msgstr "Вашата парола не може да прилича на останалата Ви лична информация." msgid "This password is too common." msgstr "Тази парола е често срещана." -msgid "Your password can’t be a commonly used password." +msgid "Your password can't be a commonly used password." msgstr "Вашата парола не може да бъде често срещана." msgid "This password is entirely numeric." msgstr "Тази парола е изцяло от цифри." -msgid "Your password can’t be entirely numeric." +msgid "Your password can't be entirely numeric." msgstr "Вашата парола не може да бъде само от цифри." #, python-format msgid "Password reset on %(site_name)s" -msgstr "Промяна на парола за %(site_name)s" +msgstr "Променена парола на %(site_name)s" msgid "" "Enter a valid username. This value may contain only English letters, " @@ -293,7 +286,7 @@ msgid "Password reset" msgstr "Забравена парола" msgid "Password reset sent" -msgstr "Нулиране на паролата е изпратено" +msgstr "Нулиране на паролата изпратено" msgid "Enter new password" msgstr "Въведете нова парола" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.mo index cb49f5c..1a6c32f 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.po index 672e234..7656fb3 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/bs/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Arza Grbic <arza.grbic@gmail.com>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-27 17:56+0000\n" -"Last-Translator: Arza Grbic <arza.grbic@gmail.com>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Bosnian (http://www.transifex.com/django/django/language/" "bs/)\n" "MIME-Version: 1.0\n" @@ -30,7 +29,7 @@ msgstr "Važni datumi" #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "%(name)s objekat sa primarnim ključem %(key)r ne postoji." +msgstr "" msgid "Password changed successfully." msgstr "Lozinka uspješno izmjenjena." @@ -49,14 +48,14 @@ msgid "last login" msgstr "posljednja prijava" msgid "No password set." -msgstr "Lozinka nije postavljena." +msgstr "" msgid "Invalid password format or unknown hashing algorithm." -msgstr "Neispravan format lozinke ili nepoznat hashing algoritam." - -msgid "The two password fields didn’t match." msgstr "" +msgid "The two password fields didn't match." +msgstr "Dva polja za lozinku se nisu poklopila." + msgid "Password" msgstr "Lozinka" @@ -67,7 +66,7 @@ msgid "Enter the same password as before, for verification." msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" @@ -131,9 +130,6 @@ msgstr "" msgid "checksum" msgstr "" -msgid "block size" -msgstr "" - msgid "name" msgstr "ime" @@ -242,19 +238,19 @@ msgstr[2] "" msgid "The password is too similar to the %(verbose_name)s." msgstr "" -msgid "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." msgstr "" msgid "This password is too common." msgstr "" -msgid "Your password can’t be a commonly used password." +msgid "Your password can't be a commonly used password." msgstr "" msgid "This password is entirely numeric." msgstr "" -msgid "Your password can’t be entirely numeric." +msgid "Your password can't be entirely numeric." msgstr "" #, python-format diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.mo index 4f72ce3..9618560 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.po index 65e03ec..3ce3a8f 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ca/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy <aaloy@apsl.net>, 2015,2017,2021 +# Antoni Aloy <aaloy@apsl.net>, 2015,2017 # Carles Barrobés <carles@barrobes.com>, 2011-2012,2014-2015 # Gil Obradors Via <gil.obradors@gmail.com>, 2019 # Gil Obradors Via <gil.obradors@gmail.com>, 2019-2020 @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-27 08:48+0000\n" -"Last-Translator: Antoni Aloy <aaloy@apsl.net>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2021-04-08 22:44+0000\n" +"Last-Translator: Marc Compte <marc@compte.cat>\n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" "MIME-Version: 1.0\n" @@ -142,9 +142,6 @@ msgstr "factor de treball" msgid "checksum" msgstr "suma de comprovació" -msgid "block size" -msgstr "tamany de bloc" - msgid "name" msgstr "nom" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.mo index 71403ae..fcbece0 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.po index 78b6fc4..ec7130d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/cs/LC_MESSAGES/django.po @@ -5,13 +5,13 @@ # Jannis Leidel <jannis@leidel.info>, 2011 # Tomáš Ehrlich <tomas.ehrlich@gmail.com>, 2015 # Vláďa Macek <macek@sandbox.cz>, 2013-2014 -# Vláďa Macek <macek@sandbox.cz>, 2015-2017,2019,2021-2022 +# Vláďa Macek <macek@sandbox.cz>, 2015-2017,2019,2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2022-01-04 18:49+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2021-03-18 23:35+0000\n" "Last-Translator: Vláďa Macek <macek@sandbox.cz>\n" "Language-Team: Czech (http://www.transifex.com/django/django/language/cs/)\n" "MIME-Version: 1.0\n" @@ -137,9 +137,6 @@ msgstr "faktor práce" msgid "checksum" msgstr "kontrolní součet" -msgid "block size" -msgstr "velikost bloku" - msgid "name" msgstr "název" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.mo index a83b012..cde1b5a 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.po index a705d21..1427052 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/da/LC_MESSAGES/django.po @@ -2,19 +2,18 @@ # # Translators: # Christian Joergensen <christian@gmta.info>, 2012 -# Erik Ramsgaard Wognsen <r4mses@gmail.com>, 2021 -# Erik Ramsgaard Wognsen <r4mses@gmail.com>, 2013-2017,2019 +# Erik Wognsen <r4mses@gmail.com>, 2013-2017,2019 # Jannis Leidel <jannis@leidel.info>, 2011 -# 85794379431c3e0f5c85c0e72a78d45b_658ddd9, 2013 +# Stevenn, 2013 # tiktuk <tiktuk@gmail.com>, 2018 # valberg <valberg@orn.li>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-23 20:27+0000\n" -"Last-Translator: Erik Ramsgaard Wognsen <r4mses@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-09-17 18:07+0000\n" +"Last-Translator: Erik Wognsen <r4mses@gmail.com>\n" "Language-Team: Danish (http://www.transifex.com/django/django/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -140,9 +139,6 @@ msgstr "work factor" msgid "checksum" msgstr "tjeksum" -msgid "block size" -msgstr "blokstørrelse" - msgid "name" msgstr "navn" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.mo index eca1f27..5d5f2f4 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.po index b167298..137df65 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/de/LC_MESSAGES/django.po @@ -3,7 +3,6 @@ # Translators: # André Hagenbruch, 2011 # Florian Apolloner <florian@apolloner.eu>, 2012 -# Florian Apolloner <florian@apolloner.eu>, 2021 # Jannis Vajen, 2013 # Jannis Leidel <jannis@leidel.info>, 2013-2017,2020 # Jannis Vajen, 2016 @@ -13,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-28 17:17+0000\n" -"Last-Translator: Raphael Michel <mail@raphaelmichel.de>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-17 22:44+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -140,9 +139,6 @@ msgstr "Arbeitsfaktor" msgid "checksum" msgstr "Prüfsumme" -msgid "block size" -msgstr "Blockgröße" - msgid "name" msgstr "Name" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo index da3e844..58435fd 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.po index e30b3b8..d57936f 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/dsb/LC_MESSAGES/django.po @@ -1,13 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Michael Wolf <milupo@sorbzilla.de>, 2016-2017,2020-2021 +# Michael Wolf <milupo@sorbzilla.de>, 2016-2017,2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-28 18:51+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-02-25 16:01+0000\n" "Last-Translator: Michael Wolf <milupo@sorbzilla.de>\n" "Language-Team: Lower Sorbian (http://www.transifex.com/django/django/" "language/dsb/)\n" @@ -136,9 +136,6 @@ msgstr "źěłowy faktor" msgid "checksum" msgstr "kontrolna suma" -msgid "block size" -msgstr "blokowa wjelikosć" - msgid "name" msgstr "mě" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.mo index 9d25fe6..18a8b2a 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.po index f74a71f..44e5c7c 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/el/LC_MESSAGES/django.po @@ -10,15 +10,14 @@ # Nick Mavrakis <mavrakis.n@gmail.com>, 2018 # Pãnoș <panos.laganakos@gmail.com>, 2014 # Pãnoș <panos.laganakos@gmail.com>, 2016 -# Serafeim Papastefanos <spapas@gmail.com>, 2021 # Yorgos Pagles <y.pagles@gmail.com>, 2011 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 09:22+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2018-09-22 09:59+0000\n" +"Last-Translator: Nick Mavrakis <mavrakis.n@gmail.com>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -61,8 +60,8 @@ msgstr "Δεν έχει τεθεί συνθηματικό." msgid "Invalid password format or unknown hashing algorithm." msgstr "Μη έγκυρη μορφή συνθηματικού ή άγνωστος αλγόριθμος hashing." -msgid "The two password fields didn’t match." -msgstr "Τα δύο πεδία κωδικών δεν ταιριάζουν." +msgid "The two password fields didn't match." +msgstr "Τα δύο πεδία συνθηματικού δεν ταιριάζουν." msgid "Password" msgstr "Συνθηματικό" @@ -74,7 +73,7 @@ msgid "Enter the same password as before, for verification." msgstr "Εισάγετε το ίδιο συνθηματικό όπως πρίν, για επιβεβαίωση." msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" "Οι ακατέργαστοι κωδικοί δεν αποθηκεύονται, οπότε δεν υπάρχει τρόπος να δείτε " @@ -143,9 +142,6 @@ msgstr "work factor" msgid "checksum" msgstr "checksum" -msgid "block size" -msgstr "" - msgid "name" msgstr "όνομα" @@ -261,21 +257,21 @@ msgstr[1] "" msgid "The password is too similar to the %(verbose_name)s." msgstr "Το συνθηματικό μοιάζει πολύ με το %(verbose_name)s." -msgid "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." msgstr "" -"Ο κωδικός σας δεν μπορεί να μοιάζει τόσο με τα άλλα προσωπικά σας στοιχεία." +"Το συνθηματικό σας δεν μπορεί να μοιάζει τόσο με άλλα προσωπικά σας στοιχεία." msgid "This password is too common." msgstr "Πολύ κοινό συνθηματικό." -msgid "Your password can’t be a commonly used password." -msgstr "Ο κωδικός σας δεν μπορεί να είναι τόσο συνηθισμένος." +msgid "Your password can't be a commonly used password." +msgstr "Το συνθηματικό δεν μπορεί να είναι τόσο συνηθισμένο." msgid "This password is entirely numeric." msgstr "Αυτό το συνθηματικό αποτελείται μόνο απο αριθμούς." -msgid "Your password can’t be entirely numeric." -msgstr "Ο κωδικός σας δε μπορεί να αποτελείται μόνον από αριθμούς." +msgid "Your password can't be entirely numeric." +msgstr "Το συνθηματικό σας δεν μπορεί να ειναι αποκλειστικά αριθμητικό." #, python-format msgid "Password reset on %(site_name)s" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/en/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/en/LC_MESSAGES/django.po index 2ad6a03..1522b33 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/en/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English <en@li.org>\n" @@ -40,7 +40,7 @@ msgstr "" msgid "Change password: %s" msgstr "" -#: contrib/auth/apps.py:16 +#: contrib/auth/apps.py:15 msgid "Authentication and Authorization" msgstr "" @@ -52,32 +52,32 @@ msgstr "" msgid "last login" msgstr "" -#: contrib/auth/forms.py:41 +#: contrib/auth/forms.py:31 msgid "No password set." msgstr "" -#: contrib/auth/forms.py:46 +#: contrib/auth/forms.py:36 msgid "Invalid password format or unknown hashing algorithm." msgstr "" -#: contrib/auth/forms.py:84 contrib/auth/forms.py:325 contrib/auth/forms.py:398 +#: contrib/auth/forms.py:78 contrib/auth/forms.py:316 contrib/auth/forms.py:389 msgid "The two password fields didn’t match." msgstr "" -#: contrib/auth/forms.py:87 contrib/auth/forms.py:140 contrib/auth/forms.py:170 -#: contrib/auth/forms.py:402 +#: contrib/auth/forms.py:81 contrib/auth/forms.py:134 contrib/auth/forms.py:170 +#: contrib/auth/forms.py:393 msgid "Password" msgstr "" -#: contrib/auth/forms.py:93 +#: contrib/auth/forms.py:87 msgid "Password confirmation" msgstr "" -#: contrib/auth/forms.py:96 contrib/auth/forms.py:411 +#: contrib/auth/forms.py:90 contrib/auth/forms.py:402 msgid "Enter the same password as before, for verification." msgstr "" -#: contrib/auth/forms.py:142 +#: contrib/auth/forms.py:136 msgid "" "Raw passwords are not stored, so there is no way to see this user’s " "password, but you can change the password using <a href=\"{}\">this form</a>." @@ -94,191 +94,185 @@ msgstr "" msgid "This account is inactive." msgstr "" -#: contrib/auth/forms.py:243 +#: contrib/auth/forms.py:241 msgid "Email" msgstr "" -#: contrib/auth/forms.py:328 +#: contrib/auth/forms.py:319 msgid "New password" msgstr "" -#: contrib/auth/forms.py:334 +#: contrib/auth/forms.py:325 msgid "New password confirmation" msgstr "" -#: contrib/auth/forms.py:370 +#: contrib/auth/forms.py:361 msgid "Your old password was entered incorrectly. Please enter it again." msgstr "" -#: contrib/auth/forms.py:373 +#: contrib/auth/forms.py:364 msgid "Old password" msgstr "" -#: contrib/auth/forms.py:408 +#: contrib/auth/forms.py:399 msgid "Password (again)" msgstr "" -#: contrib/auth/hashers.py:302 contrib/auth/hashers.py:393 -#: contrib/auth/hashers.py:482 contrib/auth/hashers.py:573 -#: contrib/auth/hashers.py:623 contrib/auth/hashers.py:664 -#: contrib/auth/hashers.py:712 contrib/auth/hashers.py:757 -#: contrib/auth/hashers.py:805 +#: contrib/auth/hashers.py:259 contrib/auth/hashers.py:333 +#: contrib/auth/hashers.py:429 contrib/auth/hashers.py:489 +#: contrib/auth/hashers.py:520 contrib/auth/hashers.py:556 +#: contrib/auth/hashers.py:592 contrib/auth/hashers.py:630 msgid "algorithm" msgstr "" -#: contrib/auth/hashers.py:303 +#: contrib/auth/hashers.py:260 msgid "iterations" msgstr "" -#: contrib/auth/hashers.py:304 contrib/auth/hashers.py:399 -#: contrib/auth/hashers.py:484 contrib/auth/hashers.py:577 -#: contrib/auth/hashers.py:624 contrib/auth/hashers.py:665 -#: contrib/auth/hashers.py:806 +#: contrib/auth/hashers.py:261 contrib/auth/hashers.py:339 +#: contrib/auth/hashers.py:431 contrib/auth/hashers.py:490 +#: contrib/auth/hashers.py:521 contrib/auth/hashers.py:631 msgid "salt" msgstr "" -#: contrib/auth/hashers.py:305 contrib/auth/hashers.py:400 -#: contrib/auth/hashers.py:578 contrib/auth/hashers.py:625 -#: contrib/auth/hashers.py:666 contrib/auth/hashers.py:713 -#: contrib/auth/hashers.py:758 contrib/auth/hashers.py:807 +#: contrib/auth/hashers.py:262 contrib/auth/hashers.py:340 +#: contrib/auth/hashers.py:491 contrib/auth/hashers.py:522 +#: contrib/auth/hashers.py:557 contrib/auth/hashers.py:593 +#: contrib/auth/hashers.py:632 msgid "hash" msgstr "" -#: contrib/auth/hashers.py:394 +#: contrib/auth/hashers.py:334 msgid "variety" msgstr "" -#: contrib/auth/hashers.py:395 +#: contrib/auth/hashers.py:335 msgid "version" msgstr "" -#: contrib/auth/hashers.py:396 +#: contrib/auth/hashers.py:336 msgid "memory cost" msgstr "" -#: contrib/auth/hashers.py:397 +#: contrib/auth/hashers.py:337 msgid "time cost" msgstr "" -#: contrib/auth/hashers.py:398 contrib/auth/hashers.py:576 +#: contrib/auth/hashers.py:338 msgid "parallelism" msgstr "" -#: contrib/auth/hashers.py:483 contrib/auth/hashers.py:574 +#: contrib/auth/hashers.py:430 msgid "work factor" msgstr "" -#: contrib/auth/hashers.py:485 +#: contrib/auth/hashers.py:432 msgid "checksum" msgstr "" -#: contrib/auth/hashers.py:575 -msgid "block size" -msgstr "" - -#: contrib/auth/models.py:58 contrib/auth/models.py:109 +#: contrib/auth/models.py:56 contrib/auth/models.py:108 msgid "name" msgstr "" -#: contrib/auth/models.py:62 +#: contrib/auth/models.py:60 msgid "content type" msgstr "" -#: contrib/auth/models.py:64 +#: contrib/auth/models.py:62 msgid "codename" msgstr "" -#: contrib/auth/models.py:69 +#: contrib/auth/models.py:67 msgid "permission" msgstr "" -#: contrib/auth/models.py:70 contrib/auth/models.py:112 +#: contrib/auth/models.py:68 contrib/auth/models.py:111 msgid "permissions" msgstr "" -#: contrib/auth/models.py:119 +#: contrib/auth/models.py:118 msgid "group" msgstr "" -#: contrib/auth/models.py:120 contrib/auth/models.py:247 +#: contrib/auth/models.py:119 contrib/auth/models.py:242 msgid "groups" msgstr "" -#: contrib/auth/models.py:238 +#: contrib/auth/models.py:233 msgid "superuser status" msgstr "" -#: contrib/auth/models.py:241 +#: contrib/auth/models.py:236 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" -#: contrib/auth/models.py:250 +#: contrib/auth/models.py:245 msgid "" "The groups this user belongs to. A user will get all permissions granted to " "each of their groups." msgstr "" -#: contrib/auth/models.py:258 +#: contrib/auth/models.py:253 msgid "user permissions" msgstr "" -#: contrib/auth/models.py:260 +#: contrib/auth/models.py:255 msgid "Specific permissions for this user." msgstr "" -#: contrib/auth/models.py:331 +#: contrib/auth/models.py:326 msgid "username" msgstr "" -#: contrib/auth/models.py:334 +#: contrib/auth/models.py:329 msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" -#: contrib/auth/models.py:337 +#: contrib/auth/models.py:332 msgid "A user with that username already exists." msgstr "" -#: contrib/auth/models.py:340 +#: contrib/auth/models.py:335 msgid "first name" msgstr "" -#: contrib/auth/models.py:341 +#: contrib/auth/models.py:336 msgid "last name" msgstr "" -#: contrib/auth/models.py:342 +#: contrib/auth/models.py:337 msgid "email address" msgstr "" -#: contrib/auth/models.py:344 +#: contrib/auth/models.py:339 msgid "staff status" msgstr "" -#: contrib/auth/models.py:346 +#: contrib/auth/models.py:341 msgid "Designates whether the user can log into this admin site." msgstr "" -#: contrib/auth/models.py:349 +#: contrib/auth/models.py:344 msgid "active" msgstr "" -#: contrib/auth/models.py:352 +#: contrib/auth/models.py:347 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" -#: contrib/auth/models.py:356 +#: contrib/auth/models.py:351 msgid "date joined" msgstr "" -#: contrib/auth/models.py:365 +#: contrib/auth/models.py:360 msgid "user" msgstr "" -#: contrib/auth/models.py:366 +#: contrib/auth/models.py:361 msgid "users" msgstr "" @@ -309,19 +303,19 @@ msgstr "" msgid "Your password can’t be too similar to your other personal information." msgstr "" -#: contrib/auth/password_validation.py:188 +#: contrib/auth/password_validation.py:183 msgid "This password is too common." msgstr "" -#: contrib/auth/password_validation.py:193 +#: contrib/auth/password_validation.py:188 msgid "Your password can’t be a commonly used password." msgstr "" -#: contrib/auth/password_validation.py:203 +#: contrib/auth/password_validation.py:198 msgid "This password is entirely numeric." msgstr "" -#: contrib/auth/password_validation.py:208 +#: contrib/auth/password_validation.py:203 msgid "Your password can’t be entirely numeric." msgstr "" @@ -342,34 +336,34 @@ msgid "" "@/./+/-/_ characters." msgstr "" -#: contrib/auth/views.py:164 +#: contrib/auth/views.py:160 msgid "Logged out" msgstr "" -#: contrib/auth/views.py:221 +#: contrib/auth/views.py:217 msgid "Password reset" msgstr "" -#: contrib/auth/views.py:248 +#: contrib/auth/views.py:244 msgid "Password reset sent" msgstr "" -#: contrib/auth/views.py:258 +#: contrib/auth/views.py:254 msgid "Enter new password" msgstr "" -#: contrib/auth/views.py:321 +#: contrib/auth/views.py:314 msgid "Password reset unsuccessful" msgstr "" -#: contrib/auth/views.py:329 +#: contrib/auth/views.py:322 msgid "Password reset complete" msgstr "" -#: contrib/auth/views.py:341 +#: contrib/auth/views.py:334 msgid "Password change" msgstr "" -#: contrib/auth/views.py:364 +#: contrib/auth/views.py:357 msgid "Password change successful" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo index bdd8328..cd7c3ad 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po index ba933b7..87df07f 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/en_AU/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield <tom@tomfifield.net>, 2014 -# Tom Fifield <tom@tomfifield.net>, 2021 +# Tom Fifield <tom@openstack.org>, 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 09:22+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: English (Australia) (http://www.transifex.com/django/django/" "language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -29,7 +28,7 @@ msgstr "Important dates" #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "%(name)s object with primary key %(key)r does not exist." +msgstr "" msgid "Password changed successfully." msgstr "Password changed successfully." @@ -39,7 +38,7 @@ msgid "Change password: %s" msgstr "Change password: %s" msgid "Authentication and Authorization" -msgstr "Authentication and Authorisation" +msgstr "" msgid "password" msgstr "password" @@ -53,8 +52,8 @@ msgstr "No password set." msgid "Invalid password format or unknown hashing algorithm." msgstr "Invalid password format or unknown hashing algorithm." -msgid "The two password fields didn’t match." -msgstr "The two password fields didn’t match." +msgid "The two password fields didn't match." +msgstr "The two password fields didn't match." msgid "Password" msgstr "Password" @@ -63,14 +62,12 @@ msgid "Password confirmation" msgstr "Password confirmation" msgid "Enter the same password as before, for verification." -msgstr "Enter the same password as before, for verification." +msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" -"Raw passwords are not stored, so there is no way to see this user’s " -"password, but you can change the password using <a href=\"{}\">this form</a>." #, python-format msgid "" @@ -114,19 +111,19 @@ msgid "hash" msgstr "hash" msgid "variety" -msgstr "variety" +msgstr "" msgid "version" -msgstr "version" +msgstr "" msgid "memory cost" -msgstr "memory cost" +msgstr "" msgid "time cost" -msgstr "time cost" +msgstr "" msgid "parallelism" -msgstr "parallelism" +msgstr "" msgid "work factor" msgstr "work factor" @@ -134,14 +131,11 @@ msgstr "work factor" msgid "checksum" msgstr "checksum" -msgid "block size" -msgstr "" - msgid "name" msgstr "name" msgid "content type" -msgstr "content type" +msgstr "" msgid "codename" msgstr "codename" @@ -172,20 +166,18 @@ msgid "" "The groups this user belongs to. A user will get all permissions granted to " "each of their groups." msgstr "" -"The groups this user belongs to. A user will get all permissions granted to " -"each of their groups." msgid "user permissions" msgstr "user permissions" msgid "Specific permissions for this user." -msgstr "Specific permissions for this user." +msgstr "" msgid "username" msgstr "username" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +msgstr "" msgid "A user with that username already exists." msgstr "A user with that username already exists." @@ -232,36 +224,32 @@ msgid_plural "" "This password is too short. It must contain at least %(min_length)d " "characters." msgstr[0] "" -"This password is too short. It must contain at least %(min_length)d " -"character." msgstr[1] "" -"This password is too short. It must contain at least %(min_length)d " -"characters." #, python-format msgid "Your password must contain at least %(min_length)d character." msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Your password must contain at least %(min_length)d character." -msgstr[1] "Your password must contain at least %(min_length)d characters." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "The password is too similar to the %(verbose_name)s." -msgstr "The password is too similar to the %(verbose_name)s." +msgstr "" -msgid "Your password can’t be too similar to your other personal information." -msgstr "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." +msgstr "" msgid "This password is too common." -msgstr "This password is too common." +msgstr "" -msgid "Your password can’t be a commonly used password." -msgstr "Your password can’t be a commonly used password." +msgid "Your password can't be a commonly used password." +msgstr "" msgid "This password is entirely numeric." -msgstr "This password is entirely numeric." +msgstr "" -msgid "Your password can’t be entirely numeric." -msgstr "Your password can’t be entirely numeric." +msgid "Your password can't be entirely numeric." +msgstr "" #, python-format msgid "Password reset on %(site_name)s" @@ -271,36 +259,32 @@ msgid "" "Enter a valid username. This value may contain only English letters, " "numbers, and @/./+/-/_ characters." msgstr "" -"Enter a valid username. This value may contain only English letters, " -"numbers, and @/./+/-/_ characters." msgid "" "Enter a valid username. This value may contain only letters, numbers, and " "@/./+/-/_ characters." msgstr "" -"Enter a valid username. This value may contain only letters, numbers, and " -"@/./+/-/_ characters." msgid "Logged out" msgstr "Logged out" msgid "Password reset" -msgstr "Password reset" +msgstr "" msgid "Password reset sent" -msgstr "Password reset sent" +msgstr "" msgid "Enter new password" -msgstr "Enter new password" +msgstr "" msgid "Password reset unsuccessful" -msgstr "Password reset unsuccessful" +msgstr "" msgid "Password reset complete" -msgstr "Password reset complete" +msgstr "" msgid "Password change" -msgstr "Password change" +msgstr "" msgid "Password change successful" -msgstr "Password change successful" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.mo index 4dc2c96..11a9f95 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.po index fbbc61e..b77ccab 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/es/LC_MESSAGES/django.po @@ -3,22 +3,22 @@ # Translators: # albertoalcolea <albertoalcolea@gmail.com>, 2014 # Antoni Aloy <aaloy@apsl.net>, 2012-2013,2015-2017 -# e4db27214f7e7544f2022c647b585925_bb0e321, 2015-2016 -# e4db27214f7e7544f2022c647b585925_bb0e321, 2020 +# Ernesto Avilés, 2015-2016 +# Ernesto Avilés, 2020 # Ernesto Rico Schmidt <e.rico.schmidt@gmail.com>, 2017 # guillem <serra.guillem@gmail.com>, 2012 # Igor Támara <igor@tamarapatino.org>, 2015 # Jannis Leidel <jannis@leidel.info>, 2011 # Josue Naaman Nistal Guerra <josuenistal@hotmail.com>, 2014 # Leonardo J. Caballero G. <leonardocaballero@gmail.com>, 2011 -# Uriel Medina <urimeba511@gmail.com>, 2020-2021 +# Uriel Medina <urimeba511@gmail.com>, 2020 # Veronicabh <vero.blazher@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-10 03:52+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-09-25 17:48+0000\n" "Last-Translator: Uriel Medina <urimeba511@gmail.com>\n" "Language-Team: Spanish (http://www.transifex.com/django/django/language/" "es/)\n" @@ -146,9 +146,6 @@ msgstr "factor trabajo" msgid "checksum" msgstr "suma de verificación" -msgid "block size" -msgstr "tamaño de bloque" - msgid "name" msgstr "nombre" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo index 7d4970a..d8e2dd3 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po index d250b42..b4f5dba 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/es_AR/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Ramiro Morales, 2013-2017,2019,2021 +# Ramiro Morales, 2013-2017,2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 13:26+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-10-01 10:21+0000\n" "Last-Translator: Ramiro Morales\n" "Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/" "language/es_AR/)\n" @@ -138,9 +138,6 @@ msgstr "work factor" msgid "checksum" msgstr "suma de verificación" -msgid "block size" -msgstr "tamaño de bloque" - msgid "name" msgstr "nombre" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.mo index 9fa962e..a5b173b 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.po index ef45334..5329a8d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/fa/LC_MESSAGES/django.po @@ -4,9 +4,8 @@ # Ahmad Hosseini <ahmadly.com@gmail.com>, 2020 # Ali Nikneshan <ali@nikneshan.com>, 2015 # Eric Hamiter <ehamiter@gmail.com>, 2013 -# Farshad Asadpour, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 -# cef32bddc4c7e18de7e89af20a3a57ef_18bb97f, 2015 +# Kaveh Karimi, 2015 # MJafar Mashhadi <raindigital2007@gmail.com>, 2018 # Pouya Abbassi, 2016 # Reza Mohammadi <reza@teeleh.ir>, 2013-2014 @@ -14,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-19 17:35+0000\n" -"Last-Translator: Farshad Asadpour\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-08-20 15:54+0000\n" +"Last-Translator: Ahmad Hosseini <ahmadly.com@gmail.com>\n" "Language-Team: Persian (http://www.transifex.com/django/django/language/" "fa/)\n" "MIME-Version: 1.0\n" @@ -141,9 +140,6 @@ msgstr "عامل کار" msgid "checksum" msgstr "جمع کنترلی" -msgid "block size" -msgstr "اندازه بلاک" - msgid "name" msgstr "نام" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.mo index 469ab20..fab9c7a 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.po index a13720b..26ac6c8 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/fi/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Aarni Koskela, 2015,2017-2018,2020-2021 +# Aarni Koskela, 2015,2017-2018,2020 # Antti Kaihola <antti.15+transifex@kaihola.fi>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 # Klaus Dahlén <klaus.dahlen@gmail.com>, 2012 @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 15:17+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-21 09:33+0000\n" "Last-Translator: Aarni Koskela\n" "Language-Team: Finnish (http://www.transifex.com/django/django/language/" "fi/)\n" @@ -137,9 +137,6 @@ msgstr "työmäärä" msgid "checksum" msgstr "tarkistussumma" -msgid "block size" -msgstr "lohkokoko" - msgid "name" msgstr "nimi" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.mo index 0d0abbd..6e066eb 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.po index 95f6e99..5dbe25b 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/fr/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Claude Paroz <claude@2xlibre.net>, 2013-2019,2021 +# Claude Paroz <claude@2xlibre.net>, 2013-2019 # Claude Paroz <claude@2xlibre.net>, 2013 # Jannis Leidel <jannis@leidel.info>, 2011 # mlorant <maxime.lorant@gmail.com>, 2014 @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-25 09:56+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-09-18 15:54+0000\n" "Last-Translator: Claude Paroz <claude@2xlibre.net>\n" "Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n" "MIME-Version: 1.0\n" @@ -138,9 +138,6 @@ msgstr "facteur travail" msgid "checksum" msgstr "somme de contrôle" -msgid "block size" -msgstr "taille de bloc" - msgid "name" msgstr "nom" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.mo index edc9d7b..2363d8a 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.po index 8883456..918d74d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/gd/LC_MESSAGES/django.po @@ -1,15 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# GunChleoc, 2015-2017,2021 +# GunChleoc, 2015-2017 # GunChleoc, 2015 # GunChleoc, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-27 12:55+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-12-13 12:36+0000\n" "Last-Translator: GunChleoc\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" @@ -141,9 +141,6 @@ msgstr "factar obrachaidh" msgid "checksum" msgstr "àireamh dhearbhaidh" -msgid "block size" -msgstr "meud nam blocaichean" - msgid "name" msgstr "ainm" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo index 921ff9c..a046ae8 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.po index 5c1a9fc..5b3372c 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/hsb/LC_MESSAGES/django.po @@ -1,13 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Michael Wolf <milupo@sorbzilla.de>, 2016-2017,2019,2021 +# Michael Wolf <milupo@sorbzilla.de>, 2016-2017,2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-28 18:17+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-09-21 19:21+0000\n" "Last-Translator: Michael Wolf <milupo@sorbzilla.de>\n" "Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" "language/hsb/)\n" @@ -134,9 +134,6 @@ msgstr "dźěłowy faktor" msgid "checksum" msgstr "pruwowanska suma" -msgid "block size" -msgstr "blokowa wulkosć" - msgid "name" msgstr "mjeno" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.mo index 4566c4b..0b139c1 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.po index 16eeda8..e404136 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ia/LC_MESSAGES/django.po @@ -1,14 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Martijn Dekker <mcdutchie@hotmail.com>, 2012,2021 +# Martijn Dekker <mcdutchie@hotmail.com>, 2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 09:22+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Interlingua (http://www.transifex.com/django/django/language/" "ia/)\n" "MIME-Version: 1.0\n" @@ -28,7 +28,7 @@ msgstr "Datas importante" #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "Le objecto %(name)s con le clave primari %(key)r non existe." +msgstr "" msgid "Password changed successfully." msgstr "Le cambio del contrasigno ha succedite." @@ -38,7 +38,7 @@ msgid "Change password: %s" msgstr "Cambia contrasigno: %s" msgid "Authentication and Authorization" -msgstr "Authentication e autorisation" +msgstr "" msgid "password" msgstr "contrasigno" @@ -47,13 +47,12 @@ msgid "last login" msgstr "ultime session" msgid "No password set." -msgstr "Nulle contrasigno definite." +msgstr "" msgid "Invalid password format or unknown hashing algorithm." msgstr "" -"Le formato del contrasigno es invalide o le algorithmo de hash es incognite." -msgid "The two password fields didn’t match." +msgid "The two password fields didn't match." msgstr "Le duo campos de contrasigno non es identic." msgid "Password" @@ -63,29 +62,24 @@ msgid "Password confirmation" msgstr "Confirma contrasigno" msgid "Enter the same password as before, for verification." -msgstr "Scribe le mesme contrasigno que antea, pro verification." +msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" -"Le contrasignos non es immagazinate in forma de texto simple, dunque il non " -"es possibile vider le contrasigno de iste usator, ma tu pote cambiar le " -"contrasigno con <a href=\"{}\">iste formulario</a>." #, python-format msgid "" "Please enter a correct %(username)s and password. Note that both fields may " "be case-sensitive." msgstr "" -"Per favor entra un %(username)s e contrasigno correcte. Nota que ambe campos " -"pote distinguer inter majusculas e minusculas." msgid "This account is inactive." msgstr "Iste conto es inactive." msgid "Email" -msgstr "E-mail" +msgstr "" msgid "New password" msgstr "Nove contrasigno" @@ -115,19 +109,19 @@ msgid "hash" msgstr "hash" msgid "variety" -msgstr "varietate" +msgstr "" msgid "version" -msgstr "version" +msgstr "" msgid "memory cost" -msgstr "costo de memoria" +msgstr "" msgid "time cost" -msgstr "costo de tempore" +msgstr "" msgid "parallelism" -msgstr "parallelismo" +msgstr "" msgid "work factor" msgstr "factor de labor" @@ -135,14 +129,11 @@ msgstr "factor de labor" msgid "checksum" msgstr "summa de controlo" -msgid "block size" -msgstr "" - msgid "name" msgstr "nomine" msgid "content type" -msgstr "typo de contento" +msgstr "" msgid "codename" msgstr "nomine de codice" @@ -173,21 +164,18 @@ msgid "" "The groups this user belongs to. A user will get all permissions granted to " "each of their groups." msgstr "" -"Le gruppos al quales iste usator pertine. Un usator recipe tote le " -"permissiones concedite a cata un de su gruppos." msgid "user permissions" msgstr "permissiones de usator" msgid "Specific permissions for this user." -msgstr "Permissiones specific pro iste usator." +msgstr "" msgid "username" msgstr "nomine de usator" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" -"Obligatori. 150 characteres o minus. Litteras, cifras e @/./+/-/_ solmente." msgid "A user with that username already exists." msgstr "Un usator con iste nomine de usator jam existe." @@ -199,7 +187,7 @@ msgid "last name" msgstr "nomine de familia" msgid "email address" -msgstr "adresse de e-mail" +msgstr "" msgid "staff status" msgstr "stato de personal" @@ -234,38 +222,32 @@ msgid_plural "" "This password is too short. It must contain at least %(min_length)d " "characters." msgstr[0] "" -"Le contrasigno es troppo curte. Debe continer al minus %(min_length)d " -"character." msgstr[1] "" -"Le contrasigno es troppo curte. Debe continer al minus %(min_length)d " -"characteres." #, python-format msgid "Your password must contain at least %(min_length)d character." msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Le contrasigno debe continer al minus %(min_length)d character." -msgstr[1] "Le contrasigno debe continer al minus %(min_length)d characteres." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "The password is too similar to the %(verbose_name)s." -msgstr "Le contrasigno es troppo simile al %(verbose_name)s." - -msgid "Your password can’t be too similar to your other personal information." msgstr "" -"Le contrasigno non pote esser troppo similar a tu altere informationes " -"personal." + +msgid "Your password can't be too similar to your other personal information." +msgstr "" msgid "This password is too common." -msgstr "Iste contrasigno es troppo commun." +msgstr "" -msgid "Your password can’t be a commonly used password." -msgstr "Le contrasigno non pote esser un contrasigno communmente usate." +msgid "Your password can't be a commonly used password." +msgstr "" msgid "This password is entirely numeric." -msgstr "Iste contrasigno es toto numeric." +msgstr "" -msgid "Your password can’t be entirely numeric." -msgstr "Le contrasigno non pote esser toto numeric." +msgid "Your password can't be entirely numeric." +msgstr "" #, python-format msgid "Password reset on %(site_name)s" @@ -275,36 +257,32 @@ msgid "" "Enter a valid username. This value may contain only English letters, " "numbers, and @/./+/-/_ characters." msgstr "" -"Entra un nomine de usator valide. Pote continer solmente litteras anglese, " -"numeros e le characteres @/./+/-/_." msgid "" "Enter a valid username. This value may contain only letters, numbers, and " "@/./+/-/_ characters." msgstr "" -"Entra un nomine de usator valide. Pote continer solmente litteras, numeros e " -"le characteres @/./+/-/_." msgid "Logged out" msgstr "Session claudite" msgid "Password reset" -msgstr "Reinitialisation del contrasigno" +msgstr "" msgid "Password reset sent" -msgstr "Reinitialisation del contrasigno inviate" +msgstr "" msgid "Enter new password" -msgstr "Scribe nove contrasigno" +msgstr "" msgid "Password reset unsuccessful" -msgstr "Reinitialisation de contrasigno fallite" +msgstr "" msgid "Password reset complete" -msgstr "Contrasigno reinitialisate con successo" +msgstr "" msgid "Password change" -msgstr "Cambio de contrasigno" +msgstr "" msgid "Password change successful" -msgstr "Contrasigno cambiate con successo" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.mo index 53cf4f2..b37f429 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.po index 8319aba..f42d5be 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/it/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Davide Targa <davide.targa@gmail.com>, 2021 # Federico Capoano <federico.capoano@teletu.it>, 2011 # Flavio Curella <flavio.curella@gmail.com>, 2013-2014 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -14,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-12 12:08+0000\n" -"Last-Translator: Davide Targa <davide.targa@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-09-24 08:45+0000\n" +"Last-Translator: palmux <palmux@gmail.com>\n" "Language-Team: Italian (http://www.transifex.com/django/django/language/" "it/)\n" "MIME-Version: 1.0\n" @@ -143,9 +142,6 @@ msgstr "work factor" msgid "checksum" msgstr "checksum" -msgid "block size" -msgstr "dimensione del blocco" - msgid "name" msgstr "nome" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.mo index 5782069..eef88ed 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.po index 85b2d42..0cc1c8b 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ja/LC_MESSAGES/django.po @@ -5,14 +5,14 @@ # arupakan125 <koh@arupaka.net>, 2020 # Masashi SHIBATA <contact@c-bata.link>, 2017 # Nikita K <hiyori.amatsuki@gmail.com>, 2019 -# Shinya Okano <tokibito@gmail.com>, 2013-2016,2021 +# Shinya Okano <tokibito@gmail.com>, 2013-2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-13 11:35+0000\n" -"Last-Translator: Shinya Okano <tokibito@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-20 06:25+0000\n" +"Last-Translator: arupakan125 <koh@arupaka.net>\n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" "MIME-Version: 1.0\n" @@ -138,9 +138,6 @@ msgstr "ワークファクター" msgid "checksum" msgstr "チェックサム" -msgid "block size" -msgstr "ブロックサイズ" - msgid "name" msgstr "名前" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/kn/LC_MESSAGES/django.po index e24561e..f1b7174 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/kn/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Personal info" msgstr "ವೈಯುಕ್ತಿಕ ಮಾಹಿತಿ" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.mo index 5f6dc95..83d34e5 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.po index 44dc210..85782cf 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ko/LC_MESSAGES/django.po @@ -5,7 +5,6 @@ # Churow Park <churow@naver.com>, 2020 # Jiyoon, Ha <cryptography@konkuk.ac.kr>, 2016 # DaHae Sung <sdh4513136@hanmail.net>, 2016 -# 코딩 영, 2021 # Geonho Kim / Leo Kim <gh.leokim@gmail.com>, 2019 # Ian Y. Choi <ianyrchoi@gmail.com>, 2015 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -20,9 +19,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-10 02:12+0000\n" -"Last-Translator: 코딩 영\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-05-07 08:17+0000\n" +"Last-Translator: Churow Park <churow@naver.com>\n" "Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -146,9 +145,6 @@ msgstr "워크 팩터" msgid "checksum" msgstr "체크섬" -msgid "block size" -msgstr "블록 크기" - msgid "name" msgstr "이름" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.mo index 7742edd..d74d3ba 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.po index 2c6a640..1eb0f8d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/ky/LC_MESSAGES/django.po @@ -1,14 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>, 2020-2021 +# Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>, 2020 # Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>, 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-27 14:11+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-11-23 00:49+0000\n" "Last-Translator: Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>\n" "Language-Team: Kyrgyz (http://www.transifex.com/django/django/language/ky/)\n" "MIME-Version: 1.0\n" @@ -133,9 +133,6 @@ msgstr "жумуш фактору" msgid "checksum" msgstr "текшерүү" -msgid "block size" -msgstr "блок өлчөмү" - msgid "name" msgstr "аты" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.mo index eb9c047..fdd7b3d 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.po index 93ac269..1fae289 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/lv/LC_MESSAGES/django.po @@ -4,14 +4,14 @@ # NullIsNot0 <nullisnot0@inbox.lv>, 2017 # NullIsNot0 <nullisnot0@inbox.lv>, 2017 # Jannis Leidel <jannis@leidel.info>, 2011 -# NullIsNot0 <nullisnot0@inbox.lv>, 2019,2021 +# NullIsNot0 <nullisnot0@inbox.lv>, 2019 # peterisb <pb@sungis.lv>, 2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-06 05:08+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-11-07 07:27+0000\n" "Last-Translator: NullIsNot0 <nullisnot0@inbox.lv>\n" "Language-Team: Latvian (http://www.transifex.com/django/django/language/" "lv/)\n" @@ -139,9 +139,6 @@ msgstr "darba faktors" msgid "checksum" msgstr "kontrolsumma" -msgid "block size" -msgstr "bloka izmērs" - msgid "name" msgstr "nosaukums" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index ff5fb70..0000000 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index b05dc59..0000000 --- a/venv/Lib/site-packages/django/contrib/auth/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,313 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-16 14:43+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Personal info" -msgstr "Info peribadi" - -msgid "Permissions" -msgstr "Kebenaran" - -msgid "Important dates" -msgstr "Tarikh-tarikh penting" - -#, python-format -msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "Objek %(name)s dengan kunci utama %(key)r tidak wujud." - -msgid "Password changed successfully." -msgstr "Kata laluan berjaya ditukar." - -#, python-format -msgid "Change password: %s" -msgstr "Tukar kata laluan: %s" - -msgid "Authentication and Authorization" -msgstr "Pengesahan dan Kebenaran" - -msgid "password" -msgstr "kata laluan" - -msgid "last login" -msgstr "log masuk terakhir" - -msgid "No password set." -msgstr "Kata laluan tidak ditetapkan." - -msgid "Invalid password format or unknown hashing algorithm." -msgstr "" -"Format kata laluan tidak sah atau algoritma hash yang tidak dapat dipastikan." - -msgid "The two password fields didn’t match." -msgstr "Medan kedua-dua kata laluan tidak sepadan." - -msgid "Password" -msgstr "Kata laluan" - -msgid "Password confirmation" -msgstr "Pengesahan kata laluan" - -msgid "Enter the same password as before, for verification." -msgstr "" -"Masukkan kata laluan yang sama seperti sebelumnya, bagi tujuan pengesahan." - -msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " -"password, but you can change the password using <a href=\"{}\">this form</a>." -msgstr "" -"Kata laluan mentah tidak disimpan, maka tiada cara untuk melihat kata laluan " -"pengguna, tetapi anda boleh menukar kata laluan menggunakan <a href=" -"\"{}\">borang ini</a>." - -#, python-format -msgid "" -"Please enter a correct %(username)s and password. Note that both fields may " -"be case-sensitive." -msgstr "" -"Sila masukkan %(username)s dan kata laluan yang betul. Ambil perhatian " -"bahawa kedua-dua medan berkemungkinan kes-sensitif. " - -msgid "This account is inactive." -msgstr "Akaun ini tidak aktif." - -msgid "Email" -msgstr "Emel" - -msgid "New password" -msgstr "Kata laluan baru" - -msgid "New password confirmation" -msgstr "Pengesahan kata laluan baru" - -msgid "Your old password was entered incorrectly. Please enter it again." -msgstr "" -"Kata laluan lama anda tidak dimasukkan dengan betul. Sila masukkan sekali " -"lagi." - -msgid "Old password" -msgstr "Kata laluan lama" - -msgid "Password (again)" -msgstr "Kata laluan (sekali lagi)" - -msgid "algorithm" -msgstr "algortima" - -msgid "iterations" -msgstr "lelaran" - -msgid "salt" -msgstr "garam" - -msgid "hash" -msgstr "hash" - -msgid "variety" -msgstr "kepelbagaian" - -msgid "version" -msgstr "versi" - -msgid "memory cost" -msgstr "kos memori" - -msgid "time cost" -msgstr "kos masa" - -msgid "parallelism" -msgstr "parallelisma" - -msgid "work factor" -msgstr "faktor kerja" - -msgid "checksum" -msgstr "checksum" - -msgid "block size" -msgstr "saiz blok" - -msgid "name" -msgstr "nama" - -msgid "content type" -msgstr "jenis kandungan" - -msgid "codename" -msgstr "nama kod" - -msgid "permission" -msgstr "kebenaran" - -msgid "permissions" -msgstr "kebenaran" - -msgid "group" -msgstr "kumpulan" - -msgid "groups" -msgstr "kumpulan-kumpulan" - -msgid "superuser status" -msgstr "status pengguna hebat" - -msgid "" -"Designates that this user has all permissions without explicitly assigning " -"them." -msgstr "" -"Menentukan bahawa pengguna ini mempunyai semua kebenaran tanpa memberikannya " -"secara eksplisit." - -msgid "" -"The groups this user belongs to. A user will get all permissions granted to " -"each of their groups." -msgstr "" -"Pengguna ini adalah ahli kepada kumpulan-kumpulan ini. Pengguna akan " -"mewarisi semua kebenaran yang diberikan kepada kumpulan-kumpulan ini." - -msgid "user permissions" -msgstr "kebenaran penguna" - -msgid "Specific permissions for this user." -msgstr "Kebenaran-kebenaran spesifik bagi pengguna ini." - -msgid "username" -msgstr "nama pengguna" - -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "" -"Diperlukan. 150 karakter atau kurang. Huruf, digit dan @/./+/-/_ sahaja." - -msgid "A user with that username already exists." -msgstr "Pengguna dengan nama pengguna ini sudah wujud." - -msgid "first name" -msgstr "nama pertama" - -msgid "last name" -msgstr "nama akhir" - -msgid "email address" -msgstr "alamat emel" - -msgid "staff status" -msgstr "status staf" - -msgid "Designates whether the user can log into this admin site." -msgstr "Menentukan samada pengguna ini boleh log masuk ke laman pentadbiran." - -msgid "active" -msgstr "aktif" - -msgid "" -"Designates whether this user should be treated as active. Unselect this " -"instead of deleting accounts." -msgstr "" -"Menentukan samada pengguna ini patut dilayan sebagai aktif. Padam pilihan " -"ini daripada menghapuskan terus akaun pengguna." - -msgid "date joined" -msgstr "tarikh serta" - -msgid "user" -msgstr "pengguna" - -msgid "users" -msgstr "pengguna-pengguna" - -#, python-format -msgid "" -"This password is too short. It must contain at least %(min_length)d " -"character." -msgid_plural "" -"This password is too short. It must contain at least %(min_length)d " -"characters." -msgstr[0] "" -"Kata laluan ini terlalu singkat. Ia harus mempunyai sekurang-kurangnya " -"%(min_length)d karakter." - -#, python-format -msgid "Your password must contain at least %(min_length)d character." -msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "" -"Kata laluan anda harus mempunyai sekurang-kurangnya %(min_length)d karakter." - -#, python-format -msgid "The password is too similar to the %(verbose_name)s." -msgstr "Kata laluan ini hampir sama dengan %(verbose_name)s." - -msgid "Your password can’t be too similar to your other personal information." -msgstr "" -"Kata laluan anda tidak boleh hampir sama dengan maklumat peribadi anda yang " -"lain." - -msgid "This password is too common." -msgstr "Kata laluan anda terlalu singkat." - -msgid "Your password can’t be a commonly used password." -msgstr "" -"Kata laluan anda tidak boleh sama dengan kata laluan yang terlalu biasa " -"digunakan." - -msgid "This password is entirely numeric." -msgstr "Aksara kata laluan ini kesemuanya terdiri daripada nombor." - -msgid "Your password can’t be entirely numeric." -msgstr "" -"Kata laluan anda tidak boleh terdiri daripada aksara nombor secara " -"sepenuhnya." - -#, python-format -msgid "Password reset on %(site_name)s" -msgstr "Penetapan semula kata laluan di %(site_name)s" - -msgid "" -"Enter a valid username. This value may contain only English letters, " -"numbers, and @/./+/-/_ characters." -msgstr "" -"Masukkan nama pengguna yang sah. Nilai ini hanya boleh mengandungi huruf " -"bahasa Inggeris, nombor, dan karakter-karakter @/./+/-/_ ." - -msgid "" -"Enter a valid username. This value may contain only letters, numbers, and " -"@/./+/-/_ characters." -msgstr "" -"Masukkan nama pengguna yang sah. Nilai boleh mengandungi huruf, mombor, dan " -"karakter-karakter @/./+/-/_ ." - -msgid "Logged out" -msgstr "Telah di log keluar" - -msgid "Password reset" -msgstr "Penetapan semula kata laluan" - -msgid "Password reset sent" -msgstr "Penetapan semula kata laluan telah dihantar" - -msgid "Enter new password" -msgstr "Masukkan kata laluan yang baru" - -msgid "Password reset unsuccessful" -msgstr "Penetapan semula kata laluan tidak berjaya " - -msgid "Password reset complete" -msgstr "Penetapan semula kata laluan telah lengkap" - -msgid "Password change" -msgstr "Penukaran kata laluan" - -msgid "Password change successful" -msgstr "Penukaran kata laluan berjaya dilakukan" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.mo index 33f7a50..9b7d78f 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.po index 3216de3..febc7ee 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/nn/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-12 08:38+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -29,7 +28,7 @@ msgstr "Viktige datoar" #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "%(name)s-objekt med primærnøkkelen %(key)r eksisterer ikkje." +msgstr "" msgid "Password changed successfully." msgstr "Passordet er endra." @@ -39,7 +38,7 @@ msgid "Change password: %s" msgstr "Endre passord: %s" msgid "Authentication and Authorization" -msgstr "Stadfesting og Autorisasjon" +msgstr "" msgid "password" msgstr "passord" @@ -48,12 +47,12 @@ msgid "last login" msgstr "siste innlogging" msgid "No password set." -msgstr "Passord ikkje sett." +msgstr "" msgid "Invalid password format or unknown hashing algorithm." -msgstr "Ugyldig passordformat eller ukjend hash-algoritme." +msgstr "" -msgid "The two password fields didn’t match." +msgid "The two password fields didn't match." msgstr "Dei to passordfelta er ikkje like." msgid "Password" @@ -63,29 +62,24 @@ msgid "Password confirmation" msgstr "Stadfesting av passord" msgid "Enter the same password as before, for verification." -msgstr "Skriv inn det samme passordet som før, for verifisering." +msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" -"Sjølve passordet vert ikkje lagra, så det finnast ingen måte å sjå denne " -"brukaren sitt passord, men du kan endra passordet med <a href=\"{}\">dette " -"skjemaet</a>." #, python-format msgid "" "Please enter a correct %(username)s and password. Note that both fields may " "be case-sensitive." msgstr "" -"Oppgje korrekt %(username)s og passord. Merk at det er skilnad på små og " -"store bokstavar." msgid "This account is inactive." msgstr "Denne kontoen er inaktiv." msgid "Email" -msgstr "E-post" +msgstr "" msgid "New password" msgstr "Nytt passord" @@ -103,46 +97,43 @@ msgid "Password (again)" msgstr "Passord (gjenta)" msgid "algorithm" -msgstr "algoritme" +msgstr "" msgid "iterations" -msgstr "iterasjonar" +msgstr "" msgid "salt" -msgstr "salt" +msgstr "" msgid "hash" -msgstr "hash" +msgstr "" msgid "variety" -msgstr "variasjon" +msgstr "" msgid "version" -msgstr "versjon" +msgstr "" msgid "memory cost" -msgstr "minnekostnad" +msgstr "" msgid "time cost" -msgstr "tidskostnad" +msgstr "" msgid "parallelism" -msgstr "parallellitet" +msgstr "" msgid "work factor" -msgstr "arbeidsfaktor" +msgstr "" msgid "checksum" -msgstr "kontrollsum" - -msgid "block size" -msgstr "blokkstorleik" +msgstr "" msgid "name" msgstr "namn" msgid "content type" -msgstr "innhaldstype" +msgstr "" msgid "codename" msgstr "kodenamn" @@ -171,19 +162,18 @@ msgid "" "The groups this user belongs to. A user will get all permissions granted to " "each of their groups." msgstr "" -"Gruppene brukaren tilhøyrer. Brukarar får løyva til gruppene dei er med i." msgid "user permissions" msgstr "Brukerløyve" msgid "Specific permissions for this user." -msgstr "Løyva til denne brukaren." +msgstr "" msgid "username" msgstr "brukarnamn" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Nødvendig. 150 teikn eller færre. Berre bokstavar, tall @/./+/-/_." +msgstr "" msgid "A user with that username already exists." msgstr "Det eksisterar allereie ein brukar med dette brukernamnet." @@ -195,7 +185,7 @@ msgid "last name" msgstr "etternamn" msgid "email address" -msgstr "e-postadresse" +msgstr "" msgid "staff status" msgstr "administrasjonsstatus" @@ -229,73 +219,67 @@ msgid_plural "" "This password is too short. It must contain at least %(min_length)d " "characters." msgstr[0] "" -"Dette passordet er for stutt. Det må innehalde minst %(min_length)d teikn." msgstr[1] "" -"Dette passordet er for stutt. Det må innehalde minst %(min_length)d teikn." #, python-format msgid "Your password must contain at least %(min_length)d character." msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Passordet ditt må innehalde minst %(min_length)d teikn." -msgstr[1] "Passordet ditt må innehalde minst %(min_length)d teikn." +msgstr[0] "" +msgstr[1] "" #, python-format msgid "The password is too similar to the %(verbose_name)s." -msgstr "Passordet er for likt %(verbose_name)s." +msgstr "" -msgid "Your password can’t be too similar to your other personal information." -msgstr "Passordet ditt kan ikkje vere for likt dine andre personopplysingar." +msgid "Your password can't be too similar to your other personal information." +msgstr "" msgid "This password is too common." -msgstr "Dette passordet er for vanleg." +msgstr "" -msgid "Your password can’t be a commonly used password." -msgstr "Passordet ditt kan ikkje vere eit ofte brukt passord." +msgid "Your password can't be a commonly used password." +msgstr "" msgid "This password is entirely numeric." -msgstr "Dette passordet innehalder berre tal." +msgstr "" -msgid "Your password can’t be entirely numeric." -msgstr "Passordet ditt kan ikkje innehalde berre tal." +msgid "Your password can't be entirely numeric." +msgstr "" #, python-format msgid "Password reset on %(site_name)s" -msgstr "Passordnullstilling på %(site_name)s" +msgstr "" msgid "" "Enter a valid username. This value may contain only English letters, " "numbers, and @/./+/-/_ characters." msgstr "" -"Oppgje eit gyldig brukarnamn. Denne verdien kan berre innehalde bokstavar, " -"tal, og @/./+/-/_ teikn." msgid "" "Enter a valid username. This value may contain only letters, numbers, and " "@/./+/-/_ characters." msgstr "" -"Oppgje eit gyldig brukarnamn. Denne verdien kan berre innehalde bokstavar, " -"tal, og @/./+/-/_ teikn." msgid "Logged out" msgstr "Logga ut" msgid "Password reset" -msgstr "Nullstill passord" +msgstr "" msgid "Password reset sent" -msgstr "Passordnullstilling utsendt" +msgstr "" msgid "Enter new password" -msgstr "Oppgje nytt passord" +msgstr "" msgid "Password reset unsuccessful" -msgstr "Passordet vart ikkje nullstilt" +msgstr "" msgid "Password reset complete" -msgstr "Passord nullstilt" +msgstr "" msgid "Password change" -msgstr "Endre passord" +msgstr "" msgid "Password change successful" -msgstr "Passord endra" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.mo index b737fa7..18f139f 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.po index 22af3e6..e291049 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/pl/LC_MESSAGES/django.po @@ -6,10 +6,10 @@ # Janusz Harkot <jh@trilab.pl>, 2015 # Karol <kfuks2@o2.pl>, 2012 # m_aciek <maciej.olko@gmail.com>, 2014 -# m_aciek <maciej.olko@gmail.com>, 2016-2017,2019,2021 +# m_aciek <maciej.olko@gmail.com>, 2016-2017,2019 # m_aciek <maciej.olko@gmail.com>, 2014-2015 # muszalski <m.muszalski@gmail.com>, 2016 -# c10516f0462e552b4c3672569f0745a7_cc5cca2 <841826256cd8f47d0e443806a8e56601_19204>, 2014 +# p <inactive+poczciwiec@transifex.com>, 2014 # Mattia Procopio <promat85@gmail.com>, 2014 # Roman Barczyński, 2012 # Tomasz Kajtoch <tomekkaj@tomekkaj.pl>, 2016 @@ -17,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 11:17+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-11-06 20:03+0000\n" "Last-Translator: m_aciek <maciej.olko@gmail.com>\n" "Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" "MIME-Version: 1.0\n" @@ -50,7 +50,7 @@ msgid "Change password: %s" msgstr "Zmień hasło: %s" msgid "Authentication and Authorization" -msgstr "Uwierzytelnienie i autoryzacja" +msgstr "Uwierzytelnianie i autoryzacja" msgid "password" msgstr "hasło" @@ -59,7 +59,7 @@ msgid "last login" msgstr "ostatnie logowanie" msgid "No password set." -msgstr "Nie ustawiono hasła." +msgstr "Hasło nie zostało ustawione." msgid "Invalid password format or unknown hashing algorithm." msgstr "" @@ -83,7 +83,7 @@ msgid "" "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" "Nie przechowujemy surowych haseł, więc nie da się zobaczyć hasła tego " -"użytkownika. Możesz jednak je zmienić używając <a href=\"{}\">tego " +"użytkownika. Możesz jednak zmienić to hasło używając <a href=\"{}\">tego " "formularza</a>." #, python-format @@ -98,7 +98,7 @@ msgid "This account is inactive." msgstr "To konto jest nieaktywne." msgid "Email" -msgstr "Adres e-mail" +msgstr "Adres email" msgid "New password" msgstr "Nowe hasło" @@ -148,9 +148,6 @@ msgstr "work factor" msgid "checksum" msgstr "suma kontrolna" -msgid "block size" -msgstr "rozmiar bloku" - msgid "name" msgstr "nazwa" @@ -196,7 +193,7 @@ msgid "Specific permissions for this user." msgstr "Szczególne uprawnienia dla tego użytkownika." msgid "username" -msgstr "nazwa użytkownika" +msgstr "użytkownik" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "Wymagana. 150 lub mniej znaków. Jedynie litery, cyfry i @/./+/-/_." @@ -211,7 +208,7 @@ msgid "last name" msgstr "nazwisko" msgid "email address" -msgstr "adres e-mail" +msgstr "adres email" msgid "staff status" msgstr "w zespole" @@ -301,7 +298,7 @@ msgstr "" "cyfry i znaki @/./+/-/_." msgid "Logged out" -msgstr "Wylogowany(-na)" +msgstr "Wylogowany" msgid "Password reset" msgstr "Zresetowanie hasła" @@ -313,10 +310,10 @@ msgid "Enter new password" msgstr "Wprowadź nowe haslo" msgid "Password reset unsuccessful" -msgstr "Resetowanie hasła nie powiodło się" +msgstr "Zresetowanie hasła nie powiodło się" msgid "Password reset complete" -msgstr "Resetowanie hasła zakończone" +msgstr "Zresetowanie hasła powiodło się" msgid "Password change" msgstr "Zmiana hasła" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.mo index 6c7d260..8c6d717 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.po index c9ccf13..b1f2ad9 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/sk/LC_MESSAGES/django.po @@ -2,18 +2,16 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# 18f25ad6fa9930fc67cb11aca9d16a27, 2012-2014 +# Juraj Bubniak <translations@jbub.eu>, 2012-2014 # Marian Andre <marian@andre.sk>, 2015,2017 # Martin Tóth <ezimir@gmail.com>, 2017-2018 -# Peter Kuma, 2021 -# Richard von Kellner, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-30 09:28+0000\n" -"Last-Translator: Richard von Kellner\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2018-05-03 06:50+0000\n" +"Last-Translator: Martin Tóth <ezimir@gmail.com>\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -57,7 +55,7 @@ msgstr "Žiadne heslo." msgid "Invalid password format or unknown hashing algorithm." msgstr "Neplatný formát hesla alebo neznámy hašovací algoritmus." -msgid "The two password fields didn’t match." +msgid "The two password fields didn't match." msgstr "Heslo a jeho potvrdenie sa nezhodujú." msgid "Password" @@ -70,7 +68,7 @@ msgid "Enter the same password as before, for verification." msgstr "Kvôli overeniu, znovu zadajte rovnaké heslo." msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" "Heslá v pôvodnom tvare nie sú ukladané, takže neexistuje spôsob zobraziť " @@ -139,9 +137,6 @@ msgstr "faktor práce" msgid "checksum" msgstr "kontrolný súčet" -msgid "block size" -msgstr "veľkosť bloku" - msgid "name" msgstr "meno" @@ -256,19 +251,19 @@ msgstr[3] "Vaše heslo musí obsahovať aspoň %(min_length)d znakov." msgid "The password is too similar to the %(verbose_name)s." msgstr "Heslo sa príliš podobá na %(verbose_name)s." -msgid "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." msgstr "Vaše heslo sa nesmie príliš podobať na ostatné osobné informácie." msgid "This password is too common." msgstr "Toto heslo je používané príliš často." -msgid "Your password can’t be a commonly used password." -msgstr "Vaše heslo nemôže byť jedno z často používaných hesiel." +msgid "Your password can't be a commonly used password." +msgstr "Vaše heslo nemôže byť jedno z často používaných." msgid "This password is entirely numeric." msgstr "Toto heslo pozostáva iba z číslic." -msgid "Your password can’t be entirely numeric." +msgid "Your password can't be entirely numeric." msgstr "Vaše heslo nemôže pozostávať iba z číslic." #, python-format diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.mo index d6d128c..ca3bf45 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.po index 827e4d8..a7a9e20 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/sr/LC_MESSAGES/django.po @@ -1,15 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Igor Jerosimić, 2020-2021 +# Igor Jerosimić, 2020 # Jannis Leidel <jannis@leidel.info>, 2011 # Janos Guljas <janos@resenje.org>, 2011-2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 19:36+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-21 20:38+0000\n" "Last-Translator: Igor Jerosimić\n" "Language-Team: Serbian (http://www.transifex.com/django/django/language/" "sr/)\n" @@ -137,9 +137,6 @@ msgstr "фактор сложености" msgid "checksum" msgstr "сума за проверу" -msgid "block size" -msgstr "величина блока" - msgid "name" msgstr "име" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo index 910b7e9..9608759 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po index d31a603..c6886fb 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/sr_Latn/LC_MESSAGES/django.po @@ -1,18 +1,17 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Igor Jerosimić, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 # Janos Guljas <janos@resenje.org>, 2011-2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 09:22+0000\n" -"Last-Translator: Transifex Bot <>\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Serbian (Latin) (http://www.transifex.com/django/django/" -"language/sr@latin/)\n" +"language/sr%40latin/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -50,13 +49,13 @@ msgid "last login" msgstr "poslednja prijava" msgid "No password set." -msgstr "Lozinka nije uneta." +msgstr "" msgid "Invalid password format or unknown hashing algorithm." msgstr "" -msgid "The two password fields didn’t match." -msgstr "" +msgid "The two password fields didn't match." +msgstr "Dva polja za lozinke se nisu poklopila." msgid "Password" msgstr "Lozinka" @@ -68,7 +67,7 @@ msgid "Enter the same password as before, for verification." msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" @@ -132,9 +131,6 @@ msgstr "faktor rada" msgid "checksum" msgstr "suma za proveru" -msgid "block size" -msgstr "" - msgid "name" msgstr "ime" @@ -243,19 +239,19 @@ msgstr[2] "" msgid "The password is too similar to the %(verbose_name)s." msgstr "" -msgid "Your password can’t be too similar to your other personal information." +msgid "Your password can't be too similar to your other personal information." msgstr "" msgid "This password is too common." msgstr "" -msgid "Your password can’t be a commonly used password." +msgid "Your password can't be a commonly used password." msgstr "" msgid "This password is entirely numeric." msgstr "" -msgid "Your password can’t be entirely numeric." +msgid "Your password can't be entirely numeric." msgstr "" #, python-format diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.mo index b867b44..79ac2cb 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.po index f884a18..9215f67 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/tr/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ # # Translators: # Ahmet Emre Aladağ <emre.aladag@isik.edu.tr>, 2013 -# BouRock, 2015-2017,2019-2021 +# BouRock, 2015-2017,2019-2020 # BouRock, 2014-2015 # Caner Başaran <basaran.caner@gmail.com>, 2013 # Cihad GÜNDOĞDU <cihadgundogdu@gmail.com>, 2014 @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-09-22 17:30+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-09-29 18:52+0000\n" "Last-Translator: BouRock\n" "Language-Team: Turkish (http://www.transifex.com/django/django/language/" "tr/)\n" @@ -143,9 +143,6 @@ msgstr "iş faktörü" msgid "checksum" msgstr "sağlama" -msgid "block size" -msgstr "blok boyutu" - msgid "name" msgstr "adı" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.mo index 4a1d8d7..c86d398 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.po index 38a88d7..36a17a1 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/uk/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ # Kirill Gagarski <gagarin.gtn@gmail.com>, 2015 # Max V. Stotsky <transifex@ms.pereslavl.ru>, 2014 # captain_m4l <qotsaman@gmail.com>, 2012 -# Mykola Zamkovoi <nickzam@gmail.com>, 2014,2021 +# Mykola Zamkovoi <nickzam@gmail.com>, 2014 # Alex Bolotov <oleksandr.bolotov@gmail.com>, 2013 # Vitaliy Kozlovskyi <ubombi@gmail.com>, 2015 # Zoriana Zaiats, 2016-2017 @@ -16,9 +16,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-10-23 17:41+0000\n" -"Last-Translator: Mykola Zamkovoi <nickzam@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2021-01-19 23:40+0000\n" +"Last-Translator: Illia Volochii <illia.volochii@gmail.com>\n" "Language-Team: Ukrainian (http://www.transifex.com/django/django/language/" "uk/)\n" "MIME-Version: 1.0\n" @@ -31,7 +31,7 @@ msgstr "" "(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" msgid "Personal info" -msgstr "Особиста інформація" +msgstr "Персональна інформація" msgid "Permissions" msgstr "Дозволи" @@ -148,9 +148,6 @@ msgstr "робочий фактор" msgid "checksum" msgstr "контрольна сума" -msgid "block size" -msgstr "розмір блоку" - msgid "name" msgstr "ім'я" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.mo index eb31978..3eb1a55 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.po index a6b540a..4790c7e 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/vi/LC_MESSAGES/django.po @@ -5,15 +5,14 @@ # Anh Phan <vietnamesel10n@gmail.com>, 2013 # Tran <hongdiepkien@gmail.com>, 2011 # Tran Van <vantxm@yahoo.co.uk>, 2012 -# tinnguyen121221, 2021 # xgenvn <xgenvn@gmail.com>, 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-12-23 17:49+0000\n" -"Last-Translator: tinnguyen121221\n" +"POT-Creation-Date: 2017-09-24 13:46+0200\n" +"PO-Revision-Date: 2017-09-24 14:24+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Vietnamese (http://www.transifex.com/django/django/language/" "vi/)\n" "MIME-Version: 1.0\n" @@ -33,7 +32,7 @@ msgstr "Những ngày quan trọng" #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr " Đối tượng %(name)s với khóa chính %(key)r không tồn tại." +msgstr "" msgid "Password changed successfully." msgstr "Mật khẩu thay đổi thành công" @@ -57,8 +56,8 @@ msgstr "Chưa đặt mật khẩu" msgid "Invalid password format or unknown hashing algorithm." msgstr "Định dạng của mật khẩu không đúng hoặc thuật toán hash chưa rõ ràng." -msgid "The two password fields didn’t match." -msgstr "Hai trường mật khẩu không giống nhau." +msgid "The two password fields didn't match." +msgstr "Hai trường mật khẩu không giống nhau" msgid "Password" msgstr "Mật khẩu" @@ -67,15 +66,12 @@ msgid "Password confirmation" msgstr "Xác nhận mật khẩu" msgid "Enter the same password as before, for verification." -msgstr "Nhập lại mật khẩu để xác nhận." +msgstr "" msgid "" -"Raw passwords are not stored, so there is no way to see this user’s " +"Raw passwords are not stored, so there is no way to see this user's " "password, but you can change the password using <a href=\"{}\">this form</a>." msgstr "" -"Mật khẩu không được lưu trữ, vì vậy không có cách nào để xem mật khẩu của " -"người dùng, nhưng bạn có thể dùng <a href=\"{}\">biểu mẫu</a> này để thay " -"đổi." #, python-format msgid "" @@ -119,19 +115,19 @@ msgid "hash" msgstr "băm" msgid "variety" -msgstr "variety" +msgstr "" msgid "version" -msgstr "version" +msgstr "" msgid "memory cost" -msgstr "memory cost" +msgstr "" msgid "time cost" -msgstr "time cost" +msgstr "" msgid "parallelism" -msgstr "parallelism" +msgstr "" msgid "work factor" msgstr "yếu tố công việc" @@ -139,14 +135,11 @@ msgstr "yếu tố công việc" msgid "checksum" msgstr "kiểm tra" -msgid "block size" -msgstr "block size" - msgid "name" msgstr "Tên" msgid "content type" -msgstr "kiểu nội dung" +msgstr "" msgid "codename" msgstr "tên mã" @@ -177,8 +170,6 @@ msgid "" "The groups this user belongs to. A user will get all permissions granted to " "each of their groups." msgstr "" -"Các nhóm người dùng này thuộc về. Người dùng sẽ nhận được tất cả các quyền " -"được cấp cho mỗi nhóm." msgid "user permissions" msgstr "quyền của người sử dụng" @@ -190,7 +181,7 @@ msgid "username" msgstr "Tên đăng nhập" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Yêu cầu. 150 ký tự hoặc ít hơn. Chỉ là chữ cái, chữ số và @/./+/-/_." +msgstr "" msgid "A user with that username already exists." msgstr "Tên đăng nhập đã được sử dụng" @@ -236,31 +227,31 @@ msgid "" msgid_plural "" "This password is too short. It must contain at least %(min_length)d " "characters." -msgstr[0] "Mật khẩu quá ngắn. Nó phải chứa ít nhất %(min_length)d ký tự." +msgstr[0] "" #, python-format msgid "Your password must contain at least %(min_length)d character." msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Mật khẩu của bạn phải chứa ít nhất %(min_length)d ký tự." +msgstr[0] "" #, python-format msgid "The password is too similar to the %(verbose_name)s." -msgstr "Mật khẩu quá giống với %(verbose_name)s." +msgstr "" -msgid "Your password can’t be too similar to your other personal information." -msgstr "Mật khẩu của bạn không được quá giống với thông tin cá nhân khác." +msgid "Your password can't be too similar to your other personal information." +msgstr "" msgid "This password is too common." -msgstr "Mật khẩu này quá phổ biến." +msgstr "" -msgid "Your password can’t be a commonly used password." -msgstr "Mật khẩu của bạn không được là mật khẩu được dùng phổ biến." +msgid "Your password can't be a commonly used password." +msgstr "" msgid "This password is entirely numeric." -msgstr "Mật khẩu này hoàn toàn là số." +msgstr "" -msgid "Your password can’t be entirely numeric." -msgstr "Mật khẩu của bạn không được hoàn toàn bằng số." +msgid "Your password can't be entirely numeric." +msgstr "" #, python-format msgid "Password reset on %(site_name)s" @@ -270,15 +261,11 @@ msgid "" "Enter a valid username. This value may contain only English letters, " "numbers, and @/./+/-/_ characters." msgstr "" -"Điền tên đăng nhập hợp lệ. Giá trị này chỉ có thể chứa các chữ cái tiếng " -"Anh, số, và các ký tự @/./+/-/_" msgid "" "Enter a valid username. This value may contain only letters, numbers, and " "@/./+/-/_ characters." msgstr "" -"Điền tên đăng nhập hợp lệ. Giá trị này chỉ có thể chứa các chữ cái, số, và " -"các ký tự @/./+/-/_" msgid "Logged out" msgstr "Đã thoát" @@ -287,7 +274,7 @@ msgid "Password reset" msgstr "Đặt lại mật khẩu" msgid "Password reset sent" -msgstr "Đã gửi hướng dẫn đặt lại mật khẩu" +msgstr "" msgid "Enter new password" msgstr "Nhập mật khẩu mới" diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo index 2b7e305..9e9ca69 100644 Binary files a/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po index 1467f58..f8d1897 100644 --- a/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/auth/locale/zh_Hans/LC_MESSAGES/django.po @@ -2,7 +2,6 @@ # # Translators: # Bo Li <bo.li@measureofquality.com>, 2020 -# lanbla <lanlinwen@buaa.edu.cn>, 2021 # David <huangtao0202@gmail.com>, 2019 # ausaki <www.ljm969087551@qq.com>, 2017 # jamin M <lxxmbyx@163.com>, 2019 @@ -11,7 +10,7 @@ # Lele Long <schemacs@gmail.com>, 2011,2015 # Liping Wang <lynn.config@gmail.com>, 2016-2017 # mozillazg <opensource.mozillazg@gmail.com>, 2016 -# Lemon Li <leeway1985@gmail.com>, 2012-2013 +# pylemon <leeway1985@gmail.com>, 2012-2013 # Wentao Han <wentao.han@gmail.com>, 2020 # hizyn <zhangyanan5552@gmail.com>, 2016 # ced773123cfad7b4e8b79ca80f736af9, 2011 @@ -20,9 +19,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-21 10:22+0200\n" -"PO-Revision-Date: 2021-11-22 03:12+0000\n" -"Last-Translator: lanbla <lanlinwen@buaa.edu.cn>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-12-09 02:32+0000\n" +"Last-Translator: Bo Li <bo.li@measureofquality.com>\n" "Language-Team: Chinese (China) (http://www.transifex.com/django/django/" "language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -145,9 +144,6 @@ msgstr "加密因子" msgid "checksum" msgstr "校验和" -msgid "block size" -msgstr "块大小" - msgid "name" msgstr "名称" diff --git a/venv/Lib/site-packages/django/contrib/auth/management/__init__.py b/venv/Lib/site-packages/django/contrib/auth/management/__init__.py index 0b5a982..1170cb0 100644 --- a/venv/Lib/site-packages/django/contrib/auth/management/__init__.py +++ b/venv/Lib/site-packages/django/contrib/auth/management/__init__.py @@ -25,43 +25,27 @@ def _get_builtin_permissions(opts): """ perms = [] for action in opts.default_permissions: - perms.append( - ( - get_permission_codename(action, opts), - "Can %s %s" % (action, opts.verbose_name_raw), - ) - ) + perms.append(( + get_permission_codename(action, opts), + 'Can %s %s' % (action, opts.verbose_name_raw) + )) return perms -def create_permissions( - app_config, - verbosity=2, - interactive=True, - using=DEFAULT_DB_ALIAS, - apps=global_apps, - **kwargs, -): +def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs): if not app_config.models_module: return # Ensure that contenttypes are created for this app. Needed if # 'django.contrib.auth' is in INSTALLED_APPS before # 'django.contrib.contenttypes'. - create_contenttypes( - app_config, - verbosity=verbosity, - interactive=interactive, - using=using, - apps=apps, - **kwargs, - ) + create_contenttypes(app_config, verbosity=verbosity, interactive=interactive, using=using, apps=apps, **kwargs) app_label = app_config.label try: app_config = apps.get_app_config(app_label) - ContentType = apps.get_model("contenttypes", "ContentType") - Permission = apps.get_model("auth", "Permission") + ContentType = apps.get_model('contenttypes', 'ContentType') + Permission = apps.get_model('auth', 'Permission') except LookupError: return @@ -76,9 +60,7 @@ def create_permissions( for klass in app_config.get_models(): # Force looking up the content types in the current database # before creating foreign keys to them. - ctype = ContentType.objects.db_manager(using).get_for_model( - klass, for_concrete_model=False - ) + ctype = ContentType.objects.db_manager(using).get_for_model(klass, for_concrete_model=False) ctypes.add(ctype) for perm in _get_all_permissions(klass._meta): @@ -87,13 +69,11 @@ def create_permissions( # Find all the Permissions that have a content_type for a model we're # looking for. We don't need to check for codenames since we already have # a list of the ones we're going to create. - all_perms = set( - Permission.objects.using(using) - .filter( - content_type__in=ctypes, - ) - .values_list("content_type", "codename") - ) + all_perms = set(Permission.objects.using(using).filter( + content_type__in=ctypes, + ).values_list( + "content_type", "codename" + )) perms = [ Permission(codename=codename, name=name, content_type=ct) @@ -117,7 +97,7 @@ def get_system_username(): # KeyError will be raised by os.getpwuid() (called by getuser()) # if there is no corresponding entry in the /etc/passwd file # (a very restricted chroot environment, for example). - return "" + return '' return result @@ -137,25 +117,23 @@ def get_default_username(check_db=True, database=DEFAULT_DB_ALIAS): # If the User model has been swapped out, we can't make any assumptions # about the default user name. if auth_app.User._meta.swapped: - return "" + return '' default_username = get_system_username() try: default_username = ( - unicodedata.normalize("NFKD", default_username) - .encode("ascii", "ignore") - .decode("ascii") - .replace(" ", "") - .lower() + unicodedata.normalize('NFKD', default_username) + .encode('ascii', 'ignore').decode('ascii') + .replace(' ', '').lower() ) except UnicodeDecodeError: - return "" + return '' # Run the username validator try: - auth_app.User._meta.get_field("username").run_validators(default_username) + auth_app.User._meta.get_field('username').run_validators(default_username) except exceptions.ValidationError: - return "" + return '' # Don't return the default username if it is already taken. if check_db and default_username: @@ -166,5 +144,5 @@ def get_default_username(check_db=True, database=DEFAULT_DB_ALIAS): except auth_app.User.DoesNotExist: pass else: - return "" + return '' return default_username diff --git a/venv/Lib/site-packages/django/contrib/auth/management/commands/changepassword.py b/venv/Lib/site-packages/django/contrib/auth/management/commands/changepassword.py index 669d8cf..b0c0a7f 100644 --- a/venv/Lib/site-packages/django/contrib/auth/management/commands/changepassword.py +++ b/venv/Lib/site-packages/django/contrib/auth/management/commands/changepassword.py @@ -22,29 +22,25 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "username", - nargs="?", - help=( - "Username to change password for; by default, it's the current " - "username." - ), + 'username', nargs='?', + help='Username to change password for; by default, it\'s the current username.', ) parser.add_argument( - "--database", + '--database', default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".', ) def handle(self, *args, **options): - if options["username"]: - username = options["username"] + if options['username']: + username = options['username'] else: username = getpass.getuser() try: - u = UserModel._default_manager.using(options["database"]).get( - **{UserModel.USERNAME_FIELD: username} - ) + u = UserModel._default_manager.using(options['database']).get(**{ + UserModel.USERNAME_FIELD: username + }) except UserModel.DoesNotExist: raise CommandError("user '%s' does not exist" % username) @@ -58,22 +54,20 @@ class Command(BaseCommand): p1 = self._get_pass() p2 = self._get_pass("Password (again): ") if p1 != p2: - self.stdout.write("Passwords do not match. Please try again.") + self.stdout.write('Passwords do not match. Please try again.') count += 1 # Don't validate passwords that don't match. continue try: validate_password(p2, u) except ValidationError as err: - self.stderr.write("\n".join(err.messages)) + self.stderr.write('\n'.join(err.messages)) count += 1 else: password_validated = True if count == MAX_TRIES: - raise CommandError( - "Aborting password change for user '%s' after %s attempts" % (u, count) - ) + raise CommandError("Aborting password change for user '%s' after %s attempts" % (u, count)) u.set_password(p1) u.save() diff --git a/venv/Lib/site-packages/django/contrib/auth/management/commands/createsuperuser.py b/venv/Lib/site-packages/django/contrib/auth/management/commands/createsuperuser.py index 5fffa55..3eb8abf 100644 --- a/venv/Lib/site-packages/django/contrib/auth/management/commands/createsuperuser.py +++ b/venv/Lib/site-packages/django/contrib/auth/management/commands/createsuperuser.py @@ -18,77 +18,69 @@ class NotRunningInTTYException(Exception): pass -PASSWORD_FIELD = "password" +PASSWORD_FIELD = 'password' class Command(BaseCommand): - help = "Used to create a superuser." + help = 'Used to create a superuser.' requires_migrations_checks = True - stealth_options = ("stdin",) + stealth_options = ('stdin',) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.UserModel = get_user_model() - self.username_field = self.UserModel._meta.get_field( - self.UserModel.USERNAME_FIELD - ) + self.username_field = self.UserModel._meta.get_field(self.UserModel.USERNAME_FIELD) def add_arguments(self, parser): parser.add_argument( - "--%s" % self.UserModel.USERNAME_FIELD, - help="Specifies the login for the superuser.", + '--%s' % self.UserModel.USERNAME_FIELD, + help='Specifies the login for the superuser.', ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", + '--noinput', '--no-input', action='store_false', dest='interactive', help=( - "Tells Django to NOT prompt the user for input of any kind. " - "You must use --%s with --noinput, along with an option for " - "any other required field. Superusers created with --noinput will " - "not be able to log in until they're given a valid password." - % self.UserModel.USERNAME_FIELD + 'Tells Django to NOT prompt the user for input of any kind. ' + 'You must use --%s with --noinput, along with an option for ' + 'any other required field. Superusers created with --noinput will ' + 'not be able to log in until they\'re given a valid password.' % + self.UserModel.USERNAME_FIELD ), ) parser.add_argument( - "--database", + '--database', default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".', ) for field_name in self.UserModel.REQUIRED_FIELDS: field = self.UserModel._meta.get_field(field_name) if field.many_to_many: - if ( - field.remote_field.through - and not field.remote_field.through._meta.auto_created - ): + if field.remote_field.through and not field.remote_field.through._meta.auto_created: raise CommandError( "Required field '%s' specifies a many-to-many " - "relation through model, which is not supported." % field_name + "relation through model, which is not supported." + % field_name ) else: parser.add_argument( - "--%s" % field_name, - action="append", + '--%s' % field_name, action='append', help=( - "Specifies the %s for the superuser. Can be used " - "multiple times." % field_name, + 'Specifies the %s for the superuser. Can be used ' + 'multiple times.' % field_name, ), ) else: parser.add_argument( - "--%s" % field_name, - help="Specifies the %s for the superuser." % field_name, + '--%s' % field_name, + help='Specifies the %s for the superuser.' % field_name, ) def execute(self, *args, **options): - self.stdin = options.get("stdin", sys.stdin) # Used for testing + self.stdin = options.get('stdin', sys.stdin) # Used for testing return super().execute(*args, **options) def handle(self, *args, **options): username = options[self.UserModel.USERNAME_FIELD] - database = options["database"] + database = options['database'] user_data = {} verbose_field_name = self.username_field.verbose_name try: @@ -99,36 +91,26 @@ class Command(BaseCommand): # If not provided, create the user with an unusable password. user_data[PASSWORD_FIELD] = None try: - if options["interactive"]: + if options['interactive']: # Same as user_data but without many to many fields and with # foreign keys as fake model instances instead of raw IDs. fake_user_data = {} - if hasattr(self.stdin, "isatty") and not self.stdin.isatty(): + if hasattr(self.stdin, 'isatty') and not self.stdin.isatty(): raise NotRunningInTTYException default_username = get_default_username(database=database) if username: - error_msg = self._validate_username( - username, verbose_field_name, database - ) + error_msg = self._validate_username(username, verbose_field_name, database) if error_msg: self.stderr.write(error_msg) username = None - elif username == "": - raise CommandError( - "%s cannot be blank." % capfirst(verbose_field_name) - ) + elif username == '': + raise CommandError('%s cannot be blank.' % capfirst(verbose_field_name)) # Prompt for username. while username is None: - message = self._get_input_message( - self.username_field, default_username - ) - username = self.get_input_data( - self.username_field, message, default_username - ) + message = self._get_input_message(self.username_field, default_username) + username = self.get_input_data(self.username_field, message, default_username) if username: - error_msg = self._validate_username( - username, verbose_field_name, database - ) + error_msg = self._validate_username(username, verbose_field_name, database) if error_msg: self.stderr.write(error_msg) username = None @@ -136,15 +118,12 @@ class Command(BaseCommand): user_data[self.UserModel.USERNAME_FIELD] = username fake_user_data[self.UserModel.USERNAME_FIELD] = ( self.username_field.remote_field.model(username) - if self.username_field.remote_field - else username + if self.username_field.remote_field else username ) # Prompt for required fields. for field_name in self.UserModel.REQUIRED_FIELDS: field = self.UserModel._meta.get_field(field_name) user_data[field_name] = options[field_name] - if user_data[field_name] is not None: - user_data[field_name] = field.clean(user_data[field_name], None) while user_data[field_name] is None: message = self._get_input_message(field) input_value = self.get_input_data(field, message) @@ -152,98 +131,74 @@ class Command(BaseCommand): if field.many_to_many and input_value: if not input_value.strip(): user_data[field_name] = None - self.stderr.write("Error: This field cannot be blank.") + self.stderr.write('Error: This field cannot be blank.') continue - user_data[field_name] = [ - pk.strip() for pk in input_value.split(",") - ] + user_data[field_name] = [pk.strip() for pk in input_value.split(',')] + if not field.many_to_many: + fake_user_data[field_name] = input_value - if not field.many_to_many: - fake_user_data[field_name] = user_data[field_name] - # Wrap any foreign keys in fake model instances. - if field.many_to_one: - fake_user_data[field_name] = field.remote_field.model( - user_data[field_name] - ) + # Wrap any foreign keys in fake model instances + if field.many_to_one: + fake_user_data[field_name] = field.remote_field.model(input_value) # Prompt for a password if the model has one. while PASSWORD_FIELD in user_data and user_data[PASSWORD_FIELD] is None: password = getpass.getpass() - password2 = getpass.getpass("Password (again): ") + password2 = getpass.getpass('Password (again): ') if password != password2: self.stderr.write("Error: Your passwords didn't match.") # Don't validate passwords that don't match. continue - if password.strip() == "": + if password.strip() == '': self.stderr.write("Error: Blank passwords aren't allowed.") # Don't validate blank passwords. continue try: validate_password(password2, self.UserModel(**fake_user_data)) except exceptions.ValidationError as err: - self.stderr.write("\n".join(err.messages)) - response = input( - "Bypass password validation and create user anyway? [y/N]: " - ) - if response.lower() != "y": + self.stderr.write('\n'.join(err.messages)) + response = input('Bypass password validation and create user anyway? [y/N]: ') + if response.lower() != 'y': continue user_data[PASSWORD_FIELD] = password else: # Non-interactive mode. # Use password from environment variable, if provided. - if ( - PASSWORD_FIELD in user_data - and "DJANGO_SUPERUSER_PASSWORD" in os.environ - ): - user_data[PASSWORD_FIELD] = os.environ["DJANGO_SUPERUSER_PASSWORD"] + if PASSWORD_FIELD in user_data and 'DJANGO_SUPERUSER_PASSWORD' in os.environ: + user_data[PASSWORD_FIELD] = os.environ['DJANGO_SUPERUSER_PASSWORD'] # Use username from environment variable, if not provided in # options. if username is None: - username = os.environ.get( - "DJANGO_SUPERUSER_" + self.UserModel.USERNAME_FIELD.upper() - ) + username = os.environ.get('DJANGO_SUPERUSER_' + self.UserModel.USERNAME_FIELD.upper()) if username is None: - raise CommandError( - "You must use --%s with --noinput." - % self.UserModel.USERNAME_FIELD - ) + raise CommandError('You must use --%s with --noinput.' % self.UserModel.USERNAME_FIELD) else: - error_msg = self._validate_username( - username, verbose_field_name, database - ) + error_msg = self._validate_username(username, verbose_field_name, database) if error_msg: raise CommandError(error_msg) user_data[self.UserModel.USERNAME_FIELD] = username for field_name in self.UserModel.REQUIRED_FIELDS: - env_var = "DJANGO_SUPERUSER_" + field_name.upper() + env_var = 'DJANGO_SUPERUSER_' + field_name.upper() value = options[field_name] or os.environ.get(env_var) if not value: - raise CommandError( - "You must use --%s with --noinput." % field_name - ) + raise CommandError('You must use --%s with --noinput.' % field_name) field = self.UserModel._meta.get_field(field_name) user_data[field_name] = field.clean(value, None) - if field.many_to_many and isinstance(user_data[field_name], str): - user_data[field_name] = [ - pk.strip() for pk in user_data[field_name].split(",") - ] - self.UserModel._default_manager.db_manager(database).create_superuser( - **user_data - ) - if options["verbosity"] >= 1: + self.UserModel._default_manager.db_manager(database).create_superuser(**user_data) + if options['verbosity'] >= 1: self.stdout.write("Superuser created successfully.") except KeyboardInterrupt: - self.stderr.write("\nOperation cancelled.") + self.stderr.write('\nOperation cancelled.') sys.exit(1) except exceptions.ValidationError as e: - raise CommandError("; ".join(e.messages)) + raise CommandError('; '.join(e.messages)) except NotRunningInTTYException: self.stdout.write( - "Superuser creation skipped due to not running in a TTY. " - "You can run `manage.py createsuperuser` in your project " - "to create one manually." + 'Superuser creation skipped due to not running in a TTY. ' + 'You can run `manage.py createsuperuser` in your project ' + 'to create one manually.' ) def get_input_data(self, field, message, default=None): @@ -252,45 +207,38 @@ class Command(BaseCommand): validation exceptions. """ raw_value = input(message) - if default and raw_value == "": + if default and raw_value == '': raw_value = default try: val = field.clean(raw_value, None) except exceptions.ValidationError as e: - self.stderr.write("Error: %s" % "; ".join(e.messages)) + self.stderr.write("Error: %s" % '; '.join(e.messages)) val = None return val def _get_input_message(self, field, default=None): - return "%s%s%s: " % ( + return '%s%s%s: ' % ( capfirst(field.verbose_name), - " (leave blank to use '%s')" % default if default else "", - " (%s.%s)" - % ( + " (leave blank to use '%s')" % default if default else '', + ' (%s.%s)' % ( field.remote_field.model._meta.object_name, - field.m2m_target_field_name() - if field.many_to_many - else field.remote_field.field_name, - ) - if field.remote_field - else "", + field.m2m_target_field_name() if field.many_to_many else field.remote_field.field_name, + ) if field.remote_field else '', ) def _validate_username(self, username, verbose_field_name, database): """Validate username. If invalid, return a string error message.""" if self.username_field.unique: try: - self.UserModel._default_manager.db_manager(database).get_by_natural_key( - username - ) + self.UserModel._default_manager.db_manager(database).get_by_natural_key(username) except self.UserModel.DoesNotExist: pass else: - return "Error: That %s is already taken." % verbose_field_name + return 'Error: That %s is already taken.' % verbose_field_name if not username: - return "%s cannot be blank." % capfirst(verbose_field_name) + return '%s cannot be blank.' % capfirst(verbose_field_name) try: self.username_field.clean(username, None) except exceptions.ValidationError as e: - return "; ".join(e.messages) + return '; '.join(e.messages) diff --git a/venv/Lib/site-packages/django/contrib/auth/middleware.py b/venv/Lib/site-packages/django/contrib/auth/middleware.py index dcc4821..5bd176e 100644 --- a/venv/Lib/site-packages/django/contrib/auth/middleware.py +++ b/venv/Lib/site-packages/django/contrib/auth/middleware.py @@ -7,27 +7,25 @@ from django.utils.functional import SimpleLazyObject def get_user(request): - if not hasattr(request, "_cached_user"): + if not hasattr(request, '_cached_user'): request._cached_user = auth.get_user(request) return request._cached_user class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): - if not hasattr(request, "session"): - raise ImproperlyConfigured( - "The Django authentication middleware requires session " - "middleware to be installed. Edit your MIDDLEWARE setting to " - "insert " - "'django.contrib.sessions.middleware.SessionMiddleware' before " - "'django.contrib.auth.middleware.AuthenticationMiddleware'." - ) + assert hasattr(request, 'session'), ( + "The Django authentication middleware requires session middleware " + "to be installed. Edit your MIDDLEWARE setting to insert " + "'django.contrib.sessions.middleware.SessionMiddleware' before " + "'django.contrib.auth.middleware.AuthenticationMiddleware'." + ) request.user = SimpleLazyObject(lambda: get_user(request)) class RemoteUserMiddleware(MiddlewareMixin): """ - Middleware for utilizing web-server-provided authentication. + Middleware for utilizing Web-server-provided authentication. If request.user is not authenticated, then this middleware attempts to authenticate the username passed in the ``REMOTE_USER`` request header. @@ -47,14 +45,13 @@ class RemoteUserMiddleware(MiddlewareMixin): def process_request(self, request): # AuthenticationMiddleware is required so that request.user exists. - if not hasattr(request, "user"): + if not hasattr(request, 'user'): raise ImproperlyConfigured( "The Django remote user auth middleware requires the" " authentication middleware to be installed. Edit your" " MIDDLEWARE setting to insert" " 'django.contrib.auth.middleware.AuthenticationMiddleware'" - " before the RemoteUserMiddleware class." - ) + " before the RemoteUserMiddleware class.") try: username = request.META[self.header] except KeyError: @@ -103,9 +100,7 @@ class RemoteUserMiddleware(MiddlewareMixin): but only if the user is authenticated via the RemoteUserBackend. """ try: - stored_backend = load_backend( - request.session.get(auth.BACKEND_SESSION_KEY, "") - ) + stored_backend = load_backend(request.session.get(auth.BACKEND_SESSION_KEY, '')) except ImportError: # backend failed to load auth.logout(request) @@ -116,7 +111,7 @@ class RemoteUserMiddleware(MiddlewareMixin): class PersistentRemoteUserMiddleware(RemoteUserMiddleware): """ - Middleware for web-server provided authentication on logon pages. + Middleware for Web-server provided authentication on logon pages. Like RemoteUserMiddleware but keeps the user authenticated even if the header (``REMOTE_USER``) is not found in the request. Useful @@ -124,5 +119,4 @@ class PersistentRemoteUserMiddleware(RemoteUserMiddleware): is only expected to happen on some "logon" URL and the rest of the application wants to use Django's authentication mechanism. """ - force_logout_if_no_header = False diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0001_initial.py index 3d1635f..4fb11d0 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0001_initial.py @@ -7,199 +7,98 @@ from django.utils import timezone class Migration(migrations.Migration): dependencies = [ - ("contenttypes", "__first__"), + ('contenttypes', '__first__'), ] operations = [ migrations.CreateModel( - name="Permission", + name='Permission', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ("name", models.CharField(max_length=50, verbose_name="name")), - ( - "content_type", - models.ForeignKey( - to="contenttypes.ContentType", - on_delete=models.CASCADE, - verbose_name="content type", - ), - ), - ("codename", models.CharField(max_length=100, verbose_name="codename")), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(max_length=50, verbose_name='name')), + ('content_type', models.ForeignKey( + to='contenttypes.ContentType', + on_delete=models.CASCADE, + to_field='id', + verbose_name='content type', + )), + ('codename', models.CharField(max_length=100, verbose_name='codename')), ], options={ - "ordering": [ - "content_type__app_label", - "content_type__model", - "codename", - ], - "unique_together": {("content_type", "codename")}, - "verbose_name": "permission", - "verbose_name_plural": "permissions", + 'ordering': ['content_type__app_label', 'content_type__model', 'codename'], + 'unique_together': {('content_type', 'codename')}, + 'verbose_name': 'permission', + 'verbose_name_plural': 'permissions', }, managers=[ - ("objects", django.contrib.auth.models.PermissionManager()), + ('objects', django.contrib.auth.models.PermissionManager()), ], ), migrations.CreateModel( - name="Group", + name='Group', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ( - "name", - models.CharField(unique=True, max_length=80, verbose_name="name"), - ), - ( - "permissions", - models.ManyToManyField( - to="auth.Permission", verbose_name="permissions", blank=True - ), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(unique=True, max_length=80, verbose_name='name')), + ('permissions', models.ManyToManyField(to='auth.Permission', verbose_name='permissions', blank=True)), ], options={ - "verbose_name": "group", - "verbose_name_plural": "groups", + 'verbose_name': 'group', + 'verbose_name_plural': 'groups', }, managers=[ - ("objects", django.contrib.auth.models.GroupManager()), + ('objects', django.contrib.auth.models.GroupManager()), ], ), migrations.CreateModel( - name="User", + name='User', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ("password", models.CharField(max_length=128, verbose_name="password")), - ( - "last_login", - models.DateTimeField( - default=timezone.now, verbose_name="last login" - ), - ), - ( - "is_superuser", - models.BooleanField( - default=False, - help_text=( - "Designates that this user has all permissions without " - "explicitly assigning them." - ), - verbose_name="superuser status", - ), - ), - ( - "username", - models.CharField( - help_text=( - "Required. 30 characters or fewer. Letters, digits and " - "@/./+/-/_ only." - ), - unique=True, - max_length=30, - verbose_name="username", - validators=[validators.UnicodeUsernameValidator()], - ), - ), - ( - "first_name", - models.CharField( - max_length=30, verbose_name="first name", blank=True - ), - ), - ( - "last_name", - models.CharField( - max_length=30, verbose_name="last name", blank=True - ), - ), - ( - "email", - models.EmailField( - max_length=75, verbose_name="email address", blank=True - ), - ), - ( - "is_staff", - models.BooleanField( - default=False, - help_text=( - "Designates whether the user can log into this admin site." - ), - verbose_name="staff status", - ), - ), - ( - "is_active", - models.BooleanField( - default=True, - verbose_name="active", - help_text=( - "Designates whether this user should be treated as active. " - "Unselect this instead of deleting accounts." - ), - ), - ), - ( - "date_joined", - models.DateTimeField( - default=timezone.now, verbose_name="date joined" - ), - ), - ( - "groups", - models.ManyToManyField( - to="auth.Group", - verbose_name="groups", - blank=True, - related_name="user_set", - related_query_name="user", - help_text=( - "The groups this user belongs to. A user will get all " - "permissions granted to each of their groups." - ), - ), - ), - ( - "user_permissions", - models.ManyToManyField( - to="auth.Permission", - verbose_name="user permissions", - blank=True, - help_text="Specific permissions for this user.", - related_name="user_set", - related_query_name="user", - ), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(default=timezone.now, verbose_name='last login')), + ('is_superuser', models.BooleanField( + default=False, + help_text='Designates that this user has all permissions without explicitly assigning them.', + verbose_name='superuser status' + )), + ('username', models.CharField( + help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, + max_length=30, verbose_name='username', + validators=[validators.UnicodeUsernameValidator()], + )), + ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), + ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), + ('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)), + ('is_staff', models.BooleanField( + default=False, help_text='Designates whether the user can log into this admin site.', + verbose_name='staff status' + )), + ('is_active', models.BooleanField( + default=True, verbose_name='active', help_text=( + 'Designates whether this user should be treated as active. Unselect this instead of deleting ' + 'accounts.' + ) + )), + ('date_joined', models.DateTimeField(default=timezone.now, verbose_name='date joined')), + ('groups', models.ManyToManyField( + to='auth.Group', verbose_name='groups', blank=True, related_name='user_set', + related_query_name='user', help_text=( + 'The groups this user belongs to. A user will get all permissions granted to each of their ' + 'groups.' + ) + )), + ('user_permissions', models.ManyToManyField( + to='auth.Permission', verbose_name='user permissions', blank=True, + help_text='Specific permissions for this user.', related_name='user_set', + related_query_name='user') + ), ], options={ - "swappable": "AUTH_USER_MODEL", - "verbose_name": "user", - "verbose_name_plural": "users", + 'swappable': 'AUTH_USER_MODEL', + 'verbose_name': 'user', + 'verbose_name_plural': 'users', }, managers=[ - ("objects", django.contrib.auth.models.UserManager()), + ('objects', django.contrib.auth.models.UserManager()), ], ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0002_alter_permission_name_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0002_alter_permission_name_max_length.py index a9ca6f5..556c320 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0002_alter_permission_name_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0002_alter_permission_name_max_length.py @@ -4,13 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0001_initial"), + ('auth', '0001_initial'), ] operations = [ migrations.AlterField( - model_name="permission", - name="name", - field=models.CharField(max_length=255, verbose_name="name"), + model_name='permission', + name='name', + field=models.CharField(max_length=255, verbose_name='name'), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0003_alter_user_email_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0003_alter_user_email_max_length.py index 8a57548..ee8a9bd 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0003_alter_user_email_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0003_alter_user_email_max_length.py @@ -4,15 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0002_alter_permission_name_max_length"), + ('auth', '0002_alter_permission_name_max_length'), ] operations = [ migrations.AlterField( - model_name="user", - name="email", - field=models.EmailField( - max_length=254, verbose_name="email address", blank=True - ), + model_name='user', + name='email', + field=models.EmailField(max_length=254, verbose_name='email address', blank=True), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0004_alter_user_username_opts.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0004_alter_user_username_opts.py index 6930e9b..a16083e 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0004_alter_user_username_opts.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0004_alter_user_username_opts.py @@ -5,24 +5,19 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0003_alter_user_email_max_length"), + ('auth', '0003_alter_user_email_max_length'), ] # No database changes; modifies validators and error_messages (#13147). operations = [ migrations.AlterField( - model_name="user", - name="username", + model_name='user', + name='username', field=models.CharField( - error_messages={"unique": "A user with that username already exists."}, - max_length=30, + error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[validators.UnicodeUsernameValidator()], - help_text=( - "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ " - "only." - ), - unique=True, - verbose_name="username", + help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', + unique=True, verbose_name='username' ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0005_alter_user_last_login_null.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0005_alter_user_last_login_null.py index 8407e2d..97cd105 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0005_alter_user_last_login_null.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0005_alter_user_last_login_null.py @@ -4,15 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0004_alter_user_username_opts"), + ('auth', '0004_alter_user_username_opts'), ] operations = [ migrations.AlterField( - model_name="user", - name="last_login", - field=models.DateTimeField( - null=True, verbose_name="last login", blank=True - ), + model_name='user', + name='last_login', + field=models.DateTimeField(null=True, verbose_name='last login', blank=True), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0006_require_contenttypes_0002.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0006_require_contenttypes_0002.py index b4e816a..48c26be 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0006_require_contenttypes_0002.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0006_require_contenttypes_0002.py @@ -4,8 +4,8 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ("auth", "0005_alter_user_last_login_null"), - ("contenttypes", "0002_remove_content_type_name"), + ('auth', '0005_alter_user_last_login_null'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py index 3c94141..42f5087 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py @@ -5,23 +5,20 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0006_require_contenttypes_0002"), + ('auth', '0006_require_contenttypes_0002'), ] operations = [ migrations.AlterField( - model_name="user", - name="username", + model_name='user', + name='username', field=models.CharField( - error_messages={"unique": "A user with that username already exists."}, - help_text=( - "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ " - "only." - ), + error_messages={'unique': 'A user with that username already exists.'}, + help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[validators.UnicodeUsernameValidator()], - verbose_name="username", + verbose_name='username', ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0008_alter_user_username_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0008_alter_user_username_max_length.py index bfb844b..7c9dae0 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0008_alter_user_username_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0008_alter_user_username_max_length.py @@ -5,23 +5,20 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0007_alter_validators_add_error_messages"), + ('auth', '0007_alter_validators_add_error_messages'), ] operations = [ migrations.AlterField( - model_name="user", - name="username", + model_name='user', + name='username', field=models.CharField( - error_messages={"unique": "A user with that username already exists."}, - help_text=( - "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ " - "only." - ), + error_messages={'unique': 'A user with that username already exists.'}, + help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[validators.UnicodeUsernameValidator()], - verbose_name="username", + verbose_name='username', ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py index e066536..b217359 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0009_alter_user_last_name_max_length.py @@ -4,15 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0008_alter_user_username_max_length"), + ('auth', '0008_alter_user_username_max_length'), ] operations = [ migrations.AlterField( - model_name="user", - name="last_name", - field=models.CharField( - blank=True, max_length=150, verbose_name="last name" - ), + model_name='user', + name='last_name', + field=models.CharField(blank=True, max_length=150, verbose_name='last name'), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0010_alter_group_name_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0010_alter_group_name_max_length.py index a58e114..67ea061 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0010_alter_group_name_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0010_alter_group_name_max_length.py @@ -4,13 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0009_alter_user_last_name_max_length"), + ('auth', '0009_alter_user_last_name_max_length'), ] operations = [ migrations.AlterField( - model_name="group", - name="name", - field=models.CharField(max_length=150, unique=True, verbose_name="name"), + model_name='group', + name='name', + field=models.CharField(max_length=150, unique=True, verbose_name='name'), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0011_update_proxy_permissions.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0011_update_proxy_permissions.py index b792141..a939244 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0011_update_proxy_permissions.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0011_update_proxy_permissions.py @@ -20,26 +20,23 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False): of the proxy model. """ style = color_style() - Permission = apps.get_model("auth", "Permission") - ContentType = apps.get_model("contenttypes", "ContentType") + Permission = apps.get_model('auth', 'Permission') + ContentType = apps.get_model('contenttypes', 'ContentType') alias = schema_editor.connection.alias for Model in apps.get_models(): opts = Model._meta if not opts.proxy: continue proxy_default_permissions_codenames = [ - "%s_%s" % (action, opts.model_name) for action in opts.default_permissions + '%s_%s' % (action, opts.model_name) + for action in opts.default_permissions ] permissions_query = Q(codename__in=proxy_default_permissions_codenames) for codename, name in opts.permissions: permissions_query = permissions_query | Q(codename=codename, name=name) content_type_manager = ContentType.objects.db_manager(alias) - concrete_content_type = content_type_manager.get_for_model( - Model, for_concrete_model=True - ) - proxy_content_type = content_type_manager.get_for_model( - Model, for_concrete_model=False - ) + concrete_content_type = content_type_manager.get_for_model(Model, for_concrete_model=True) + proxy_content_type = content_type_manager.get_for_model(Model, for_concrete_model=False) old_content_type = proxy_content_type if reverse else concrete_content_type new_content_type = concrete_content_type if reverse else proxy_content_type try: @@ -49,11 +46,9 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False): content_type=old_content_type, ).update(content_type=new_content_type) except IntegrityError: - old = "{}_{}".format(old_content_type.app_label, old_content_type.model) - new = "{}_{}".format(new_content_type.app_label, new_content_type.model) - sys.stdout.write( - style.WARNING(WARNING.format(old=old, new=new, query=permissions_query)) - ) + old = '{}_{}'.format(old_content_type.app_label, old_content_type.model) + new = '{}_{}'.format(new_content_type.app_label, new_content_type.model) + sys.stdout.write(style.WARNING(WARNING.format(old=old, new=new, query=permissions_query))) def revert_proxy_model_permissions(apps, schema_editor): @@ -66,11 +61,9 @@ def revert_proxy_model_permissions(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("auth", "0010_alter_group_name_max_length"), - ("contenttypes", "0002_remove_content_type_name"), + ('auth', '0010_alter_group_name_max_length'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ - migrations.RunPython( - update_proxy_model_permissions, revert_proxy_model_permissions - ), + migrations.RunPython(update_proxy_model_permissions, revert_proxy_model_permissions), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py b/venv/Lib/site-packages/django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py index 839c950..69f123f 100644 --- a/venv/Lib/site-packages/django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py +++ b/venv/Lib/site-packages/django/contrib/auth/migrations/0012_alter_user_first_name_max_length.py @@ -4,15 +4,13 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("auth", "0011_update_proxy_permissions"), + ('auth', '0011_update_proxy_permissions'), ] operations = [ migrations.AlterField( - model_name="user", - name="first_name", - field=models.CharField( - blank=True, max_length=150, verbose_name="first name" - ), + model_name='user', + name='first_name', + field=models.CharField(blank=True, max_length=150, verbose_name='first name'), ), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/mixins.py b/venv/Lib/site-packages/django/contrib/auth/mixins.py index 0e46000..02f9d23 100644 --- a/venv/Lib/site-packages/django/contrib/auth/mixins.py +++ b/venv/Lib/site-packages/django/contrib/auth/mixins.py @@ -12,9 +12,8 @@ class AccessMixin: Abstract CBV mixin that gives access mixins the same customizable functionality. """ - login_url = None - permission_denied_message = "" + permission_denied_message = '' raise_exception = False redirect_field_name = REDIRECT_FIELD_NAME @@ -25,9 +24,8 @@ class AccessMixin: login_url = self.login_url or settings.LOGIN_URL if not login_url: raise ImproperlyConfigured( - f"{self.__class__.__name__} is missing the login_url attribute. Define " - f"{self.__class__.__name__}.login_url, settings.LOGIN_URL, or override " - f"{self.__class__.__name__}.get_login_url()." + '{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override ' + '{0}.get_login_url().'.format(self.__class__.__name__) ) return str(login_url) @@ -53,8 +51,9 @@ class AccessMixin: # path as the "next" url. login_scheme, login_netloc = urlparse(resolved_login_url)[:2] current_scheme, current_netloc = urlparse(path)[:2] - if (not login_scheme or login_scheme == current_scheme) and ( - not login_netloc or login_netloc == current_netloc + if ( + (not login_scheme or login_scheme == current_scheme) and + (not login_netloc or login_netloc == current_netloc) ): path = self.request.get_full_path() return redirect_to_login( @@ -66,7 +65,6 @@ class AccessMixin: class LoginRequiredMixin(AccessMixin): """Verify that the current user is authenticated.""" - def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return self.handle_no_permission() @@ -75,7 +73,6 @@ class LoginRequiredMixin(AccessMixin): class PermissionRequiredMixin(AccessMixin): """Verify that the current user has all specified permissions.""" - permission_required = None def get_permission_required(self): @@ -85,10 +82,8 @@ class PermissionRequiredMixin(AccessMixin): """ if self.permission_required is None: raise ImproperlyConfigured( - f"{self.__class__.__name__} is missing the " - f"permission_required attribute. Define " - f"{self.__class__.__name__}.permission_required, or override " - f"{self.__class__.__name__}.get_permission_required()." + '{0} is missing the permission_required attribute. Define {0}.permission_required, or override ' + '{0}.get_permission_required().'.format(self.__class__.__name__) ) if isinstance(self.permission_required, str): perms = (self.permission_required,) @@ -117,9 +112,7 @@ class UserPassesTestMixin(AccessMixin): def test_func(self): raise NotImplementedError( - "{} is missing the implementation of the test_func() method.".format( - self.__class__.__name__ - ) + '{} is missing the implementation of the test_func() method.'.format(self.__class__.__name__) ) def get_test_func(self): diff --git a/venv/Lib/site-packages/django/contrib/auth/models.py b/venv/Lib/site-packages/django/contrib/auth/models.py index 53bb681..5f092f0 100644 --- a/venv/Lib/site-packages/django/contrib/auth/models.py +++ b/venv/Lib/site-packages/django/contrib/auth/models.py @@ -19,7 +19,7 @@ def update_last_login(sender, user, **kwargs): the user logging in. """ user.last_login = timezone.now() - user.save(update_fields=["last_login"]) + user.save(update_fields=['last_login']) class PermissionManager(models.Manager): @@ -28,9 +28,7 @@ class PermissionManager(models.Manager): def get_by_natural_key(self, codename, app_label, model): return self.get( codename=codename, - content_type=ContentType.objects.db_manager(self.db).get_by_natural_key( - app_label, model - ), + content_type=ContentType.objects.db_manager(self.db).get_by_natural_key(app_label, model), ) @@ -57,37 +55,34 @@ class Permission(models.Model): The permissions listed above are automatically created for each model. """ - - name = models.CharField(_("name"), max_length=255) + name = models.CharField(_('name'), max_length=255) content_type = models.ForeignKey( ContentType, models.CASCADE, - verbose_name=_("content type"), + verbose_name=_('content type'), ) - codename = models.CharField(_("codename"), max_length=100) + codename = models.CharField(_('codename'), max_length=100) objects = PermissionManager() class Meta: - verbose_name = _("permission") - verbose_name_plural = _("permissions") - unique_together = [["content_type", "codename"]] - ordering = ["content_type__app_label", "content_type__model", "codename"] + verbose_name = _('permission') + verbose_name_plural = _('permissions') + unique_together = [['content_type', 'codename']] + ordering = ['content_type__app_label', 'content_type__model', 'codename'] def __str__(self): - return "%s | %s" % (self.content_type, self.name) + return '%s | %s' % (self.content_type, self.name) def natural_key(self): return (self.codename,) + self.content_type.natural_key() - - natural_key.dependencies = ["contenttypes.contenttype"] + natural_key.dependencies = ['contenttypes.contenttype'] class GroupManager(models.Manager): """ The manager for the auth's Group model. """ - use_in_migrations = True def get_by_natural_key(self, name): @@ -111,19 +106,18 @@ class Group(models.Model): members-only portion of your site, or sending them members-only email messages. """ - - name = models.CharField(_("name"), max_length=150, unique=True) + name = models.CharField(_('name'), max_length=150, unique=True) permissions = models.ManyToManyField( Permission, - verbose_name=_("permissions"), + verbose_name=_('permissions'), blank=True, ) objects = GroupManager() class Meta: - verbose_name = _("group") - verbose_name_plural = _("groups") + verbose_name = _('group') + verbose_name_plural = _('groups') def __str__(self): return self.name @@ -140,14 +134,12 @@ class UserManager(BaseUserManager): Create and save a user with the given username, email, and password. """ if not username: - raise ValueError("The given username must be set") + raise ValueError('The given username must be set') email = self.normalize_email(email) # Lookup the real model class from the global app registry so this # manager method can be used in migrations. This is fine because # managers are by definition working on the real model. - GlobalUserModel = apps.get_model( - self.model._meta.app_label, self.model._meta.object_name - ) + GlobalUserModel = apps.get_model(self.model._meta.app_label, self.model._meta.object_name) username = GlobalUserModel.normalize_username(username) user = self.model(username=username, email=email, **extra_fields) user.password = make_password(password) @@ -155,40 +147,39 @@ class UserManager(BaseUserManager): return user def create_user(self, username, email=None, password=None, **extra_fields): - extra_fields.setdefault("is_staff", False) - extra_fields.setdefault("is_superuser", False) + extra_fields.setdefault('is_staff', False) + extra_fields.setdefault('is_superuser', False) return self._create_user(username, email, password, **extra_fields) def create_superuser(self, username, email=None, password=None, **extra_fields): - extra_fields.setdefault("is_staff", True) - extra_fields.setdefault("is_superuser", True) + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) - if extra_fields.get("is_staff") is not True: - raise ValueError("Superuser must have is_staff=True.") - if extra_fields.get("is_superuser") is not True: - raise ValueError("Superuser must have is_superuser=True.") + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') return self._create_user(username, email, password, **extra_fields) - def with_perm( - self, perm, is_active=True, include_superusers=True, backend=None, obj=None - ): + def with_perm(self, perm, is_active=True, include_superusers=True, backend=None, obj=None): if backend is None: backends = auth._get_backends(return_tuples=True) if len(backends) == 1: backend, _ = backends[0] else: raise ValueError( - "You have multiple authentication backends configured and " - "therefore must provide the `backend` argument." + 'You have multiple authentication backends configured and ' + 'therefore must provide the `backend` argument.' ) elif not isinstance(backend, str): raise TypeError( - "backend must be a dotted import path string (got %r)." % backend + 'backend must be a dotted import path string (got %r).' + % backend ) else: backend = auth.load_backend(backend) - if hasattr(backend, "with_perm"): + if hasattr(backend, 'with_perm'): return backend.with_perm( perm, is_active=is_active, @@ -201,7 +192,7 @@ class UserManager(BaseUserManager): # A few helper functions for common logic between User and AnonymousUser. def _user_get_permissions(user, obj, from_name): permissions = set() - name = "get_%s_permissions" % from_name + name = 'get_%s_permissions' % from_name for backend in auth.get_backends(): if hasattr(backend, name): permissions.update(getattr(backend, name)(user, obj)) @@ -213,7 +204,7 @@ def _user_has_perm(user, perm, obj): A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): - if not hasattr(backend, "has_perm"): + if not hasattr(backend, 'has_perm'): continue try: if backend.has_perm(user, perm, obj): @@ -228,7 +219,7 @@ def _user_has_module_perms(user, app_label): A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): - if not hasattr(backend, "has_module_perms"): + if not hasattr(backend, 'has_module_perms'): continue try: if backend.has_module_perms(user, app_label): @@ -243,31 +234,30 @@ class PermissionsMixin(models.Model): Add the fields and methods necessary to support the Group and Permission models using the ModelBackend. """ - is_superuser = models.BooleanField( - _("superuser status"), + _('superuser status'), default=False, help_text=_( - "Designates that this user has all permissions without " - "explicitly assigning them." + 'Designates that this user has all permissions without ' + 'explicitly assigning them.' ), ) groups = models.ManyToManyField( Group, - verbose_name=_("groups"), + verbose_name=_('groups'), blank=True, help_text=_( - "The groups this user belongs to. A user will get all permissions " - "granted to each of their groups." + 'The groups this user belongs to. A user will get all permissions ' + 'granted to each of their groups.' ), related_name="user_set", related_query_name="user", ) user_permissions = models.ManyToManyField( Permission, - verbose_name=_("user permissions"), + verbose_name=_('user permissions'), blank=True, - help_text=_("Specific permissions for this user."), + help_text=_('Specific permissions for this user.'), related_name="user_set", related_query_name="user", ) @@ -281,7 +271,7 @@ class PermissionsMixin(models.Model): Query all available auth backends. If an object is passed in, return only permissions matching this object. """ - return _user_get_permissions(self, obj, "user") + return _user_get_permissions(self, obj, 'user') def get_group_permissions(self, obj=None): """ @@ -289,10 +279,10 @@ class PermissionsMixin(models.Model): groups. Query all available auth backends. If an object is passed in, return only permissions matching this object. """ - return _user_get_permissions(self, obj, "group") + return _user_get_permissions(self, obj, 'group') def get_all_permissions(self, obj=None): - return _user_get_permissions(self, obj, "all") + return _user_get_permissions(self, obj, 'all') def has_perm(self, perm, obj=None): """ @@ -335,48 +325,45 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): Username and password are required. Other fields are optional. """ - username_validator = UnicodeUsernameValidator() username = models.CharField( - _("username"), + _('username'), max_length=150, unique=True, - help_text=_( - "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." - ), + help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ - "unique": _("A user with that username already exists."), + 'unique': _("A user with that username already exists."), }, ) - first_name = models.CharField(_("first name"), max_length=150, blank=True) - last_name = models.CharField(_("last name"), max_length=150, blank=True) - email = models.EmailField(_("email address"), blank=True) + first_name = models.CharField(_('first name'), max_length=150, blank=True) + last_name = models.CharField(_('last name'), max_length=150, blank=True) + email = models.EmailField(_('email address'), blank=True) is_staff = models.BooleanField( - _("staff status"), + _('staff status'), default=False, - help_text=_("Designates whether the user can log into this admin site."), + help_text=_('Designates whether the user can log into this admin site.'), ) is_active = models.BooleanField( - _("active"), + _('active'), default=True, help_text=_( - "Designates whether this user should be treated as active. " - "Unselect this instead of deleting accounts." + 'Designates whether this user should be treated as active. ' + 'Unselect this instead of deleting accounts.' ), ) - date_joined = models.DateTimeField(_("date joined"), default=timezone.now) + date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() - EMAIL_FIELD = "email" - USERNAME_FIELD = "username" - REQUIRED_FIELDS = ["email"] + EMAIL_FIELD = 'email' + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email'] class Meta: - verbose_name = _("user") - verbose_name_plural = _("users") + verbose_name = _('user') + verbose_name_plural = _('users') abstract = True def clean(self): @@ -387,7 +374,7 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): """ Return the first_name plus the last_name, with a space in between. """ - full_name = "%s %s" % (self.first_name, self.last_name) + full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): @@ -406,15 +393,14 @@ class User(AbstractUser): Username and password are required. Other fields are optional. """ - class Meta(AbstractUser.Meta): - swappable = "AUTH_USER_MODEL" + swappable = 'AUTH_USER_MODEL' class AnonymousUser: id = None pk = None - username = "" + username = '' is_staff = False is_active = False is_superuser = False @@ -422,7 +408,7 @@ class AnonymousUser: _user_permissions = EmptyManager(Permission) def __str__(self): - return "AnonymousUser" + return 'AnonymousUser' def __eq__(self, other): return isinstance(other, self.__class__) @@ -431,30 +417,19 @@ class AnonymousUser: return 1 # instances always return the same hash value def __int__(self): - raise TypeError( - "Cannot cast AnonymousUser to int. Are you trying to use it in place of " - "User?" - ) + raise TypeError('Cannot cast AnonymousUser to int. Are you trying to use it in place of User?') def save(self): - raise NotImplementedError( - "Django doesn't provide a DB representation for AnonymousUser." - ) + raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.") def delete(self): - raise NotImplementedError( - "Django doesn't provide a DB representation for AnonymousUser." - ) + raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.") def set_password(self, raw_password): - raise NotImplementedError( - "Django doesn't provide a DB representation for AnonymousUser." - ) + raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.") def check_password(self, raw_password): - raise NotImplementedError( - "Django doesn't provide a DB representation for AnonymousUser." - ) + raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.") @property def groups(self): @@ -465,13 +440,13 @@ class AnonymousUser: return self._user_permissions def get_user_permissions(self, obj=None): - return _user_get_permissions(self, obj, "user") + return _user_get_permissions(self, obj, 'user') def get_group_permissions(self, obj=None): return set() def get_all_permissions(self, obj=None): - return _user_get_permissions(self, obj, "all") + return _user_get_permissions(self, obj, 'all') def has_perm(self, perm, obj=None): return _user_has_perm(self, perm, obj=obj) diff --git a/venv/Lib/site-packages/django/contrib/auth/password_validation.py b/venv/Lib/site-packages/django/contrib/auth/password_validation.py index e5c9710..845f4d8 100644 --- a/venv/Lib/site-packages/django/contrib/auth/password_validation.py +++ b/venv/Lib/site-packages/django/contrib/auth/password_validation.py @@ -6,15 +6,12 @@ from pathlib import Path from django.conf import settings from django.core.exceptions import ( - FieldDoesNotExist, - ImproperlyConfigured, - ValidationError, + FieldDoesNotExist, ImproperlyConfigured, ValidationError, ) -from django.utils.functional import cached_property, lazy +from django.utils.functional import lazy from django.utils.html import format_html, format_html_join from django.utils.module_loading import import_string -from django.utils.translation import gettext as _ -from django.utils.translation import ngettext +from django.utils.translation import gettext as _, ngettext @functools.lru_cache(maxsize=None) @@ -26,14 +23,11 @@ def get_password_validators(validator_config): validators = [] for validator in validator_config: try: - klass = import_string(validator["NAME"]) + klass = import_string(validator['NAME']) except ImportError: - msg = ( - "The module in NAME could not be imported: %s. Check your " - "AUTH_PASSWORD_VALIDATORS setting." - ) - raise ImproperlyConfigured(msg % validator["NAME"]) - validators.append(klass(**validator.get("OPTIONS", {}))) + msg = "The module in NAME could not be imported: %s. Check your AUTH_PASSWORD_VALIDATORS setting." + raise ImproperlyConfigured(msg % validator['NAME']) + validators.append(klass(**validator.get('OPTIONS', {}))) return validators @@ -65,7 +59,7 @@ def password_changed(password, user=None, password_validators=None): if password_validators is None: password_validators = get_default_password_validators() for validator in password_validators: - password_changed = getattr(validator, "password_changed", lambda *a: None) + password_changed = getattr(validator, 'password_changed', lambda *a: None) password_changed(password, user) @@ -87,10 +81,8 @@ def _password_validators_help_text_html(password_validators=None): in an <ul>. """ help_texts = password_validators_help_texts(password_validators) - help_items = format_html_join( - "", "<li>{}</li>", ((help_text,) for help_text in help_texts) - ) - return format_html("<ul>{}</ul>", help_items) if help_items else "" + help_items = format_html_join('', '<li>{}</li>', ((help_text,) for help_text in help_texts)) + return format_html('<ul>{}</ul>', help_items) if help_items else '' password_validators_help_text_html = lazy(_password_validators_help_text_html, str) @@ -100,7 +92,6 @@ class MinimumLengthValidator: """ Validate whether the password is of a minimum length. """ - def __init__(self, min_length=8): self.min_length = min_length @@ -108,52 +99,20 @@ class MinimumLengthValidator: if len(password) < self.min_length: raise ValidationError( ngettext( - "This password is too short. It must contain at least " - "%(min_length)d character.", - "This password is too short. It must contain at least " - "%(min_length)d characters.", - self.min_length, + "This password is too short. It must contain at least %(min_length)d character.", + "This password is too short. It must contain at least %(min_length)d characters.", + self.min_length ), - code="password_too_short", - params={"min_length": self.min_length}, + code='password_too_short', + params={'min_length': self.min_length}, ) def get_help_text(self): return ngettext( "Your password must contain at least %(min_length)d character.", "Your password must contain at least %(min_length)d characters.", - self.min_length, - ) % {"min_length": self.min_length} - - -def exceeds_maximum_length_ratio(password, max_similarity, value): - """ - Test that value is within a reasonable range of password. - - The following ratio calculations are based on testing SequenceMatcher like - this: - - for i in range(0,6): - print(10**i, SequenceMatcher(a='A', b='A'*(10**i)).quick_ratio()) - - which yields: - - 1 1.0 - 10 0.18181818181818182 - 100 0.019801980198019802 - 1000 0.001998001998001998 - 10000 0.00019998000199980003 - 100000 1.999980000199998e-05 - - This means a length_ratio of 10 should never yield a similarity higher than - 0.2, for 100 this is down to 0.02 and for 1000 it is 0.002. This can be - calculated via 2 / length_ratio. As a result we avoid the potentially - expensive sequence matching. - """ - pwd_len = len(password) - length_bound_similarity = max_similarity / 2 * pwd_len - value_len = len(value) - return pwd_len >= 10 * value_len and value_len < length_bound_similarity + self.min_length + ) % {'min_length': self.min_length} class UserAttributeSimilarityValidator: @@ -167,51 +126,35 @@ class UserAttributeSimilarityValidator: example, a password is validated against either part of an email address, as well as the full address. """ - - DEFAULT_USER_ATTRIBUTES = ("username", "first_name", "last_name", "email") + DEFAULT_USER_ATTRIBUTES = ('username', 'first_name', 'last_name', 'email') def __init__(self, user_attributes=DEFAULT_USER_ATTRIBUTES, max_similarity=0.7): self.user_attributes = user_attributes - if max_similarity < 0.1: - raise ValueError("max_similarity must be at least 0.1") self.max_similarity = max_similarity def validate(self, password, user=None): if not user: return - password = password.lower() for attribute_name in self.user_attributes: value = getattr(user, attribute_name, None) if not value or not isinstance(value, str): continue - value_lower = value.lower() - value_parts = re.split(r"\W+", value_lower) + [value_lower] + value_parts = re.split(r'\W+', value) + [value] for value_part in value_parts: - if exceeds_maximum_length_ratio( - password, self.max_similarity, value_part - ): - continue - if ( - SequenceMatcher(a=password, b=value_part).quick_ratio() - >= self.max_similarity - ): + if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity: try: - verbose_name = str( - user._meta.get_field(attribute_name).verbose_name - ) + verbose_name = str(user._meta.get_field(attribute_name).verbose_name) except FieldDoesNotExist: verbose_name = attribute_name raise ValidationError( _("The password is too similar to the %(verbose_name)s."), - code="password_too_similar", - params={"verbose_name": verbose_name}, + code='password_too_similar', + params={'verbose_name': verbose_name}, ) def get_help_text(self): - return _( - "Your password can’t be too similar to your other personal information." - ) + return _('Your password can’t be too similar to your other personal information.') class CommonPasswordValidator: @@ -224,16 +167,11 @@ class CommonPasswordValidator: https://gist.github.com/roycewilliams/281ce539915a947a23db17137d91aeb7 The password list must be lowercased to match the comparison in validate(). """ - - @cached_property - def DEFAULT_PASSWORD_LIST_PATH(self): - return Path(__file__).resolve().parent / "common-passwords.txt.gz" + DEFAULT_PASSWORD_LIST_PATH = Path(__file__).resolve().parent / 'common-passwords.txt.gz' def __init__(self, password_list_path=DEFAULT_PASSWORD_LIST_PATH): - if password_list_path is CommonPasswordValidator.DEFAULT_PASSWORD_LIST_PATH: - password_list_path = self.DEFAULT_PASSWORD_LIST_PATH try: - with gzip.open(password_list_path, "rt", encoding="utf-8") as f: + with gzip.open(password_list_path, 'rt', encoding='utf-8') as f: self.passwords = {x.strip() for x in f} except OSError: with open(password_list_path) as f: @@ -243,24 +181,23 @@ class CommonPasswordValidator: if password.lower().strip() in self.passwords: raise ValidationError( _("This password is too common."), - code="password_too_common", + code='password_too_common', ) def get_help_text(self): - return _("Your password can’t be a commonly used password.") + return _('Your password can’t be a commonly used password.') class NumericPasswordValidator: """ Validate whether the password is alphanumeric. """ - def validate(self, password, user=None): if password.isdigit(): raise ValidationError( _("This password is entirely numeric."), - code="password_entirely_numeric", + code='password_entirely_numeric', ) def get_help_text(self): - return _("Your password can’t be entirely numeric.") + return _('Your password can’t be entirely numeric.') diff --git a/venv/Lib/site-packages/django/contrib/auth/tokens.py b/venv/Lib/site-packages/django/contrib/auth/tokens.py index de8e17a..c3863d1 100644 --- a/venv/Lib/site-packages/django/contrib/auth/tokens.py +++ b/venv/Lib/site-packages/django/contrib/auth/tokens.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, time from django.conf import settings from django.utils.crypto import constant_time_compare, salted_hmac @@ -10,21 +10,15 @@ class PasswordResetTokenGenerator: Strategy object used to generate and check tokens for the password reset mechanism. """ - key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator" algorithm = None - _secret = None + secret = None def __init__(self): - self.algorithm = self.algorithm or "sha256" - - def _get_secret(self): - return self._secret or settings.SECRET_KEY - - def _set_secret(self, secret): - self._secret = secret - - secret = property(_get_secret, _set_secret) + self.secret = self.secret or settings.SECRET_KEY + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # self.algorithm = self.algorithm or 'sha256' + self.algorithm = self.algorithm or settings.DEFAULT_HASHING_ALGORITHM def make_token(self, user): """ @@ -42,6 +36,8 @@ class PasswordResetTokenGenerator: # Parse the token try: ts_b36, _ = token.split("-") + # RemovedInDjango40Warning. + legacy_token = len(ts_b36) < 4 except ValueError: return False @@ -52,15 +48,28 @@ class PasswordResetTokenGenerator: # Check that the timestamp/uid has not been tampered with if not constant_time_compare(self._make_token_with_timestamp(user, ts), token): - return False + # RemovedInDjango40Warning: when the deprecation ends, replace + # with: + # return False + if not constant_time_compare( + self._make_token_with_timestamp(user, ts, legacy=True), + token, + ): + return False + # RemovedInDjango40Warning: convert days to seconds and round to + # midnight (server time) for pre-Django 3.1 tokens. + now = self._now() + if legacy_token: + ts *= 24 * 60 * 60 + ts += int((now - datetime.combine(now.date(), time.min)).total_seconds()) # Check the timestamp is within limit. - if (self._num_seconds(self._now()) - ts) > settings.PASSWORD_RESET_TIMEOUT: + if (self._num_seconds(now) - ts) > settings.PASSWORD_RESET_TIMEOUT: return False return True - def _make_token_with_timestamp(self, user, timestamp): + def _make_token_with_timestamp(self, user, timestamp, legacy=False): # timestamp is number of seconds since 2001-1-1. Converted to base 36, # this gives us a 6 digit string until about 2069. ts_b36 = int_to_base36(timestamp) @@ -68,10 +77,11 @@ class PasswordResetTokenGenerator: self.key_salt, self._make_hash_value(user, timestamp), secret=self.secret, - algorithm=self.algorithm, - ).hexdigest()[ - ::2 - ] # Limit to shorten the URL. + # RemovedInDjango40Warning: when the deprecation ends, remove the + # legacy argument and replace with: + # algorithm=self.algorithm, + algorithm='sha1' if legacy else self.algorithm, + ).hexdigest()[::2] # Limit to shorten the URL. return "%s-%s" % (ts_b36, hash_string) def _make_hash_value(self, user, timestamp): @@ -91,14 +101,10 @@ class PasswordResetTokenGenerator: """ # Truncate microseconds so that tokens are consistent even if the # database doesn't support microseconds. - login_timestamp = ( - "" - if user.last_login is None - else user.last_login.replace(microsecond=0, tzinfo=None) - ) + login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None) email_field = user.get_email_field_name() - email = getattr(user, email_field, "") or "" - return f"{user.pk}{user.password}{login_timestamp}{timestamp}{email}" + email = getattr(user, email_field, '') or '' + return f'{user.pk}{user.password}{login_timestamp}{timestamp}{email}' def _num_seconds(self, dt): return int((dt - datetime(2001, 1, 1)).total_seconds()) diff --git a/venv/Lib/site-packages/django/contrib/auth/urls.py b/venv/Lib/site-packages/django/contrib/auth/urls.py index 699ba61..cd4af69 100644 --- a/venv/Lib/site-packages/django/contrib/auth/urls.py +++ b/venv/Lib/site-packages/django/contrib/auth/urls.py @@ -7,30 +7,14 @@ from django.contrib.auth import views from django.urls import path urlpatterns = [ - path("login/", views.LoginView.as_view(), name="login"), - path("logout/", views.LogoutView.as_view(), name="logout"), - path( - "password_change/", views.PasswordChangeView.as_view(), name="password_change" - ), - path( - "password_change/done/", - views.PasswordChangeDoneView.as_view(), - name="password_change_done", - ), - path("password_reset/", views.PasswordResetView.as_view(), name="password_reset"), - path( - "password_reset/done/", - views.PasswordResetDoneView.as_view(), - name="password_reset_done", - ), - path( - "reset/<uidb64>/<token>/", - views.PasswordResetConfirmView.as_view(), - name="password_reset_confirm", - ), - path( - "reset/done/", - views.PasswordResetCompleteView.as_view(), - name="password_reset_complete", - ), + path('login/', views.LoginView.as_view(), name='login'), + path('logout/', views.LogoutView.as_view(), name='logout'), + + path('password_change/', views.PasswordChangeView.as_view(), name='password_change'), + path('password_change/done/', views.PasswordChangeDoneView.as_view(), name='password_change_done'), + + path('password_reset/', views.PasswordResetView.as_view(), name='password_reset'), + path('password_reset/done/', views.PasswordResetDoneView.as_view(), name='password_reset_done'), + path('reset/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), + path('reset/done/', views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), ] diff --git a/venv/Lib/site-packages/django/contrib/auth/validators.py b/venv/Lib/site-packages/django/contrib/auth/validators.py index 55f7028..9345c5c 100644 --- a/venv/Lib/site-packages/django/contrib/auth/validators.py +++ b/venv/Lib/site-packages/django/contrib/auth/validators.py @@ -7,19 +7,19 @@ from django.utils.translation import gettext_lazy as _ @deconstructible class ASCIIUsernameValidator(validators.RegexValidator): - regex = r"^[\w.@+-]+\Z" + regex = r'^[\w.@+-]+\Z' message = _( - "Enter a valid username. This value may contain only English letters, " - "numbers, and @/./+/-/_ characters." + 'Enter a valid username. This value may contain only English letters, ' + 'numbers, and @/./+/-/_ characters.' ) flags = re.ASCII @deconstructible class UnicodeUsernameValidator(validators.RegexValidator): - regex = r"^[\w.@+-]+\Z" + regex = r'^[\w.@+-]+\Z' message = _( - "Enter a valid username. This value may contain only letters, " - "numbers, and @/./+/-/_ characters." + 'Enter a valid username. This value may contain only letters, ' + 'numbers, and @/./+/-/_ characters.' ) flags = 0 diff --git a/venv/Lib/site-packages/django/contrib/auth/views.py b/venv/Lib/site-packages/django/contrib/auth/views.py index 7749e9f..529400d 100644 --- a/venv/Lib/site-packages/django/contrib/auth/views.py +++ b/venv/Lib/site-packages/django/contrib/auth/views.py @@ -1,27 +1,25 @@ from urllib.parse import urlparse, urlunparse from django.conf import settings - # Avoid shadowing the login() and logout() views below. -from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model -from django.contrib.auth import login as auth_login -from django.contrib.auth import logout as auth_logout -from django.contrib.auth import update_session_auth_hash +from django.contrib.auth import ( + REDIRECT_FIELD_NAME, get_user_model, login as auth_login, + logout as auth_logout, update_session_auth_hash, +) from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import ( - AuthenticationForm, - PasswordChangeForm, - PasswordResetForm, - SetPasswordForm, + AuthenticationForm, PasswordChangeForm, PasswordResetForm, SetPasswordForm, ) from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site -from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.core.exceptions import ValidationError from django.http import HttpResponseRedirect, QueryDict from django.shortcuts import resolve_url from django.urls import reverse_lazy from django.utils.decorators import method_decorator -from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode +from django.utils.http import ( + url_has_allowed_host_and_scheme, urlsafe_base64_decode, +) from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect @@ -43,12 +41,10 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView): """ Display the login form and handle the login action. """ - form_class = AuthenticationForm authentication_form = None - next_page = None redirect_field_name = REDIRECT_FIELD_NAME - template_name = "registration/login.html" + template_name = 'registration/login.html' redirect_authenticated_user = False extra_context = None @@ -67,30 +63,28 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView): return super().dispatch(request, *args, **kwargs) def get_success_url(self): - return self.get_redirect_url() or self.get_default_redirect_url() + url = self.get_redirect_url() + return url or resolve_url(settings.LOGIN_REDIRECT_URL) def get_redirect_url(self): """Return the user-originating redirect URL if it's safe.""" redirect_to = self.request.POST.get( - self.redirect_field_name, self.request.GET.get(self.redirect_field_name, "") + self.redirect_field_name, + self.request.GET.get(self.redirect_field_name, '') ) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) - return redirect_to if url_is_safe else "" - - def get_default_redirect_url(self): - """Return the default redirect URL.""" - return resolve_url(self.next_page or settings.LOGIN_REDIRECT_URL) + return redirect_to if url_is_safe else '' def get_form_class(self): return self.authentication_form or self.form_class def get_form_kwargs(self): kwargs = super().get_form_kwargs() - kwargs["request"] = self.request + kwargs['request'] = self.request return kwargs def form_valid(self, form): @@ -101,14 +95,12 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) current_site = get_current_site(self.request) - context.update( - { - self.redirect_field_name: self.get_redirect_url(), - "site": current_site, - "site_name": current_site.name, - **(self.extra_context or {}), - } - ) + context.update({ + self.redirect_field_name: self.get_redirect_url(), + 'site': current_site, + 'site_name': current_site.name, + **(self.extra_context or {}) + }) return context @@ -116,10 +108,9 @@ class LogoutView(SuccessURLAllowedHostsMixin, TemplateView): """ Log out the user and display the 'You are logged out' message. """ - next_page = None redirect_field_name = REDIRECT_FIELD_NAME - template_name = "registration/logged_out.html" + template_name = 'registration/logged_out.html' extra_context = None @method_decorator(never_cache) @@ -143,12 +134,11 @@ class LogoutView(SuccessURLAllowedHostsMixin, TemplateView): else: next_page = self.next_page - if ( - self.redirect_field_name in self.request.POST - or self.redirect_field_name in self.request.GET - ): + if (self.redirect_field_name in self.request.POST or + self.redirect_field_name in self.request.GET): next_page = self.request.POST.get( - self.redirect_field_name, self.request.GET.get(self.redirect_field_name) + self.redirect_field_name, + self.request.GET.get(self.redirect_field_name) ) url_is_safe = url_has_allowed_host_and_scheme( url=next_page, @@ -164,14 +154,12 @@ class LogoutView(SuccessURLAllowedHostsMixin, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) current_site = get_current_site(self.request) - context.update( - { - "site": current_site, - "site_name": current_site.name, - "title": _("Logged out"), - **(self.extra_context or {}), - } - ) + context.update({ + 'site': current_site, + 'site_name': current_site.name, + 'title': _('Logged out'), + **(self.extra_context or {}) + }) return context @@ -193,7 +181,7 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N if redirect_field_name: querystring = QueryDict(login_url_parts[4], mutable=True) querystring[redirect_field_name] = next - login_url_parts[4] = querystring.urlencode(safe="/") + login_url_parts[4] = querystring.urlencode(safe='/') return HttpResponseRedirect(urlunparse(login_url_parts)) @@ -205,26 +193,28 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N # prompts for a new password # - PasswordResetCompleteView shows a success message for the above - class PasswordContextMixin: extra_context = None def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context.update({"title": self.title, **(self.extra_context or {})}) + context.update({ + 'title': self.title, + **(self.extra_context or {}) + }) return context class PasswordResetView(PasswordContextMixin, FormView): - email_template_name = "registration/password_reset_email.html" + email_template_name = 'registration/password_reset_email.html' extra_email_context = None form_class = PasswordResetForm from_email = None html_email_template_name = None - subject_template_name = "registration/password_reset_subject.txt" - success_url = reverse_lazy("password_reset_done") - template_name = "registration/password_reset_form.html" - title = _("Password reset") + subject_template_name = 'registration/password_reset_subject.txt' + success_url = reverse_lazy('password_reset_done') + template_name = 'registration/password_reset_form.html' + title = _('Password reset') token_generator = default_token_generator @method_decorator(csrf_protect) @@ -233,50 +223,47 @@ class PasswordResetView(PasswordContextMixin, FormView): def form_valid(self, form): opts = { - "use_https": self.request.is_secure(), - "token_generator": self.token_generator, - "from_email": self.from_email, - "email_template_name": self.email_template_name, - "subject_template_name": self.subject_template_name, - "request": self.request, - "html_email_template_name": self.html_email_template_name, - "extra_email_context": self.extra_email_context, + 'use_https': self.request.is_secure(), + 'token_generator': self.token_generator, + 'from_email': self.from_email, + 'email_template_name': self.email_template_name, + 'subject_template_name': self.subject_template_name, + 'request': self.request, + 'html_email_template_name': self.html_email_template_name, + 'extra_email_context': self.extra_email_context, } form.save(**opts) return super().form_valid(form) -INTERNAL_RESET_SESSION_TOKEN = "_password_reset_token" +INTERNAL_RESET_SESSION_TOKEN = '_password_reset_token' class PasswordResetDoneView(PasswordContextMixin, TemplateView): - template_name = "registration/password_reset_done.html" - title = _("Password reset sent") + template_name = 'registration/password_reset_done.html' + title = _('Password reset sent') class PasswordResetConfirmView(PasswordContextMixin, FormView): form_class = SetPasswordForm post_reset_login = False post_reset_login_backend = None - reset_url_token = "set-password" - success_url = reverse_lazy("password_reset_complete") - template_name = "registration/password_reset_confirm.html" - title = _("Enter new password") + reset_url_token = 'set-password' + success_url = reverse_lazy('password_reset_complete') + template_name = 'registration/password_reset_confirm.html' + title = _('Enter new password') token_generator = default_token_generator @method_decorator(sensitive_post_parameters()) @method_decorator(never_cache) def dispatch(self, *args, **kwargs): - if "uidb64" not in kwargs or "token" not in kwargs: - raise ImproperlyConfigured( - "The URL path must contain 'uidb64' and 'token' parameters." - ) + assert 'uidb64' in kwargs and 'token' in kwargs self.validlink = False - self.user = self.get_user(kwargs["uidb64"]) + self.user = self.get_user(kwargs['uidb64']) if self.user is not None: - token = kwargs["token"] + token = kwargs['token'] if token == self.reset_url_token: session_token = self.request.session.get(INTERNAL_RESET_SESSION_TOKEN) if self.token_generator.check_token(self.user, session_token): @@ -290,9 +277,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): # avoids the possibility of leaking the token in the # HTTP Referer header. self.request.session[INTERNAL_RESET_SESSION_TOKEN] = token - redirect_url = self.request.path.replace( - token, self.reset_url_token - ) + redirect_url = self.request.path.replace(token, self.reset_url_token) return HttpResponseRedirect(redirect_url) # Display the "Password reset unsuccessful" page. @@ -303,19 +288,13 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): # urlsafe_base64_decode() decodes to bytestring uid = urlsafe_base64_decode(uidb64).decode() user = UserModel._default_manager.get(pk=uid) - except ( - TypeError, - ValueError, - OverflowError, - UserModel.DoesNotExist, - ValidationError, - ): + except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist, ValidationError): user = None return user def get_form_kwargs(self): kwargs = super().get_form_kwargs() - kwargs["user"] = self.user + kwargs['user'] = self.user return kwargs def form_valid(self, form): @@ -328,33 +307,31 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.validlink: - context["validlink"] = True + context['validlink'] = True else: - context.update( - { - "form": None, - "title": _("Password reset unsuccessful"), - "validlink": False, - } - ) + context.update({ + 'form': None, + 'title': _('Password reset unsuccessful'), + 'validlink': False, + }) return context class PasswordResetCompleteView(PasswordContextMixin, TemplateView): - template_name = "registration/password_reset_complete.html" - title = _("Password reset complete") + template_name = 'registration/password_reset_complete.html' + title = _('Password reset complete') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["login_url"] = resolve_url(settings.LOGIN_URL) + context['login_url'] = resolve_url(settings.LOGIN_URL) return context class PasswordChangeView(PasswordContextMixin, FormView): form_class = PasswordChangeForm - success_url = reverse_lazy("password_change_done") - template_name = "registration/password_change_form.html" - title = _("Password change") + success_url = reverse_lazy('password_change_done') + template_name = 'registration/password_change_form.html' + title = _('Password change') @method_decorator(sensitive_post_parameters()) @method_decorator(csrf_protect) @@ -364,7 +341,7 @@ class PasswordChangeView(PasswordContextMixin, FormView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() - kwargs["user"] = self.request.user + kwargs['user'] = self.request.user return kwargs def form_valid(self, form): @@ -376,8 +353,8 @@ class PasswordChangeView(PasswordContextMixin, FormView): class PasswordChangeDoneView(PasswordContextMixin, TemplateView): - template_name = "registration/password_change_done.html" - title = _("Password change successful") + template_name = 'registration/password_change_done.html' + title = _('Password change successful') @method_decorator(login_required) def dispatch(self, *args, **kwargs): diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/admin.py b/venv/Lib/site-packages/django/contrib/contenttypes/admin.py index 617d6d2..dd3e77c 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/admin.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/admin.py @@ -4,8 +4,7 @@ from django.contrib.admin.checks import InlineModelAdminChecks from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.forms import ( - BaseGenericInlineFormSet, - generic_inlineformset_factory, + BaseGenericInlineFormSet, generic_inlineformset_factory, ) from django.core import checks from django.core.exceptions import FieldDoesNotExist @@ -19,12 +18,11 @@ class GenericInlineModelAdminChecks(InlineModelAdminChecks): return [] def _check_relation(self, obj, parent_model): - # There's no FK, but we do need to confirm that the ct_field and - # ct_fk_field are valid, and that they are part of a GenericForeignKey. + # There's no FK, but we do need to confirm that the ct_field and ct_fk_field are valid, + # and that they are part of a GenericForeignKey. gfks = [ - f - for f in obj.model._meta.private_fields + f for f in obj.model._meta.private_fields if isinstance(f, GenericForeignKey) ] if not gfks: @@ -32,7 +30,7 @@ class GenericInlineModelAdminChecks(InlineModelAdminChecks): checks.Error( "'%s' has no GenericForeignKey." % obj.model._meta.label, obj=obj.__class__, - id="admin.E301", + id='admin.E301' ) ] else: @@ -42,13 +40,11 @@ class GenericInlineModelAdminChecks(InlineModelAdminChecks): except FieldDoesNotExist: return [ checks.Error( - "'ct_field' references '%s', which is not a field on '%s'." - % ( - obj.ct_field, - obj.model._meta.label, + "'ct_field' references '%s', which is not a field on '%s'." % ( + obj.ct_field, obj.model._meta.label, ), obj=obj.__class__, - id="admin.E302", + id='admin.E302' ) ] @@ -57,13 +53,11 @@ class GenericInlineModelAdminChecks(InlineModelAdminChecks): except FieldDoesNotExist: return [ checks.Error( - "'ct_fk_field' references '%s', which is not a field on '%s'." - % ( - obj.ct_fk_field, - obj.model._meta.label, + "'ct_fk_field' references '%s', which is not a field on '%s'." % ( + obj.ct_fk_field, obj.model._meta.label, ), obj=obj.__class__, - id="admin.E303", + id='admin.E303' ) ] @@ -75,15 +69,11 @@ class GenericInlineModelAdminChecks(InlineModelAdminChecks): return [ checks.Error( - "'%s' has no GenericForeignKey using content type field '%s' and " - "object ID field '%s'." - % ( - obj.model._meta.label, - obj.ct_field, - obj.ct_fk_field, + "'%s' has no GenericForeignKey using content type field '%s' and object ID field '%s'." % ( + obj.model._meta.label, obj.ct_field, obj.ct_fk_field, ), obj=obj.__class__, - id="admin.E304", + id='admin.E304' ) ] @@ -96,48 +86,42 @@ class GenericInlineModelAdmin(InlineModelAdmin): checks_class = GenericInlineModelAdminChecks def get_formset(self, request, obj=None, **kwargs): - if "fields" in kwargs: - fields = kwargs.pop("fields") + if 'fields' in kwargs: + fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) exclude = [*(self.exclude or []), *self.get_readonly_fields(request, obj)] - if ( - self.exclude is None - and hasattr(self.form, "_meta") - and self.form._meta.exclude - ): + if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # GenericInlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) defaults = { - "ct_field": self.ct_field, - "fk_field": self.ct_fk_field, - "form": self.form, - "formfield_callback": partial(self.formfield_for_dbfield, request=request), - "formset": self.formset, - "extra": self.get_extra(request, obj), - "can_delete": can_delete, - "can_order": False, - "fields": fields, - "min_num": self.get_min_num(request, obj), - "max_num": self.get_max_num(request, obj), - "exclude": exclude, + 'ct_field': self.ct_field, + 'fk_field': self.ct_fk_field, + 'form': self.form, + 'formfield_callback': partial(self.formfield_for_dbfield, request=request), + 'formset': self.formset, + 'extra': self.get_extra(request, obj), + 'can_delete': can_delete, + 'can_order': False, + 'fields': fields, + 'min_num': self.get_min_num(request, obj), + 'max_num': self.get_max_num(request, obj), + 'exclude': exclude, **kwargs, } - if defaults["fields"] is None and not modelform_defines_fields( - defaults["form"] - ): - defaults["fields"] = ALL_FIELDS + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = ALL_FIELDS return generic_inlineformset_factory(self.model, **defaults) class GenericStackedInline(GenericInlineModelAdmin): - template = "admin/edit_inline/stacked.html" + template = 'admin/edit_inline/stacked.html' class GenericTabularInline(GenericInlineModelAdmin): - template = "admin/edit_inline/tabular.html" + template = 'admin/edit_inline/tabular.html' diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/apps.py b/venv/Lib/site-packages/django/contrib/contenttypes/apps.py index 11dfb91..390afb3 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/apps.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/apps.py @@ -1,18 +1,19 @@ from django.apps import AppConfig from django.contrib.contenttypes.checks import ( - check_generic_foreign_keys, - check_model_name_lengths, + check_generic_foreign_keys, check_model_name_lengths, ) from django.core import checks from django.db.models.signals import post_migrate, pre_migrate from django.utils.translation import gettext_lazy as _ -from .management import create_contenttypes, inject_rename_contenttypes_operations +from .management import ( + create_contenttypes, inject_rename_contenttypes_operations, +) class ContentTypesConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.contenttypes" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.contenttypes' verbose_name = _("Content Types") def ready(self): diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/checks.py b/venv/Lib/site-packages/django/contrib/contenttypes/checks.py index 753c5d2..3e802ea 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/checks.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/checks.py @@ -10,14 +10,10 @@ def check_generic_foreign_keys(app_configs=None, **kwargs): if app_configs is None: models = apps.get_models() else: - models = chain.from_iterable( - app_config.get_models() for app_config in app_configs - ) + models = chain.from_iterable(app_config.get_models() for app_config in app_configs) errors = [] fields = ( - obj - for model in models - for obj in vars(model).values() + obj for model in models for obj in vars(model).values() if isinstance(obj, GenericForeignKey) ) for field in fields: @@ -29,18 +25,17 @@ def check_model_name_lengths(app_configs=None, **kwargs): if app_configs is None: models = apps.get_models() else: - models = chain.from_iterable( - app_config.get_models() for app_config in app_configs - ) + models = chain.from_iterable(app_config.get_models() for app_config in app_configs) errors = [] for model in models: if len(model._meta.model_name) > 100: errors.append( Error( - "Model names must be at most 100 characters (got %d)." - % (len(model._meta.model_name),), + 'Model names must be at most 100 characters (got %d).' % ( + len(model._meta.model_name), + ), obj=model, - id="contenttypes.E005", + id='contenttypes.E005', ) ) return errors diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/fields.py b/venv/Lib/site-packages/django/contrib/contenttypes/fields.py index 338562e..4aa3dda 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/fields.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/fields.py @@ -1,5 +1,6 @@ import functools import itertools +import operator from collections import defaultdict from django.contrib.contenttypes.models import ContentType @@ -10,12 +11,9 @@ from django.db.models import DO_NOTHING, ForeignObject, ForeignObjectRel from django.db.models.base import ModelBase, make_foreign_order_accessors from django.db.models.fields.mixins import FieldCacheMixin from django.db.models.fields.related import ( - ReverseManyToOneDescriptor, - lazy_related_operation, + ReverseManyToOneDescriptor, lazy_related_operation, ) from django.db.models.query_utils import PathInfo -from django.db.models.sql import AND -from django.db.models.sql.where import WhereNode from django.utils.functional import cached_property @@ -42,9 +40,7 @@ class GenericForeignKey(FieldCacheMixin): related_model = None remote_field = None - def __init__( - self, ct_field="content_type", fk_field="object_id", for_concrete_model=True - ): + def __init__(self, ct_field='content_type', fk_field='object_id', for_concrete_model=True): self.ct_field = ct_field self.fk_field = fk_field self.for_concrete_model = for_concrete_model @@ -74,7 +70,7 @@ class GenericForeignKey(FieldCacheMixin): def __str__(self): model = self.model - return "%s.%s" % (model._meta.label, self.name) + return '%s.%s' % (model._meta.label, self.name) def check(self, **kwargs): return [ @@ -87,9 +83,9 @@ class GenericForeignKey(FieldCacheMixin): if self.name.endswith("_"): return [ checks.Error( - "Field names must not end with an underscore.", + 'Field names must not end with an underscore.', obj=self, - id="fields.E001", + id='fields.E001', ) ] else: @@ -104,7 +100,7 @@ class GenericForeignKey(FieldCacheMixin): "The GenericForeignKey object ID references the " "nonexistent field '%s'." % self.fk_field, obj=self, - id="contenttypes.E001", + id='contenttypes.E001', ) ] else: @@ -121,37 +117,40 @@ class GenericForeignKey(FieldCacheMixin): return [ checks.Error( "The GenericForeignKey content type references the " - "nonexistent field '%s.%s'." - % (self.model._meta.object_name, self.ct_field), + "nonexistent field '%s.%s'." % ( + self.model._meta.object_name, self.ct_field + ), obj=self, - id="contenttypes.E002", + id='contenttypes.E002', ) ] else: if not isinstance(field, models.ForeignKey): return [ checks.Error( - "'%s.%s' is not a ForeignKey." - % (self.model._meta.object_name, self.ct_field), + "'%s.%s' is not a ForeignKey." % ( + self.model._meta.object_name, self.ct_field + ), hint=( "GenericForeignKeys must use a ForeignKey to " "'contenttypes.ContentType' as the 'content_type' field." ), obj=self, - id="contenttypes.E003", + id='contenttypes.E003', ) ] elif field.remote_field.model != ContentType: return [ checks.Error( - "'%s.%s' is not a ForeignKey to 'contenttypes.ContentType'." - % (self.model._meta.object_name, self.ct_field), + "'%s.%s' is not a ForeignKey to 'contenttypes.ContentType'." % ( + self.model._meta.object_name, self.ct_field + ), hint=( "GenericForeignKeys must use a ForeignKey to " "'contenttypes.ContentType' as the 'content_type' field." ), obj=self, - id="contenttypes.E004", + id='contenttypes.E004', ) ] else: @@ -163,8 +162,7 @@ class GenericForeignKey(FieldCacheMixin): def get_content_type(self, obj=None, id=None, using=None): if obj is not None: return ContentType.objects.db_manager(obj._state.db).get_for_model( - obj, for_concrete_model=self.for_concrete_model - ) + obj, for_concrete_model=self.for_concrete_model) elif id is not None: return ContentType.objects.db_manager(using).get_for_id(id) else: @@ -203,13 +201,10 @@ class GenericForeignKey(FieldCacheMixin): if ct_id is None: return None else: - model = self.get_content_type( - id=ct_id, using=obj._state.db - ).model_class() - return ( - model._meta.pk.get_prep_value(getattr(obj, self.fk_field)), - model, - ) + model = self.get_content_type(id=ct_id, + using=obj._state.db).model_class() + return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)), + model) return ( ret_val, @@ -217,7 +212,7 @@ class GenericForeignKey(FieldCacheMixin): gfk_key, True, self.name, - False, + True, ) def __get__(self, instance, cls=None): @@ -233,12 +228,8 @@ class GenericForeignKey(FieldCacheMixin): pk_val = getattr(instance, self.fk_field) rel_obj = self.get_cached_value(instance, default=None) - if rel_obj is None and self.is_cached(instance): - return rel_obj if rel_obj is not None: - ct_match = ( - ct_id == self.get_content_type(obj=rel_obj, using=instance._state.db).id - ) + ct_match = ct_id == self.get_content_type(obj=rel_obj, using=instance._state.db).id pk_match = rel_obj._meta.pk.to_python(pk_val) == rel_obj.pk if ct_match and pk_match: return rel_obj @@ -270,21 +261,11 @@ class GenericRel(ForeignObjectRel): Used by GenericRelation to store information about the relation. """ - def __init__( - self, - field, - to, - related_name=None, - related_query_name=None, - limit_choices_to=None, - ): + def __init__(self, field, to, related_name=None, related_query_name=None, limit_choices_to=None): super().__init__( - field, - to, - related_name=related_query_name or "+", + field, to, related_name=related_query_name or '+', related_query_name=related_query_name, - limit_choices_to=limit_choices_to, - on_delete=DO_NOTHING, + limit_choices_to=limit_choices_to, on_delete=DO_NOTHING, ) @@ -306,30 +287,21 @@ class GenericRelation(ForeignObject): mti_inherited = False - def __init__( - self, - to, - object_id_field="object_id", - content_type_field="content_type", - for_concrete_model=True, - related_query_name=None, - limit_choices_to=None, - **kwargs, - ): - kwargs["rel"] = self.rel_class( - self, - to, + def __init__(self, to, object_id_field='object_id', content_type_field='content_type', + for_concrete_model=True, related_query_name=None, limit_choices_to=None, **kwargs): + kwargs['rel'] = self.rel_class( + self, to, related_query_name=related_query_name, limit_choices_to=limit_choices_to, ) # Reverse relations are always nullable (Django can't enforce that a # foreign key on the related model points to this model). - kwargs["null"] = True - kwargs["blank"] = True - kwargs["on_delete"] = models.CASCADE - kwargs["editable"] = False - kwargs["serialize"] = False + kwargs['null'] = True + kwargs['blank'] = True + kwargs['on_delete'] = models.CASCADE + kwargs['editable'] = False + kwargs['serialize'] = False # This construct is somewhat of an abuse of ForeignObject. This field # represents a relation from pk to object_id field. But, this relation @@ -355,9 +327,9 @@ class GenericRelation(ForeignObject): GenericRelation. """ return ( - isinstance(field, GenericForeignKey) - and field.ct_field == self.content_type_field_name - and field.fk_field == self.object_id_field_name + isinstance(field, GenericForeignKey) and + field.ct_field == self.content_type_field_name and + field.fk_field == self.object_id_field_name ) def _check_generic_foreign_key_existence(self): @@ -373,7 +345,7 @@ class GenericRelation(ForeignObject): "'%s', but that model does not have a GenericForeignKey." % target._meta.label, obj=self, - id="contenttypes.E004", + id='contenttypes.E004', ) ] else: @@ -381,12 +353,7 @@ class GenericRelation(ForeignObject): def resolve_related_fields(self): self.to_fields = [self.model._meta.pk.name] - return [ - ( - self.remote_field.model._meta.get_field(self.object_id_field_name), - self.model._meta.pk, - ) - ] + return [(self.remote_field.model._meta.get_field(self.object_id_field_name), self.model._meta.pk)] def _get_path_info_with_parent(self, filtered_relation): """ @@ -405,17 +372,15 @@ class GenericRelation(ForeignObject): opts = self.remote_field.model._meta.concrete_model._meta parent_opts = opts.get_field(self.object_id_field_name).model._meta target = parent_opts.pk - path.append( - PathInfo( - from_opts=self.model._meta, - to_opts=parent_opts, - target_fields=(target,), - join_field=self.remote_field, - m2m=True, - direct=False, - filtered_relation=filtered_relation, - ) - ) + path.append(PathInfo( + from_opts=self.model._meta, + to_opts=parent_opts, + target_fields=(target,), + join_field=self.remote_field, + m2m=True, + direct=False, + filtered_relation=filtered_relation, + )) # Collect joins needed for the parent -> child chain. This is easiest # to do if we collect joins for the child -> parent chain and then # reverse the direction (call to reverse() and use of @@ -437,46 +402,42 @@ class GenericRelation(ForeignObject): return self._get_path_info_with_parent(filtered_relation) else: target = opts.pk - return [ - PathInfo( - from_opts=self.model._meta, - to_opts=opts, - target_fields=(target,), - join_field=self.remote_field, - m2m=True, - direct=False, - filtered_relation=filtered_relation, - ) - ] + return [PathInfo( + from_opts=self.model._meta, + to_opts=opts, + target_fields=(target,), + join_field=self.remote_field, + m2m=True, + direct=False, + filtered_relation=filtered_relation, + )] def get_reverse_path_info(self, filtered_relation=None): opts = self.model._meta from_opts = self.remote_field.model._meta - return [ - PathInfo( - from_opts=from_opts, - to_opts=opts, - target_fields=(opts.pk,), - join_field=self, - m2m=not self.unique, - direct=False, - filtered_relation=filtered_relation, - ) - ] + return [PathInfo( + from_opts=from_opts, + to_opts=opts, + target_fields=(opts.pk,), + join_field=self, + m2m=not self.unique, + direct=False, + filtered_relation=filtered_relation, + )] def value_to_string(self, obj): qs = getattr(obj, self.name).all() return str([instance.pk for instance in qs]) def contribute_to_class(self, cls, name, **kwargs): - kwargs["private_only"] = True + kwargs['private_only'] = True super().contribute_to_class(cls, name, **kwargs) self.model = cls # Disable the reverse relation for fields inherited by subclasses of a # model in multi-table inheritance. The reverse relation points to the # field of the base model. if self.mti_inherited: - self.remote_field.related_name = "+" + self.remote_field.related_name = '+' self.remote_field.related_query_name = None setattr(cls, self.name, ReverseGenericManyToOneDescriptor(self.remote_field)) @@ -486,16 +447,10 @@ class GenericRelation(ForeignObject): if not cls._meta.abstract: def make_generic_foreign_order_accessors(related_model, model): - if self._is_matching_generic_foreign_key( - model._meta.order_with_respect_to - ): + if self._is_matching_generic_foreign_key(model._meta.order_with_respect_to): make_foreign_order_accessors(model, related_model) - lazy_related_operation( - make_generic_foreign_order_accessors, - self.model, - self.remote_field.model, - ) + lazy_related_operation(make_generic_foreign_order_accessors, self.model, self.remote_field.model) def set_attributes_from_rel(self): pass @@ -507,29 +462,26 @@ class GenericRelation(ForeignObject): """ Return the content type associated with this field's model. """ - return ContentType.objects.get_for_model( - self.model, for_concrete_model=self.for_concrete_model - ) + return ContentType.objects.get_for_model(self.model, + for_concrete_model=self.for_concrete_model) - def get_extra_restriction(self, alias, remote_alias): + def get_extra_restriction(self, where_class, alias, remote_alias): field = self.remote_field.model._meta.get_field(self.content_type_field_name) contenttype_pk = self.get_content_type().pk - lookup = field.get_lookup("exact")(field.get_col(remote_alias), contenttype_pk) - return WhereNode([lookup], connector=AND) + cond = where_class() + lookup = field.get_lookup('exact')(field.get_col(remote_alias), contenttype_pk) + cond.add(lookup, 'AND') + return cond def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS): """ Return all objects related to ``objs`` via this ``GenericRelation``. """ - return self.remote_field.model._base_manager.db_manager(using).filter( - **{ - "%s__pk" - % self.content_type_field_name: ContentType.objects.db_manager(using) - .get_for_model(self.model, for_concrete_model=self.for_concrete_model) - .pk, - "%s__in" % self.object_id_field_name: [obj.pk for obj in objs], - } - ) + return self.remote_field.model._base_manager.db_manager(using).filter(**{ + "%s__pk" % self.content_type_field_name: ContentType.objects.db_manager(using).get_for_model( + self.model, for_concrete_model=self.for_concrete_model).pk, + "%s__in" % self.object_id_field_name: [obj.pk for obj in objs] + }) class ReverseGenericManyToOneDescriptor(ReverseManyToOneDescriptor): @@ -578,7 +530,7 @@ def create_generic_related_manager(superclass, rel): self.pk_val = instance.pk self.core_filters = { - "%s__pk" % self.content_type_field_name: self.content_type.id, + '%s__pk' % self.content_type_field_name: self.content_type.id, self.object_id_field_name: self.pk_val, } @@ -586,7 +538,6 @@ def create_generic_related_manager(superclass, rel): manager = getattr(self.model, manager) manager_class = create_generic_related_manager(manager.__class__, rel) return manager_class(instance=self.instance) - do_not_call_in_templates = True def __str__(self): @@ -620,20 +571,20 @@ def create_generic_related_manager(superclass, rel): queryset = queryset.using(queryset._db or self._db) # Group instances by content types. content_type_queries = ( - models.Q( - (f"{self.content_type_field_name}__pk", content_type_id), - (f"{self.object_id_field_name}__in", {obj.pk for obj in objs}), - ) + models.Q(**{ + '%s__pk' % self.content_type_field_name: content_type_id, + '%s__in' % self.object_id_field_name: {obj.pk for obj in objs} + }) for content_type_id, objs in itertools.groupby( sorted(instances, key=lambda obj: self.get_content_type(obj).pk), lambda obj: self.get_content_type(obj).pk, ) ) - query = models.Q(*content_type_queries, _connector=models.Q.OR) + query = functools.reduce(operator.or_, content_type_queries) # We (possibly) need to convert object IDs to the type of the # instances' PK in order to match up instances: object_id_converter = instances[0]._meta.pk.to_python - content_type_id_field_name = "%s_id" % self.content_type_field_name + content_type_id_field_name = '%s_id' % self.content_type_field_name return ( queryset.filter(query), lambda relobj: ( @@ -652,10 +603,9 @@ def create_generic_related_manager(superclass, rel): def check_and_update_obj(obj): if not isinstance(obj, self.model): - raise TypeError( - "'%s' instance expected, got %r" - % (self.model._meta.object_name, obj) - ) + raise TypeError("'%s' instance expected, got %r" % ( + self.model._meta.object_name, obj + )) setattr(obj, self.content_type_field_name, self.content_type) setattr(obj, self.object_id_field_name, self.pk_val) @@ -670,30 +620,25 @@ def create_generic_related_manager(superclass, rel): check_and_update_obj(obj) pks.append(obj.pk) - self.model._base_manager.using(db).filter(pk__in=pks).update( - **{ - self.content_type_field_name: self.content_type, - self.object_id_field_name: self.pk_val, - } - ) + self.model._base_manager.using(db).filter(pk__in=pks).update(**{ + self.content_type_field_name: self.content_type, + self.object_id_field_name: self.pk_val, + }) else: with transaction.atomic(using=db, savepoint=False): for obj in objs: check_and_update_obj(obj) obj.save() - add.alters_data = True def remove(self, *objs, bulk=True): if not objs: return self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk) - remove.alters_data = True def clear(self, *, bulk=True): self._clear(self, bulk) - clear.alters_data = True def _clear(self, queryset, bulk): @@ -708,7 +653,6 @@ def create_generic_related_manager(superclass, rel): with transaction.atomic(using=db, savepoint=False): for obj in queryset: obj.delete() - _clear.alters_data = True def set(self, objs, *, bulk=True, clear=False): @@ -732,7 +676,6 @@ def create_generic_related_manager(superclass, rel): self.remove(*old_objs) self.add(*new_objs, bulk=bulk) - set.alters_data = True def create(self, **kwargs): @@ -741,7 +684,6 @@ def create_generic_related_manager(superclass, rel): kwargs[self.object_id_field_name] = self.pk_val db = router.db_for_write(self.model, instance=self.instance) return super().using(db).create(**kwargs) - create.alters_data = True def get_or_create(self, **kwargs): @@ -749,7 +691,6 @@ def create_generic_related_manager(superclass, rel): kwargs[self.object_id_field_name] = self.pk_val db = router.db_for_write(self.model, instance=self.instance) return super().using(db).get_or_create(**kwargs) - get_or_create.alters_data = True def update_or_create(self, **kwargs): @@ -757,7 +698,6 @@ def create_generic_related_manager(superclass, rel): kwargs[self.object_id_field_name] = self.pk_val db = router.db_for_write(self.model, instance=self.instance) return super().using(db).update_or_create(**kwargs) - update_or_create.alters_data = True return GenericRelatedObjectManager diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/forms.py b/venv/Lib/site-packages/django/contrib/contenttypes/forms.py index c0ff4f7..92a58d4 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/forms.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/forms.py @@ -9,26 +9,13 @@ class BaseGenericInlineFormSet(BaseModelFormSet): A formset for generic inline objects to a parent. """ - def __init__( - self, - data=None, - files=None, - instance=None, - save_as_new=False, - prefix=None, - queryset=None, - **kwargs, - ): + def __init__(self, data=None, files=None, instance=None, save_as_new=False, + prefix=None, queryset=None, **kwargs): opts = self.model._meta self.instance = instance self.rel_name = ( - opts.app_label - + "-" - + opts.model_name - + "-" - + self.ct_field.name - + "-" - + self.ct_fk_field.name + opts.app_label + '-' + opts.model_name + '-' + + self.ct_field.name + '-' + self.ct_fk_field.name ) self.save_as_new = save_as_new if self.instance is None or self.instance.pk is None: @@ -36,14 +23,11 @@ class BaseGenericInlineFormSet(BaseModelFormSet): else: if queryset is None: queryset = self.model._default_manager - qs = queryset.filter( - **{ - self.ct_field.name: ContentType.objects.get_for_model( - self.instance, for_concrete_model=self.for_concrete_model - ), - self.ct_fk_field.name: self.instance.pk, - } - ) + qs = queryset.filter(**{ + self.ct_field.name: ContentType.objects.get_for_model( + self.instance, for_concrete_model=self.for_concrete_model), + self.ct_fk_field.name: self.instance.pk, + }) super().__init__(queryset=qs, data=data, files=files, prefix=prefix, **kwargs) def initial_form_count(self): @@ -55,45 +39,25 @@ class BaseGenericInlineFormSet(BaseModelFormSet): def get_default_prefix(cls): opts = cls.model._meta return ( - opts.app_label - + "-" - + opts.model_name - + "-" - + cls.ct_field.name - + "-" - + cls.ct_fk_field.name + opts.app_label + '-' + opts.model_name + '-' + + cls.ct_field.name + '-' + cls.ct_fk_field.name ) def save_new(self, form, commit=True): - setattr( - form.instance, - self.ct_field.get_attname(), - ContentType.objects.get_for_model(self.instance).pk, - ) + setattr(form.instance, self.ct_field.get_attname(), ContentType.objects.get_for_model(self.instance).pk) setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk) return form.save(commit=commit) -def generic_inlineformset_factory( - model, - form=ModelForm, - formset=BaseGenericInlineFormSet, - ct_field="content_type", - fk_field="object_id", - fields=None, - exclude=None, - extra=3, - can_order=False, - can_delete=True, - max_num=None, - formfield_callback=None, - validate_max=False, - for_concrete_model=True, - min_num=None, - validate_min=False, - absolute_max=None, - can_delete_extra=True, -): +def generic_inlineformset_factory(model, form=ModelForm, + formset=BaseGenericInlineFormSet, + ct_field="content_type", fk_field="object_id", + fields=None, exclude=None, + extra=3, can_order=False, can_delete=True, + max_num=None, formfield_callback=None, + validate_max=False, for_concrete_model=True, + min_num=None, validate_min=False, + absolute_max=None, can_delete_extra=True): """ Return a ``GenericInlineFormSet`` for the given kwargs. @@ -103,29 +67,16 @@ def generic_inlineformset_factory( opts = model._meta # if there is no field called `ct_field` let the exception propagate ct_field = opts.get_field(ct_field) - if ( - not isinstance(ct_field, models.ForeignKey) - or ct_field.remote_field.model != ContentType - ): + if not isinstance(ct_field, models.ForeignKey) or ct_field.remote_field.model != ContentType: raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field) fk_field = opts.get_field(fk_field) # let the exception propagate exclude = [*(exclude or []), ct_field.name, fk_field.name] FormSet = modelformset_factory( - model, - form=form, - formfield_callback=formfield_callback, - formset=formset, - extra=extra, - can_delete=can_delete, - can_order=can_order, - fields=fields, - exclude=exclude, - max_num=max_num, - validate_max=validate_max, - min_num=min_num, - validate_min=validate_min, - absolute_max=absolute_max, - can_delete_extra=can_delete_extra, + model, form=form, formfield_callback=formfield_callback, + formset=formset, extra=extra, can_delete=can_delete, + can_order=can_order, fields=fields, exclude=exclude, max_num=max_num, + validate_max=validate_max, min_num=min_num, validate_min=validate_min, + absolute_max=absolute_max, can_delete_extra=can_delete_extra, ) FormSet.ct_field = ct_field FormSet.ct_fk_field = fk_field diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo index 20e9b66..a861e2f 100644 Binary files a/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po index 9169509..ac56556 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/contenttypes/locale/bg/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Boris Chervenkov <office@sentido.bg>, 2012 # Georgi Kostadinov <grgkostadinov@gmail.com>, 2012 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -10,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2022-01-13 18:20+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -38,9 +37,9 @@ msgid "Content type %(ct_id)s object has no associated model" msgstr "Обект с тип на съдържанието %(ct_id)s няма асоцииран модел." #, python-format -msgid "Content type %(ct_id)s object %(obj_id)s doesn’t exist" +msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist" msgstr "Обект %(obj_id)s с тип на съдържанието %(ct_id)s не съществува." #, python-format -msgid "%(ct_name)s objects don’t have a get_absolute_url() method" +msgid "%(ct_name)s objects don't have a get_absolute_url() method" msgstr "%(ct_name)s обекти нямат метода get_absolute_url()" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po index e2f1396..6c5c821 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Content Types" msgstr "კონტენტის ტიპები" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po index d4bde11..10e5ae6 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/contenttypes/locale/kn/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Content Types" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 3d28522..0000000 Binary files a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 1aa74e0..0000000 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,41 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-11-16 12:42+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Content Types" -msgstr "Jenis kandungan" - -msgid "python model class name" -msgstr "nama kelas model python" - -msgid "content type" -msgstr "jenis kandungan" - -msgid "content types" -msgstr "jenis-jenis kandungan" - -#, python-format -msgid "Content type %(ct_id)s object has no associated model" -msgstr "Jenis kandungan %(ct_id)s tidak mempunyai model yang berkaitan" - -#, python-format -msgid "Content type %(ct_id)s object %(obj_id)s doesn’t exist" -msgstr "Jenis kandungan %(ct_id)s objek %(obj_id)s tidak wujud" - -#, python-format -msgid "%(ct_name)s objects don’t have a get_absolute_url() method" -msgstr "Objek-objek %(ct_name)s tidak mempunyai kaedah get_absolute_url()" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo index c60510c..5f2645f 100644 Binary files a/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po index c441b43..bfcaea4 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/contenttypes/locale/nn/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # jensadne <jensadne@pvv.ntnu.no>, 2013 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-10-21 18:49+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -20,7 +19,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Content Types" -msgstr "Innhaldstypar" +msgstr "" msgid "python model class name" msgstr "python-modell klassenamn" @@ -36,9 +35,9 @@ msgid "Content type %(ct_id)s object has no associated model" msgstr "Innhaldstype %(ct_id)s-objektet har ingen modell knytta til seg" #, python-format -msgid "Content type %(ct_id)s object %(obj_id)s doesn’t exist" -msgstr "Innhaldstype %(ct_id)s-objekt %(obj_id)s eksisterer ikkje" +msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist" +msgstr "Innhaldstype %(ct_id)s-objektet med id %(obj_id)s finst ikkje" #, python-format -msgid "%(ct_name)s objects don’t have a get_absolute_url() method" -msgstr "%(ct_name)s-objekt har ikkje ein get_absolute_url() metode" +msgid "%(ct_name)s objects don't have a get_absolute_url() method" +msgstr "%(ct_name)s-objekt har ingen get_absolute_url()-metode" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo index 5ce1a4d..ef659ef 100644 Binary files a/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po index 9ebe055..bc9b33b 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/contenttypes/locale/sk/LC_MESSAGES/django.po @@ -4,14 +4,13 @@ # Jannis Leidel <jannis@leidel.info>, 2011 # Marian Andre <marian@andre.sk>, 2012 # Martin Tóth <ezimir@gmail.com>, 2017 -# Peter Kuma, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-07-24 21:06+0000\n" -"Last-Translator: Peter Kuma\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Martin Tóth <ezimir@gmail.com>\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -37,9 +36,9 @@ msgid "Content type %(ct_id)s object has no associated model" msgstr "Objekt typu obsahu %(ct_id)s nemá pridružený model" #, python-format -msgid "Content type %(ct_id)s object %(obj_id)s doesn’t exist" -msgstr "Typ obsahu %(ct_id)s objekt %(obj_id)s neexistuje" +msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist" +msgstr "Objekt %(obj_id)s typu obsahu %(ct_id)s neexistuje" #, python-format -msgid "%(ct_name)s objects don’t have a get_absolute_url() method" -msgstr " Objekty %(ct_name)s nemajú metódu get_absolute_url()" +msgid "%(ct_name)s objects don't have a get_absolute_url() method" +msgstr " Objekty %(ct_name)s neobsahujú metódu get_absolute_url()" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/management/__init__.py b/venv/Lib/site-packages/django/contrib/contenttypes/management/__init__.py index 903b9ab..4971fa0 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/management/__init__.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/management/__init__.py @@ -1,5 +1,7 @@ from django.apps import apps as global_apps -from django.db import DEFAULT_DB_ALIAS, IntegrityError, migrations, router, transaction +from django.db import ( + DEFAULT_DB_ALIAS, IntegrityError, migrations, router, transaction, +) class RenameContentType(migrations.RunPython): @@ -10,22 +12,20 @@ class RenameContentType(migrations.RunPython): super().__init__(self.rename_forward, self.rename_backward) def _rename(self, apps, schema_editor, old_model, new_model): - ContentType = apps.get_model("contenttypes", "ContentType") + ContentType = apps.get_model('contenttypes', 'ContentType') db = schema_editor.connection.alias if not router.allow_migrate_model(db, ContentType): return try: - content_type = ContentType.objects.db_manager(db).get_by_natural_key( - self.app_label, old_model - ) + content_type = ContentType.objects.db_manager(db).get_by_natural_key(self.app_label, old_model) except ContentType.DoesNotExist: pass else: content_type.model = new_model try: with transaction.atomic(using=db): - content_type.save(using=db, update_fields={"model"}) + content_type.save(using=db, update_fields={'model'}) except IntegrityError: # Gracefully fallback if a stale content type causes a # conflict as remove_stale_contenttypes will take care of @@ -43,9 +43,7 @@ class RenameContentType(migrations.RunPython): self._rename(apps, schema_editor, self.new_model, self.old_model) -def inject_rename_contenttypes_operations( - plan=None, apps=global_apps, using=DEFAULT_DB_ALIAS, **kwargs -): +def inject_rename_contenttypes_operations(plan=None, apps=global_apps, using=DEFAULT_DB_ALIAS, **kwargs): """ Insert a `RenameContentType` operation after every planned `RenameModel` operation. @@ -55,7 +53,7 @@ def inject_rename_contenttypes_operations( # Determine whether or not the ContentType model is available. try: - ContentType = apps.get_model("contenttypes", "ContentType") + ContentType = apps.get_model('contenttypes', 'ContentType') except LookupError: available = False else: @@ -64,7 +62,7 @@ def inject_rename_contenttypes_operations( available = True for migration, backward in plan: - if (migration.app_label, migration.name) == ("contenttypes", "0001_initial"): + if (migration.app_label, migration.name) == ('contenttypes', '0001_initial'): # There's no point in going forward if the initial contenttypes # migration is unapplied as the ContentType model will be # unavailable from this point. @@ -80,9 +78,7 @@ def inject_rename_contenttypes_operations( for index, operation in enumerate(migration.operations): if isinstance(operation, migrations.RenameModel): operation = RenameContentType( - migration.app_label, - operation.old_name_lower, - operation.new_name_lower, + migration.app_label, operation.old_name_lower, operation.new_name_lower ) inserts.append((index + 1, operation)) for inserted, (index, operation) in enumerate(inserts): @@ -99,18 +95,14 @@ def get_contenttypes_and_models(app_config, using, ContentType): ct.model: ct for ct in ContentType.objects.using(using).filter(app_label=app_config.label) } - app_models = {model._meta.model_name: model for model in app_config.get_models()} + app_models = { + model._meta.model_name: model + for model in app_config.get_models() + } return content_types, app_models -def create_contenttypes( - app_config, - verbosity=2, - interactive=True, - using=DEFAULT_DB_ALIAS, - apps=global_apps, - **kwargs, -): +def create_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs): """ Create content types for models in the given app. """ @@ -120,13 +112,11 @@ def create_contenttypes( app_label = app_config.label try: app_config = apps.get_app_config(app_label) - ContentType = apps.get_model("contenttypes", "ContentType") + ContentType = apps.get_model('contenttypes', 'ContentType') except LookupError: return - content_types, app_models = get_contenttypes_and_models( - app_config, using, ContentType - ) + content_types, app_models = get_contenttypes_and_models(app_config, using, ContentType) if not app_models: return diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py b/venv/Lib/site-packages/django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py index aa42d01..e2856bb 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py @@ -8,23 +8,18 @@ from django.db.models.deletion import Collector class Command(BaseCommand): + def add_arguments(self, parser): parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, + '--database', default=DEFAULT_DB_ALIAS, help='Nominates the database to use. Defaults to the "default" database.', ) parser.add_argument( - "--include-stale-apps", - action="store_true", - default=False, + '--include-stale-apps', action='store_true', default=False, help=( "Deletes stale content types including ones from previously " "installed apps that have been removed from INSTALLED_APPS." @@ -32,17 +27,17 @@ class Command(BaseCommand): ) def handle(self, **options): - db = options["database"] - include_stale_apps = options["include_stale_apps"] - interactive = options["interactive"] - verbosity = options["verbosity"] + db = options['database'] + include_stale_apps = options['include_stale_apps'] + interactive = options['interactive'] + verbosity = options['verbosity'] if not router.allow_migrate_model(db, ContentType): return ContentType.objects.clear_cache() apps_content_types = itertools.groupby( - ContentType.objects.using(db).order_by("app_label", "model"), + ContentType.objects.using(db).order_by('app_label', 'model'), lambda obj: obj.app_label, ) for app_label, content_types in apps_content_types: @@ -55,24 +50,18 @@ class Command(BaseCommand): if interactive: ct_info = [] for ct in to_remove: - ct_info.append( - " - Content type for %s.%s" % (ct.app_label, ct.model) - ) + ct_info.append(' - Content type for %s.%s' % (ct.app_label, ct.model)) collector = NoFastDeleteCollector(using=using) collector.collect([ct]) for obj_type, objs in collector.data.items(): if objs != {ct}: - ct_info.append( - " - %s %s object(s)" - % ( - len(objs), - obj_type._meta.label, - ) - ) - content_type_display = "\n".join(ct_info) - self.stdout.write( - """Some content types in your database are stale and can be deleted. + ct_info.append(' - %s %s object(s)' % ( + len(objs), + obj_type._meta.label, + )) + content_type_display = '\n'.join(ct_info) + self.stdout.write("""Some content types in your database are stale and can be deleted. Any objects that depend on these content types will also be deleted. The content types and dependent objects that would be deleted are: @@ -82,20 +71,15 @@ This list doesn't include any cascade deletions to data outside of Django's models (uncommon). Are you sure you want to delete these content types? -If you're unsure, answer 'no'.""" - % content_type_display - ) +If you're unsure, answer 'no'.""" % content_type_display) ok_to_delete = input("Type 'yes' to continue, or 'no' to cancel: ") else: - ok_to_delete = "yes" + ok_to_delete = 'yes' - if ok_to_delete == "yes": + if ok_to_delete == 'yes': for ct in to_remove: if verbosity >= 2: - self.stdout.write( - "Deleting stale content type '%s | %s'" - % (ct.app_label, ct.model) - ) + self.stdout.write("Deleting stale content type '%s | %s'" % (ct.app_label, ct.model)) ct.delete() else: if verbosity >= 2: diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0001_initial.py index 5468fb6..e55c320 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0001_initial.py @@ -4,43 +4,31 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [] + dependencies = [ + ] operations = [ migrations.CreateModel( - name="ContentType", + name='ContentType', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ("name", models.CharField(max_length=100)), - ("app_label", models.CharField(max_length=100)), - ( - "model", - models.CharField( - max_length=100, verbose_name="python model class name" - ), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(max_length=100)), + ('app_label', models.CharField(max_length=100)), + ('model', models.CharField(max_length=100, verbose_name='python model class name')), ], options={ - "ordering": ("name",), - "db_table": "django_content_type", - "verbose_name": "content type", - "verbose_name_plural": "content types", + 'ordering': ('name',), + 'db_table': 'django_content_type', + 'verbose_name': 'content type', + 'verbose_name_plural': 'content types', }, bases=(models.Model,), managers=[ - ("objects", django.contrib.contenttypes.models.ContentTypeManager()), + ('objects', django.contrib.contenttypes.models.ContentTypeManager()), ], ), migrations.AlterUniqueTogether( - name="contenttype", - unique_together={("app_label", "model")}, + name='contenttype', + unique_together={('app_label', 'model')}, ), ] diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0002_remove_content_type_name.py b/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0002_remove_content_type_name.py index 3bee3a8..c88e603 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0002_remove_content_type_name.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/migrations/0002_remove_content_type_name.py @@ -2,7 +2,7 @@ from django.db import migrations, models def add_legacy_name(apps, schema_editor): - ContentType = apps.get_model("contenttypes", "ContentType") + ContentType = apps.get_model('contenttypes', 'ContentType') for ct in ContentType.objects.all(): try: ct.name = apps.get_model(ct.app_label, ct.model)._meta.object_name @@ -14,29 +14,26 @@ def add_legacy_name(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("contenttypes", "0001_initial"), + ('contenttypes', '0001_initial'), ] operations = [ migrations.AlterModelOptions( - name="contenttype", - options={ - "verbose_name": "content type", - "verbose_name_plural": "content types", - }, + name='contenttype', + options={'verbose_name': 'content type', 'verbose_name_plural': 'content types'}, ), migrations.AlterField( - model_name="contenttype", - name="name", + model_name='contenttype', + name='name', field=models.CharField(max_length=100, null=True), ), migrations.RunPython( migrations.RunPython.noop, add_legacy_name, - hints={"model_name": "contenttype"}, + hints={'model_name': 'contenttype'}, ), migrations.RemoveField( - model_name="contenttype", - name="name", + model_name='contenttype', + name='name', ), ] diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/models.py b/venv/Lib/site-packages/django/contrib/contenttypes/models.py index ef4f305..edc4887 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/models.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/models.py @@ -81,7 +81,10 @@ class ContentTypeManager(models.Manager): results[model] = ct if needed_opts: # Lookup required content types from the DB. - cts = self.filter(app_label__in=needed_app_labels, model__in=needed_models) + cts = self.filter( + app_label__in=needed_app_labels, + model__in=needed_models + ) for ct in cts: opts_models = needed_opts.pop(ct.model_class()._meta, []) for model in opts_models: @@ -120,9 +123,8 @@ class ContentTypeManager(models.Manager): def _add_to_cache(self, using, ct): """Insert a ContentType into the cache.""" - # Note it's possible for ContentType objects to be stale; model_class() - # will return None. Hence, there is no reliance on - # model._meta.app_label here, just using the model fields instead. + # Note it's possible for ContentType objects to be stale; model_class() will return None. + # Hence, there is no reliance on model._meta.app_label here, just using the model fields instead. key = (ct.app_label, ct.model) self._cache.setdefault(using, {})[key] = ct self._cache.setdefault(using, {})[ct.id] = ct @@ -130,14 +132,14 @@ class ContentTypeManager(models.Manager): class ContentType(models.Model): app_label = models.CharField(max_length=100) - model = models.CharField(_("python model class name"), max_length=100) + model = models.CharField(_('python model class name'), max_length=100) objects = ContentTypeManager() class Meta: - verbose_name = _("content type") - verbose_name_plural = _("content types") - db_table = "django_content_type" - unique_together = [["app_label", "model"]] + verbose_name = _('content type') + verbose_name_plural = _('content types') + db_table = 'django_content_type' + unique_together = [['app_label', 'model']] def __str__(self): return self.app_labeled_name @@ -154,7 +156,7 @@ class ContentType(models.Model): model = self.model_class() if not model: return self.model - return "%s | %s" % (model._meta.app_label, model._meta.verbose_name) + return '%s | %s' % (model._meta.app_label, model._meta.verbose_name) def model_class(self): """Return the model class for this type of content.""" diff --git a/venv/Lib/site-packages/django/contrib/contenttypes/views.py b/venv/Lib/site-packages/django/contrib/contenttypes/views.py index bfde73c..3231e6b 100644 --- a/venv/Lib/site-packages/django/contrib/contenttypes/views.py +++ b/venv/Lib/site-packages/django/contrib/contenttypes/views.py @@ -15,22 +15,22 @@ def shortcut(request, content_type_id, object_id): content_type = ContentType.objects.get(pk=content_type_id) if not content_type.model_class(): raise Http404( - _("Content type %(ct_id)s object has no associated model") - % {"ct_id": content_type_id} + _("Content type %(ct_id)s object has no associated model") % + {'ct_id': content_type_id} ) obj = content_type.get_object_for_this_type(pk=object_id) except (ObjectDoesNotExist, ValueError): raise Http404( - _("Content type %(ct_id)s object %(obj_id)s doesn’t exist") - % {"ct_id": content_type_id, "obj_id": object_id} + _('Content type %(ct_id)s object %(obj_id)s doesn’t exist') % + {'ct_id': content_type_id, 'obj_id': object_id} ) try: get_absolute_url = obj.get_absolute_url except AttributeError: raise Http404( - _("%(ct_name)s objects don’t have a get_absolute_url() method") - % {"ct_name": content_type.name} + _('%(ct_name)s objects don’t have a get_absolute_url() method') % + {'ct_name': content_type.name} ) absurl = get_absolute_url() @@ -38,7 +38,7 @@ def shortcut(request, content_type_id, object_id): # if necessary. # If the object actually defines a domain, we're done. - if absurl.startswith(("http://", "https://", "//")): + if absurl.startswith(('http://', 'https://', '//')): return HttpResponseRedirect(absurl) # Otherwise, we need to introspect the object's relationships for a @@ -48,8 +48,8 @@ def shortcut(request, content_type_id, object_id): except ObjectDoesNotExist: object_domain = None - if apps.is_installed("django.contrib.sites"): - Site = apps.get_model("sites.Site") + if apps.is_installed('django.contrib.sites'): + Site = apps.get_model('sites.Site') opts = obj._meta for field in opts.many_to_many: @@ -83,6 +83,6 @@ def shortcut(request, content_type_id, object_id): # to whatever get_absolute_url() returned. if object_domain is not None: protocol = request.scheme - return HttpResponseRedirect("%s://%s%s" % (protocol, object_domain, absurl)) + return HttpResponseRedirect('%s://%s%s' % (protocol, object_domain, absurl)) else: return HttpResponseRedirect(absurl) diff --git a/venv/Lib/site-packages/django/contrib/flatpages/admin.py b/venv/Lib/site-packages/django/contrib/flatpages/admin.py index 5bbc2ad..ead6b52 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/admin.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/admin.py @@ -8,15 +8,12 @@ from django.utils.translation import gettext_lazy as _ class FlatPageAdmin(admin.ModelAdmin): form = FlatpageForm fieldsets = ( - (None, {"fields": ("url", "title", "content", "sites")}), - ( - _("Advanced options"), - { - "classes": ("collapse",), - "fields": ("registration_required", "template_name"), - }, - ), + (None, {'fields': ('url', 'title', 'content', 'sites')}), + (_('Advanced options'), { + 'classes': ('collapse',), + 'fields': ('registration_required', 'template_name'), + }), ) - list_display = ("url", "title") - list_filter = ("sites", "registration_required") - search_fields = ("url", "title") + list_display = ('url', 'title') + list_filter = ('sites', 'registration_required') + search_fields = ('url', 'title') diff --git a/venv/Lib/site-packages/django/contrib/flatpages/apps.py b/venv/Lib/site-packages/django/contrib/flatpages/apps.py index eb9f470..4f5ef17 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/apps.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/apps.py @@ -3,6 +3,6 @@ from django.utils.translation import gettext_lazy as _ class FlatPagesConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.flatpages" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.flatpages' verbose_name = _("Flat Pages") diff --git a/venv/Lib/site-packages/django/contrib/flatpages/forms.py b/venv/Lib/site-packages/django/contrib/flatpages/forms.py index a6619c5..f5ee764 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/forms.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/forms.py @@ -2,19 +2,15 @@ from django import forms from django.conf import settings from django.contrib.flatpages.models import FlatPage from django.core.exceptions import ValidationError -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ class FlatpageForm(forms.ModelForm): url = forms.RegexField( label=_("URL"), max_length=100, - regex=r"^[-\w/\.~]+$", - help_text=_( - "Example: “/about/contact/”. Make sure to have leading and trailing " - "slashes." - ), + regex=r'^[-\w/\.~]+$', + help_text=_('Example: “/about/contact/”. Make sure to have leading and trailing slashes.'), error_messages={ "invalid": _( "This value must contain only letters, numbers, dots, " @@ -25,38 +21,38 @@ class FlatpageForm(forms.ModelForm): class Meta: model = FlatPage - fields = "__all__" + fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not self._trailing_slash_required(): - self.fields["url"].help_text = _( - "Example: “/about/contact”. Make sure to have a leading slash." + self.fields['url'].help_text = _( + 'Example: “/about/contact”. Make sure to have a leading slash.' ) def _trailing_slash_required(self): return ( - settings.APPEND_SLASH - and "django.middleware.common.CommonMiddleware" in settings.MIDDLEWARE + settings.APPEND_SLASH and + 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE ) def clean_url(self): - url = self.cleaned_data["url"] - if not url.startswith("/"): + url = self.cleaned_data['url'] + if not url.startswith('/'): raise ValidationError( gettext("URL is missing a leading slash."), - code="missing_leading_slash", + code='missing_leading_slash', ) - if self._trailing_slash_required() and not url.endswith("/"): + if self._trailing_slash_required() and not url.endswith('/'): raise ValidationError( gettext("URL is missing a trailing slash."), - code="missing_trailing_slash", + code='missing_trailing_slash', ) return url def clean(self): - url = self.cleaned_data.get("url") - sites = self.cleaned_data.get("sites") + url = self.cleaned_data.get('url') + sites = self.cleaned_data.get('sites') same_url = FlatPage.objects.filter(url=url) if self.instance.pk: @@ -66,9 +62,9 @@ class FlatpageForm(forms.ModelForm): for site in sites: if same_url.filter(sites=site).exists(): raise ValidationError( - _("Flatpage with url %(url)s already exists for site %(site)s"), - code="duplicate_url", - params={"url": url, "site": site}, + _('Flatpage with url %(url)s already exists for site %(site)s'), + code='duplicate_url', + params={'url': url, 'site': site}, ) return super().clean() diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo index e7633dd..456d4ca 100644 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po index f7e3535..0b00b8e 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/bg/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Jannis Leidel <jannis@leidel.info>, 2011 # Venelin Stoykov <vkstoykov@gmail.com>, 2016 # vestimir <vestimir@gmail.com>, 2014 @@ -9,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2022-01-14 11:31+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Venelin Stoykov <vkstoykov@gmail.com>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -30,31 +29,27 @@ msgid "URL" msgstr "URL" msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Пример: \"/about/contact/\". Началната и крайната наклонена чертичка са " -"задължителни." +"Пример: '/about/contact/'. Началната и крайната наклонена чертичка са " +"задължителни. " msgid "" "This value must contain only letters, numbers, dots, underscores, dashes, " "slashes or tildes." msgstr "" "Тази стойност трябва да съдържа само букви, цифри, точки, долни тирета, " -"тирета, наклонени черти или тилди." - -msgid "Example: “/about/contact”. Make sure to have a leading slash." -msgstr "" -"Пример: \"/about/contact/\". Началната наклонена чертичка е задължителна. " +"наклонени черти или tildes." msgid "URL is missing a leading slash." -msgstr "В URL липсва начална наклонена черта." +msgstr "URL липсва водеща черта." msgid "URL is missing a trailing slash." -msgstr "В URL липсва завършваща наклонена черта." +msgstr "URL липсва наклонена черта." #, python-format msgid "Flatpage with url %(url)s already exists for site %(site)s" -msgstr "Flatpage с url %(url)s вече съществува за сайт %(site)s" +msgstr "Flatpage с url %(url)s вече съществува за site %(site)s" msgid "title" msgstr "заглавие" @@ -69,11 +64,11 @@ msgid "template name" msgstr "име на шаблон" msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." msgstr "" -"Пример: \"flatpages/contact_page.html\". Ако това не е указано, системата " -"ще използва \"flatpages/default.html\". " +"Пример: 'flatpages/contact_page.html'. Ако това не е указано, системата ще " +"използва 'flatpages/default.html'. " msgid "registration required" msgstr "изисква се регистрация" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo index f9174a1..cae3e1e 100644 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.po index 1f34dae..f8c5aac 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/el/LC_MESSAGES/django.po @@ -2,7 +2,6 @@ # # Translators: # Dimitris Glezos <glezos@transifex.com>, 2011 -# Fotis Athineos <fotis@transifex.com>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 # Pãnoș <panos.laganakos@gmail.com>, 2014 # Pãnoș <panos.laganakos@gmail.com>, 2016,2019 @@ -10,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-08-04 06:27+0000\n" -"Last-Translator: Fotis Athineos <fotis@transifex.com>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-01-25 19:37+0000\n" +"Last-Translator: Pãnoș <panos.laganakos@gmail.com>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -30,9 +29,9 @@ msgid "URL" msgstr "URL" msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Παράδειγμα: “/about/contact/“. Βεβαιωθείτε ότι περιέχει καθέτους στην αρχή " +"Παράδειγμα: '/about/contact/'. Βεβαιωθείτε ότι περιέχει καθέτους στην αρχή " "και το τέλος." msgid "" @@ -42,9 +41,9 @@ msgstr "" "Η τιμή αυτή πρέπει να περιέχει μόνο γράμματα, αριθμούς, τελείες, παύλες, " "κάτω παύλες, καθέτους ή περισπωμένες." -msgid "Example: “/about/contact”. Make sure to have a leading slash." +msgid "Example: '/about/contact'. Make sure to have a leading slash." msgstr "" -"Παράδειγμα: “/about/contact/“. Βεβαιωθείτε ότι περιέχει κάθετο στην αρχή." +"Παράδειγμα: '/about/contact/'. Βεβαιωθείτε ότι περιέχει κάθετο στην αρχή." msgid "URL is missing a leading slash." msgstr "Λείπει μια αρχική κάθετος από την διεύθυνση." @@ -70,11 +69,11 @@ msgid "template name" msgstr "όνομα περιγράμματος" msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." msgstr "" -"Παράδειγμα: “flatpages/contact_page.html“. Αν δεν δηλωθεί, το σύστημα θα " -"χρησιμοποιήσει το “flatpages/default.html“." +"Παράδειγμα: 'flatpages/contact_page.html'. Αν δεν δηλωθεί, το σύστημα θα " +"χρησιμοποιήσει το 'flatpages/default.html'." msgid "registration required" msgstr "απαιτείται εγγραφή" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo index 354cbe6..856050c 100644 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po index 9e09c70..7d59861 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/en_AU/LC_MESSAGES/django.po @@ -1,17 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Katie McLaughlin <katie@glasnt.com>, 2021 -# Tom Fifield <tom@tomfifield.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-06-23 07:20+0000\n" -"Last-Translator: Katie McLaughlin <katie@glasnt.com>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" +"POT-Creation-Date: 2015-03-18 09:16+0100\n" +"PO-Revision-Date: 2015-03-18 08:34+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: English (Australia) (http://www.transifex.com/projects/p/" +"django/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -19,70 +17,58 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Advanced options" -msgstr "Advanced options" +msgstr "" msgid "Flat Pages" -msgstr "Flat Pages" +msgstr "" msgid "URL" -msgstr "URL" +msgstr "" msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." msgid "" "This value must contain only letters, numbers, dots, underscores, dashes, " "slashes or tildes." msgstr "" -"This value must contain only letters, numbers, dots, underscores, dashes, " -"slashes or tildes." - -msgid "Example: “/about/contact”. Make sure to have a leading slash." -msgstr "Example: “/about/contact”. Make sure to have a leading slash." msgid "URL is missing a leading slash." -msgstr "URL is missing a leading slash." +msgstr "" msgid "URL is missing a trailing slash." -msgstr "URL is missing a trailing slash." +msgstr "" #, python-format msgid "Flatpage with url %(url)s already exists for site %(site)s" -msgstr "Flatpage with url %(url)s already exists for site %(site)s" +msgstr "" msgid "title" -msgstr "title" +msgstr "" msgid "content" -msgstr "content" +msgstr "" msgid "enable comments" -msgstr "enable comments" +msgstr "" msgid "template name" -msgstr "template name" +msgstr "" msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." msgstr "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." msgid "registration required" -msgstr "registration required" +msgstr "" msgid "If this is checked, only logged-in users will be able to view the page." msgstr "" -"If this is checked, only logged-in users will be able to view the page." - -msgid "sites" -msgstr "sites" msgid "flat page" -msgstr "flat page" +msgstr "" msgid "flat pages" -msgstr "flat pages" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po index 3bf4bcc..12dcb14 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Advanced options" msgstr "დამატებითი პარამეტრები" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po index 83198cc..9d86fb9 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/kn/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Advanced options" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 7af7bbd..0000000 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 4315687..0000000 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,87 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-11-16 12:59+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Advanced options" -msgstr "Pilihan lanjut" - -msgid "Flat Pages" -msgstr "Muka rata" - -msgid "URL" -msgstr "URL" - -msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." -msgstr "" -"Contoh: \"/berkenaan/hubungi/\". Pastikan ada sempang awalan dan akhiran." - -msgid "" -"This value must contain only letters, numbers, dots, underscores, dashes, " -"slashes or tildes." -msgstr "" -"Niali ini hanya boleh mengandugi huruf, nombor, titik, garis-bawah, " -"sengkang, sempang atau tilde." - -msgid "Example: “/about/contact”. Make sure to have a leading slash." -msgstr "Contoh: \"/berkenaan/hubungi\". Pastikan ada sempang awalan." - -msgid "URL is missing a leading slash." -msgstr "URL tidak mempunyai sempang awalan." - -msgid "URL is missing a trailing slash." -msgstr "URL tidak mempunyai sempang akhiran." - -#, python-format -msgid "Flatpage with url %(url)s already exists for site %(site)s" -msgstr "Mukarata dengan url %(url)s sudah wujud bagi laman %(site)s" - -msgid "title" -msgstr "tajuk" - -msgid "content" -msgstr "kandungan" - -msgid "enable comments" -msgstr "hidupkan komen" - -msgid "template name" -msgstr "nama templat" - -msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." -msgstr "" -"Contoh: \"mukarata/ruangan_hubungi.html\". Sekiranya ini tidak diberikam, " -"sistem akan menggunakan \"flatpages/defulat.html\"." - -msgid "registration required" -msgstr "Pendaftaran diperlukan" - -msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "" -"Sekiranya ini ditanda, hanya pengguna yang log-masuk sahaja akan dapat " -"melihat ruangan ini." - -msgid "sites" -msgstr "laman-laman" - -msgid "flat page" -msgstr "muka rata" - -msgid "flat pages" -msgstr "muka rata" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo index a62a709..14a9abc 100644 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po index 55c92ea..f8c1785 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/nn/LC_MESSAGES/django.po @@ -3,14 +3,13 @@ # Translators: # hgrimelid <havard@grimelid.com>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-11-12 08:48+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -23,16 +22,15 @@ msgid "Advanced options" msgstr "Avanserte innstillingar" msgid "Flat Pages" -msgstr "Flatsider" +msgstr "" msgid "URL" msgstr "Nettadresse" msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Eksempel: “/om/kontakt/”. Kontroller at det er ein skråstrek framanfor og " -"bak." +"Eksempel: '/om/kontakt/'. Kontroller at det er ein skråstrek framfor og bak." msgid "" "This value must contain only letters, numbers, dots, underscores, dashes, " @@ -41,19 +39,15 @@ msgstr "" "Dette feltet kan berre innehalde bokstavar, nummer, skilleteikn, " "understrekar, bindestrekar, skråstrekar eller tilder." -msgid "Example: “/about/contact”. Make sure to have a leading slash." -msgstr "" -"Eksempel: “/om/kontakt/”. Kontroller at det er ein skråstrek framanfor." - msgid "URL is missing a leading slash." -msgstr "Nettadressa manglar ein skråstrek framanfor." +msgstr "" msgid "URL is missing a trailing slash." -msgstr "Nettadressa manglar ein skråstrek bak." +msgstr "" #, python-format msgid "Flatpage with url %(url)s already exists for site %(site)s" -msgstr "Flatside med nettadresse %(url)s eksisterer allereie for sida %(site)s" +msgstr "" msgid "title" msgstr "tittel" @@ -68,11 +62,11 @@ msgid "template name" msgstr "malnamn" msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." msgstr "" -"Døme: “flatpages/contact_page.html”. Dersom denne ikkje er oppgjeve vert " -"“flatpages/default.html” brukt." +"Døme: 'flatpages/kontakt_side.html'. Dersom denne ikkje er gjeve, vil " +"'flatpages/default.html' bli brukt." msgid "registration required" msgstr "krevar registrering" @@ -81,7 +75,7 @@ msgid "If this is checked, only logged-in users will be able to view the page." msgstr "Dersom denne er kryssa av, kan berre innlogga brukarar sjå sida." msgid "sites" -msgstr "nettstadar" +msgstr "" msgid "flat page" msgstr "flatside" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo index 485b834..a2e3552 100644 Binary files a/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po index 27d1f08..a0bc53d 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/flatpages/locale/sk/LC_MESSAGES/django.po @@ -4,14 +4,13 @@ # Jannis Leidel <jannis@leidel.info>, 2011 # Marian Andre <marian@andre.sk>, 2011-2012,2014 # Martin Tóth <ezimir@gmail.com>, 2017 -# Peter Kuma, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-07-24 21:01+0000\n" -"Last-Translator: Peter Kuma\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-01-18 00:18+0000\n" +"Last-Translator: Ramiro Morales\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -30,10 +29,9 @@ msgid "URL" msgstr "URL" msgid "" -"Example: “/about/contact/”. Make sure to have leading and trailing slashes." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Príklad: “/about/contact/”. Zabezpečte, aby boli lomky na začiatku a na " -"konci." +"Príklad: '/about/contact/'. Uistite sa, že máte lomky na začiatku a na konci." msgid "" "This value must contain only letters, numbers, dots, underscores, dashes, " @@ -42,8 +40,8 @@ msgstr "" "Táto hodnota musí obsahovať len písmená, číslice, bodky, podčiarovníky, " "pomlčky, lomky alebo vlnovky." -msgid "Example: “/about/contact”. Make sure to have a leading slash." -msgstr "Príklad: “/about/contact”. Zabezpečte, aby bola lomka na začiatku." +msgid "Example: '/about/contact'. Make sure to have a leading slash." +msgstr "" msgid "URL is missing a leading slash." msgstr "V URL chýba úvodná lomka." @@ -68,11 +66,11 @@ msgid "template name" msgstr "názov šablóny" msgid "" -"Example: “flatpages/contact_page.html”. If this isn’t provided, the system " -"will use “flatpages/default.html”." +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." msgstr "" -"Príklad: “flatpages/contact_page.html”. Ak nie je uvedené, systém použije " -"“flatpages/default.html”." +"Príklad: 'flatpages/contact_page.html'. Ak nič nenapíšete, systém použije " +"'flatpages/default.html'." msgid "registration required" msgstr "nutná registrácia" diff --git a/venv/Lib/site-packages/django/contrib/flatpages/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/flatpages/migrations/0001_initial.py index 631ebbc..867cd6d 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/migrations/0001_initial.py @@ -4,65 +4,35 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("sites", "0001_initial"), + ('sites', '0001_initial'), ] operations = [ migrations.CreateModel( - name="FlatPage", + name='FlatPage', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ( - "url", - models.CharField(max_length=100, verbose_name="URL", db_index=True), - ), - ("title", models.CharField(max_length=200, verbose_name="title")), - ("content", models.TextField(verbose_name="content", blank=True)), - ( - "enable_comments", - models.BooleanField(default=False, verbose_name="enable comments"), - ), - ( - "template_name", - models.CharField( - help_text=( - "Example: “flatpages/contact_page.html”. If this isn’t " - "provided, the system will use “flatpages/default.html”." - ), - max_length=70, - verbose_name="template name", - blank=True, - ), - ), - ( - "registration_required", - models.BooleanField( - default=False, - help_text=( - "If this is checked, only logged-in users will be able to " - "view the page." - ), - verbose_name="registration required", - ), - ), - ( - "sites", - models.ManyToManyField(to="sites.Site", verbose_name="sites"), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('url', models.CharField(max_length=100, verbose_name='URL', db_index=True)), + ('title', models.CharField(max_length=200, verbose_name='title')), + ('content', models.TextField(verbose_name='content', blank=True)), + ('enable_comments', models.BooleanField(default=False, verbose_name='enable comments')), + ('template_name', models.CharField( + help_text=( + 'Example: “flatpages/contact_page.html”. If this isn’t provided, the system will use ' + '“flatpages/default.html”.' + ), max_length=70, verbose_name='template name', blank=True + )), + ('registration_required', models.BooleanField( + default=False, help_text='If this is checked, only logged-in users will be able to view the page.', + verbose_name='registration required' + )), + ('sites', models.ManyToManyField(to='sites.Site', verbose_name='sites')), ], options={ - "ordering": ["url"], - "db_table": "django_flatpage", - "verbose_name": "flat page", - "verbose_name_plural": "flat pages", + 'ordering': ['url'], + 'db_table': 'django_flatpage', + 'verbose_name': 'flat page', + 'verbose_name_plural': 'flat pages', }, bases=(models.Model,), ), diff --git a/venv/Lib/site-packages/django/contrib/flatpages/models.py b/venv/Lib/site-packages/django/contrib/flatpages/models.py index d001cd1..2f2473b 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/models.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/models.py @@ -6,33 +6,31 @@ from django.utils.translation import gettext_lazy as _ class FlatPage(models.Model): - url = models.CharField(_("URL"), max_length=100, db_index=True) - title = models.CharField(_("title"), max_length=200) - content = models.TextField(_("content"), blank=True) - enable_comments = models.BooleanField(_("enable comments"), default=False) + url = models.CharField(_('URL'), max_length=100, db_index=True) + title = models.CharField(_('title'), max_length=200) + content = models.TextField(_('content'), blank=True) + enable_comments = models.BooleanField(_('enable comments'), default=False) template_name = models.CharField( - _("template name"), + _('template name'), max_length=70, blank=True, help_text=_( - "Example: “flatpages/contact_page.html”. If this isn’t provided, " - "the system will use “flatpages/default.html”." + 'Example: “flatpages/contact_page.html”. If this isn’t provided, ' + 'the system will use “flatpages/default.html”.' ), ) registration_required = models.BooleanField( - _("registration required"), - help_text=_( - "If this is checked, only logged-in users will be able to view the page." - ), + _('registration required'), + help_text=_("If this is checked, only logged-in users will be able to view the page."), default=False, ) - sites = models.ManyToManyField(Site, verbose_name=_("sites")) + sites = models.ManyToManyField(Site, verbose_name=_('sites')) class Meta: - db_table = "django_flatpage" - verbose_name = _("flat page") - verbose_name_plural = _("flat pages") - ordering = ["url"] + db_table = 'django_flatpage' + verbose_name = _('flat page') + verbose_name_plural = _('flat pages') + ordering = ['url'] def __str__(self): return "%s -- %s" % (self.url, self.title) @@ -40,10 +38,10 @@ class FlatPage(models.Model): def get_absolute_url(self): from .views import flatpage - for url in (self.url.lstrip("/"), self.url): + for url in (self.url.lstrip('/'), self.url): try: - return reverse(flatpage, kwargs={"url": url}) + return reverse(flatpage, kwargs={'url': url}) except NoReverseMatch: pass # Handle script prefix manually because we bypass reverse() - return iri_to_uri(get_script_prefix().rstrip("/") + self.url) + return iri_to_uri(get_script_prefix().rstrip('/') + self.url) diff --git a/venv/Lib/site-packages/django/contrib/flatpages/sitemaps.py b/venv/Lib/site-packages/django/contrib/flatpages/sitemaps.py index 405507c..a144023 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/sitemaps.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/sitemaps.py @@ -5,10 +5,8 @@ from django.core.exceptions import ImproperlyConfigured class FlatPageSitemap(Sitemap): def items(self): - if not django_apps.is_installed("django.contrib.sites"): - raise ImproperlyConfigured( - "FlatPageSitemap requires django.contrib.sites, which isn't installed." - ) - Site = django_apps.get_model("sites.Site") + if not django_apps.is_installed('django.contrib.sites'): + raise ImproperlyConfigured("FlatPageSitemap requires django.contrib.sites, which isn't installed.") + Site = django_apps.get_model('sites.Site') current_site = Site.objects.get_current() return current_site.flatpage_set.filter(registration_required=False) diff --git a/venv/Lib/site-packages/django/contrib/flatpages/templatetags/flatpages.py b/venv/Lib/site-packages/django/contrib/flatpages/templatetags/flatpages.py index 9ec6b88..1d99104 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/templatetags/flatpages.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/templatetags/flatpages.py @@ -19,16 +19,15 @@ class FlatpageNode(template.Node): self.user = None def render(self, context): - if "request" in context: - site_pk = get_current_site(context["request"]).pk + if 'request' in context: + site_pk = get_current_site(context['request']).pk else: site_pk = settings.SITE_ID flatpages = FlatPage.objects.filter(sites__id=site_pk) # If a prefix was specified, add a filter if self.starts_with: flatpages = flatpages.filter( - url__startswith=self.starts_with.resolve(context) - ) + url__startswith=self.starts_with.resolve(context)) # If the provided user is not authenticated, or no user # was provided, filter the list to only public flatpages. @@ -40,7 +39,7 @@ class FlatpageNode(template.Node): flatpages = flatpages.filter(registration_required=False) context[self.context_name] = flatpages - return "" + return '' @register.tag @@ -71,10 +70,9 @@ def get_flatpages(parser, token): {% get_flatpages '/about/' for someuser as about_pages %} """ bits = token.split_contents() - syntax_message = ( - "%(tag_name)s expects a syntax of %(tag_name)s " - "['url_starts_with'] [for user] as context_name" % {"tag_name": bits[0]} - ) + syntax_message = ("%(tag_name)s expects a syntax of %(tag_name)s " + "['url_starts_with'] [for user] as context_name" % + {'tag_name': bits[0]}) # Must have at 3-6 bits in the tag if 3 <= len(bits) <= 6: # If there's an even number of bits, there's no prefix @@ -84,13 +82,13 @@ def get_flatpages(parser, token): prefix = None # The very last bit must be the context name - if bits[-2] != "as": + if bits[-2] != 'as': raise template.TemplateSyntaxError(syntax_message) context_name = bits[-1] # If there are 5 or 6 bits, there is a user defined if len(bits) >= 5: - if bits[-4] != "for": + if bits[-4] != 'for': raise template.TemplateSyntaxError(syntax_message) user = bits[-3] else: diff --git a/venv/Lib/site-packages/django/contrib/flatpages/urls.py b/venv/Lib/site-packages/django/contrib/flatpages/urls.py index d4480b3..a087fe8 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/urls.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/urls.py @@ -2,5 +2,5 @@ from django.contrib.flatpages import views from django.urls import path urlpatterns = [ - path("<path:url>", views.flatpage, name="django.contrib.flatpages.views.flatpage"), + path('<path:url>', views.flatpage, name='django.contrib.flatpages.views.flatpage'), ] diff --git a/venv/Lib/site-packages/django/contrib/flatpages/views.py b/venv/Lib/site-packages/django/contrib/flatpages/views.py index 776f179..2722ec5 100644 --- a/venv/Lib/site-packages/django/contrib/flatpages/views.py +++ b/venv/Lib/site-packages/django/contrib/flatpages/views.py @@ -7,7 +7,7 @@ from django.template import loader from django.utils.safestring import mark_safe from django.views.decorators.csrf import csrf_protect -DEFAULT_TEMPLATE = "flatpages/default.html" +DEFAULT_TEMPLATE = 'flatpages/default.html' # This view is called from FlatpageFallbackMiddleware.process_response # when a 404 is raised, which often means CsrfViewMiddleware.process_view @@ -30,16 +30,16 @@ def flatpage(request, url): flatpage `flatpages.flatpages` object """ - if not url.startswith("/"): - url = "/" + url + if not url.startswith('/'): + url = '/' + url site_id = get_current_site(request).id try: f = get_object_or_404(FlatPage, url=url, sites=site_id) except Http404: - if not url.endswith("/") and settings.APPEND_SLASH: - url += "/" + if not url.endswith('/') and settings.APPEND_SLASH: + url += '/' f = get_object_or_404(FlatPage, url=url, sites=site_id) - return HttpResponsePermanentRedirect("%s/" % request.path) + return HttpResponsePermanentRedirect('%s/' % request.path) else: raise return render_flatpage(request, f) @@ -54,7 +54,6 @@ def render_flatpage(request, f): # logged in, redirect to the login page. if f.registration_required and not request.user.is_authenticated: from django.contrib.auth.views import redirect_to_login - return redirect_to_login(request.path) if f.template_name: template = loader.select_template((f.template_name, DEFAULT_TEMPLATE)) @@ -67,4 +66,4 @@ def render_flatpage(request, f): f.title = mark_safe(f.title) f.content = mark_safe(f.content) - return HttpResponse(template.render({"flatpage": f}, request)) + return HttpResponse(template.render({'flatpage': f}, request)) diff --git a/venv/Lib/site-packages/django/contrib/gis/admin/__init__.py b/venv/Lib/site-packages/django/contrib/gis/admin/__init__.py index d5bd54c..b0edaf0 100644 --- a/venv/Lib/site-packages/django/contrib/gis/admin/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/admin/__init__.py @@ -1,34 +1,12 @@ from django.contrib.admin import ( - HORIZONTAL, - VERTICAL, - AdminSite, - ModelAdmin, - StackedInline, - TabularInline, - action, - autodiscover, - display, - register, - site, + HORIZONTAL, VERTICAL, AdminSite, ModelAdmin, StackedInline, TabularInline, + action, autodiscover, display, register, site, ) -from django.contrib.gis.admin.options import GeoModelAdmin, GISModelAdmin, OSMGeoAdmin +from django.contrib.gis.admin.options import GeoModelAdmin, OSMGeoAdmin from django.contrib.gis.admin.widgets import OpenLayersWidget __all__ = [ - "HORIZONTAL", - "VERTICAL", - "AdminSite", - "ModelAdmin", - "StackedInline", - "TabularInline", - "action", - "autodiscover", - "display", - "register", - "site", - "GISModelAdmin", - "OpenLayersWidget", - # RemovedInDjango50Warning. - "GeoModelAdmin", - "OSMGeoAdmin", + 'HORIZONTAL', 'VERTICAL', 'AdminSite', 'ModelAdmin', 'StackedInline', + 'TabularInline', 'action', 'autodiscover', 'display', 'register', 'site', + 'GeoModelAdmin', 'OSMGeoAdmin', 'OpenLayersWidget', ] diff --git a/venv/Lib/site-packages/django/contrib/gis/admin/options.py b/venv/Lib/site-packages/django/contrib/gis/admin/options.py index 9eb6af6..9b758a7 100644 --- a/venv/Lib/site-packages/django/contrib/gis/admin/options.py +++ b/venv/Lib/site-packages/django/contrib/gis/admin/options.py @@ -1,43 +1,17 @@ -import warnings - from django.contrib.admin import ModelAdmin from django.contrib.gis.admin.widgets import OpenLayersWidget from django.contrib.gis.db import models -from django.contrib.gis.forms import OSMWidget from django.contrib.gis.gdal import OGRGeomType from django.forms import Media -from django.utils.deprecation import RemovedInDjango50Warning - -class GeoModelAdminMixin: - gis_widget = OSMWidget - gis_widget_kwargs = {} - - def formfield_for_dbfield(self, db_field, request, **kwargs): - if isinstance(db_field, models.GeometryField) and ( - db_field.dim < 3 or self.gis_widget.supports_3d - ): - kwargs["widget"] = self.gis_widget(**self.gis_widget_kwargs) - return db_field.formfield(**kwargs) - else: - return super().formfield_for_dbfield(db_field, request, **kwargs) - - -class GISModelAdmin(GeoModelAdminMixin, ModelAdmin): - pass - - -# RemovedInDjango50Warning. spherical_mercator_srid = 3857 -# RemovedInDjango50Warning. class GeoModelAdmin(ModelAdmin): """ The administration options class for Geographic models. Map settings may be overloaded from their defaults to create custom maps. """ - # The default map settings that may be overloaded -- still subject # to API changes. default_lon = 0 @@ -60,28 +34,16 @@ class GeoModelAdmin(ModelAdmin): map_width = 600 map_height = 400 map_srid = 4326 - map_template = "gis/admin/openlayers.html" - openlayers_url = ( - "https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js" - ) + map_template = 'gis/admin/openlayers.html' + openlayers_url = 'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js' point_zoom = num_zoom - 6 - wms_url = "http://vmap0.tiles.osgeo.org/wms/vmap0" - wms_layer = "basic" - wms_name = "OpenLayers WMS" - wms_options = {"format": "image/jpeg"} + wms_url = 'http://vmap0.tiles.osgeo.org/wms/vmap0' + wms_layer = 'basic' + wms_name = 'OpenLayers WMS' + wms_options = {'format': 'image/jpeg'} debug = False widget = OpenLayersWidget - def __init__(self, *args, **kwargs): - warnings.warn( - "django.contrib.gis.admin.GeoModelAdmin and OSMGeoAdmin are " - "deprecated in favor of django.contrib.admin.ModelAdmin and " - "django.contrib.gis.admin.GISModelAdmin.", - RemovedInDjango50Warning, - stacklevel=2, - ) - super().__init__(*args, **kwargs) - @property def media(self): "Injects OpenLayers JavaScript into the admin." @@ -95,7 +57,7 @@ class GeoModelAdmin(ModelAdmin): """ if isinstance(db_field, models.GeometryField) and db_field.dim < 3: # Setting the widget with the newly defined widget. - kwargs["widget"] = self.get_map_widget(db_field) + kwargs['widget'] = self.get_map_widget(db_field) return db_field.formfield(**kwargs) else: return super().formfield_for_dbfield(db_field, request, **kwargs) @@ -106,75 +68,67 @@ class GeoModelAdmin(ModelAdmin): in the `widget` attribute) using the settings from the attributes set in this class. """ - is_collection = db_field.geom_type in ( - "MULTIPOINT", - "MULTILINESTRING", - "MULTIPOLYGON", - "GEOMETRYCOLLECTION", - ) + is_collection = db_field.geom_type in ('MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON', 'GEOMETRYCOLLECTION') if is_collection: - if db_field.geom_type == "GEOMETRYCOLLECTION": - collection_type = "Any" + if db_field.geom_type == 'GEOMETRYCOLLECTION': + collection_type = 'Any' else: - collection_type = OGRGeomType(db_field.geom_type.replace("MULTI", "")) + collection_type = OGRGeomType(db_field.geom_type.replace('MULTI', '')) else: - collection_type = "None" + collection_type = 'None' class OLMap(self.widget): template_name = self.map_template geom_type = db_field.geom_type - wms_options = "" + wms_options = '' if self.wms_options: wms_options = ["%s: '%s'" % pair for pair in self.wms_options.items()] - wms_options = ", %s" % ", ".join(wms_options) + wms_options = ', %s' % ', '.join(wms_options) params = { - "default_lon": self.default_lon, - "default_lat": self.default_lat, - "default_zoom": self.default_zoom, - "display_wkt": self.debug or self.display_wkt, - "geom_type": OGRGeomType(db_field.geom_type), - "field_name": db_field.name, - "is_collection": is_collection, - "scrollable": self.scrollable, - "layerswitcher": self.layerswitcher, - "collection_type": collection_type, - "is_generic": db_field.geom_type == "GEOMETRY", - "is_linestring": db_field.geom_type - in ("LINESTRING", "MULTILINESTRING"), - "is_polygon": db_field.geom_type in ("POLYGON", "MULTIPOLYGON"), - "is_point": db_field.geom_type in ("POINT", "MULTIPOINT"), - "num_zoom": self.num_zoom, - "max_zoom": self.max_zoom, - "min_zoom": self.min_zoom, - "units": self.units, # likely should get from object - "max_resolution": self.max_resolution, - "max_extent": self.max_extent, - "modifiable": self.modifiable, - "mouse_position": self.mouse_position, - "scale_text": self.scale_text, - "map_width": self.map_width, - "map_height": self.map_height, - "point_zoom": self.point_zoom, - "srid": self.map_srid, - "display_srid": self.display_srid, - "wms_url": self.wms_url, - "wms_layer": self.wms_layer, - "wms_name": self.wms_name, - "wms_options": wms_options, - "debug": self.debug, + 'default_lon': self.default_lon, + 'default_lat': self.default_lat, + 'default_zoom': self.default_zoom, + 'display_wkt': self.debug or self.display_wkt, + 'geom_type': OGRGeomType(db_field.geom_type), + 'field_name': db_field.name, + 'is_collection': is_collection, + 'scrollable': self.scrollable, + 'layerswitcher': self.layerswitcher, + 'collection_type': collection_type, + 'is_generic': db_field.geom_type == 'GEOMETRY', + 'is_linestring': db_field.geom_type in ('LINESTRING', 'MULTILINESTRING'), + 'is_polygon': db_field.geom_type in ('POLYGON', 'MULTIPOLYGON'), + 'is_point': db_field.geom_type in ('POINT', 'MULTIPOINT'), + 'num_zoom': self.num_zoom, + 'max_zoom': self.max_zoom, + 'min_zoom': self.min_zoom, + 'units': self.units, # likely should get from object + 'max_resolution': self.max_resolution, + 'max_extent': self.max_extent, + 'modifiable': self.modifiable, + 'mouse_position': self.mouse_position, + 'scale_text': self.scale_text, + 'map_width': self.map_width, + 'map_height': self.map_height, + 'point_zoom': self.point_zoom, + 'srid': self.map_srid, + 'display_srid': self.display_srid, + 'wms_url': self.wms_url, + 'wms_layer': self.wms_layer, + 'wms_name': self.wms_name, + 'wms_options': wms_options, + 'debug': self.debug, } - return OLMap -# RemovedInDjango50Warning. class OSMGeoAdmin(GeoModelAdmin): - map_template = "gis/admin/osm.html" + map_template = 'gis/admin/osm.html' num_zoom = 20 map_srid = spherical_mercator_srid - max_extent = "-20037508,-20037508,20037508,20037508" - max_resolution = "156543.0339" + max_extent = '-20037508,-20037508,20037508,20037508' + max_resolution = '156543.0339' point_zoom = num_zoom - 6 - units = "m" + units = 'm' diff --git a/venv/Lib/site-packages/django/contrib/gis/admin/widgets.py b/venv/Lib/site-packages/django/contrib/gis/admin/widgets.py index 420c170..1ce94ec 100644 --- a/venv/Lib/site-packages/django/contrib/gis/admin/widgets.py +++ b/venv/Lib/site-packages/django/contrib/gis/admin/widgets.py @@ -7,27 +7,26 @@ from django.utils import translation # Creating a template context that contains Django settings # values needed by admin map templates. -geo_context = {"LANGUAGE_BIDI": translation.get_language_bidi()} -logger = logging.getLogger("django.contrib.gis") +geo_context = {'LANGUAGE_BIDI': translation.get_language_bidi()} +logger = logging.getLogger('django.contrib.gis') class OpenLayersWidget(Textarea): """ Render an OpenLayers map using the WKT of the geometry. """ - def get_context(self, name, value, attrs): # Update the template parameters with any attributes passed in. if attrs: self.params.update(attrs) - self.params["editable"] = self.params["modifiable"] + self.params['editable'] = self.params['modifiable'] else: - self.params["editable"] = True + self.params['editable'] = True # Defaulting the WKT value to a blank string -- this # will be tested in the JavaScript and the appropriate # interface will be constructed. - self.params["wkt"] = "" + self.params['wkt'] = '' # If a string reaches here (via a validation error on another # field) then just reconstruct the Geometry. @@ -38,29 +37,26 @@ class OpenLayersWidget(Textarea): logger.error("Error creating geometry from value '%s' (%s)", value, err) value = None - if ( - value - and value.geom_type.upper() != self.geom_type - and self.geom_type != "GEOMETRY" - ): + if (value and value.geom_type.upper() != self.geom_type and + self.geom_type != 'GEOMETRY'): value = None # Constructing the dictionary of the map options. - self.params["map_options"] = self.map_options() + self.params['map_options'] = self.map_options() # Constructing the JavaScript module name using the name of # the GeometryField (passed in via the `attrs` keyword). # Use the 'name' attr for the field name (rather than 'field') - self.params["name"] = name + self.params['name'] = name # note: we must switch out dashes for underscores since js # functions are created using the module variable - js_safe_name = self.params["name"].replace("-", "_") - self.params["module"] = "geodjango_%s" % js_safe_name + js_safe_name = self.params['name'].replace('-', '_') + self.params['module'] = 'geodjango_%s' % js_safe_name if value: # Transforming the geometry to the projection used on the # OpenLayers map. - srid = self.params["srid"] + srid = self.params['srid'] if value.srid != srid: try: ogr = value.ogr @@ -69,17 +65,15 @@ class OpenLayersWidget(Textarea): except GDALException as err: logger.error( "Error transforming geometry from srid '%s' to srid '%s' (%s)", - value.srid, - srid, - err, + value.srid, srid, err ) - wkt = "" + wkt = '' else: wkt = value.wkt # Setting the parameter WKT with that of the transformed # geometry. - self.params["wkt"] = wkt + self.params['wkt'] = wkt self.params.update(geo_context) return self.params @@ -88,31 +82,30 @@ class OpenLayersWidget(Textarea): """Build the map options hash for the OpenLayers template.""" # JavaScript construction utilities for the Bounds and Projection. def ol_bounds(extent): - return "new OpenLayers.Bounds(%s)" % extent + return 'new OpenLayers.Bounds(%s)' % extent def ol_projection(srid): return 'new OpenLayers.Projection("EPSG:%s")' % srid # An array of the parameter name, the name of their OpenLayers # counterpart, and the type of variable they are. - map_types = [ - ("srid", "projection", "srid"), - ("display_srid", "displayProjection", "srid"), - ("units", "units", str), - ("max_resolution", "maxResolution", float), - ("max_extent", "maxExtent", "bounds"), - ("num_zoom", "numZoomLevels", int), - ("max_zoom", "maxZoomLevels", int), - ("min_zoom", "minZoomLevel", int), - ] + map_types = [('srid', 'projection', 'srid'), + ('display_srid', 'displayProjection', 'srid'), + ('units', 'units', str), + ('max_resolution', 'maxResolution', float), + ('max_extent', 'maxExtent', 'bounds'), + ('num_zoom', 'numZoomLevels', int), + ('max_zoom', 'maxZoomLevels', int), + ('min_zoom', 'minZoomLevel', int), + ] # Building the map options hash. map_options = {} for param_name, js_name, option_type in map_types: if self.params.get(param_name, False): - if option_type == "srid": + if option_type == 'srid': value = ol_projection(self.params[param_name]) - elif option_type == "bounds": + elif option_type == 'bounds': value = ol_bounds(self.params[param_name]) elif option_type in (float, int): value = self.params[param_name] diff --git a/venv/Lib/site-packages/django/contrib/gis/apps.py b/venv/Lib/site-packages/django/contrib/gis/apps.py index 6282501..e582e76 100644 --- a/venv/Lib/site-packages/django/contrib/gis/apps.py +++ b/venv/Lib/site-packages/django/contrib/gis/apps.py @@ -4,11 +4,9 @@ from django.utils.translation import gettext_lazy as _ class GISConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.gis" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.gis' verbose_name = _("GIS") def ready(self): - serializers.BUILTIN_SERIALIZERS.setdefault( - "geojson", "django.contrib.gis.serializers.geojson" - ) + serializers.BUILTIN_SERIALIZERS.setdefault('geojson', 'django.contrib.gis.serializers.geojson') diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/adapter.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/adapter.py index b472e3a..f6f2719 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/adapter.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/adapter.py @@ -2,16 +2,14 @@ class WKTAdapter: """ An adaptor for Geometries sent to the MySQL and Oracle database backends. """ - def __init__(self, geom): self.wkt = geom.wkt self.srid = geom.srid def __eq__(self, other): return ( - isinstance(other, WKTAdapter) - and self.wkt == other.wkt - and self.srid == other.srid + isinstance(other, WKTAdapter) and + self.wkt == other.wkt and self.srid == other.srid ) def __hash__(self): diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/features.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/features.py index cc4ce10..cf52c4e 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/features.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/features.py @@ -60,15 +60,15 @@ class BaseSpatialFeatures: @property def supports_bbcontains_lookup(self): - return "bbcontains" in self.connection.ops.gis_operators + return 'bbcontains' in self.connection.ops.gis_operators @property def supports_contained_lookup(self): - return "contained" in self.connection.ops.gis_operators + return 'contained' in self.connection.ops.gis_operators @property def supports_crosses_lookup(self): - return "crosses" in self.connection.ops.gis_operators + return 'crosses' in self.connection.ops.gis_operators @property def supports_distances_lookups(self): @@ -76,11 +76,11 @@ class BaseSpatialFeatures: @property def supports_dwithin_lookup(self): - return "dwithin" in self.connection.ops.gis_operators + return 'dwithin' in self.connection.ops.gis_operators @property def supports_relate_lookup(self): - return "relate" in self.connection.ops.gis_operators + return 'relate' in self.connection.ops.gis_operators @property def supports_isvalid_lookup(self): @@ -104,7 +104,7 @@ class BaseSpatialFeatures: return models.Union not in self.connection.ops.disallowed_aggregates def __getattr__(self, name): - m = re.match(r"has_(\w*)_function$", name) + m = re.match(r'has_(\w*)_function$', name) if m: func_name = m[1] return func_name not in self.connection.ops.unsupported_functions diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/models.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/models.py index 589c872..c25c1cc 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/models.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/models.py @@ -6,14 +6,13 @@ class SpatialRefSysMixin: The SpatialRefSysMixin is a class used by the database-dependent SpatialRefSys objects to reduce redundant code. """ - @property def srs(self): """ Return a GDAL SpatialReference object. """ # TODO: Is caching really necessary here? Is complexity worth it? - if hasattr(self, "_srs"): + if hasattr(self, '_srs'): # Returning a clone of the cached SpatialReference object. return self._srs.clone() else: @@ -32,10 +31,7 @@ class SpatialRefSysMixin: except Exception as e: msg = e - raise Exception( - "Could not get OSR SpatialReference from WKT: %s\nError:\n%s" - % (self.wkt, msg) - ) + raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg)) @property def ellipsoid(self): @@ -53,12 +49,12 @@ class SpatialRefSysMixin: @property def spheroid(self): "Return the spheroid name for this spatial reference." - return self.srs["spheroid"] + return self.srs['spheroid'] @property def datum(self): "Return the datum for this spatial reference." - return self.srs["datum"] + return self.srs['datum'] @property def projected(self): @@ -121,7 +117,7 @@ class SpatialRefSysMixin: """ srs = gdal.SpatialReference(wkt) sphere_params = srs.ellipsoid - sphere_name = srs["spheroid"] + sphere_name = srs['spheroid'] if not string: return sphere_name, sphere_params diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/operations.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/operations.py index e7bffb1..84b1785 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/base/operations.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/base/operations.py @@ -1,7 +1,8 @@ from django.contrib.gis.db.models import GeometryField from django.contrib.gis.db.models.functions import Distance -from django.contrib.gis.measure import Area as AreaMeasure -from django.contrib.gis.measure import Distance as DistanceMeasure +from django.contrib.gis.measure import ( + Area as AreaMeasure, Distance as DistanceMeasure, +) from django.db import NotSupportedError from django.utils.functional import cached_property @@ -17,7 +18,7 @@ class BaseSpatialOperations: spatial_version = None # How the geometry column should be selected. - select = "%s" + select = '%s' @cached_property def select_extent(self): @@ -26,7 +27,7 @@ class BaseSpatialOperations: # Aggregates disallowed_aggregates = () - geom_func_prefix = "" + geom_func_prefix = '' # Mapping between Django function names and backend names, when names do not # match; used in spatial_function_name(). @@ -34,36 +35,12 @@ class BaseSpatialOperations: # Set of known unsupported functions of the backend unsupported_functions = { - "Area", - "AsGeoJSON", - "AsGML", - "AsKML", - "AsSVG", - "Azimuth", - "BoundingCircle", - "Centroid", - "Difference", - "Distance", - "Envelope", - "GeoHash", - "GeometryDistance", - "Intersection", - "IsValid", - "Length", - "LineLocatePoint", - "MakeValid", - "MemSize", - "NumGeometries", - "NumPoints", - "Perimeter", - "PointOnSurface", - "Reverse", - "Scale", - "SnapToGrid", - "SymDifference", - "Transform", - "Translate", - "Union", + 'Area', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG', 'Azimuth', + 'BoundingCircle', 'Centroid', 'Difference', 'Distance', 'Envelope', + 'GeoHash', 'GeometryDistance', 'Intersection', 'IsValid', 'Length', + 'LineLocatePoint', 'MakeValid', 'MemSize', 'NumGeometries', + 'NumPoints', 'Perimeter', 'PointOnSurface', 'Reverse', 'Scale', + 'SnapToGrid', 'SymDifference', 'Transform', 'Translate', 'Union', } # Constructors @@ -72,14 +49,10 @@ class BaseSpatialOperations: # Default conversion functions for aggregates; will be overridden if implemented # for the spatial backend. def convert_extent(self, box, srid): - raise NotImplementedError( - "Aggregate extent not implemented for this spatial backend." - ) + raise NotImplementedError('Aggregate extent not implemented for this spatial backend.') def convert_extent3d(self, box, srid): - raise NotImplementedError( - "Aggregate 3D extent not implemented for this spatial backend." - ) + raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.') # For quoting column values, rather than columns. def geo_quote_name(self, name): @@ -91,18 +64,14 @@ class BaseSpatialOperations: Return the database column type for the geometry field on the spatial backend. """ - raise NotImplementedError( - "subclasses of BaseSpatialOperations must provide a geo_db_type() method" - ) + raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_type() method') def get_distance(self, f, value, lookup_type): """ Return the distance parameters for the given geometry field, lookup value, and lookup type. """ - raise NotImplementedError( - "Distance operations not available on this spatial backend." - ) + raise NotImplementedError('Distance operations not available on this spatial backend.') def get_geom_placeholder(self, f, value, compiler): """ @@ -111,62 +80,48 @@ class BaseSpatialOperations: stored procedure call to the transformation function of the spatial backend. """ - def transform_value(value, field): return value is not None and value.srid != field.srid - if hasattr(value, "as_sql"): + if hasattr(value, 'as_sql'): return ( - "%s(%%s, %s)" % (self.spatial_function_name("Transform"), f.srid) + '%s(%%s, %s)' % (self.spatial_function_name('Transform'), f.srid) if transform_value(value.output_field, f) - else "%s" + else '%s' ) if transform_value(value, f): # Add Transform() to the SQL placeholder. - return "%s(%s(%%s,%s), %s)" % ( - self.spatial_function_name("Transform"), - self.from_text, - value.srid, - f.srid, + return '%s(%s(%%s,%s), %s)' % ( + self.spatial_function_name('Transform'), + self.from_text, value.srid, f.srid, ) elif self.connection.features.has_spatialrefsys_table: - return "%s(%%s,%s)" % (self.from_text, f.srid) + return '%s(%%s,%s)' % (self.from_text, f.srid) else: # For backwards compatibility on MySQL (#27464). - return "%s(%%s)" % self.from_text + return '%s(%%s)' % self.from_text def check_expression_support(self, expression): if isinstance(expression, self.disallowed_aggregates): raise NotSupportedError( - "%s spatial aggregation is not supported by this database backend." - % expression.name + "%s spatial aggregation is not supported by this database backend." % expression.name ) super().check_expression_support(expression) def spatial_aggregate_name(self, agg_name): - raise NotImplementedError( - "Aggregate support not implemented for this spatial backend." - ) + raise NotImplementedError('Aggregate support not implemented for this spatial backend.') def spatial_function_name(self, func_name): if func_name in self.unsupported_functions: - raise NotSupportedError( - "This backend doesn't support the %s function." % func_name - ) + raise NotSupportedError("This backend doesn't support the %s function." % func_name) return self.function_names.get(func_name, self.geom_func_prefix + func_name) # Routines for getting the OGC-compliant models. def geometry_columns(self): - raise NotImplementedError( - "Subclasses of BaseSpatialOperations must provide a geometry_columns() " - "method." - ) + raise NotImplementedError('Subclasses of BaseSpatialOperations must provide a geometry_columns() method.') def spatial_ref_sys(self): - raise NotImplementedError( - "subclasses of BaseSpatialOperations must a provide spatial_ref_sys() " - "method" - ) + raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_ref_sys() method') distance_expr_for_lookup = staticmethod(Distance) @@ -178,17 +133,15 @@ class BaseSpatialOperations: def get_geometry_converter(self, expression): raise NotImplementedError( - "Subclasses of BaseSpatialOperations must provide a " - "get_geometry_converter() method." + 'Subclasses of BaseSpatialOperations must provide a ' + 'get_geometry_converter() method.' ) def get_area_att_for_field(self, field): if field.geodetic(self.connection): if self.connection.features.supports_area_geodetic: - return "sq_m" - raise NotImplementedError( - "Area on geodetic coordinate systems not supported." - ) + return 'sq_m' + raise NotImplementedError('Area on geodetic coordinate systems not supported.') else: units_name = field.units_name(self.connection) if units_name: @@ -198,7 +151,7 @@ class BaseSpatialOperations: dist_att = None if field.geodetic(self.connection): if self.connection.features.supports_distance_geodetic: - dist_att = "m" + dist_att = 'm' else: units = field.units_name(self.connection) if units: diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/base.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/base.py index 4abc052..fccea59 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/base.py @@ -1,4 +1,6 @@ -from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper +from django.db.backends.mysql.base import ( + DatabaseWrapper as MySQLDatabaseWrapper, +) from .features import DatabaseFeatures from .introspection import MySQLIntrospection diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/features.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/features.py index 8999a38..613ccce 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/features.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/features.py @@ -1,5 +1,7 @@ from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures -from django.db.backends.mysql.features import DatabaseFeatures as MySQLDatabaseFeatures +from django.db.backends.mysql.features import ( + DatabaseFeatures as MySQLDatabaseFeatures, +) from django.utils.functional import cached_property @@ -12,13 +14,13 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): supports_transform = False supports_null_geometries = False supports_num_points_poly = False - unsupported_geojson_options = {"crs"} + unsupported_geojson_options = {'crs'} @cached_property def empty_intersection_returns_none(self): return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version < (5, 7, 5) + not self.connection.mysql_is_mariadb and + self.connection.mysql_version < (5, 7, 5) ) @cached_property @@ -29,16 +31,13 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): @cached_property def django_test_skips(self): skips = super().django_test_skips - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, - 0, - 0, + if ( + not self.connection.mysql_is_mariadb and + self.connection.mysql_version < (8, 0, 0) ): - skips.update( - { - "MySQL < 8 gives different results.": { - "gis_tests.geoapp.tests.GeoLookupTest.test_disjoint_lookup", - }, - } - ) + skips.update({ + 'MySQL < 8 gives different results.': { + 'gis_tests.geoapp.tests.GeoLookupTest.test_disjoint_lookup', + }, + }) return skips diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/introspection.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/introspection.py index 3561869..3dc4ae6 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/introspection.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/introspection.py @@ -8,13 +8,14 @@ class MySQLIntrospection(DatabaseIntrospection): # Updating the data_types_reverse dictionary with the appropriate # type for Geometry fields. data_types_reverse = DatabaseIntrospection.data_types_reverse.copy() - data_types_reverse[FIELD_TYPE.GEOMETRY] = "GeometryField" + data_types_reverse[FIELD_TYPE.GEOMETRY] = 'GeometryField' def get_geometry_type(self, table_name, description): with self.connection.cursor() as cursor: # In order to get the specific geometry type of the field, # we introspect on the table definition using `DESCRIBE`. - cursor.execute("DESCRIBE %s" % self.connection.ops.quote_name(table_name)) + cursor.execute('DESCRIBE %s' % + self.connection.ops.quote_name(table_name)) # Increment over description info until we get to the geometry # column. for column, typ, null, key, default, extra in cursor.fetchall(): @@ -30,8 +31,8 @@ class MySQLIntrospection(DatabaseIntrospection): def supports_spatial_index(self, cursor, table_name): # Supported with MyISAM/Aria, or InnoDB on MySQL 5.7.5+/MariaDB 10.2.2+ storage_engine = self.get_storage_engine(cursor, table_name) - if storage_engine == "InnoDB": + if storage_engine == 'InnoDB': return self.connection.mysql_version >= ( (10, 2, 2) if self.connection.mysql_is_mariadb else (5, 7, 5) ) - return storage_engine in ("MyISAM", "Aria") + return storage_engine in ('MyISAM', 'Aria') diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/operations.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/operations.py index 728e93a..3ca609f 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/operations.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/operations.py @@ -1,6 +1,8 @@ from django.contrib.gis.db import models from django.contrib.gis.db.backends.base.adapter import WKTAdapter -from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations +from django.contrib.gis.db.backends.base.operations import ( + BaseSpatialOperations, +) from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.geos.geometry import GEOSGeometryBase from django.contrib.gis.geos.prototypes.io import wkb_r @@ -10,8 +12,8 @@ from django.utils.functional import cached_property class MySQLOperations(BaseSpatialOperations, DatabaseOperations): - name = "mysql" - geom_func_prefix = "ST_" + name = 'mysql' + geom_func_prefix = 'ST_' Adapter = WKTAdapter @@ -25,71 +27,53 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): @cached_property def select(self): - return self.geom_func_prefix + "AsBinary(%s)" + return self.geom_func_prefix + 'AsBinary(%s)' @cached_property def from_text(self): - return self.geom_func_prefix + "GeomFromText" + return self.geom_func_prefix + 'GeomFromText' @cached_property def gis_operators(self): operators = { - "bbcontains": SpatialOperator( - func="MBRContains" - ), # For consistency w/PostGIS API - "bboverlaps": SpatialOperator(func="MBROverlaps"), # ... - "contained": SpatialOperator(func="MBRWithin"), # ... - "contains": SpatialOperator(func="ST_Contains"), - "crosses": SpatialOperator(func="ST_Crosses"), - "disjoint": SpatialOperator(func="ST_Disjoint"), - "equals": SpatialOperator(func="ST_Equals"), - "exact": SpatialOperator(func="ST_Equals"), - "intersects": SpatialOperator(func="ST_Intersects"), - "overlaps": SpatialOperator(func="ST_Overlaps"), - "same_as": SpatialOperator(func="ST_Equals"), - "touches": SpatialOperator(func="ST_Touches"), - "within": SpatialOperator(func="ST_Within"), + 'bbcontains': SpatialOperator(func='MBRContains'), # For consistency w/PostGIS API + 'bboverlaps': SpatialOperator(func='MBROverlaps'), # ... + 'contained': SpatialOperator(func='MBRWithin'), # ... + 'contains': SpatialOperator(func='ST_Contains'), + 'crosses': SpatialOperator(func='ST_Crosses'), + 'disjoint': SpatialOperator(func='ST_Disjoint'), + 'equals': SpatialOperator(func='ST_Equals'), + 'exact': SpatialOperator(func='ST_Equals'), + 'intersects': SpatialOperator(func='ST_Intersects'), + 'overlaps': SpatialOperator(func='ST_Overlaps'), + 'same_as': SpatialOperator(func='ST_Equals'), + 'touches': SpatialOperator(func='ST_Touches'), + 'within': SpatialOperator(func='ST_Within'), } if self.connection.mysql_is_mariadb: - operators["relate"] = SpatialOperator(func="ST_Relate") + operators['relate'] = SpatialOperator(func='ST_Relate') return operators disallowed_aggregates = ( - models.Collect, - models.Extent, - models.Extent3D, - models.MakeLine, + models.Collect, models.Extent, models.Extent3D, models.MakeLine, models.Union, ) @cached_property def unsupported_functions(self): unsupported = { - "AsGML", - "AsKML", - "AsSVG", - "Azimuth", - "BoundingCircle", - "ForcePolygonCW", - "GeometryDistance", - "LineLocatePoint", - "MakeValid", - "MemSize", - "Perimeter", - "PointOnSurface", - "Reverse", - "Scale", - "SnapToGrid", - "Transform", - "Translate", + 'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle', + 'ForcePolygonCW', 'GeometryDistance', 'LineLocatePoint', + 'MakeValid', 'MemSize', 'Perimeter', 'PointOnSurface', 'Reverse', + 'Scale', 'SnapToGrid', 'Transform', 'Translate', } if self.connection.mysql_is_mariadb: - unsupported.remove("PointOnSurface") - unsupported.update({"GeoHash", "IsValid"}) + unsupported.remove('PointOnSurface') + unsupported.update({'GeoHash', 'IsValid'}) if self.connection.mysql_version < (10, 2, 4): - unsupported.add("AsGeoJSON") + unsupported.add('AsGeoJSON') elif self.connection.mysql_version < (5, 7, 5): - unsupported.update({"AsGeoJSON", "GeoHash", "IsValid"}) + unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'}) return unsupported def geo_db_type(self, f): @@ -100,12 +84,10 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError( - "Only numeric values of degree units are allowed on " - "geodetic distance queries." + 'Only numeric values of degree units are allowed on ' + 'geodetic distance queries.' ) - dist_param = getattr( - value, Distance.unit_attname(f.units_name(self.connection)) - ) + dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param] @@ -123,5 +105,4 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): if srid: geom.srid = srid return geom - return converter diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/schema.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/schema.py index 28f7048..679b2bc 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/schema.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/mysql/schema.py @@ -4,12 +4,12 @@ from django.contrib.gis.db.models import GeometryField from django.db import OperationalError from django.db.backends.mysql.schema import DatabaseSchemaEditor -logger = logging.getLogger("django.contrib.gis") +logger = logging.getLogger('django.contrib.gis') class MySQLGISSchemaEditor(DatabaseSchemaEditor): - sql_add_spatial_index = "CREATE SPATIAL INDEX %(index)s ON %(table)s(%(column)s)" - sql_drop_spatial_index = "DROP INDEX %(index)s ON %(table)s" + sql_add_spatial_index = 'CREATE SPATIAL INDEX %(index)s ON %(table)s(%(column)s)' + sql_drop_spatial_index = 'DROP INDEX %(index)s ON %(table)s' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -18,10 +18,7 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): def skip_default(self, field): # Geometry fields are stored as BLOB/TEXT, for which MySQL < 8.0.13 and # MariaDB < 10.2.1 don't support defaults. - if ( - isinstance(field, GeometryField) - and not self._supports_limited_data_type_defaults - ): + if isinstance(field, GeometryField) and not self._supports_limited_data_type_defaults: return True return super().skip_default(field) @@ -32,11 +29,10 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): qn = self.connection.ops.quote_name db_table = model._meta.db_table self.geometry_sql.append( - self.sql_add_spatial_index - % { - "index": qn(self._create_spatial_index_name(model, field)), - "table": qn(db_table), - "column": qn(field.column), + self.sql_add_spatial_index % { + 'index': qn(self._create_spatial_index_name(model, field)), + 'table': qn(db_table), + 'column': qn(field.column), } ) return column_sql @@ -53,22 +49,21 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): if isinstance(field, GeometryField) and field.spatial_index: qn = self.connection.ops.quote_name sql = self.sql_drop_spatial_index % { - "index": qn(self._create_spatial_index_name(model, field)), - "table": qn(model._meta.db_table), + 'index': qn(self._create_spatial_index_name(model, field)), + 'table': qn(model._meta.db_table), } try: self.execute(sql) except OperationalError: logger.error( "Couldn't remove spatial index: %s (may be expected " - "if your storage engine doesn't support them).", - sql, + "if your storage engine doesn't support them).", sql ) super().remove_field(model, field) def _create_spatial_index_name(self, model, field): - return "%s_%s_id" % (model._meta.db_table, field.column) + return '%s_%s_id' % (model._meta.db_table, field.column) def create_spatial_indexes(self): for sql in self.geometry_sql: @@ -77,7 +72,6 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor): except OperationalError: logger.error( "Cannot create SPATIAL INDEX %s. Only MyISAM and (as of " - "MySQL 5.7.5) InnoDB support them.", - sql, + "MySQL 5.7.5) InnoDB support them.", sql ) self.geometry_sql = [] diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/adapter.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/adapter.py index 7556b12..3f96178 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/adapter.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/adapter.py @@ -19,9 +19,7 @@ class OracleSpatialAdapter(WKTAdapter): if self._polygon_must_be_fixed(geom): geom = self._fix_polygon(geom) elif isinstance(geom, GeometryCollection): - if any( - isinstance(g, Polygon) and self._polygon_must_be_fixed(g) for g in geom - ): + if any(isinstance(g, Polygon) and self._polygon_must_be_fixed(g) for g in geom): geom = self._fix_geometry_collection(geom) self.wkt = geom.wkt @@ -29,9 +27,12 @@ class OracleSpatialAdapter(WKTAdapter): @staticmethod def _polygon_must_be_fixed(poly): - return not poly.empty and ( - not poly.exterior_ring.is_counterclockwise - or any(x.is_counterclockwise for x in poly) + return ( + not poly.empty and + ( + not poly.exterior_ring.is_counterclockwise or + any(x.is_counterclockwise for x in poly) + ) ) @classmethod diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/base.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/base.py index d43516a..0093ef8 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/base.py @@ -1,4 +1,6 @@ -from django.db.backends.oracle.base import DatabaseWrapper as OracleDatabaseWrapper +from django.db.backends.oracle.base import ( + DatabaseWrapper as OracleDatabaseWrapper, +) from .features import DatabaseFeatures from .introspection import OracleIntrospection diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/features.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/features.py index 7ea4709..c458eb3 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/features.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/features.py @@ -11,4 +11,4 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures): supports_perimeter_geodetic = True supports_dwithin_distance_expr = False supports_tolerance_parameter = True - unsupported_geojson_options = {"bbox", "crs", "precision"} + unsupported_geojson_options = {'bbox', 'crs', 'precision'} diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/introspection.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/introspection.py index 096fee5..493fe74 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/introspection.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/introspection.py @@ -12,7 +12,7 @@ class OracleIntrospection(DatabaseIntrospection): def data_types_reverse(self): return { **super().data_types_reverse, - cx_Oracle.OBJECT: "GeometryField", + cx_Oracle.OBJECT: 'GeometryField', } def get_geometry_type(self, table_name, description): @@ -22,26 +22,26 @@ class OracleIntrospection(DatabaseIntrospection): cursor.execute( 'SELECT "DIMINFO", "SRID" FROM "USER_SDO_GEOM_METADATA" ' 'WHERE "TABLE_NAME"=%s AND "COLUMN_NAME"=%s', - (table_name.upper(), description.name.upper()), + (table_name.upper(), description.name.upper()) ) row = cursor.fetchone() except Exception as exc: raise Exception( - "Could not find entry in USER_SDO_GEOM_METADATA " + 'Could not find entry in USER_SDO_GEOM_METADATA ' 'corresponding to "%s"."%s"' % (table_name, description.name) ) from exc # TODO: Research way to find a more specific geometry field type for # the column's contents. - field_type = "GeometryField" + field_type = 'GeometryField' # Getting the field parameters. field_params = {} dim, srid = row if srid != 4326: - field_params["srid"] = srid + field_params['srid'] = srid # Size of object array (SDO_DIM_ARRAY) is number of dimensions. dim = dim.size() if dim != 2: - field_params["dim"] = dim + field_params['dim'] = dim return field_type, field_params diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/models.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/models.py index f06f731..6876eec 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/models.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/models.py @@ -19,12 +19,12 @@ class OracleGeometryColumns(models.Model): # TODO: Add support for `diminfo` column (type MDSYS.SDO_DIM_ARRAY). class Meta: - app_label = "gis" - db_table = "USER_SDO_GEOM_METADATA" + app_label = 'gis' + db_table = 'USER_SDO_GEOM_METADATA' managed = False def __str__(self): - return "%s - %s (SRID: %s)" % (self.table_name, self.column_name, self.srid) + return '%s - %s (SRID: %s)' % (self.table_name, self.column_name, self.srid) @classmethod def table_name_col(cls): @@ -32,7 +32,7 @@ class OracleGeometryColumns(models.Model): Return the name of the metadata column used to store the feature table name. """ - return "table_name" + return 'table_name' @classmethod def geom_col_name(cls): @@ -40,7 +40,7 @@ class OracleGeometryColumns(models.Model): Return the name of the metadata column used to store the feature geometry column. """ - return "column_name" + return 'column_name' class OracleSpatialRefSys(models.Model, SpatialRefSysMixin): @@ -55,8 +55,8 @@ class OracleSpatialRefSys(models.Model, SpatialRefSysMixin): cs_bounds = models.PolygonField(null=True) class Meta: - app_label = "gis" - db_table = "CS_SRS" + app_label = 'gis' + db_table = 'CS_SRS' managed = False @property diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/operations.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/operations.py index ba7e3ca..9b363aa 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/operations.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/operations.py @@ -10,7 +10,9 @@ import re from django.contrib.gis.db import models -from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations +from django.contrib.gis.db.backends.base.operations import ( + BaseSpatialOperations, +) from django.contrib.gis.db.backends.oracle.adapter import OracleSpatialAdapter from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.geos.geometry import GEOSGeometry, GEOSGeometryBase @@ -18,7 +20,7 @@ from django.contrib.gis.geos.prototypes.io import wkb_r from django.contrib.gis.measure import Distance from django.db.backends.oracle.operations import DatabaseOperations -DEFAULT_TOLERANCE = "0.05" +DEFAULT_TOLERANCE = '0.05' class SDOOperator(SpatialOperator): @@ -30,63 +32,57 @@ class SDODWithin(SpatialOperator): class SDODisjoint(SpatialOperator): - sql_template = ( - "SDO_GEOM.RELATE(%%(lhs)s, 'DISJOINT', %%(rhs)s, %s) = 'DISJOINT'" - % DEFAULT_TOLERANCE - ) + sql_template = "SDO_GEOM.RELATE(%%(lhs)s, 'DISJOINT', %%(rhs)s, %s) = 'DISJOINT'" % DEFAULT_TOLERANCE class SDORelate(SpatialOperator): sql_template = "SDO_RELATE(%(lhs)s, %(rhs)s, 'mask=%(mask)s') = 'TRUE'" def check_relate_argument(self, arg): - masks = ( - "TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|" - "CONTAINS|COVERS|ANYINTERACT|ON" - ) - mask_regex = re.compile(r"^(%s)(\+(%s))*$" % (masks, masks), re.I) + masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' + mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) if not isinstance(arg, str) or not mask_regex.match(arg): raise ValueError('Invalid SDO_RELATE mask: "%s"' % arg) def as_sql(self, connection, lookup, template_params, sql_params): - template_params["mask"] = sql_params[-1] + template_params['mask'] = sql_params[-1] return super().as_sql(connection, lookup, template_params, sql_params[:-1]) class OracleOperations(BaseSpatialOperations, DatabaseOperations): - name = "oracle" + name = 'oracle' oracle = True disallowed_aggregates = (models.Collect, models.Extent3D, models.MakeLine) Adapter = OracleSpatialAdapter - extent = "SDO_AGGR_MBR" - unionagg = "SDO_AGGR_UNION" + extent = 'SDO_AGGR_MBR' + unionagg = 'SDO_AGGR_UNION' - from_text = "SDO_GEOMETRY" + from_text = 'SDO_GEOMETRY' function_names = { - "Area": "SDO_GEOM.SDO_AREA", - "AsGeoJSON": "SDO_UTIL.TO_GEOJSON", - "AsWKB": "SDO_UTIL.TO_WKBGEOMETRY", - "AsWKT": "SDO_UTIL.TO_WKTGEOMETRY", - "BoundingCircle": "SDO_GEOM.SDO_MBC", - "Centroid": "SDO_GEOM.SDO_CENTROID", - "Difference": "SDO_GEOM.SDO_DIFFERENCE", - "Distance": "SDO_GEOM.SDO_DISTANCE", - "Envelope": "SDO_GEOM_MBR", - "Intersection": "SDO_GEOM.SDO_INTERSECTION", - "IsValid": "SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT", - "Length": "SDO_GEOM.SDO_LENGTH", - "NumGeometries": "SDO_UTIL.GETNUMELEM", - "NumPoints": "SDO_UTIL.GETNUMVERTICES", - "Perimeter": "SDO_GEOM.SDO_LENGTH", - "PointOnSurface": "SDO_GEOM.SDO_POINTONSURFACE", - "Reverse": "SDO_UTIL.REVERSE_LINESTRING", - "SymDifference": "SDO_GEOM.SDO_XOR", - "Transform": "SDO_CS.TRANSFORM", - "Union": "SDO_GEOM.SDO_UNION", + 'Area': 'SDO_GEOM.SDO_AREA', + 'AsGeoJSON': 'SDO_UTIL.TO_GEOJSON', + 'AsWKB': 'SDO_UTIL.TO_WKBGEOMETRY', + 'AsWKT': 'SDO_UTIL.TO_WKTGEOMETRY', + 'BoundingCircle': 'SDO_GEOM.SDO_MBC', + 'Centroid': 'SDO_GEOM.SDO_CENTROID', + 'Difference': 'SDO_GEOM.SDO_DIFFERENCE', + 'Distance': 'SDO_GEOM.SDO_DISTANCE', + 'Envelope': 'SDO_GEOM_MBR', + 'Intersection': 'SDO_GEOM.SDO_INTERSECTION', + 'IsValid': 'SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT', + 'Length': 'SDO_GEOM.SDO_LENGTH', + 'NumGeometries': 'SDO_UTIL.GETNUMELEM', + 'NumPoints': 'SDO_UTIL.GETNUMVERTICES', + 'Perimeter': 'SDO_GEOM.SDO_LENGTH', + 'PointOnSurface': 'SDO_GEOM.SDO_POINTONSURFACE', + 'Reverse': 'SDO_UTIL.REVERSE_LINESTRING', + 'SymDifference': 'SDO_GEOM.SDO_XOR', + 'Transform': 'SDO_CS.TRANSFORM', + 'Union': 'SDO_GEOM.SDO_UNION', } # We want to get SDO Geometries as WKT because it is much easier to @@ -94,40 +90,28 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): # However, this adversely affects performance (i.e., Java is called # to convert to WKT on every query). If someone wishes to write a # SDO_GEOMETRY(...) parser in Python, let me know =) - select = "SDO_UTIL.TO_WKBGEOMETRY(%s)" + select = 'SDO_UTIL.TO_WKBGEOMETRY(%s)' gis_operators = { - "contains": SDOOperator(func="SDO_CONTAINS"), - "coveredby": SDOOperator(func="SDO_COVEREDBY"), - "covers": SDOOperator(func="SDO_COVERS"), - "disjoint": SDODisjoint(), - "intersects": SDOOperator( - func="SDO_OVERLAPBDYINTERSECT" - ), # TODO: Is this really the same as ST_Intersects()? - "equals": SDOOperator(func="SDO_EQUAL"), - "exact": SDOOperator(func="SDO_EQUAL"), - "overlaps": SDOOperator(func="SDO_OVERLAPS"), - "same_as": SDOOperator(func="SDO_EQUAL"), - # Oracle uses a different syntax, e.g., 'mask=inside+touch' - "relate": SDORelate(), - "touches": SDOOperator(func="SDO_TOUCH"), - "within": SDOOperator(func="SDO_INSIDE"), - "dwithin": SDODWithin(), + 'contains': SDOOperator(func='SDO_CONTAINS'), + 'coveredby': SDOOperator(func='SDO_COVEREDBY'), + 'covers': SDOOperator(func='SDO_COVERS'), + 'disjoint': SDODisjoint(), + 'intersects': SDOOperator(func='SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? + 'equals': SDOOperator(func='SDO_EQUAL'), + 'exact': SDOOperator(func='SDO_EQUAL'), + 'overlaps': SDOOperator(func='SDO_OVERLAPS'), + 'same_as': SDOOperator(func='SDO_EQUAL'), + 'relate': SDORelate(), # Oracle uses a different syntax, e.g., 'mask=inside+touch' + 'touches': SDOOperator(func='SDO_TOUCH'), + 'within': SDOOperator(func='SDO_INSIDE'), + 'dwithin': SDODWithin(), } unsupported_functions = { - "AsKML", - "AsSVG", - "Azimuth", - "ForcePolygonCW", - "GeoHash", - "GeometryDistance", - "LineLocatePoint", - "MakeValid", - "MemSize", - "Scale", - "SnapToGrid", - "Translate", + 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash', + 'GeometryDistance', 'LineLocatePoint', 'MakeValid', 'MemSize', + 'Scale', 'SnapToGrid', 'Translate', } def geo_quote_name(self, name): @@ -140,17 +124,15 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): # table. ext_geom = GEOSGeometry(memoryview(clob.read())) gtype = str(ext_geom.geom_type) - if gtype == "Polygon": + if gtype == 'Polygon': # Construct the 4-tuple from the coordinates in the polygon. shell = ext_geom.shell ll, ur = shell[0][:2], shell[2][:2] - elif gtype == "Point": + elif gtype == 'Point': ll = ext_geom.coords[:2] ur = ll else: - raise Exception( - "Unexpected geometry type returned for extent: %s" % gtype - ) + raise Exception('Unexpected geometry type returned for extent: %s' % gtype) xmin, ymin = ll xmax, ymax = ur return (xmin, ymin, xmax, ymax) @@ -163,7 +145,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): backends, no stored procedure is necessary and it's the same for all geometry types. """ - return "MDSYS.SDO_GEOMETRY" + return 'MDSYS.SDO_GEOMETRY' def get_distance(self, f, value, lookup_type): """ @@ -179,47 +161,47 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): if f.geodetic(self.connection): dist_param = value.m else: - dist_param = getattr( - value, Distance.unit_attname(f.units_name(self.connection)) - ) + dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value # dwithin lookups on Oracle require a special string parameter # that starts with "distance=". - if lookup_type == "dwithin": - dist_param = "distance=%s" % dist_param + if lookup_type == 'dwithin': + dist_param = 'distance=%s' % dist_param return [dist_param] def get_geom_placeholder(self, f, value, compiler): if value is None: - return "NULL" + return 'NULL' return super().get_geom_placeholder(f, value, compiler) def spatial_aggregate_name(self, agg_name): """ Return the spatial aggregate SQL name. """ - agg_name = "unionagg" if agg_name.lower() == "union" else agg_name.lower() + agg_name = 'unionagg' if agg_name.lower() == 'union' else agg_name.lower() return getattr(self, agg_name) # Routines for getting the OGC-compliant models. def geometry_columns(self): - from django.contrib.gis.db.backends.oracle.models import OracleGeometryColumns - + from django.contrib.gis.db.backends.oracle.models import ( + OracleGeometryColumns, + ) return OracleGeometryColumns def spatial_ref_sys(self): - from django.contrib.gis.db.backends.oracle.models import OracleSpatialRefSys - + from django.contrib.gis.db.backends.oracle.models import ( + OracleSpatialRefSys, + ) return OracleSpatialRefSys def modify_insert_params(self, placeholder, params): """Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial backend due to #10888. """ - if placeholder == "NULL": + if placeholder == 'NULL': return [] return super().modify_insert_params(placeholder, params) @@ -236,8 +218,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): if srid: geom.srid = srid return geom - return converter def get_area_att_for_field(self, field): - return "sq_m" + return 'sq_m' diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/schema.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/schema.py index d8fdd53..7bccee7 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/schema.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/oracle/schema.py @@ -4,7 +4,7 @@ from django.db.backends.utils import strip_quotes, truncate_name class OracleGISSchemaEditor(DatabaseSchemaEditor): - sql_add_geometry_metadata = """ + sql_add_geometry_metadata = (""" INSERT INTO USER_SDO_GEOM_METADATA ("TABLE_NAME", "COLUMN_NAME", "DIMINFO", "SRID") VALUES ( @@ -15,18 +15,13 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor): MDSYS.SDO_DIM_ELEMENT('LAT', %(dim1)s, %(dim3)s, %(tolerance)s) ), %(srid)s - )""" - sql_add_spatial_index = ( - "CREATE INDEX %(index)s ON %(table)s(%(column)s) " - "INDEXTYPE IS MDSYS.SPATIAL_INDEX" - ) - sql_drop_spatial_index = "DROP INDEX %(index)s" - sql_clear_geometry_table_metadata = ( - "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s" - ) + )""") + sql_add_spatial_index = 'CREATE INDEX %(index)s ON %(table)s(%(column)s) INDEXTYPE IS MDSYS.SPATIAL_INDEX' + sql_drop_spatial_index = 'DROP INDEX %(index)s' + sql_clear_geometry_table_metadata = 'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s' sql_clear_geometry_field_metadata = ( - "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s " - "AND COLUMN_NAME = %(column)s" + 'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s ' + 'AND COLUMN_NAME = %(column)s' ) def __init__(self, *args, **kwargs): @@ -41,27 +36,23 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor): if isinstance(field, GeometryField): db_table = model._meta.db_table self.geometry_sql.append( - self.sql_add_geometry_metadata - % { - "table": self.geo_quote_name(db_table), - "column": self.geo_quote_name(field.column), - "dim0": field._extent[0], - "dim1": field._extent[1], - "dim2": field._extent[2], - "dim3": field._extent[3], - "tolerance": field._tolerance, - "srid": field.srid, + self.sql_add_geometry_metadata % { + 'table': self.geo_quote_name(db_table), + 'column': self.geo_quote_name(field.column), + 'dim0': field._extent[0], + 'dim1': field._extent[1], + 'dim2': field._extent[2], + 'dim3': field._extent[3], + 'tolerance': field._tolerance, + 'srid': field.srid, } ) if field.spatial_index: self.geometry_sql.append( - self.sql_add_spatial_index - % { - "index": self.quote_name( - self._create_spatial_index_name(model, field) - ), - "table": self.quote_name(db_table), - "column": self.quote_name(field.column), + self.sql_add_spatial_index % { + 'index': self.quote_name(self._create_spatial_index_name(model, field)), + 'table': self.quote_name(db_table), + 'column': self.quote_name(field.column), } ) return column_sql @@ -72,12 +63,9 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor): def delete_model(self, model): super().delete_model(model) - self.execute( - self.sql_clear_geometry_table_metadata - % { - "table": self.geo_quote_name(model._meta.db_table), - } - ) + self.execute(self.sql_clear_geometry_table_metadata % { + 'table': self.geo_quote_name(model._meta.db_table), + }) def add_field(self, model, field): super().add_field(model, field) @@ -85,22 +73,14 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor): def remove_field(self, model, field): if isinstance(field, GeometryField): - self.execute( - self.sql_clear_geometry_field_metadata - % { - "table": self.geo_quote_name(model._meta.db_table), - "column": self.geo_quote_name(field.column), - } - ) + self.execute(self.sql_clear_geometry_field_metadata % { + 'table': self.geo_quote_name(model._meta.db_table), + 'column': self.geo_quote_name(field.column), + }) if field.spatial_index: - self.execute( - self.sql_drop_spatial_index - % { - "index": self.quote_name( - self._create_spatial_index_name(model, field) - ), - } - ) + self.execute(self.sql_drop_spatial_index % { + 'index': self.quote_name(self._create_spatial_index_name(model, field)), + }) super().remove_field(model, field) def run_geometry_sql(self): @@ -111,6 +91,4 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor): def _create_spatial_index_name(self, model, field): # Oracle doesn't allow object names > 30 characters. Use this scheme # instead of self._create_index_name() for backwards compatibility. - return truncate_name( - "%s_%s_id" % (strip_quotes(model._meta.db_table), field.column), 30 - ) + return truncate_name('%s_%s_id' % (strip_quotes(model._meta.db_table), field.column), 30) diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/adapter.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/adapter.py index 50e6c94..6611442 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/adapter.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/adapter.py @@ -31,9 +31,7 @@ class PostGISAdapter: if proto == ISQLQuote: return self else: - raise Exception( - "Error implementing psycopg2 protocol. Is psycopg2 installed?" - ) + raise Exception('Error implementing psycopg2 protocol. Is psycopg2 installed?') def __eq__(self, other): return isinstance(other, PostGISAdapter) and self.ewkb == other.ewkb @@ -62,9 +60,9 @@ class PostGISAdapter: """ if self.is_geometry: # Psycopg will figure out whether to use E'\\000' or '\000'. - return "%s(%s)" % ( - "ST_GeogFromWKB" if self.geography else "ST_GeomFromEWKB", - self._adapter.getquoted().decode(), + return '%s(%s)' % ( + 'ST_GeogFromWKB' if self.geography else 'ST_GeomFromEWKB', + self._adapter.getquoted().decode() ) else: # For rasters, add explicit type cast to WKB string. diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/base.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/base.py index 87a3000..5b93d7c 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/base.py @@ -14,7 +14,7 @@ class DatabaseWrapper(Psycopg2DatabaseWrapper): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if kwargs.get("alias", "") != NO_DB_ALIAS: + if kwargs.get('alias', '') != NO_DB_ALIAS: self.features = DatabaseFeatures(self) self.ops = PostGISOperations(self) self.introspection = PostGISIntrospection(self) diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/const.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/const.py index 2f4393d..193aa3f 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/const.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/const.py @@ -15,23 +15,13 @@ POSTGIS_TO_GDAL = [1, 1, 1, 3, 1, 3, 2, 5, 4, None, 6, 7, None, None] # # Scale, origin, and skew have x and y values. PostGIS currently uses # a fixed endianness (1) and there is only one version (0). -POSTGIS_HEADER_STRUCTURE = "B H H d d d d d d i H H" +POSTGIS_HEADER_STRUCTURE = 'B H H d d d d d d i H H' # Lookup values to convert GDAL pixel types to struct characters. This is # used to pack and unpack the pixel values of PostGIS raster bands. GDAL_TO_STRUCT = [ - None, - "B", - "H", - "h", - "L", - "l", - "f", - "d", - None, - None, - None, - None, + None, 'B', 'H', 'h', 'L', 'l', 'f', 'd', + None, None, None, None, ] # Size of the packed value in bytes for different numerical types. @@ -39,24 +29,24 @@ GDAL_TO_STRUCT = [ # when decomposing them into GDALRasters. # See https://docs.python.org/library/struct.html#format-characters STRUCT_SIZE = { - "b": 1, # Signed char - "B": 1, # Unsigned char - "?": 1, # _Bool - "h": 2, # Short - "H": 2, # Unsigned short - "i": 4, # Integer - "I": 4, # Unsigned Integer - "l": 4, # Long - "L": 4, # Unsigned Long - "f": 4, # Float - "d": 8, # Double + 'b': 1, # Signed char + 'B': 1, # Unsigned char + '?': 1, # _Bool + 'h': 2, # Short + 'H': 2, # Unsigned short + 'i': 4, # Integer + 'I': 4, # Unsigned Integer + 'l': 4, # Long + 'L': 4, # Unsigned Long + 'f': 4, # Float + 'd': 8, # Double } # Pixel type specifies type of pixel values in a band. Storage flag specifies # whether the band data is stored as part of the datum or is to be found on the # server's filesystem. There are currently 11 supported pixel value types, so 4 # bits are enough to account for all. Reserve the upper 4 bits for generic -# flags. See -# https://trac.osgeo.org/postgis/wiki/WKTRaster/RFC/RFC1_V0SerialFormat#Pixeltypeandstorageflag +# flags. +# See https://trac.osgeo.org/postgis/wiki/WKTRaster/RFC/RFC1_V0SerialFormat#Pixeltypeandstorageflag BANDTYPE_PIXTYPE_MASK = 0x0F BANDTYPE_FLAG_HASNODATA = 1 << 6 diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/introspection.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/introspection.py index 766c0fa..0647012 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/introspection.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/introspection.py @@ -6,11 +6,11 @@ class PostGISIntrospection(DatabaseIntrospection): postgis_oid_lookup = {} # Populated when introspection is performed. ignored_tables = DatabaseIntrospection.ignored_tables + [ - "geography_columns", - "geometry_columns", - "raster_columns", - "spatial_ref_sys", - "raster_overviews", + 'geography_columns', + 'geometry_columns', + 'raster_columns', + 'spatial_ref_sys', + 'raster_overviews', ] def get_field_type(self, data_type, description): @@ -21,15 +21,9 @@ class PostGISIntrospection(DatabaseIntrospection): # requests upon connection initialization, the `data_types_reverse` # dictionary isn't updated until introspection is performed here. with self.connection.cursor() as cursor: - cursor.execute( - "SELECT oid, typname " - "FROM pg_type " - "WHERE typname IN ('geometry', 'geography')" - ) + cursor.execute("SELECT oid, typname FROM pg_type WHERE typname IN ('geometry', 'geography')") self.postgis_oid_lookup = dict(cursor.fetchall()) - self.data_types_reverse.update( - (oid, "GeometryField") for oid in self.postgis_oid_lookup - ) + self.data_types_reverse.update((oid, 'GeometryField') for oid in self.postgis_oid_lookup) return super().get_field_type(data_type, description) def get_geometry_type(self, table_name, description): @@ -40,32 +34,27 @@ class PostGISIntrospection(DatabaseIntrospection): metadata tables to determine the geometry type. """ with self.connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(""" SELECT t.coord_dimension, t.srid, t.type FROM ( SELECT * FROM geometry_columns UNION ALL SELECT * FROM geography_columns ) AS t WHERE t.f_table_name = %s AND t.f_geometry_column = %s - """, - (table_name, description.name), - ) + """, (table_name, description.name)) row = cursor.fetchone() if not row: - raise Exception( - 'Could not find a geometry or geography column for "%s"."%s"' - % (table_name, description.name) - ) + raise Exception('Could not find a geometry or geography column for "%s"."%s"' % + (table_name, description.name)) dim, srid, field_type = row # OGRGeomType does not require GDAL and makes it easy to convert # from OGC geom type name to Django field. field_type = OGRGeomType(field_type).django # Getting any GeometryField keyword arguments that are not the default. field_params = {} - if self.postgis_oid_lookup.get(description.type_code) == "geography": - field_params["geography"] = True + if self.postgis_oid_lookup.get(description.type_code) == 'geography': + field_params['geography'] = True if srid != 4326: - field_params["srid"] = srid + field_params['srid'] = srid if dim != 2: - field_params["dim"] = dim + field_params['dim'] = dim return field_type, field_params diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/models.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/models.py index b7b5682..0cd4d55 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/models.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/models.py @@ -10,7 +10,6 @@ class PostGISGeometryColumns(models.Model): The 'geometry_columns' view from PostGIS. See the PostGIS documentation at Ch. 4.3.2. """ - f_table_catalog = models.CharField(max_length=256) f_table_schema = models.CharField(max_length=256) f_table_name = models.CharField(max_length=256) @@ -20,12 +19,12 @@ class PostGISGeometryColumns(models.Model): type = models.CharField(max_length=30) class Meta: - app_label = "gis" - db_table = "geometry_columns" + app_label = 'gis' + db_table = 'geometry_columns' managed = False def __str__(self): - return "%s.%s - %dD %s field (SRID: %d)" % ( + return '%s.%s - %dD %s field (SRID: %d)' % ( self.f_table_name, self.f_geometry_column, self.coord_dimension, @@ -39,7 +38,7 @@ class PostGISGeometryColumns(models.Model): Return the name of the metadata column used to store the feature table name. """ - return "f_table_name" + return 'f_table_name' @classmethod def geom_col_name(cls): @@ -47,7 +46,7 @@ class PostGISGeometryColumns(models.Model): Return the name of the metadata column used to store the feature geometry column. """ - return "f_geometry_column" + return 'f_geometry_column' class PostGISSpatialRefSys(models.Model, SpatialRefSysMixin): @@ -55,7 +54,6 @@ class PostGISSpatialRefSys(models.Model, SpatialRefSysMixin): The 'spatial_ref_sys' table from PostGIS. See the PostGIS documentation at Ch. 4.2.1. """ - srid = models.IntegerField(primary_key=True) auth_name = models.CharField(max_length=256) auth_srid = models.IntegerField() @@ -63,8 +61,8 @@ class PostGISSpatialRefSys(models.Model, SpatialRefSysMixin): proj4text = models.CharField(max_length=2048) class Meta: - app_label = "gis" - db_table = "spatial_ref_sys" + app_label = 'gis' + db_table = 'spatial_ref_sys' managed = False @property diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/operations.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/operations.py index 36bec4b..f068f28 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/operations.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/operations.py @@ -1,7 +1,9 @@ import re from django.conf import settings -from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations +from django.contrib.gis.db.backends.base.operations import ( + BaseSpatialOperations, +) from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.db.models import GeometryField, RasterField from django.contrib.gis.gdal import GDALRaster @@ -20,7 +22,7 @@ from .models import PostGISGeometryColumns, PostGISSpatialRefSys from .pgraster import from_pgraster # Identifier to mark raster lookups as bilateral. -BILATERAL = "bilateral" +BILATERAL = 'bilateral' class PostGISOperator(SpatialOperator): @@ -37,72 +39,56 @@ class PostGISOperator(SpatialOperator): def as_sql(self, connection, lookup, template_params, *args): if lookup.lhs.output_field.geography and not self.geography: - raise ValueError( - 'PostGIS geography does not support the "%s" ' - "function/operator." % (self.func or self.op,) - ) + raise ValueError('PostGIS geography does not support the "%s" ' + 'function/operator.' % (self.func or self.op,)) template_params = self.check_raster(lookup, template_params) return super().as_sql(connection, lookup, template_params, *args) def check_raster(self, lookup, template_params): - spheroid = lookup.rhs_params and lookup.rhs_params[-1] == "spheroid" + spheroid = lookup.rhs_params and lookup.rhs_params[-1] == 'spheroid' # Check which input is a raster. - lhs_is_raster = lookup.lhs.field.geom_type == "RASTER" + lhs_is_raster = lookup.lhs.field.geom_type == 'RASTER' rhs_is_raster = isinstance(lookup.rhs, GDALRaster) # Look for band indices and inject them if provided. if lookup.band_lhs is not None and lhs_is_raster: if not self.func: - raise ValueError( - "Band indices are not allowed for this operator, it works on bbox " - "only." - ) - template_params["lhs"] = "%s, %s" % ( - template_params["lhs"], - lookup.band_lhs, - ) + raise ValueError('Band indices are not allowed for this operator, it works on bbox only.') + template_params['lhs'] = '%s, %s' % (template_params['lhs'], lookup.band_lhs) if lookup.band_rhs is not None and rhs_is_raster: if not self.func: - raise ValueError( - "Band indices are not allowed for this operator, it works on bbox " - "only." - ) - template_params["rhs"] = "%s, %s" % ( - template_params["rhs"], - lookup.band_rhs, - ) + raise ValueError('Band indices are not allowed for this operator, it works on bbox only.') + template_params['rhs'] = '%s, %s' % (template_params['rhs'], lookup.band_rhs) # Convert rasters to polygons if necessary. if not self.raster or spheroid: # Operators without raster support. if lhs_is_raster: - template_params["lhs"] = "ST_Polygon(%s)" % template_params["lhs"] + template_params['lhs'] = 'ST_Polygon(%s)' % template_params['lhs'] if rhs_is_raster: - template_params["rhs"] = "ST_Polygon(%s)" % template_params["rhs"] + template_params['rhs'] = 'ST_Polygon(%s)' % template_params['rhs'] elif self.raster == BILATERAL: # Operators with raster support but don't support mixed (rast-geom) # lookups. if lhs_is_raster and not rhs_is_raster: - template_params["lhs"] = "ST_Polygon(%s)" % template_params["lhs"] + template_params['lhs'] = 'ST_Polygon(%s)' % template_params['lhs'] elif rhs_is_raster and not lhs_is_raster: - template_params["rhs"] = "ST_Polygon(%s)" % template_params["rhs"] + template_params['rhs'] = 'ST_Polygon(%s)' % template_params['rhs'] return template_params class ST_Polygon(Func): - function = "ST_Polygon" + function = 'ST_Polygon' def __init__(self, expr): super().__init__(expr) expr = self.source_expressions[0] if isinstance(expr, Value) and not expr._output_field_or_none: - self.source_expressions[0] = Value( - expr.value, output_field=RasterField(srid=expr.value.srid) - ) + self.source_expressions[0] = Value(expr.value, output_field=RasterField(srid=expr.value.srid)) @cached_property def output_field(self): @@ -110,70 +96,64 @@ class ST_Polygon(Func): class PostGISOperations(BaseSpatialOperations, DatabaseOperations): - name = "postgis" + name = 'postgis' postgis = True - geom_func_prefix = "ST_" + geom_func_prefix = 'ST_' Adapter = PostGISAdapter - collect = geom_func_prefix + "Collect" - extent = geom_func_prefix + "Extent" - extent3d = geom_func_prefix + "3DExtent" - length3d = geom_func_prefix + "3DLength" - makeline = geom_func_prefix + "MakeLine" - perimeter3d = geom_func_prefix + "3DPerimeter" - unionagg = geom_func_prefix + "Union" + collect = geom_func_prefix + 'Collect' + extent = geom_func_prefix + 'Extent' + extent3d = geom_func_prefix + '3DExtent' + length3d = geom_func_prefix + '3DLength' + makeline = geom_func_prefix + 'MakeLine' + perimeter3d = geom_func_prefix + '3DPerimeter' + unionagg = geom_func_prefix + 'Union' gis_operators = { - "bbcontains": PostGISOperator(op="~", raster=True), - "bboverlaps": PostGISOperator(op="&&", geography=True, raster=True), - "contained": PostGISOperator(op="@", raster=True), - "overlaps_left": PostGISOperator(op="&<", raster=BILATERAL), - "overlaps_right": PostGISOperator(op="&>", raster=BILATERAL), - "overlaps_below": PostGISOperator(op="&<|"), - "overlaps_above": PostGISOperator(op="|&>"), - "left": PostGISOperator(op="<<"), - "right": PostGISOperator(op=">>"), - "strictly_below": PostGISOperator(op="<<|"), - "strictly_above": PostGISOperator(op="|>>"), - "same_as": PostGISOperator(op="~=", raster=BILATERAL), - "exact": PostGISOperator(op="~=", raster=BILATERAL), # alias of same_as - "contains": PostGISOperator(func="ST_Contains", raster=BILATERAL), - "contains_properly": PostGISOperator( - func="ST_ContainsProperly", raster=BILATERAL - ), - "coveredby": PostGISOperator( - func="ST_CoveredBy", geography=True, raster=BILATERAL - ), - "covers": PostGISOperator(func="ST_Covers", geography=True, raster=BILATERAL), - "crosses": PostGISOperator(func="ST_Crosses"), - "disjoint": PostGISOperator(func="ST_Disjoint", raster=BILATERAL), - "equals": PostGISOperator(func="ST_Equals"), - "intersects": PostGISOperator( - func="ST_Intersects", geography=True, raster=BILATERAL - ), - "overlaps": PostGISOperator(func="ST_Overlaps", raster=BILATERAL), - "relate": PostGISOperator(func="ST_Relate"), - "touches": PostGISOperator(func="ST_Touches", raster=BILATERAL), - "within": PostGISOperator(func="ST_Within", raster=BILATERAL), - "dwithin": PostGISOperator(func="ST_DWithin", geography=True, raster=BILATERAL), + 'bbcontains': PostGISOperator(op='~', raster=True), + 'bboverlaps': PostGISOperator(op='&&', geography=True, raster=True), + 'contained': PostGISOperator(op='@', raster=True), + 'overlaps_left': PostGISOperator(op='&<', raster=BILATERAL), + 'overlaps_right': PostGISOperator(op='&>', raster=BILATERAL), + 'overlaps_below': PostGISOperator(op='&<|'), + 'overlaps_above': PostGISOperator(op='|&>'), + 'left': PostGISOperator(op='<<'), + 'right': PostGISOperator(op='>>'), + 'strictly_below': PostGISOperator(op='<<|'), + 'strictly_above': PostGISOperator(op='|>>'), + 'same_as': PostGISOperator(op='~=', raster=BILATERAL), + 'exact': PostGISOperator(op='~=', raster=BILATERAL), # alias of same_as + 'contains': PostGISOperator(func='ST_Contains', raster=BILATERAL), + 'contains_properly': PostGISOperator(func='ST_ContainsProperly', raster=BILATERAL), + 'coveredby': PostGISOperator(func='ST_CoveredBy', geography=True, raster=BILATERAL), + 'covers': PostGISOperator(func='ST_Covers', geography=True, raster=BILATERAL), + 'crosses': PostGISOperator(func='ST_Crosses'), + 'disjoint': PostGISOperator(func='ST_Disjoint', raster=BILATERAL), + 'equals': PostGISOperator(func='ST_Equals'), + 'intersects': PostGISOperator(func='ST_Intersects', geography=True, raster=BILATERAL), + 'overlaps': PostGISOperator(func='ST_Overlaps', raster=BILATERAL), + 'relate': PostGISOperator(func='ST_Relate'), + 'touches': PostGISOperator(func='ST_Touches', raster=BILATERAL), + 'within': PostGISOperator(func='ST_Within', raster=BILATERAL), + 'dwithin': PostGISOperator(func='ST_DWithin', geography=True, raster=BILATERAL), } unsupported_functions = set() - select = "%s::bytea" + select = '%s::bytea' select_extent = None @cached_property def function_names(self): function_names = { - "AsWKB": "ST_AsBinary", - "AsWKT": "ST_AsText", - "BoundingCircle": "ST_MinimumBoundingCircle", - "NumPoints": "ST_NPoints", + 'AsWKB': 'ST_AsBinary', + 'AsWKT': 'ST_AsText', + 'BoundingCircle': 'ST_MinimumBoundingCircle', + 'NumPoints': 'ST_NPoints', } if self.spatial_version < (2, 4, 0): - function_names["ForcePolygonCW"] = "ST_ForceRHR" + function_names['ForcePolygonCW'] = 'ST_ForceRHR' return function_names @cached_property @@ -185,13 +165,13 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): # can be mitigated by setting `POSTGIS_VERSION` with a 3-tuple # comprising user-supplied values for the major, minor, and # subminor revision of PostGIS. - if hasattr(settings, "POSTGIS_VERSION"): + if hasattr(settings, 'POSTGIS_VERSION'): version = settings.POSTGIS_VERSION else: # Run a basic query to check the status of the connection so we're # sure we only raise the error below if the problem comes from # PostGIS and not from PostgreSQL itself (see #24862). - self._get_postgis_func("version") + self._get_postgis_func('version') try: vtup = self.postgis_version_tuple() @@ -199,9 +179,9 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): raise ImproperlyConfigured( 'Cannot determine PostGIS version for database "%s" ' 'using command "SELECT postgis_lib_version()". ' - "GeoDjango requires at least PostGIS version 2.4. " - "Was the database created from a spatial database " - "template?" % self.connection.settings_dict["NAME"] + 'GeoDjango requires at least PostGIS version 2.3. ' + 'Was the database created from a spatial database ' + 'template?' % self.connection.settings_dict['NAME'] ) version = vtup[1:] return version @@ -214,7 +194,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): """ if box is None: return None - ll, ur = box[4:-1].split(",") + ll, ur = box[4:-1].split(',') xmin, ymin = map(float, ll.split()) xmax, ymax = map(float, ur.split()) return (xmin, ymin, xmax, ymax) @@ -227,7 +207,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): """ if box3d is None: return None - ll, ur = box3d[6:-1].split(",") + ll, ur = box3d[6:-1].split(',') xmin, ymin, zmin = map(float, ll.split()) xmax, ymax, zmax = map(float, ur.split()) return (xmin, ymin, zmin, xmax, ymax, zmax) @@ -236,24 +216,22 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): """ Return the database field type for the given spatial field. """ - if f.geom_type == "RASTER": - return "raster" + if f.geom_type == 'RASTER': + return 'raster' # Type-based geometries. # TODO: Support 'M' extension. if f.dim == 3: - geom_type = f.geom_type + "Z" + geom_type = f.geom_type + 'Z' else: geom_type = f.geom_type if f.geography: if f.srid != 4326: - raise NotSupportedError( - "PostGIS only supports geography columns with an SRID of 4326." - ) + raise NotSupportedError('PostGIS only supports geography columns with an SRID of 4326.') - return "geography(%s,%d)" % (geom_type, f.srid) + return 'geography(%s,%d)' % (geom_type, f.srid) else: - return "geometry(%s,%d)" % (geom_type, f.srid) + return 'geometry(%s,%d)' % (geom_type, f.srid) def get_distance(self, f, dist_val, lookup_type): """ @@ -276,16 +254,12 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): if geography: dist_param = value.m elif geodetic: - if lookup_type == "dwithin": - raise ValueError( - "Only numeric values of degree units are " - "allowed on geographic DWithin queries." - ) + if lookup_type == 'dwithin': + raise ValueError('Only numeric values of degree units are ' + 'allowed on geographic DWithin queries.') dist_param = value.m else: - dist_param = getattr( - value, Distance.unit_attname(f.units_name(self.connection)) - ) + dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value @@ -298,12 +272,12 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): not in the SRID of the field. Specifically, this routine will substitute in the ST_Transform() function call. """ - transform_func = self.spatial_function_name("Transform") - if hasattr(value, "as_sql"): + transform_func = self.spatial_function_name('Transform') + if hasattr(value, 'as_sql'): if value.field.srid == f.srid: - placeholder = "%s" + placeholder = '%s' else: - placeholder = "%s(%%s, %s)" % (transform_func, f.srid) + placeholder = '%s(%%s, %s)' % (transform_func, f.srid) return placeholder # Get the srid for this object @@ -315,9 +289,9 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): # Adding Transform() to the SQL placeholder if the value srid # is not equal to the field srid. if value_srid is None or value_srid == f.srid: - placeholder = "%s" + placeholder = '%s' else: - placeholder = "%s(%%s, %s)" % (transform_func, f.srid) + placeholder = '%s(%%s, %s)' % (transform_func, f.srid) return placeholder @@ -327,28 +301,28 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): """ # Close out the connection. See #9437. with self.connection.temporary_connection() as cursor: - cursor.execute("SELECT %s()" % func) + cursor.execute('SELECT %s()' % func) return cursor.fetchone()[0] def postgis_geos_version(self): "Return the version of the GEOS library used with PostGIS." - return self._get_postgis_func("postgis_geos_version") + return self._get_postgis_func('postgis_geos_version') def postgis_lib_version(self): "Return the version number of the PostGIS library used with PostgreSQL." - return self._get_postgis_func("postgis_lib_version") + return self._get_postgis_func('postgis_lib_version') def postgis_proj_version(self): """Return the version of the PROJ library used with PostGIS.""" - return self._get_postgis_func("postgis_proj_version") + return self._get_postgis_func('postgis_proj_version') def postgis_version(self): "Return PostGIS version number and compile-time options." - return self._get_postgis_func("postgis_version") + return self._get_postgis_func('postgis_version') def postgis_full_version(self): "Return PostGIS version number and compile-time options." - return self._get_postgis_func("postgis_full_version") + return self._get_postgis_func('postgis_full_version') def postgis_version_tuple(self): """ @@ -363,16 +337,16 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): Return the version of PROJ used by PostGIS as a tuple of the major, minor, and subminor release numbers. """ - proj_regex = re.compile(r"(\d+)\.(\d+)\.(\d+)") + proj_regex = re.compile(r'(\d+)\.(\d+)\.(\d+)') proj_ver_str = self.postgis_proj_version() m = proj_regex.search(proj_ver_str) if m: return tuple(map(int, m.groups())) else: - raise Exception("Could not determine PROJ version from PostGIS.") + raise Exception('Could not determine PROJ version from PostGIS.') def spatial_aggregate_name(self, agg_name): - if agg_name == "Extent3D": + if agg_name == 'Extent3D': return self.extent3d else: return self.geom_func_prefix + agg_name @@ -392,15 +366,15 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): return super().distance_expr_for_lookup( self._normalize_distance_lookup_arg(lhs), self._normalize_distance_lookup_arg(rhs), - **kwargs, + **kwargs ) @staticmethod def _normalize_distance_lookup_arg(arg): is_raster = ( - arg.field.geom_type == "RASTER" - if hasattr(arg, "field") - else isinstance(arg, GDALRaster) + arg.field.geom_type == 'RASTER' + if hasattr(arg, 'field') else + isinstance(arg, GDALRaster) ) return ST_Polygon(arg) if is_raster else arg @@ -410,8 +384,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): def converter(value, expression, connection): return None if value is None else GEOSGeometryBase(read(value), geom_class) - return converter def get_area_att_for_field(self, field): - return "sq_m" + return 'sq_m' diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/pgraster.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/pgraster.py index 2279434..bfdeb6f 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/pgraster.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/pgraster.py @@ -3,13 +3,8 @@ import struct from django.core.exceptions import ValidationError from .const import ( - BANDTYPE_FLAG_HASNODATA, - BANDTYPE_PIXTYPE_MASK, - GDAL_TO_POSTGIS, - GDAL_TO_STRUCT, - POSTGIS_HEADER_STRUCTURE, - POSTGIS_TO_GDAL, - STRUCT_SIZE, + BANDTYPE_FLAG_HASNODATA, BANDTYPE_PIXTYPE_MASK, GDAL_TO_POSTGIS, + GDAL_TO_STRUCT, POSTGIS_HEADER_STRUCTURE, POSTGIS_TO_GDAL, STRUCT_SIZE, ) @@ -17,14 +12,14 @@ def pack(structure, data): """ Pack data into hex string with little endian format. """ - return struct.pack("<" + structure, *data) + return struct.pack('<' + structure, *data) def unpack(structure, data): """ Unpack little endian hexlified binary string into a list. """ - return struct.unpack("<" + structure, bytes.fromhex(data)) + return struct.unpack('<' + structure, bytes.fromhex(data)) def chunk(data, index): @@ -51,7 +46,7 @@ def from_pgraster(data): while data: # Get pixel type for this band pixeltype_with_flags, data = chunk(data, 2) - pixeltype_with_flags = unpack("B", pixeltype_with_flags)[0] + pixeltype_with_flags = unpack('B', pixeltype_with_flags)[0] pixeltype = pixeltype_with_flags & BANDTYPE_PIXTYPE_MASK # Convert datatype from PostGIS to GDAL & get pack type and size @@ -67,11 +62,11 @@ def from_pgraster(data): # Chunk and unpack band data (pack size times nr of pixels) band, data = chunk(data, pack_size * header[10] * header[11]) - band_result = {"data": bytes.fromhex(band)} + band_result = {'data': bytes.fromhex(band)} # Set the nodata value if the nodata flag is set. if pixeltype_with_flags & BANDTYPE_FLAG_HASNODATA: - band_result["nodata_value"] = nodata + band_result['nodata_value'] = nodata # Append band data to band list bands.append(band_result) @@ -86,14 +81,13 @@ def from_pgraster(data): raise ValidationError("Band pixeltypes are not all equal.") return { - "srid": int(header[9]), - "width": header[10], - "height": header[11], - "datatype": pixeltypes[0], - "origin": (header[5], header[6]), - "scale": (header[3], header[4]), - "skew": (header[7], header[8]), - "bands": bands, + 'srid': int(header[9]), + 'width': header[10], 'height': header[11], + 'datatype': pixeltypes[0], + 'origin': (header[5], header[6]), + 'scale': (header[3], header[4]), + 'skew': (header[7], header[8]), + 'bands': bands, } @@ -105,18 +99,9 @@ def to_pgraster(rast): # the endianness and the PostGIS Raster Version, both are fixed by # PostGIS at the moment. rasterheader = ( - 1, - 0, - len(rast.bands), - rast.scale.x, - rast.scale.y, - rast.origin.x, - rast.origin.y, - rast.skew.x, - rast.skew.y, - rast.srs.srid, - rast.width, - rast.height, + 1, 0, len(rast.bands), rast.scale.x, rast.scale.y, + rast.origin.x, rast.origin.y, rast.skew.x, rast.skew.y, + rast.srs.srid, rast.width, rast.height, ) # Pack raster header. @@ -134,7 +119,7 @@ def to_pgraster(rast): # For example, if the byte value is 71, then the datatype is # 71 & ~BANDTYPE_FLAG_HASNODATA = 7 (32BSI) # and the nodata value is True. - structure = "B" + GDAL_TO_STRUCT[band.datatype()] + structure = 'B' + GDAL_TO_STRUCT[band.datatype()] # Get band pixel type in PostGIS notation pixeltype = GDAL_TO_POSTGIS[band.datatype()] diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/schema.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/schema.py index 77a9096..da45d63 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/schema.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/postgis/schema.py @@ -1,60 +1,58 @@ +from django.db.backends.ddl_references import Statement from django.db.backends.postgresql.schema import DatabaseSchemaEditor -from django.db.models.expressions import Col, Func class PostGISSchemaEditor(DatabaseSchemaEditor): - geom_index_type = "GIST" - geom_index_ops_nd = "GIST_GEOMETRY_OPS_ND" - rast_index_template = "ST_ConvexHull(%(expressions)s)" + geom_index_type = 'GIST' + geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND' + rast_index_wrapper = 'ST_ConvexHull(%s)' - sql_alter_column_to_3d = ( - "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force3D(%(column)s)::%(type)s" - ) - sql_alter_column_to_2d = ( - "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force2D(%(column)s)::%(type)s" - ) + sql_alter_column_to_3d = "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force3D(%(column)s)::%(type)s" + sql_alter_column_to_2d = "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force2D(%(column)s)::%(type)s" def geo_quote_name(self, name): return self.connection.ops.geo_quote_name(name) def _field_should_be_indexed(self, model, field): - if getattr(field, "spatial_index", False): + if getattr(field, 'spatial_index', False): return True return super()._field_should_be_indexed(model, field) def _create_index_sql(self, model, *, fields=None, **kwargs): - if fields is None or len(fields) != 1 or not hasattr(fields[0], "geodetic"): + if fields is None or len(fields) != 1 or not hasattr(fields[0], 'geodetic'): return super()._create_index_sql(model, fields=fields, **kwargs) field = fields[0] - expressions = None - opclasses = None - if field.geom_type == "RASTER": + field_column = self.quote_name(field.column) + + if field.geom_type == 'RASTER': # For raster fields, wrap index creation SQL statement with ST_ConvexHull. # Indexes on raster columns are based on the convex hull of the raster. - expressions = Func(Col(None, field), template=self.rast_index_template) - fields = None + field_column = self.rast_index_wrapper % field_column elif field.dim > 2 and not field.geography: # Use "nd" ops which are fast on multidimensional cases - opclasses = [self.geom_index_ops_nd] - name = kwargs.get("name") - if not name: - name = self._create_index_name(model._meta.db_table, [field.column], "_id") + field_column = "%s %s" % (field_column, self.geom_index_ops_nd) + if kwargs.get('name') is None: + index_name = '%s_%s_id' % (model._meta.db_table, field.column) + else: + index_name = kwargs['name'] - return super()._create_index_sql( - model, - fields=fields, - name=name, - using=" USING %s" % self.geom_index_type, - opclasses=opclasses, - expressions=expressions, + return Statement( + self.sql_create_index, + name=self.quote_name(index_name), + table=self.quote_name(model._meta.db_table), + using=' USING %s' % self.geom_index_type, + columns=field_column, + extra='', + condition='', + include='', ) def _alter_column_type_sql(self, table, old_field, new_field, new_type): """ Special case when dimension changed. """ - if not hasattr(old_field, "dim") or not hasattr(new_field, "dim"): + if not hasattr(old_field, 'dim') or not hasattr(new_field, 'dim'): return super()._alter_column_type_sql(table, old_field, new_field, new_type) if old_field.dim == 2 and new_field.dim == 3: @@ -65,8 +63,7 @@ class PostGISSchemaEditor(DatabaseSchemaEditor): sql_alter = self.sql_alter_column_type return ( ( - sql_alter - % { + sql_alter % { "column": self.quote_name(new_field.column), "type": new_type, }, diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py index 91d14dc..2a36159 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py @@ -4,7 +4,6 @@ from django.db.backends.sqlite3.base import Database class SpatiaLiteAdapter(WKTAdapter): "SQLite adapter for geometry objects." - def __conform__(self, protocol): if protocol is Database.PrepareProtocol: return str(self) diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/base.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/base.py index 3359a7a..1afba58 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/base.py @@ -2,7 +2,9 @@ from ctypes.util import find_library from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.db.backends.sqlite3.base import DatabaseWrapper as SQLiteDatabaseWrapper +from django.db.backends.sqlite3.base import ( + DatabaseWrapper as SQLiteDatabaseWrapper, +) from .client import SpatiaLiteClient from .features import DatabaseFeatures @@ -25,16 +27,12 @@ class DatabaseWrapper(SQLiteDatabaseWrapper): # (`libspatialite`). If it's not in the system library path (e.g., it # cannot be found by `ctypes.util.find_library`), then it may be set # manually in the settings via the `SPATIALITE_LIBRARY_PATH` setting. - self.lib_spatialite_paths = [ - name - for name in [ - getattr(settings, "SPATIALITE_LIBRARY_PATH", None), - "mod_spatialite.so", - "mod_spatialite", - find_library("spatialite"), - ] - if name is not None - ] + self.lib_spatialite_paths = [name for name in [ + getattr(settings, 'SPATIALITE_LIBRARY_PATH', None), + 'mod_spatialite.so', + 'mod_spatialite', + find_library('spatialite'), + ] if name is not None] super().__init__(*args, **kwargs) def get_new_connection(self, conn_params): @@ -44,26 +42,26 @@ class DatabaseWrapper(SQLiteDatabaseWrapper): conn.enable_load_extension(True) except AttributeError: raise ImproperlyConfigured( - "SpatiaLite requires SQLite to be configured to allow " - "extension loading." + 'SpatiaLite requires SQLite to be configured to allow ' + 'extension loading.' ) # Load the SpatiaLite library extension on the connection. for path in self.lib_spatialite_paths: try: conn.load_extension(path) except Exception: - if getattr(settings, "SPATIALITE_LIBRARY_PATH", None): + if getattr(settings, 'SPATIALITE_LIBRARY_PATH', None): raise ImproperlyConfigured( - "Unable to load the SpatiaLite library extension " - "as specified in your SPATIALITE_LIBRARY_PATH setting." + 'Unable to load the SpatiaLite library extension ' + 'as specified in your SPATIALITE_LIBRARY_PATH setting.' ) continue else: break else: raise ImproperlyConfigured( - "Unable to load the SpatiaLite library extension. " - "Library names tried: %s" % ", ".join(self.lib_spatialite_paths) + 'Unable to load the SpatiaLite library extension. ' + 'Library names tried: %s' % ', '.join(self.lib_spatialite_paths) ) return conn @@ -73,7 +71,4 @@ class DatabaseWrapper(SQLiteDatabaseWrapper): with self.cursor() as cursor: cursor.execute("PRAGMA table_info(geometry_columns);") if cursor.fetchall() == []: - if self.ops.spatial_version < (5,): - cursor.execute("SELECT InitSpatialMetaData(1)") - else: - cursor.execute("SELECT InitSpatialMetaDataFull(1)") + cursor.execute("SELECT InitSpatialMetaData(1)") diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/client.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/client.py index 527fe15..c9dfd1a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/client.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/client.py @@ -2,4 +2,4 @@ from django.db.backends.sqlite3.client import DatabaseClient class SpatiaLiteClient(DatabaseClient): - executable_name = "spatialite" + executable_name = 'spatialite' diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/features.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/features.py index 9504bb0..947d874 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/features.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/features.py @@ -11,16 +11,14 @@ class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures): @cached_property def supports_area_geodetic(self): - return bool(self.connection.ops.geom_lib_version()) + return bool(self.connection.ops.lwgeom_version()) @cached_property def django_test_skips(self): skips = super().django_test_skips - skips.update( - { - "SpatiaLite doesn't support distance lookups with Distance objects.": { - "gis_tests.geogapp.tests.GeographyTest.test02_distance_lookup", - }, - } - ) + skips.update({ + "SpatiaLite doesn't support distance lookups with Distance objects.": { + 'gis_tests.geogapp.tests.GeographyTest.test02_distance_lookup', + }, + }) return skips diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py index 8d0003f..0feeb24 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py @@ -1,7 +1,6 @@ from django.contrib.gis.gdal import OGRGeomType from django.db.backends.sqlite3.introspection import ( - DatabaseIntrospection, - FlexibleFieldLookupDict, + DatabaseIntrospection, FlexibleFieldLookupDict, ) @@ -10,16 +9,15 @@ class GeoFlexibleFieldLookupDict(FlexibleFieldLookupDict): Subclass that includes updates the `base_data_types_reverse` dict for geometry field types. """ - base_data_types_reverse = { **FlexibleFieldLookupDict.base_data_types_reverse, - "point": "GeometryField", - "linestring": "GeometryField", - "polygon": "GeometryField", - "multipoint": "GeometryField", - "multilinestring": "GeometryField", - "multipolygon": "GeometryField", - "geometrycollection": "GeometryField", + 'point': 'GeometryField', + 'linestring': 'GeometryField', + 'polygon': 'GeometryField', + 'multipoint': 'GeometryField', + 'multilinestring': 'GeometryField', + 'multipolygon': 'GeometryField', + 'geometrycollection': 'GeometryField', } @@ -29,18 +27,14 @@ class SpatiaLiteIntrospection(DatabaseIntrospection): def get_geometry_type(self, table_name, description): with self.connection.cursor() as cursor: # Querying the `geometry_columns` table to get additional metadata. - cursor.execute( - "SELECT coord_dimension, srid, geometry_type " - "FROM geometry_columns " - "WHERE f_table_name=%s AND f_geometry_column=%s", - (table_name, description.name), - ) + cursor.execute('SELECT coord_dimension, srid, geometry_type ' + 'FROM geometry_columns ' + 'WHERE f_table_name=%s AND f_geometry_column=%s', + (table_name, description.name)) row = cursor.fetchone() if not row: - raise Exception( - 'Could not find a geometry column for "%s"."%s"' - % (table_name, description.name) - ) + raise Exception('Could not find a geometry column for "%s"."%s"' % + (table_name, description.name)) # OGRGeomType does not require GDAL and makes it easy to convert # from OGC geom type name to Django field. @@ -57,21 +51,18 @@ class SpatiaLiteIntrospection(DatabaseIntrospection): srid = row[1] field_params = {} if srid != 4326: - field_params["srid"] = srid - if (isinstance(dim, str) and "Z" in dim) or dim == 3: - field_params["dim"] = 3 + field_params['srid'] = srid + if (isinstance(dim, str) and 'Z' in dim) or dim == 3: + field_params['dim'] = 3 return field_type, field_params def get_constraints(self, cursor, table_name): constraints = super().get_constraints(cursor, table_name) - cursor.execute( - "SELECT f_geometry_column " - "FROM geometry_columns " - "WHERE f_table_name=%s AND spatial_index_enabled=1", - (table_name,), - ) + cursor.execute('SELECT f_geometry_column ' + 'FROM geometry_columns ' + 'WHERE f_table_name=%s AND spatial_index_enabled=1', (table_name,)) for row in cursor.fetchall(): - constraints["%s__spatial__index" % row[0]] = { + constraints['%s__spatial__index' % row[0]] = { "columns": [row[0]], "primary_key": False, "unique": False, diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/models.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/models.py index 7cc98ae..577c723 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/models.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/models.py @@ -9,21 +9,20 @@ class SpatialiteGeometryColumns(models.Model): """ The 'geometry_columns' table from SpatiaLite. """ - f_table_name = models.CharField(max_length=256) f_geometry_column = models.CharField(max_length=256) coord_dimension = models.IntegerField() srid = models.IntegerField(primary_key=True) spatial_index_enabled = models.IntegerField() - type = models.IntegerField(db_column="geometry_type") + type = models.IntegerField(db_column='geometry_type') class Meta: - app_label = "gis" - db_table = "geometry_columns" + app_label = 'gis' + db_table = 'geometry_columns' managed = False def __str__(self): - return "%s.%s - %dD %s field (SRID: %d)" % ( + return '%s.%s - %dD %s field (SRID: %d)' % ( self.f_table_name, self.f_geometry_column, self.coord_dimension, @@ -37,7 +36,7 @@ class SpatialiteGeometryColumns(models.Model): Return the name of the metadata column used to store the feature table name. """ - return "f_table_name" + return 'f_table_name' @classmethod def geom_col_name(cls): @@ -45,14 +44,13 @@ class SpatialiteGeometryColumns(models.Model): Return the name of the metadata column used to store the feature geometry column. """ - return "f_geometry_column" + return 'f_geometry_column' class SpatialiteSpatialRefSys(models.Model, SpatialRefSysMixin): """ The 'spatial_ref_sys' table from SpatiaLite. """ - srid = models.IntegerField(primary_key=True) auth_name = models.CharField(max_length=256) auth_srid = models.IntegerField() @@ -61,8 +59,8 @@ class SpatialiteSpatialRefSys(models.Model, SpatialRefSysMixin): srtext = models.CharField(max_length=2048) class Meta: - app_label = "gis" - db_table = "spatial_ref_sys" + app_label = 'gis' + db_table = 'spatial_ref_sys' managed = False @property diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/operations.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/operations.py index 8003fcb..1fc1cf2 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/operations.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/operations.py @@ -3,7 +3,9 @@ SQL functions reference lists: https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html """ from django.contrib.gis.db import models -from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations +from django.contrib.gis.db.backends.base.operations import ( + BaseSpatialOperations, +) from django.contrib.gis.db.backends.spatialite.adapter import SpatiaLiteAdapter from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.geos.geometry import GEOSGeometry, GEOSGeometryBase @@ -18,69 +20,69 @@ from django.utils.version import get_version_tuple class SpatialiteNullCheckOperator(SpatialOperator): def as_sql(self, connection, lookup, template_params, sql_params): sql, params = super().as_sql(connection, lookup, template_params, sql_params) - return "%s > 0" % sql, params + return '%s > 0' % sql, params class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): - name = "spatialite" + name = 'spatialite' spatialite = True Adapter = SpatiaLiteAdapter - collect = "Collect" - extent = "Extent" - makeline = "MakeLine" - unionagg = "GUnion" + collect = 'Collect' + extent = 'Extent' + makeline = 'MakeLine' + unionagg = 'GUnion' - from_text = "GeomFromText" + from_text = 'GeomFromText' gis_operators = { # Binary predicates - "equals": SpatialiteNullCheckOperator(func="Equals"), - "disjoint": SpatialiteNullCheckOperator(func="Disjoint"), - "touches": SpatialiteNullCheckOperator(func="Touches"), - "crosses": SpatialiteNullCheckOperator(func="Crosses"), - "within": SpatialiteNullCheckOperator(func="Within"), - "overlaps": SpatialiteNullCheckOperator(func="Overlaps"), - "contains": SpatialiteNullCheckOperator(func="Contains"), - "intersects": SpatialiteNullCheckOperator(func="Intersects"), - "relate": SpatialiteNullCheckOperator(func="Relate"), - "coveredby": SpatialiteNullCheckOperator(func="CoveredBy"), - "covers": SpatialiteNullCheckOperator(func="Covers"), + 'equals': SpatialiteNullCheckOperator(func='Equals'), + 'disjoint': SpatialiteNullCheckOperator(func='Disjoint'), + 'touches': SpatialiteNullCheckOperator(func='Touches'), + 'crosses': SpatialiteNullCheckOperator(func='Crosses'), + 'within': SpatialiteNullCheckOperator(func='Within'), + 'overlaps': SpatialiteNullCheckOperator(func='Overlaps'), + 'contains': SpatialiteNullCheckOperator(func='Contains'), + 'intersects': SpatialiteNullCheckOperator(func='Intersects'), + 'relate': SpatialiteNullCheckOperator(func='Relate'), + 'coveredby': SpatialiteNullCheckOperator(func='CoveredBy'), + 'covers': SpatialiteNullCheckOperator(func='Covers'), # Returns true if B's bounding box completely contains A's bounding box. - "contained": SpatialOperator(func="MbrWithin"), + 'contained': SpatialOperator(func='MbrWithin'), # Returns true if A's bounding box completely contains B's bounding box. - "bbcontains": SpatialOperator(func="MbrContains"), + 'bbcontains': SpatialOperator(func='MbrContains'), # Returns true if A's bounding box overlaps B's bounding box. - "bboverlaps": SpatialOperator(func="MbrOverlaps"), + 'bboverlaps': SpatialOperator(func='MbrOverlaps'), # These are implemented here as synonyms for Equals - "same_as": SpatialiteNullCheckOperator(func="Equals"), - "exact": SpatialiteNullCheckOperator(func="Equals"), + 'same_as': SpatialiteNullCheckOperator(func='Equals'), + 'exact': SpatialiteNullCheckOperator(func='Equals'), # Distance predicates - "dwithin": SpatialOperator(func="PtDistWithin"), + 'dwithin': SpatialOperator(func='PtDistWithin'), } disallowed_aggregates = (models.Extent3D,) - select = "CAST (AsEWKB(%s) AS BLOB)" + select = 'CAST (AsEWKB(%s) AS BLOB)' function_names = { - "AsWKB": "St_AsBinary", - "ForcePolygonCW": "ST_ForceLHR", - "Length": "ST_Length", - "LineLocatePoint": "ST_Line_Locate_Point", - "NumPoints": "ST_NPoints", - "Reverse": "ST_Reverse", - "Scale": "ScaleCoords", - "Translate": "ST_Translate", - "Union": "ST_Union", + 'AsWKB': 'St_AsBinary', + 'ForcePolygonCW': 'ST_ForceLHR', + 'Length': 'ST_Length', + 'LineLocatePoint': 'ST_Line_Locate_Point', + 'NumPoints': 'ST_NPoints', + 'Reverse': 'ST_Reverse', + 'Scale': 'ScaleCoords', + 'Translate': 'ST_Translate', + 'Union': 'ST_Union', } @cached_property def unsupported_functions(self): - unsupported = {"BoundingCircle", "GeometryDistance", "MemSize"} - if not self.geom_lib_version(): - unsupported |= {"Azimuth", "GeoHash", "MakeValid"} + unsupported = {'BoundingCircle', 'GeometryDistance', 'MemSize'} + if not self.lwgeom_version(): + unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'} return unsupported @cached_property @@ -91,11 +93,12 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): except Exception as exc: raise ImproperlyConfigured( 'Cannot determine the SpatiaLite version for the "%s" database. ' - "Was the SpatiaLite initialization SQL loaded on this database?" - % (self.connection.settings_dict["NAME"],) + 'Was the SpatiaLite initialization SQL loaded on this database?' % ( + self.connection.settings_dict['NAME'], + ) ) from exc if version < (4, 3, 0): - raise ImproperlyConfigured("GeoDjango supports SpatiaLite 4.3.0 and above.") + raise ImproperlyConfigured('GeoDjango supports SpatiaLite 4.3.0 and above.') return version def convert_extent(self, box): @@ -126,16 +129,14 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): - if lookup_type == "dwithin": + if lookup_type == 'dwithin': raise ValueError( - "Only numeric values of degree units are allowed on " - "geographic DWithin queries." + 'Only numeric values of degree units are allowed on ' + 'geographic DWithin queries.' ) dist_param = value.m else: - dist_param = getattr( - value, Distance.unit_attname(f.units_name(self.connection)) - ) + dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param] @@ -148,7 +149,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): """ cursor = self.connection._cursor() try: - cursor.execute("SELECT %s" % func) + cursor.execute('SELECT %s' % func) row = cursor.fetchone() finally: cursor.close() @@ -156,33 +157,19 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): def geos_version(self): "Return the version of GEOS used by SpatiaLite as a string." - return self._get_spatialite_func("geos_version()") + return self._get_spatialite_func('geos_version()') def proj_version(self): """Return the version of the PROJ library used by SpatiaLite.""" - return self._get_spatialite_func("proj4_version()") + return self._get_spatialite_func('proj4_version()') def lwgeom_version(self): """Return the version of LWGEOM library used by SpatiaLite.""" - return self._get_spatialite_func("lwgeom_version()") - - def rttopo_version(self): - """Return the version of RTTOPO library used by SpatiaLite.""" - return self._get_spatialite_func("rttopo_version()") - - def geom_lib_version(self): - """ - Return the version of the version-dependant geom library used by - SpatiaLite. - """ - if self.spatial_version >= (5,): - return self.rttopo_version() - else: - return self.lwgeom_version() + return self._get_spatialite_func('lwgeom_version()') def spatialite_version(self): "Return the SpatiaLite library version as a string." - return self._get_spatialite_func("spatialite_version()") + return self._get_spatialite_func('spatialite_version()') def spatialite_version_tuple(self): """ @@ -197,7 +184,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): Return the spatial aggregate SQL template and function for the given Aggregate instance. """ - agg_name = "unionagg" if agg_name.lower() == "union" else agg_name.lower() + agg_name = 'unionagg' if agg_name.lower() == 'union' else agg_name.lower() return getattr(self, agg_name) # Routines for getting the OGC-compliant models. @@ -205,14 +192,12 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): from django.contrib.gis.db.backends.spatialite.models import ( SpatialiteGeometryColumns, ) - return SpatialiteGeometryColumns def spatial_ref_sys(self): from django.contrib.gis.db.backends.spatialite.models import ( SpatialiteSpatialRefSys, ) - return SpatialiteSpatialRefSys def get_geometry_converter(self, expression): @@ -221,5 +206,4 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): def converter(value, expression, connection): return None if value is None else GEOSGeometryBase(read(value), geom_class) - return converter diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/schema.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/schema.py index d37632e..066ce6d 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/schema.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/spatialite/schema.py @@ -14,9 +14,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): "%(geom_type)s, %(dim)s)" ) sql_remove_geometry_metadata = "SELECT DiscardGeometryColumn(%(table)s, %(column)s)" - sql_discard_geometry_columns = ( - "DELETE FROM %(geom_table)s WHERE f_table_name = %(table)s" - ) + sql_discard_geometry_columns = "DELETE FROM %(geom_table)s WHERE f_table_name = %(table)s" sql_update_geometry_columns = ( "UPDATE %(geom_table)s SET f_table_name = %(new_table)s " "WHERE f_table_name = %(old_table)s" @@ -38,14 +36,12 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): def column_sql(self, model, field, include_default=False): from django.contrib.gis.db.models import GeometryField - if not isinstance(field, GeometryField): return super().column_sql(model, field, include_default) # Geometry columns are created by the `AddGeometryColumn` function self.geometry_sql.append( - self.sql_add_geometry_column - % { + self.sql_add_geometry_column % { "table": self.geo_quote_name(model._meta.db_table), "column": self.geo_quote_name(field.column), "srid": field.srid, @@ -57,8 +53,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): if field.spatial_index: self.geometry_sql.append( - self.sql_add_spatial_index - % { + self.sql_add_spatial_index % { "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), } @@ -67,15 +62,13 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): def remove_geometry_metadata(self, model, field): self.execute( - self.sql_remove_geometry_metadata - % { + self.sql_remove_geometry_metadata % { "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), } ) self.execute( - self.sql_drop_spatial_index - % { + self.sql_drop_spatial_index % { "table": model._meta.db_table, "column": field.column, } @@ -99,8 +92,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): for geom_table in self.geometry_tables: try: self.execute( - self.sql_discard_geometry_columns - % { + self.sql_discard_geometry_columns % { "geom_table": geom_table, "table": self.quote_name(model._meta.db_table), } @@ -111,7 +103,6 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): def add_field(self, model, field): from django.contrib.gis.db.models import GeometryField - if isinstance(field, GeometryField): # Populate self.geometry_sql self.column_sql(model, field) @@ -134,17 +125,14 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): else: super().remove_field(model, field) - def alter_db_table( - self, model, old_db_table, new_db_table, disable_constraints=True - ): + def alter_db_table(self, model, old_db_table, new_db_table, disable_constraints=True): from django.contrib.gis.db.models import GeometryField # Remove geometry-ness from temp table for field in model._meta.local_fields: if isinstance(field, GeometryField): self.execute( - self.sql_remove_geometry_metadata - % { + self.sql_remove_geometry_metadata % { "table": self.quote_name(old_db_table), "column": self.quote_name(field.column), } @@ -155,8 +143,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): for geom_table in self.geometry_tables: try: self.execute( - self.sql_update_geometry_columns - % { + self.sql_update_geometry_columns % { "geom_table": geom_table, "old_table": self.quote_name(old_db_table), "new_table": self.quote_name(new_db_table), @@ -167,25 +154,15 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): # Re-add geometry-ness and rename spatial index tables for field in model._meta.local_fields: if isinstance(field, GeometryField): - self.execute( - self.sql_recover_geometry_metadata - % { - "table": self.geo_quote_name(new_db_table), - "column": self.geo_quote_name(field.column), - "srid": field.srid, - "geom_type": self.geo_quote_name(field.geom_type), - "dim": field.dim, - } - ) - if getattr(field, "spatial_index", False): - self.execute( - self.sql_rename_table - % { - "old_table": self.quote_name( - "idx_%s_%s" % (old_db_table, field.column) - ), - "new_table": self.quote_name( - "idx_%s_%s" % (new_db_table, field.column) - ), - } - ) + self.execute(self.sql_recover_geometry_metadata % { + "table": self.geo_quote_name(new_db_table), + "column": self.geo_quote_name(field.column), + "srid": field.srid, + "geom_type": self.geo_quote_name(field.geom_type), + "dim": field.dim, + }) + if getattr(field, 'spatial_index', False): + self.execute(self.sql_rename_table % { + "old_table": self.quote_name("idx_%s_%s" % (old_db_table, field.column)), + "new_table": self.quote_name("idx_%s_%s" % (new_db_table, field.column)), + }) diff --git a/venv/Lib/site-packages/django/contrib/gis/db/backends/utils.py b/venv/Lib/site-packages/django/contrib/gis/db/backends/utils.py index ffb7420..d479009 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/backends/utils.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/backends/utils.py @@ -8,7 +8,6 @@ class SpatialOperator: """ Class encapsulating the behavior specific to a GIS operation (used by lookups). """ - sql_template = None def __init__(self, op=None, func=None): @@ -18,11 +17,11 @@ class SpatialOperator: @property def default_template(self): if self.func: - return "%(func)s(%(lhs)s, %(rhs)s)" + return '%(func)s(%(lhs)s, %(rhs)s)' else: - return "%(lhs)s %(op)s %(rhs)s" + return '%(lhs)s %(op)s %(rhs)s' def as_sql(self, connection, lookup, template_params, sql_params): sql_template = self.sql_template or lookup.sql_template or self.default_template - template_params.update({"op": self.op, "func": self.func}) + template_params.update({'op': self.op, 'func': self.func}) return sql_template % template_params, sql_params diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/__init__.py b/venv/Lib/site-packages/django/contrib/gis/db/models/__init__.py index 0d5d45d..2e47255 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/__init__.py @@ -5,26 +5,14 @@ import django.contrib.gis.db.models.lookups # NOQA from django.contrib.gis.db.models.aggregates import * # NOQA from django.contrib.gis.db.models.aggregates import __all__ as aggregates_all from django.contrib.gis.db.models.fields import ( - GeometryCollectionField, - GeometryField, - LineStringField, - MultiLineStringField, - MultiPointField, - MultiPolygonField, - PointField, - PolygonField, - RasterField, + GeometryCollectionField, GeometryField, LineStringField, + MultiLineStringField, MultiPointField, MultiPolygonField, PointField, + PolygonField, RasterField, ) __all__ = models_all + aggregates_all __all__ += [ - "GeometryCollectionField", - "GeometryField", - "LineStringField", - "MultiLineStringField", - "MultiPointField", - "MultiPolygonField", - "PointField", - "PolygonField", - "RasterField", + 'GeometryCollectionField', 'GeometryField', 'LineStringField', + 'MultiLineStringField', 'MultiPointField', 'MultiPolygonField', 'PointField', + 'PolygonField', 'RasterField', ] diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/aggregates.py b/venv/Lib/site-packages/django/contrib/gis/db/models/aggregates.py index c19cbd0..3aa46a5 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/aggregates.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/aggregates.py @@ -1,13 +1,10 @@ from django.contrib.gis.db.models.fields import ( - ExtentField, - GeometryCollectionField, - GeometryField, - LineStringField, + ExtentField, GeometryCollectionField, GeometryField, LineStringField, ) from django.db.models import Aggregate, Value from django.utils.functional import cached_property -__all__ = ["Collect", "Extent", "Extent3D", "MakeLine", "Union"] +__all__ = ['Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] class GeoAggregate(Aggregate): @@ -26,45 +23,37 @@ class GeoAggregate(Aggregate): compiler, connection, function=function or connection.ops.spatial_aggregate_name(self.name), - **extra_context, + **extra_context ) def as_oracle(self, compiler, connection, **extra_context): if not self.is_extent: - tolerance = self.extra.get("tolerance") or getattr(self, "tolerance", 0.05) + tolerance = self.extra.get('tolerance') or getattr(self, 'tolerance', 0.05) clone = self.copy() - clone.set_source_expressions( - [ - *self.get_source_expressions(), - Value(tolerance), - ] - ) - template = "%(function)s(SDOAGGRTYPE(%(expressions)s))" - return clone.as_sql( - compiler, connection, template=template, **extra_context - ) + clone.set_source_expressions([ + *self.get_source_expressions(), + Value(tolerance), + ]) + template = '%(function)s(SDOAGGRTYPE(%(expressions)s))' + return clone.as_sql(compiler, connection, template=template, **extra_context) return self.as_sql(compiler, connection, **extra_context) - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) for expr in c.get_source_expressions(): - if not hasattr(expr.field, "geom_type"): - raise ValueError( - "Geospatial aggregates only allowed on geometry fields." - ) + if not hasattr(expr.field, 'geom_type'): + raise ValueError('Geospatial aggregates only allowed on geometry fields.') return c class Collect(GeoAggregate): - name = "Collect" + name = 'Collect' output_field_class = GeometryCollectionField class Extent(GeoAggregate): - name = "Extent" - is_extent = "2D" + name = 'Extent' + is_extent = '2D' def __init__(self, expression, **extra): super().__init__(expression, output_field=ExtentField(), **extra) @@ -74,8 +63,8 @@ class Extent(GeoAggregate): class Extent3D(GeoAggregate): - name = "Extent3D" - is_extent = "3D" + name = 'Extent3D' + is_extent = '3D' def __init__(self, expression, **extra): super().__init__(expression, output_field=ExtentField(), **extra) @@ -85,10 +74,10 @@ class Extent3D(GeoAggregate): class MakeLine(GeoAggregate): - name = "MakeLine" + name = 'MakeLine' output_field_class = LineStringField class Union(GeoAggregate): - name = "Union" + name = 'Union' output_field_class = GeometryField diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/fields.py b/venv/Lib/site-packages/django/contrib/gis/db/models/fields.py index 889c1cf..16b3bb5 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/fields.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/fields.py @@ -4,15 +4,8 @@ from django.contrib.gis import forms, gdal from django.contrib.gis.db.models.proxy import SpatialProxy from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.geos import ( - GeometryCollection, - GEOSException, - GEOSGeometry, - LineString, - MultiLineString, - MultiPoint, - MultiPolygon, - Point, - Polygon, + GeometryCollection, GEOSException, GEOSGeometry, LineString, + MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, ) from django.core.exceptions import ImproperlyConfigured from django.db.models import Field @@ -24,9 +17,7 @@ from django.utils.translation import gettext_lazy as _ _srid_cache = defaultdict(dict) -SRIDCacheEntry = namedtuple( - "SRIDCacheEntry", ["units", "units_name", "spheroid", "geodetic"] -) +SRIDCacheEntry = namedtuple('SRIDCacheEntry', ['units', 'units_name', 'spheroid', 'geodetic']) def get_srid_info(srid, connection): @@ -36,7 +27,6 @@ def get_srid_info(srid, connection): table for the given database connection. These results are cached. """ from django.contrib.gis.gdal import SpatialReference - global _srid_cache try: @@ -46,14 +36,9 @@ def get_srid_info(srid, connection): SpatialRefSys = None alias, get_srs = ( - ( - connection.alias, - lambda srid: SpatialRefSys.objects.using(connection.alias) - .get(srid=srid) - .srs, - ) - if SpatialRefSys - else (None, SpatialReference) + (connection.alias, lambda srid: SpatialRefSys.objects.using(connection.alias).get(srid=srid).srs) + if SpatialRefSys else + (None, SpatialReference) ) if srid not in _srid_cache[alias]: srs = get_srs(srid) @@ -61,8 +46,7 @@ def get_srid_info(srid, connection): _srid_cache[alias][srid] = SRIDCacheEntry( units=units, units_name=units_name, - spheroid='SPHEROID["%s",%s,%s]' - % (srs["spheroid"], srs.semi_major, srs.inverse_flattening), + spheroid='SPHEROID["%s",%s,%s]' % (srs['spheroid'], srs.semi_major, srs.inverse_flattening), geodetic=srs.geographic, ) @@ -77,7 +61,6 @@ class BaseSpatialField(Field): properties that are common to all GIS fields such as the characteristics of the spatial reference system of the field. """ - description = _("The base GIS field.") empty_strings_allowed = False @@ -105,7 +88,7 @@ class BaseSpatialField(Field): # Setting the verbose_name keyword argument with the positional # first parameter, so this works like normal fields. - kwargs["verbose_name"] = verbose_name + kwargs['verbose_name'] = verbose_name super().__init__(**kwargs) @@ -113,9 +96,9 @@ class BaseSpatialField(Field): name, path, args, kwargs = super().deconstruct() # Always include SRID for less fragility; include spatial index if it's # not the default value. - kwargs["srid"] = self.srid + kwargs['srid'] = self.srid if self.spatial_index is not True: - kwargs["spatial_index"] = self.spatial_index + kwargs['spatial_index'] = self.spatial_index return name, path, args, kwargs def db_type(self, connection): @@ -163,10 +146,10 @@ class BaseSpatialField(Field): return connection.ops.Adapter( super().get_db_prep_value(value, connection, *args, **kwargs), **( - {"geography": True} + {'geography': True} if self.geography and connection.features.supports_geography else {} - ), + ) ) def get_raster_prep_value(self, value, is_candidate): @@ -184,9 +167,7 @@ class BaseSpatialField(Field): try: return gdal.GDALRaster(value) except GDALException: - raise ValueError( - "Couldn't create spatial object from lookup value '%s'." % value - ) + raise ValueError("Couldn't create spatial object from lookup value '%s'." % value) def get_prep_value(self, value): obj = super().get_prep_value(value) @@ -198,9 +179,7 @@ class BaseSpatialField(Field): pass else: # Check if input is a candidate for conversion to raster or geometry. - is_candidate = isinstance(obj, (bytes, str)) or hasattr( - obj, "__geo_interface__" - ) + is_candidate = isinstance(obj, (bytes, str)) or hasattr(obj, '__geo_interface__') # Try to convert the input to raster. raster = self.get_raster_prep_value(obj, is_candidate) @@ -210,14 +189,9 @@ class BaseSpatialField(Field): try: obj = GEOSGeometry(obj) except (GEOSException, GDALException): - raise ValueError( - "Couldn't create spatial object from lookup value '%s'." % obj - ) + raise ValueError("Couldn't create spatial object from lookup value '%s'." % obj) else: - raise ValueError( - "Cannot use object with type %s for a spatial lookup parameter." - % type(obj).__name__ - ) + raise ValueError('Cannot use object with type %s for a spatial lookup parameter.' % type(obj).__name__) # Assigning the SRID value. obj.srid = self.get_srid(obj) @@ -228,25 +202,14 @@ class GeometryField(BaseSpatialField): """ The base Geometry field -- maps to the OpenGIS Specification Geometry type. """ - - description = _( - "The base Geometry field — maps to the OpenGIS Specification Geometry type." - ) + description = _('The base Geometry field — maps to the OpenGIS Specification Geometry type.') form_class = forms.GeometryField # The OpenGIS Geometry name. - geom_type = "GEOMETRY" + geom_type = 'GEOMETRY' geom_class = None - def __init__( - self, - verbose_name=None, - dim=2, - geography=False, - *, - extent=(-180.0, -90.0, 180.0, 90.0), - tolerance=0.05, - **kwargs, - ): + def __init__(self, verbose_name=None, dim=2, geography=False, *, extent=(-180.0, -90.0, 180.0, 90.0), + tolerance=0.05, **kwargs): """ The initialization function for geometry fields. In addition to the parameters from BaseSpatialField, it takes the following as keyword @@ -281,36 +244,30 @@ class GeometryField(BaseSpatialField): name, path, args, kwargs = super().deconstruct() # Include kwargs if they're not the default values. if self.dim != 2: - kwargs["dim"] = self.dim + kwargs['dim'] = self.dim if self.geography is not False: - kwargs["geography"] = self.geography + kwargs['geography'] = self.geography if self._extent != (-180.0, -90.0, 180.0, 90.0): - kwargs["extent"] = self._extent + kwargs['extent'] = self._extent if self._tolerance != 0.05: - kwargs["tolerance"] = self._tolerance + kwargs['tolerance'] = self._tolerance return name, path, args, kwargs def contribute_to_class(self, cls, name, **kwargs): super().contribute_to_class(cls, name, **kwargs) # Setup for lazy-instantiated Geometry object. - setattr( - cls, - self.attname, - SpatialProxy(self.geom_class or GEOSGeometry, self, load_func=GEOSGeometry), - ) + setattr(cls, self.attname, SpatialProxy(self.geom_class or GEOSGeometry, self, load_func=GEOSGeometry)) def formfield(self, **kwargs): defaults = { - "form_class": self.form_class, - "geom_type": self.geom_type, - "srid": self.srid, + 'form_class': self.form_class, + 'geom_type': self.geom_type, + 'srid': self.srid, **kwargs, } - if self.dim > 2 and not getattr( - defaults["form_class"].widget, "supports_3d", False - ): - defaults.setdefault("widget", forms.Textarea) + if self.dim > 2 and not getattr(defaults['form_class'].widget, 'supports_3d', False): + defaults.setdefault('widget', forms.Textarea) return super().formfield(**defaults) def select_format(self, compiler, sql, params): @@ -326,49 +283,49 @@ class GeometryField(BaseSpatialField): # The OpenGIS Geometry Type Fields class PointField(GeometryField): - geom_type = "POINT" + geom_type = 'POINT' geom_class = Point form_class = forms.PointField description = _("Point") class LineStringField(GeometryField): - geom_type = "LINESTRING" + geom_type = 'LINESTRING' geom_class = LineString form_class = forms.LineStringField description = _("Line string") class PolygonField(GeometryField): - geom_type = "POLYGON" + geom_type = 'POLYGON' geom_class = Polygon form_class = forms.PolygonField description = _("Polygon") class MultiPointField(GeometryField): - geom_type = "MULTIPOINT" + geom_type = 'MULTIPOINT' geom_class = MultiPoint form_class = forms.MultiPointField description = _("Multi-point") class MultiLineStringField(GeometryField): - geom_type = "MULTILINESTRING" + geom_type = 'MULTILINESTRING' geom_class = MultiLineString form_class = forms.MultiLineStringField description = _("Multi-line string") class MultiPolygonField(GeometryField): - geom_type = "MULTIPOLYGON" + geom_type = 'MULTIPOLYGON' geom_class = MultiPolygon form_class = forms.MultiPolygonField description = _("Multi polygon") class GeometryCollectionField(GeometryField): - geom_type = "GEOMETRYCOLLECTION" + geom_type = 'GEOMETRYCOLLECTION' geom_class = GeometryCollection form_class = forms.GeometryCollectionField description = _("Geometry collection") @@ -393,18 +350,13 @@ class RasterField(BaseSpatialField): """ description = _("Raster Field") - geom_type = "RASTER" + geom_type = 'RASTER' geography = False def _check_connection(self, connection): # Make sure raster fields are used only on backends with raster support. - if ( - not connection.features.gis_enabled - or not connection.features.supports_raster - ): - raise ImproperlyConfigured( - "Raster fields require backends with raster support." - ) + if not connection.features.gis_enabled or not connection.features.supports_raster: + raise ImproperlyConfigured('Raster fields require backends with raster support.') def db_type(self, connection): self._check_connection(connection) @@ -423,13 +375,12 @@ class RasterField(BaseSpatialField): def get_transform(self, name): from django.contrib.gis.db.models.lookups import RasterBandTransform - try: band_index = int(name) return type( - "SpecificRasterBandTransform", + 'SpecificRasterBandTransform', (RasterBandTransform,), - {"band_index": band_index}, + {'band_index': band_index} ) except ValueError: pass diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/functions.py b/venv/Lib/site-packages/django/contrib/gis/db/models/functions.py index 5f6c7b5..1f2d372 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/functions.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/functions.py @@ -6,14 +6,8 @@ from django.contrib.gis.geos import GEOSGeometry from django.core.exceptions import FieldError from django.db import NotSupportedError from django.db.models import ( - BinaryField, - BooleanField, - FloatField, - Func, - IntegerField, - TextField, - Transform, - Value, + BinaryField, BooleanField, FloatField, Func, IntegerField, TextField, + Transform, Value, ) from django.db.models.functions import Cast from django.utils.functional import cached_property @@ -38,21 +32,12 @@ class GeoFuncMixin: except FieldError: output_field = None geom = expr.value - if ( - not isinstance(geom, GEOSGeometry) - or output_field - and not isinstance(output_field, GeometryField) - ): - raise TypeError( - "%s function requires a geometric argument in position %d." - % (self.name, pos + 1) - ) + if not isinstance(geom, GEOSGeometry) or output_field and not isinstance(output_field, GeometryField): + raise TypeError("%s function requires a geometric argument in position %d." % (self.name, pos + 1)) if not geom.srid and not output_field: raise ValueError("SRID is required for all geometries.") if not output_field: - self.source_expressions[pos] = Value( - geom, output_field=GeometryField(srid=geom.srid) - ) + self.source_expressions[pos] = Value(geom, output_field=GeometryField(srid=geom.srid)) @property def name(self): @@ -76,11 +61,8 @@ class GeoFuncMixin: field = source_fields[pos] if not isinstance(field, GeometryField): raise TypeError( - "%s function requires a GeometryField in position %s, got %s." - % ( - self.name, - pos + 1, - type(field).__name__, + "%s function requires a GeometryField in position %s, got %s." % ( + self.name, pos + 1, type(field).__name__, ) ) @@ -90,17 +72,15 @@ class GeoFuncMixin: expr_srid = expr.output_field.srid if expr_srid != base_srid: # Automatic SRID conversion so objects are comparable. - res.source_expressions[pos] = Transform( - expr, base_srid - ).resolve_expression(*args, **kwargs) + res.source_expressions[pos] = Transform(expr, base_srid).resolve_expression(*args, **kwargs) return res - def _handle_param(self, value, param_name="", check_types=None): - if not hasattr(value, "resolve_expression"): + def _handle_param(self, value, param_name='', check_types=None): + if not hasattr(value, 'resolve_expression'): if check_types and not isinstance(value, check_types): raise TypeError( - "The %s parameter has the wrong type: should be %s." - % (param_name, check_types) + "The %s parameter has the wrong type: should be %s." % ( + param_name, check_types) ) return value @@ -120,17 +100,13 @@ class SQLiteDecimalToFloatMixin: By default, Decimal values are converted to str by the SQLite backend, which is not acceptable by the GIS functions expecting numeric values. """ - def as_sqlite(self, compiler, connection, **extra_context): copy = self.copy() - copy.set_source_expressions( - [ - Value(float(expr.value)) - if hasattr(expr, "value") and isinstance(expr.value, Decimal) - else expr - for expr in copy.get_source_expressions() - ] - ) + copy.set_source_expressions([ + Value(float(expr.value)) if hasattr(expr, 'value') and isinstance(expr.value, Decimal) + else expr + for expr in copy.get_source_expressions() + ]) return copy.as_sql(compiler, connection, **extra_context) @@ -138,13 +114,11 @@ class OracleToleranceMixin: tolerance = 0.05 def as_oracle(self, compiler, connection, **extra_context): - tolerance = Value( - self._handle_param( - self.extra.get("tolerance", self.tolerance), - "tolerance", - NUMERIC_TYPES, - ) - ) + tolerance = Value(self._handle_param( + self.extra.get('tolerance', self.tolerance), + 'tolerance', + NUMERIC_TYPES, + )) clone = self.copy() clone.set_source_expressions([*self.get_source_expressions(), tolerance]) return clone.as_sql(compiler, connection, **extra_context) @@ -158,18 +132,14 @@ class Area(OracleToleranceMixin, GeoFunc): return AreaField(self.geo_field) def as_sql(self, compiler, connection, **extra_context): - if not connection.features.supports_area_geodetic and self.geo_field.geodetic( - connection - ): - raise NotSupportedError( - "Area on geodetic coordinate systems not supported." - ) + if not connection.features.supports_area_geodetic and self.geo_field.geodetic(connection): + raise NotSupportedError('Area on geodetic coordinate systems not supported.') return super().as_sql(compiler, connection, **extra_context) def as_sqlite(self, compiler, connection, **extra_context): if self.geo_field.geodetic(connection): - extra_context["template"] = "%(function)s(%(expressions)s, %(spheroid)d)" - extra_context["spheroid"] = True + extra_context['template'] = '%(function)s(%(expressions)s, %(spheroid)d)' + extra_context['spheroid'] = True return self.as_sql(compiler, connection, **extra_context) @@ -185,7 +155,7 @@ class AsGeoJSON(GeoFunc): def __init__(self, expression, bbox=False, crs=False, precision=8, **extra): expressions = [expression] if precision is not None: - expressions.append(self._handle_param(precision, "precision", int)) + expressions.append(self._handle_param(precision, 'precision', int)) options = 0 if crs and bbox: options = 3 @@ -211,7 +181,7 @@ class AsGML(GeoFunc): def __init__(self, expression, version=2, precision=8, **extra): expressions = [version, expression] if precision is not None: - expressions.append(self._handle_param(precision, "precision", int)) + expressions.append(self._handle_param(precision, 'precision', int)) super().__init__(*expressions, **extra) def as_oracle(self, compiler, connection, **extra_context): @@ -219,11 +189,7 @@ class AsGML(GeoFunc): version = source_expressions[0] clone = self.copy() clone.set_source_expressions([source_expressions[1]]) - extra_context["function"] = ( - "SDO_UTIL.TO_GML311GEOMETRY" - if version.value == 3 - else "SDO_UTIL.TO_GMLGEOMETRY" - ) + extra_context['function'] = 'SDO_UTIL.TO_GML311GEOMETRY' if version.value == 3 else 'SDO_UTIL.TO_GMLGEOMETRY' return super(AsGML, clone).as_sql(compiler, connection, **extra_context) @@ -233,7 +199,7 @@ class AsKML(GeoFunc): def __init__(self, expression, precision=8, **extra): expressions = [expression] if precision is not None: - expressions.append(self._handle_param(precision, "precision", int)) + expressions.append(self._handle_param(precision, 'precision', int)) super().__init__(*expressions, **extra) @@ -241,13 +207,11 @@ class AsSVG(GeoFunc): output_field = TextField() def __init__(self, expression, relative=False, precision=8, **extra): - relative = ( - relative if hasattr(relative, "resolve_expression") else int(relative) - ) + relative = relative if hasattr(relative, 'resolve_expression') else int(relative) expressions = [ expression, relative, - self._handle_param(precision, "precision", int), + self._handle_param(precision, 'precision', int), ] super().__init__(*expressions, **extra) @@ -269,9 +233,7 @@ class BoundingCircle(OracleToleranceMixin, GeomOutputGeoFunc): def as_oracle(self, compiler, connection, **extra_context): clone = self.copy() clone.set_source_expressions([self.get_source_expressions()[0]]) - return super(BoundingCircle, clone).as_oracle( - compiler, connection, **extra_context - ) + return super(BoundingCircle, clone).as_oracle(compiler, connection, **extra_context) class Centroid(OracleToleranceMixin, GeomOutputGeoFunc): @@ -299,7 +261,7 @@ class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFunc): def __init__(self, expr1, expr2, spheroid=None, **extra): expressions = [expr1, expr2] if spheroid is not None: - self.spheroid = self._handle_param(spheroid, "spheroid", bool) + self.spheroid = self._handle_param(spheroid, 'spheroid', bool) super().__init__(*expressions, **extra) def as_postgresql(self, compiler, connection, **extra_context): @@ -317,29 +279,21 @@ class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFunc): ) if not geography and self.geo_field.geodetic(connection): - # Geometry fields with geodetic (lon/lat) coordinates need special - # distance functions. + # Geometry fields with geodetic (lon/lat) coordinates need special distance functions if self.spheroid: - # DistanceSpheroid is more accurate and resource intensive than - # DistanceSphere. - function = connection.ops.spatial_function_name("DistanceSpheroid") + # DistanceSpheroid is more accurate and resource intensive than DistanceSphere + function = connection.ops.spatial_function_name('DistanceSpheroid') # Replace boolean param by the real spheroid of the base field - clone.source_expressions.append( - Value(self.geo_field.spheroid(connection)) - ) + clone.source_expressions.append(Value(self.geo_field.spheroid(connection))) else: - function = connection.ops.spatial_function_name("DistanceSphere") - return super(Distance, clone).as_sql( - compiler, connection, function=function, **extra_context - ) + function = connection.ops.spatial_function_name('DistanceSphere') + return super(Distance, clone).as_sql(compiler, connection, function=function, **extra_context) def as_sqlite(self, compiler, connection, **extra_context): if self.geo_field.geodetic(connection): # SpatiaLite returns NULL instead of zero on geodetic coordinates - extra_context[ - "template" - ] = "COALESCE(%(function)s(%(expressions)s, %(spheroid)s), 0)" - extra_context["spheroid"] = int(bool(self.spheroid)) + extra_context['template'] = 'COALESCE(%(function)s(%(expressions)s, %(spheroid)s), 0)' + extra_context['spheroid'] = int(bool(self.spheroid)) return super().as_sql(compiler, connection, **extra_context) @@ -357,7 +311,7 @@ class GeoHash(GeoFunc): def __init__(self, expression, precision=None, **extra): expressions = [expression] if precision is not None: - expressions.append(self._handle_param(precision, "precision", int)) + expressions.append(self._handle_param(precision, 'precision', int)) super().__init__(*expressions, **extra) def as_mysql(self, compiler, connection, **extra_context): @@ -371,8 +325,8 @@ class GeoHash(GeoFunc): class GeometryDistance(GeoFunc): output_field = FloatField() arity = 2 - function = "" - arg_joiner = " <-> " + function = '' + arg_joiner = ' <-> ' geom_param_pos = (0, 1) @@ -383,7 +337,7 @@ class Intersection(OracleToleranceMixin, GeomOutputGeoFunc): @BaseSpatialField.register_lookup class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform): - lookup_name = "isvalid" + lookup_name = 'isvalid' output_field = BooleanField() def as_oracle(self, compiler, connection, **extra_context): @@ -397,13 +351,8 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): super().__init__(expr1, **extra) def as_sql(self, compiler, connection, **extra_context): - if ( - self.geo_field.geodetic(connection) - and not connection.features.supports_length_geodetic - ): - raise NotSupportedError( - "This backend doesn't support Length on geodetic fields" - ) + if self.geo_field.geodetic(connection) and not connection.features.supports_length_geodetic: + raise NotSupportedError("This backend doesn't support Length on geodetic fields") return super().as_sql(compiler, connection, **extra_context) def as_postgresql(self, compiler, connection, **extra_context): @@ -413,20 +362,18 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): clone.source_expressions.append(Value(self.spheroid)) elif self.geo_field.geodetic(connection): # Geometry fields with geodetic (lon/lat) coordinates need length_spheroid - function = connection.ops.spatial_function_name("LengthSpheroid") + function = connection.ops.spatial_function_name('LengthSpheroid') clone.source_expressions.append(Value(self.geo_field.spheroid(connection))) else: dim = min(f.dim for f in self.get_source_fields() if f) if dim > 2: function = connection.ops.length3d - return super(Length, clone).as_sql( - compiler, connection, function=function, **extra_context - ) + return super(Length, clone).as_sql(compiler, connection, function=function, **extra_context) def as_sqlite(self, compiler, connection, **extra_context): function = None if self.geo_field.geodetic(connection): - function = "GeodesicLength" if self.spheroid else "GreatCircleLength" + function = 'GeodesicLength' if self.spheroid else 'GreatCircleLength' return super().as_sql(compiler, connection, function=function, **extra_context) @@ -461,9 +408,7 @@ class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc): def as_postgresql(self, compiler, connection, **extra_context): function = None if self.geo_field.geodetic(connection) and not self.source_is_geography(): - raise NotSupportedError( - "ST_Perimeter cannot use a non-projected non-geography field." - ) + raise NotSupportedError("ST_Perimeter cannot use a non-projected non-geography field.") dim = min(f.dim for f in self.get_source_fields()) if dim > 2: function = connection.ops.perimeter3d @@ -487,11 +432,11 @@ class Scale(SQLiteDecimalToFloatMixin, GeomOutputGeoFunc): def __init__(self, expression, x, y, z=0.0, **extra): expressions = [ expression, - self._handle_param(x, "x", NUMERIC_TYPES), - self._handle_param(y, "y", NUMERIC_TYPES), + self._handle_param(x, 'x', NUMERIC_TYPES), + self._handle_param(y, 'y', NUMERIC_TYPES), ] if z != 0.0: - expressions.append(self._handle_param(z, "z", NUMERIC_TYPES)) + expressions.append(self._handle_param(z, 'z', NUMERIC_TYPES)) super().__init__(*expressions, **extra) @@ -501,16 +446,16 @@ class SnapToGrid(SQLiteDecimalToFloatMixin, GeomOutputGeoFunc): expressions = [expression] if nargs in (1, 2): expressions.extend( - [self._handle_param(arg, "", NUMERIC_TYPES) for arg in args] + [self._handle_param(arg, '', NUMERIC_TYPES) for arg in args] ) elif nargs == 4: # Reverse origin and size param ordering expressions += [ - *(self._handle_param(arg, "", NUMERIC_TYPES) for arg in args[2:]), - *(self._handle_param(arg, "", NUMERIC_TYPES) for arg in args[0:2]), + *(self._handle_param(arg, '', NUMERIC_TYPES) for arg in args[2:]), + *(self._handle_param(arg, '', NUMERIC_TYPES) for arg in args[0:2]), ] else: - raise ValueError("Must provide 1, 2, or 4 arguments to `SnapToGrid`.") + raise ValueError('Must provide 1, 2, or 4 arguments to `SnapToGrid`.') super().__init__(*expressions, **extra) @@ -523,10 +468,10 @@ class Transform(GeomOutputGeoFunc): def __init__(self, expression, srid, **extra): expressions = [ expression, - self._handle_param(srid, "srid", int), + self._handle_param(srid, 'srid', int), ] - if "output_field" not in extra: - extra["output_field"] = GeometryField(srid=srid) + if 'output_field' not in extra: + extra['output_field'] = GeometryField(srid=srid) super().__init__(*expressions, **extra) diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/lookups.py b/venv/Lib/site-packages/django/contrib/gis/db/models/lookups.py index 07762d0..c1d021b 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/lookups.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/lookups.py @@ -27,10 +27,10 @@ class GISLookup(Lookup): def process_rhs_params(self): if self.rhs_params: # Check if a band index was passed in the query argument. - if len(self.rhs_params) == (2 if self.lookup_name == "relate" else 1): + if len(self.rhs_params) == (2 if self.lookup_name == 'relate' else 1): self.process_band_indices() elif len(self.rhs_params) > 1: - raise ValueError("Tuple too long for lookup %s." % self.lookup_name) + raise ValueError('Tuple too long for lookup %s.' % self.lookup_name) elif isinstance(self.lhs, RasterBandTransform): self.process_band_indices(only_lhs=True) @@ -55,7 +55,7 @@ class GISLookup(Lookup): def get_db_prep_lookup(self, value, connection): # get_db_prep_lookup is called by process_rhs from super class - return ("%s", [connection.ops.Adapter(value)]) + return ('%s', [connection.ops.Adapter(value)]) def process_rhs(self, compiler, connection): if isinstance(self.rhs, Query): @@ -64,9 +64,7 @@ class GISLookup(Lookup): if isinstance(self.rhs, Expression): self.rhs = self.rhs.resolve_expression(compiler.query) rhs, rhs_params = super().process_rhs(compiler, connection) - placeholder = connection.ops.get_geom_placeholder( - self.lhs.output_field, self.rhs, compiler - ) + placeholder = connection.ops.get_geom_placeholder(self.lhs.output_field, self.rhs, compiler) return placeholder % rhs, rhs_params def get_rhs_op(self, connection, rhs): @@ -80,12 +78,7 @@ class GISLookup(Lookup): rhs_sql, rhs_params = self.process_rhs(compiler, connection) sql_params = (*lhs_params, *rhs_params) - template_params = { - "lhs": lhs_sql, - "rhs": rhs_sql, - "value": "%s", - **self.template_params, - } + template_params = {'lhs': lhs_sql, 'rhs': rhs_sql, 'value': '%s', **self.template_params} rhs_op = self.get_rhs_op(connection, rhs_sql) return rhs_op.as_sql(connection, self, template_params, sql_params) @@ -94,15 +87,13 @@ class GISLookup(Lookup): # Geometry operators # ------------------ - @BaseSpatialField.register_lookup class OverlapsLeftLookup(GISLookup): """ The overlaps_left operator returns true if A's bounding box overlaps or is to the left of B's bounding box. """ - - lookup_name = "overlaps_left" + lookup_name = 'overlaps_left' @BaseSpatialField.register_lookup @@ -111,8 +102,7 @@ class OverlapsRightLookup(GISLookup): The 'overlaps_right' operator returns true if A's bounding box overlaps or is to the right of B's bounding box. """ - - lookup_name = "overlaps_right" + lookup_name = 'overlaps_right' @BaseSpatialField.register_lookup @@ -121,8 +111,7 @@ class OverlapsBelowLookup(GISLookup): The 'overlaps_below' operator returns true if A's bounding box overlaps or is below B's bounding box. """ - - lookup_name = "overlaps_below" + lookup_name = 'overlaps_below' @BaseSpatialField.register_lookup @@ -131,8 +120,7 @@ class OverlapsAboveLookup(GISLookup): The 'overlaps_above' operator returns true if A's bounding box overlaps or is above B's bounding box. """ - - lookup_name = "overlaps_above" + lookup_name = 'overlaps_above' @BaseSpatialField.register_lookup @@ -141,8 +129,7 @@ class LeftLookup(GISLookup): The 'left' operator returns true if A's bounding box is strictly to the left of B's bounding box. """ - - lookup_name = "left" + lookup_name = 'left' @BaseSpatialField.register_lookup @@ -151,8 +138,7 @@ class RightLookup(GISLookup): The 'right' operator returns true if A's bounding box is strictly to the right of B's bounding box. """ - - lookup_name = "right" + lookup_name = 'right' @BaseSpatialField.register_lookup @@ -161,8 +147,7 @@ class StrictlyBelowLookup(GISLookup): The 'strictly_below' operator returns true if A's bounding box is strictly below B's bounding box. """ - - lookup_name = "strictly_below" + lookup_name = 'strictly_below' @BaseSpatialField.register_lookup @@ -171,8 +156,7 @@ class StrictlyAboveLookup(GISLookup): The 'strictly_above' operator returns true if A's bounding box is strictly above B's bounding box. """ - - lookup_name = "strictly_above" + lookup_name = 'strictly_above' @BaseSpatialField.register_lookup @@ -182,11 +166,10 @@ class SameAsLookup(GISLookup): equality of two features. So if A and B are the same feature, vertex-by-vertex, the operator returns true. """ - - lookup_name = "same_as" + lookup_name = 'same_as' -BaseSpatialField.register_lookup(SameAsLookup, "exact") +BaseSpatialField.register_lookup(SameAsLookup, 'exact') @BaseSpatialField.register_lookup @@ -195,18 +178,15 @@ class BBContainsLookup(GISLookup): The 'bbcontains' operator returns true if A's bounding box completely contains by B's bounding box. """ - - lookup_name = "bbcontains" + lookup_name = 'bbcontains' @BaseSpatialField.register_lookup class BBOverlapsLookup(GISLookup): """ - The 'bboverlaps' operator returns true if A's bounding box overlaps B's - bounding box. + The 'bboverlaps' operator returns true if A's bounding box overlaps B's bounding box. """ - - lookup_name = "bboverlaps" + lookup_name = 'bboverlaps' @BaseSpatialField.register_lookup @@ -215,71 +195,69 @@ class ContainedLookup(GISLookup): The 'contained' operator returns true if A's bounding box is completely contained by B's bounding box. """ - - lookup_name = "contained" + lookup_name = 'contained' # ------------------ # Geometry functions # ------------------ - @BaseSpatialField.register_lookup class ContainsLookup(GISLookup): - lookup_name = "contains" + lookup_name = 'contains' @BaseSpatialField.register_lookup class ContainsProperlyLookup(GISLookup): - lookup_name = "contains_properly" + lookup_name = 'contains_properly' @BaseSpatialField.register_lookup class CoveredByLookup(GISLookup): - lookup_name = "coveredby" + lookup_name = 'coveredby' @BaseSpatialField.register_lookup class CoversLookup(GISLookup): - lookup_name = "covers" + lookup_name = 'covers' @BaseSpatialField.register_lookup class CrossesLookup(GISLookup): - lookup_name = "crosses" + lookup_name = 'crosses' @BaseSpatialField.register_lookup class DisjointLookup(GISLookup): - lookup_name = "disjoint" + lookup_name = 'disjoint' @BaseSpatialField.register_lookup class EqualsLookup(GISLookup): - lookup_name = "equals" + lookup_name = 'equals' @BaseSpatialField.register_lookup class IntersectsLookup(GISLookup): - lookup_name = "intersects" + lookup_name = 'intersects' @BaseSpatialField.register_lookup class OverlapsLookup(GISLookup): - lookup_name = "overlaps" + lookup_name = 'overlaps' @BaseSpatialField.register_lookup class RelateLookup(GISLookup): - lookup_name = "relate" - sql_template = "%(func)s(%(lhs)s, %(rhs)s, %%s)" - pattern_regex = _lazy_re_compile(r"^[012TF\*]{9}$") + lookup_name = 'relate' + sql_template = '%(func)s(%(lhs)s, %(rhs)s, %%s)' + pattern_regex = _lazy_re_compile(r'^[012TF\*]{9}$') def process_rhs(self, compiler, connection): # Check the pattern argument pattern = self.rhs_params[0] backend_op = connection.ops.gis_operators[self.lookup_name] - if hasattr(backend_op, "check_relate_argument"): + if hasattr(backend_op, 'check_relate_argument'): backend_op.check_relate_argument(pattern) elif not isinstance(pattern, str) or not self.pattern_regex.match(pattern): raise ValueError('Invalid intersection matrix pattern "%s".' % pattern) @@ -289,107 +267,93 @@ class RelateLookup(GISLookup): @BaseSpatialField.register_lookup class TouchesLookup(GISLookup): - lookup_name = "touches" + lookup_name = 'touches' @BaseSpatialField.register_lookup class WithinLookup(GISLookup): - lookup_name = "within" + lookup_name = 'within' class DistanceLookupBase(GISLookup): distance = True - sql_template = "%(func)s(%(lhs)s, %(rhs)s) %(op)s %(value)s" + sql_template = '%(func)s(%(lhs)s, %(rhs)s) %(op)s %(value)s' def process_rhs_params(self): if not 1 <= len(self.rhs_params) <= 3: - raise ValueError( - "2, 3, or 4-element tuple required for '%s' lookup." % self.lookup_name - ) - elif len(self.rhs_params) == 3 and self.rhs_params[2] != "spheroid": - raise ValueError( - "For 4-element tuples the last argument must be the 'spheroid' " - "directive." - ) + raise ValueError("2, 3, or 4-element tuple required for '%s' lookup." % self.lookup_name) + elif len(self.rhs_params) == 3 and self.rhs_params[2] != 'spheroid': + raise ValueError("For 4-element tuples the last argument must be the 'spheroid' directive.") # Check if the second parameter is a band index. - if len(self.rhs_params) > 1 and self.rhs_params[1] != "spheroid": + if len(self.rhs_params) > 1 and self.rhs_params[1] != 'spheroid': self.process_band_indices() def process_distance(self, compiler, connection): dist_param = self.rhs_params[0] return ( compiler.compile(dist_param.resolve_expression(compiler.query)) - if hasattr(dist_param, "resolve_expression") - else ( - "%s", - connection.ops.get_distance( - self.lhs.output_field, self.rhs_params, self.lookup_name - ), - ) + if hasattr(dist_param, 'resolve_expression') else + ('%s', connection.ops.get_distance(self.lhs.output_field, self.rhs_params, self.lookup_name)) ) @BaseSpatialField.register_lookup class DWithinLookup(DistanceLookupBase): - lookup_name = "dwithin" - sql_template = "%(func)s(%(lhs)s, %(rhs)s, %(value)s)" + lookup_name = 'dwithin' + sql_template = '%(func)s(%(lhs)s, %(rhs)s, %(value)s)' def process_distance(self, compiler, connection): dist_param = self.rhs_params[0] if ( - not connection.features.supports_dwithin_distance_expr - and hasattr(dist_param, "resolve_expression") - and not isinstance(dist_param, Distance) + not connection.features.supports_dwithin_distance_expr and + hasattr(dist_param, 'resolve_expression') and + not isinstance(dist_param, Distance) ): raise NotSupportedError( - "This backend does not support expressions for specifying " - "distance in the dwithin lookup." + 'This backend does not support expressions for specifying ' + 'distance in the dwithin lookup.' ) return super().process_distance(compiler, connection) def process_rhs(self, compiler, connection): dist_sql, dist_params = self.process_distance(compiler, connection) - self.template_params["value"] = dist_sql + self.template_params['value'] = dist_sql rhs_sql, params = super().process_rhs(compiler, connection) return rhs_sql, params + dist_params class DistanceLookupFromFunction(DistanceLookupBase): def as_sql(self, compiler, connection): - spheroid = ( - len(self.rhs_params) == 2 and self.rhs_params[-1] == "spheroid" - ) or None - distance_expr = connection.ops.distance_expr_for_lookup( - self.lhs, self.rhs, spheroid=spheroid - ) + spheroid = (len(self.rhs_params) == 2 and self.rhs_params[-1] == 'spheroid') or None + distance_expr = connection.ops.distance_expr_for_lookup(self.lhs, self.rhs, spheroid=spheroid) sql, params = compiler.compile(distance_expr.resolve_expression(compiler.query)) dist_sql, dist_params = self.process_distance(compiler, connection) return ( - "%(func)s %(op)s %(dist)s" % {"func": sql, "op": self.op, "dist": dist_sql}, + '%(func)s %(op)s %(dist)s' % {'func': sql, 'op': self.op, 'dist': dist_sql}, params + dist_params, ) @BaseSpatialField.register_lookup class DistanceGTLookup(DistanceLookupFromFunction): - lookup_name = "distance_gt" - op = ">" + lookup_name = 'distance_gt' + op = '>' @BaseSpatialField.register_lookup class DistanceGTELookup(DistanceLookupFromFunction): - lookup_name = "distance_gte" - op = ">=" + lookup_name = 'distance_gte' + op = '>=' @BaseSpatialField.register_lookup class DistanceLTLookup(DistanceLookupFromFunction): - lookup_name = "distance_lt" - op = "<" + lookup_name = 'distance_lt' + op = '<' @BaseSpatialField.register_lookup class DistanceLTELookup(DistanceLookupFromFunction): - lookup_name = "distance_lte" - op = "<=" + lookup_name = 'distance_lte' + op = '<=' diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/proxy.py b/venv/Lib/site-packages/django/contrib/gis/db/models/proxy.py index 4db365d..adf2bae 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/proxy.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/proxy.py @@ -37,7 +37,7 @@ class SpatialProxy(DeferredAttribute): if isinstance(geo_value, self._klass): geo_obj = geo_value - elif (geo_value is None) or (geo_value == ""): + elif (geo_value is None) or (geo_value == ''): geo_obj = None else: # Otherwise, a geometry or raster object is built using the field's @@ -57,10 +57,8 @@ class SpatialProxy(DeferredAttribute): # The geographic type of the field. gtype = self.field.geom_type - if gtype == "RASTER" and ( - value is None or isinstance(value, (str, dict, self._klass)) - ): - # For raster fields, ensure input is None or a string, dict, or + if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))): + # For raster fields, assure input is None or a string, dict, or # raster instance. pass elif isinstance(value, self._klass): @@ -73,10 +71,8 @@ class SpatialProxy(DeferredAttribute): # Set geometries with None, WKT, HEX, or WKB pass else: - raise TypeError( - "Cannot set %s SpatialProxy (%s) with value of type: %s" - % (instance.__class__.__name__, gtype, type(value)) - ) + raise TypeError('Cannot set %s SpatialProxy (%s) with value of type: %s' % ( + instance.__class__.__name__, gtype, type(value))) # Setting the objects dictionary with the value, and returning. instance.__dict__[self.field.attname] = value diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/sql/__init__.py b/venv/Lib/site-packages/django/contrib/gis/db/models/sql/__init__.py index 1376e8d..850c644 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/sql/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/sql/__init__.py @@ -1,6 +1,7 @@ -from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField +from django.contrib.gis.db.models.sql.conversion import ( + AreaField, DistanceField, +) __all__ = [ - "AreaField", - "DistanceField", + 'AreaField', 'DistanceField', ] diff --git a/venv/Lib/site-packages/django/contrib/gis/db/models/sql/conversion.py b/venv/Lib/site-packages/django/contrib/gis/db/models/sql/conversion.py index be71231..99ab51e 100644 --- a/venv/Lib/site-packages/django/contrib/gis/db/models/sql/conversion.py +++ b/venv/Lib/site-packages/django/contrib/gis/db/models/sql/conversion.py @@ -10,14 +10,13 @@ from django.db import models class AreaField(models.FloatField): "Wrapper for Area values." - def __init__(self, geo_field): super().__init__() self.geo_field = geo_field def get_prep_value(self, value): if not isinstance(value, Area): - raise ValueError("AreaField only accepts Area measurement objects.") + raise ValueError('AreaField only accepts Area measurement objects.') return value def get_db_prep_value(self, value, connection, prepared=False): @@ -38,12 +37,11 @@ class AreaField(models.FloatField): return Area(**{area_att: value}) if area_att else value def get_internal_type(self): - return "AreaField" + return 'AreaField' class DistanceField(models.FloatField): "Wrapper for Distance values." - def __init__(self, geo_field): super().__init__() self.geo_field = geo_field @@ -58,9 +56,7 @@ class DistanceField(models.FloatField): return value distance_att = connection.ops.get_distance_att_for_field(self.geo_field) if not distance_att: - raise ValueError( - "Distance measure is supplied, but units are unknown for result." - ) + raise ValueError('Distance measure is supplied, but units are unknown for result.') return getattr(value, distance_att) def from_db_value(self, value, expression, connection): @@ -70,4 +66,4 @@ class DistanceField(models.FloatField): return Distance(**{distance_att: value}) if distance_att else value def get_internal_type(self): - return "DistanceField" + return 'DistanceField' diff --git a/venv/Lib/site-packages/django/contrib/gis/feeds.py b/venv/Lib/site-packages/django/contrib/gis/feeds.py index ebd4511..cfc078b 100644 --- a/venv/Lib/site-packages/django/contrib/gis/feeds.py +++ b/venv/Lib/site-packages/django/contrib/gis/feeds.py @@ -14,7 +14,7 @@ class GeoFeedMixin: a single white space. Given a tuple of coordinates, return a string GeoRSS representation. """ - return " ".join("%f %f" % (coord[1], coord[0]) for coord in coords) + return ' '.join('%f %f' % (coord[1], coord[0]) for coord in coords) def add_georss_point(self, handler, coords, w3c_geo=False): """ @@ -24,15 +24,15 @@ class GeoFeedMixin: """ if w3c_geo: lon, lat = coords[:2] - handler.addQuickElement("geo:lat", "%f" % lat) - handler.addQuickElement("geo:lon", "%f" % lon) + handler.addQuickElement('geo:lat', '%f' % lat) + handler.addQuickElement('geo:lon', '%f' % lon) else: - handler.addQuickElement("georss:point", self.georss_coords((coords,))) + handler.addQuickElement('georss:point', self.georss_coords((coords,))) def add_georss_element(self, handler, item, w3c_geo=False): """Add a GeoRSS XML element using the given item and handler.""" # Getting the Geometry object. - geom = item.get("geometry") + geom = item.get('geometry') if geom is not None: if isinstance(geom, (list, tuple)): # Special case if a tuple/list was passed in. The tuple may be @@ -43,7 +43,7 @@ class GeoFeedMixin: if len(geom) == 2: box_coords = geom else: - raise ValueError("Only should be two sets of coordinates.") + raise ValueError('Only should be two sets of coordinates.') else: if len(geom) == 2: # Point: (X, Y) @@ -52,46 +52,36 @@ class GeoFeedMixin: # Box: (X0, Y0, X1, Y1) box_coords = (geom[:2], geom[2:]) else: - raise ValueError("Only should be 2 or 4 numeric elements.") + raise ValueError('Only should be 2 or 4 numeric elements.') # If a GeoRSS box was given via tuple. if box_coords is not None: if w3c_geo: - raise ValueError( - "Cannot use simple GeoRSS box in W3C Geo feeds." - ) - handler.addQuickElement( - "georss:box", self.georss_coords(box_coords) - ) + raise ValueError('Cannot use simple GeoRSS box in W3C Geo feeds.') + handler.addQuickElement('georss:box', self.georss_coords(box_coords)) else: # Getting the lowercase geometry type. gtype = str(geom.geom_type).lower() - if gtype == "point": + if gtype == 'point': self.add_georss_point(handler, geom.coords, w3c_geo=w3c_geo) else: if w3c_geo: - raise ValueError("W3C Geo only supports Point geometries.") + raise ValueError('W3C Geo only supports Point geometries.') # For formatting consistent w/the GeoRSS simple standard: # http://georss.org/1.0#simple - if gtype in ("linestring", "linearring"): - handler.addQuickElement( - "georss:line", self.georss_coords(geom.coords) - ) - elif gtype in ("polygon",): + if gtype in ('linestring', 'linearring'): + handler.addQuickElement('georss:line', self.georss_coords(geom.coords)) + elif gtype in ('polygon',): # Only support the exterior ring. - handler.addQuickElement( - "georss:polygon", self.georss_coords(geom[0].coords) - ) + handler.addQuickElement('georss:polygon', self.georss_coords(geom[0].coords)) else: - raise ValueError( - 'Geometry type "%s" not supported.' % geom.geom_type - ) + raise ValueError('Geometry type "%s" not supported.' % geom.geom_type) # ### SyndicationFeed subclasses ### class GeoRSSFeed(Rss201rev2Feed, GeoFeedMixin): def rss_attributes(self): attrs = super().rss_attributes() - attrs["xmlns:georss"] = "http://www.georss.org/georss" + attrs['xmlns:georss'] = 'http://www.georss.org/georss' return attrs def add_item_elements(self, handler, item): @@ -106,7 +96,7 @@ class GeoRSSFeed(Rss201rev2Feed, GeoFeedMixin): class GeoAtom1Feed(Atom1Feed, GeoFeedMixin): def root_attributes(self): attrs = super().root_attributes() - attrs["xmlns:georss"] = "http://www.georss.org/georss" + attrs['xmlns:georss'] = 'http://www.georss.org/georss' return attrs def add_item_elements(self, handler, item): @@ -121,7 +111,7 @@ class GeoAtom1Feed(Atom1Feed, GeoFeedMixin): class W3CGeoFeed(Rss201rev2Feed, GeoFeedMixin): def rss_attributes(self): attrs = super().rss_attributes() - attrs["xmlns:geo"] = "http://www.w3.org/2003/01/geo/wgs84_pos#" + attrs['xmlns:geo'] = 'http://www.w3.org/2003/01/geo/wgs84_pos#' return attrs def add_item_elements(self, handler, item): @@ -141,11 +131,10 @@ class Feed(BaseFeed): methods on their own subclasses so that geo-referenced information may placed in the feed. """ - feed_type = GeoRSSFeed def feed_extra_kwargs(self, obj): - return {"geometry": self._get_dynamic_attr("geometry", obj)} + return {'geometry': self._get_dynamic_attr('geometry', obj)} def item_extra_kwargs(self, item): - return {"geometry": self._get_dynamic_attr("item_geometry", item)} + return {'geometry': self._get_dynamic_attr('item_geometry', item)} diff --git a/venv/Lib/site-packages/django/contrib/gis/forms/__init__.py b/venv/Lib/site-packages/django/contrib/gis/forms/__init__.py index c07720b..237cbac 100644 --- a/venv/Lib/site-packages/django/contrib/gis/forms/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/forms/__init__.py @@ -1,13 +1,8 @@ from django.forms import * # NOQA from .fields import ( # NOQA - GeometryCollectionField, - GeometryField, - LineStringField, - MultiLineStringField, - MultiPointField, - MultiPolygonField, - PointField, + GeometryCollectionField, GeometryField, LineStringField, + MultiLineStringField, MultiPointField, MultiPolygonField, PointField, PolygonField, ) from .widgets import BaseGeometryWidget, OpenLayersWidget, OSMWidget # NOQA diff --git a/venv/Lib/site-packages/django/contrib/gis/forms/fields.py b/venv/Lib/site-packages/django/contrib/gis/forms/fields.py index 1fd3153..301d770 100644 --- a/venv/Lib/site-packages/django/contrib/gis/forms/fields.py +++ b/venv/Lib/site-packages/django/contrib/gis/forms/fields.py @@ -13,18 +13,15 @@ class GeometryField(forms.Field): accepted by GEOSGeometry is accepted by this form. By default, this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON. """ - widget = OpenLayersWidget - geom_type = "GEOMETRY" + geom_type = 'GEOMETRY' default_error_messages = { - "required": _("No geometry value provided."), - "invalid_geom": _("Invalid geometry value."), - "invalid_geom_type": _("Invalid geometry type."), - "transform_error": _( - "An error occurred when transforming the geometry " - "to the SRID of the geometry form field." - ), + 'required': _('No geometry value provided.'), + 'invalid_geom': _('Invalid geometry value.'), + 'invalid_geom_type': _('Invalid geometry type.'), + 'transform_error': _('An error occurred when transforming the geometry ' + 'to the SRID of the geometry form field.'), } def __init__(self, *, srid=None, geom_type=None, **kwargs): @@ -32,7 +29,7 @@ class GeometryField(forms.Field): if geom_type is not None: self.geom_type = geom_type super().__init__(**kwargs) - self.widget.attrs["geom_type"] = self.geom_type + self.widget.attrs['geom_type'] = self.geom_type def to_python(self, value): """Transform the value to a Geometry object.""" @@ -40,7 +37,7 @@ class GeometryField(forms.Field): return None if not isinstance(value, GEOSGeometry): - if hasattr(self.widget, "deserialize"): + if hasattr(self.widget, 'deserialize'): try: value = self.widget.deserialize(value) except GDALException: @@ -51,9 +48,7 @@ class GeometryField(forms.Field): except (GEOSException, ValueError, TypeError): value = None if value is None: - raise ValidationError( - self.error_messages["invalid_geom"], code="invalid_geom" - ) + raise ValidationError(self.error_messages['invalid_geom'], code='invalid_geom') # Try to set the srid if not value.srid: @@ -76,13 +71,8 @@ class GeometryField(forms.Field): # Ensuring that the geometry is of the correct type (indicated # using the OGC string label). - if ( - str(geom.geom_type).upper() != self.geom_type - and self.geom_type != "GEOMETRY" - ): - raise ValidationError( - self.error_messages["invalid_geom_type"], code="invalid_geom_type" - ) + if str(geom.geom_type).upper() != self.geom_type and self.geom_type != 'GEOMETRY': + raise ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type') # Transforming the geometry if the SRID was set. if self.srid and self.srid != -1 and self.srid != geom.srid: @@ -90,13 +80,12 @@ class GeometryField(forms.Field): geom.transform(self.srid) except GEOSException: raise ValidationError( - self.error_messages["transform_error"], code="transform_error" - ) + self.error_messages['transform_error'], code='transform_error') return geom def has_changed(self, initial, data): - """Compare geographic value of data with its initial value.""" + """ Compare geographic value of data with its initial value. """ try: data = self.to_python(data) @@ -117,28 +106,28 @@ class GeometryField(forms.Field): class GeometryCollectionField(GeometryField): - geom_type = "GEOMETRYCOLLECTION" + geom_type = 'GEOMETRYCOLLECTION' class PointField(GeometryField): - geom_type = "POINT" + geom_type = 'POINT' class MultiPointField(GeometryField): - geom_type = "MULTIPOINT" + geom_type = 'MULTIPOINT' class LineStringField(GeometryField): - geom_type = "LINESTRING" + geom_type = 'LINESTRING' class MultiLineStringField(GeometryField): - geom_type = "MULTILINESTRING" + geom_type = 'MULTILINESTRING' class PolygonField(GeometryField): - geom_type = "POLYGON" + geom_type = 'POLYGON' class MultiPolygonField(GeometryField): - geom_type = "MULTIPOLYGON" + geom_type = 'MULTIPOLYGON' diff --git a/venv/Lib/site-packages/django/contrib/gis/forms/widgets.py b/venv/Lib/site-packages/django/contrib/gis/forms/widgets.py index 0f53ee2..e38f173 100644 --- a/venv/Lib/site-packages/django/contrib/gis/forms/widgets.py +++ b/venv/Lib/site-packages/django/contrib/gis/forms/widgets.py @@ -7,7 +7,7 @@ from django.contrib.gis.geos import GEOSException, GEOSGeometry from django.forms.widgets import Widget from django.utils import translation -logger = logging.getLogger("django.contrib.gis") +logger = logging.getLogger('django.contrib.gis') class BaseGeometryWidget(Widget): @@ -15,25 +15,24 @@ class BaseGeometryWidget(Widget): The base class for rich geometry widgets. Render a map using the WKT of the geometry. """ - - geom_type = "GEOMETRY" + geom_type = 'GEOMETRY' map_srid = 4326 map_width = 600 map_height = 400 display_raw = False supports_3d = False - template_name = "" # set on subclasses + template_name = '' # set on subclasses def __init__(self, attrs=None): self.attrs = {} - for key in ("geom_type", "map_srid", "map_width", "map_height", "display_raw"): + for key in ('geom_type', 'map_srid', 'map_width', 'map_height', 'display_raw'): self.attrs[key] = getattr(self, key) if attrs: self.attrs.update(attrs) def serialize(self, value): - return value.wkt if value else "" + return value.wkt if value else '' def deserialize(self, value): try: @@ -59,47 +58,40 @@ class BaseGeometryWidget(Widget): except gdal.GDALException as err: logger.error( "Error transforming geometry from srid '%s' to srid '%s' (%s)", - value.srid, - self.map_srid, - err, + value.srid, self.map_srid, err ) - geom_type = gdal.OGRGeomType(self.attrs["geom_type"]).name - context.update( - self.build_attrs( - self.attrs, - { - "name": name, - "module": "geodjango_%s" % name.replace("-", "_"), # JS-safe - "serialized": self.serialize(value), - "geom_type": "Geometry" if geom_type == "Unknown" else geom_type, - "STATIC_URL": settings.STATIC_URL, - "LANGUAGE_BIDI": translation.get_language_bidi(), - **(attrs or {}), - }, - ) - ) + geom_type = gdal.OGRGeomType(self.attrs['geom_type']).name + context.update(self.build_attrs(self.attrs, { + 'name': name, + 'module': 'geodjango_%s' % name.replace('-', '_'), # JS-safe + 'serialized': self.serialize(value), + 'geom_type': 'Geometry' if geom_type == 'Unknown' else geom_type, + 'STATIC_URL': settings.STATIC_URL, + 'LANGUAGE_BIDI': translation.get_language_bidi(), + **(attrs or {}), + })) return context class OpenLayersWidget(BaseGeometryWidget): - template_name = "gis/openlayers.html" + template_name = 'gis/openlayers.html' map_srid = 3857 class Media: css = { - "all": ( - "https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.css", - "gis/css/ol3.css", + 'all': ( + 'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.css', + 'gis/css/ol3.css', ) } js = ( - "https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.js", - "gis/js/OLMapWidget.js", + 'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.js', + 'gis/js/OLMapWidget.js', ) def serialize(self, value): - return value.json if value else "" + return value.json if value else '' def deserialize(self, value): geom = super().deserialize(value) @@ -113,15 +105,14 @@ class OSMWidget(OpenLayersWidget): """ An OpenLayers/OpenStreetMap-based widget. """ - - template_name = "gis/openlayers-osm.html" + template_name = 'gis/openlayers-osm.html' default_lon = 5 default_lat = 47 default_zoom = 12 def __init__(self, attrs=None): super().__init__() - for key in ("default_lon", "default_lat", "default_zoom"): + for key in ('default_lon', 'default_lat', 'default_zoom'): self.attrs[key] = getattr(self, key) if attrs: self.attrs.update(attrs) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/__init__.py b/venv/Lib/site-packages/django/contrib/gis/gdal/__init__.py index 9ed6e31..1d8c688 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/__init__.py @@ -28,31 +28,22 @@ from django.contrib.gis.gdal.datasource import DataSource from django.contrib.gis.gdal.driver import Driver from django.contrib.gis.gdal.envelope import Envelope -from django.contrib.gis.gdal.error import GDALException, SRSException, check_err +from django.contrib.gis.gdal.error import ( + GDALException, SRSException, check_err, +) from django.contrib.gis.gdal.geometries import OGRGeometry from django.contrib.gis.gdal.geomtype import OGRGeomType from django.contrib.gis.gdal.libgdal import ( - GDAL_VERSION, - gdal_full_version, - gdal_version, + GDAL_VERSION, gdal_full_version, gdal_version, ) from django.contrib.gis.gdal.raster.source import GDALRaster -from django.contrib.gis.gdal.srs import AxisOrder, CoordTransform, SpatialReference +from django.contrib.gis.gdal.srs import ( + AxisOrder, CoordTransform, SpatialReference, +) __all__ = ( - "AxisOrder", - "Driver", - "DataSource", - "CoordTransform", - "Envelope", - "GDALException", - "GDALRaster", - "GDAL_VERSION", - "OGRGeometry", - "OGRGeomType", - "SpatialReference", - "SRSException", - "check_err", - "gdal_version", - "gdal_full_version", + 'AxisOrder', 'Driver', 'DataSource', 'CoordTransform', 'Envelope', + 'GDALException', 'GDALRaster', 'GDAL_VERSION', 'OGRGeometry', + 'OGRGeomType', 'SpatialReference', 'SRSException', 'check_err', + 'gdal_version', 'gdal_full_version', ) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/datasource.py b/venv/Lib/site-packages/django/contrib/gis/gdal/datasource.py index d1695aa..6b7112b 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/datasource.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/datasource.py @@ -44,15 +44,15 @@ from django.contrib.gis.gdal.prototypes import ds as capi from django.utils.encoding import force_bytes, force_str -# For more information, see the OGR C API documentation: -# https://gdal.org/api/vector_c_api.html +# For more information, see the OGR C API source code: +# https://www.gdal.org/ogr__api_8h.html # # The OGR_DS_* routines are relevant here. class DataSource(GDALBase): "Wraps an OGR Data Source object." destructor = capi.destroy_ds - def __init__(self, ds_input, ds_driver=False, write=False, encoding="utf-8"): + def __init__(self, ds_input, ds_driver=False, write=False, encoding='utf-8'): # The write flag. if write: self._write = 1 @@ -73,12 +73,10 @@ class DataSource(GDALBase): # Making the error message more clear rather than something # like "Invalid pointer returned from OGROpen". raise GDALException('Could not open the datasource at "%s"' % ds_input) - elif isinstance(ds_input, self.ptr_type) and isinstance( - ds_driver, Driver.ptr_type - ): + elif isinstance(ds_input, self.ptr_type) and isinstance(ds_driver, Driver.ptr_type): ds = ds_input else: - raise GDALException("Invalid data source input type: %s" % type(ds_input)) + raise GDALException('Invalid data source input type: %s' % type(ds_input)) if ds: self.ptr = ds @@ -93,17 +91,14 @@ class DataSource(GDALBase): try: layer = capi.get_layer_by_name(self.ptr, force_bytes(index)) except GDALException: - raise IndexError("Invalid OGR layer name given: %s." % index) + raise IndexError('Invalid OGR layer name given: %s.' % index) elif isinstance(index, int): if 0 <= index < self.layer_count: layer = capi.get_layer(self._ptr, index) else: - raise IndexError( - "Index out of range when accessing layers in a datasource: %s." - % index - ) + raise IndexError('Index out of range when accessing layers in a datasource: %s.' % index) else: - raise TypeError("Invalid index type: %s" % type(index)) + raise TypeError('Invalid index type: %s' % type(index)) return Layer(layer, self) def __len__(self): @@ -112,7 +107,7 @@ class DataSource(GDALBase): def __str__(self): "Return OGR GetName and Driver for the Data Source." - return "%s (%s)" % (self.name, self.driver) + return '%s (%s)' % (self.name, self.driver) @property def layer_count(self): diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/driver.py b/venv/Lib/site-packages/django/contrib/gis/gdal/driver.py index 0ce7a2c..13d92e9 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/driver.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/driver.py @@ -2,35 +2,33 @@ from ctypes import c_void_p from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import GDALException -from django.contrib.gis.gdal.prototypes import ds as vcapi -from django.contrib.gis.gdal.prototypes import raster as rcapi +from django.contrib.gis.gdal.prototypes import ds as vcapi, raster as rcapi from django.utils.encoding import force_bytes, force_str class Driver(GDALBase): """ Wrap a GDAL/OGR Data Source Driver. - For more information, see the C API documentation: - https://gdal.org/api/vector_c_api.html - https://gdal.org/api/raster_c_api.html + For more information, see the C API source code: + https://www.gdal.org/gdal_8h.html - https://www.gdal.org/ogr__api_8h.html """ # Case-insensitive aliases for some GDAL/OGR Drivers. # For a complete list of original driver names see - # https://gdal.org/drivers/vector/ - # https://gdal.org/drivers/raster/ + # https://www.gdal.org/ogr_formats.html (vector) + # https://www.gdal.org/formats_list.html (raster) _alias = { # vector - "esri": "ESRI Shapefile", - "shp": "ESRI Shapefile", - "shape": "ESRI Shapefile", - "tiger": "TIGER", - "tiger/line": "TIGER", + 'esri': 'ESRI Shapefile', + 'shp': 'ESRI Shapefile', + 'shape': 'ESRI Shapefile', + 'tiger': 'TIGER', + 'tiger/line': 'TIGER', # raster - "tiff": "GTiff", - "tif": "GTiff", - "jpeg": "JPEG", - "jpg": "JPEG", + 'tiff': 'GTiff', + 'tif': 'GTiff', + 'jpeg': 'JPEG', + 'jpg': 'JPEG', } def __init__(self, dr_input): @@ -62,15 +60,11 @@ class Driver(GDALBase): elif isinstance(dr_input, c_void_p): driver = dr_input else: - raise GDALException( - "Unrecognized input type for GDAL/OGR Driver: %s" % type(dr_input) - ) + raise GDALException('Unrecognized input type for GDAL/OGR Driver: %s' % type(dr_input)) # Making sure we get a valid pointer to the OGR Driver if not driver: - raise GDALException( - "Could not initialize GDAL/OGR Driver on input: %s" % dr_input - ) + raise GDALException('Could not initialize GDAL/OGR Driver on input: %s' % dr_input) self.ptr = driver def __str__(self): diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/envelope.py b/venv/Lib/site-packages/django/contrib/gis/gdal/envelope.py index 4c2c1e4..7921c97 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/envelope.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/envelope.py @@ -17,15 +17,14 @@ from django.contrib.gis.gdal.error import GDALException # The OGR definition of an Envelope is a C structure containing four doubles. # See the 'ogr_core.h' source file for more information: -# https://gdal.org/doxygen/ogr__core_8h_source.html +# https://www.gdal.org/ogr__core_8h_source.html class OGREnvelope(Structure): "Represent the OGREnvelope C Structure." - _fields_ = [ - ("MinX", c_double), - ("MaxX", c_double), - ("MinY", c_double), - ("MaxY", c_double), - ] + _fields_ = [("MinX", c_double), + ("MaxX", c_double), + ("MinY", c_double), + ("MaxY", c_double), + ] class Envelope: @@ -48,25 +47,23 @@ class Envelope: elif isinstance(args[0], (tuple, list)): # A tuple was passed in. if len(args[0]) != 4: - raise GDALException( - "Incorrect number of tuple elements (%d)." % len(args[0]) - ) + raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0])) else: self._from_sequence(args[0]) else: - raise TypeError("Incorrect type of argument: %s" % type(args[0])) + raise TypeError('Incorrect type of argument: %s' % type(args[0])) elif len(args) == 4: # Individual parameters passed in. # Thanks to ww for the help self._from_sequence([float(a) for a in args]) else: - raise GDALException("Incorrect number (%d) of arguments." % len(args)) + raise GDALException('Incorrect number (%d) of arguments.' % len(args)) # Checking the x,y coordinates if self.min_x > self.max_x: - raise GDALException("Envelope minimum X > maximum X.") + raise GDALException('Envelope minimum X > maximum X.') if self.min_y > self.max_y: - raise GDALException("Envelope minimum Y > maximum Y.") + raise GDALException('Envelope minimum Y > maximum Y.') def __eq__(self, other): """ @@ -74,21 +71,13 @@ class Envelope: other Envelopes and 4-tuples. """ if isinstance(other, Envelope): - return ( - (self.min_x == other.min_x) - and (self.min_y == other.min_y) - and (self.max_x == other.max_x) - and (self.max_y == other.max_y) - ) + return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ + (self.max_x == other.max_x) and (self.max_y == other.max_y) elif isinstance(other, tuple) and len(other) == 4: - return ( - (self.min_x == other[0]) - and (self.min_y == other[1]) - and (self.max_x == other[2]) - and (self.max_y == other[3]) - ) + return (self.min_x == other[0]) and (self.min_y == other[1]) and \ + (self.max_x == other[2]) and (self.max_y == other[3]) else: - raise GDALException("Equivalence testing only works with other Envelopes.") + raise GDALException('Equivalence testing only works with other Envelopes.') def __str__(self): "Return a string representation of the tuple." @@ -115,16 +104,12 @@ class Envelope: if len(args) == 1: if isinstance(args[0], Envelope): return self.expand_to_include(args[0].tuple) - elif hasattr(args[0], "x") and hasattr(args[0], "y"): - return self.expand_to_include( - args[0].x, args[0].y, args[0].x, args[0].y - ) + elif hasattr(args[0], 'x') and hasattr(args[0], 'y'): + return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y) elif isinstance(args[0], (tuple, list)): # A tuple was passed in. if len(args[0]) == 2: - return self.expand_to_include( - (args[0][0], args[0][1], args[0][0], args[0][1]) - ) + return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1])) elif len(args[0]) == 4: (minx, miny, maxx, maxy) = args[0] if minx < self._envelope.MinX: @@ -136,11 +121,9 @@ class Envelope: if maxy > self._envelope.MaxY: self._envelope.MaxY = maxy else: - raise GDALException( - "Incorrect number of tuple elements (%d)." % len(args[0]) - ) + raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0])) else: - raise TypeError("Incorrect type of argument: %s" % type(args[0])) + raise TypeError('Incorrect type of argument: %s' % type(args[0])) elif len(args) == 2: # An x and an y parameter were passed in return self.expand_to_include((args[0], args[1], args[0], args[1])) @@ -148,7 +131,7 @@ class Envelope: # Individual parameters passed in. return self.expand_to_include(args) else: - raise GDALException("Incorrect number (%d) of arguments." % len(args[0])) + raise GDALException('Incorrect number (%d) of arguments.' % len(args[0])) @property def min_x(self): @@ -189,15 +172,7 @@ class Envelope: def wkt(self): "Return WKT representing a Polygon for this envelope." # TODO: Fix significant figures. - return "POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))" % ( - self.min_x, - self.min_y, - self.min_x, - self.max_y, - self.max_x, - self.max_y, - self.max_x, - self.min_y, - self.min_x, - self.min_y, - ) + return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \ + (self.min_x, self.min_y, self.min_x, self.max_y, + self.max_x, self.max_y, self.max_x, self.min_y, + self.min_x, self.min_y) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/error.py b/venv/Lib/site-packages/django/contrib/gis/gdal/error.py index df19c2e..71b862e 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/error.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/error.py @@ -18,29 +18,29 @@ class SRSException(Exception): # OGR Error Codes OGRERR_DICT = { - 1: (GDALException, "Not enough data."), - 2: (GDALException, "Not enough memory."), - 3: (GDALException, "Unsupported geometry type."), - 4: (GDALException, "Unsupported operation."), - 5: (GDALException, "Corrupt data."), - 6: (GDALException, "OGR failure."), - 7: (SRSException, "Unsupported SRS."), - 8: (GDALException, "Invalid handle."), + 1: (GDALException, 'Not enough data.'), + 2: (GDALException, 'Not enough memory.'), + 3: (GDALException, 'Unsupported geometry type.'), + 4: (GDALException, 'Unsupported operation.'), + 5: (GDALException, 'Corrupt data.'), + 6: (GDALException, 'OGR failure.'), + 7: (SRSException, 'Unsupported SRS.'), + 8: (GDALException, 'Invalid handle.'), } # CPL Error Codes -# https://gdal.org/api/cpl.html#cpl-error-h +# https://www.gdal.org/cpl__error_8h.html CPLERR_DICT = { - 1: (GDALException, "AppDefined"), - 2: (GDALException, "OutOfMemory"), - 3: (GDALException, "FileIO"), - 4: (GDALException, "OpenFailed"), - 5: (GDALException, "IllegalArg"), - 6: (GDALException, "NotSupported"), - 7: (GDALException, "AssertionFailed"), - 8: (GDALException, "NoWriteAccess"), - 9: (GDALException, "UserInterrupt"), - 10: (GDALException, "ObjectNull"), + 1: (GDALException, 'AppDefined'), + 2: (GDALException, 'OutOfMemory'), + 3: (GDALException, 'FileIO'), + 4: (GDALException, 'OpenFailed'), + 5: (GDALException, 'IllegalArg'), + 6: (GDALException, 'NotSupported'), + 7: (GDALException, 'AssertionFailed'), + 8: (GDALException, 'NoWriteAccess'), + 9: (GDALException, 'UserInterrupt'), + 10: (GDALException, 'ObjectNull'), } ERR_NONE = 0 diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/feature.py b/venv/Lib/site-packages/django/contrib/gis/gdal/feature.py index 6f08969..9f8063d 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/feature.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/feature.py @@ -2,13 +2,12 @@ from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.field import Field from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType -from django.contrib.gis.gdal.prototypes import ds as capi -from django.contrib.gis.gdal.prototypes import geom as geom_api +from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api from django.utils.encoding import force_bytes, force_str # For more information, see the OGR C API source code: -# https://gdal.org/api/vector_c_api.html +# https://www.gdal.org/ogr__api_8h.html # # The OGR_F_* routines are relevant here. class Feature(GDALBase): @@ -16,7 +15,6 @@ class Feature(GDALBase): This class that wraps an OGR Feature, needs to be instantiated from a Layer object. """ - destructor = capi.destroy_feature def __init__(self, feat, layer): @@ -24,7 +22,7 @@ class Feature(GDALBase): Initialize Feature from a pointer and its Layer object. """ if not feat: - raise GDALException("Cannot create OGR Feature, invalid pointer given.") + raise GDALException('Cannot create OGR Feature, invalid pointer given.') self.ptr = feat self._layer = layer @@ -40,9 +38,7 @@ class Feature(GDALBase): elif 0 <= index < self.num_fields: i = index else: - raise IndexError( - "Index out of range when accessing field in a feature: %s." % index - ) + raise IndexError('Index out of range when accessing field in a feature: %s.' % index) return Field(self, i) def __len__(self): @@ -51,7 +47,7 @@ class Feature(GDALBase): def __str__(self): "The string name of the feature." - return "Feature FID %d in Layer<%s>" % (self.fid, self.layer_name) + return 'Feature FID %d in Layer<%s>' % (self.fid, self.layer_name) def __eq__(self, other): "Do equivalence testing on the features." @@ -85,9 +81,8 @@ class Feature(GDALBase): force_str( capi.get_field_name(capi.get_field_defn(self._layer._ldefn, i)), self.encoding, - strings_only=True, - ) - for i in range(self.num_fields) + strings_only=True + ) for i in range(self.num_fields) ] @property @@ -109,12 +104,12 @@ class Feature(GDALBase): object. May take a string of the field name or a Field object as parameters. """ - field_name = getattr(field, "name", field) + field_name = getattr(field, 'name', field) return self[field_name].value def index(self, field_name): "Return the index of the given field name." i = capi.get_field_index(self.ptr, force_bytes(field_name)) if i < 0: - raise IndexError("Invalid OFT field name given: %s." % field_name) + raise IndexError('Invalid OFT field name given: %s.' % field_name) return i diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/field.py b/venv/Lib/site-packages/django/contrib/gis/gdal/field.py index b9964ab..dfd25d7 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/field.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/field.py @@ -8,7 +8,7 @@ from django.utils.encoding import force_str # For more information, see the OGR C API source code: -# https://gdal.org/api/vector_c_api.html +# https://www.gdal.org/ogr__api_8h.html # # The OGR_Fld_* routines are relevant here. class Field(GDALBase): @@ -28,7 +28,7 @@ class Field(GDALBase): # Getting the pointer for this field. fld_ptr = capi.get_feat_field_defn(feat.ptr, index) if not fld_ptr: - raise GDALException("Cannot create OGR Field, invalid pointer given.") + raise GDALException('Cannot create OGR Field, invalid pointer given.') self.ptr = fld_ptr # Setting the class depending upon the OGR Field Type (OFT) @@ -41,26 +41,14 @@ class Field(GDALBase): # #### Field Methods #### def as_double(self): "Retrieve the Field's value as a double (float)." - return ( - capi.get_field_as_double(self._feat.ptr, self._index) - if self.is_set - else None - ) + return capi.get_field_as_double(self._feat.ptr, self._index) if self.is_set else None def as_int(self, is_64=False): "Retrieve the Field's value as an integer." if is_64: - return ( - capi.get_field_as_integer64(self._feat.ptr, self._index) - if self.is_set - else None - ) + return capi.get_field_as_integer64(self._feat.ptr, self._index) if self.is_set else None else: - return ( - capi.get_field_as_integer(self._feat.ptr, self._index) - if self.is_set - else None - ) + return capi.get_field_as_integer(self._feat.ptr, self._index) if self.is_set else None def as_string(self): "Retrieve the Field's value as a string." @@ -75,22 +63,12 @@ class Field(GDALBase): return None yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)] status = capi.get_field_as_datetime( - self._feat.ptr, - self._index, - byref(yy), - byref(mm), - byref(dd), - byref(hh), - byref(mn), - byref(ss), - byref(tz), - ) + self._feat.ptr, self._index, byref(yy), byref(mm), byref(dd), + byref(hh), byref(mn), byref(ss), byref(tz)) if status: return (yy, mm, dd, hh, mn, ss, tz) else: - raise GDALException( - "Unable to retrieve date & time information from the field." - ) + raise GDALException('Unable to retrieve date & time information from the field.') # #### Field Properties #### @property diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/geometries.py b/venv/Lib/site-packages/django/contrib/gis/gdal/geometries.py index f74dc68..0aa1161 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/geometries.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/geometries.py @@ -1,7 +1,7 @@ """ The OGRGeometry is a wrapper for using the OGR Geometry class - (see https://gdal.org/api/ogrgeometry_cpp.html#_CPPv411OGRGeometry). - OGRGeometry may be instantiated when reading geometries from OGR Data Sources + (see https://www.gdal.org/classOGRGeometry.html). OGRGeometry + may be instantiated when reading geometries from OGR Data Sources (e.g. SHP files), or when given OGC WKT (a string). While the 'full' API is not present yet, the API is "pythonic" unlike @@ -28,7 +28,7 @@ >>> print(mpnt.proj) +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs >>> print(mpnt) - MULTIPOINT (-89.99993037860248 29.99979788655764,-89.99993037860248 29.99979788655764) + MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641) The OGRGeomType class is to make it easy to specify an OGR geometry type: >>> from django.contrib.gis.gdal import OGRGeomType @@ -46,20 +46,18 @@ from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope from django.contrib.gis.gdal.error import GDALException, SRSException from django.contrib.gis.gdal.geomtype import OGRGeomType -from django.contrib.gis.gdal.prototypes import geom as capi -from django.contrib.gis.gdal.prototypes import srs as srs_api +from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api from django.contrib.gis.gdal.srs import CoordTransform, SpatialReference from django.contrib.gis.geometry import hex_regex, json_regex, wkt_regex from django.utils.encoding import force_bytes # For more information, see the OGR C API source code: -# https://gdal.org/api/vector_c_api.html +# https://www.gdal.org/ogr__api_8h.html # # The OGR_G_* routines are relevant here. class OGRGeometry(GDALBase): """Encapsulate an OGR geometry.""" - destructor = capi.destroy_geom def __init__(self, geom_input, srs=None): @@ -76,18 +74,16 @@ class OGRGeometry(GDALBase): wkt_m = wkt_regex.match(geom_input) json_m = json_regex.match(geom_input) if wkt_m: - if wkt_m["srid"]: + if wkt_m['srid']: # If there's EWKT, set the SRS w/value of the SRID. - srs = int(wkt_m["srid"]) - if wkt_m["type"].upper() == "LINEARRING": + srs = int(wkt_m['srid']) + if wkt_m['type'].upper() == 'LINEARRING': # OGR_G_CreateFromWkt doesn't work with LINEARRING WKT. # See https://trac.osgeo.org/gdal/ticket/1992. - g = capi.create_geom(OGRGeomType(wkt_m["type"]).num) - capi.import_wkt(g, byref(c_char_p(wkt_m["wkt"].encode()))) + g = capi.create_geom(OGRGeomType(wkt_m['type']).num) + capi.import_wkt(g, byref(c_char_p(wkt_m['wkt'].encode()))) else: - g = capi.from_wkt( - byref(c_char_p(wkt_m["wkt"].encode())), None, byref(c_void_p()) - ) + g = capi.from_wkt(byref(c_char_p(wkt_m['wkt'].encode())), None, byref(c_void_p())) elif json_m: g = self._from_json(geom_input.encode()) else: @@ -105,17 +101,12 @@ class OGRGeometry(GDALBase): # OGR pointer (c_void_p) was the input. g = geom_input else: - raise GDALException( - "Invalid input type for OGR Geometry construction: %s" - % type(geom_input) - ) + raise GDALException('Invalid input type for OGR Geometry construction: %s' % type(geom_input)) # Now checking the Geometry pointer before finishing initialization # by setting the pointer for the object. if not g: - raise GDALException( - "Cannot create OGR Geometry from input: %s" % geom_input - ) + raise GDALException('Cannot create OGR Geometry from input: %s' % geom_input) self.ptr = g # Assigning the SpatialReference object to the geometry, if valid. @@ -138,15 +129,13 @@ class OGRGeometry(GDALBase): wkb, srs = state ptr = capi.from_wkb(wkb, None, byref(c_void_p()), len(wkb)) if not ptr: - raise GDALException("Invalid OGRGeometry loaded from pickled state.") + raise GDALException('Invalid OGRGeometry loaded from pickled state.') self.ptr = ptr self.srs = srs @classmethod def _from_wkb(cls, geom_input): - return capi.from_wkb( - bytes(geom_input), None, byref(c_void_p()), len(geom_input) - ) + return capi.from_wkb(bytes(geom_input), None, byref(c_void_p()), len(geom_input)) @staticmethod def _from_json(geom_input): @@ -156,10 +145,8 @@ class OGRGeometry(GDALBase): def from_bbox(cls, bbox): "Construct a Polygon from a bounding box (4-tuple)." x0, y0, x1, y1 = bbox - return OGRGeometry( - "POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))" - % (x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) - ) + return OGRGeometry('POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % ( + x0, y0, x0, y1, x1, y1, x1, y0, x0, y0)) @staticmethod def from_json(geom_input): @@ -211,7 +198,7 @@ class OGRGeometry(GDALBase): def _set_coord_dim(self, dim): "Set the coordinate dimension of this Geometry." if dim not in (2, 3): - raise ValueError("Geometry dimension must be either 2 or 3") + raise ValueError('Geometry dimension must be either 2 or 3') capi.set_coord_dim(self.ptr, dim) coord_dim = property(_get_coord_dim, _set_coord_dim) @@ -291,9 +278,7 @@ class OGRGeometry(GDALBase): elif srs is None: srs_ptr = None else: - raise TypeError( - "Cannot assign spatial reference with object of type: %s" % type(srs) - ) + raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs)) capi.assign_srs(self.ptr, srs_ptr) srs = property(_get_srs, _set_srs) @@ -309,21 +294,19 @@ class OGRGeometry(GDALBase): if isinstance(srid, int) or srid is None: self.srs = srid else: - raise TypeError("SRID must be set with an integer.") + raise TypeError('SRID must be set with an integer.') srid = property(_get_srid, _set_srid) # #### Output Methods #### def _geos_ptr(self): from django.contrib.gis.geos import GEOSGeometry - return GEOSGeometry._from_wkb(self.wkb) @property def geos(self): "Return a GEOSGeometry object from this OGRGeometry." from django.contrib.gis.geos import GEOSGeometry - return GEOSGeometry(self._geos_ptr(), self.srid) @property @@ -342,7 +325,6 @@ class OGRGeometry(GDALBase): Return the GeoJSON representation of this Geometry. """ return capi.to_json(self.ptr) - geojson = json @property @@ -358,7 +340,7 @@ class OGRGeometry(GDALBase): @property def wkb(self): "Return the WKB representation of the Geometry." - if sys.byteorder == "little": + if sys.byteorder == 'little': byteorder = 1 # wkbNDR (from ogr_core.h) else: byteorder = 0 # wkbXDR @@ -379,7 +361,7 @@ class OGRGeometry(GDALBase): "Return the EWKT representation of the Geometry." srs = self.srs if srs and srs.srid: - return "SRID=%s;%s" % (srs.srid, self.wkt) + return 'SRID=%s;%s' % (srs.srid, self.wkt) else: return self.wkt @@ -420,19 +402,15 @@ class OGRGeometry(GDALBase): sr = SpatialReference(coord_trans) capi.geom_transform_to(self.ptr, sr.ptr) else: - raise TypeError( - "Transform only accepts CoordTransform, " - "SpatialReference, string, and integer objects." - ) + raise TypeError('Transform only accepts CoordTransform, ' + 'SpatialReference, string, and integer objects.') # #### Topology Methods #### def _topology(self, func, other): """A generalized function for topology operations, takes a GDAL function and the other geometry to perform the operation on.""" if not isinstance(other, OGRGeometry): - raise TypeError( - "Must use another OGRGeometry object for topology operations!" - ) + raise TypeError('Must use another OGRGeometry object for topology operations!') # Returning the output of the given function with the other geometry's # pointer. @@ -522,14 +500,14 @@ class OGRGeometry(GDALBase): # The subclasses for OGR Geometry. class Point(OGRGeometry): + def _geos_ptr(self): from django.contrib.gis import geos - return geos.Point._create_empty() if self.empty else super()._geos_ptr() @classmethod def _create_empty(cls): - return capi.create_geom(OGRGeomType("point").num) + return capi.create_geom(OGRGeomType('point').num) @property def x(self): @@ -554,11 +532,11 @@ class Point(OGRGeometry): return (self.x, self.y) elif self.coord_dim == 3: return (self.x, self.y, self.z) - coords = tuple class LineString(OGRGeometry): + def __getitem__(self, index): "Return the Point at the given index." if 0 <= index < self.point_count: @@ -572,9 +550,7 @@ class LineString(OGRGeometry): elif dim == 3: return (x.value, y.value, z.value) else: - raise IndexError( - "Index out of range when accessing points of a line string: %s." % index - ) + raise IndexError('Index out of range when accessing points of a line string: %s.' % index) def __len__(self): "Return the number of points in the LineString." @@ -584,7 +560,6 @@ class LineString(OGRGeometry): def tuple(self): "Return the tuple representation of this LineString." return tuple(self[i] for i in range(len(self))) - coords = tuple def _listarr(self, func): @@ -617,6 +592,7 @@ class LinearRing(LineString): class Polygon(OGRGeometry): + def __len__(self): "Return the number of interior rings in this Polygon." return self.geom_count @@ -624,27 +600,21 @@ class Polygon(OGRGeometry): def __getitem__(self, index): "Get the ring at the specified index." if 0 <= index < self.geom_count: - return OGRGeometry( - capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs - ) + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) else: - raise IndexError( - "Index out of range when accessing rings of a polygon: %s." % index - ) + raise IndexError('Index out of range when accessing rings of a polygon: %s.' % index) # Polygon Properties @property def shell(self): "Return the shell of this Polygon." return self[0] # First ring is the shell - exterior_ring = shell @property def tuple(self): "Return a tuple of LinearRing coordinate tuples." return tuple(self[i].tuple for i in range(self.geom_count)) - coords = tuple @property @@ -657,7 +627,7 @@ class Polygon(OGRGeometry): def centroid(self): "Return the centroid (a Point) of this Polygon." # The centroid is a Point, create a geometry for this. - p = OGRGeometry(OGRGeomType("Point")) + p = OGRGeometry(OGRGeomType('Point')) capi.get_centroid(self.ptr, p.ptr) return p @@ -669,14 +639,9 @@ class GeometryCollection(OGRGeometry): def __getitem__(self, index): "Get the Geometry at the specified index." if 0 <= index < self.geom_count: - return OGRGeometry( - capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs - ) + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) else: - raise IndexError( - "Index out of range when accessing geometry in a collection: %s." - % index - ) + raise IndexError('Index out of range when accessing geometry in a collection: %s.' % index) def __len__(self): "Return the number of geometries in this Geometry Collection." @@ -694,7 +659,7 @@ class GeometryCollection(OGRGeometry): tmp = OGRGeometry(geom) capi.add_geom(self.ptr, tmp.ptr) else: - raise GDALException("Must add an OGRGeometry.") + raise GDALException('Must add an OGRGeometry.') @property def point_count(self): @@ -706,7 +671,6 @@ class GeometryCollection(OGRGeometry): def tuple(self): "Return a tuple representation of this Geometry Collection." return tuple(self[i].tuple for i in range(self.geom_count)) - coords = tuple diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/geomtype.py b/venv/Lib/site-packages/django/contrib/gis/gdal/geomtype.py index 8b77caf..591c680 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/geomtype.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/geomtype.py @@ -8,24 +8,24 @@ class OGRGeomType: # Dictionary of acceptable OGRwkbGeometryType s and their string names. _types = { - 0: "Unknown", - 1: "Point", - 2: "LineString", - 3: "Polygon", - 4: "MultiPoint", - 5: "MultiLineString", - 6: "MultiPolygon", - 7: "GeometryCollection", - 100: "None", - 101: "LinearRing", - 102: "PointZ", - 1 + wkb25bit: "Point25D", - 2 + wkb25bit: "LineString25D", - 3 + wkb25bit: "Polygon25D", - 4 + wkb25bit: "MultiPoint25D", - 5 + wkb25bit: "MultiLineString25D", - 6 + wkb25bit: "MultiPolygon25D", - 7 + wkb25bit: "GeometryCollection25D", + 0: 'Unknown', + 1: 'Point', + 2: 'LineString', + 3: 'Polygon', + 4: 'MultiPoint', + 5: 'MultiLineString', + 6: 'MultiPolygon', + 7: 'GeometryCollection', + 100: 'None', + 101: 'LinearRing', + 102: 'PointZ', + 1 + wkb25bit: 'Point25D', + 2 + wkb25bit: 'LineString25D', + 3 + wkb25bit: 'Polygon25D', + 4 + wkb25bit: 'MultiPoint25D', + 5 + wkb25bit: 'MultiLineString25D', + 6 + wkb25bit: 'MultiPolygon25D', + 7 + wkb25bit: 'GeometryCollection25D', } # Reverse type dictionary, keyed by lowercase of the name. _str_types = {v.lower(): k for k, v in _types.items()} @@ -36,17 +36,17 @@ class OGRGeomType: num = type_input.num elif isinstance(type_input, str): type_input = type_input.lower() - if type_input == "geometry": - type_input = "unknown" + if type_input == 'geometry': + type_input = 'unknown' num = self._str_types.get(type_input) if num is None: raise GDALException('Invalid OGR String Type "%s"' % type_input) elif isinstance(type_input, int): if type_input not in self._types: - raise GDALException("Invalid OGR Integer Type: %d" % type_input) + raise GDALException('Invalid OGR Integer Type: %d' % type_input) num = type_input else: - raise TypeError("Invalid OGR input type given.") + raise TypeError('Invalid OGR input type given.') # Setting the OGR geometry type number. self.num = num @@ -77,19 +77,19 @@ class OGRGeomType: @property def django(self): "Return the Django GeometryField for this OGR Type." - s = self.name.replace("25D", "") - if s in ("LinearRing", "None"): + s = self.name.replace('25D', '') + if s in ('LinearRing', 'None'): return None - elif s == "Unknown": - s = "Geometry" - elif s == "PointZ": - s = "Point" - return s + "Field" + elif s == 'Unknown': + s = 'Geometry' + elif s == 'PointZ': + s = 'Point' + return s + 'Field' def to_multi(self): """ Transform Point, LineString, Polygon, and their 25D equivalents to their Multi... counterpart. """ - if self.name.startswith(("Point", "LineString", "Polygon")): + if self.name.startswith(('Point', 'LineString', 'Polygon')): self.num += 3 diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/layer.py b/venv/Lib/site-packages/django/contrib/gis/gdal/layer.py index 1afa046..4f7a2b3 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/layer.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/layer.py @@ -7,22 +7,19 @@ from django.contrib.gis.gdal.feature import Feature from django.contrib.gis.gdal.field import OGRFieldTypes from django.contrib.gis.gdal.geometries import OGRGeometry from django.contrib.gis.gdal.geomtype import OGRGeomType -from django.contrib.gis.gdal.prototypes import ds as capi -from django.contrib.gis.gdal.prototypes import geom as geom_api -from django.contrib.gis.gdal.prototypes import srs as srs_api +from django.contrib.gis.gdal.prototypes import ( + ds as capi, geom as geom_api, srs as srs_api, +) from django.contrib.gis.gdal.srs import SpatialReference from django.utils.encoding import force_bytes, force_str # For more information, see the OGR C API source code: -# https://gdal.org/api/vector_c_api.html +# https://www.gdal.org/ogr__api_8h.html # # The OGR_L_* routines are relevant here. class Layer(GDALBase): - """ - A class that wraps an OGR Layer, needs to be instantiated from a DataSource - object. - """ + "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." def __init__(self, layer_ptr, ds): """ @@ -32,12 +29,12 @@ class Layer(GDALBase): collection of the `DataSource` while this Layer is still active. """ if not layer_ptr: - raise GDALException("Cannot create Layer, invalid pointer given") + raise GDALException('Cannot create Layer, invalid pointer given') self.ptr = layer_ptr self._ds = ds self._ldefn = capi.get_layer_defn(self._ptr) # Does the Layer support random reading? - self._random_read = self.test_capability(b"RandomRead") + self._random_read = self.test_capability(b'RandomRead') def __getitem__(self, index): "Get the Feature at the specified index." @@ -46,16 +43,14 @@ class Layer(GDALBase): # number of features because the beginning and ending feature IDs # are not guaranteed to be 0 and len(layer)-1, respectively. if index < 0: - raise IndexError("Negative indices are not allowed on OGR Layers.") + raise IndexError('Negative indices are not allowed on OGR Layers.') return self._make_feature(index) elif isinstance(index, slice): # A slice was given start, stop, stride = index.indices(self.num_feat) return [self._make_feature(fid) for fid in range(start, stop, stride)] else: - raise TypeError( - "Integers and slices may only be used when indexing OGR Layers." - ) + raise TypeError('Integers and slices may only be used when indexing OGR Layers.') def __iter__(self): "Iterate over each Feature in the Layer." @@ -92,7 +87,7 @@ class Layer(GDALBase): if feat.fid == feat_id: return feat # Should have returned a Feature, raise an IndexError. - raise IndexError("Invalid feature id: %s." % feat_id) + raise IndexError('Invalid feature id: %s.' % feat_id) # #### Layer properties #### @property @@ -138,14 +133,10 @@ class Layer(GDALBase): Return a list of string names corresponding to each of the Fields available in this Layer. """ - return [ - force_str( - capi.get_field_name(capi.get_field_defn(self._ldefn, i)), - self._ds.encoding, - strings_only=True, - ) - for i in range(self.num_fields) - ] + return [force_str( + capi.get_field_name(capi.get_field_defn(self._ldefn, i)), + self._ds.encoding, strings_only=True, + ) for i in range(self.num_fields)] @property def field_types(self): @@ -154,26 +145,20 @@ class Layer(GDALBase): return the list [OFTInteger, OFTReal, OFTString] for an OGR layer that has an integer, a floating-point, and string fields. """ - return [ - OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))] - for i in range(self.num_fields) - ] + return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))] + for i in range(self.num_fields)] @property def field_widths(self): "Return a list of the maximum field widths for the features." - return [ - capi.get_field_width(capi.get_field_defn(self._ldefn, i)) - for i in range(self.num_fields) - ] + return [capi.get_field_width(capi.get_field_defn(self._ldefn, i)) + for i in range(self.num_fields)] @property def field_precisions(self): "Return the field precisions for the features." - return [ - capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) - for i in range(self.num_fields) - ] + return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) + for i in range(self.num_fields)] def _get_spatial_filter(self): try: @@ -186,7 +171,7 @@ class Layer(GDALBase): capi.set_spatial_filter(self.ptr, filter.ptr) elif isinstance(filter, (tuple, list)): if not len(filter) == 4: - raise ValueError("Spatial filter list/tuple must have 4 elements.") + raise ValueError('Spatial filter list/tuple must have 4 elements.') # Map c_double onto params -- if a bad type is passed in it # will be caught here. xmin, ymin, xmax, ymax = map(c_double, filter) @@ -194,10 +179,7 @@ class Layer(GDALBase): elif filter is None: capi.set_spatial_filter(self.ptr, None) else: - raise TypeError( - "Spatial filter must be either an OGRGeometry instance, a 4-tuple, or " - "None." - ) + raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.') spatial_filter = property(_get_spatial_filter, _set_spatial_filter) @@ -208,7 +190,7 @@ class Layer(GDALBase): in the Layer. """ if field_name not in self.fields: - raise GDALException("invalid field name: %s" % field_name) + raise GDALException('invalid field name: %s' % field_name) return [feat.get(field_name) for feat in self] def get_geoms(self, geos=False): @@ -218,7 +200,6 @@ class Layer(GDALBase): """ if geos: from django.contrib.gis.geos import GEOSGeometry - return [GEOSGeometry(feat.geom.wkb) for feat in self] else: return [feat.geom for feat in self] diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/libgdal.py b/venv/Lib/site-packages/django/contrib/gis/gdal/libgdal.py index 6a7b786..2580791 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/libgdal.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/libgdal.py @@ -7,45 +7,29 @@ from ctypes.util import find_library from django.contrib.gis.gdal.error import GDALException from django.core.exceptions import ImproperlyConfigured -logger = logging.getLogger("django.contrib.gis") +logger = logging.getLogger('django.contrib.gis') # Custom library path set? try: from django.conf import settings - lib_path = settings.GDAL_LIBRARY_PATH except (AttributeError, ImportError, ImproperlyConfigured, OSError): lib_path = None if lib_path: lib_names = None -elif os.name == "nt": +elif os.name == 'nt': # Windows NT shared libraries lib_names = [ - "gdal303", - "gdal302", - "gdal301", - "gdal300", - "gdal204", - "gdal203", - "gdal202", - "gdal201", - "gdal20", + 'gdal302', 'gdal301', 'gdal300', + 'gdal204', 'gdal203', 'gdal202', 'gdal201', 'gdal20', ] -elif os.name == "posix": +elif os.name == 'posix': # *NIX library names. lib_names = [ - "gdal", - "GDAL", - "gdal3.3.0", - "gdal3.2.0", - "gdal3.1.0", - "gdal3.0.0", - "gdal2.4.0", - "gdal2.3.0", - "gdal2.2.0", - "gdal2.1.0", - "gdal2.0.0", + 'gdal', 'GDAL', + 'gdal3.2.0', 'gdal3.1.0', 'gdal3.0.0', + 'gdal2.4.0', 'gdal2.3.0', 'gdal2.2.0', 'gdal2.1.0', 'gdal2.0.0', ] else: raise ImproperlyConfigured('GDAL is unsupported on OS "%s".' % os.name) @@ -61,7 +45,7 @@ if lib_names: if lib_path is None: raise ImproperlyConfigured( 'Could not find the GDAL library (tried "%s"). Is GDAL installed? ' - "If it is, try setting GDAL_LIBRARY_PATH in your settings." + 'If it is, try setting GDAL_LIBRARY_PATH in your settings.' % '", "'.join(lib_names) ) @@ -72,9 +56,8 @@ lgdal = CDLL(lib_path) # STDCALL, while others are not. Thus, the library will also need to # be loaded up as WinDLL for said OSR functions that require the # different calling convention. -if os.name == "nt": +if os.name == 'nt': from ctypes import WinDLL - lwingdal = WinDLL(lib_path) @@ -83,7 +66,7 @@ def std_call(func): Return the correct STDCALL function for certain OSR routines on Win32 platforms. """ - if os.name == "nt": + if os.name == 'nt': return lwingdal[func] else: return lgdal[func] @@ -92,24 +75,24 @@ def std_call(func): # #### Version-information functions. #### # Return GDAL library version information with the given key. -_version_info = std_call("GDALVersionInfo") +_version_info = std_call('GDALVersionInfo') _version_info.argtypes = [c_char_p] _version_info.restype = c_char_p def gdal_version(): "Return only the GDAL version number information." - return _version_info(b"RELEASE_NAME") + return _version_info(b'RELEASE_NAME') def gdal_full_version(): "Return the full GDAL version information." - return _version_info(b"") + return _version_info(b'') def gdal_version_info(): ver = gdal_version() - m = re.match(rb"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<subminor>\d+))?", ver) + m = re.match(br'^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<subminor>\d+))?', ver) if not m: raise GDALException('Could not parse GDAL version string "%s"' % ver) major, minor, subminor = m.groups() @@ -123,7 +106,7 @@ CPLErrorHandler = CFUNCTYPE(None, c_int, c_int, c_char_p) def err_handler(error_class, error_number, message): - logger.error("GDAL_ERROR %d: %s", error_number, message) + logger.error('GDAL_ERROR %d: %s', error_number, message) err_handler = CPLErrorHandler(err_handler) @@ -136,5 +119,5 @@ def function(name, args, restype): return func -set_error_handler = function("CPLSetErrorHandler", [CPLErrorHandler], CPLErrorHandler) +set_error_handler = function('CPLSetErrorHandler', [CPLErrorHandler], CPLErrorHandler) set_error_handler(err_handler) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/ds.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/ds.py index 13e032c..907589c 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/ds.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/ds.py @@ -8,15 +8,8 @@ from ctypes import POINTER, c_char_p, c_double, c_int, c_long, c_void_p from django.contrib.gis.gdal.envelope import OGREnvelope from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal from django.contrib.gis.gdal.prototypes.generation import ( - bool_output, - const_string_output, - double_output, - geom_output, - int64_output, - int_output, - srs_output, - void_output, - voidptr_output, + bool_output, const_string_output, double_output, geom_output, int64_output, + int_output, srs_output, void_output, voidptr_output, ) c_int_p = POINTER(c_int) # shortcut type @@ -25,13 +18,9 @@ c_int_p = POINTER(c_int) # shortcut type register_all = void_output(lgdal.OGRRegisterAll, [], errcheck=False) cleanup_all = void_output(lgdal.OGRCleanupAll, [], errcheck=False) get_driver = voidptr_output(lgdal.OGRGetDriver, [c_int]) -get_driver_by_name = voidptr_output( - lgdal.OGRGetDriverByName, [c_char_p], errcheck=False -) +get_driver_by_name = voidptr_output(lgdal.OGRGetDriverByName, [c_char_p], errcheck=False) get_driver_count = int_output(lgdal.OGRGetDriverCount, []) -get_driver_name = const_string_output( - lgdal.OGR_Dr_GetName, [c_void_p], decoding="ascii" -) +get_driver_name = const_string_output(lgdal.OGR_Dr_GetName, [c_void_p], decoding='ascii') # DataSource open_ds = voidptr_output(lgdal.OGROpen, [c_char_p, c_int, POINTER(c_void_p)]) @@ -52,13 +41,10 @@ get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p]) reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False) test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p]) get_spatial_filter = geom_output(lgdal.OGR_L_GetSpatialFilter, [c_void_p]) -set_spatial_filter = void_output( - lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False -) +set_spatial_filter = void_output(lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False) set_spatial_filter_rect = void_output( lgdal.OGR_L_SetSpatialFilterRect, - [c_void_p, c_double, c_double, c_double, c_double], - errcheck=False, + [c_void_p, c_double, c_double, c_double, c_double], errcheck=False ) # Feature Definition Routines @@ -78,20 +64,16 @@ get_feat_field_defn = voidptr_output(lgdal.OGR_F_GetFieldDefnRef, [c_void_p, c_i get_fid = int_output(lgdal.OGR_F_GetFID, [c_void_p]) get_field_as_datetime = int_output( lgdal.OGR_F_GetFieldAsDateTime, - [c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p], + [c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p] ) get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int]) get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int]) -get_field_as_integer64 = int64_output( - lgdal.OGR_F_GetFieldAsInteger64, [c_void_p, c_int] -) +get_field_as_integer64 = int64_output(lgdal.OGR_F_GetFieldAsInteger64, [c_void_p, c_int]) if GDAL_VERSION >= (2, 2): is_field_set = bool_output(lgdal.OGR_F_IsFieldSetAndNotNull, [c_void_p, c_int]) else: is_field_set = bool_output(lgdal.OGR_F_IsFieldSet, [c_void_p, c_int]) -get_field_as_string = const_string_output( - lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int] -) +get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int]) get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p]) # Field Routines diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py index 52bb7cb..405f2e7 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py @@ -4,7 +4,9 @@ """ from ctypes import c_void_p, string_at -from django.contrib.gis.gdal.error import GDALException, SRSException, check_err +from django.contrib.gis.gdal.error import ( + GDALException, SRSException, check_err, +) from django.contrib.gis.gdal.libgdal import lgdal @@ -61,7 +63,6 @@ def check_string(result, func, cargs, offset=-1, str_result=False): lgdal.VSIFree(ptr) return s - # ### DataSource, Layer error-checking ### @@ -79,9 +80,7 @@ def check_geom(result, func, cargs): if isinstance(result, int): result = c_void_p(result) if not result: - raise GDALException( - 'Invalid geometry pointer returned from "%s".' % func.__name__ - ) + raise GDALException('Invalid geometry pointer returned from "%s".' % func.__name__) return result @@ -97,9 +96,7 @@ def check_srs(result, func, cargs): if isinstance(result, int): result = c_void_p(result) if not result: - raise SRSException( - 'Invalid spatial reference pointer returned from "%s".' % func.__name__ - ) + raise SRSException('Invalid spatial reference pointer returned from "%s".' % func.__name__) return result diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/generation.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/generation.py index 230e56f..0b26585 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/generation.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/generation.py @@ -2,19 +2,14 @@ This module contains functions that generate ctypes prototypes for the GDAL routines. """ -from ctypes import POINTER, c_bool, c_char_p, c_double, c_int, c_int64, c_void_p +from ctypes import ( + POINTER, c_bool, c_char_p, c_double, c_int, c_int64, c_void_p, +) from functools import partial from django.contrib.gis.gdal.prototypes.errcheck import ( - check_arg_errcode, - check_const_string, - check_errcode, - check_geom, - check_geom_offset, - check_pointer, - check_srs, - check_str_arg, - check_string, + check_arg_errcode, check_const_string, check_errcode, check_geom, + check_geom_offset, check_pointer, check_srs, check_str_arg, check_string, ) @@ -60,7 +55,6 @@ def geom_output(func, argtypes, offset=None): def geomerrcheck(result, func, cargs): return check_geom_offset(result, func, cargs, offset) - func.errcheck = geomerrcheck return func @@ -106,7 +100,6 @@ def const_string_output(func, argtypes, offset=None, decoding=None, cpl=False): if res and decoding: res = res.decode(decoding) return res - func.errcheck = _check_const return func @@ -136,7 +129,6 @@ def string_output(func, argtypes, offset=-1, str_result=False, decoding=None): if res and decoding: res = res.decode(decoding) return res - func.errcheck = _check_str return func diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/geom.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/geom.py index 06c2a75..f7745f5 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/geom.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/geom.py @@ -4,13 +4,8 @@ from django.contrib.gis.gdal.envelope import OGREnvelope from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.prototypes.errcheck import check_envelope from django.contrib.gis.gdal.prototypes.generation import ( - const_string_output, - double_output, - geom_output, - int_output, - srs_output, - string_output, - void_output, + const_string_output, double_output, geom_output, int_output, srs_output, + string_output, void_output, ) @@ -39,12 +34,8 @@ def topology_func(f): # GeoJSON routines. from_json = geom_output(lgdal.OGR_G_CreateGeometryFromJson, [c_char_p]) -to_json = string_output( - lgdal.OGR_G_ExportToJson, [c_void_p], str_result=True, decoding="ascii" -) -to_kml = string_output( - lgdal.OGR_G_ExportToKML, [c_void_p, c_char_p], str_result=True, decoding="ascii" -) +to_json = string_output(lgdal.OGR_G_ExportToJson, [c_void_p], str_result=True, decoding='ascii') +to_kml = string_output(lgdal.OGR_G_ExportToKML, [c_void_p, c_char_p], str_result=True, decoding='ascii') # GetX, GetY, GetZ all return doubles. getx = pnt_func(lgdal.OGR_G_GetX) @@ -52,14 +43,8 @@ gety = pnt_func(lgdal.OGR_G_GetY) getz = pnt_func(lgdal.OGR_G_GetZ) # Geometry creation routines. -from_wkb = geom_output( - lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2 -) -from_wkt = geom_output( - lgdal.OGR_G_CreateFromWkt, - [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], - offset=-1, -) +from_wkb = geom_output(lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2) +from_wkt = geom_output(lgdal.OGR_G_CreateFromWkt, [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], offset=-1) from_gml = geom_output(lgdal.OGR_G_CreateFromGML, [c_char_p]) create_geom = geom_output(lgdal.OGR_G_CreateGeometry, [c_int]) clone_geom = geom_output(lgdal.OGR_G_Clone, [c_void_p]) @@ -79,21 +64,13 @@ import_wkt = void_output(lgdal.OGR_G_ImportFromWkt, [c_void_p, POINTER(c_char_p) destroy_geom = void_output(lgdal.OGR_G_DestroyGeometry, [c_void_p], errcheck=False) # Geometry export routines. -to_wkb = void_output( - lgdal.OGR_G_ExportToWkb, None, errcheck=True -) # special handling for WKB. -to_wkt = string_output( - lgdal.OGR_G_ExportToWkt, [c_void_p, POINTER(c_char_p)], decoding="ascii" -) -to_gml = string_output( - lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True, decoding="ascii" -) +to_wkb = void_output(lgdal.OGR_G_ExportToWkb, None, errcheck=True) # special handling for WKB. +to_wkt = string_output(lgdal.OGR_G_ExportToWkt, [c_void_p, POINTER(c_char_p)], decoding='ascii') +to_gml = string_output(lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True, decoding='ascii') get_wkbsize = int_output(lgdal.OGR_G_WkbSize, [c_void_p]) # Geometry spatial-reference related routines. -assign_srs = void_output( - lgdal.OGR_G_AssignSpatialReference, [c_void_p, c_void_p], errcheck=False -) +assign_srs = void_output(lgdal.OGR_G_AssignSpatialReference, [c_void_p, c_void_p], errcheck=False) get_geom_srs = srs_output(lgdal.OGR_G_GetSpatialReference, [c_void_p]) # Geometry properties @@ -101,23 +78,16 @@ get_area = double_output(lgdal.OGR_G_GetArea, [c_void_p]) get_centroid = void_output(lgdal.OGR_G_Centroid, [c_void_p, c_void_p]) get_dims = int_output(lgdal.OGR_G_GetDimension, [c_void_p]) get_coord_dim = int_output(lgdal.OGR_G_GetCoordinateDimension, [c_void_p]) -set_coord_dim = void_output( - lgdal.OGR_G_SetCoordinateDimension, [c_void_p, c_int], errcheck=False -) -is_empty = int_output( - lgdal.OGR_G_IsEmpty, [c_void_p], errcheck=lambda result, func, cargs: bool(result) -) +set_coord_dim = void_output(lgdal.OGR_G_SetCoordinateDimension, [c_void_p, c_int], errcheck=False) +is_empty = int_output(lgdal.OGR_G_IsEmpty, [c_void_p], errcheck=lambda result, func, cargs: bool(result)) get_geom_count = int_output(lgdal.OGR_G_GetGeometryCount, [c_void_p]) -get_geom_name = const_string_output( - lgdal.OGR_G_GetGeometryName, [c_void_p], decoding="ascii" -) +get_geom_name = const_string_output(lgdal.OGR_G_GetGeometryName, [c_void_p], decoding='ascii') get_geom_type = int_output(lgdal.OGR_G_GetGeometryType, [c_void_p]) get_point_count = int_output(lgdal.OGR_G_GetPointCount, [c_void_p]) get_point = void_output( lgdal.OGR_G_GetPoint, - [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)], - errcheck=False, + [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)], errcheck=False ) geom_close_rings = void_output(lgdal.OGR_G_CloseRings, [c_void_p], errcheck=False) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/raster.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/raster.py index 59b930c..32b4f70 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/raster.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/raster.py @@ -5,20 +5,16 @@ related data structures. from ctypes import POINTER, c_bool, c_char_p, c_double, c_int, c_void_p from functools import partial -from django.contrib.gis.gdal.libgdal import std_call +from django.contrib.gis.gdal.libgdal import GDAL_VERSION, std_call from django.contrib.gis.gdal.prototypes.generation import ( - chararray_output, - const_string_output, - double_output, - int_output, - void_output, - voidptr_output, + chararray_output, const_string_output, double_output, int_output, + void_output, voidptr_output, ) # For more detail about c function names and definitions see -# https://gdal.org/api/raster_c_api.html -# https://gdal.org/doxygen/gdalwarper_8h.html -# https://gdal.org/api/gdal_utils.html +# https://gdal.org/gdal_8h.html +# https://gdal.org/gdalwarper_8h.html +# https://www.gdal.org/gdal__utils_8h.html # Prepare partial functions that use cpl error codes void_output = partial(void_output, cpl=True) @@ -26,152 +22,87 @@ const_string_output = partial(const_string_output, cpl=True) double_output = partial(double_output, cpl=True) # Raster Driver Routines -register_all = void_output(std_call("GDALAllRegister"), [], errcheck=False) -get_driver = voidptr_output(std_call("GDALGetDriver"), [c_int]) -get_driver_by_name = voidptr_output( - std_call("GDALGetDriverByName"), [c_char_p], errcheck=False -) -get_driver_count = int_output(std_call("GDALGetDriverCount"), []) -get_driver_description = const_string_output(std_call("GDALGetDescription"), [c_void_p]) +register_all = void_output(std_call('GDALAllRegister'), [], errcheck=False) +get_driver = voidptr_output(std_call('GDALGetDriver'), [c_int]) +get_driver_by_name = voidptr_output(std_call('GDALGetDriverByName'), [c_char_p], errcheck=False) +get_driver_count = int_output(std_call('GDALGetDriverCount'), []) +get_driver_description = const_string_output(std_call('GDALGetDescription'), [c_void_p]) # Raster Data Source Routines -create_ds = voidptr_output( - std_call("GDALCreate"), [c_void_p, c_char_p, c_int, c_int, c_int, c_int, c_void_p] -) -open_ds = voidptr_output(std_call("GDALOpen"), [c_char_p, c_int]) -close_ds = void_output(std_call("GDALClose"), [c_void_p], errcheck=False) -flush_ds = int_output(std_call("GDALFlushCache"), [c_void_p]) +create_ds = voidptr_output(std_call('GDALCreate'), [c_void_p, c_char_p, c_int, c_int, c_int, c_int, c_void_p]) +open_ds = voidptr_output(std_call('GDALOpen'), [c_char_p, c_int]) +close_ds = void_output(std_call('GDALClose'), [c_void_p], errcheck=False) +flush_ds = int_output(std_call('GDALFlushCache'), [c_void_p]) copy_ds = voidptr_output( - std_call("GDALCreateCopy"), - [c_void_p, c_char_p, c_void_p, c_int, POINTER(c_char_p), c_void_p, c_void_p], -) -add_band_ds = void_output(std_call("GDALAddBand"), [c_void_p, c_int]) -get_ds_description = const_string_output(std_call("GDALGetDescription"), [c_void_p]) -get_ds_driver = voidptr_output(std_call("GDALGetDatasetDriver"), [c_void_p]) -get_ds_info = const_string_output(std_call("GDALInfo"), [c_void_p, c_void_p]) -get_ds_xsize = int_output(std_call("GDALGetRasterXSize"), [c_void_p]) -get_ds_ysize = int_output(std_call("GDALGetRasterYSize"), [c_void_p]) -get_ds_raster_count = int_output(std_call("GDALGetRasterCount"), [c_void_p]) -get_ds_raster_band = voidptr_output(std_call("GDALGetRasterBand"), [c_void_p, c_int]) -get_ds_projection_ref = const_string_output( - std_call("GDALGetProjectionRef"), [c_void_p] -) -set_ds_projection_ref = void_output(std_call("GDALSetProjection"), [c_void_p, c_char_p]) -get_ds_geotransform = void_output( - std_call("GDALGetGeoTransform"), [c_void_p, POINTER(c_double * 6)], errcheck=False -) -set_ds_geotransform = void_output( - std_call("GDALSetGeoTransform"), [c_void_p, POINTER(c_double * 6)] + std_call('GDALCreateCopy'), + [c_void_p, c_char_p, c_void_p, c_int, POINTER(c_char_p), c_void_p, c_void_p] ) +add_band_ds = void_output(std_call('GDALAddBand'), [c_void_p, c_int]) +get_ds_description = const_string_output(std_call('GDALGetDescription'), [c_void_p]) +get_ds_driver = voidptr_output(std_call('GDALGetDatasetDriver'), [c_void_p]) +get_ds_xsize = int_output(std_call('GDALGetRasterXSize'), [c_void_p]) +get_ds_ysize = int_output(std_call('GDALGetRasterYSize'), [c_void_p]) +get_ds_raster_count = int_output(std_call('GDALGetRasterCount'), [c_void_p]) +get_ds_raster_band = voidptr_output(std_call('GDALGetRasterBand'), [c_void_p, c_int]) +get_ds_projection_ref = const_string_output(std_call('GDALGetProjectionRef'), [c_void_p]) +set_ds_projection_ref = void_output(std_call('GDALSetProjection'), [c_void_p, c_char_p]) +get_ds_geotransform = void_output(std_call('GDALGetGeoTransform'), [c_void_p, POINTER(c_double * 6)], errcheck=False) +set_ds_geotransform = void_output(std_call('GDALSetGeoTransform'), [c_void_p, POINTER(c_double * 6)]) -get_ds_metadata = chararray_output( - std_call("GDALGetMetadata"), [c_void_p, c_char_p], errcheck=False -) -set_ds_metadata = void_output( - std_call("GDALSetMetadata"), [c_void_p, POINTER(c_char_p), c_char_p] -) -get_ds_metadata_domain_list = chararray_output( - std_call("GDALGetMetadataDomainList"), [c_void_p], errcheck=False -) -get_ds_metadata_item = const_string_output( - std_call("GDALGetMetadataItem"), [c_void_p, c_char_p, c_char_p] -) -set_ds_metadata_item = const_string_output( - std_call("GDALSetMetadataItem"), [c_void_p, c_char_p, c_char_p, c_char_p] -) -free_dsl = void_output(std_call("CSLDestroy"), [POINTER(c_char_p)], errcheck=False) +get_ds_metadata = chararray_output(std_call('GDALGetMetadata'), [c_void_p, c_char_p], errcheck=False) +set_ds_metadata = void_output(std_call('GDALSetMetadata'), [c_void_p, POINTER(c_char_p), c_char_p]) +get_ds_metadata_domain_list = chararray_output(std_call('GDALGetMetadataDomainList'), [c_void_p], errcheck=False) +get_ds_metadata_item = const_string_output(std_call('GDALGetMetadataItem'), [c_void_p, c_char_p, c_char_p]) +set_ds_metadata_item = const_string_output(std_call('GDALSetMetadataItem'), [c_void_p, c_char_p, c_char_p, c_char_p]) +free_dsl = void_output(std_call('CSLDestroy'), [POINTER(c_char_p)], errcheck=False) + +if GDAL_VERSION >= (2, 1): + get_ds_info = const_string_output(std_call('GDALInfo'), [c_void_p, c_void_p]) +else: + get_ds_info = None # Raster Band Routines band_io = void_output( - std_call("GDALRasterIO"), - [ - c_void_p, - c_int, - c_int, - c_int, - c_int, - c_int, - c_void_p, - c_int, - c_int, - c_int, - c_int, - c_int, - ], -) -get_band_xsize = int_output(std_call("GDALGetRasterBandXSize"), [c_void_p]) -get_band_ysize = int_output(std_call("GDALGetRasterBandYSize"), [c_void_p]) -get_band_index = int_output(std_call("GDALGetBandNumber"), [c_void_p]) -get_band_description = const_string_output(std_call("GDALGetDescription"), [c_void_p]) -get_band_ds = voidptr_output(std_call("GDALGetBandDataset"), [c_void_p]) -get_band_datatype = int_output(std_call("GDALGetRasterDataType"), [c_void_p]) -get_band_color_interp = int_output( - std_call("GDALGetRasterColorInterpretation"), [c_void_p] -) -get_band_nodata_value = double_output( - std_call("GDALGetRasterNoDataValue"), [c_void_p, POINTER(c_int)] -) -set_band_nodata_value = void_output( - std_call("GDALSetRasterNoDataValue"), [c_void_p, c_double] -) -delete_band_nodata_value = void_output( - std_call("GDALDeleteRasterNoDataValue"), [c_void_p] + std_call('GDALRasterIO'), + [c_void_p, c_int, c_int, c_int, c_int, c_int, c_void_p, c_int, c_int, c_int, c_int, c_int] ) +get_band_xsize = int_output(std_call('GDALGetRasterBandXSize'), [c_void_p]) +get_band_ysize = int_output(std_call('GDALGetRasterBandYSize'), [c_void_p]) +get_band_index = int_output(std_call('GDALGetBandNumber'), [c_void_p]) +get_band_description = const_string_output(std_call('GDALGetDescription'), [c_void_p]) +get_band_ds = voidptr_output(std_call('GDALGetBandDataset'), [c_void_p]) +get_band_datatype = int_output(std_call('GDALGetRasterDataType'), [c_void_p]) +get_band_color_interp = int_output(std_call('GDALGetRasterColorInterpretation'), [c_void_p]) +get_band_nodata_value = double_output(std_call('GDALGetRasterNoDataValue'), [c_void_p, POINTER(c_int)]) +set_band_nodata_value = void_output(std_call('GDALSetRasterNoDataValue'), [c_void_p, c_double]) +if GDAL_VERSION >= (2, 1): + delete_band_nodata_value = void_output(std_call('GDALDeleteRasterNoDataValue'), [c_void_p]) +else: + delete_band_nodata_value = None get_band_statistics = void_output( - std_call("GDALGetRasterStatistics"), + std_call('GDALGetRasterStatistics'), [ - c_void_p, - c_int, - c_int, - POINTER(c_double), - POINTER(c_double), - POINTER(c_double), - POINTER(c_double), - c_void_p, - c_void_p, + c_void_p, c_int, c_int, POINTER(c_double), POINTER(c_double), + POINTER(c_double), POINTER(c_double), c_void_p, c_void_p, ], ) compute_band_statistics = void_output( - std_call("GDALComputeRasterStatistics"), - [ - c_void_p, - c_int, - POINTER(c_double), - POINTER(c_double), - POINTER(c_double), - POINTER(c_double), - c_void_p, - c_void_p, - ], + std_call('GDALComputeRasterStatistics'), + [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_void_p, c_void_p], ) # Reprojection routine reproject_image = void_output( - std_call("GDALReprojectImage"), - [ - c_void_p, - c_char_p, - c_void_p, - c_char_p, - c_int, - c_double, - c_double, - c_void_p, - c_void_p, - c_void_p, - ], + std_call('GDALReprojectImage'), + [c_void_p, c_char_p, c_void_p, c_char_p, c_int, c_double, c_double, c_void_p, c_void_p, c_void_p] ) auto_create_warped_vrt = voidptr_output( - std_call("GDALAutoCreateWarpedVRT"), - [c_void_p, c_char_p, c_char_p, c_int, c_double, c_void_p], + std_call('GDALAutoCreateWarpedVRT'), + [c_void_p, c_char_p, c_char_p, c_int, c_double, c_void_p] ) # Create VSI gdal raster files from in-memory buffers. -# https://gdal.org/api/cpl.html#cpl-vsi-h -create_vsi_file_from_mem_buffer = voidptr_output( - std_call("VSIFileFromMemBuffer"), [c_char_p, c_void_p, c_int, c_int] -) -get_mem_buffer_from_vsi_file = voidptr_output( - std_call("VSIGetMemFileBuffer"), [c_char_p, POINTER(c_int), c_bool] -) -unlink_vsi_file = int_output(std_call("VSIUnlink"), [c_char_p]) +# https://gdal.org/cpl__vsi_8h.html +create_vsi_file_from_mem_buffer = voidptr_output(std_call('VSIFileFromMemBuffer'), [c_char_p, c_void_p, c_int, c_int]) +get_mem_buffer_from_vsi_file = voidptr_output(std_call('VSIGetMemFileBuffer'), [c_char_p, POINTER(c_int), c_bool]) +unlink_vsi_file = int_output(std_call('VSIUnlink'), [c_char_p]) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/srs.py b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/srs.py index 22db907..5d080d8 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/srs.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/prototypes/srs.py @@ -2,11 +2,7 @@ from ctypes import POINTER, c_char_p, c_int, c_void_p from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal, std_call from django.contrib.gis.gdal.prototypes.generation import ( - const_string_output, - double_output, - int_output, - srs_output, - string_output, + const_string_output, double_output, int_output, srs_output, string_output, void_output, ) @@ -29,18 +25,14 @@ def units_func(f): # Creation & destruction. -clone_srs = srs_output(std_call("OSRClone"), [c_void_p]) -new_srs = srs_output(std_call("OSRNewSpatialReference"), [c_char_p]) +clone_srs = srs_output(std_call('OSRClone'), [c_void_p]) +new_srs = srs_output(std_call('OSRNewSpatialReference'), [c_char_p]) release_srs = void_output(lgdal.OSRRelease, [c_void_p], errcheck=False) -destroy_srs = void_output( - std_call("OSRDestroySpatialReference"), [c_void_p], errcheck=False -) +destroy_srs = void_output(std_call('OSRDestroySpatialReference'), [c_void_p], errcheck=False) srs_validate = void_output(lgdal.OSRValidate, [c_void_p]) if GDAL_VERSION >= (3, 0): - set_axis_strategy = void_output( - lgdal.OSRSetAxisMappingStrategy, [c_void_p, c_int], errcheck=False - ) + set_axis_strategy = void_output(lgdal.OSRSetAxisMappingStrategy, [c_void_p, c_int], errcheck=False) # Getting the semi_major, semi_minor, and flattening functions. semi_major = srs_double(lgdal.OSRGetSemiMajor) @@ -50,9 +42,9 @@ invflattening = srs_double(lgdal.OSRGetInvFlattening) # WKT, PROJ, EPSG, XML importation routines. from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)]) from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p]) -from_epsg = void_output(std_call("OSRImportFromEPSG"), [c_void_p, c_int]) +from_epsg = void_output(std_call('OSRImportFromEPSG'), [c_void_p, c_int]) from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p]) -from_user_input = void_output(std_call("OSRSetFromUserInput"), [c_void_p, c_char_p]) +from_user_input = void_output(std_call('OSRSetFromUserInput'), [c_void_p, c_char_p]) # Morphing to/from ESRI WKT. morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p]) @@ -66,37 +58,20 @@ linear_units = units_func(lgdal.OSRGetLinearUnits) angular_units = units_func(lgdal.OSRGetAngularUnits) # For exporting to WKT, PROJ, "Pretty" WKT, and XML. -to_wkt = string_output( - std_call("OSRExportToWkt"), [c_void_p, POINTER(c_char_p)], decoding="utf-8" -) -to_proj = string_output( - std_call("OSRExportToProj4"), [c_void_p, POINTER(c_char_p)], decoding="ascii" -) +to_wkt = string_output(std_call('OSRExportToWkt'), [c_void_p, POINTER(c_char_p)], decoding='utf-8') +to_proj = string_output(std_call('OSRExportToProj4'), [c_void_p, POINTER(c_char_p)], decoding='ascii') to_pretty_wkt = string_output( - std_call("OSRExportToPrettyWkt"), - [c_void_p, POINTER(c_char_p), c_int], - offset=-2, - decoding="utf-8", + std_call('OSRExportToPrettyWkt'), + [c_void_p, POINTER(c_char_p), c_int], offset=-2, decoding='utf-8' ) # Memory leak fixed in GDAL 1.5; still exists in 1.4. -to_xml = string_output( - lgdal.OSRExportToXML, - [c_void_p, POINTER(c_char_p), c_char_p], - offset=-2, - decoding="utf-8", -) +to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2, decoding='utf-8') # String attribute retrieval routines. -get_attr_value = const_string_output( - std_call("OSRGetAttrValue"), [c_void_p, c_char_p, c_int], decoding="utf-8" -) -get_auth_name = const_string_output( - lgdal.OSRGetAuthorityName, [c_void_p, c_char_p], decoding="ascii" -) -get_auth_code = const_string_output( - lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p], decoding="ascii" -) +get_attr_value = const_string_output(std_call('OSRGetAttrValue'), [c_void_p, c_char_p, c_int], decoding='utf-8') +get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p], decoding='ascii') +get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p], decoding='ascii') # SRS Properties isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p]) @@ -104,7 +79,5 @@ islocal = int_output(lgdal.OSRIsLocal, [c_void_p]) isprojected = int_output(lgdal.OSRIsProjected, [c_void_p]) # Coordinate transformation -new_ct = srs_output(std_call("OCTNewCoordinateTransformation"), [c_void_p, c_void_p]) -destroy_ct = void_output( - std_call("OCTDestroyCoordinateTransformation"), [c_void_p], errcheck=False -) +new_ct = srs_output(std_call('OCTNewCoordinateTransformation'), [c_void_p, c_void_p]) +destroy_ct = void_output(std_call('OCTDestroyCoordinateTransformation'), [c_void_p], errcheck=False) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/band.py b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/band.py index c3ec960..61c6535 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/band.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/band.py @@ -7,10 +7,7 @@ from django.contrib.gis.shortcuts import numpy from django.utils.encoding import force_str from .const import ( - GDAL_COLOR_TYPES, - GDAL_INTEGER_TYPES, - GDAL_PIXEL_TYPES, - GDAL_TO_CTYPES, + GDAL_COLOR_TYPES, GDAL_INTEGER_TYPES, GDAL_PIXEL_TYPES, GDAL_TO_CTYPES, ) @@ -18,7 +15,6 @@ class GDALBand(GDALRasterBase): """ Wrap a GDAL raster band, needs to be obtained from a GDALRaster object. """ - def __init__(self, source, index): self.source = source self._ptr = capi.get_ds_raster_band(source._ptr, index) @@ -83,14 +79,8 @@ class GDALBand(GDALRasterBase): # Prepare array with arguments for capi function smin, smax, smean, sstd = c_double(), c_double(), c_double(), c_double() stats_args = [ - self._ptr, - c_int(approximate), - byref(smin), - byref(smax), - byref(smean), - byref(sstd), - c_void_p(), - c_void_p(), + self._ptr, c_int(approximate), byref(smin), byref(smax), + byref(smean), byref(sstd), c_void_p(), c_void_p(), ] if refresh or self._stats_refresh: @@ -162,9 +152,11 @@ class GDALBand(GDALRasterBase): Set the nodata value for this band. """ if value is None: + if not capi.delete_band_nodata_value: + raise ValueError('GDAL >= 2.1 required to delete nodata values.') capi.delete_band_nodata_value(self._ptr) elif not isinstance(value, (int, float)): - raise ValueError("Nodata value must be numeric or None.") + raise ValueError('Nodata value must be numeric or None.') else: capi.set_band_nodata_value(self._ptr, value) self._flush() @@ -198,10 +190,10 @@ class GDALBand(GDALRasterBase): size = size or (self.width - offset[0], self.height - offset[1]) shape = shape or size if any(x <= 0 for x in size): - raise ValueError("Offset too big for this raster.") + raise ValueError('Offset too big for this raster.') if size[0] > self.width or size[1] > self.height: - raise ValueError("Size is larger than raster.") + raise ValueError('Size is larger than raster.') # Create ctypes type array generator ctypes_array = GDAL_TO_CTYPES[self.datatype()] * (shape[0] * shape[1]) @@ -216,28 +208,15 @@ class GDALBand(GDALRasterBase): access_flag = 1 # Instantiate ctypes array holding the input data - if isinstance(data, (bytes, memoryview)) or ( - numpy and isinstance(data, numpy.ndarray) - ): + if isinstance(data, (bytes, memoryview)) or (numpy and isinstance(data, numpy.ndarray)): data_array = ctypes_array.from_buffer_copy(data) else: data_array = ctypes_array(*data) # Access band - capi.band_io( - self._ptr, - access_flag, - offset[0], - offset[1], - size[0], - size[1], - byref(data_array), - shape[0], - shape[1], - self.datatype(), - 0, - 0, - ) + capi.band_io(self._ptr, access_flag, offset[0], offset[1], + size[0], size[1], byref(data_array), shape[0], + shape[1], self.datatype(), 0, 0) # Return data as numpy array if possible, otherwise as list if data is None: @@ -270,4 +249,4 @@ class BandList(list): try: return GDALBand(self.source, index + 1) except GDALException: - raise GDALException("Unable to get band index %d" % index) + raise GDALException('Unable to get band index %d' % index) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/base.py b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/base.py index 3d95c90..c5b438e 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/base.py @@ -6,7 +6,6 @@ class GDALRasterBase(GDALBase): """ Attributes that exist on both GDALRaster and GDALBand. """ - @property def metadata(self): """ @@ -16,7 +15,7 @@ class GDALRasterBase(GDALBase): """ # The initial metadata domain list contains the default domain. # The default is returned if domain name is None. - domain_list = ["DEFAULT"] + domain_list = ['DEFAULT'] # Get additional metadata domains from the raster. meta_list = capi.get_ds_metadata_domain_list(self._ptr) @@ -39,7 +38,7 @@ class GDALRasterBase(GDALBase): # Get metadata for this domain. data = capi.get_ds_metadata( self._ptr, - (None if domain == "DEFAULT" else domain.encode()), + (None if domain == 'DEFAULT' else domain.encode()), ) if not data: continue @@ -49,12 +48,12 @@ class GDALRasterBase(GDALBase): counter = 0 item = data[counter] while item: - key, val = item.decode().split("=") + key, val = item.decode().split('=') domain_meta[key] = val counter += 1 item = data[counter] # The default domain values are returned if domain is None. - result[domain or "DEFAULT"] = domain_meta + result[domain or 'DEFAULT'] = domain_meta return result @metadata.setter @@ -66,12 +65,11 @@ class GDALRasterBase(GDALBase): # Loop through domains. for domain, metadata in value.items(): # Set the domain to None for the default, otherwise encode. - domain = None if domain == "DEFAULT" else domain.encode() + domain = None if domain == 'DEFAULT' else domain.encode() # Set each metadata entry separately. for meta_name, meta_value in metadata.items(): capi.set_ds_metadata_item( - self._ptr, - meta_name.encode(), + self._ptr, meta_name.encode(), meta_value.encode() if meta_value else None, domain, ) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/const.py b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/const.py index 6d3761d..f9793e6 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/const.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/const.py @@ -1,22 +1,24 @@ """ GDAL - Constant definitions """ -from ctypes import c_double, c_float, c_int16, c_int32, c_ubyte, c_uint16, c_uint32 +from ctypes import ( + c_double, c_float, c_int16, c_int32, c_ubyte, c_uint16, c_uint32, +) -# See https://gdal.org/api/raster_c_api.html#_CPPv412GDALDataType +# See https://www.gdal.org/gdal_8h.html#a22e22ce0a55036a96f652765793fb7a4 GDAL_PIXEL_TYPES = { - 0: "GDT_Unknown", # Unknown or unspecified type - 1: "GDT_Byte", # Eight bit unsigned integer - 2: "GDT_UInt16", # Sixteen bit unsigned integer - 3: "GDT_Int16", # Sixteen bit signed integer - 4: "GDT_UInt32", # Thirty-two bit unsigned integer - 5: "GDT_Int32", # Thirty-two bit signed integer - 6: "GDT_Float32", # Thirty-two bit floating point - 7: "GDT_Float64", # Sixty-four bit floating point - 8: "GDT_CInt16", # Complex Int16 - 9: "GDT_CInt32", # Complex Int32 - 10: "GDT_CFloat32", # Complex Float32 - 11: "GDT_CFloat64", # Complex Float64 + 0: 'GDT_Unknown', # Unknown or unspecified type + 1: 'GDT_Byte', # Eight bit unsigned integer + 2: 'GDT_UInt16', # Sixteen bit unsigned integer + 3: 'GDT_Int16', # Sixteen bit signed integer + 4: 'GDT_UInt32', # Thirty-two bit unsigned integer + 5: 'GDT_Int32', # Thirty-two bit signed integer + 6: 'GDT_Float32', # Thirty-two bit floating point + 7: 'GDT_Float64', # Sixty-four bit floating point + 8: 'GDT_CInt16', # Complex Int16 + 9: 'GDT_CInt32', # Complex Int32 + 10: 'GDT_CFloat32', # Complex Float32 + 11: 'GDT_CFloat64', # Complex Float64 } # A list of gdal datatypes that are integers. @@ -27,57 +29,44 @@ GDAL_INTEGER_TYPES = [1, 2, 3, 4, 5] # or to hold the space for data to be read into. The lookup below helps # selecting the right ctypes object for a given gdal pixel type. GDAL_TO_CTYPES = [ - None, - c_ubyte, - c_uint16, - c_int16, - c_uint32, - c_int32, - c_float, - c_double, - None, - None, - None, - None, + None, c_ubyte, c_uint16, c_int16, c_uint32, c_int32, + c_float, c_double, None, None, None, None ] # List of resampling algorithms that can be used to warp a GDALRaster. GDAL_RESAMPLE_ALGORITHMS = { - "NearestNeighbour": 0, - "Bilinear": 1, - "Cubic": 2, - "CubicSpline": 3, - "Lanczos": 4, - "Average": 5, - "Mode": 6, + 'NearestNeighbour': 0, + 'Bilinear': 1, + 'Cubic': 2, + 'CubicSpline': 3, + 'Lanczos': 4, + 'Average': 5, + 'Mode': 6, } -# See https://gdal.org/api/raster_c_api.html#_CPPv415GDALColorInterp +# See https://www.gdal.org/gdal_8h.html#ace76452d94514561fffa8ea1d2a5968c GDAL_COLOR_TYPES = { - 0: "GCI_Undefined", # Undefined, default value, i.e. not known - 1: "GCI_GrayIndex", # Grayscale - 2: "GCI_PaletteIndex", # Paletted - 3: "GCI_RedBand", # Red band of RGBA image - 4: "GCI_GreenBand", # Green band of RGBA image - 5: "GCI_BlueBand", # Blue band of RGBA image - 6: "GCI_AlphaBand", # Alpha (0=transparent, 255=opaque) - 7: "GCI_HueBand", # Hue band of HLS image - 8: "GCI_SaturationBand", # Saturation band of HLS image - 9: "GCI_LightnessBand", # Lightness band of HLS image - 10: "GCI_CyanBand", # Cyan band of CMYK image - 11: "GCI_MagentaBand", # Magenta band of CMYK image - 12: "GCI_YellowBand", # Yellow band of CMYK image - 13: "GCI_BlackBand", # Black band of CMLY image - 14: "GCI_YCbCr_YBand", # Y Luminance - 15: "GCI_YCbCr_CbBand", # Cb Chroma - 16: "GCI_YCbCr_CrBand", # Cr Chroma, also GCI_Max + 0: 'GCI_Undefined', # Undefined, default value, i.e. not known + 1: 'GCI_GrayIndex', # Greyscale + 2: 'GCI_PaletteIndex', # Paletted + 3: 'GCI_RedBand', # Red band of RGBA image + 4: 'GCI_GreenBand', # Green band of RGBA image + 5: 'GCI_BlueBand', # Blue band of RGBA image + 6: 'GCI_AlphaBand', # Alpha (0=transparent, 255=opaque) + 7: 'GCI_HueBand', # Hue band of HLS image + 8: 'GCI_SaturationBand', # Saturation band of HLS image + 9: 'GCI_LightnessBand', # Lightness band of HLS image + 10: 'GCI_CyanBand', # Cyan band of CMYK image + 11: 'GCI_MagentaBand', # Magenta band of CMYK image + 12: 'GCI_YellowBand', # Yellow band of CMYK image + 13: 'GCI_BlackBand', # Black band of CMLY image + 14: 'GCI_YCbCr_YBand', # Y Luminance + 15: 'GCI_YCbCr_CbBand', # Cb Chroma + 16: 'GCI_YCbCr_CrBand', # Cr Chroma, also GCI_Max } -# GDAL virtual filesystems prefix. -VSI_FILESYSTEM_PREFIX = "/vsi" - # Fixed base path for buffer-based GDAL in-memory files. -VSI_MEM_FILESYSTEM_BASE_PATH = "/vsimem/" +VSI_FILESYSTEM_BASE_PATH = '/vsimem/' # Should the memory file system take ownership of the buffer, freeing it when # the file is deleted? (No, GDALRaster.__del__() will delete the buffer.) diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/source.py b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/source.py index ca78757..d312936 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/raster/source.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/raster/source.py @@ -3,14 +3,7 @@ import os import sys import uuid from ctypes import ( - addressof, - byref, - c_buffer, - c_char_p, - c_double, - c_int, - c_void_p, - string_at, + addressof, byref, c_buffer, c_char_p, c_double, c_int, c_void_p, string_at, ) from django.contrib.gis.gdal.driver import Driver @@ -19,11 +12,8 @@ from django.contrib.gis.gdal.prototypes import raster as capi from django.contrib.gis.gdal.raster.band import BandList from django.contrib.gis.gdal.raster.base import GDALRasterBase from django.contrib.gis.gdal.raster.const import ( - GDAL_RESAMPLE_ALGORITHMS, - VSI_DELETE_BUFFER_ON_READ, - VSI_FILESYSTEM_PREFIX, - VSI_MEM_FILESYSTEM_BASE_PATH, - VSI_TAKE_BUFFER_OWNERSHIP, + GDAL_RESAMPLE_ALGORITHMS, VSI_DELETE_BUFFER_ON_READ, + VSI_FILESYSTEM_BASE_PATH, VSI_TAKE_BUFFER_OWNERSHIP, ) from django.contrib.gis.gdal.srs import SpatialReference, SRSException from django.contrib.gis.geometry import json_regex @@ -33,9 +23,9 @@ from django.utils.functional import cached_property class TransformPoint(list): indices = { - "origin": (0, 3), - "scale": (1, 5), - "skew": (2, 4), + 'origin': (0, 3), + 'scale': (1, 5), + 'skew': (2, 4), } def __init__(self, raster, prop): @@ -70,7 +60,6 @@ class GDALRaster(GDALRasterBase): """ Wrap a raster GDAL Data Source object. """ - destructor = capi.close_ds def __init__(self, ds_input, write=False): @@ -84,8 +73,9 @@ class GDALRaster(GDALRasterBase): # If input is a valid file path, try setting file as source. if isinstance(ds_input, str): - if not ds_input.startswith(VSI_FILESYSTEM_PREFIX) and not os.path.exists( - ds_input + if ( + not ds_input.startswith(VSI_FILESYSTEM_BASE_PATH) and + not os.path.exists(ds_input) ): raise GDALException( 'Unable to read raster source input "%s".' % ds_input @@ -94,9 +84,7 @@ class GDALRaster(GDALRasterBase): # GDALOpen will auto-detect the data source type. self._ptr = capi.open_ds(force_bytes(ds_input), self._write) except GDALException as err: - raise GDALException( - 'Could not open the datasource at "{}" ({}).'.format(ds_input, err) - ) + raise GDALException('Could not open the datasource at "{}" ({}).'.format(ds_input, err)) elif isinstance(ds_input, bytes): # Create a new raster in write mode. self._write = 1 @@ -107,7 +95,7 @@ class GDALRaster(GDALRasterBase): # deleted. self._ds_input = c_buffer(ds_input) # Create random name to reference in vsimem filesystem. - vsi_path = os.path.join(VSI_MEM_FILESYSTEM_BASE_PATH, str(uuid.uuid4())) + vsi_path = os.path.join(VSI_FILESYSTEM_BASE_PATH, str(uuid.uuid4())) # Create vsimem file from buffer. capi.create_vsi_file_from_mem_buffer( force_bytes(vsi_path), @@ -121,36 +109,30 @@ class GDALRaster(GDALRasterBase): except GDALException: # Remove the broken file from the VSI filesystem. capi.unlink_vsi_file(force_bytes(vsi_path)) - raise GDALException("Failed creating VSI raster from the input buffer.") + raise GDALException('Failed creating VSI raster from the input buffer.') elif isinstance(ds_input, dict): # A new raster needs to be created in write mode self._write = 1 # Create driver (in memory by default) - driver = Driver(ds_input.get("driver", "MEM")) + driver = Driver(ds_input.get('driver', 'MEM')) # For out of memory drivers, check filename argument - if driver.name != "MEM" and "name" not in ds_input: - raise GDALException( - 'Specify name for creation of raster with driver "{}".'.format( - driver.name - ) - ) + if driver.name != 'MEM' and 'name' not in ds_input: + raise GDALException('Specify name for creation of raster with driver "{}".'.format(driver.name)) # Check if width and height where specified - if "width" not in ds_input or "height" not in ds_input: - raise GDALException( - "Specify width and height attributes for JSON or dict input." - ) + if 'width' not in ds_input or 'height' not in ds_input: + raise GDALException('Specify width and height attributes for JSON or dict input.') # Check if srid was specified - if "srid" not in ds_input: - raise GDALException("Specify srid for JSON or dict input.") + if 'srid' not in ds_input: + raise GDALException('Specify srid for JSON or dict input.') # Create null terminated gdal options array. papsz_options = [] - for key, val in ds_input.get("papsz_options", {}).items(): - option = "{}={}".format(key, val) + for key, val in ds_input.get('papsz_options', {}).items(): + option = '{}={}'.format(key, val) papsz_options.append(option.upper().encode()) papsz_options.append(None) @@ -160,54 +142,51 @@ class GDALRaster(GDALRasterBase): # Create GDAL Raster self._ptr = capi.create_ds( driver._ptr, - force_bytes(ds_input.get("name", "")), - ds_input["width"], - ds_input["height"], - ds_input.get("nr_of_bands", len(ds_input.get("bands", []))), - ds_input.get("datatype", 6), + force_bytes(ds_input.get('name', '')), + ds_input['width'], + ds_input['height'], + ds_input.get('nr_of_bands', len(ds_input.get('bands', []))), + ds_input.get('datatype', 6), byref(papsz_options), ) # Set band data if provided - for i, band_input in enumerate(ds_input.get("bands", [])): + for i, band_input in enumerate(ds_input.get('bands', [])): band = self.bands[i] - if "nodata_value" in band_input: - band.nodata_value = band_input["nodata_value"] + if 'nodata_value' in band_input: + band.nodata_value = band_input['nodata_value'] # Instantiate band filled with nodata values if only # partial input data has been provided. if band.nodata_value is not None and ( - "data" not in band_input - or "size" in band_input - or "shape" in band_input - ): + 'data' not in band_input or + 'size' in band_input or + 'shape' in band_input): band.data(data=(band.nodata_value,), shape=(1, 1)) # Set band data values from input. band.data( - data=band_input.get("data"), - size=band_input.get("size"), - shape=band_input.get("shape"), - offset=band_input.get("offset"), + data=band_input.get('data'), + size=band_input.get('size'), + shape=band_input.get('shape'), + offset=band_input.get('offset'), ) # Set SRID - self.srs = ds_input.get("srid") + self.srs = ds_input.get('srid') # Set additional properties if provided - if "origin" in ds_input: - self.origin.x, self.origin.y = ds_input["origin"] + if 'origin' in ds_input: + self.origin.x, self.origin.y = ds_input['origin'] - if "scale" in ds_input: - self.scale.x, self.scale.y = ds_input["scale"] + if 'scale' in ds_input: + self.scale.x, self.scale.y = ds_input['scale'] - if "skew" in ds_input: - self.skew.x, self.skew.y = ds_input["skew"] + if 'skew' in ds_input: + self.skew.x, self.skew.y = ds_input['skew'] elif isinstance(ds_input, c_void_p): # Instantiate the object using an existing pointer to a gdal raster. self._ptr = ds_input else: - raise GDALException( - 'Invalid data source input type: "{}".'.format(type(ds_input)) - ) + raise GDALException('Invalid data source input type: "{}".'.format(type(ds_input))) def __del__(self): if self.is_vsi_based: @@ -222,7 +201,7 @@ class GDALRaster(GDALRasterBase): """ Short-hand representation because WKB may be very large. """ - return "<Raster object at %s>" % hex(addressof(self._ptr)) + return '<Raster object at %s>' % hex(addressof(self._ptr)) def _flush(self): """ @@ -233,16 +212,12 @@ class GDALRaster(GDALRasterBase): """ # Raise an Exception if the value is being changed in read mode. if not self._write: - raise GDALException( - "Raster needs to be opened in write mode to change values." - ) + raise GDALException('Raster needs to be opened in write mode to change values.') capi.flush_ds(self._ptr) @property def vsi_buffer(self): - if not ( - self.is_vsi_based and self.name.startswith(VSI_MEM_FILESYSTEM_BASE_PATH) - ): + if not self.is_vsi_based: return None # Prepare an integer that will contain the buffer length. out_length = c_int() @@ -257,7 +232,7 @@ class GDALRaster(GDALRasterBase): @cached_property def is_vsi_based(self): - return self._ptr and self.name.startswith(VSI_FILESYSTEM_PREFIX) + return self._ptr and self.name.startswith(VSI_FILESYSTEM_BASE_PATH) @property def name(self): @@ -298,7 +273,7 @@ class GDALRaster(GDALRasterBase): wkt = capi.get_ds_projection_ref(self._ptr) if not wkt: return None - return SpatialReference(wkt, srs_type="wkt") + return SpatialReference(wkt, srs_type='wkt') except SRSException: return None @@ -314,7 +289,7 @@ class GDALRaster(GDALRasterBase): elif isinstance(value, (int, str)): srs = SpatialReference(value) else: - raise ValueError("Could not create a SpatialReference from input.") + raise ValueError('Could not create a SpatialReference from input.') capi.set_ds_projection_ref(self._ptr, srs.wkt.encode()) self._flush() @@ -348,7 +323,7 @@ class GDALRaster(GDALRasterBase): def geotransform(self, values): "Set the geotransform for the data source." if len(values) != 6 or not all(isinstance(x, (int, float)) for x in values): - raise ValueError("Geotransform must consist of 6 numeric values.") + raise ValueError('Geotransform must consist of 6 numeric values.') # Create ctypes double array with input and write data values = (c_double * 6)(*values) capi.set_ds_geotransform(self._ptr, byref(values)) @@ -359,21 +334,21 @@ class GDALRaster(GDALRasterBase): """ Coordinates of the raster origin. """ - return TransformPoint(self, "origin") + return TransformPoint(self, 'origin') @property def scale(self): """ Pixel scale in units of the raster projection. """ - return TransformPoint(self, "scale") + return TransformPoint(self, 'scale') @property def skew(self): """ Skew of pixels (rotation parameters). """ - return TransformPoint(self, "skew") + return TransformPoint(self, 'skew') @property def extent(self): @@ -395,7 +370,7 @@ class GDALRaster(GDALRasterBase): def bands(self): return BandList(self) - def warp(self, ds_input, resampling="NearestNeighbour", max_error=0.0): + def warp(self, ds_input, resampling='NearestNeighbour', max_error=0.0): """ Return a warped GDALRaster with the given input characteristics. @@ -413,23 +388,23 @@ class GDALRaster(GDALRasterBase): consult the GDAL_RESAMPLE_ALGORITHMS constant. """ # Get the parameters defining the geotransform, srid, and size of the raster - ds_input.setdefault("width", self.width) - ds_input.setdefault("height", self.height) - ds_input.setdefault("srid", self.srs.srid) - ds_input.setdefault("origin", self.origin) - ds_input.setdefault("scale", self.scale) - ds_input.setdefault("skew", self.skew) + ds_input.setdefault('width', self.width) + ds_input.setdefault('height', self.height) + ds_input.setdefault('srid', self.srs.srid) + ds_input.setdefault('origin', self.origin) + ds_input.setdefault('scale', self.scale) + ds_input.setdefault('skew', self.skew) # Get the driver, name, and datatype of the target raster - ds_input.setdefault("driver", self.driver.name) + ds_input.setdefault('driver', self.driver.name) - if "name" not in ds_input: - ds_input["name"] = self.name + "_copy." + self.driver.name + if 'name' not in ds_input: + ds_input['name'] = self.name + '_copy.' + self.driver.name - if "datatype" not in ds_input: - ds_input["datatype"] = self.bands[0].datatype() + if 'datatype' not in ds_input: + ds_input['datatype'] = self.bands[0].datatype() # Instantiate raster bands filled with nodata values. - ds_input["bands"] = [{"nodata_value": bnd.nodata_value} for bnd in self.bands] + ds_input['bands'] = [{'nodata_value': bnd.nodata_value} for bnd in self.bands] # Create target raster target = GDALRaster(ds_input, write=True) @@ -439,16 +414,10 @@ class GDALRaster(GDALRasterBase): # Reproject image capi.reproject_image( - self._ptr, - self.srs.wkt.encode(), - target._ptr, - target.srs.wkt.encode(), - algorithm, - 0.0, - max_error, - c_void_p(), - c_void_p(), - c_void_p(), + self._ptr, self.srs.wkt.encode(), + target._ptr, target.srs.wkt.encode(), + algorithm, 0.0, max_error, + c_void_p(), c_void_p(), c_void_p() ) # Make sure all data is written to file @@ -460,10 +429,10 @@ class GDALRaster(GDALRasterBase): """Return a clone of this GDALRaster.""" if name: clone_name = name - elif self.driver.name != "MEM": - clone_name = self.name + "_copy." + self.driver.name + elif self.driver.name != 'MEM': + clone_name = self.name + '_copy.' + self.driver.name else: - clone_name = os.path.join(VSI_MEM_FILESYSTEM_BASE_PATH, str(uuid.uuid4())) + clone_name = os.path.join(VSI_FILESYSTEM_BASE_PATH, str(uuid.uuid4())) return GDALRaster( capi.copy_ds( self.driver._ptr, @@ -477,9 +446,8 @@ class GDALRaster(GDALRasterBase): write=self._write, ) - def transform( - self, srs, driver=None, name=None, resampling="NearestNeighbour", max_error=0.0 - ): + def transform(self, srs, driver=None, name=None, resampling='NearestNeighbour', + max_error=0.0): """ Return a copy of this raster reprojected into the given spatial reference system. @@ -493,39 +461,35 @@ class GDALRaster(GDALRasterBase): target_srs = SpatialReference(srs) else: raise TypeError( - "Transform only accepts SpatialReference, string, and integer " - "objects." + 'Transform only accepts SpatialReference, string, and integer ' + 'objects.' ) if target_srs.srid == self.srid and (not driver or driver == self.driver.name): return self.clone(name) # Create warped virtual dataset in the target reference system target = capi.auto_create_warped_vrt( - self._ptr, - self.srs.wkt.encode(), - target_srs.wkt.encode(), - algorithm, - max_error, - c_void_p(), + self._ptr, self.srs.wkt.encode(), target_srs.wkt.encode(), + algorithm, max_error, c_void_p() ) target = GDALRaster(target) # Construct the target warp dictionary from the virtual raster data = { - "srid": target_srs.srid, - "width": target.width, - "height": target.height, - "origin": [target.origin.x, target.origin.y], - "scale": [target.scale.x, target.scale.y], - "skew": [target.skew.x, target.skew.y], + 'srid': target_srs.srid, + 'width': target.width, + 'height': target.height, + 'origin': [target.origin.x, target.origin.y], + 'scale': [target.scale.x, target.scale.y], + 'skew': [target.skew.x, target.skew.y], } # Set the driver and filepath if provided if driver: - data["driver"] = driver + data['driver'] = driver if name: - data["name"] = name + data['name'] = name # Warp the raster into new srid return self.warp(data, resampling=resampling, max_error=max_error) @@ -536,4 +500,6 @@ class GDALRaster(GDALRasterBase): Return information about this raster in a string format equivalent to the output of the gdalinfo command line utility. """ + if not capi.get_ds_info: + raise ValueError('GDAL ≥ 2.1 is required for using the info property.') return capi.get_ds_info(self.ptr, None).decode() diff --git a/venv/Lib/site-packages/django/contrib/gis/gdal/srs.py b/venv/Lib/site-packages/django/contrib/gis/gdal/srs.py index 2e335fe..fd65096 100644 --- a/venv/Lib/site-packages/django/contrib/gis/gdal/srs.py +++ b/venv/Lib/site-packages/django/contrib/gis/gdal/srs.py @@ -43,14 +43,13 @@ class AxisOrder(IntEnum): class SpatialReference(GDALBase): """ - A wrapper for the OGRSpatialReference object. According to the GDAL web site, + A wrapper for the OGRSpatialReference object. According to the GDAL Web site, the SpatialReference object "provide[s] services to represent coordinate systems (projections and datums) and to transform between them." """ - destructor = capi.release_srs - def __init__(self, srs_input="", srs_type="user", axis_order=None): + def __init__(self, srs_input='', srs_type='user', axis_order=None): """ Create a GDAL OSR Spatial Reference object from the given input. The input may be string of OGC Well Known Text (WKT), an integer @@ -59,58 +58,56 @@ class SpatialReference(GDALBase): """ if not isinstance(axis_order, (type(None), AxisOrder)): raise ValueError( - "SpatialReference.axis_order must be an AxisOrder instance." + 'SpatialReference.axis_order must be an AxisOrder instance.' ) self.axis_order = axis_order or AxisOrder.TRADITIONAL - if srs_type == "wkt": - self.ptr = capi.new_srs(c_char_p(b"")) + if srs_type == 'wkt': + self.ptr = capi.new_srs(c_char_p(b'')) self.import_wkt(srs_input) if self.axis_order == AxisOrder.TRADITIONAL and GDAL_VERSION >= (3, 0): capi.set_axis_strategy(self.ptr, self.axis_order) elif self.axis_order != AxisOrder.TRADITIONAL and GDAL_VERSION < (3, 0): - raise ValueError("%s is not supported in GDAL < 3.0." % self.axis_order) + raise ValueError('%s is not supported in GDAL < 3.0.' % self.axis_order) return elif isinstance(srs_input, str): try: # If SRID is a string, e.g., '4326', then make acceptable # as user input. srid = int(srs_input) - srs_input = "EPSG:%d" % srid + srs_input = 'EPSG:%d' % srid except ValueError: pass elif isinstance(srs_input, int): # EPSG integer code was input. - srs_type = "epsg" + srs_type = 'epsg' elif isinstance(srs_input, self.ptr_type): srs = srs_input - srs_type = "ogr" + srs_type = 'ogr' else: raise TypeError('Invalid SRS type "%s"' % srs_type) - if srs_type == "ogr": + if srs_type == 'ogr': # Input is already an SRS pointer. srs = srs_input else: # Creating a new SRS pointer, using the string buffer. - buf = c_char_p(b"") + buf = c_char_p(b'') srs = capi.new_srs(buf) # If the pointer is NULL, throw an exception. if not srs: - raise SRSException( - "Could not create spatial reference from: %s" % srs_input - ) + raise SRSException('Could not create spatial reference from: %s' % srs_input) else: self.ptr = srs if self.axis_order == AxisOrder.TRADITIONAL and GDAL_VERSION >= (3, 0): capi.set_axis_strategy(self.ptr, self.axis_order) elif self.axis_order != AxisOrder.TRADITIONAL and GDAL_VERSION < (3, 0): - raise ValueError("%s is not supported in GDAL < 3.0." % self.axis_order) + raise ValueError('%s is not supported in GDAL < 3.0.' % self.axis_order) # Importing from either the user input string or an integer SRID. - if srs_type == "user": + if srs_type == 'user': self.import_user_input(srs_input) - elif srs_type == "epsg": + elif srs_type == 'epsg': self.import_epsg(srs_input) def __getitem__(self, target): @@ -131,8 +128,7 @@ class SpatialReference(GDALBase): 4326 >>> print(srs['TOWGS84', 4]) # the fourth value in this wkt 0 - >>> # For the units authority, have to use the pipe symbole. - >>> print(srs['UNIT|AUTHORITY']) + >>> print(srs['UNIT|AUTHORITY']) # For the units authority, have to use the pipe symbole. EPSG >>> print(srs['UNIT|AUTHORITY', 1]) # The authority value for the units 9122 @@ -192,11 +188,11 @@ class SpatialReference(GDALBase): def name(self): "Return the name of this Spatial Reference." if self.projected: - return self.attr_value("PROJCS") + return self.attr_value('PROJCS') elif self.geographic: - return self.attr_value("GEOGCS") + return self.attr_value('GEOGCS') elif self.local: - return self.attr_value("LOCAL_CS") + return self.attr_value('LOCAL_CS') else: return None @@ -204,7 +200,7 @@ class SpatialReference(GDALBase): def srid(self): "Return the SRID of top-level authority, or None if undefined." try: - return int(self.attr_value("AUTHORITY", 1)) + return int(self.attr_value('AUTHORITY', 1)) except (TypeError, ValueError): return None @@ -337,7 +333,7 @@ class SpatialReference(GDALBase): return self.proj @property - def xml(self, dialect=""): + def xml(self, dialect=''): "Return the XML representation of this Spatial Reference." return capi.to_xml(self.ptr, byref(c_char_p()), force_bytes(dialect)) @@ -348,10 +344,8 @@ class CoordTransform(GDALBase): def __init__(self, source, target): "Initialize on a source and target SpatialReference objects." - if not isinstance(source, SpatialReference) or not isinstance( - target, SpatialReference - ): - raise TypeError("source and target must be of type SpatialReference") + if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): + raise TypeError('source and target must be of type SpatialReference') self.ptr = capi.new_ct(source._ptr, target._ptr) self._srs1_name = source.name self._srs2_name = target.name diff --git a/venv/Lib/site-packages/django/contrib/gis/geoip2/__init__.py b/venv/Lib/site-packages/django/contrib/gis/geoip2/__init__.py index fa57b12..2d7d7a7 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geoip2/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/geoip2/__init__.py @@ -11,7 +11,7 @@ downloaded from MaxMind at http://dev.maxmind.com/geoip/geoip2/geolite2/. Grab GeoLite2-Country.mmdb.gz and GeoLite2-City.mmdb.gz, and unzip them in the directory corresponding to settings.GEOIP_PATH. """ -__all__ = ["HAS_GEOIP2"] +__all__ = ['HAS_GEOIP2'] try: import geoip2 # NOQA @@ -19,6 +19,5 @@ except ImportError: HAS_GEOIP2 = False else: from .base import GeoIP2, GeoIP2Exception - HAS_GEOIP2 = True - __all__ += ["GeoIP2", "GeoIP2Exception"] + __all__ += ['GeoIP2', 'GeoIP2Exception'] diff --git a/venv/Lib/site-packages/django/contrib/gis/geoip2/base.py b/venv/Lib/site-packages/django/contrib/gis/geoip2/base.py index 221ae1f..6b35a8a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geoip2/base.py +++ b/venv/Lib/site-packages/django/contrib/gis/geoip2/base.py @@ -11,9 +11,9 @@ from .resources import City, Country # Creating the settings dictionary with any settings, if needed. GEOIP_SETTINGS = { - "GEOIP_PATH": getattr(settings, "GEOIP_PATH", None), - "GEOIP_CITY": getattr(settings, "GEOIP_CITY", "GeoLite2-City.mmdb"), - "GEOIP_COUNTRY": getattr(settings, "GEOIP_COUNTRY", "GeoLite2-Country.mmdb"), + 'GEOIP_PATH': getattr(settings, 'GEOIP_PATH', None), + 'GEOIP_CITY': getattr(settings, 'GEOIP_CITY', 'GeoLite2-City.mmdb'), + 'GEOIP_COUNTRY': getattr(settings, 'GEOIP_COUNTRY', 'GeoLite2-Country.mmdb'), } @@ -33,13 +33,11 @@ class GeoIP2: MODE_FILE = 4 # Load database into memory. Pure Python. MODE_MEMORY = 8 - cache_options = frozenset( - (MODE_AUTO, MODE_MMAP_EXT, MODE_MMAP, MODE_FILE, MODE_MEMORY) - ) + cache_options = frozenset((MODE_AUTO, MODE_MMAP_EXT, MODE_MMAP, MODE_FILE, MODE_MEMORY)) # Paths to the city & country binary databases. - _city_file = "" - _country_file = "" + _city_file = '' + _country_file = '' # Initially, pointers to GeoIP file references are NULL. _city = None @@ -72,31 +70,29 @@ class GeoIP2: if cache in self.cache_options: self._cache = cache else: - raise GeoIP2Exception("Invalid GeoIP caching option: %s" % cache) + raise GeoIP2Exception('Invalid GeoIP caching option: %s' % cache) # Getting the GeoIP data path. - path = path or GEOIP_SETTINGS["GEOIP_PATH"] + path = path or GEOIP_SETTINGS['GEOIP_PATH'] if not path: - raise GeoIP2Exception( - "GeoIP path must be provided via parameter or the GEOIP_PATH setting." - ) + raise GeoIP2Exception('GeoIP path must be provided via parameter or the GEOIP_PATH setting.') path = to_path(path) if path.is_dir(): # Constructing the GeoIP database filenames using the settings # dictionary. If the database files for the GeoLite country # and/or city datasets exist, then try to open them. - country_db = path / (country or GEOIP_SETTINGS["GEOIP_COUNTRY"]) + country_db = path / (country or GEOIP_SETTINGS['GEOIP_COUNTRY']) if country_db.is_file(): self._country = geoip2.database.Reader(str(country_db), mode=cache) self._country_file = country_db - city_db = path / (city or GEOIP_SETTINGS["GEOIP_CITY"]) + city_db = path / (city or GEOIP_SETTINGS['GEOIP_CITY']) if city_db.is_file(): self._city = geoip2.database.Reader(str(city_db), mode=cache) self._city_file = city_db if not self._reader: - raise GeoIP2Exception("Could not load a database from %s." % path) + raise GeoIP2Exception('Could not load a database from %s.' % path) elif path.is_file(): # Otherwise, some detective work will be needed to figure out # whether the given database path is for the GeoIP country or city @@ -104,20 +100,18 @@ class GeoIP2: reader = geoip2.database.Reader(str(path), mode=cache) db_type = reader.metadata().database_type - if db_type.endswith("City"): + if db_type.endswith('City'): # GeoLite City database detected. self._city = reader self._city_file = path - elif db_type.endswith("Country"): + elif db_type.endswith('Country'): # GeoIP Country database detected. self._country = reader self._country_file = path else: - raise GeoIP2Exception( - "Unable to recognize database edition: %s" % db_type - ) + raise GeoIP2Exception('Unable to recognize database edition: %s' % db_type) else: - raise GeoIP2Exception("GeoIP path must be a valid file or directory.") + raise GeoIP2Exception('GeoIP path must be a valid file or directory.') @property def _reader(self): @@ -137,37 +131,27 @@ class GeoIP2: def __repr__(self): meta = self._reader.metadata() - version = "[v%s.%s]" % ( - meta.binary_format_major_version, - meta.binary_format_minor_version, - ) - return ( - '<%(cls)s %(version)s _country_file="%(country)s", _city_file="%(city)s">' - % { - "cls": self.__class__.__name__, - "version": version, - "country": self._country_file, - "city": self._city_file, - } - ) + version = '[v%s.%s]' % (meta.binary_format_major_version, meta.binary_format_minor_version) + return '<%(cls)s %(version)s _country_file="%(country)s", _city_file="%(city)s">' % { + 'cls': self.__class__.__name__, + 'version': version, + 'country': self._country_file, + 'city': self._city_file, + } def _check_query(self, query, country=False, city=False, city_or_country=False): "Check the query and database availability." # Making sure a string was passed in for the query. if not isinstance(query, str): - raise TypeError( - "GeoIP query must be a string, not type %s" % type(query).__name__ - ) + raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__) # Extra checks for the existence of country and city databases. if city_or_country and not (self._country or self._city): - raise GeoIP2Exception("Invalid GeoIP country and city data files.") + raise GeoIP2Exception('Invalid GeoIP country and city data files.') elif country and not self._country: - raise GeoIP2Exception( - "Invalid GeoIP country data file: %s" % self._country_file - ) + raise GeoIP2Exception('Invalid GeoIP country data file: %s' % self._country_file) elif city and not self._city: - raise GeoIP2Exception("Invalid GeoIP city data file: %s" % self._city_file) + raise GeoIP2Exception('Invalid GeoIP city data file: %s' % self._city_file) # Return the query string back to the caller. GeoIP2 only takes IP addresses. try: @@ -189,12 +173,12 @@ class GeoIP2: def country_code(self, query): "Return the country code for the given IP Address or FQDN." enc_query = self._check_query(query, city_or_country=True) - return self.country(enc_query)["country_code"] + return self.country(enc_query)['country_code'] def country_name(self, query): "Return the country name for the given IP Address or FQDN." enc_query = self._check_query(query, city_or_country=True) - return self.country(enc_query)["country_name"] + return self.country(enc_query)['country_name'] def country(self, query): """ @@ -207,7 +191,7 @@ class GeoIP2: return Country(self._country_or_city(enc_query)) # #### Coordinate retrieval routines #### - def coords(self, query, ordering=("longitude", "latitude")): + def coords(self, query, ordering=('longitude', 'latitude')): cdict = self.city(query) if cdict is None: return None @@ -220,14 +204,13 @@ class GeoIP2: def lat_lon(self, query): "Return a tuple of the (latitude, longitude) for the given query." - return self.coords(query, ("latitude", "longitude")) + return self.coords(query, ('latitude', 'longitude')) def geos(self, query): "Return a GEOS Point object for the given query." ll = self.lon_lat(query) if ll: from django.contrib.gis.geos import Point - return Point(ll, srid=4326) else: return None @@ -237,10 +220,7 @@ class GeoIP2: def info(self): "Return information about the GeoIP library and databases in use." meta = self._reader.metadata() - return "GeoIP Library:\n\t%s.%s\n" % ( - meta.binary_format_major_version, - meta.binary_format_minor_version, - ) + return 'GeoIP Library:\n\t%s.%s\n' % (meta.binary_format_major_version, meta.binary_format_minor_version) @classmethod def open(cls, full_path, cache): diff --git a/venv/Lib/site-packages/django/contrib/gis/geoip2/resources.py b/venv/Lib/site-packages/django/contrib/gis/geoip2/resources.py index 74f4228..08923b0 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geoip2/resources.py +++ b/venv/Lib/site-packages/django/contrib/gis/geoip2/resources.py @@ -1,22 +1,22 @@ def City(response): return { - "city": response.city.name, - "continent_code": response.continent.code, - "continent_name": response.continent.name, - "country_code": response.country.iso_code, - "country_name": response.country.name, - "dma_code": response.location.metro_code, - "is_in_european_union": response.country.is_in_european_union, - "latitude": response.location.latitude, - "longitude": response.location.longitude, - "postal_code": response.postal.code, - "region": response.subdivisions[0].iso_code if response.subdivisions else None, - "time_zone": response.location.time_zone, + 'city': response.city.name, + 'continent_code': response.continent.code, + 'continent_name': response.continent.name, + 'country_code': response.country.iso_code, + 'country_name': response.country.name, + 'dma_code': response.location.metro_code, + 'is_in_european_union': response.country.is_in_european_union, + 'latitude': response.location.latitude, + 'longitude': response.location.longitude, + 'postal_code': response.postal.code, + 'region': response.subdivisions[0].iso_code if response.subdivisions else None, + 'time_zone': response.location.time_zone, } def Country(response): return { - "country_code": response.country.iso_code, - "country_name": response.country.name, + 'country_code': response.country.iso_code, + 'country_name': response.country.name, } diff --git a/venv/Lib/site-packages/django/contrib/gis/geometry.py b/venv/Lib/site-packages/django/contrib/gis/geometry.py index 71e245f..815d825 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geometry.py +++ b/venv/Lib/site-packages/django/contrib/gis/geometry.py @@ -4,14 +4,14 @@ from django.utils.regex_helper import _lazy_re_compile # Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure # to prevent potentially malicious input from reaching the underlying C -# library. Not a substitute for good web security programming practices. -hex_regex = _lazy_re_compile(r"^[0-9A-F]+$", re.I) +# library. Not a substitute for good Web security programming practices. +hex_regex = _lazy_re_compile(r'^[0-9A-F]+$', re.I) wkt_regex = _lazy_re_compile( - r"^(SRID=(?P<srid>\-?\d+);)?" - r"(?P<wkt>" - r"(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|" - r"MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)" - r"[ACEGIMLONPSRUTYZ\d,\.\-\+\(\) ]+)$", - re.I, + r'^(SRID=(?P<srid>\-?\d+);)?' + r'(?P<wkt>' + r'(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|' + r'MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)' + r'[ACEGIMLONPSRUTYZ\d,\.\-\+\(\) ]+)$', + re.I ) -json_regex = _lazy_re_compile(r"^(\s+)?\{.*}(\s+)?$", re.DOTALL) +json_regex = _lazy_re_compile(r'^(\s+)?\{.*}(\s+)?$', re.DOTALL) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/__init__.py b/venv/Lib/site-packages/django/contrib/gis/geos/__init__.py index 27de1ca..65e7e54 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/__init__.py @@ -3,10 +3,7 @@ The GeoDjango GEOS module. Please consult the GeoDjango documentation for more details: https://docs.djangoproject.com/en/dev/ref/contrib/gis/geos/ """ from .collections import ( # NOQA - GeometryCollection, - MultiLineString, - MultiPoint, - MultiPolygon, + GeometryCollection, MultiLineString, MultiPoint, MultiPolygon, ) from .error import GEOSException # NOQA from .factory import fromfile, fromstr # NOQA diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/collections.py b/venv/Lib/site-packages/django/contrib/gis/geos/collections.py index abfec8a..bf584c9 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/collections.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/collections.py @@ -2,6 +2,8 @@ This module houses the Geometry Collection objects: GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon """ +from ctypes import byref, c_int, c_uint + from django.contrib.gis.geos import prototypes as capi from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin from django.contrib.gis.geos.libgeos import GEOM_PTR @@ -46,15 +48,12 @@ class GeometryCollection(GEOSGeometry): # ### Methods for compatibility with ListMixin ### def _create_collection(self, length, items): # Creating the geometry pointer array. - geoms = (GEOM_PTR * length)( - *[ - # this is a little sloppy, but makes life easier - # allow GEOSGeometry types (python wrappers) or pointer types - capi.geom_clone(getattr(g, "ptr", g)) - for g in items - ] - ) - return capi.create_collection(self._typeid, geoms, length) + geoms = (GEOM_PTR * length)(*[ + # this is a little sloppy, but makes life easier + # allow GEOSGeometry types (python wrappers) or pointer types + capi.geom_clone(getattr(g, 'ptr', g)) for g in items + ]) + return capi.create_collection(c_int(self._typeid), byref(geoms), c_uint(length)) def _get_single_internal(self, index): return capi.get_geomn(self.ptr, index) @@ -62,9 +61,7 @@ class GeometryCollection(GEOSGeometry): def _get_single_external(self, index): "Return the Geometry from this Collection at the given index (0-based)." # Checking the index and returning the corresponding GEOS geometry. - return GEOSGeometry( - capi.geom_clone(self._get_single_internal(index)), srid=self.srid - ) + return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid) def _set_list(self, length, items): "Create a new collection, and destroy the contents of the previous pointer." @@ -81,13 +78,12 @@ class GeometryCollection(GEOSGeometry): @property def kml(self): "Return the KML for this Geometry Collection." - return "<MultiGeometry>%s</MultiGeometry>" % "".join(g.kml for g in self) + return '<MultiGeometry>%s</MultiGeometry>' % ''.join(g.kml for g in self) @property def tuple(self): "Return a tuple of all the coordinates in this Geometry Collection" return tuple(g.tuple for g in self) - coords = tuple @@ -109,12 +105,4 @@ class MultiPolygon(GeometryCollection): # Setting the allowed types here since GeometryCollection is defined before # its subclasses. -GeometryCollection._allowed = ( - Point, - LineString, - LinearRing, - Polygon, - MultiPoint, - MultiLineString, - MultiPolygon, -) +GeometryCollection._allowed = (Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/coordseq.py b/venv/Lib/site-packages/django/contrib/gis/geos/coordseq.py index 07a3b7d..d6a7c6c 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/coordseq.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/coordseq.py @@ -20,7 +20,7 @@ class GEOSCoordSeq(GEOSBase): def __init__(self, ptr, z=False): "Initialize from a GEOS pointer." if not isinstance(ptr, CS_PTR): - raise TypeError("Coordinate sequence should initialize with a CS_PTR.") + raise TypeError('Coordinate sequence should initialize with a CS_PTR.') self._ptr = ptr self._z = z @@ -50,9 +50,7 @@ class GEOSCoordSeq(GEOSBase): elif numpy and isinstance(value, numpy.ndarray): pass else: - raise TypeError( - "Must set coordinate with a sequence (list, tuple, or numpy array)." - ) + raise TypeError('Must set coordinate with a sequence (list, tuple, or numpy array).') # Checking the dims of the input if self.dims == 3 and self._z: n_args = 3 @@ -61,7 +59,7 @@ class GEOSCoordSeq(GEOSBase): n_args = 2 point_setter = self._set_point_2d if len(value) != n_args: - raise TypeError("Dimension of value does not match.") + raise TypeError('Dimension of value does not match.') self._checkindex(index) point_setter(index, value) @@ -69,7 +67,7 @@ class GEOSCoordSeq(GEOSBase): def _checkindex(self, index): "Check the given index." if not (0 <= index < self.size): - raise IndexError("invalid GEOS Geometry index: %s" % index) + raise IndexError('invalid GEOS Geometry index: %s' % index) def _checkdim(self, dim): "Check the given dimension." @@ -182,13 +180,11 @@ class GEOSCoordSeq(GEOSBase): # Getting the substitution string depending on whether the coordinates have # a Z dimension. if self.hasz: - substr = "%s,%s,%s " + substr = '%s,%s,%s ' else: - substr = "%s,%s,0 " - return ( - "<coordinates>%s</coordinates>" - % "".join(substr % self[i] for i in range(len(self))).strip() - ) + substr = '%s,%s,0 ' + return '<coordinates>%s</coordinates>' % \ + ''.join(substr % self[i] for i in range(len(self))).strip() @property def tuple(self): diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/factory.py b/venv/Lib/site-packages/django/contrib/gis/geos/factory.py index 2c1c247..46ed1f0 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/factory.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/factory.py @@ -8,7 +8,7 @@ def fromfile(file_h): """ # If given a file name, get a real handle. if isinstance(file_h, str): - with open(file_h, "rb") as file_h: + with open(file_h, 'rb') as file_h: buf = file_h.read() else: buf = file_h.read() diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/geometry.py b/venv/Lib/site-packages/django/contrib/gis/geos/geometry.py index 8cee9a3..bdb3868 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/geometry.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/geometry.py @@ -14,7 +14,9 @@ from django.contrib.gis.geos.error import GEOSException from django.contrib.gis.geos.libgeos import GEOM_PTR from django.contrib.gis.geos.mutable_list import ListMixin from django.contrib.gis.geos.prepared import PreparedGeometry -from django.contrib.gis.geos.prototypes.io import ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w +from django.contrib.gis.geos.prototypes.io import ( + ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w, +) from django.utils.deconstruct import deconstructible from django.utils.encoding import force_bytes, force_str @@ -36,15 +38,12 @@ class GEOSGeometryBase(GEOSBase): if GEOSGeometryBase._GEOS_CLASSES is None: # Inner imports avoid import conflicts with GEOSGeometry. from .collections import ( - GeometryCollection, - MultiLineString, - MultiPoint, + GeometryCollection, MultiLineString, MultiPoint, MultiPolygon, ) from .linestring import LinearRing, LineString from .point import Point from .polygon import Polygon - GEOSGeometryBase._GEOS_CLASSES = { 0: Point, 1: LineString, @@ -63,9 +62,7 @@ class GEOSGeometryBase(GEOSBase): "Perform post-initialization setup." # Setting the coordinate sequence for the geometry (will be None on # geometries that do not have coordinate sequences) - self._cs = ( - GEOSCoordSeq(capi.get_cs(self.ptr), self.hasz) if self.has_cs else None - ) + self._cs = GEOSCoordSeq(capi.get_cs(self.ptr), self.hasz) if self.has_cs else None def __copy__(self): """ @@ -88,7 +85,7 @@ class GEOSGeometryBase(GEOSBase): def __repr__(self): "Short-hand representation because WKT may be very large." - return "<%s object at %s>" % (self.geom_type, hex(addressof(self.ptr))) + return '<%s object at %s>' % (self.geom_type, hex(addressof(self.ptr))) # Pickling support def _to_pickle_wkb(self): @@ -107,7 +104,7 @@ class GEOSGeometryBase(GEOSBase): wkb, srid = state ptr = self._from_pickle_wkb(wkb) if not ptr: - raise GEOSException("Invalid Geometry loaded from pickled state.") + raise GEOSException('Invalid Geometry loaded from pickled state.') self.ptr = ptr self._post_init() self.srid = srid @@ -120,17 +117,17 @@ class GEOSGeometryBase(GEOSBase): def from_ewkt(ewkt): ewkt = force_bytes(ewkt) srid = None - parts = ewkt.split(b";", 1) + parts = ewkt.split(b';', 1) if len(parts) == 2: srid_part, wkt = parts - match = re.match(rb"SRID=(?P<srid>\-?\d+)", srid_part) + match = re.match(br'SRID=(?P<srid>\-?\d+)', srid_part) if not match: - raise ValueError("EWKT has invalid SRID part.") - srid = int(match["srid"]) + raise ValueError('EWKT has invalid SRID part.') + srid = int(match['srid']) else: wkt = ewkt if not wkt: - raise ValueError("Expected WKT but got an empty string.") + raise ValueError('Expected WKT but got an empty string.') return GEOSGeometry(GEOSGeometry._from_wkt(wkt), srid=srid) @staticmethod @@ -152,11 +149,7 @@ class GEOSGeometryBase(GEOSBase): other = GEOSGeometry.from_ewkt(other) except (ValueError, GEOSException): return False - return ( - isinstance(other, GEOSGeometry) - and self.srid == other.srid - and self.equals_exact(other) - ) + return isinstance(other, GEOSGeometry) and self.srid == other.srid and self.equals_exact(other) def __hash__(self): return hash((self.srid, self.wkt)) @@ -321,7 +314,7 @@ class GEOSGeometryBase(GEOSBase): two Geometries match the elements in pattern. """ if not isinstance(pattern, str) or len(pattern) > 9: - raise GEOSException("invalid intersection matrix pattern") + raise GEOSException('invalid intersection matrix pattern') return capi.geos_relatepattern(self.ptr, other.ptr, force_bytes(pattern)) def touches(self, other): @@ -360,7 +353,7 @@ class GEOSGeometryBase(GEOSBase): Return the EWKT (SRID + WKT) of the Geometry. """ srid = self.srid - return "SRID=%s;%s" % (srid, self.wkt) if srid else self.wkt + return 'SRID=%s;%s' % (srid, self.wkt) if srid else self.wkt @property def wkt(self): @@ -393,7 +386,6 @@ class GEOSGeometryBase(GEOSBase): Return GeoJSON representation of this Geometry. """ return self.ogr.json - geojson = json @property @@ -418,7 +410,7 @@ class GEOSGeometryBase(GEOSBase): def kml(self): "Return the KML representation of this Geometry." gtype = self.geom_type - return "<%s>%s</%s>" % (gtype, self.coord_seq.kml, gtype) + return '<%s>%s</%s>' % (gtype, self.coord_seq.kml, gtype) @property def prepared(self): @@ -492,7 +484,7 @@ class GEOSGeometryBase(GEOSBase): self._post_init() self.srid = g.srid else: - raise GEOSException("Transformed WKB was invalid.") + raise GEOSException('Transformed WKB was invalid.') # #### Topology Routines #### def _topology(self, gptr): @@ -514,9 +506,7 @@ class GEOSGeometryBase(GEOSBase): """ return self._topology(capi.geos_buffer(self.ptr, width, quadsegs)) - def buffer_with_style( - self, width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0 - ): + def buffer_with_style(self, width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0): """ Same as buffer() but allows customizing the style of the buffer. @@ -525,9 +515,7 @@ class GEOSGeometryBase(GEOSBase): Mitre ratio limit only affects mitered join style. """ return self._topology( - capi.geos_bufferwithstyle( - self.ptr, width, quadsegs, end_cap_style, join_style, mitre_limit - ), + capi.geos_bufferwithstyle(self.ptr, width, quadsegs, end_cap_style, join_style, mitre_limit), ) @property @@ -618,7 +606,7 @@ class GEOSGeometryBase(GEOSBase): the Geometry. """ if not isinstance(other, GEOSGeometry): - raise TypeError("distance() works only on other GEOS Geometries.") + raise TypeError('distance() works only on other GEOS Geometries.') return capi.geos_distance(self.ptr, other.ptr, byref(c_double())) @property @@ -628,7 +616,6 @@ class GEOSGeometryBase(GEOSBase): (xmin, ymin, xmax, ymax). """ from .point import Point - env = self.envelope if isinstance(env, Point): xmin, ymin = env.tuple @@ -655,7 +642,6 @@ class LinearGeometryMixin: """ Used for LineString and MultiLineString. """ - def interpolate(self, distance): return self._topology(capi.geos_interpolate(self.ptr, distance)) @@ -664,16 +650,14 @@ class LinearGeometryMixin: def project(self, point): from .point import Point - if not isinstance(point, Point): - raise TypeError("locate_point argument must be a Point") + raise TypeError('locate_point argument must be a Point') return capi.geos_project(self.ptr, point.ptr) def project_normalized(self, point): from .point import Point - if not isinstance(point, Point): - raise TypeError("locate_point argument must be a Point") + raise TypeError('locate_point argument must be a Point') return capi.geos_project_normalized(self.ptr, point.ptr) @property @@ -717,9 +701,9 @@ class GEOSGeometry(GEOSGeometryBase, ListMixin): wkt_m = wkt_regex.match(geo_input) if wkt_m: # Handle WKT input. - if wkt_m["srid"]: - input_srid = int(wkt_m["srid"]) - g = self._from_wkt(force_bytes(wkt_m["wkt"])) + if wkt_m['srid']: + input_srid = int(wkt_m['srid']) + g = self._from_wkt(force_bytes(wkt_m['wkt'])) elif hex_regex.match(geo_input): # Handle HEXEWKB input. g = wkb_r().read(force_bytes(geo_input)) @@ -729,7 +713,7 @@ class GEOSGeometry(GEOSGeometryBase, ListMixin): g = ogr._geos_ptr() input_srid = ogr.srid else: - raise ValueError("String input unrecognized as WKT EWKT, and HEXEWKB.") + raise ValueError('String input unrecognized as WKT EWKT, and HEXEWKB.') elif isinstance(geo_input, GEOM_PTR): # When the input is a pointer to a geometry (GEOM_PTR). g = geo_input @@ -739,14 +723,14 @@ class GEOSGeometry(GEOSGeometryBase, ListMixin): elif isinstance(geo_input, GEOSGeometry): g = capi.geom_clone(geo_input.ptr) else: - raise TypeError("Improper geometry input type: %s" % type(geo_input)) + raise TypeError('Improper geometry input type: %s' % type(geo_input)) if not g: - raise GEOSException("Could not initialize GEOS Geometry with given input.") + raise GEOSException('Could not initialize GEOS Geometry with given input.') input_srid = input_srid or capi.geos_get_srid(g) or None if input_srid and srid and input_srid != srid: - raise ValueError("Input geometry already has SRID: %d." % input_srid) + raise ValueError('Input geometry already has SRID: %d.' % input_srid) super().__init__(g, None) # Set the SRID, if given. diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/io.py b/venv/Lib/site-packages/django/contrib/gis/geos/io.py index d789806..15027ff 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/io.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/io.py @@ -5,13 +5,10 @@ reader and writer classes. """ from django.contrib.gis.geos.geometry import GEOSGeometry from django.contrib.gis.geos.prototypes.io import ( - WKBWriter, - WKTWriter, - _WKBReader, - _WKTReader, + WKBWriter, WKTWriter, _WKBReader, _WKTReader, ) -__all__ = ["WKBWriter", "WKTWriter", "WKBReader", "WKTReader"] +__all__ = ['WKBWriter', 'WKTWriter', 'WKBReader', 'WKTReader'] # Public classes for (WKB|WKT)Reader, which return GEOSGeometry diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/libgeos.py b/venv/Lib/site-packages/django/contrib/gis/geos/libgeos.py index 0002bef..2cdb5d3 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/libgeos.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/libgeos.py @@ -15,14 +15,13 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.functional import SimpleLazyObject, cached_property from django.utils.version import get_version_tuple -logger = logging.getLogger("django.contrib.gis") +logger = logging.getLogger('django.contrib.gis') def load_geos(): # Custom library path set? try: from django.conf import settings - lib_path = settings.GEOS_LIBRARY_PATH except (AttributeError, ImportError, ImproperlyConfigured, OSError): lib_path = None @@ -30,12 +29,12 @@ def load_geos(): # Setting the appropriate names for the GEOS-C library. if lib_path: lib_names = None - elif os.name == "nt": + elif os.name == 'nt': # Windows NT libraries - lib_names = ["geos_c", "libgeos_c-1"] - elif os.name == "posix": + lib_names = ['geos_c', 'libgeos_c-1'] + elif os.name == 'posix': # *NIX libraries - lib_names = ["geos_c", "GEOS"] + lib_names = ['geos_c', 'GEOS'] else: raise ImportError('Unsupported OS "%s"' % os.name) @@ -52,7 +51,8 @@ def load_geos(): if lib_path is None: raise ImportError( 'Could not find the GEOS library (tried "%s"). ' - "Try setting GEOS_LIBRARY_PATH in your settings." % '", "'.join(lib_names) + 'Try setting GEOS_LIBRARY_PATH in your settings.' % + '", "'.join(lib_names) ) # Getting the GEOS C library. The C interface (CDLL) is used for # both *NIX and Windows. @@ -82,7 +82,7 @@ def notice_h(fmt, lst): warn_msg = fmt % lst except TypeError: warn_msg = fmt - logger.warning("GEOS_NOTICE: %s\n", warn_msg) + logger.warning('GEOS_NOTICE: %s\n', warn_msg) notice_h = NOTICEFUNC(notice_h) @@ -96,7 +96,7 @@ def error_h(fmt, lst): err_msg = fmt % lst except TypeError: err_msg = fmt - logger.error("GEOS_ERROR: %s\n", err_msg) + logger.error('GEOS_ERROR: %s\n', err_msg) error_h = ERRORFUNC(error_h) @@ -135,7 +135,6 @@ class GEOSFuncFactory: """ Lazy loading of GEOS functions. """ - argtypes = None restype = None errcheck = None @@ -155,7 +154,6 @@ class GEOSFuncFactory: @cached_property def func(self): from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc - func = GEOSFunc(self.func_name) func.argtypes = self.argtypes or [] func.restype = self.restype diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/linestring.py b/venv/Lib/site-packages/django/contrib/gis/geos/linestring.py index 78a265c..a85ecff 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/linestring.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/linestring.py @@ -29,15 +29,11 @@ class LineString(LinearGeometryMixin, GEOSGeometry): else: coords = args - if not ( - isinstance(coords, (tuple, list)) - or numpy - and isinstance(coords, numpy.ndarray) - ): - raise TypeError("Invalid initialization input for LineStrings.") + if not (isinstance(coords, (tuple, list)) or numpy and isinstance(coords, numpy.ndarray)): + raise TypeError('Invalid initialization input for LineStrings.') # If SRID was passed in with the keyword arguments - srid = kwargs.get("srid") + srid = kwargs.get('srid') ncoords = len(coords) if not ncoords: @@ -46,8 +42,7 @@ class LineString(LinearGeometryMixin, GEOSGeometry): if ncoords < self._minlength: raise ValueError( - "%s requires at least %d points, got %s." - % ( + '%s requires at least %d points, got %s.' % ( self.__class__.__name__, self._minlength, ncoords, @@ -58,7 +53,7 @@ class LineString(LinearGeometryMixin, GEOSGeometry): if numpy_coords: shape = coords.shape # Using numpy's shape. if len(shape) != 2: - raise TypeError("Too many dimensions.") + raise TypeError('Too many dimensions.') self._checkdim(shape[1]) ndim = shape[1] else: @@ -68,15 +63,13 @@ class LineString(LinearGeometryMixin, GEOSGeometry): # Incrementing through each of the coordinates and verifying for coord in coords: if not isinstance(coord, (tuple, list, Point)): - raise TypeError( - "Each coordinate should be a sequence (list or tuple)" - ) + raise TypeError('Each coordinate should be a sequence (list or tuple)') if ndim is None: ndim = len(coord) self._checkdim(ndim) elif len(coord) != ndim: - raise TypeError("Dimension mismatch.") + raise TypeError('Dimension mismatch.') # Creating a coordinate sequence object because it is easier to # set the points using its methods. @@ -129,21 +122,20 @@ class LineString(LinearGeometryMixin, GEOSGeometry): self._post_init() else: # can this happen? - raise GEOSException("Geometry resulting from slice deletion was invalid.") + raise GEOSException('Geometry resulting from slice deletion was invalid.') def _set_single(self, index, value): self._cs[index] = value def _checkdim(self, dim): if dim not in (2, 3): - raise TypeError("Dimension mismatch.") + raise TypeError('Dimension mismatch.') # #### Sequence Properties #### @property def tuple(self): "Return a tuple version of the geometry from the coordinate sequence." return self._cs.tuple - coords = tuple def _listarr(self, func): @@ -189,5 +181,7 @@ class LinearRing(LineString): @property def is_counterclockwise(self): if self.empty: - raise ValueError("Orientation of an empty LinearRing cannot be determined.") + raise ValueError( + 'Orientation of an empty LinearRing cannot be determined.' + ) return self._cs.is_counterclockwise diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/mutable_list.py b/venv/Lib/site-packages/django/contrib/gis/geos/mutable_list.py index 36131fe..b04a212 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/mutable_list.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/mutable_list.py @@ -60,10 +60,10 @@ class ListMixin: # ### Python initialization and special list interface methods ### def __init__(self, *args, **kwargs): - if not hasattr(self, "_get_single_internal"): + if not hasattr(self, '_get_single_internal'): self._get_single_internal = self._get_single_external - if not hasattr(self, "_set_single"): + if not hasattr(self, '_set_single'): self._set_single = self._set_single_rebuild self._assign_extended_slice = self._assign_extended_slice_rebuild @@ -72,9 +72,7 @@ class ListMixin: def __getitem__(self, index): "Get the item(s) at the specified index/slice." if isinstance(index, slice): - return [ - self._get_single_external(i) for i in range(*index.indices(len(self))) - ] + return [self._get_single_external(i) for i in range(*index.indices(len(self)))] else: index = self._checkindex(index) return self._get_single_external(index) @@ -93,9 +91,9 @@ class ListMixin: indexRange = range(*index.indices(origLen)) newLen = origLen - len(indexRange) - newItems = ( - self._get_single_internal(i) for i in range(origLen) if i not in indexRange - ) + newItems = (self._get_single_internal(i) + for i in range(origLen) + if i not in indexRange) self._rebuild(newLen, newItems) @@ -110,28 +108,28 @@ class ListMixin: # ### Special methods for arithmetic operations ### def __add__(self, other): - "add another list-like object" + 'add another list-like object' return self.__class__([*self, *other]) def __radd__(self, other): - "add to another list-like object" + 'add to another list-like object' return other.__class__([*other, *self]) def __iadd__(self, other): - "add another list-like object to self" + 'add another list-like object to self' self.extend(other) return self def __mul__(self, n): - "multiply" + 'multiply' return self.__class__(list(self) * n) def __rmul__(self, n): - "multiply" + 'multiply' return self.__class__(list(self) * n) def __imul__(self, n): - "multiply" + 'multiply' if n <= 0: del self[:] else: @@ -181,16 +179,16 @@ class ListMixin: for i in range(0, len(self)): if self[i] == val: return i - raise ValueError("%s not found in object" % val) + raise ValueError('%s not found in object' % val) # ## Mutating ## def append(self, val): "Standard list append method" - self[len(self) :] = [val] + self[len(self):] = [val] def extend(self, vals): "Standard list extend method" - self[len(self) :] = vals + self[len(self):] = vals def insert(self, index, val): "Standard list insert method" @@ -219,9 +217,9 @@ class ListMixin: # ### Private routines ### def _rebuild(self, newLen, newItems): if newLen and newLen < self._minlength: - raise ValueError("Must have at least %d items" % self._minlength) + raise ValueError('Must have at least %d items' % self._minlength) if self._maxlength is not None and newLen > self._maxlength: - raise ValueError("Cannot have more than %d items" % self._maxlength) + raise ValueError('Cannot have more than %d items' % self._maxlength) self._set_list(newLen, newItems) @@ -234,19 +232,19 @@ class ListMixin: return index if -length <= index < 0: return index + length - raise IndexError("invalid index: %s" % index) + raise IndexError('invalid index: %s' % index) def _check_allowed(self, items): - if hasattr(self, "_allowed"): + if hasattr(self, '_allowed'): if False in [isinstance(val, self._allowed) for val in items]: - raise TypeError("Invalid type encountered in the arguments.") + raise TypeError('Invalid type encountered in the arguments.') def _set_slice(self, index, values): "Assign values to a slice of the object" try: valueList = list(values) except TypeError: - raise TypeError("can only assign an iterable to a slice") + raise TypeError('can only assign an iterable to a slice') self._check_allowed(valueList) @@ -261,14 +259,13 @@ class ListMixin: self._assign_extended_slice(start, stop, step, valueList) def _assign_extended_slice_rebuild(self, start, stop, step, valueList): - "Assign an extended slice by rebuilding entire list" + 'Assign an extended slice by rebuilding entire list' indexList = range(start, stop, step) # extended slice, only allow assigning slice of same size if len(valueList) != len(indexList): - raise ValueError( - "attempt to assign sequence of size %d " - "to extended slice of size %d" % (len(valueList), len(indexList)) - ) + raise ValueError('attempt to assign sequence of size %d ' + 'to extended slice of size %d' + % (len(valueList), len(indexList))) # we're not changing the length of the sequence newLen = len(self) @@ -284,20 +281,19 @@ class ListMixin: self._rebuild(newLen, newItems()) def _assign_extended_slice(self, start, stop, step, valueList): - "Assign an extended slice by re-assigning individual items" + 'Assign an extended slice by re-assigning individual items' indexList = range(start, stop, step) # extended slice, only allow assigning slice of same size if len(valueList) != len(indexList): - raise ValueError( - "attempt to assign sequence of size %d " - "to extended slice of size %d" % (len(valueList), len(indexList)) - ) + raise ValueError('attempt to assign sequence of size %d ' + 'to extended slice of size %d' + % (len(valueList), len(indexList))) for i, val in zip(indexList, valueList): self._set_single(i, val) def _assign_simple_slice(self, start, stop, valueList): - "Assign a simple slice; Can assign slice of any length" + 'Assign a simple slice; Can assign slice of any length' origLen = len(self) stop = max(start, stop) newLen = origLen - stop + start + len(valueList) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/point.py b/venv/Lib/site-packages/django/contrib/gis/geos/point.py index 06b2668..00fdd96 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/point.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/point.py @@ -32,7 +32,7 @@ class Point(GEOSGeometry): else: coords = [x, y] else: - raise TypeError("Invalid parameters given for Point initialization.") + raise TypeError('Invalid parameters given for Point initialization.') point = self._create_point(len(coords), coords) @@ -47,9 +47,7 @@ class Point(GEOSGeometry): return self._create_empty() if wkb is None else super()._from_pickle_wkb(wkb) def _ogr_ptr(self): - return ( - gdal.geometries.Point._create_empty() if self.empty else super()._ogr_ptr() - ) + return gdal.geometries.Point._create_empty() if self.empty else super()._ogr_ptr() @classmethod def _create_empty(cls): @@ -64,7 +62,7 @@ class Point(GEOSGeometry): return capi.create_point(None) if ndim < 2 or ndim > 3: - raise TypeError("Invalid point dimension: %s" % ndim) + raise TypeError('Invalid point dimension: %s' % ndim) cs = capi.create_cs(c_uint(1), c_uint(ndim)) i = iter(coords) @@ -86,7 +84,7 @@ class Point(GEOSGeometry): self._post_init() else: # can this happen? - raise GEOSException("Geometry resulting from slice deletion was invalid.") + raise GEOSException('Geometry resulting from slice deletion was invalid.') def _set_single(self, index, value): self._cs.setOrdinate(index, 0, value) @@ -144,7 +142,7 @@ class Point(GEOSGeometry): def z(self, value): "Set the Z component of the Point." if not self.hasz: - raise GEOSException("Cannot set Z on 2D Point.") + raise GEOSException('Cannot set Z on 2D Point.') self._cs.setOrdinate(2, 0, value) # ### Tuple setting and retrieval routines. ### diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/polygon.py b/venv/Lib/site-packages/django/contrib/gis/geos/polygon.py index e384614..d857bf0 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/polygon.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/polygon.py @@ -1,3 +1,5 @@ +from ctypes import byref, c_uint + from django.contrib.gis.geos import prototypes as capi from django.contrib.gis.geos.geometry import GEOSGeometry from django.contrib.gis.geos.libgeos import GEOM_PTR @@ -32,8 +34,7 @@ class Polygon(GEOSGeometry): ext_ring, *init_holes = args n_holes = len(init_holes) - # If initialized as Polygon(shell, (LinearRing, LinearRing)) - # [for backward-compatibility] + # If initialized as Polygon(shell, (LinearRing, LinearRing)) [for backward-compatibility] if n_holes == 1 and isinstance(init_holes[0], (tuple, list)): if not init_holes[0]: init_holes = () @@ -60,10 +61,8 @@ class Polygon(GEOSGeometry): x0, y0, x1, y1 = bbox for z in bbox: if not isinstance(z, (float, int)): - return GEOSGeometry( - "POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))" - % (x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) - ) + return GEOSGeometry('POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % + (x0, y0, x0, y1, x1, y1, x1, y0, x0, y0)) return Polygon(((x0, y0), (x0, y1), (x1, y1), (x1, y0), (x0, y0))) # ### These routines are needed for list-like operation w/ListMixin ### @@ -86,11 +85,12 @@ class Polygon(GEOSGeometry): n_holes = length - 1 if n_holes: - holes_param = (GEOM_PTR * n_holes)(*[self._clone(r) for r in rings]) + holes = (GEOM_PTR * n_holes)(*[self._clone(r) for r in rings]) + holes_param = byref(holes) else: holes_param = None - return capi.create_polygon(shell, holes_param, n_holes) + return capi.create_polygon(shell, holes_param, c_uint(n_holes)) def _clone(self, g): if isinstance(g, GEOM_PTR): @@ -98,14 +98,8 @@ class Polygon(GEOSGeometry): else: return capi.geom_clone(g.ptr) - def _construct_ring( - self, - param, - msg=( - "Parameter must be a sequence of LinearRings or objects that can " - "initialize to LinearRings" - ), - ): + def _construct_ring(self, param, msg=( + 'Parameter must be a sequence of LinearRings or objects that can initialize to LinearRings')): "Try to construct a ring from the given parameter." if isinstance(param, LinearRing): return param @@ -144,9 +138,7 @@ class Polygon(GEOSGeometry): return capi.get_intring(self.ptr, index - 1) def _get_single_external(self, index): - return GEOSGeometry( - capi.geom_clone(self._get_single_internal(index)), srid=self.srid - ) + return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid) _set_single = GEOSGeometry._set_single_rebuild _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild @@ -174,17 +166,13 @@ class Polygon(GEOSGeometry): def tuple(self): "Get the tuple for each ring in this Polygon." return tuple(self[i].tuple for i in range(len(self))) - coords = tuple @property def kml(self): "Return the KML representation of this Polygon." - inner_kml = "".join( + inner_kml = ''.join( "<innerBoundaryIs>%s</innerBoundaryIs>" % self[i + 1].kml for i in range(self.num_interior_rings) ) - return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % ( - self[0].kml, - inner_kml, - ) + return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prepared.py b/venv/Lib/site-packages/django/contrib/gis/geos/prepared.py index 9c77d8a..789432f 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prepared.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prepared.py @@ -8,7 +8,6 @@ class PreparedGeometry(GEOSBase): At the moment this includes the contains covers, and intersects operations. """ - ptr_type = capi.PREPGEOM_PTR destructor = capi.prepared_destroy @@ -18,7 +17,6 @@ class PreparedGeometry(GEOSBase): # See #21662 self._base_geom = geom from .geometry import GEOSGeometry - if not isinstance(geom, GEOSGeometry): raise TypeError self.ptr = capi.geos_prepare(geom.ptr) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/__init__.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/__init__.py index 0081a5a..844ae4c 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/__init__.py @@ -5,61 +5,22 @@ """ from django.contrib.gis.geos.prototypes.coordseq import ( # NOQA - create_cs, - cs_clone, - cs_getdims, - cs_getordinate, - cs_getsize, - cs_getx, - cs_gety, - cs_getz, - cs_is_ccw, - cs_setordinate, - cs_setx, - cs_sety, - cs_setz, + create_cs, cs_clone, cs_getdims, cs_getordinate, cs_getsize, cs_getx, + cs_gety, cs_getz, cs_is_ccw, cs_setordinate, cs_setx, cs_sety, cs_setz, get_cs, ) from django.contrib.gis.geos.prototypes.geom import ( # NOQA - create_collection, - create_empty_polygon, - create_linearring, - create_linestring, - create_point, - create_polygon, - destroy_geom, - geom_clone, - geos_get_srid, - geos_normalize, - geos_set_srid, - geos_type, - geos_typeid, - get_dims, - get_extring, - get_geomn, - get_intring, - get_nrings, - get_num_coords, + create_collection, create_empty_polygon, create_linearring, + create_linestring, create_point, create_polygon, destroy_geom, geom_clone, + geos_get_srid, geos_normalize, geos_set_srid, geos_type, geos_typeid, + get_dims, get_extring, get_geomn, get_intring, get_nrings, get_num_coords, get_num_geoms, ) from django.contrib.gis.geos.prototypes.misc import * # NOQA from django.contrib.gis.geos.prototypes.predicates import ( # NOQA - geos_contains, - geos_covers, - geos_crosses, - geos_disjoint, - geos_equals, - geos_equalsexact, - geos_hasz, - geos_intersects, - geos_isclosed, - geos_isempty, - geos_isring, - geos_issimple, - geos_isvalid, - geos_overlaps, - geos_relatepattern, - geos_touches, - geos_within, + geos_contains, geos_covers, geos_crosses, geos_disjoint, geos_equals, + geos_equalsexact, geos_hasz, geos_intersects, geos_isclosed, geos_isempty, + geos_isring, geos_issimple, geos_isvalid, geos_overlaps, + geos_relatepattern, geos_touches, geos_within, ) from django.contrib.gis.geos.prototypes.topology import * # NOQA diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/coordseq.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/coordseq.py index ed05de9..aab5d3e 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/coordseq.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/coordseq.py @@ -1,14 +1,16 @@ from ctypes import POINTER, c_byte, c_double, c_int, c_uint from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, GEOSFuncFactory -from django.contrib.gis.geos.prototypes.errcheck import GEOSException, last_arg_byref +from django.contrib.gis.geos.prototypes.errcheck import ( + GEOSException, last_arg_byref, +) # ## Error-checking routines specific to coordinate sequences. ## def check_cs_op(result, func, cargs): "Check the status code of a coordinate sequence operation." if result == 0: - raise GEOSException("Could not set value on coordinate sequence") + raise GEOSException('Could not set value on coordinate sequence') else: return result @@ -47,9 +49,7 @@ class CsOperation(GEOSFuncFactory): else: argtypes = [CS_PTR, c_uint, dbl_param] - super().__init__( - *args, **{**kwargs, "errcheck": errcheck, "argtypes": argtypes} - ) + super().__init__(*args, **{**kwargs, 'errcheck': errcheck, 'argtypes': argtypes}) class CsOutput(GEOSFuncFactory): @@ -59,7 +59,7 @@ class CsOutput(GEOSFuncFactory): def errcheck(result, func, cargs): if not result: raise GEOSException( - "Error encountered checking Coordinate Sequence returned from GEOS " + 'Error encountered checking Coordinate Sequence returned from GEOS ' 'C function "%s".' % func.__name__ ) return result @@ -68,28 +68,26 @@ class CsOutput(GEOSFuncFactory): # ## Coordinate Sequence ctypes prototypes ## # Coordinate Sequence constructors & cloning. -cs_clone = CsOutput("GEOSCoordSeq_clone", argtypes=[CS_PTR]) -create_cs = CsOutput("GEOSCoordSeq_create", argtypes=[c_uint, c_uint]) -get_cs = CsOutput("GEOSGeom_getCoordSeq", argtypes=[GEOM_PTR]) +cs_clone = CsOutput('GEOSCoordSeq_clone', argtypes=[CS_PTR]) +create_cs = CsOutput('GEOSCoordSeq_create', argtypes=[c_uint, c_uint]) +get_cs = CsOutput('GEOSGeom_getCoordSeq', argtypes=[GEOM_PTR]) # Getting, setting ordinate -cs_getordinate = CsOperation("GEOSCoordSeq_getOrdinate", ordinate=True, get=True) -cs_setordinate = CsOperation("GEOSCoordSeq_setOrdinate", ordinate=True) +cs_getordinate = CsOperation('GEOSCoordSeq_getOrdinate', ordinate=True, get=True) +cs_setordinate = CsOperation('GEOSCoordSeq_setOrdinate', ordinate=True) # For getting, x, y, z -cs_getx = CsOperation("GEOSCoordSeq_getX", get=True) -cs_gety = CsOperation("GEOSCoordSeq_getY", get=True) -cs_getz = CsOperation("GEOSCoordSeq_getZ", get=True) +cs_getx = CsOperation('GEOSCoordSeq_getX', get=True) +cs_gety = CsOperation('GEOSCoordSeq_getY', get=True) +cs_getz = CsOperation('GEOSCoordSeq_getZ', get=True) # For setting, x, y, z -cs_setx = CsOperation("GEOSCoordSeq_setX") -cs_sety = CsOperation("GEOSCoordSeq_setY") -cs_setz = CsOperation("GEOSCoordSeq_setZ") +cs_setx = CsOperation('GEOSCoordSeq_setX') +cs_sety = CsOperation('GEOSCoordSeq_setY') +cs_setz = CsOperation('GEOSCoordSeq_setZ') # These routines return size & dimensions. -cs_getsize = CsInt("GEOSCoordSeq_getSize") -cs_getdims = CsInt("GEOSCoordSeq_getDimensions") +cs_getsize = CsInt('GEOSCoordSeq_getSize') +cs_getdims = CsInt('GEOSCoordSeq_getDimensions') -cs_is_ccw = GEOSFuncFactory( - "GEOSCoordSeq_isCCW", restype=c_int, argtypes=[CS_PTR, POINTER(c_byte)] -) +cs_is_ccw = GEOSFuncFactory('GEOSCoordSeq_isCCW', restype=c_int, argtypes=[CS_PTR, POINTER(c_byte)]) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/errcheck.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/errcheck.py index a527f51..7d5f842 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/errcheck.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/errcheck.py @@ -8,7 +8,7 @@ from django.contrib.gis.geos.libgeos import GEOSFuncFactory # Getting the `free` routine used to free the memory allocated for # string pointers returned by GEOS. -free = GEOSFuncFactory("GEOSFree") +free = GEOSFuncFactory('GEOSFree') free.argtypes = [c_void_p] @@ -29,19 +29,14 @@ def check_dbl(result, func, cargs): def check_geom(result, func, cargs): "Error checking on routines that return Geometries." if not result: - raise GEOSException( - 'Error encountered checking Geometry returned from GEOS C function "%s".' - % func.__name__ - ) + raise GEOSException('Error encountered checking Geometry returned from GEOS C function "%s".' % func.__name__) return result def check_minus_one(result, func, cargs): "Error checking on routines that should not return -1." if result == -1: - raise GEOSException( - 'Error encountered in GEOS C function "%s".' % func.__name__ - ) + raise GEOSException('Error encountered in GEOS C function "%s".' % func.__name__) else: return result @@ -53,9 +48,7 @@ def check_predicate(result, func, cargs): elif result == 0: return False else: - raise GEOSException( - 'Error encountered on GEOS C predicate function "%s".' % func.__name__ - ) + raise GEOSException('Error encountered on GEOS C predicate function "%s".' % func.__name__) def check_sized_string(result, func, cargs): @@ -65,9 +58,7 @@ def check_sized_string(result, func, cargs): This frees the memory allocated by GEOS at the result pointer. """ if not result: - raise GEOSException( - 'Invalid string pointer returned by GEOS C function "%s"' % func.__name__ - ) + raise GEOSException('Invalid string pointer returned by GEOS C function "%s"' % func.__name__) # A c_size_t object is passed in by reference for the second # argument on these routines, and its needed to determine the # correct size. @@ -84,10 +75,7 @@ def check_string(result, func, cargs): This frees the memory allocated by GEOS at the result pointer. """ if not result: - raise GEOSException( - 'Error encountered checking string return value in GEOS C function "%s".' - % func.__name__ - ) + raise GEOSException('Error encountered checking string return value in GEOS C function "%s".' % func.__name__) # Getting the string value at the pointer address. s = string_at(result) # Freeing the memory allocated within GEOS diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/geom.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/geom.py index fa40a0b..a84f710 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/geom.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/geom.py @@ -1,10 +1,8 @@ -from ctypes import POINTER, c_char_p, c_int, c_ubyte, c_uint +from ctypes import POINTER, c_char_p, c_int, c_ubyte from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, GEOSFuncFactory from django.contrib.gis.geos.prototypes.errcheck import ( - check_geom, - check_minus_one, - check_string, + check_geom, check_minus_one, check_string, ) # This is the return type used by binary output (WKB, HEX) routines. @@ -46,45 +44,38 @@ class StringFromGeom(GEOSFuncFactory): # ### ctypes prototypes ### # The GEOS geometry type, typeid, num_coordinates and number of geometries -geos_normalize = IntFromGeom("GEOSNormalize") -geos_type = StringFromGeom("GEOSGeomType") -geos_typeid = IntFromGeom("GEOSGeomTypeId") -get_dims = GEOSFuncFactory("GEOSGeom_getDimensions", argtypes=[GEOM_PTR], restype=c_int) -get_num_coords = IntFromGeom("GEOSGetNumCoordinates") -get_num_geoms = IntFromGeom("GEOSGetNumGeometries") +geos_normalize = IntFromGeom('GEOSNormalize') +geos_type = StringFromGeom('GEOSGeomType') +geos_typeid = IntFromGeom('GEOSGeomTypeId') +get_dims = GEOSFuncFactory('GEOSGeom_getDimensions', argtypes=[GEOM_PTR], restype=c_int) +get_num_coords = IntFromGeom('GEOSGetNumCoordinates') +get_num_geoms = IntFromGeom('GEOSGetNumGeometries') # Geometry creation factories -create_point = GeomOutput("GEOSGeom_createPoint", argtypes=[CS_PTR]) -create_linestring = GeomOutput("GEOSGeom_createLineString", argtypes=[CS_PTR]) -create_linearring = GeomOutput("GEOSGeom_createLinearRing", argtypes=[CS_PTR]) +create_point = GeomOutput('GEOSGeom_createPoint', argtypes=[CS_PTR]) +create_linestring = GeomOutput('GEOSGeom_createLineString', argtypes=[CS_PTR]) +create_linearring = GeomOutput('GEOSGeom_createLinearRing', argtypes=[CS_PTR]) -# Polygon and collection creation routines need argument types defined -# for compatibility with some platforms, e.g. macOS ARM64. With argtypes -# defined, arrays are automatically cast and byref() calls are not needed. -create_polygon = GeomOutput( - "GEOSGeom_createPolygon", - argtypes=[GEOM_PTR, POINTER(GEOM_PTR), c_uint], -) -create_empty_polygon = GeomOutput("GEOSGeom_createEmptyPolygon", argtypes=[]) -create_collection = GeomOutput( - "GEOSGeom_createCollection", - argtypes=[c_int, POINTER(GEOM_PTR), c_uint], -) +# Polygon and collection creation routines are special and will not +# have their argument types defined. +create_polygon = GeomOutput('GEOSGeom_createPolygon') +create_empty_polygon = GeomOutput('GEOSGeom_createEmptyPolygon') +create_collection = GeomOutput('GEOSGeom_createCollection') # Ring routines -get_extring = GeomOutput("GEOSGetExteriorRing", argtypes=[GEOM_PTR]) -get_intring = GeomOutput("GEOSGetInteriorRingN", argtypes=[GEOM_PTR, c_int]) -get_nrings = IntFromGeom("GEOSGetNumInteriorRings") +get_extring = GeomOutput('GEOSGetExteriorRing', argtypes=[GEOM_PTR]) +get_intring = GeomOutput('GEOSGetInteriorRingN', argtypes=[GEOM_PTR, c_int]) +get_nrings = IntFromGeom('GEOSGetNumInteriorRings') # Collection Routines -get_geomn = GeomOutput("GEOSGetGeometryN", argtypes=[GEOM_PTR, c_int]) +get_geomn = GeomOutput('GEOSGetGeometryN', argtypes=[GEOM_PTR, c_int]) # Cloning -geom_clone = GEOSFuncFactory("GEOSGeom_clone", argtypes=[GEOM_PTR], restype=GEOM_PTR) +geom_clone = GEOSFuncFactory('GEOSGeom_clone', argtypes=[GEOM_PTR], restype=GEOM_PTR) # Destruction routine. -destroy_geom = GEOSFuncFactory("GEOSGeom_destroy", argtypes=[GEOM_PTR]) +destroy_geom = GEOSFuncFactory('GEOSGeom_destroy', argtypes=[GEOM_PTR]) # SRID routines -geos_get_srid = GEOSFuncFactory("GEOSGetSRID", argtypes=[GEOM_PTR], restype=c_int) -geos_set_srid = GEOSFuncFactory("GEOSSetSRID", argtypes=[GEOM_PTR, c_int]) +geos_get_srid = GEOSFuncFactory('GEOSGetSRID', argtypes=[GEOM_PTR], restype=c_int) +geos_set_srid = GEOSFuncFactory('GEOSSetSRID', argtypes=[GEOM_PTR, c_int]) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/io.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/io.py index 555d0e3..4a1180a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/io.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/io.py @@ -3,14 +3,10 @@ from ctypes import POINTER, Structure, byref, c_byte, c_char_p, c_int, c_size_t from django.contrib.gis.geos.base import GEOSBase from django.contrib.gis.geos.libgeos import ( - GEOM_PTR, - GEOSFuncFactory, - geos_version_tuple, + GEOM_PTR, GEOSFuncFactory, geos_version_tuple, ) from django.contrib.gis.geos.prototypes.errcheck import ( - check_geom, - check_sized_string, - check_string, + check_geom, check_sized_string, check_string, ) from django.contrib.gis.geos.prototypes.geom import c_uchar_p, geos_char_p from django.utils.encoding import force_bytes @@ -39,43 +35,33 @@ WKB_READ_PTR = POINTER(WKBReader_st) WKB_WRITE_PTR = POINTER(WKBReader_st) # WKTReader routines -wkt_reader_create = GEOSFuncFactory("GEOSWKTReader_create", restype=WKT_READ_PTR) -wkt_reader_destroy = GEOSFuncFactory("GEOSWKTReader_destroy", argtypes=[WKT_READ_PTR]) +wkt_reader_create = GEOSFuncFactory('GEOSWKTReader_create', restype=WKT_READ_PTR) +wkt_reader_destroy = GEOSFuncFactory('GEOSWKTReader_destroy', argtypes=[WKT_READ_PTR]) wkt_reader_read = GEOSFuncFactory( - "GEOSWKTReader_read", - argtypes=[WKT_READ_PTR, c_char_p], - restype=GEOM_PTR, - errcheck=check_geom, + 'GEOSWKTReader_read', argtypes=[WKT_READ_PTR, c_char_p], restype=GEOM_PTR, errcheck=check_geom ) # WKTWriter routines -wkt_writer_create = GEOSFuncFactory("GEOSWKTWriter_create", restype=WKT_WRITE_PTR) -wkt_writer_destroy = GEOSFuncFactory("GEOSWKTWriter_destroy", argtypes=[WKT_WRITE_PTR]) +wkt_writer_create = GEOSFuncFactory('GEOSWKTWriter_create', restype=WKT_WRITE_PTR) +wkt_writer_destroy = GEOSFuncFactory('GEOSWKTWriter_destroy', argtypes=[WKT_WRITE_PTR]) wkt_writer_write = GEOSFuncFactory( - "GEOSWKTWriter_write", - argtypes=[WKT_WRITE_PTR, GEOM_PTR], - restype=geos_char_p, - errcheck=check_string, + 'GEOSWKTWriter_write', argtypes=[WKT_WRITE_PTR, GEOM_PTR], restype=geos_char_p, errcheck=check_string ) wkt_writer_get_outdim = GEOSFuncFactory( - "GEOSWKTWriter_getOutputDimension", argtypes=[WKT_WRITE_PTR], restype=c_int + 'GEOSWKTWriter_getOutputDimension', argtypes=[WKT_WRITE_PTR], restype=c_int ) wkt_writer_set_outdim = GEOSFuncFactory( - "GEOSWKTWriter_setOutputDimension", argtypes=[WKT_WRITE_PTR, c_int] + 'GEOSWKTWriter_setOutputDimension', argtypes=[WKT_WRITE_PTR, c_int] ) -wkt_writer_set_trim = GEOSFuncFactory( - "GEOSWKTWriter_setTrim", argtypes=[WKT_WRITE_PTR, c_byte] -) -wkt_writer_set_precision = GEOSFuncFactory( - "GEOSWKTWriter_setRoundingPrecision", argtypes=[WKT_WRITE_PTR, c_int] -) +wkt_writer_set_trim = GEOSFuncFactory('GEOSWKTWriter_setTrim', argtypes=[WKT_WRITE_PTR, c_byte]) +wkt_writer_set_precision = GEOSFuncFactory('GEOSWKTWriter_setRoundingPrecision', argtypes=[WKT_WRITE_PTR, c_int]) # WKBReader routines -wkb_reader_create = GEOSFuncFactory("GEOSWKBReader_create", restype=WKB_READ_PTR) -wkb_reader_destroy = GEOSFuncFactory("GEOSWKBReader_destroy", argtypes=[WKB_READ_PTR]) +wkb_reader_create = GEOSFuncFactory('GEOSWKBReader_create', restype=WKB_READ_PTR) +wkb_reader_destroy = GEOSFuncFactory('GEOSWKBReader_destroy', argtypes=[WKB_READ_PTR]) class WKBReadFunc(GEOSFuncFactory): @@ -89,12 +75,12 @@ class WKBReadFunc(GEOSFuncFactory): errcheck = staticmethod(check_geom) -wkb_reader_read = WKBReadFunc("GEOSWKBReader_read") -wkb_reader_read_hex = WKBReadFunc("GEOSWKBReader_readHEX") +wkb_reader_read = WKBReadFunc('GEOSWKBReader_read') +wkb_reader_read_hex = WKBReadFunc('GEOSWKBReader_readHEX') # WKBWriter routines -wkb_writer_create = GEOSFuncFactory("GEOSWKBWriter_create", restype=WKB_WRITE_PTR) -wkb_writer_destroy = GEOSFuncFactory("GEOSWKBWriter_destroy", argtypes=[WKB_WRITE_PTR]) +wkb_writer_create = GEOSFuncFactory('GEOSWKBWriter_create', restype=WKB_WRITE_PTR) +wkb_writer_destroy = GEOSFuncFactory('GEOSWKBWriter_destroy', argtypes=[WKB_WRITE_PTR]) # WKB Writing prototypes. @@ -104,8 +90,8 @@ class WKBWriteFunc(GEOSFuncFactory): errcheck = staticmethod(check_sized_string) -wkb_writer_write = WKBWriteFunc("GEOSWKBWriter_write") -wkb_writer_write_hex = WKBWriteFunc("GEOSWKBWriter_writeHEX") +wkb_writer_write = WKBWriteFunc('GEOSWKBWriter_write') +wkb_writer_write_hex = WKBWriteFunc('GEOSWKBWriter_writeHEX') # WKBWriter property getter/setter prototypes. @@ -118,22 +104,17 @@ class WKBWriterSet(GEOSFuncFactory): argtypes = [WKB_WRITE_PTR, c_int] -wkb_writer_get_byteorder = WKBWriterGet("GEOSWKBWriter_getByteOrder") -wkb_writer_set_byteorder = WKBWriterSet("GEOSWKBWriter_setByteOrder") -wkb_writer_get_outdim = WKBWriterGet("GEOSWKBWriter_getOutputDimension") -wkb_writer_set_outdim = WKBWriterSet("GEOSWKBWriter_setOutputDimension") -wkb_writer_get_include_srid = WKBWriterGet( - "GEOSWKBWriter_getIncludeSRID", restype=c_byte -) -wkb_writer_set_include_srid = WKBWriterSet( - "GEOSWKBWriter_setIncludeSRID", argtypes=[WKB_WRITE_PTR, c_byte] -) +wkb_writer_get_byteorder = WKBWriterGet('GEOSWKBWriter_getByteOrder') +wkb_writer_set_byteorder = WKBWriterSet('GEOSWKBWriter_setByteOrder') +wkb_writer_get_outdim = WKBWriterGet('GEOSWKBWriter_getOutputDimension') +wkb_writer_set_outdim = WKBWriterSet('GEOSWKBWriter_setOutputDimension') +wkb_writer_get_include_srid = WKBWriterGet('GEOSWKBWriter_getIncludeSRID', restype=c_byte) +wkb_writer_set_include_srid = WKBWriterSet('GEOSWKBWriter_setIncludeSRID', argtypes=[WKB_WRITE_PTR, c_byte]) # ### Base I/O Class ### class IOBase(GEOSBase): "Base class for GEOS I/O objects." - def __init__(self): # Getting the pointer with the constructor. self.ptr = self._constructor() @@ -141,7 +122,6 @@ class IOBase(GEOSBase): # __del__ is too late (import error). self.destructor.func - # ### Base WKB/WKT Reading and Writing objects ### @@ -203,7 +183,7 @@ class WKTWriter(IOBase): @outdim.setter def outdim(self, new_dim): if new_dim not in (2, 3): - raise ValueError("WKT output dimension must be 2 or 3") + raise ValueError('WKT output dimension must be 2 or 3') wkt_writer_set_outdim(self.ptr, new_dim) @property @@ -223,9 +203,7 @@ class WKTWriter(IOBase): @precision.setter def precision(self, precision): if (not isinstance(precision, int) or precision < 0) and precision is not None: - raise AttributeError( - "WKT output rounding precision must be non-negative integer or None." - ) + raise AttributeError('WKT output rounding precision must be non-negative integer or None.') if precision != self._precision: self._precision = precision wkt_writer_set_precision(self.ptr, -1 if precision is None else precision) @@ -243,37 +221,34 @@ class WKBWriter(IOBase): def _handle_empty_point(self, geom): from django.contrib.gis.geos import Point - if isinstance(geom, Point) and geom.empty: if self.srid: # PostGIS uses POINT(NaN NaN) for WKB representation of empty # points. Use it for EWKB as it's a PostGIS specific format. # https://trac.osgeo.org/postgis/ticket/3181 - geom = Point(float("NaN"), float("NaN"), srid=geom.srid) + geom = Point(float('NaN'), float('NaN'), srid=geom.srid) else: - raise ValueError("Empty point is not representable in WKB.") + raise ValueError('Empty point is not representable in WKB.') return geom def write(self, geom): "Return the WKB representation of the given geometry." from django.contrib.gis.geos import Polygon - geom = self._handle_empty_point(geom) wkb = wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())) if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty: # Fix GEOS output for empty polygon. # See https://trac.osgeo.org/geos/ticket/680. - wkb = wkb[:-8] + b"\0" * 4 + wkb = wkb[:-8] + b'\0' * 4 return memoryview(wkb) def write_hex(self, geom): "Return the HEXEWKB representation of the given geometry." from django.contrib.gis.geos.polygon import Polygon - geom = self._handle_empty_point(geom) wkb = wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t())) if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty: - wkb = wkb[:-16] + b"0" * 8 + wkb = wkb[:-16] + b'0' * 8 return wkb # ### WKBWriter Properties ### @@ -284,9 +259,7 @@ class WKBWriter(IOBase): def _set_byteorder(self, order): if order not in (0, 1): - raise ValueError( - "Byte order parameter must be 0 (Big Endian) or 1 (Little Endian)." - ) + raise ValueError('Byte order parameter must be 0 (Big Endian) or 1 (Little Endian).') wkb_writer_set_byteorder(self.ptr, order) byteorder = property(_get_byteorder, _set_byteorder) @@ -299,7 +272,7 @@ class WKBWriter(IOBase): @outdim.setter def outdim(self, new_dim): if new_dim not in (2, 3): - raise ValueError("WKB output dimension must be 2 or 3") + raise ValueError('WKB output dimension must be 2 or 3') wkb_writer_set_outdim(self.ptr, new_dim) # Property for getting/setting the include srid flag. diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/misc.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/misc.py index fccd0ec..016c832 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/misc.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/misc.py @@ -8,7 +8,7 @@ from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string from django.contrib.gis.geos.prototypes.geom import geos_char_p -__all__ = ["geos_area", "geos_distance", "geos_length", "geos_isvalidreason"] +__all__ = ['geos_area', 'geos_distance', 'geos_length', 'geos_isvalidreason'] class DblFromGeom(GEOSFuncFactory): @@ -16,7 +16,6 @@ class DblFromGeom(GEOSFuncFactory): Argument is a Geometry, return type is double that is passed in by reference as the last argument. """ - restype = c_int # Status code returned errcheck = staticmethod(check_dbl) @@ -24,11 +23,9 @@ class DblFromGeom(GEOSFuncFactory): # ### ctypes prototypes ### # Area, distance, and length prototypes. -geos_area = DblFromGeom("GEOSArea", argtypes=[GEOM_PTR, POINTER(c_double)]) -geos_distance = DblFromGeom( - "GEOSDistance", argtypes=[GEOM_PTR, GEOM_PTR, POINTER(c_double)] -) -geos_length = DblFromGeom("GEOSLength", argtypes=[GEOM_PTR, POINTER(c_double)]) +geos_area = DblFromGeom('GEOSArea', argtypes=[GEOM_PTR, POINTER(c_double)]) +geos_distance = DblFromGeom('GEOSDistance', argtypes=[GEOM_PTR, GEOM_PTR, POINTER(c_double)]) +geos_length = DblFromGeom('GEOSLength', argtypes=[GEOM_PTR, POINTER(c_double)]) geos_isvalidreason = GEOSFuncFactory( - "GEOSisValidReason", restype=geos_char_p, errcheck=check_string, argtypes=[GEOM_PTR] + 'GEOSisValidReason', restype=geos_char_p, errcheck=check_string, argtypes=[GEOM_PTR] ) diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/predicates.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/predicates.py index d2e113a..d4681c1 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/predicates.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/predicates.py @@ -22,26 +22,22 @@ class BinaryPredicate(UnaryPredicate): # ## Unary Predicates ## -geos_hasz = UnaryPredicate("GEOSHasZ") -geos_isclosed = UnaryPredicate("GEOSisClosed") -geos_isempty = UnaryPredicate("GEOSisEmpty") -geos_isring = UnaryPredicate("GEOSisRing") -geos_issimple = UnaryPredicate("GEOSisSimple") -geos_isvalid = UnaryPredicate("GEOSisValid") +geos_hasz = UnaryPredicate('GEOSHasZ') +geos_isclosed = UnaryPredicate('GEOSisClosed') +geos_isempty = UnaryPredicate('GEOSisEmpty') +geos_isring = UnaryPredicate('GEOSisRing') +geos_issimple = UnaryPredicate('GEOSisSimple') +geos_isvalid = UnaryPredicate('GEOSisValid') # ## Binary Predicates ## -geos_contains = BinaryPredicate("GEOSContains") -geos_covers = BinaryPredicate("GEOSCovers") -geos_crosses = BinaryPredicate("GEOSCrosses") -geos_disjoint = BinaryPredicate("GEOSDisjoint") -geos_equals = BinaryPredicate("GEOSEquals") -geos_equalsexact = BinaryPredicate( - "GEOSEqualsExact", argtypes=[GEOM_PTR, GEOM_PTR, c_double] -) -geos_intersects = BinaryPredicate("GEOSIntersects") -geos_overlaps = BinaryPredicate("GEOSOverlaps") -geos_relatepattern = BinaryPredicate( - "GEOSRelatePattern", argtypes=[GEOM_PTR, GEOM_PTR, c_char_p] -) -geos_touches = BinaryPredicate("GEOSTouches") -geos_within = BinaryPredicate("GEOSWithin") +geos_contains = BinaryPredicate('GEOSContains') +geos_covers = BinaryPredicate('GEOSCovers') +geos_crosses = BinaryPredicate('GEOSCrosses') +geos_disjoint = BinaryPredicate('GEOSDisjoint') +geos_equals = BinaryPredicate('GEOSEquals') +geos_equalsexact = BinaryPredicate('GEOSEqualsExact', argtypes=[GEOM_PTR, GEOM_PTR, c_double]) +geos_intersects = BinaryPredicate('GEOSIntersects') +geos_overlaps = BinaryPredicate('GEOSOverlaps') +geos_relatepattern = BinaryPredicate('GEOSRelatePattern', argtypes=[GEOM_PTR, GEOM_PTR, c_char_p]) +geos_touches = BinaryPredicate('GEOSTouches') +geos_within = BinaryPredicate('GEOSWithin') diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/prepared.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/prepared.py index 4fdfab3..52c3156 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/prepared.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/prepared.py @@ -1,11 +1,13 @@ from ctypes import c_byte -from django.contrib.gis.geos.libgeos import GEOM_PTR, PREPGEOM_PTR, GEOSFuncFactory +from django.contrib.gis.geos.libgeos import ( + GEOM_PTR, PREPGEOM_PTR, GEOSFuncFactory, +) from django.contrib.gis.geos.prototypes.errcheck import check_predicate # Prepared geometry constructor and destructors. -geos_prepare = GEOSFuncFactory("GEOSPrepare", argtypes=[GEOM_PTR], restype=PREPGEOM_PTR) -prepared_destroy = GEOSFuncFactory("GEOSPreparedGeom_destroy", argtypes=[PREPGEOM_PTR]) +geos_prepare = GEOSFuncFactory('GEOSPrepare', argtypes=[GEOM_PTR], restype=PREPGEOM_PTR) +prepared_destroy = GEOSFuncFactory('GEOSPreparedGeom_destroy', argtypes=[PREPGEOM_PTR]) # Prepared geometry binary predicate support. @@ -15,12 +17,12 @@ class PreparedPredicate(GEOSFuncFactory): errcheck = staticmethod(check_predicate) -prepared_contains = PreparedPredicate("GEOSPreparedContains") -prepared_contains_properly = PreparedPredicate("GEOSPreparedContainsProperly") -prepared_covers = PreparedPredicate("GEOSPreparedCovers") -prepared_crosses = PreparedPredicate("GEOSPreparedCrosses") -prepared_disjoint = PreparedPredicate("GEOSPreparedDisjoint") -prepared_intersects = PreparedPredicate("GEOSPreparedIntersects") -prepared_overlaps = PreparedPredicate("GEOSPreparedOverlaps") -prepared_touches = PreparedPredicate("GEOSPreparedTouches") -prepared_within = PreparedPredicate("GEOSPreparedWithin") +prepared_contains = PreparedPredicate('GEOSPreparedContains') +prepared_contains_properly = PreparedPredicate('GEOSPreparedContainsProperly') +prepared_covers = PreparedPredicate('GEOSPreparedCovers') +prepared_crosses = PreparedPredicate('GEOSPreparedCrosses') +prepared_disjoint = PreparedPredicate('GEOSPreparedDisjoint') +prepared_intersects = PreparedPredicate('GEOSPreparedIntersects') +prepared_overlaps = PreparedPredicate('GEOSPreparedOverlaps') +prepared_touches = PreparedPredicate('GEOSPreparedTouches') +prepared_within = PreparedPredicate('GEOSPreparedWithin') diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py index d4f7ffb..500f6a6 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py @@ -1,12 +1,13 @@ import threading from django.contrib.gis.geos.base import GEOSBase -from django.contrib.gis.geos.libgeos import CONTEXT_PTR, error_h, lgeos, notice_h +from django.contrib.gis.geos.libgeos import ( + CONTEXT_PTR, error_h, lgeos, notice_h, +) class GEOSContextHandle(GEOSBase): """Represent a GEOS context handle.""" - ptr_type = CONTEXT_PTR destructor = lgeos.finishGEOS_r @@ -30,11 +31,10 @@ class GEOSFunc: Serve as a wrapper for GEOS C Functions. Use thread-safe function variants when available. """ - def __init__(self, func_name): # GEOS thread-safe function signatures end with '_r' and take an # additional context handle parameter. - self.cfunc = getattr(lgeos, func_name + "_r") + self.cfunc = getattr(lgeos, func_name + '_r') # Create a reference to thread_context so it's not garbage-collected # before an attempt to call this object. self.thread_context = thread_context diff --git a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/topology.py b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/topology.py index e61eae9..9ca0dce 100644 --- a/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/topology.py +++ b/venv/Lib/site-packages/django/contrib/gis/geos/prototypes/topology.py @@ -6,9 +6,7 @@ from ctypes import c_double, c_int from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory from django.contrib.gis.geos.prototypes.errcheck import ( - check_geom, - check_minus_one, - check_string, + check_geom, check_minus_one, check_string, ) from django.contrib.gis.geos.prototypes.geom import geos_char_p @@ -21,52 +19,35 @@ class Topology(GEOSFuncFactory): # Topology Routines -geos_boundary = Topology("GEOSBoundary") -geos_buffer = Topology("GEOSBuffer", argtypes=[GEOM_PTR, c_double, c_int]) -geos_bufferwithstyle = Topology( - "GEOSBufferWithStyle", argtypes=[GEOM_PTR, c_double, c_int, c_int, c_int, c_double] -) -geos_centroid = Topology("GEOSGetCentroid") -geos_convexhull = Topology("GEOSConvexHull") -geos_difference = Topology("GEOSDifference", argtypes=[GEOM_PTR, GEOM_PTR]) -geos_envelope = Topology("GEOSEnvelope") -geos_intersection = Topology("GEOSIntersection", argtypes=[GEOM_PTR, GEOM_PTR]) -geos_linemerge = Topology("GEOSLineMerge") -geos_pointonsurface = Topology("GEOSPointOnSurface") -geos_preservesimplify = Topology( - "GEOSTopologyPreserveSimplify", argtypes=[GEOM_PTR, c_double] -) -geos_simplify = Topology("GEOSSimplify", argtypes=[GEOM_PTR, c_double]) -geos_symdifference = Topology("GEOSSymDifference", argtypes=[GEOM_PTR, GEOM_PTR]) -geos_union = Topology("GEOSUnion", argtypes=[GEOM_PTR, GEOM_PTR]) +geos_boundary = Topology('GEOSBoundary') +geos_buffer = Topology('GEOSBuffer', argtypes=[GEOM_PTR, c_double, c_int]) +geos_bufferwithstyle = Topology('GEOSBufferWithStyle', argtypes=[GEOM_PTR, c_double, c_int, c_int, c_int, c_double]) +geos_centroid = Topology('GEOSGetCentroid') +geos_convexhull = Topology('GEOSConvexHull') +geos_difference = Topology('GEOSDifference', argtypes=[GEOM_PTR, GEOM_PTR]) +geos_envelope = Topology('GEOSEnvelope') +geos_intersection = Topology('GEOSIntersection', argtypes=[GEOM_PTR, GEOM_PTR]) +geos_linemerge = Topology('GEOSLineMerge') +geos_pointonsurface = Topology('GEOSPointOnSurface') +geos_preservesimplify = Topology('GEOSTopologyPreserveSimplify', argtypes=[GEOM_PTR, c_double]) +geos_simplify = Topology('GEOSSimplify', argtypes=[GEOM_PTR, c_double]) +geos_symdifference = Topology('GEOSSymDifference', argtypes=[GEOM_PTR, GEOM_PTR]) +geos_union = Topology('GEOSUnion', argtypes=[GEOM_PTR, GEOM_PTR]) -geos_unary_union = GEOSFuncFactory( - "GEOSUnaryUnion", argtypes=[GEOM_PTR], restype=GEOM_PTR -) +geos_unary_union = GEOSFuncFactory('GEOSUnaryUnion', argtypes=[GEOM_PTR], restype=GEOM_PTR) # GEOSRelate returns a string, not a geometry. geos_relate = GEOSFuncFactory( - "GEOSRelate", - argtypes=[GEOM_PTR, GEOM_PTR], - restype=geos_char_p, - errcheck=check_string, + 'GEOSRelate', argtypes=[GEOM_PTR, GEOM_PTR], restype=geos_char_p, errcheck=check_string ) # Linear referencing routines geos_project = GEOSFuncFactory( - "GEOSProject", - argtypes=[GEOM_PTR, GEOM_PTR], - restype=c_double, - errcheck=check_minus_one, + 'GEOSProject', argtypes=[GEOM_PTR, GEOM_PTR], restype=c_double, errcheck=check_minus_one ) -geos_interpolate = Topology("GEOSInterpolate", argtypes=[GEOM_PTR, c_double]) +geos_interpolate = Topology('GEOSInterpolate', argtypes=[GEOM_PTR, c_double]) geos_project_normalized = GEOSFuncFactory( - "GEOSProjectNormalized", - argtypes=[GEOM_PTR, GEOM_PTR], - restype=c_double, - errcheck=check_minus_one, -) -geos_interpolate_normalized = Topology( - "GEOSInterpolateNormalized", argtypes=[GEOM_PTR, c_double] + 'GEOSProjectNormalized', argtypes=[GEOM_PTR, GEOM_PTR], restype=c_double, errcheck=check_minus_one ) +geos_interpolate_normalized = Topology('GEOSInterpolateNormalized', argtypes=[GEOM_PTR, c_double]) diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.mo index 03265fb..e73429d 100644 Binary files a/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.po index 8e02152..e99ab2a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/gis/locale/el/LC_MESSAGES/django.po @@ -4,7 +4,6 @@ # Anastasiadis Stavros <anastasiadis.st00@gmail.com>, 2014 # Dimitris Glezos <glezos@transifex.com>, 2011 # Elena Andreou <helenaandreou.ha@gmail.com>, 2016 -# Fotis Athineos <fotis@transifex.com>, 2021 # Kostas Papadimitriou <vinilios@gmail.com>, 2012 # Nick Mavrakis <mavrakis.n@gmail.com>, 2016 # Pãnoș <panos.laganakos@gmail.com>, 2016 @@ -12,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-08-04 06:23+0000\n" -"Last-Translator: Fotis Athineos <fotis@transifex.com>\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -29,9 +28,9 @@ msgid "The base GIS field." msgstr "Το βασικό GIS πεδίο." msgid "" -"The base Geometry field — maps to the OpenGIS Specification Geometry type." +"The base Geometry field -- maps to the OpenGIS Specification Geometry type." msgstr "" -"Το βασικό Γεωμετρικό πεδίο — αντιστοιχεί στον τύπο της Γεωμετρικής " +"Το βασικό Γεωμετρικό πεδίο -- αντιστοιχεί στον τύπο της Γεωμετρικής " "Προδιαγραφής OpenGIS." msgid "Point" @@ -90,5 +89,5 @@ msgid "No feeds are registered." msgstr "Δεν υπάρχουν εγγεγραμμένες ροές ειδήσεων." #, python-format -msgid "Slug %r isn’t registered." +msgid "Slug %r isn't registered." msgstr "Το slug %r δεν έχει καταχωρηθεί." diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/ka/LC_MESSAGES/django.po index 31bb23c..52f95b8 100644 --- a/venv/Lib/site-packages/django/contrib/gis/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/gis/locale/ka/LC_MESSAGES/django.po @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "GIS" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/kk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/kk/LC_MESSAGES/django.po index 2666802..cd5e947 100644 --- a/venv/Lib/site-packages/django/contrib/gis/locale/kk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/gis/locale/kk/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kk\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "GIS" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/kn/LC_MESSAGES/django.po index 0ec1b16..b2b29a8 100644 --- a/venv/Lib/site-packages/django/contrib/gis/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/gis/locale/kn/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "GIS" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index e907a89..0000000 Binary files a/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 689bbbd..0000000 --- a/venv/Lib/site-packages/django/contrib/gis/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,87 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-11-16 13:19+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "GIS" -msgstr "GIS" - -msgid "The base GIS field." -msgstr "Medan asas GIS" - -msgid "" -"The base Geometry field — maps to the OpenGIS Specification Geometry type." -msgstr "" -"Medan asas geometri - disuaikan ke jenis Spesifikasi Geometri OpenGIS ." - -msgid "Point" -msgstr "Titik" - -msgid "Line string" -msgstr "Rentetan baris" - -msgid "Polygon" -msgstr "Polygon" - -msgid "Multi-point" -msgstr "Berbilang-titik" - -msgid "Multi-line string" -msgstr "Rentetan berbilang-baris" - -msgid "Multi polygon" -msgstr "Berbilang polygon" - -msgid "Geometry collection" -msgstr "Koleksi geometri" - -msgid "Extent Aggregate Field" -msgstr "Medan Agregat Takat" - -msgid "Raster Field" -msgstr "Medan Raster" - -msgid "No geometry value provided." -msgstr "Tiada nilai geometri diberikan." - -msgid "Invalid geometry value." -msgstr "Nilai geometri tidak sah." - -msgid "Invalid geometry type." -msgstr "Jenis geometri tidak sah." - -msgid "" -"An error occurred when transforming the geometry to the SRID of the geometry " -"form field." -msgstr "" -"Ralat telah berlaku semasa mengubah geometri kepada SRID geometri medan " -"borang." - -msgid "Delete all Features" -msgstr "Hapuskan semua Ciri-cri" - -msgid "WKT debugging window:" -msgstr "Tingkap penyahpijatan WKT:" - -msgid "Debugging window (serialized value)" -msgstr "Tingkap penyahpijatan (nilai bersiri)" - -msgid "No feeds are registered." -msgstr "Tiada feed didaftarkan." - -#, python-format -msgid "Slug %r isn’t registered." -msgstr "Slug %r tidak didaftarkan." diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.mo index a18e002..dad1cdb 100644 Binary files a/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.po index 9413b33..69c433f 100644 --- a/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/gis/locale/nn/LC_MESSAGES/django.po @@ -2,15 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 -# Vibeke Uthaug, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-08 17:27+0200\n" -"PO-Revision-Date: 2021-11-18 15:49+0000\n" -"Last-Translator: Vibeke Uthaug\n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -20,15 +18,14 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "GIS" -msgstr "GIS" +msgstr "" msgid "The base GIS field." -msgstr "GIS-basefeltet" +msgstr "" msgid "" -"The base Geometry field — maps to the OpenGIS Specification Geometry type." +"The base Geometry field -- maps to the OpenGIS Specification Geometry type." msgstr "" -"Geometry-basefeltet - tilordnes til OpenGIS Specification Geometry-typen." msgid "Point" msgstr "Punkt" @@ -52,19 +49,19 @@ msgid "Geometry collection" msgstr "Geometrisamling" msgid "Extent Aggregate Field" -msgstr "Extent Aggregate-felt" +msgstr "" msgid "Raster Field" -msgstr "Raster-felt" +msgstr "" msgid "No geometry value provided." msgstr "Ingen geometriverdi oppgjeve." msgid "Invalid geometry value." -msgstr "Ugyldig geometriverdi." +msgstr "Ugyldig geometriverdi" msgid "Invalid geometry type." -msgstr "Ugyldig geometritype." +msgstr "Ugyldig geometritype" msgid "" "An error occurred when transforming the geometry to the SRID of the geometry " @@ -74,17 +71,17 @@ msgstr "" "geometrifeltet." msgid "Delete all Features" -msgstr "Slett alle Features" +msgstr "" msgid "WKT debugging window:" -msgstr "WKT feilsøking-vindauge:" +msgstr "" msgid "Debugging window (serialized value)" -msgstr "Feilsøking-vindauge (serialisert verdi)" +msgstr "" msgid "No feeds are registered." -msgstr "Ingen feeds er registrert." +msgstr "" #, python-format -msgid "Slug %r isn’t registered." -msgstr "Slug %r er ikkje registrert." +msgid "Slug %r isn't registered." +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/gis/management/commands/inspectdb.py b/venv/Lib/site-packages/django/contrib/gis/management/commands/inspectdb.py index 1bcc1a0..8c6f629 100644 --- a/venv/Lib/site-packages/django/contrib/gis/management/commands/inspectdb.py +++ b/venv/Lib/site-packages/django/contrib/gis/management/commands/inspectdb.py @@ -1,18 +1,16 @@ -from django.core.management.commands.inspectdb import Command as InspectDBCommand +from django.core.management.commands.inspectdb import ( + Command as InspectDBCommand, +) class Command(InspectDBCommand): - db_module = "django.contrib.gis.db" + db_module = 'django.contrib.gis.db' def get_field_type(self, connection, table_name, row): - field_type, field_params, field_notes = super().get_field_type( - connection, table_name, row - ) - if field_type == "GeometryField": + field_type, field_params, field_notes = super().get_field_type(connection, table_name, row) + if field_type == 'GeometryField': # Getting a more specific field type and any additional parameters # from the `get_geometry_type` routine for the spatial backend. - field_type, geo_params = connection.introspection.get_geometry_type( - table_name, row - ) + field_type, geo_params = connection.introspection.get_geometry_type(table_name, row) field_params.update(geo_params) return field_type, field_params, field_notes diff --git a/venv/Lib/site-packages/django/contrib/gis/management/commands/ogrinspect.py b/venv/Lib/site-packages/django/contrib/gis/management/commands/ogrinspect.py index 2d2bd63..12c1db3 100644 --- a/venv/Lib/site-packages/django/contrib/gis/management/commands/ogrinspect.py +++ b/venv/Lib/site-packages/django/contrib/gis/management/commands/ogrinspect.py @@ -10,7 +10,6 @@ class LayerOptionAction(argparse.Action): Custom argparse action for the `ogrinspect` `layer_key` keyword option which may be an integer or a string. """ - def __call__(self, parser, namespace, value, option_string=None): try: setattr(namespace, self.dest, int(value)) @@ -24,92 +23,80 @@ class ListOptionAction(argparse.Action): a string list. If the string is 'True'/'true' then the option value will be a boolean instead. """ - def __call__(self, parser, namespace, value, option_string=None): - if value.lower() == "true": + if value.lower() == 'true': setattr(namespace, self.dest, True) else: - setattr(namespace, self.dest, value.split(",")) + setattr(namespace, self.dest, value.split(',')) class Command(BaseCommand): help = ( - "Inspects the given OGR-compatible data source (e.g., a shapefile) and " - "outputs\na GeoDjango model with the given model name. For example:\n" - " ./manage.py ogrinspect zipcode.shp Zipcode" + 'Inspects the given OGR-compatible data source (e.g., a shapefile) and outputs\n' + 'a GeoDjango model with the given model name. For example:\n' + ' ./manage.py ogrinspect zipcode.shp Zipcode' ) requires_system_checks = [] def add_arguments(self, parser): - parser.add_argument("data_source", help="Path to the data source.") - parser.add_argument("model_name", help="Name of the model to create.") + parser.add_argument('data_source', help='Path to the data source.') + parser.add_argument('model_name', help='Name of the model to create.') parser.add_argument( - "--blank", - action=ListOptionAction, - default=False, - help="Use a comma separated list of OGR field names to add " - "the `blank=True` option to the field definition. Set to `true` " - "to apply to all applicable fields.", + '--blank', + action=ListOptionAction, default=False, + help='Use a comma separated list of OGR field names to add ' + 'the `blank=True` option to the field definition. Set to `true` ' + 'to apply to all applicable fields.', ) parser.add_argument( - "--decimal", - action=ListOptionAction, - default=False, - help="Use a comma separated list of OGR float fields to " - "generate `DecimalField` instead of the default " - "`FloatField`. Set to `true` to apply to all OGR float fields.", + '--decimal', + action=ListOptionAction, default=False, + help='Use a comma separated list of OGR float fields to ' + 'generate `DecimalField` instead of the default ' + '`FloatField`. Set to `true` to apply to all OGR float fields.', ) parser.add_argument( - "--geom-name", - default="geom", - help="Specifies the model name for the Geometry Field (defaults to `geom`)", + '--geom-name', default='geom', + help='Specifies the model name for the Geometry Field (defaults to `geom`)' ) parser.add_argument( - "--layer", - dest="layer_key", - action=LayerOptionAction, - default=0, - help="The key for specifying which layer in the OGR data " - "source to use. Defaults to 0 (the first layer). May be " - "an integer or a string identifier for the layer.", + '--layer', dest='layer_key', + action=LayerOptionAction, default=0, + help='The key for specifying which layer in the OGR data ' + 'source to use. Defaults to 0 (the first layer). May be ' + 'an integer or a string identifier for the layer.', ) parser.add_argument( - "--multi-geom", - action="store_true", - help="Treat the geometry in the data source as a geometry collection.", + '--multi-geom', action='store_true', + help='Treat the geometry in the data source as a geometry collection.', ) parser.add_argument( - "--name-field", - help="Specifies a field name to return for the __str__() method.", + '--name-field', + help='Specifies a field name to return for the __str__() method.', ) parser.add_argument( - "--no-imports", - action="store_false", - dest="imports", - help="Do not include `from django.contrib.gis.db import models` statement.", + '--no-imports', action='store_false', dest='imports', + help='Do not include `from django.contrib.gis.db import models` statement.', ) parser.add_argument( - "--null", - action=ListOptionAction, - default=False, - help="Use a comma separated list of OGR field names to add " - "the `null=True` option to the field definition. Set to `true` " - "to apply to all applicable fields.", + '--null', action=ListOptionAction, default=False, + help='Use a comma separated list of OGR field names to add ' + 'the `null=True` option to the field definition. Set to `true` ' + 'to apply to all applicable fields.', ) parser.add_argument( - "--srid", - help="The SRID to use for the Geometry Field. If it can be " - "determined, the SRID of the data source is used.", + '--srid', + help='The SRID to use for the Geometry Field. If it can be ' + 'determined, the SRID of the data source is used.', ) parser.add_argument( - "--mapping", - action="store_true", - help="Generate mapping dictionary for use with `LayerMapping`.", + '--mapping', action='store_true', + help='Generate mapping dictionary for use with `LayerMapping`.', ) def handle(self, *args, **options): - data_source, model_name = options.pop("data_source"), options.pop("model_name") + data_source, model_name = options.pop('data_source'), options.pop('model_name') # Getting the OGR DataSource from the string parameter. try: @@ -122,43 +109,26 @@ class Command(BaseCommand): from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping # Filter options to params accepted by `_ogrinspect` - ogr_options = { - k: v - for k, v in options.items() - if k in get_func_args(_ogrinspect) and v is not None - } + ogr_options = {k: v for k, v in options.items() + if k in get_func_args(_ogrinspect) and v is not None} output = [s for s in _ogrinspect(ds, model_name, **ogr_options)] - if options["mapping"]: + if options['mapping']: # Constructing the keyword arguments for `mapping`, and # calling it on the data source. kwargs = { - "geom_name": options["geom_name"], - "layer_key": options["layer_key"], - "multi_geom": options["multi_geom"], + 'geom_name': options['geom_name'], + 'layer_key': options['layer_key'], + 'multi_geom': options['multi_geom'], } mapping_dict = mapping(ds, **kwargs) # This extra legwork is so that the dictionary definition comes # out in the same order as the fields in the model definition. rev_mapping = {v: k for k, v in mapping_dict.items()} - output.extend( - [ - "", - "", - "# Auto-generated `LayerMapping` dictionary for %s model" - % model_name, - "%s_mapping = {" % model_name.lower(), - ] + output.extend(['', '', '# Auto-generated `LayerMapping` dictionary for %s model' % model_name, + '%s_mapping = {' % model_name.lower()]) + output.extend(" '%s': '%s'," % ( + rev_mapping[ogr_fld], ogr_fld) for ogr_fld in ds[options['layer_key']].fields ) - output.extend( - " '%s': '%s'," % (rev_mapping[ogr_fld], ogr_fld) - for ogr_fld in ds[options["layer_key"]].fields - ) - output.extend( - [ - " '%s': '%s'," - % (options["geom_name"], mapping_dict[options["geom_name"]]), - "}", - ] - ) - return "\n".join(output) + output.extend([" '%s': '%s'," % (options['geom_name'], mapping_dict[options['geom_name']]), '}']) + return '\n'.join(output) diff --git a/venv/Lib/site-packages/django/contrib/gis/measure.py b/venv/Lib/site-packages/django/contrib/gis/measure.py index 640f0e0..7e571f1 100644 --- a/venv/Lib/site-packages/django/contrib/gis/measure.py +++ b/venv/Lib/site-packages/django/contrib/gis/measure.py @@ -38,7 +38,7 @@ and Geoff Biggs' PhD work on dimensioned units for robotics. from decimal import Decimal from functools import total_ordering -__all__ = ["A", "Area", "D", "Distance"] +__all__ = ['A', 'Area', 'D', 'Distance'] NUMERIC_TYPES = (int, float, Decimal) AREA_PREFIX = "sq_" @@ -73,17 +73,13 @@ class MeasureBase: if name in self.UNITS: return self.standard / self.UNITS[name] else: - raise AttributeError("Unknown unit type: %s" % name) + raise AttributeError('Unknown unit type: %s' % name) def __repr__(self): - return "%s(%s=%s)" % ( - pretty_name(self), - self._default_unit, - getattr(self, self._default_unit), - ) + return '%s(%s=%s)' % (pretty_name(self), self._default_unit, getattr(self, self._default_unit)) def __str__(self): - return "%s %s" % (getattr(self, self._default_unit), self._default_unit) + return '%s %s' % (getattr(self, self._default_unit), self._default_unit) # **** Comparison methods **** @@ -108,65 +104,49 @@ class MeasureBase: if isinstance(other, self.__class__): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard + other.standard)}, + **{self.STANDARD_UNIT: (self.standard + other.standard)} ) else: - raise TypeError( - "%(class)s must be added with %(class)s" % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be added with %(class)s' % {"class": pretty_name(self)}) def __iadd__(self, other): if isinstance(other, self.__class__): self.standard += other.standard return self else: - raise TypeError( - "%(class)s must be added with %(class)s" % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be added with %(class)s' % {"class": pretty_name(self)}) def __sub__(self, other): if isinstance(other, self.__class__): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard - other.standard)}, + **{self.STANDARD_UNIT: (self.standard - other.standard)} ) else: - raise TypeError( - "%(class)s must be subtracted from %(class)s" - % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be subtracted from %(class)s' % {"class": pretty_name(self)}) def __isub__(self, other): if isinstance(other, self.__class__): self.standard -= other.standard return self else: - raise TypeError( - "%(class)s must be subtracted from %(class)s" - % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be subtracted from %(class)s' % {"class": pretty_name(self)}) def __mul__(self, other): if isinstance(other, NUMERIC_TYPES): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard * other)}, + **{self.STANDARD_UNIT: (self.standard * other)} ) else: - raise TypeError( - "%(class)s must be multiplied with number" - % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be multiplied with number' % {"class": pretty_name(self)}) def __imul__(self, other): if isinstance(other, NUMERIC_TYPES): self.standard *= float(other) return self else: - raise TypeError( - "%(class)s must be multiplied with number" - % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be multiplied with number' % {"class": pretty_name(self)}) def __rmul__(self, other): return self * other @@ -177,22 +157,17 @@ class MeasureBase: if isinstance(other, NUMERIC_TYPES): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard / other)}, + **{self.STANDARD_UNIT: (self.standard / other)} ) else: - raise TypeError( - "%(class)s must be divided with number or %(class)s" - % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be divided with number or %(class)s' % {"class": pretty_name(self)}) def __itruediv__(self, other): if isinstance(other, NUMERIC_TYPES): self.standard /= float(other) return self else: - raise TypeError( - "%(class)s must be divided with number" % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be divided with number' % {"class": pretty_name(self)}) def __bool__(self): return bool(self.standard) @@ -224,7 +199,7 @@ class MeasureBase: val += self.UNITS[u] * value default_unit = u else: - raise AttributeError("Unknown unit type: %s" % unit) + raise AttributeError('Unknown unit type: %s' % unit) return val, default_unit @classmethod @@ -242,87 +217,85 @@ class MeasureBase: elif lower in cls.LALIAS: return cls.LALIAS[lower] else: - raise Exception( - 'Could not find a unit keyword associated with "%s"' % unit_str - ) + raise Exception('Could not find a unit keyword associated with "%s"' % unit_str) class Distance(MeasureBase): STANDARD_UNIT = "m" UNITS = { - "chain": 20.1168, - "chain_benoit": 20.116782, - "chain_sears": 20.1167645, - "british_chain_benoit": 20.1167824944, - "british_chain_sears": 20.1167651216, - "british_chain_sears_truncated": 20.116756, - "cm": 0.01, - "british_ft": 0.304799471539, - "british_yd": 0.914398414616, - "clarke_ft": 0.3047972654, - "clarke_link": 0.201166195164, - "fathom": 1.8288, - "ft": 0.3048, - "furlong": 201.168, - "german_m": 1.0000135965, - "gold_coast_ft": 0.304799710181508, - "indian_yd": 0.914398530744, - "inch": 0.0254, - "km": 1000.0, - "link": 0.201168, - "link_benoit": 0.20116782, - "link_sears": 0.20116765, - "m": 1.0, - "mi": 1609.344, - "mm": 0.001, - "nm": 1852.0, - "nm_uk": 1853.184, - "rod": 5.0292, - "sears_yd": 0.91439841, - "survey_ft": 0.304800609601, - "um": 0.000001, - "yd": 0.9144, + 'chain': 20.1168, + 'chain_benoit': 20.116782, + 'chain_sears': 20.1167645, + 'british_chain_benoit': 20.1167824944, + 'british_chain_sears': 20.1167651216, + 'british_chain_sears_truncated': 20.116756, + 'cm': 0.01, + 'british_ft': 0.304799471539, + 'british_yd': 0.914398414616, + 'clarke_ft': 0.3047972654, + 'clarke_link': 0.201166195164, + 'fathom': 1.8288, + 'ft': 0.3048, + 'furlong': 201.168, + 'german_m': 1.0000135965, + 'gold_coast_ft': 0.304799710181508, + 'indian_yd': 0.914398530744, + 'inch': 0.0254, + 'km': 1000.0, + 'link': 0.201168, + 'link_benoit': 0.20116782, + 'link_sears': 0.20116765, + 'm': 1.0, + 'mi': 1609.344, + 'mm': 0.001, + 'nm': 1852.0, + 'nm_uk': 1853.184, + 'rod': 5.0292, + 'sears_yd': 0.91439841, + 'survey_ft': 0.304800609601, + 'um': 0.000001, + 'yd': 0.9144, } # Unit aliases for `UNIT` terms encountered in Spatial Reference WKT. ALIAS = { - "centimeter": "cm", - "foot": "ft", - "inches": "inch", - "kilometer": "km", - "kilometre": "km", - "meter": "m", - "metre": "m", - "micrometer": "um", - "micrometre": "um", - "millimeter": "mm", - "millimetre": "mm", - "mile": "mi", - "yard": "yd", - "British chain (Benoit 1895 B)": "british_chain_benoit", - "British chain (Sears 1922)": "british_chain_sears", - "British chain (Sears 1922 truncated)": "british_chain_sears_truncated", - "British foot (Sears 1922)": "british_ft", - "British foot": "british_ft", - "British yard (Sears 1922)": "british_yd", - "British yard": "british_yd", - "Clarke's Foot": "clarke_ft", - "Clarke's link": "clarke_link", - "Chain (Benoit)": "chain_benoit", - "Chain (Sears)": "chain_sears", - "Foot (International)": "ft", - "Furrow Long": "furlong", - "German legal metre": "german_m", - "Gold Coast foot": "gold_coast_ft", - "Indian yard": "indian_yd", - "Link (Benoit)": "link_benoit", - "Link (Sears)": "link_sears", - "Nautical Mile": "nm", - "Nautical Mile (UK)": "nm_uk", - "US survey foot": "survey_ft", - "U.S. Foot": "survey_ft", - "Yard (Indian)": "indian_yd", - "Yard (Sears)": "sears_yd", + 'centimeter': 'cm', + 'foot': 'ft', + 'inches': 'inch', + 'kilometer': 'km', + 'kilometre': 'km', + 'meter': 'm', + 'metre': 'm', + 'micrometer': 'um', + 'micrometre': 'um', + 'millimeter': 'mm', + 'millimetre': 'mm', + 'mile': 'mi', + 'yard': 'yd', + 'British chain (Benoit 1895 B)': 'british_chain_benoit', + 'British chain (Sears 1922)': 'british_chain_sears', + 'British chain (Sears 1922 truncated)': 'british_chain_sears_truncated', + 'British foot (Sears 1922)': 'british_ft', + 'British foot': 'british_ft', + 'British yard (Sears 1922)': 'british_yd', + 'British yard': 'british_yd', + "Clarke's Foot": 'clarke_ft', + "Clarke's link": 'clarke_link', + 'Chain (Benoit)': 'chain_benoit', + 'Chain (Sears)': 'chain_sears', + 'Foot (International)': 'ft', + 'Furrow Long': 'furlong', + 'German legal metre': 'german_m', + 'Gold Coast foot': 'gold_coast_ft', + 'Indian yard': 'indian_yd', + 'Link (Benoit)': 'link_benoit', + 'Link (Sears)': 'link_sears', + 'Nautical Mile': 'nm', + 'Nautical Mile (UK)': 'nm_uk', + 'US survey foot': 'survey_ft', + 'U.S. Foot': 'survey_ft', + 'Yard (Indian)': 'indian_yd', + 'Yard (Sears)': 'sears_yd' } LALIAS = {k.lower(): v for k, v in ALIAS.items()} @@ -330,39 +303,34 @@ class Distance(MeasureBase): if isinstance(other, self.__class__): return Area( default_unit=AREA_PREFIX + self._default_unit, - **{AREA_PREFIX + self.STANDARD_UNIT: (self.standard * other.standard)}, + **{AREA_PREFIX + self.STANDARD_UNIT: (self.standard * other.standard)} ) elif isinstance(other, NUMERIC_TYPES): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard * other)}, + **{self.STANDARD_UNIT: (self.standard * other)} ) else: - raise TypeError( - "%(distance)s must be multiplied with number or %(distance)s" - % { - "distance": pretty_name(self.__class__), - } - ) + raise TypeError('%(distance)s must be multiplied with number or %(distance)s' % { + "distance": pretty_name(self.__class__), + }) class Area(MeasureBase): STANDARD_UNIT = AREA_PREFIX + Distance.STANDARD_UNIT # Getting the square units values and the alias dictionary. - UNITS = {"%s%s" % (AREA_PREFIX, k): v**2 for k, v in Distance.UNITS.items()} - ALIAS = {k: "%s%s" % (AREA_PREFIX, v) for k, v in Distance.ALIAS.items()} + UNITS = {'%s%s' % (AREA_PREFIX, k): v ** 2 for k, v in Distance.UNITS.items()} + ALIAS = {k: '%s%s' % (AREA_PREFIX, v) for k, v in Distance.ALIAS.items()} LALIAS = {k.lower(): v for k, v in ALIAS.items()} def __truediv__(self, other): if isinstance(other, NUMERIC_TYPES): return self.__class__( default_unit=self._default_unit, - **{self.STANDARD_UNIT: (self.standard / other)}, + **{self.STANDARD_UNIT: (self.standard / other)} ) else: - raise TypeError( - "%(class)s must be divided by a number" % {"class": pretty_name(self)} - ) + raise TypeError('%(class)s must be divided by a number' % {"class": pretty_name(self)}) # Shortcuts diff --git a/venv/Lib/site-packages/django/contrib/gis/ptr.py b/venv/Lib/site-packages/django/contrib/gis/ptr.py index 6754701..a5a117a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/ptr.py +++ b/venv/Lib/site-packages/django/contrib/gis/ptr.py @@ -6,7 +6,6 @@ class CPointerBase: Base class for objects that have a pointer access property that controls access to the underlying C pointer. """ - _ptr = None # Initially the pointer is NULL. ptr_type = c_void_p destructor = None @@ -18,16 +17,14 @@ class CPointerBase: # aren't passed to routines -- that's very bad. if self._ptr: return self._ptr - raise self.null_ptr_exception_class( - "NULL %s pointer encountered." % self.__class__.__name__ - ) + raise self.null_ptr_exception_class('NULL %s pointer encountered.' % self.__class__.__name__) @ptr.setter def ptr(self, ptr): # Only allow the pointer to be set with pointers of the compatible # type or None (NULL). if not (ptr is None or isinstance(ptr, self.ptr_type)): - raise TypeError("Incompatible pointer type: %s." % type(ptr)) + raise TypeError('Incompatible pointer type: %s.' % type(ptr)) self._ptr = ptr def __del__(self): diff --git a/venv/Lib/site-packages/django/contrib/gis/serializers/geojson.py b/venv/Lib/site-packages/django/contrib/gis/serializers/geojson.py index cdb187e..3cd0154 100644 --- a/venv/Lib/site-packages/django/contrib/gis/serializers/geojson.py +++ b/venv/Lib/site-packages/django/contrib/gis/serializers/geojson.py @@ -7,29 +7,23 @@ class Serializer(JSONSerializer): """ Convert a queryset to GeoJSON, http://geojson.org/ """ - def _init_options(self): super()._init_options() - self.geometry_field = self.json_kwargs.pop("geometry_field", None) - self.srid = self.json_kwargs.pop("srid", 4326) - if ( - self.selected_fields is not None - and self.geometry_field is not None - and self.geometry_field not in self.selected_fields - ): + self.geometry_field = self.json_kwargs.pop('geometry_field', None) + self.srid = self.json_kwargs.pop('srid', 4326) + if (self.selected_fields is not None and self.geometry_field is not None and + self.geometry_field not in self.selected_fields): self.selected_fields = [*self.selected_fields, self.geometry_field] def start_serialization(self): self._init_options() self._cts = {} # cache of CoordTransform's self.stream.write( - '{"type": "FeatureCollection", ' - '"crs": {"type": "name", "properties": {"name": "EPSG:%d"}},' - ' "features": [' % self.srid - ) + '{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "EPSG:%d"}},' + ' "features": [' % self.srid) def end_serialization(self): - self.stream.write("]}") + self.stream.write(']}') def start_object(self, obj): super().start_object(obj) @@ -37,7 +31,7 @@ class Serializer(JSONSerializer): if self.geometry_field is None: # Find the first declared geometry field for field in obj._meta.fields: - if hasattr(field, "geom_type"): + if hasattr(field, 'geom_type'): self.geometry_field = field.name break @@ -46,19 +40,15 @@ class Serializer(JSONSerializer): "type": "Feature", "properties": self._current, } - if ( - self.selected_fields is None or "pk" in self.selected_fields - ) and "pk" not in data["properties"]: + if ((self.selected_fields is None or 'pk' in self.selected_fields) and + 'pk' not in data["properties"]): data["properties"]["pk"] = obj._meta.pk.value_to_string(obj) if self._geometry: if self._geometry.srid != self.srid: - # If needed, transform the geometry in the srid of the global - # geojson srid. + # If needed, transform the geometry in the srid of the global geojson srid if self._geometry.srid not in self._cts: srs = SpatialReference(self.srid) - self._cts[self._geometry.srid] = CoordTransform( - self._geometry.srs, srs - ) + self._cts[self._geometry.srid] = CoordTransform(self._geometry.srs, srs) self._geometry.transform(self._cts[self._geometry.srid]) data["geometry"] = eval(self._geometry.geojson) else: diff --git a/venv/Lib/site-packages/django/contrib/gis/shortcuts.py b/venv/Lib/site-packages/django/contrib/gis/shortcuts.py index 33ff2ac..aa68140 100644 --- a/venv/Lib/site-packages/django/contrib/gis/shortcuts.py +++ b/venv/Lib/site-packages/django/contrib/gis/shortcuts.py @@ -15,8 +15,8 @@ except ImportError: def compress_kml(kml): "Return compressed KMZ from the given KML string." kmz = BytesIO() - with zipfile.ZipFile(kmz, "a", zipfile.ZIP_DEFLATED) as zf: - zf.writestr("doc.kml", kml.encode(settings.DEFAULT_CHARSET)) + with zipfile.ZipFile(kmz, 'a', zipfile.ZIP_DEFLATED) as zf: + zf.writestr('doc.kml', kml.encode(settings.DEFAULT_CHARSET)) kmz.seek(0) return kmz.read() @@ -25,7 +25,7 @@ def render_to_kml(*args, **kwargs): "Render the response as KML (using the correct MIME type)." return HttpResponse( loader.render_to_string(*args, **kwargs), - content_type="application/vnd.google-earth.kml+xml", + content_type='application/vnd.google-earth.kml+xml', ) @@ -36,5 +36,5 @@ def render_to_kmz(*args, **kwargs): """ return HttpResponse( compress_kml(loader.render_to_string(*args, **kwargs)), - content_type="application/vnd.google-earth.kmz", + content_type='application/vnd.google-earth.kmz', ) diff --git a/venv/Lib/site-packages/django/contrib/gis/sitemaps/__init__.py b/venv/Lib/site-packages/django/contrib/gis/sitemaps/__init__.py index 3654bfc..7337e65 100644 --- a/venv/Lib/site-packages/django/contrib/gis/sitemaps/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/sitemaps/__init__.py @@ -1,4 +1,4 @@ # Geo-enabled Sitemap classes. from django.contrib.gis.sitemaps.kml import KMLSitemap, KMZSitemap -__all__ = ["KMLSitemap", "KMZSitemap"] +__all__ = ['KMLSitemap', 'KMZSitemap'] diff --git a/venv/Lib/site-packages/django/contrib/gis/sitemaps/kml.py b/venv/Lib/site-packages/django/contrib/gis/sitemaps/kml.py index a84b5ae..103dc20 100644 --- a/venv/Lib/site-packages/django/contrib/gis/sitemaps/kml.py +++ b/venv/Lib/site-packages/django/contrib/gis/sitemaps/kml.py @@ -9,8 +9,7 @@ class KMLSitemap(Sitemap): """ A minimal hook to produce KML sitemaps. """ - - geo_format = "kml" + geo_format = 'kml' def __init__(self, locations=None): # If no locations specified, then we try to build for @@ -32,22 +31,15 @@ class KMLSitemap(Sitemap): if isinstance(source, models.base.ModelBase): for field in source._meta.fields: if isinstance(field, GeometryField): - kml_sources.append( - ( - source._meta.app_label, - source._meta.model_name, - field.name, - ) - ) + kml_sources.append((source._meta.app_label, + source._meta.model_name, + field.name)) elif isinstance(source, (list, tuple)): if len(source) != 3: - raise ValueError( - "Must specify a 3-tuple of (app_label, module_name, " - "field_name)." - ) + raise ValueError('Must specify a 3-tuple of (app_label, module_name, field_name).') kml_sources.append(source) else: - raise TypeError("KML Sources must be a model or a 3-tuple.") + raise TypeError('KML Sources must be a model or a 3-tuple.') return kml_sources def get_urls(self, page=1, site=None, protocol=None): @@ -57,7 +49,7 @@ class KMLSitemap(Sitemap): """ urls = Sitemap.get_urls(self, page=page, site=site, protocol=protocol) for url in urls: - url["geo_format"] = self.geo_format + url['geo_format'] = self.geo_format return urls def items(self): @@ -65,14 +57,14 @@ class KMLSitemap(Sitemap): def location(self, obj): return reverse( - "django.contrib.gis.sitemaps.views.%s" % self.geo_format, + 'django.contrib.gis.sitemaps.views.%s' % self.geo_format, kwargs={ - "label": obj[0], - "model": obj[1], - "field_name": obj[2], + 'label': obj[0], + 'model': obj[1], + 'field_name': obj[2], }, ) class KMZSitemap(KMLSitemap): - geo_format = "kmz" + geo_format = 'kmz' diff --git a/venv/Lib/site-packages/django/contrib/gis/sitemaps/views.py b/venv/Lib/site-packages/django/contrib/gis/sitemaps/views.py index 17eb54e..be91d92 100644 --- a/venv/Lib/site-packages/django/contrib/gis/sitemaps/views.py +++ b/venv/Lib/site-packages/django/contrib/gis/sitemaps/views.py @@ -17,10 +17,7 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB try: klass = apps.get_model(label, model) except LookupError: - raise Http404( - 'You must supply a valid app label and module name. Got "%s.%s"' - % (label, model) - ) + raise Http404('You must supply a valid app label and module name. Got "%s.%s"' % (label, model)) if field_name: try: @@ -28,7 +25,7 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB if not isinstance(field, GeometryField): raise FieldDoesNotExist except FieldDoesNotExist: - raise Http404("Invalid geometry field.") + raise Http404('Invalid geometry field.') connection = connections[using] @@ -41,9 +38,8 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB placemarks = [] if connection.features.has_Transform_function: qs = klass._default_manager.using(using).annotate( - **{"%s_4326" % field_name: Transform(field_name, 4326)} - ) - field_name += "_4326" + **{'%s_4326' % field_name: Transform(field_name, 4326)}) + field_name += '_4326' else: qs = klass._default_manager.using(using).all() for mod in qs: @@ -55,7 +51,7 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB render = render_to_kmz else: render = render_to_kml - return render("gis/kml/placemarks.kml", {"places": placemarks}) + return render('gis/kml/placemarks.kml', {'places': placemarks}) def kmz(request, label, model, field_name=None, using=DEFAULT_DB_ALIAS): diff --git a/venv/Lib/site-packages/django/contrib/gis/utils/__init__.py b/venv/Lib/site-packages/django/contrib/gis/utils/__init__.py index 26334fb..c195ded 100644 --- a/venv/Lib/site-packages/django/contrib/gis/utils/__init__.py +++ b/venv/Lib/site-packages/django/contrib/gis/utils/__init__.py @@ -10,8 +10,7 @@ try: # LayerMapping requires DJANGO_SETTINGS_MODULE to be set, # and ImproperlyConfigured is raised if that's not the case. from django.contrib.gis.utils.layermapping import ( # NOQA - LayerMapError, - LayerMapping, + LayerMapError, LayerMapping, ) except ImproperlyConfigured: pass diff --git a/venv/Lib/site-packages/django/contrib/gis/utils/layermapping.py b/venv/Lib/site-packages/django/contrib/gis/utils/layermapping.py index 922deee..ad7b961 100644 --- a/venv/Lib/site-packages/django/contrib/gis/utils/layermapping.py +++ b/venv/Lib/site-packages/django/contrib/gis/utils/layermapping.py @@ -7,26 +7,16 @@ https://docs.djangoproject.com/en/dev/ref/contrib/gis/layermapping/ """ import sys -from decimal import Decimal -from decimal import InvalidOperation as DecimalInvalidOperation +from decimal import Decimal, InvalidOperation as DecimalInvalidOperation from pathlib import Path from django.contrib.gis.db.models import GeometryField from django.contrib.gis.gdal import ( - CoordTransform, - DataSource, - GDALException, - OGRGeometry, - OGRGeomType, + CoordTransform, DataSource, GDALException, OGRGeometry, OGRGeomType, SpatialReference, ) from django.contrib.gis.gdal.field import ( - OFTDate, - OFTDateTime, - OFTInteger, - OFTInteger64, - OFTReal, - OFTString, + OFTDate, OFTDateTime, OFTInteger, OFTInteger64, OFTReal, OFTString, OFTTime, ) from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist @@ -60,12 +50,12 @@ class LayerMapping: # Acceptable 'base' types for a multi-geometry type. MULTI_TYPES = { - 1: OGRGeomType("MultiPoint"), - 2: OGRGeomType("MultiLineString"), - 3: OGRGeomType("MultiPolygon"), - OGRGeomType("Point25D").num: OGRGeomType("MultiPoint25D"), - OGRGeomType("LineString25D").num: OGRGeomType("MultiLineString25D"), - OGRGeomType("Polygon25D").num: OGRGeomType("MultiPolygon25D"), + 1: OGRGeomType('MultiPoint'), + 2: OGRGeomType('MultiLineString'), + 3: OGRGeomType('MultiPolygon'), + OGRGeomType('Point25D').num: OGRGeomType('MultiPoint25D'), + OGRGeomType('LineString25D').num: OGRGeomType('MultiLineString25D'), + OGRGeomType('Polygon25D').num: OGRGeomType('MultiPolygon25D'), } # Acceptable Django field types and corresponding acceptable OGR # counterparts. @@ -93,19 +83,10 @@ class LayerMapping: models.PositiveSmallIntegerField: (OFTInteger, OFTReal, OFTString), } - def __init__( - self, - model, - data, - mapping, - layer=0, - source_srs=None, - encoding="utf-8", - transaction_mode="commit_on_success", - transform=True, - unique=None, - using=None, - ): + def __init__(self, model, data, mapping, layer=0, + source_srs=None, encoding='utf-8', + transaction_mode='commit_on_success', + transform=True, unique=None, using=None): """ A LayerMapping object is initialized using the given Model (not an instance), a DataSource (or string path to an OGR-supported data file), and a mapping @@ -151,7 +132,6 @@ class LayerMapping: # Making sure the encoding exists, if not a LookupError # exception will be thrown. from codecs import lookup - lookup(encoding) self.encoding = encoding else: @@ -159,7 +139,7 @@ class LayerMapping: if unique: self.check_unique(unique) - transaction_mode = "autocommit" # Has to be set to autocommit. + transaction_mode = 'autocommit' # Has to be set to autocommit. self.unique = unique else: self.unique = None @@ -167,12 +147,12 @@ class LayerMapping: # Setting the transaction decorator with the function in the # transaction modes dictionary. self.transaction_mode = transaction_mode - if transaction_mode == "autocommit": + if transaction_mode == 'autocommit': self.transaction_decorator = None - elif transaction_mode == "commit_on_success": + elif transaction_mode == 'commit_on_success': self.transaction_decorator = transaction.atomic else: - raise LayerMapError("Unrecognized transaction mode: %s" % transaction_mode) + raise LayerMapError('Unrecognized transaction mode: %s' % transaction_mode) # #### Checking routines used during initialization #### def check_fid_range(self, fid_range): @@ -209,9 +189,7 @@ class LayerMapping: try: idx = ogr_fields.index(ogr_map_fld) except ValueError: - raise LayerMapError( - 'Given mapping OGR field "%s" not found in OGR Layer.' % ogr_map_fld - ) + raise LayerMapError('Given mapping OGR field "%s" not found in OGR Layer.' % ogr_map_fld) return idx # No need to increment through each feature in the model, simply check @@ -222,44 +200,32 @@ class LayerMapping: try: model_field = self.model._meta.get_field(field_name) except FieldDoesNotExist: - raise LayerMapError( - 'Given mapping field "%s" not in given Model fields.' % field_name - ) + raise LayerMapError('Given mapping field "%s" not in given Model fields.' % field_name) # Getting the string name for the Django field class (e.g., 'PointField'). fld_name = model_field.__class__.__name__ if isinstance(model_field, GeometryField): if self.geom_field: - raise LayerMapError( - "LayerMapping does not support more than one GeometryField per " - "model." - ) + raise LayerMapError('LayerMapping does not support more than one GeometryField per model.') # Getting the coordinate dimension of the geometry field. coord_dim = model_field.dim try: if coord_dim == 3: - gtype = OGRGeomType(ogr_name + "25D") + gtype = OGRGeomType(ogr_name + '25D') else: gtype = OGRGeomType(ogr_name) except GDALException: - raise LayerMapError( - 'Invalid mapping for GeometryField "%s".' % field_name - ) + raise LayerMapError('Invalid mapping for GeometryField "%s".' % field_name) # Making sure that the OGR Layer's Geometry is compatible. ltype = self.layer.geom_type - if not ( - ltype.name.startswith(gtype.name) - or self.make_multi(ltype, model_field) - ): - raise LayerMapError( - "Invalid mapping geometry; model has %s%s, " - "layer geometry type is %s." - % (fld_name, "(dim=3)" if coord_dim == 3 else "", ltype) - ) + if not (ltype.name.startswith(gtype.name) or self.make_multi(ltype, model_field)): + raise LayerMapError('Invalid mapping geometry; model has %s%s, ' + 'layer geometry type is %s.' % + (fld_name, '(dim=3)' if coord_dim == 3 else '', ltype)) # Setting the `geom_field` attribute w/the name of the model field # that is a Geometry. Also setting the coordinate dimension @@ -276,19 +242,15 @@ class LayerMapping: try: rel_model._meta.get_field(rel_name) except FieldDoesNotExist: - raise LayerMapError( - 'ForeignKey mapping field "%s" not in %s fields.' - % (rel_name, rel_model.__class__.__name__) - ) + raise LayerMapError('ForeignKey mapping field "%s" not in %s fields.' % + (rel_name, rel_model.__class__.__name__)) fields_val = rel_model else: - raise TypeError("ForeignKey mapping must be of dictionary type.") + raise TypeError('ForeignKey mapping must be of dictionary type.') else: # Is the model field type supported by LayerMapping? if model_field.__class__ not in self.FIELD_TYPES: - raise LayerMapError( - 'Django field type "%s" has no OGR mapping (yet).' % fld_name - ) + raise LayerMapError('Django field type "%s" has no OGR mapping (yet).' % fld_name) # Is the OGR field in the Layer? idx = check_ogr_fld(ogr_name) @@ -296,10 +258,8 @@ class LayerMapping: # Can the OGR field type be mapped to the Django field type? if not issubclass(ogr_field, self.FIELD_TYPES[model_field.__class__]): - raise LayerMapError( - 'OGR field "%s" (of type %s) cannot be mapped to Django %s.' - % (ogr_field, ogr_field.__name__, fld_name) - ) + raise LayerMapError('OGR field "%s" (of type %s) cannot be mapped to Django %s.' % + (ogr_field, ogr_field.__name__, fld_name)) fields_val = model_field self.fields[field_name] = fields_val @@ -318,7 +278,7 @@ class LayerMapping: sr = self.layer.srs if not sr: - raise LayerMapError("No source reference system defined.") + raise LayerMapError('No source reference system defined.') else: return sr @@ -334,9 +294,7 @@ class LayerMapping: if unique not in self.mapping: raise ValueError else: - raise TypeError( - "Unique keyword argument must be set with a tuple, list, or string." - ) + raise TypeError('Unique keyword argument must be set with a tuple, list, or string.') # Keyword argument retrieval routines #### def feature_kwargs(self, feat): @@ -357,7 +315,7 @@ class LayerMapping: try: val = self.verify_geom(feat.geom, model_field) except GDALException: - raise LayerMapError("Could not retrieve geometry from feature.") + raise LayerMapError('Could not retrieve geometry from feature.') elif isinstance(model_field, models.base.ModelBase): # The related _model_, not a field was passed in -- indicating # another mapping for the related Model. @@ -389,34 +347,23 @@ class LayerMapping: Verify if the OGR Field contents are acceptable to the model field. If they are, return the verified value, otherwise raise an exception. """ - if isinstance(ogr_field, OFTString) and isinstance( - model_field, (models.CharField, models.TextField) - ): + if (isinstance(ogr_field, OFTString) and + isinstance(model_field, (models.CharField, models.TextField))): if self.encoding and ogr_field.value is not None: # The encoding for OGR data sources may be specified here # (e.g., 'cp437' for Census Bureau boundary files). val = force_str(ogr_field.value, self.encoding) else: val = ogr_field.value - if ( - model_field.max_length - and val is not None - and len(val) > model_field.max_length - ): - raise InvalidString( - "%s model field maximum string length is %s, given %s characters." - % (model_field.name, model_field.max_length, len(val)) - ) - elif isinstance(ogr_field, OFTReal) and isinstance( - model_field, models.DecimalField - ): + if model_field.max_length and val is not None and len(val) > model_field.max_length: + raise InvalidString('%s model field maximum string length is %s, given %s characters.' % + (model_field.name, model_field.max_length, len(val))) + elif isinstance(ogr_field, OFTReal) and isinstance(model_field, models.DecimalField): try: # Creating an instance of the Decimal value to use. d = Decimal(str(ogr_field.value)) except DecimalInvalidOperation: - raise InvalidDecimal( - "Could not construct decimal from: %s" % ogr_field.value - ) + raise InvalidDecimal('Could not construct decimal from: %s' % ogr_field.value) # Getting the decimal value as a tuple. dtup = d.as_tuple() @@ -437,21 +384,17 @@ class LayerMapping: # InvalidDecimal exception. if n_prec > max_prec: raise InvalidDecimal( - "A DecimalField with max_digits %d, decimal_places %d must " - "round to an absolute value less than 10^%d." - % (model_field.max_digits, model_field.decimal_places, max_prec) + 'A DecimalField with max_digits %d, decimal_places %d must ' + 'round to an absolute value less than 10^%d.' % + (model_field.max_digits, model_field.decimal_places, max_prec) ) val = d - elif isinstance(ogr_field, (OFTReal, OFTString)) and isinstance( - model_field, models.IntegerField - ): + elif isinstance(ogr_field, (OFTReal, OFTString)) and isinstance(model_field, models.IntegerField): # Attempt to convert any OFTReal and OFTString value to an OFTInteger. try: val = int(ogr_field.value) except ValueError: - raise InvalidInteger( - "Could not construct integer from: %s" % ogr_field.value - ) + raise InvalidInteger('Could not construct integer from: %s' % ogr_field.value) else: val = ogr_field.value return val @@ -468,17 +411,15 @@ class LayerMapping: # Constructing and verifying the related model keyword arguments. fk_kwargs = {} for field_name, ogr_name in rel_mapping.items(): - fk_kwargs[field_name] = self.verify_ogr_field( - feat[ogr_name], rel_model._meta.get_field(field_name) - ) + fk_kwargs[field_name] = self.verify_ogr_field(feat[ogr_name], rel_model._meta.get_field(field_name)) # Attempting to retrieve and return the related model. try: return rel_model.objects.using(self.using).get(**fk_kwargs) except ObjectDoesNotExist: raise MissingForeignKey( - "No ForeignKey %s model found with keyword arguments: %s" - % (rel_model.__name__, fk_kwargs) + 'No ForeignKey %s model found with keyword arguments: %s' % + (rel_model.__name__, fk_kwargs) ) def verify_geom(self, geom, model_field): @@ -514,17 +455,13 @@ class LayerMapping: SpatialRefSys = self.spatial_backend.spatial_ref_sys() try: # Getting the target spatial reference system - target_srs = ( - SpatialRefSys.objects.using(self.using) - .get(srid=self.geo_field.srid) - .srs - ) + target_srs = SpatialRefSys.objects.using(self.using).get(srid=self.geo_field.srid).srs # Creating the CoordTransform object return CoordTransform(self.source_srs, target_srs) except Exception as exc: raise LayerMapError( - "Could not translate between the data source and model geometry." + 'Could not translate between the data source and model geometry.' ) from exc def geometry_field(self): @@ -539,21 +476,11 @@ class LayerMapping: Given the OGRGeomType for a geometry and its associated GeometryField, determine whether the geometry should be turned into a GeometryCollection. """ - return ( - geom_type.num in self.MULTI_TYPES - and model_field.__class__.__name__ == "Multi%s" % geom_type.django - ) + return (geom_type.num in self.MULTI_TYPES and + model_field.__class__.__name__ == 'Multi%s' % geom_type.django) - def save( - self, - verbose=False, - fid_range=False, - step=False, - progress=False, - silent=False, - stream=sys.stdout, - strict=False, - ): + def save(self, verbose=False, fid_range=False, step=False, + progress=False, silent=False, stream=sys.stdout, strict=False): """ Save the contents from the OGR DataSource Layer into the database according to the mapping dictionary given at initialization. @@ -619,9 +546,7 @@ class LayerMapping: if strict: raise elif not silent: - stream.write( - "Ignoring Feature ID %s because: %s\n" % (feat.fid, msg) - ) + stream.write('Ignoring Feature ID %s because: %s\n' % (feat.fid, msg)) else: # Constructing the model using the keyword args is_update = False @@ -659,29 +584,23 @@ class LayerMapping: m.save(using=self.using) num_saved += 1 if verbose: - stream.write( - "%s: %s\n" % ("Updated" if is_update else "Saved", m) - ) + stream.write('%s: %s\n' % ('Updated' if is_update else 'Saved', m)) except Exception as msg: if strict: # Bailing out if the `strict` keyword is set. if not silent: stream.write( - "Failed to save the feature (id: %s) into the " - "model with the keyword arguments:\n" % feat.fid + 'Failed to save the feature (id: %s) into the ' + 'model with the keyword arguments:\n' % feat.fid ) - stream.write("%s\n" % kwargs) + stream.write('%s\n' % kwargs) raise elif not silent: - stream.write( - "Failed to save %s:\n %s\nContinuing\n" % (kwargs, msg) - ) + stream.write('Failed to save %s:\n %s\nContinuing\n' % (kwargs, msg)) # Printing progress information, if requested. if progress and num_feat % progress_interval == 0: - stream.write( - "Processed %d features, saved %d ...\n" % (num_feat, num_saved) - ) + stream.write('Processed %d features, saved %d ...\n' % (num_feat, num_saved)) # Only used for status output purposes -- incremental saving uses the # values returned here. @@ -694,10 +613,7 @@ class LayerMapping: if step and isinstance(step, int) and step < nfeat: # Incremental saving is requested at the given interval (step) if default_range: - raise LayerMapError( - "The `step` keyword may not be used in conjunction with the " - "`fid_range` keyword." - ) + raise LayerMapError('The `step` keyword may not be used in conjunction with the `fid_range` keyword.') beg, num_feat, num_saved = (0, 0, 0) indices = range(step, nfeat, step) n_i = len(indices) @@ -714,9 +630,7 @@ class LayerMapping: num_feat, num_saved = _save(step_slice, num_feat, num_saved) beg = end except Exception: # Deliberately catch everything - stream.write( - "%s\nFailed to save slice: %s\n" % ("=-" * 20, step_slice) - ) + stream.write('%s\nFailed to save slice: %s\n' % ('=-' * 20, step_slice)) raise else: # Otherwise, just calling the previously defined _save() function. diff --git a/venv/Lib/site-packages/django/contrib/gis/utils/ogrinfo.py b/venv/Lib/site-packages/django/contrib/gis/utils/ogrinfo.py index eafa23c..324e41a 100644 --- a/venv/Lib/site-packages/django/contrib/gis/utils/ogrinfo.py +++ b/venv/Lib/site-packages/django/contrib/gis/utils/ogrinfo.py @@ -20,9 +20,7 @@ def ogrinfo(data_source, num_features=10): elif isinstance(data_source, DataSource): pass else: - raise Exception( - "Data source parameter must be a string or a DataSource object." - ) + raise Exception('Data source parameter must be a string or a DataSource object.') for i, layer in enumerate(data_source): print("data source : %s" % data_source.name) @@ -46,8 +44,8 @@ def ogrinfo(data_source, num_features=10): if isinstance(val, str): val_fmt = ' ("%s")' else: - val_fmt = " (%s)" + val_fmt = ' (%s)' output += val_fmt % val else: - output += " (None)" + output += ' (None)' print(output) diff --git a/venv/Lib/site-packages/django/contrib/gis/utils/ogrinspect.py b/venv/Lib/site-packages/django/contrib/gis/utils/ogrinspect.py index 40ca0cb..8c83fd5 100644 --- a/venv/Lib/site-packages/django/contrib/gis/utils/ogrinspect.py +++ b/venv/Lib/site-packages/django/contrib/gis/utils/ogrinspect.py @@ -5,17 +5,12 @@ models for GeoDjango and/or mapping dictionaries for use with the """ from django.contrib.gis.gdal import DataSource from django.contrib.gis.gdal.field import ( - OFTDate, - OFTDateTime, - OFTInteger, - OFTInteger64, - OFTReal, - OFTString, + OFTDate, OFTDateTime, OFTInteger, OFTInteger64, OFTReal, OFTString, OFTTime, ) -def mapping(data_source, geom_name="geom", layer_key=0, multi_geom=False): +def mapping(data_source, geom_name='geom', layer_key=0, multi_geom=False): """ Given a DataSource, generate a dictionary that may be used for invoking the LayerMapping utility. @@ -35,9 +30,7 @@ def mapping(data_source, geom_name="geom", layer_key=0, multi_geom=False): elif isinstance(data_source, DataSource): pass else: - raise TypeError( - "Data source parameter must be a string or a DataSource object." - ) + raise TypeError('Data source parameter must be a string or a DataSource object.') # Creating the dictionary. _mapping = {} @@ -45,8 +38,8 @@ def mapping(data_source, geom_name="geom", layer_key=0, multi_geom=False): # Generating the field name for each field in the layer. for field in data_source[layer_key].fields: mfield = field.lower() - if mfield[-1:] == "_": - mfield += "field" + if mfield[-1:] == '_': + mfield += 'field' _mapping[mfield] = field gtype = data_source[layer_key].geom_type if multi_geom: @@ -123,22 +116,12 @@ def ogrinspect(*args, **kwargs): Note: Call the _ogrinspect() helper to do the heavy lifting. """ - return "\n".join(_ogrinspect(*args, **kwargs)) + return '\n'.join(_ogrinspect(*args, **kwargs)) -def _ogrinspect( - data_source, - model_name, - geom_name="geom", - layer_key=0, - srid=None, - multi_geom=False, - name_field=None, - imports=True, - decimal=False, - blank=False, - null=False, -): +def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=None, + multi_geom=False, name_field=None, imports=True, + decimal=False, blank=False, null=False): """ Helper routine for `ogrinspect` that generates GeoDjango models corresponding to the given data source. See the `ogrinspect` docstring for more details. @@ -149,9 +132,7 @@ def _ogrinspect( elif isinstance(data_source, DataSource): pass else: - raise TypeError( - "Data source parameter must be a string or a DataSource object." - ) + raise TypeError('Data source parameter must be a string or a DataSource object.') # Getting the layer corresponding to the layer key and getting # a string listing of all OGR fields in the Layer. @@ -167,7 +148,6 @@ def _ogrinspect( return [s.lower() for s in ogr_fields] else: return [] - null_fields = process_kwarg(null) blank_fields = process_kwarg(blank) decimal_fields = process_kwarg(decimal) @@ -176,30 +156,29 @@ def _ogrinspect( def get_kwargs_str(field_name): kwlist = [] if field_name.lower() in null_fields: - kwlist.append("null=True") + kwlist.append('null=True') if field_name.lower() in blank_fields: - kwlist.append("blank=True") + kwlist.append('blank=True') if kwlist: - return ", " + ", ".join(kwlist) + return ', ' + ', '.join(kwlist) else: - return "" + return '' # For those wishing to disable the imports. if imports: - yield "# This is an auto-generated Django model module created by ogrinspect." - yield "from django.contrib.gis.db import models" - yield "" - yield "" + yield '# This is an auto-generated Django model module created by ogrinspect.' + yield 'from django.contrib.gis.db import models' + yield '' + yield '' - yield "class %s(models.Model):" % model_name + yield 'class %s(models.Model):' % model_name for field_name, width, precision, field_type in zip( - ogr_fields, layer.field_widths, layer.field_precisions, layer.field_types - ): + ogr_fields, layer.field_widths, layer.field_precisions, layer.field_types): # The model field name. mfield = field_name.lower() - if mfield[-1:] == "_": - mfield += "field" + if mfield[-1:] == '_': + mfield += 'field' # Getting the keyword args string. kwargs_str = get_kwargs_str(field_name) @@ -209,34 +188,25 @@ def _ogrinspect( # may also be mapped to `DecimalField` if specified in the # `decimal` keyword. if field_name.lower() in decimal_fields: - yield ( - " %s = models.DecimalField(max_digits=%d, decimal_places=%d%s)" - ) % ( - mfield, - width, - precision, - kwargs_str, + yield ' %s = models.DecimalField(max_digits=%d, decimal_places=%d%s)' % ( + mfield, width, precision, kwargs_str ) else: - yield " %s = models.FloatField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.FloatField(%s)' % (mfield, kwargs_str[2:]) elif field_type is OFTInteger: - yield " %s = models.IntegerField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.IntegerField(%s)' % (mfield, kwargs_str[2:]) elif field_type is OFTInteger64: - yield " %s = models.BigIntegerField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.BigIntegerField(%s)' % (mfield, kwargs_str[2:]) elif field_type is OFTString: - yield " %s = models.CharField(max_length=%s%s)" % ( - mfield, - width, - kwargs_str, - ) + yield ' %s = models.CharField(max_length=%s%s)' % (mfield, width, kwargs_str) elif field_type is OFTDate: - yield " %s = models.DateField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.DateField(%s)' % (mfield, kwargs_str[2:]) elif field_type is OFTDateTime: - yield " %s = models.DateTimeField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.DateTimeField(%s)' % (mfield, kwargs_str[2:]) elif field_type is OFTTime: - yield " %s = models.TimeField(%s)" % (mfield, kwargs_str[2:]) + yield ' %s = models.TimeField(%s)' % (mfield, kwargs_str[2:]) else: - raise TypeError("Unknown field type %s in %s" % (field_type, mfield)) + raise TypeError('Unknown field type %s in %s' % (field_type, mfield)) # TODO: Autodetection of multigeometry types (see #7218). gtype = layer.geom_type @@ -247,21 +217,21 @@ def _ogrinspect( # Setting up the SRID keyword string. if srid is None: if layer.srs is None: - srid_str = "srid=-1" + srid_str = 'srid=-1' else: srid = layer.srs.srid if srid is None: - srid_str = "srid=-1" + srid_str = 'srid=-1' elif srid == 4326: # WGS84 is already the default. - srid_str = "" + srid_str = '' else: - srid_str = "srid=%s" % srid + srid_str = 'srid=%s' % srid else: - srid_str = "srid=%s" % srid + srid_str = 'srid=%s' % srid - yield " %s = models.%s(%s)" % (geom_name, geom_field, srid_str) + yield ' %s = models.%s(%s)' % (geom_name, geom_field, srid_str) if name_field: - yield "" - yield " def __str__(self): return self.%s" % name_field + yield '' + yield ' def __str__(self): return self.%s' % name_field diff --git a/venv/Lib/site-packages/django/contrib/gis/utils/srs.py b/venv/Lib/site-packages/django/contrib/gis/utils/srs.py index 2204767..b10cf26 100644 --- a/venv/Lib/site-packages/django/contrib/gis/utils/srs.py +++ b/venv/Lib/site-packages/django/contrib/gis/utils/srs.py @@ -2,9 +2,8 @@ from django.contrib.gis.gdal import SpatialReference from django.db import DEFAULT_DB_ALIAS, connections -def add_srs_entry( - srs, auth_name="EPSG", auth_srid=None, ref_sys_name=None, database=None -): +def add_srs_entry(srs, auth_name='EPSG', auth_srid=None, ref_sys_name=None, + database=None): """ Take a GDAL SpatialReference system and add its information to the `spatial_ref_sys` table of the spatial backend. Doing this enables @@ -36,10 +35,11 @@ def add_srs_entry( database = database or DEFAULT_DB_ALIAS connection = connections[database] - if not hasattr(connection.ops, "spatial_version"): - raise Exception("The `add_srs_entry` utility only works with spatial backends.") + if not hasattr(connection.ops, 'spatial_version'): + raise Exception('The `add_srs_entry` utility only works ' + 'with spatial backends.') if not connection.features.supports_add_srs_entry: - raise Exception("This utility does not support your database backend.") + raise Exception('This utility does not support your database backend.') SpatialRefSys = connection.ops.spatial_ref_sys() # If argument is not a `SpatialReference` instance, use it as parameter @@ -48,26 +48,24 @@ def add_srs_entry( srs = SpatialReference(srs) if srs.srid is None: - raise Exception( - "Spatial reference requires an SRID to be " - "compatible with the spatial backend." - ) + raise Exception('Spatial reference requires an SRID to be ' + 'compatible with the spatial backend.') # Initializing the keyword arguments dictionary for both PostGIS # and SpatiaLite. kwargs = { - "srid": srs.srid, - "auth_name": auth_name, - "auth_srid": auth_srid or srs.srid, - "proj4text": srs.proj4, + 'srid': srs.srid, + 'auth_name': auth_name, + 'auth_srid': auth_srid or srs.srid, + 'proj4text': srs.proj4, } # Backend-specific fields for the SpatialRefSys model. srs_field_names = {f.name for f in SpatialRefSys._meta.get_fields()} - if "srtext" in srs_field_names: - kwargs["srtext"] = srs.wkt - if "ref_sys_name" in srs_field_names: + if 'srtext' in srs_field_names: + kwargs['srtext'] = srs.wkt + if 'ref_sys_name' in srs_field_names: # SpatiaLite specific - kwargs["ref_sys_name"] = ref_sys_name or srs.name + kwargs['ref_sys_name'] = ref_sys_name or srs.name # Creating the spatial_ref_sys model. try: diff --git a/venv/Lib/site-packages/django/contrib/gis/views.py b/venv/Lib/site-packages/django/contrib/gis/views.py index 346fb5b..5b29db9 100644 --- a/venv/Lib/site-packages/django/contrib/gis/views.py +++ b/venv/Lib/site-packages/django/contrib/gis/views.py @@ -7,16 +7,14 @@ def feed(request, url, feed_dict=None): if not feed_dict: raise Http404(_("No feeds are registered.")) - slug = url.partition("/")[0] + slug = url.partition('/')[0] try: f = feed_dict[slug] except KeyError: - raise Http404(_("Slug %r isn’t registered.") % slug) + raise Http404(_('Slug %r isn’t registered.') % slug) instance = f() - instance.feed_url = getattr(f, "feed_url", None) or request.path - instance.title_template = f.title_template or ("feeds/%s_title.html" % slug) - instance.description_template = f.description_template or ( - "feeds/%s_description.html" % slug - ) + instance.feed_url = getattr(f, 'feed_url', None) or request.path + instance.title_template = f.title_template or ('feeds/%s_title.html' % slug) + instance.description_template = f.description_template or ('feeds/%s_description.html' % slug) return instance(request) diff --git a/venv/Lib/site-packages/django/contrib/humanize/apps.py b/venv/Lib/site-packages/django/contrib/humanize/apps.py index 817ae96..c5fcbca 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/apps.py +++ b/venv/Lib/site-packages/django/contrib/humanize/apps.py @@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _ class HumanizeConfig(AppConfig): - name = "django.contrib.humanize" + name = 'django.contrib.humanize' verbose_name = _("Humanize") diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo index 169bb73..71c8c4f 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.po index 94c7f87..b739fbf 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/ar/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2020-2021 +# Bashar Al-Abdulhadi, 2020 # Bashar Al-Abdulhadi, 2014 # Eyad Toma <d.eyad.t@gmail.com>, 2013 # Jannis Leidel <jannis@leidel.info>, 2011 @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-15 21:36+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2020-07-15 00:48+0000\n" "Last-Translator: Bashar Al-Abdulhadi\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" @@ -80,6 +80,16 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f مليون" +msgstr[1] "%(value).1f مليون" +msgstr[2] "%(value).1f مليونان" +msgstr[3] "%(value).1f مليون" +msgstr[4] "%(value).1f مليون" +msgstr[5] "%(value).1f مليون" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -90,6 +100,16 @@ msgstr[3] "%(value)s ملايين" msgstr[4] "%(value)s مليون" msgstr[5] "%(value)s مليون" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f مليار" +msgstr[1] "%(value).1f مليار" +msgstr[2] "%(value).1f ملياران" +msgstr[3] "%(value).1f مليار" +msgstr[4] "%(value).1f مليار" +msgstr[5] "%(value).1f مليار" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -100,6 +120,16 @@ msgstr[3] "%(value)s مليار" msgstr[4] "%(value)s مليار" msgstr[5] "%(value)s مليار" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f ترليون" +msgstr[1] "%(value).1f ترليون" +msgstr[2] "%(value).1f ترليونان" +msgstr[3] "%(value).1f ترليونات" +msgstr[4] "%(value).1f ترليون" +msgstr[5] "%(value).1f ترليون" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -110,6 +140,16 @@ msgstr[3] "%(value)s ترليون" msgstr[4] "%(value)s ترليون" msgstr[5] "%(value)s ترليون" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f كوادرليون" +msgstr[1] "%(value).1f كوادرليون" +msgstr[2] "%(value).1f كوادرليون" +msgstr[3] "%(value).1f كوادرليون" +msgstr[4] "%(value).1f كوادرليون" +msgstr[5] "%(value).1f كوادرليون" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -120,6 +160,16 @@ msgstr[3] "%(value)s كوادرليون" msgstr[4] "%(value)s كوادرليون" msgstr[5] "%(value)s كوادرليون" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f كوينتيليون" +msgstr[1] "%(value).1f كوينتيليون" +msgstr[2] "%(value).1f كوينتيليون" +msgstr[3] "%(value).1f كوينتيليون" +msgstr[4] "%(value).1f كوينتيليون" +msgstr[5] "%(value).1f كوينتيليون" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -130,6 +180,16 @@ msgstr[3] "%(value)s كوينتيليون" msgstr[4] "%(value)s كوينتيليون" msgstr[5] "%(value)s كوينتيليون" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f سكستيليون" +msgstr[1] "%(value).1f سكستيليون" +msgstr[2] "%(value).1f سكستيليون" +msgstr[3] "%(value).1f سكستيليون" +msgstr[4] "%(value).1f سكستيليون" +msgstr[5] "%(value).1f سكستيليون" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -140,6 +200,16 @@ msgstr[3] "%(value)s سكستيليون" msgstr[4] "%(value)s سكستيليون" msgstr[5] "%(value)s سكستيليون" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f سبتيليون" +msgstr[1] "%(value).1f سبتيليون" +msgstr[2] "%(value).1f سبتيليون" +msgstr[3] "%(value).1f سبتيليون" +msgstr[4] "%(value).1f سبتيليون" +msgstr[5] "%(value).1f سبتيليون" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -150,6 +220,16 @@ msgstr[3] "%(value)s سبتيليون" msgstr[4] "%(value)s سبتيليون" msgstr[5] "%(value)s سبتيليون" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f أقتيليون" +msgstr[1] "%(value).1f أقتيليون" +msgstr[2] "%(value).1f أقتيليون" +msgstr[3] "%(value).1f أقتيليون" +msgstr[4] "%(value).1f أقتيليون" +msgstr[5] "%(value).1f أقتيليون" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -160,6 +240,16 @@ msgstr[3] "%(value)s أقتيليون" msgstr[4] "%(value)s أقتيليون" msgstr[5] "%(value)s أقتيليون" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f نانليون" +msgstr[1] "%(value).1f نانليون" +msgstr[2] "%(value).1f نانليون" +msgstr[3] "%(value).1f نانليون" +msgstr[4] "%(value).1f نانليون" +msgstr[5] "%(value).1f نانليون" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -170,6 +260,16 @@ msgstr[3] "%(value)s نانليون" msgstr[4] "%(value)s نانليون" msgstr[5] "%(value)s نانليون" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f ديسيليون" +msgstr[1] "%(value).1f ديسيليون" +msgstr[2] "%(value).1f ديسيليون" +msgstr[3] "%(value).1f ديسيليون" +msgstr[4] "%(value).1f ديسيليون" +msgstr[5] "%(value).1f ديسيليون" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -180,6 +280,16 @@ msgstr[3] "%(value)s ديسيليون" msgstr[4] "%(value)s ديسيليون" msgstr[5] "%(value)s ديسيليون" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f جوجول" +msgstr[1] "%(value).1f جوجول" +msgstr[2] "%(value).1f جوجول" +msgstr[3] "%(value).1f جوجول" +msgstr[4] "%(value).1f جوجول" +msgstr[5] "%(value).1f جوجول" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -316,134 +426,134 @@ msgstr "%(delta)s من الآن" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d سنة" -msgstr[1] "%(num)d سنة" -msgstr[2] "%(num)d سنتين" -msgstr[3] "%(num)d سنوات" -msgstr[4] "%(num)d سنوات" -msgstr[5] "%(num)d سنوات" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d سنة" +msgstr[1] "%d سنة" +msgstr[2] "%d سنتين" +msgstr[3] "%d سنوات" +msgstr[4] "%d سنين" +msgstr[5] "%d سنوات" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d شهر" -msgstr[1] "%(num)d شهر" -msgstr[2] "%(num)d شهرين" -msgstr[3] "%(num)d أشهر" -msgstr[4] "%(num)d أشهر" -msgstr[5] "%(num)d أشهر" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d شهر" +msgstr[1] "%d شهر" +msgstr[2] "%d شهرين" +msgstr[3] "%d أشهر" +msgstr[4] "%d شهور" +msgstr[5] "%d أشهر" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d أسبوع" -msgstr[1] "%(num)d أسبوع" -msgstr[2] "%(num)d أسبوعين" -msgstr[3] "%(num)d أسابيع" -msgstr[4] "%(num)d أسبوع" -msgstr[5] "%(num)d أسابيع" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d أسبوع" +msgstr[1] "%d أسبوع" +msgstr[2] "%d أسبوعين" +msgstr[3] "%d أسابيع" +msgstr[4] "%d اسبوع" +msgstr[5] "%d أسابيع" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d يوم" -msgstr[1] "%(num)d يوم" -msgstr[2] "%(num)d يومين" -msgstr[3] "%(num)d أيام" -msgstr[4] "%(num)d يوم" -msgstr[5] "%(num)d أيام" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d يوم" +msgstr[1] "%d يوم" +msgstr[2] "%d يومين" +msgstr[3] "%d أيام" +msgstr[4] "%d أيام" +msgstr[5] "%d يوم" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ساعة" -msgstr[1] "%(num)d ساعة" -msgstr[2] "%(num)d ساعتين" -msgstr[3] "%(num)d ساعات" -msgstr[4] "%(num)d ساعة" -msgstr[5] "%(num)d ساعات" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ساعة" +msgstr[1] "%d ساعة" +msgstr[2] "%d ساعتين" +msgstr[3] "%d ساعات" +msgstr[4] "%d ساعة" +msgstr[5] "%d ساعة" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d دقيقة" -msgstr[1] "%(num)d دقيقة" -msgstr[2] "%(num)d دقيقتين" -msgstr[3] "%(num)d دقائق" -msgstr[4] "%(num)d دقيقة" -msgstr[5] "%(num)d دقيقة" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d دقيقة" +msgstr[1] "%d دقيقة" +msgstr[2] "%d دقيقتين" +msgstr[3] "%d دقائق" +msgstr[4] "%d دقيقة" +msgstr[5] "%d دقيقة" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d سنة" -msgstr[1] "%(num)d سنة" -msgstr[2] "%(num)d سنتين" -msgstr[3] "%(num)d سنوات" -msgstr[4] "%(num)d سنة" -msgstr[5] "%(num)d سنوات" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d سنة" +msgstr[1] "%d سنة" +msgstr[2] "%d سنتين" +msgstr[3] "%d سنوات" +msgstr[4] "%d سنين" +msgstr[5] "%d سنة" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d شهر" -msgstr[1] "%(num)d شهر" -msgstr[2] "%(num)d شهرين" -msgstr[3] "%(num)d أشهر" -msgstr[4] "%(num)d شهر" -msgstr[5] "%(num)d أشهر" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d شهر" +msgstr[1] "%d شهر" +msgstr[2] "%d شهرين" +msgstr[3] "%d شهور" +msgstr[4] "%d أشهر" +msgstr[5] "%d شهر" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d أسبوع" -msgstr[1] "%(num)d أسبوع" -msgstr[2] "%(num)d أسبوعين" -msgstr[3] "%(num)d أسابيع" -msgstr[4] "%(num)d أسبوع" -msgstr[5] "%(num)d أسابيع" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d أسبوع" +msgstr[1] "%d أسبوع" +msgstr[2] "%d أسبوعين" +msgstr[3] "%d أسابيع" +msgstr[4] "%d أسبوع" +msgstr[5] "%d أسبوع" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d يوم" -msgstr[1] "%(num)d يوم" -msgstr[2] "%(num)d يومين" -msgstr[3] "%(num)d أيام" -msgstr[4] "%(num)d يوم" -msgstr[5] "%(num)d أيام" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d يوم" +msgstr[1] "%d يوم" +msgstr[2] "%d يومين" +msgstr[3] "%d أيام" +msgstr[4] "%d يوم" +msgstr[5] "%d يوم" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ساعة" -msgstr[1] "%(num)d ساعة" -msgstr[2] "%(num)d ساعتين" -msgstr[3] "%(num)d ساعات" -msgstr[4] "%(num)d ساعة" -msgstr[5] "%(num)d ساعات" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ساعة" +msgstr[1] "%d ساعة" +msgstr[2] "%d ساعتين" +msgstr[3] "%d ساعات" +msgstr[4] "%d ساعة" +msgstr[5] "%d ساعة" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d دقيقة" -msgstr[1] "%(num)d دقيقة" -msgstr[2] "%(num)d دقيقتين" -msgstr[3] "%(num)d دقائق" -msgstr[4] "%(num)d دقيقة" -msgstr[5] "%(num)d دقيقة" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d دقيقة" +msgstr[1] "%d دقيقة" +msgstr[2] "%d دقيقتين" +msgstr[3] "%d دقائق" +msgstr[4] "%d دقيقة" +msgstr[5] "%d دقيقة" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.mo index ed7b846..508415d 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.po index 565b5b3..05441b0 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/be/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # # Translators: # Viktar Palstsiuk <vipals@gmail.com>, 2014-2015 -# znotdead <zhirafchik@gmail.com>, 2019,2021 +# znotdead <zhirafchik@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-22 16:47+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-10-16 19:15+0000\n" "Last-Translator: znotdead <zhirafchik@gmail.com>\n" "Language-Team: Belarusian (http://www.transifex.com/django/django/language/" "be/)\n" @@ -78,6 +78,14 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}ы" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f мільён" +msgstr[1] "%(value).1f мільёны" +msgstr[2] "%(value).1f мільёнаў" +msgstr[3] "%(value).1f мільёнаў" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -86,6 +94,14 @@ msgstr[1] "%(value)s мільёны" msgstr[2] "%(value)s мільёнаў" msgstr[3] "%(value)s мільёнаў" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f мільярд" +msgstr[1] "%(value).1f мільярды" +msgstr[2] "%(value).1f мільярдаў" +msgstr[3] "%(value).1f мільярдаў" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -94,6 +110,14 @@ msgstr[1] "%(value)s мільярды" msgstr[2] "%(value)s мільярдаў" msgstr[3] "%(value)s мільярдаў" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f трыльён" +msgstr[1] "%(value).1f трыльёны" +msgstr[2] "%(value).1f трыльёнаў" +msgstr[3] "%(value).1f трыльёнаў" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -102,6 +126,14 @@ msgstr[1] "%(value)s трыльёны" msgstr[2] "%(value)s трыльёнаў" msgstr[3] "%(value)s трыльёнаў" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f квадрыльён" +msgstr[1] "%(value).1f квадрыльёны" +msgstr[2] "%(value).1f квадрыльёнаў" +msgstr[3] "%(value).1f квадрыльёнаў" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -110,6 +142,14 @@ msgstr[1] "%(value)s квадрыльёны" msgstr[2] "%(value)s квадрыльёнаў" msgstr[3] "%(value)s квадрыльёнаў" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f квінтыльён" +msgstr[1] "%(value).1f квінтыльёны" +msgstr[2] "%(value).1f квінтыльёнаў" +msgstr[3] "%(value).1f квінтыльёнаў" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -118,6 +158,14 @@ msgstr[1] "%(value)s квінтыльёны" msgstr[2] "%(value)s квінтыльёнаў" msgstr[3] "%(value)s квінтыльёнаў" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f сэкстыльён" +msgstr[1] "%(value).1f сэкстыльёны" +msgstr[2] "%(value).1f сэкстыльёнаў" +msgstr[3] "%(value).1f сэкстыльёнаў" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -126,6 +174,14 @@ msgstr[1] "%(value)s сэкстыльёны" msgstr[2] "%(value)s сэкстыльёнаў" msgstr[3] "%(value)s сэкстыльёнаў" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f сэптыльён" +msgstr[1] "%(value).1f сэптыльёны" +msgstr[2] "%(value).1f сэптыльёнаў" +msgstr[3] "%(value).1f сэптыльёнаў" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -134,6 +190,14 @@ msgstr[1] "%(value)s сэптыльёны" msgstr[2] "%(value)s сэптыльёнаў" msgstr[3] "%(value)s сэптыльёнаў" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f актыльён" +msgstr[1] "%(value).1f актыльёны" +msgstr[2] "%(value).1f актыльёнаў" +msgstr[3] "%(value).1f актыльёнаў" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -142,6 +206,14 @@ msgstr[1] "%(value)s актыльёны" msgstr[2] "%(value)s актыльёнаў" msgstr[3] "%(value)s актыльёнаў" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f нанільён" +msgstr[1] "%(value).1f нанільёны" +msgstr[2] "%(value).1f нанільёнаў" +msgstr[3] "%(value).1f нанільёнаў" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -150,6 +222,14 @@ msgstr[1] "%(value)s нанільёны" msgstr[2] "%(value)s нанільёнаў" msgstr[3] "%(value)s нанільёнаў" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f дэцыльён" +msgstr[1] "%(value).1f дэцыльёны" +msgstr[2] "%(value).1f дэцыльёнаў" +msgstr[3] "%(value).1f дэцыльёнаў" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -158,6 +238,14 @@ msgstr[1] "%(value)s дэцыльёны" msgstr[2] "%(value)s дэцыльёнаў" msgstr[3] "%(value)s дэцыльёнаў" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f ґуґал" +msgstr[1] "%(value).1f ґуґлы" +msgstr[2] "%(value).1f ґуґлаў" +msgstr[3] "%(value).1f ґуґлаў" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -280,110 +368,110 @@ msgstr "праз %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d год" -msgstr[1] "%(num)d гадоў" -msgstr[2] "%(num)d гадоў" -msgstr[3] "%(num)d гадоў" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d год" +msgstr[1] "%d гады" +msgstr[2] "%d гадоў" +msgstr[3] "%d гадоў" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месяц" -msgstr[1] "%(num)d месяцаў" -msgstr[2] "%(num)d месяцаў" -msgstr[3] "%(num)d месяцаў" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месяц" +msgstr[1] "%d месяцы" +msgstr[2] "%d месяцаў" +msgstr[3] "%d месяцаў" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d тыдзень" -msgstr[1] "%(num)d тыдняў" -msgstr[2] "%(num)d тыдняў" -msgstr[3] "%(num)d тыдняў" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d тыдзень" +msgstr[1] "%d тыдні" +msgstr[2] "%d тыдняў" +msgstr[3] "%d тыдняў" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d дзень" -msgstr[1] "%(num)d дзён" -msgstr[2] "%(num)d дзён" -msgstr[3] "%(num)d дзён" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d дзень" +msgstr[1] "%d дні" +msgstr[2] "%d дзён" +msgstr[3] "%d дзён" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d гадзіна" -msgstr[1] "%(num)d гадзін" -msgstr[2] "%(num)d гадзін" -msgstr[3] "%(num)d гадзін" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d гадзіна" +msgstr[1] "%d гадзіны" +msgstr[2] "%d гадзін" +msgstr[3] "%d гадзін" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d хвіліна" -msgstr[1] "%(num)d хвілін" -msgstr[2] "%(num)d хвілін" -msgstr[3] "%(num)d хвілін" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d хвіліна" +msgstr[1] "%d хвіліны" +msgstr[2] "%d хвілінаў" +msgstr[3] "%d хвілінаў" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d год" -msgstr[1] "%(num)d гадоў" -msgstr[2] "%(num)d гадоў" -msgstr[3] "%(num)d гадоў" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d год" +msgstr[1] "%d гады" +msgstr[2] "%d гадоў" +msgstr[3] "%d гадоў" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месяц" -msgstr[1] "%(num)d месяцаў" -msgstr[2] "%(num)d месяцаў" -msgstr[3] "%(num)d месяцаў" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d месяц" +msgstr[1] "%d месяцы" +msgstr[2] "%d месяцаў" +msgstr[3] "%d месяцаў" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d тыдзень" -msgstr[1] "%(num)d тыдняў" -msgstr[2] "%(num)d тыдняў" -msgstr[3] "%(num)d тыдняў" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d тыдзень" +msgstr[1] "%d тыдні" +msgstr[2] "%d тыдняў" +msgstr[3] "%d тыдняў" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d дзень" -msgstr[1] "%(num)d дзён" -msgstr[2] "%(num)d дзён" -msgstr[3] "%(num)d дзён" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d дзень" +msgstr[1] "%d дня" +msgstr[2] "%d дзён" +msgstr[3] "%d дзён" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d гадзіна" -msgstr[1] "%(num)d гадзін" -msgstr[2] "%(num)d гадзін" -msgstr[3] "%(num)d гадзін" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d гадзіна" +msgstr[1] "%d гадзіны" +msgstr[2] "%d гадзін" +msgstr[3] "%d гадзін" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d хвіліна" -msgstr[1] "%(num)d хвілін" -msgstr[2] "%(num)d хвілін" -msgstr[3] "%(num)d хвілін" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d хвіліна" +msgstr[1] "%d хвіліны" +msgstr[2] "%d хвілінаў" +msgstr[3] "%d хвілінаў" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo index 85cdff8..fb3f047 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.po index a4f6ad6..994549d 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/bg/LC_MESSAGES/django.po @@ -1,18 +1,17 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Georgi Kostadinov <grgkostadinov@gmail.com>, 2012 # Jannis Leidel <jannis@leidel.info>, 2011 -# Todor Lubenov <tlubenov@gmail.com>, 2011,2015 +# Todor Lubenov <tgl.sysdev@gmail.com>, 2011,2015 # Venelin Stoykov <vkstoykov@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2022-01-14 11:48+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Venelin Stoykov <vkstoykov@gmail.com>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -22,62 +21,25 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Humanize" -msgstr "Очовечаване" +msgstr "Облагородявам" -#. Translators: Ordinal format for 11 (11th), 12 (12th), and 13 (13th). -msgctxt "ordinal 11, 12, 13" -msgid "{}th" -msgstr "{}ти" +msgid "th" +msgstr "и" -#. Translators: Ordinal format when value ends with 0, e.g. 80th. -msgctxt "ordinal 0" -msgid "{}th" -msgstr "{}ен" +msgid "st" +msgstr "ви" -#. Translators: Ordinal format when value ends with 1, e.g. 81st, except 11. -msgctxt "ordinal 1" -msgid "{}st" -msgstr "{}ви" +msgid "nd" +msgstr "ри" -#. Translators: Ordinal format when value ends with 2, e.g. 82nd, except 12. -msgctxt "ordinal 2" -msgid "{}nd" -msgstr "{}ри" +msgid "rd" +msgstr "ти" -#. Translators: Ordinal format when value ends with 3, e.g. 83th, except 13. -msgctxt "ordinal 3" -msgid "{}rd" -msgstr "{}ти" - -#. Translators: Ordinal format when value ends with 4, e.g. 84th. -msgctxt "ordinal 4" -msgid "{}th" -msgstr "{}ти" - -#. Translators: Ordinal format when value ends with 5, e.g. 85th. -msgctxt "ordinal 5" -msgid "{}th" -msgstr "{}ти" - -#. Translators: Ordinal format when value ends with 6, e.g. 86th. -msgctxt "ordinal 6" -msgid "{}th" -msgstr "{}ти" - -#. Translators: Ordinal format when value ends with 7, e.g. 87th. -msgctxt "ordinal 7" -msgid "{}th" -msgstr "{}ми" - -#. Translators: Ordinal format when value ends with 8, e.g. 88th. -msgctxt "ordinal 8" -msgid "{}th" -msgstr "{}ми" - -#. Translators: Ordinal format when value ends with 9, e.g. 89th. -msgctxt "ordinal 9" -msgid "{}th" -msgstr "{}ти" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f милион" +msgstr[1] "%(value).1f милиона" #, python-format msgid "%(value)s million" @@ -85,60 +47,120 @@ msgid_plural "%(value)s million" msgstr[0] "%(value)s милион" msgstr[1] "%(value)s милиона" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f милиард" +msgstr[1] "%(value).1f милиарда" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s милиард" msgstr[1] "%(value)s милиарда" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f трилион" +msgstr[1] "%(value).1f трилиона" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s трилион" msgstr[1] "%(value)s трилиона" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f квадрилион" +msgstr[1] "%(value).1f квадрилиона" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s квадрилион" msgstr[1] "%(value)s квадрилиона" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f квинтилион" +msgstr[1] "%(value).1f квинтилиона" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s квинтилион" msgstr[1] "%(value)s квинтилиона" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f секстилион" +msgstr[1] "%(value).1f секстилиона" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s секстилион" msgstr[1] "%(value)s секстилиона" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f септилион" +msgstr[1] "%(value).1f септилиона" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s септилион" msgstr[1] "%(value)s септилиона" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f октилион" +msgstr[1] "%(value).1f октилиона" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s октилион" msgstr[1] "%(value)s октилиона" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f нонилион" +msgstr[1] "%(value).1f нонилиона" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s нонилион" msgstr[1] "%(value)s нонилиона" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f децилион" +msgstr[1] "%(value).1f децилиона" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s децилион" msgstr[1] "%(value)s децилиона" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f гугол" +msgstr[1] "%(value).1f гугола" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -176,157 +198,68 @@ msgid "today" msgstr "днес" msgid "tomorrow" -msgstr "утре" +msgstr "Утре" msgid "yesterday" msgstr "вчера" -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' #, python-format +msgctxt "naturaltime" msgid "%(delta)s ago" msgstr "преди %(delta)s" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "an hour ago" -msgid_plural "%(count)s hours ago" -msgstr[0] "преди един час" -msgstr[1] "преди %(count)s часа" - -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "a minute ago" -msgid_plural "%(count)s minutes ago" -msgstr[0] "преди минута" -msgstr[1] "преди %(count)s минути" - -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "a second ago" -msgid_plural "%(count)s seconds ago" -msgstr[0] "преди секунда" -msgstr[1] "преди %(count)s секунди" - msgid "now" msgstr "сега" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. +#, python-format +msgid "a second ago" +msgid_plural "%(count)s seconds ago" +msgstr[0] "преди секунда" +msgstr[1] "преди %(count)s секунди" + +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. +#, python-format +msgid "a minute ago" +msgid_plural "%(count)s minutes ago" +msgstr[0] "преди минута" +msgstr[1] "преди %(count)s минути" + +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. +#, python-format +msgid "an hour ago" +msgid_plural "%(count)s hours ago" +msgstr[0] "преди един час" +msgstr[1] "преди %(count)s часа" + +#, python-format +msgctxt "naturaltime" +msgid "%(delta)s from now" +msgstr "%(delta)s от сега" + +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "a second from now" msgid_plural "%(count)s seconds from now" msgstr[0] "след секунда" -msgstr[1] "след %(count)s секунди" +msgstr[1] "%(count)s секунди от сега" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "a minute from now" msgid_plural "%(count)s minutes from now" msgstr[0] "след минута" -msgstr[1] "след %(count)s минути" +msgstr[1] "%(count)s минути от сега" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "an hour from now" msgid_plural "%(count)s hours from now" msgstr[0] "след един час" -msgstr[1] "след %(count)s часа" - -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' -#, python-format -msgid "%(delta)s from now" -msgstr "след %(delta)s" - -#. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d година" -msgstr[1] "%(num)d години" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месец" -msgstr[1] "%(num)d месеца" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d седмица" -msgstr[1] "%(num)d седмици" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d ден" -msgstr[1] "%(num)d дни" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d час" -msgstr[1] "%(num)d часа" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d минута" -msgstr[1] "%(num)d минути" - -#. Translators: 'naturaltime-future' strings will be included in '%(delta)s -#. from now' -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d година" -msgstr[1] "%(num)d години" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d месец" -msgstr[1] "%(num)d месеца" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d седмица" -msgstr[1] "%(num)d седмици" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d ден" -msgstr[1] "%(num)d дни" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d час" -msgstr[1] "%(num)d часа" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d минута" -msgstr[1] "%(num)d минути" +msgstr[1] "%(count)s часа от сега" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo index 348f920..4d3a1ee 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.po index 4befcc6..e1ce8a4 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/ca/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy <aaloy@apsl.net>, 2011-2012,2019,2021 +# Antoni Aloy <aaloy@apsl.net>, 2011-2012,2019 # Carles Barrobés <carles@barrobes.com>, 2011,2014 # GerardoGa <ggarciamaristany@gmail.com>, 2018 # Gil Obradors Via <gil.obradors@gmail.com>, 2019 @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-27 09:05+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-08-30 08:19+0000\n" "Last-Translator: Antoni Aloy <aaloy@apsl.net>\n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" @@ -79,66 +79,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}è" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milió" +msgstr[1] "%(value).1f milions" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s milió" msgstr[1] "%(value)s milions" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliard" +msgstr[1] "%(value).1f miliards" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s miliard" msgstr[1] "%(value)s miliards" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f bilió" +msgstr[1] "%(value).1f bilions" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s bilió" msgstr[1] "%(value)s bilió" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f quadrilió" +msgstr[1] "%(value).1f quadrilions" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s quadrilió" msgstr[1] "%(value)s quadrilions" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f quintilió" +msgstr[1] "%(value).1f quintilions" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s quintilió" msgstr[1] "%(value)s quintilions" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sextilió" +msgstr[1] "%(value).1f sextilions" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s sextilió" msgstr[1] "%(value)s sextilions" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septilió" +msgstr[1] "%(value).1f septilions" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septilió" msgstr[1] "%(value)s septilions" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f octilió" +msgstr[1] "%(value).1f octilions" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s octilió" msgstr[1] "%(value)s octilions" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f nonilió" +msgstr[1] "%(value).1f nonilions" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s nonilió" msgstr[1] "%(value)s nonilions" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f decilió" +msgstr[1] "%(value).1f decilions" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s decilió" msgstr[1] "%(value)s decilions" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f googols" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -247,86 +313,86 @@ msgstr "fa %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d any" -msgstr[1] "%(num)d anys" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d any" +msgstr[1] "%d anys" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d mesos" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d mesos" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d setmana" -msgstr[1] "%(num)d setmanes" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d setmana" +msgstr[1] "%d setmanes" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dia" -msgstr[1] "%(num)d dies" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dia" +msgstr[1] "%d dies" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d hores" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d hores" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minuts" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minuts" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d any" -msgstr[1] "%(num)d anys" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d any" +msgstr[1] "%d anys" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d mesos" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d mesos" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d setmana" -msgstr[1] "%(num)d setmanes" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d setmana" +msgstr[1] "%d setmanes" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dia" -msgstr[1] "%(num)d dies" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dia" +msgstr[1] "%d dies" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d hores" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d hores" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minuts" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minuts" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo index 4b46222..4951d85 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.po index f61b7e5..679569c 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/cs/LC_MESSAGES/django.po @@ -279,110 +279,110 @@ msgstr "za %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rokem" -msgstr[1] "%(num)d lety" -msgstr[2] "%(num)d rokem" -msgstr[3] "%(num)d lety" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rokem" +msgstr[1] "%d lety" +msgstr[2] "%d rokem" +msgstr[3] "%d lety" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsícem" -msgstr[1] "%(num)d měsíci" -msgstr[2] "%(num)d měsícem" -msgstr[3] "%(num)d měsíci" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsícem" +msgstr[1] "%d měsíci" +msgstr[2] "%d měsícem" +msgstr[3] "%d měsíci" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d týdnem" -msgstr[1] "%(num)d týdny" -msgstr[2] "%(num)d týdny" -msgstr[3] "%(num)d týdny" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d týdnem" +msgstr[1] "%d týdny" +msgstr[2] "%d týdny" +msgstr[3] "%d týdny" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dnem" -msgstr[1] "%(num)d dny" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dny" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dnem" +msgstr[1] "%d dny" +msgstr[2] "%d dny" +msgstr[3] "%d dny" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodinou" -msgstr[1] "%(num)d hodinami" -msgstr[2] "%(num)d hodinami" -msgstr[3] "%(num)d hodinami" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodinou" +msgstr[1] "%d hodinami" +msgstr[2] "%d hodinami" +msgstr[3] "%d hodinami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutou" -msgstr[1] "%(num)d minutami" -msgstr[2] "%(num)d minutami" -msgstr[3] "%(num)d minutami" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutou" +msgstr[1] "%d minutami" +msgstr[2] "%d minutami" +msgstr[3] "%d minutami" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rok" -msgstr[1] "%(num)d roky" -msgstr[2] "%(num)d let" -msgstr[3] "%(num)d let" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d roky" +msgstr[2] "%d let" +msgstr[3] "%d let" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsíc" -msgstr[1] "%(num)d měsíce" -msgstr[2] "%(num)d měsíců" -msgstr[3] "%(num)d měsíců" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsíc" +msgstr[1] "%d měsíce" +msgstr[2] "%d měsíců" +msgstr[3] "%d měsíců" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d týden" -msgstr[1] "%(num)d týdny" -msgstr[2] "%(num)d týdnů" -msgstr[3] "%(num)d týdnů" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d týden" +msgstr[1] "%d týdny" +msgstr[2] "%d týdnů" +msgstr[3] "%d týdnů" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d den" -msgstr[1] "%(num)d dny" -msgstr[2] "%(num)d dní" -msgstr[3] "%(num)d dní" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d den" +msgstr[1] "%d dny" +msgstr[2] "%d dní" +msgstr[3] "%d dní" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodina" -msgstr[1] "%(num)d hodiny" -msgstr[2] "%(num)d hodin" -msgstr[3] "%(num)d hodin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodina" +msgstr[1] "%d hodiny" +msgstr[2] "%d hodin" +msgstr[3] "%d hodin" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuty" -msgstr[2] "%(num)d minut" -msgstr[3] "%(num)d minut" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minuty" +msgstr[2] "%d minut" +msgstr[3] "%d minut" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.mo index bb8cd61..5a152e0 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.po index 8ad82a0..1feefe0 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/da/LC_MESSAGES/django.po @@ -2,17 +2,16 @@ # # Translators: # Christian Joergensen <christian@gmta.info>, 2012 -# Erik Ramsgaard Wognsen <r4mses@gmail.com>, 2021 -# Erik Ramsgaard Wognsen <r4mses@gmail.com>, 2014,2018 +# Erik Wognsen <r4mses@gmail.com>, 2014,2018 # Jannis Leidel <jannis@leidel.info>, 2011 # valberg <valberg@orn.li>, 2014 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-23 21:30+0000\n" -"Last-Translator: Erik Ramsgaard Wognsen <r4mses@gmail.com>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-18 21:12+0000\n" +"Last-Translator: Erik Wognsen <r4mses@gmail.com>\n" "Language-Team: Danish (http://www.transifex.com/django/django/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -78,66 +77,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f million" +msgstr[1] "%(value).1f millioner" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s million" msgstr[1] "%(value)s millioner" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f milliard" +msgstr[1] "%(value).1f milliarder" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s milliard" msgstr[1] "%(value)s milliarder" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f billion" +msgstr[1] "%(value).1f billioner" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s billion" msgstr[1] "%(value)s billioner" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f billiard" +msgstr[1] "%(value).1f billiarder" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s billiard" msgstr[1] "%(value)s billiarder" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trillion" +msgstr[1] "%(value).1f trillioner" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s trillion" msgstr[1] "%(value)s trillioner" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f trilliard" +msgstr[1] "%(value).1f trilliarder" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s trilliard" msgstr[1] "%(value)s trilliarder" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f kvadrillion" +msgstr[1] "%(value).1f kvadrillioner" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s kvadrillion" msgstr[1] "%(value)s kvadrillioner" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f kvadrilliard" +msgstr[1] "%(value).1f kvadrilliarder" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s kvadrilliard" msgstr[1] "%(value)s kvadrilliarder" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f kvintillion" +msgstr[1] "%(value).1f kvintillioner" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s kvintillion" msgstr[1] "%(value)s kvintillioner" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f kvintilliard" +msgstr[1] "%(value).1f kvintilliarder" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s kvintilliard" msgstr[1] "%(value)s kvintilliarder" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f gogol" +msgstr[1] "%(value).1f gogoler" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -246,86 +311,86 @@ msgstr "%(delta)s fra nu af" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d år" +msgstr[1] "%d år" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d måned" -msgstr[1] "%(num)d måneder" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d måned" +msgstr[1] "%d måneder" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d uge" -msgstr[1] "%(num)d uger" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d uge" +msgstr[1] "%d uger" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dag" +msgstr[1] "%d dage" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timer" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d time" +msgstr[1] "%d timer" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutter" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutter" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d år" +msgstr[1] "%d år" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d måned" -msgstr[1] "%(num)d måneder" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d måned" +msgstr[1] "%d måneder" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d uge" -msgstr[1] "%(num)d uger" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d uge" +msgstr[1] "%d uger" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dag" +msgstr[1] "%d dage" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timer" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d time" +msgstr[1] "%d timer" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutter" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutter" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.mo index df46d3c..bc66b48 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.po index 1fa0a64..90d90f0 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/de/LC_MESSAGES/django.po @@ -3,15 +3,14 @@ # Translators: # André Hagenbruch, 2011 # Claude Paroz <claude@2xlibre.net>, 2013 -# Florian Apolloner <florian@apolloner.eu>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011,2013-2014,2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-28 17:20+0000\n" -"Last-Translator: Raphael Michel <mail@raphaelmichel.de>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-08-14 08:29+0000\n" +"Last-Translator: Florian Apolloner <florian@apolloner.eu>\n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,66 +76,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f Million" +msgstr[1] "%(value).1f Millionen" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s Million" msgstr[1] "%(value)s Millionen" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f Milliarde" +msgstr[1] "%(value).1f Milliarden" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s Milliarde" msgstr[1] "%(value)s Milliarden" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f Billion" +msgstr[1] "%(value).1f Billionen" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s Billion" msgstr[1] "%(value)s Billionen" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f Billiarde" +msgstr[1] "%(value).1f Billiarden" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s Billiarde" msgstr[1] "%(value)s Billiarden" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f Trillion" +msgstr[1] "%(value).1f Trillionen" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s Trillion" msgstr[1] "%(value)s Trillionen" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f Trilliarde" +msgstr[1] "%(value).1f Trilliarden" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s Trilliarde" msgstr[1] "%(value)s Trilliarden" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f Quadrillion" +msgstr[1] "%(value).1f Quadrillionen" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s Quadrillion" msgstr[1] "%(value)s Quadrillionen" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f Quadrilliarde" +msgstr[1] "%(value).1f Quadrilliarden" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s Quadrilliarde" msgstr[1] "%(value)s Quadrilliarden" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f Quintillion" +msgstr[1] "%(value).1f Quintillionen" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s Quintillion" msgstr[1] "%(value)s Quintillionen" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f Quintilliarde" +msgstr[1] "%(value).1f Quintilliarden" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s Quintilliarde" msgstr[1] "%(value)s Quintilliarden" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f Sedezilliarde" +msgstr[1] "%(value).1f Sedezilliarden" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -245,86 +310,86 @@ msgstr "%(delta)s von jetzt an" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d Jahr" -msgstr[1] "%(num)d Jahre" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d Jahr" +msgstr[1] "%d Jahre" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d Monat" -msgstr[1] "%(num)d Monate" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d Monat" +msgstr[1] "%d Monate" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d Woche" -msgstr[1] "%(num)d Wochen" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d Woche" +msgstr[1] "%d Wochen" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d Tage" -msgstr[1] "%(num)d Tage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d Tag" +msgstr[1] "%d Tage" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d Stunde" -msgstr[1] "%(num)d Stunden" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d Stunde" +msgstr[1] "%d Stunden" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d Minute" -msgstr[1] "%(num)d Minuten" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d Minute" +msgstr[1] "%d Minuten" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d Jahr" -msgstr[1] "%(num)d Jahre" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d Jahr" +msgstr[1] "%d Jahre" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d Monat" -msgstr[1] "%(num)d Monate" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d Monat" +msgstr[1] "%d Monate" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d Woche" -msgstr[1] "%(num)d Wochen" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d Woche" +msgstr[1] "%d Wochen" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d Tag" -msgstr[1] "%(num)d Tage" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d Tag" +msgstr[1] "%d Tage" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d Stunde" -msgstr[1] "%(num)d Stunden" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d Stunde" +msgstr[1] "%d Stunden" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d Minute" -msgstr[1] "%(num)d Minuten" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d Minute" +msgstr[1] "%d Minuten" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo index bbd9821..6becef5 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po index 11ab238..9814da3 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/dsb/LC_MESSAGES/django.po @@ -1,13 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Michael Wolf <milupo@sorbzilla.de>, 2016,2018,2021 +# Michael Wolf <milupo@sorbzilla.de>, 2016,2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-28 19:07+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-26 10:54+0000\n" "Last-Translator: Michael Wolf <milupo@sorbzilla.de>\n" "Language-Team: Lower Sorbian (http://www.transifex.com/django/django/" "language/dsb/)\n" @@ -76,6 +76,14 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milion" +msgstr[1] "%(value).1f miliona" +msgstr[2] "%(value).1f miliony" +msgstr[3] "%(value).1f milionow" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -84,6 +92,14 @@ msgstr[1] "%(value)s miliona" msgstr[2] "%(value)s miliony" msgstr[3] "%(value)s milionow" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliarda" +msgstr[1] "%(value).1f miliarźe" +msgstr[2] "%(value).1f miliardy" +msgstr[3] "%(value).1f miliardow" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -92,6 +108,14 @@ msgstr[1] "%(value)s miliarźe" msgstr[2] "%(value)s miliardy" msgstr[3] "%(value)s miliardow" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f bilion" +msgstr[1] "%(value).1f biliona" +msgstr[2] "%(value).1f biliony" +msgstr[3] "%(value).1f bilionow" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -100,6 +124,14 @@ msgstr[1] "%(value)s biliona" msgstr[2] "%(value)s biliony" msgstr[3] "%(value)s bilionow" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f biliarda" +msgstr[1] "%(value).1f biliarźe" +msgstr[2] "%(value).1f biliardy" +msgstr[3] "%(value).1f biliardow" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -108,6 +140,14 @@ msgstr[1] "%(value)s biliarźe" msgstr[2] "%(value)s biliardy" msgstr[3] "%(value)s biliardow" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trilion" +msgstr[1] "%(value).1f triliona" +msgstr[2] "%(value).1f triliony" +msgstr[3] "%(value).1f trilionow" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -116,6 +156,14 @@ msgstr[1] "%(value)s triliona" msgstr[2] "%(value)s triliony" msgstr[3] "%(value)s trilionow" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f triliarda" +msgstr[1] "%(value).1f triliarźe" +msgstr[2] "%(value).1f triliardy" +msgstr[3] "%(value).1f triliardow" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -124,6 +172,14 @@ msgstr[1] "%(value)s triliarźe" msgstr[2] "%(value)s triliardy" msgstr[3] "%(value)s triliardow" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f kwadrilion" +msgstr[1] "%(value).1f kwadriliona" +msgstr[2] "%(value).1f kwadriliony" +msgstr[3] "%(value).1f kwadrilionow" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -132,6 +188,14 @@ msgstr[1] "%(value)s kwadriliona" msgstr[2] "%(value)s kwadriliony" msgstr[3] "%(value)s kwadrilionow" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f kwadriliarda" +msgstr[1] "%(value).1f kwadriliarźe" +msgstr[2] "%(value).1f kwadriliardy" +msgstr[3] "%(value).1f kwadriliardow" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -140,6 +204,14 @@ msgstr[1] "%(value)s kwadriliarźe" msgstr[2] "%(value)s kwadriliardy" msgstr[3] "%(value)s kwadriliardow" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f kwintilion" +msgstr[1] "%(value).1f kwintiliona" +msgstr[2] "%(value).1f kwintiliony" +msgstr[3] "%(value).1f kwintilionow" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -148,6 +220,14 @@ msgstr[1] "%(value)s kwintiliona" msgstr[2] "%(value)s kwintiliony" msgstr[3] "%(value)s kwintilionow" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f kwintiliarda" +msgstr[1] "%(value).1f kwintiliarźe" +msgstr[2] "%(value).1f kwintiliardy" +msgstr[3] "%(value).1f kwintiliardow" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -156,6 +236,14 @@ msgstr[1] "%(value)s kwintiliarźe" msgstr[2] "%(value)s kwintiliardy" msgstr[3] "%(value)s kwintiliardow" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f sedeciliarda" +msgstr[1] "%(value).1f sedeciliarźe" +msgstr[2] "%(value).1f sedeciliardy" +msgstr[3] "%(value).1f sedeciliardow" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -278,110 +366,110 @@ msgstr "%(delta)s wótněnta" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)d lěśe" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%dlětom" +msgstr[1] "%dlětoma" +msgstr[2] "%dlětami" +msgstr[3] "%dlětami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mjasec" -msgstr[1] "%(num)d mjaseca" -msgstr[2] "%(num)d mjasece" -msgstr[3] "%(num)dmjasecow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mjasecom" +msgstr[1] "%d mjasecoma" +msgstr[2] "%d mjasecami" +msgstr[3] "%d mjasecami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tyźeń" -msgstr[1] "%(num)d tyźenja" -msgstr[2] "%(num)d tyźenje" -msgstr[3] "%(num)d tyźenjow" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tyźenjom" +msgstr[1] "%d tyźenjoma" +msgstr[2] "%d tyźenjami" +msgstr[3] "%d tyźenjami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d źeń" -msgstr[1] "%(num)d dnja" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dnjom" +msgstr[1] "%d dnjoma" +msgstr[2] "%d dnjami" +msgstr[3] "%d dnjami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d góźina" -msgstr[1] "%(num)d góźinje" -msgstr[2] "%(num)d góźiny" -msgstr[3] "%(num)d góźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d góźinu" +msgstr[1] "%d góźinoma" +msgstr[2] "%d góźinami" +msgstr[3] "%d góźinami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuśe" -msgstr[2] "%(num)d minuty" -msgstr[3] "%(num)d minutow" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutu" +msgstr[1] "%d minutoma" +msgstr[2] "%d minutami" +msgstr[3] "%d minutami" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)d lěśe" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%dlěto" +msgstr[1] "%d lěśe" +msgstr[2] "%d lěta" +msgstr[3] "%d lět" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mjasec" -msgstr[1] "%(num)d mjaseca" -msgstr[2] "%(num)d mjasece" -msgstr[3] "%(num)dmjasecow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mjasec" +msgstr[1] "%d mjaseca" +msgstr[2] "%d mjasece" +msgstr[3] "%d mjasecow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tyźeń" -msgstr[1] "%(num)d tyźenja" -msgstr[2] "%(num)d tyźenje" -msgstr[3] "%(num)d tyźenjow" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tyźeń" +msgstr[1] "%d tyźenja" +msgstr[2] "%d tyźenje" +msgstr[3] "%d tyźenjow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d źeń" -msgstr[1] "%(num)d dnja" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d źeń" +msgstr[1] "%d dnja" +msgstr[2] "%d dny" +msgstr[3] "%d dnjow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d góźina" -msgstr[1] "%(num)d góźinje" -msgstr[2] "%(num)d góźiny" -msgstr[3] "%(num)d góźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d góźina" +msgstr[1] "%d góźinje" +msgstr[2] "%d góźiny" +msgstr[3] "%d góźinow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuta" -msgstr[1] "%(num)d minuśe" -msgstr[2] "%(num)d minuty" -msgstr[3] "%(num)d minutow" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuta" +msgstr[1] "%d minuśe" +msgstr[2] "%d minuty" +msgstr[3] "%d minuty" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/en/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/en/LC_MESSAGES/django.po index cf9ec2f..88f8982 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/en/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English <en@li.org>\n" @@ -283,48 +283,48 @@ msgstr "" #: contrib/humanize/templatetags/humanize.py:205 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" +msgid "%d year" +msgid_plural "%d years" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:206 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" +msgid "%d month" +msgid_plural "%d months" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:207 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" +msgid "%d week" +msgid_plural "%d weeks" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:208 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" +msgid "%d day" +msgid_plural "%d days" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:209 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" +msgid "%d hour" +msgid_plural "%d hours" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:210 #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" @@ -332,47 +332,47 @@ msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:214 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" +msgid "%d year" +msgid_plural "%d years" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:215 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" +msgid "%d month" +msgid_plural "%d months" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:216 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" +msgid "%d week" +msgid_plural "%d weeks" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:217 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" +msgid "%d day" +msgid_plural "%d days" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:218 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" +msgid "%d hour" +msgid_plural "%d hours" msgstr[0] "" msgstr[1] "" #: contrib/humanize/templatetags/humanize.py:219 #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo index b381a0f..fcc2fc5 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po index 6417eb8..6521245 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/en_AU/LC_MESSAGES/django.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield <tom@tomfifield.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-22 07:22+0000\n" -"Last-Translator: Transifex Bot <>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2014-10-05 20:11+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: English (Australia) (http://www.transifex.com/projects/p/" +"django/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,62 +17,25 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Humanize" -msgstr "Humanise" +msgstr "" -#. Translators: Ordinal format for 11 (11th), 12 (12th), and 13 (13th). -msgctxt "ordinal 11, 12, 13" -msgid "{}th" -msgstr "{}th" +msgid "th" +msgstr "" -#. Translators: Ordinal format when value ends with 0, e.g. 80th. -msgctxt "ordinal 0" -msgid "{}th" -msgstr "{}th" +msgid "st" +msgstr "" -#. Translators: Ordinal format when value ends with 1, e.g. 81st, except 11. -msgctxt "ordinal 1" -msgid "{}st" -msgstr "{}st" +msgid "nd" +msgstr "" -#. Translators: Ordinal format when value ends with 2, e.g. 82nd, except 12. -msgctxt "ordinal 2" -msgid "{}nd" -msgstr "{}nd" +msgid "rd" +msgstr "" -#. Translators: Ordinal format when value ends with 3, e.g. 83th, except 13. -msgctxt "ordinal 3" -msgid "{}rd" -msgstr "{}rd" - -#. Translators: Ordinal format when value ends with 4, e.g. 84th. -msgctxt "ordinal 4" -msgid "{}th" -msgstr "{}th" - -#. Translators: Ordinal format when value ends with 5, e.g. 85th. -msgctxt "ordinal 5" -msgid "{}th" -msgstr "{}th" - -#. Translators: Ordinal format when value ends with 6, e.g. 86th. -msgctxt "ordinal 6" -msgid "{}th" -msgstr "{}th" - -#. Translators: Ordinal format when value ends with 7, e.g. 87th. -msgctxt "ordinal 7" -msgid "{}th" -msgstr "{}th" - -#. Translators: Ordinal format when value ends with 8, e.g. 88th. -msgctxt "ordinal 8" -msgid "{}th" -msgstr "{}th" - -#. Translators: Ordinal format when value ends with 9, e.g. 89th. -msgctxt "ordinal 9" -msgid "{}th" -msgstr "{}th" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "" +msgstr[1] "" #, python-format msgid "%(value)s million" @@ -81,60 +43,120 @@ msgid_plural "%(value)s million" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "" msgstr[1] "" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "" +msgstr[1] "" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -177,152 +199,63 @@ msgstr "" msgid "yesterday" msgstr "" -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' #, python-format +msgctxt "naturaltime" msgid "%(delta)s ago" msgstr "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "an hour ago" -msgid_plural "%(count)s hours ago" -msgstr[0] "" -msgstr[1] "" +msgid "now" +msgstr "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "a minute ago" -msgid_plural "%(count)s minutes ago" -msgstr[0] "" -msgstr[1] "" - -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "a second ago" msgid_plural "%(count)s seconds ago" msgstr[0] "" msgstr[1] "" -msgid "now" +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. +#, python-format +msgid "a minute ago" +msgid_plural "%(count)s minutes ago" +msgstr[0] "" +msgstr[1] "" + +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. +#, python-format +msgid "an hour ago" +msgid_plural "%(count)s hours ago" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgctxt "naturaltime" +msgid "%(delta)s from now" msgstr "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "a second from now" msgid_plural "%(count)s seconds from now" msgstr[0] "" msgstr[1] "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "a minute from now" msgid_plural "%(count)s minutes from now" msgstr[0] "" msgstr[1] "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format msgid "an hour from now" msgid_plural "%(count)s hours from now" msgstr[0] "" msgstr[1] "" - -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' -#, python-format -msgid "%(delta)s from now" -msgstr "" - -#. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" - -#. Translators: 'naturaltime-future' strings will be included in '%(delta)s -#. from now' -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "" -msgstr[1] "" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "" -msgstr[1] "" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.mo index 36238b3..6d7c5f4 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.po index 8010bf2..1cf4e1b 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/es/LC_MESSAGES/django.po @@ -2,20 +2,20 @@ # # Translators: # Antoni Aloy <aaloy@apsl.net>, 2012 -# e4db27214f7e7544f2022c647b585925_bb0e321, 2014 +# Ernesto Avilés, 2014 # Ignacio José Lizarán Rus <ilizaran@gmail.com>, 2019 # Jannis Leidel <jannis@leidel.info>, 2011 # Leonardo J. Caballero G. <leonardocaballero@gmail.com>, 2011 # Luigy, 2019 # ntrrgc <ntrrgc@gmail.com>, 2014 -# Uriel Medina <urimeba511@gmail.com>, 2020-2021 +# Uriel Medina <urimeba511@gmail.com>, 2020 # Veronicabh <vero.blazher@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-10 04:02+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2020-09-29 18:51+0000\n" "Last-Translator: Uriel Medina <urimeba511@gmail.com>\n" "Language-Team: Spanish (http://www.transifex.com/django/django/language/" "es/)\n" @@ -83,66 +83,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}º" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f millón" +msgstr[1] "%(value).1f millones" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s millon" msgstr[1] "%(value)s millones" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f millardo" +msgstr[1] "%(value).1f millardos" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s millardo" msgstr[1] "%(value)s millardos" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f billón" +msgstr[1] "%(value).1f billones" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s billón" msgstr[1] "%(value)s billones" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f billardo" +msgstr[1] "%(value).1f billardos" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s billardos" msgstr[1] "%(value)s billardos" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trillón" +msgstr[1] "%(value).1f trillones" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s trillón" msgstr[1] "%(value)s trillones" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f trillardo" +msgstr[1] "%(value).1f trillardos" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s trillardo" msgstr[1] "%(value)s trillardos" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f cuatrillón" +msgstr[1] "%(value).1f cuatrillones" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s cuatrillón" msgstr[1] "%(value)s cuatrillones" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f cuatrillardo" +msgstr[1] "%(value).1f cuatrillardos" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s cuatrillardo" msgstr[1] "%(value)s cuatrillardos" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f quintillón" +msgstr[1] "%(value).1f quintillones" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s quintillón" msgstr[1] "%(value)s quintillones" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f quintillardo" +msgstr[1] "%(value).1f quintillardos" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s quintillardo" msgstr[1] "%(value)s quintillardos" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f gúgoles" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -251,86 +317,86 @@ msgstr "%(delta)s desde ahora" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d año" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minutos" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d año" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minutos" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo index 62767df..52a5db4 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po index 11624ab..28cf2e7 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/es_AR/LC_MESSAGES/django.po @@ -5,13 +5,13 @@ # Jannis Leidel <jannis@leidel.info>, 2011 # lardissone <lardissone@gmail.com>, 2014 # lardissone <lardissone@gmail.com>, 2014 -# Ramiro Morales, 2012,2014-2015,2018,2021 +# Ramiro Morales, 2012,2014-2015,2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-19 15:15+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-07-31 18:16+0000\n" "Last-Translator: Ramiro Morales\n" "Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/" "language/es_AR/)\n" @@ -79,66 +79,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}.º" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f millón" +msgstr[1] "%(value).1f millones" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s millón" msgstr[1] "%(value)s millones" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f millardo" +msgstr[1] "%(value).1f millardos" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s millardo" msgstr[1] "%(value)s millardos" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f billón" +msgstr[1] "%(value).1f billones" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s billón" msgstr[1] "%(value)s billones" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f mil billones" +msgstr[1] "%(value).1f miles de billones" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s mil billones" msgstr[1] "%(value)s miles de billones" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trillón" +msgstr[1] "%(value).1f trilliones" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s trillón" msgstr[1] "%(value)s trilliones" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f mil trillones" +msgstr[1] "%(value).1f miles de trillones" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s mil trillones" msgstr[1] "%(value)s miles de trillones" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f cuatrillón" +msgstr[1] "%(value).1f cuatrillones" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s cuatrillón" msgstr[1] "%(value)s cuatrillones" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f mil cuatrillones" +msgstr[1] "%(value).1f miles de cuatrillones" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s mil cuatrillones" msgstr[1] "%(value)s miles de cuatrillones" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f quintillón" +msgstr[1] "%(value).1f quintillones" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s quintillón" msgstr[1] "%(value)s quintillones" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f mil quintillones" +msgstr[1] "%(value).1f miles de quintillones" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s mil quintillones" msgstr[1] "%(value)s miles de quintillones" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f \"gúgol\"" +msgstr[1] "%(value).1f \"gúgols\"" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -247,86 +313,86 @@ msgstr "dentro de %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d año" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minutos" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d año" -msgstr[1] "%(num)d años" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d año" +msgstr[1] "%d años" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mes" -msgstr[1] "%(num)d meses" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mes" +msgstr[1] "%d meses" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semana" -msgstr[1] "%(num)d semanas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semana" +msgstr[1] "%d semanas" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d día" -msgstr[1] "%(num)d días" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d día" +msgstr[1] "%d días" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hora" -msgstr[1] "%(num)d horas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hora" +msgstr[1] "%d horas" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minutos" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuto" +msgstr[1] "%d minutos" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.mo index 85274d0..9dad18d 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.po index 16cf8fb..ff70eb6 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/et/LC_MESSAGES/django.po @@ -4,17 +4,16 @@ # Claude Paroz <claude@2xlibre.net>, 2013 # Jannis Leidel <jannis@leidel.info>, 2011 # Janno Liivak <jannolii@gmail.com>, 2013 -# Martin <martinpajuste@gmail.com>, 2021 -# Martin <martinpajuste@gmail.com>, 2019 +# Martin Pajuste <martinpajuste@gmail.com>, 2019 # Marti Raudsepp <marti@juffo.org>, 2014 # Ragnar Rebase <rrebase@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-19 08:47+0000\n" -"Last-Translator: Martin <martinpajuste@gmail.com>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-12-28 02:34+0000\n" +"Last-Translator: Ragnar Rebase <rrebase@gmail.com>\n" "Language-Team: Estonian (http://www.transifex.com/django/django/language/" "et/)\n" "MIME-Version: 1.0\n" @@ -81,66 +80,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f miljonit" +msgstr[1] "%(value).1f miljonit" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s miljon" msgstr[1] "%(value)s miljonit" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miljardit" +msgstr[1] "%(value).1f miljardit" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s miljard" msgstr[1] "%(value)s miljardit" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f triljonit" +msgstr[1] "%(value).1f triljonit" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s triljon" msgstr[1] "%(value)s triljonit" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f kvadriljonit" +msgstr[1] "%(value).1f kvadriljonit" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s kvadriljon" msgstr[1] "%(value)s kvadriljonit" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f kvintiljonit" +msgstr[1] "%(value).1f kvintiljonit" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s kvintiljon" msgstr[1] "%(value)s kvintiljonit" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sekstiljonit" +msgstr[1] "%(value).1f sekstiljonit" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s sekstiljon" msgstr[1] "%(value)s sekstiljonit" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septiljonit" +msgstr[1] "%(value).1f septiljonit" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septiljon" msgstr[1] "%(value)s septiljonit" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f oktiljonit" +msgstr[1] "%(value).1f oktiljonit" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s oktiljon" msgstr[1] "%(value)s oktiljonit" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f noniljonit" +msgstr[1] "%(value).1f noniljonit" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s noniljon" msgstr[1] "%(value)s noniljonit" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f detsiljonit" +msgstr[1] "%(value).1f detsiljonit" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s detsiljon" msgstr[1] "%(value)s detsiljonit" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googolit" +msgstr[1] "%(value).1f googolit" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -249,86 +314,86 @@ msgstr "%(delta)s praegusest hetkest" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d aasta" -msgstr[1] "%(num)d aastat" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d aastat" +msgstr[1] "%d aastat" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d kuu" -msgstr[1] "%(num)d kuud" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuud" +msgstr[1] "%d kuud" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nädal" -msgstr[1] "%(num)d nädalat" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nädalat" +msgstr[1] "%d nädalat" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d päev" -msgstr[1] "%(num)d päeva" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päev" +msgstr[1] "%d päeva" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tund" -msgstr[1] "%(num)d tundi" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tund" +msgstr[1] "%d tundi" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutit" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d aasta" -msgstr[1] "%(num)d aastat" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d aasta" +msgstr[1] "%d aastat" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d kuu" -msgstr[1] "%(num)d kuud" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuu" +msgstr[1] "%d kuud" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nädal" -msgstr[1] "%(num)d nädalat" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nädal" +msgstr[1] "%d nädalat" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d päev" -msgstr[1] "%(num)d päeva" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päev" +msgstr[1] "%d päeva" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tund" -msgstr[1] "%(num)d tundi" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tund" +msgstr[1] "%d tundi" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minut" -msgstr[1] "%(num)d minutit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minut" +msgstr[1] "%d minutit" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo index 54466b8..c4346b8 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.po index aee5335..c00988a 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/fi/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Aarni Koskela, 2015,2020-2021 +# Aarni Koskela, 2015,2020 # Antti Kaihola <antti.15+transifex@kaihola.fi>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 # Lasse Liehu <larso@gmx.com>, 2015 @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-22 15:15+0000\n" -"Last-Translator: Aarni Koskela\n" +"POT-Creation-Date: 2021-01-15 09:00+0100\n" +"PO-Revision-Date: 2021-01-15 10:30+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Finnish (http://www.transifex.com/django/django/language/" "fi/)\n" "MIME-Version: 1.0\n" @@ -246,86 +246,86 @@ msgstr "%(delta)s nykyhetkestä" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d vuosi" -msgstr[1] "%(num)d vuotta" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d vuosi" +msgstr[1] "%d vuotta" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)dkuukausi" -msgstr[1] "%(num)dkuukautta" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuukausi" +msgstr[1] "%d kuukautta" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d viikko" -msgstr[1] "%(num)d viikkoa" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d viikko" +msgstr[1] "%d viikkoa" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)dpäivä" -msgstr[1] "%(num)d päivää" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päivä" +msgstr[1] "%d päivää" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tunti" -msgstr[1] "%(num)d tuntia" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tunti" +msgstr[1] "%d tuntia" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuutti" -msgstr[1] "%(num)d minuuttia" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuutti" +msgstr[1] "%d minuuttia" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d vuosi" -msgstr[1] "%(num)d vuotta" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d vuosi" +msgstr[1] "%d vuotta" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d kuukausi" -msgstr[1] "%(num)d kuukautta " +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d kuukausi" +msgstr[1] "%d kuukautta" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d viikko" -msgstr[1] "%(num)d viikkoa" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d viikko" +msgstr[1] "%d viikkoa" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d päivä" -msgstr[1] "%(num)d päivää" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d päivä" +msgstr[1] "%d päivää" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d tunti" -msgstr[1] "%(num)d tuntia" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d tunti" +msgstr[1] "%d tuntia" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuutti" -msgstr[1] "%(num)d minuuttia" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuutti" +msgstr[1] "%d minuuttia" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo index 213c87e..8eb642c 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.po index 5d92a4f..5e9a42f 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/fr/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Claude Paroz <claude@2xlibre.net>, 2013-2014,2018-2019,2021 +# Claude Paroz <claude@2xlibre.net>, 2013-2014,2018-2019 # Claude Paroz <claude@2xlibre.net>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 # Jean-Baptiste Mora, 2014 @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-25 10:04+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-03-02 14:04+0000\n" "Last-Translator: Claude Paroz <claude@2xlibre.net>\n" "Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n" "MIME-Version: 1.0\n" @@ -77,66 +77,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}<sup>e</sup>" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f million" +msgstr[1] "%(value).1f millions" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s million" msgstr[1] "%(value)s millions" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f milliard" +msgstr[1] "%(value).1f milliards" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s milliard" msgstr[1] "%(value)s milliards" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f billion" +msgstr[1] "%(value).1f billions" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s billion" msgstr[1] "%(value)s billions" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f quadrillion" +msgstr[1] "%(value).1f quadrillions" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s quadrillion" msgstr[1] "%(value)s quadrillions" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f quintillion" +msgstr[1] "%(value).1f quintillions" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s quintillion" msgstr[1] "%(value)s quintillions" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sextillion" +msgstr[1] "%(value).1f sextillions" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s sextillion" msgstr[1] "%(value)s sextillion" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septillion" +msgstr[1] "%(value).1f septillions" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septillion" msgstr[1] "%(value)s septillions" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f octillion" +msgstr[1] "%(value).1f octillions" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s octillion" msgstr[1] "%(value)s octillions" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f nonillion" +msgstr[1] "%(value).1f nonillions" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s nonillion" msgstr[1] "%(value)s nonillions" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f décillion" +msgstr[1] "%(value).1f décillions" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s décillion" msgstr[1] "%(value)s décillions" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f gogol" +msgstr[1] "%(value).1f gogols" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -245,86 +311,86 @@ msgstr "dans %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d année" -msgstr[1] "%(num)d années" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d année" +msgstr[1] "%d ans" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mois" -msgstr[1] "%(num)d mois" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mois" +msgstr[1] "%d mois" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semaine" -msgstr[1] "%(num)d semaines" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semaine" +msgstr[1] "%d semaines" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d jour" -msgstr[1] "%(num)d jours" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d jour" +msgstr[1] "%d jours" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d heure" -msgstr[1] "%(num)d heures" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d heure" +msgstr[1] "%d heures" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minute" -msgstr[1] "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minute" +msgstr[1] "%d minutes" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d année" -msgstr[1] "%(num)d années" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d année" +msgstr[1] "%d ans" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mois" -msgstr[1] "%(num)d mois" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mois" +msgstr[1] "%d mois" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d semaine" -msgstr[1] "%(num)d semaines" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semaine" +msgstr[1] "%d semaines" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d jour" -msgstr[1] "%(num)d jours" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d jour" +msgstr[1] "%d jours" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d heure" -msgstr[1] "%(num)d heures" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d heure" +msgstr[1] "%d heures" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minute" -msgstr[1] "%(num)d minutes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minute" +msgstr[1] "%d minutes" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.mo index cbd4232..04cda3e 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.po index 3fb1327..f65c8cd 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/he/LC_MESSAGES/django.po @@ -4,14 +4,13 @@ # Alex Gaynor <inactive+Alex@transifex.com>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 # Meir Kriheli <mkriheli@gmail.com>, 2012,2014,2019 -# Uri Rodberg <uri@speedy.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-22 07:22+0000\n" -"Last-Translator: Uri Rodberg <uri@speedy.net>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-03-19 16:20+0000\n" +"Last-Translator: Meir Kriheli <mkriheli@gmail.com>\n" "Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -78,90 +77,178 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "ה־{}" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f מיליון" +msgstr[1] "%(value).1f מיליונים" +msgstr[2] "%(value).1f מיליונים" +msgstr[3] "%(value).1f מיליונים" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" -msgstr[0] "מיליון" +msgstr[0] "%(value)s מיליון" msgstr[1] "%(value)s מיליון" msgstr[2] "%(value)s מיליון" msgstr[3] "%(value)s מיליון" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f מיליארד" +msgstr[1] "%(value).1f מיליארדים" +msgstr[2] "%(value).1f מיליארדים" +msgstr[3] "%(value).1f מיליארדים" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" -msgstr[0] "מיליארד" +msgstr[0] "%(value)s מיליארד" msgstr[1] "%(value)s מיליארד" msgstr[2] "%(value)s מיליארד" msgstr[3] "%(value)s מיליארד" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f טריליון" +msgstr[1] "%(value).1f טריליונים" +msgstr[2] "%(value).1f טריליונים" +msgstr[3] "%(value).1f טריליונים" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" -msgstr[0] "טריליון" +msgstr[0] "%(value)s טריליון" msgstr[1] "%(value)s טריליון" msgstr[2] "%(value)s טריליון" msgstr[3] "%(value)s טריליון" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f קוודריליון" +msgstr[1] "%(value).1f קוודריליון" +msgstr[2] "%(value).1f קוודריליון" +msgstr[3] "%(value).1f קוודריליון" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" -msgstr[0] "קוודריליון" +msgstr[0] "%(value)s קוודריליון" msgstr[1] "%(value)s קוודריליון" msgstr[2] "%(value)s קוודריליון" msgstr[3] "%(value)s קוודריליון" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f קווינטיליון" +msgstr[1] "%(value).1f קווינטיליון" +msgstr[2] "%(value).1f קווינטיליון" +msgstr[3] "%(value).1f קווינטיליון" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" -msgstr[0] "קווינטיליון" +msgstr[0] "%(value)s קווינטיליון" msgstr[1] "%(value)s קווינטיליון" msgstr[2] "%(value)s קווינטיליון" msgstr[3] "%(value)s קווינטיליון" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f סקסטיליון" +msgstr[1] "%(value).1f סקסטיליון" +msgstr[2] "%(value).1f סקסטיליון" +msgstr[3] "%(value).1f סקסטיליון" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" -msgstr[0] "סקסטיליון" +msgstr[0] "%(value)s סקסטיליון" msgstr[1] "%(value)s סקסטיליון" msgstr[2] "%(value)s סקסטיליון" msgstr[3] "%(value)s סקסטיליון" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f ספטיליון" +msgstr[1] "%(value).1f ספטיליון" +msgstr[2] "%(value).1f ספטיליון" +msgstr[3] "%(value).1f ספטיליון" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" -msgstr[0] "ספטיליון" +msgstr[0] "%(value)s ספטיליון" msgstr[1] "%(value)s ספטיליון" msgstr[2] "%(value)s ספטיליון" msgstr[3] "%(value)s ספטיליון" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f אוקטיליון" +msgstr[1] "%(value).1f אוקטיליון" +msgstr[2] "%(value).1f אוקטיליון" +msgstr[3] "%(value).1f אוקטיליון" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" -msgstr[0] "אוקטיליון" +msgstr[0] "%(value)s אוקטיליון" msgstr[1] "%(value)s אוקטיליון" msgstr[2] "%(value)s אוקטיליון" msgstr[3] "%(value)s אוקטיליון" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f נוניליון" +msgstr[1] "%(value).1f נוניליון" +msgstr[2] "%(value).1f נוניליון" +msgstr[3] "%(value).1f נוניליון" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" -msgstr[0] "נוניליון" +msgstr[0] "%(value)s נוניליון" msgstr[1] "%(value)s נוניליון" msgstr[2] "%(value)s נוניליון" msgstr[3] "%(value)s נוניליון" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f דציליון" +msgstr[1] "%(value).1f דציליון" +msgstr[2] "%(value).1f דציליון" +msgstr[3] "%(value).1f דציליון" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" -msgstr[0] "דציליון" +msgstr[0] "%(value)s דציליון" msgstr[1] "%(value)s דציליון" msgstr[2] "%(value)s דציליון" msgstr[3] "%(value)s דציליון" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f גוגול" +msgstr[1] "%(value).1f גוגול" +msgstr[2] "%(value).1f גוגול" +msgstr[3] "%(value).1f גוגול" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" -msgstr[0] "גוגול" +msgstr[0] "%(value)s גוגול" msgstr[1] "%(value)s גוגול" msgstr[2] "%(value)s גוגול" msgstr[3] "%(value)s גוגול" @@ -214,7 +301,7 @@ msgstr "לפני %(delta)s" msgid "an hour ago" msgid_plural "%(count)s hours ago" msgstr[0] "לפני שעה" -msgstr[1] "לפני שעתיים" +msgstr[1] "לפני %(count)s שעות" msgstr[2] "לפני %(count)s שעות" msgstr[3] "לפני %(count)s שעות" @@ -233,7 +320,7 @@ msgstr[3] "לפני %(count)s דקות" #, python-format msgid "a second ago" msgid_plural "%(count)s seconds ago" -msgstr[0] "לפני שנייה" +msgstr[0] "לפני שניה" msgstr[1] "לפני %(count)s שניות" msgstr[2] "לפני %(count)s שניות" msgstr[3] "לפני %(count)s שניות" @@ -246,7 +333,7 @@ msgstr "עכשיו" #, python-format msgid "a second from now" msgid_plural "%(count)s seconds from now" -msgstr[0] "בעוד שנייה" +msgstr[0] "בעוד שניה" msgstr[1] "בעוד %(count)s שניות" msgstr[2] "בעוד %(count)s שניות" msgstr[3] "בעוד %(count)s שניות" @@ -267,7 +354,7 @@ msgstr[3] "בעוד %(count)s דקות" msgid "an hour from now" msgid_plural "%(count)s hours from now" msgstr[0] "בעוד שעה" -msgstr[1] "בעוד שעתיים" +msgstr[1] "בעוד %(count)s שעות" msgstr[2] "בעוד %(count)s שעות" msgstr[3] "בעוד %(count)s שעות" @@ -280,110 +367,110 @@ msgstr "בעוד %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "שנה" -msgstr[1] "שנתיים" -msgstr[2] "%(num)d שנים" -msgstr[3] "%(num)d שנים" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "שנה %d" +msgstr[1] "%d שנים" +msgstr[2] "%d שנים" +msgstr[3] "%d שנים" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "חודש" -msgstr[1] "חודשיים" -msgstr[2] "%(num)d חודשים" -msgstr[3] "%(num)d חודשים" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "חודש %d" +msgstr[1] "%d חודשים" +msgstr[2] "%d חודשים" +msgstr[3] "%d חודשים" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "שבוע" -msgstr[1] "שבועיים" -msgstr[2] "%(num)d שבועות" -msgstr[3] "%(num)d שבועות" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "שבוע %d" +msgstr[1] "%d שבועות" +msgstr[2] "%d שבועות" +msgstr[3] "%d שבועות" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "יום" -msgstr[1] "יומיים" -msgstr[2] "%(num)d ימים" -msgstr[3] "%(num)d ימים" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "יום %d" +msgstr[1] "%d ימים" +msgstr[2] "%d ימים" +msgstr[3] "%d ימים" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "שעה" -msgstr[1] "שעתיים" -msgstr[2] "%(num)d שעות" -msgstr[3] "%(num)d שעות" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "שעה %d" +msgstr[1] "%d שעות" +msgstr[2] "%d שעות" +msgstr[3] "%d שעות" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "דקה" -msgstr[1] "%(num)d דקות" -msgstr[2] "%(num)d דקות" -msgstr[3] "%(num)d דקות" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "דקה %d" +msgstr[1] "%d דקות" +msgstr[2] "%d דקות" +msgstr[3] "%d דקות" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "שנה" -msgstr[1] "שנתיים" -msgstr[2] "%(num)d שנים" -msgstr[3] "%(num)d שנים" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "שנה %d" +msgstr[1] "%d שנים" +msgstr[2] "%d שנים" +msgstr[3] "%d שנים" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "חודש" -msgstr[1] "חודשיים" -msgstr[2] "%(num)d חודשים" -msgstr[3] "%(num)d חודשים" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "חודש %d" +msgstr[1] "%d חודשים" +msgstr[2] "%d חודשים" +msgstr[3] "%d חודשים" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "שבוע" -msgstr[1] "שבועיים" -msgstr[2] "%(num)d שבועות" -msgstr[3] "%(num)d שבועות" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "שבוע %d" +msgstr[1] "%d שבועות" +msgstr[2] "%d שבועות" +msgstr[3] "%d שבועות" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "יום" -msgstr[1] "יומיים" -msgstr[2] "%(num)d ימים" -msgstr[3] "%(num)d ימים" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "יום %d" +msgstr[1] "%d ימים" +msgstr[2] "%d ימים" +msgstr[3] "%d ימים" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "שעה" -msgstr[1] "שעתיים" -msgstr[2] "%(num)d שעות" -msgstr[3] "%(num)d שעות" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "שעה %d" +msgstr[1] "%d שעות" +msgstr[2] "%d שעות" +msgstr[3] "%d שעות" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "דקה" -msgstr[1] "%(num)d דקות" -msgstr[2] "%(num)d דקות" -msgstr[3] "%(num)d דקות" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "דקה %d" +msgstr[1] "%d דקות" +msgstr[2] "%d דקות" +msgstr[3] "%d דקות" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo index d9618ca..42fcea6 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po index 7446faa..e6f2210 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/hsb/LC_MESSAGES/django.po @@ -1,13 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Michael Wolf <milupo@sorbzilla.de>, 2016,2018,2021 +# Michael Wolf <milupo@sorbzilla.de>, 2016,2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-28 18:41+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-26 11:14+0000\n" "Last-Translator: Michael Wolf <milupo@sorbzilla.de>\n" "Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" "language/hsb/)\n" @@ -76,6 +76,14 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milion" +msgstr[1] "%(value).1f milionaj" +msgstr[2] "%(value).1f miliony" +msgstr[3] "%(value).1f milionow" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -84,6 +92,14 @@ msgstr[1] "%(value)s milionaj" msgstr[2] "%(value)s miliony" msgstr[3] "%(value)s milionow" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliarda" +msgstr[1] "%(value).1f miliardźe" +msgstr[2] "%(value).1f miliardy" +msgstr[3] "%(value).1f miliardow" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -92,6 +108,14 @@ msgstr[1] "%(value)s miliardźe" msgstr[2] "%(value)s miliardy" msgstr[3] "%(value)s miliardow" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f bilion" +msgstr[1] "%(value).1f bilionaj" +msgstr[2] "%(value).1f biliony" +msgstr[3] "%(value).1f bilionow" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -100,6 +124,14 @@ msgstr[1] "%(value)s bilionaj" msgstr[2] "%(value)s biliony" msgstr[3] "%(value)s bilionow" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f biliarda" +msgstr[1] "%(value).1f biliardźe" +msgstr[2] "%(value).1f biliardy" +msgstr[3] "%(value).1f biliardow" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -108,6 +140,14 @@ msgstr[1] "%(value)s biliardźe" msgstr[2] "%(value)s biliardy" msgstr[3] "%(value)s biliardow" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trilion" +msgstr[1] "%(value).1f trilionaj" +msgstr[2] "%(value).1f triliony" +msgstr[3] "%(value).1f trilionow" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -116,6 +156,14 @@ msgstr[1] "%(value)s trilionaj" msgstr[2] "%(value)s triliony" msgstr[3] "%(value)s trilionow" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f triliarda" +msgstr[1] "%(value).1f triliardźe" +msgstr[2] "%(value).1f triliardy" +msgstr[3] "%(value).1f triliardow" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -124,6 +172,14 @@ msgstr[1] "%(value)s triliardźe" msgstr[2] "%(value)s triliardy" msgstr[3] "%(value)s triliardow" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f kwadrilion" +msgstr[1] "%(value).1f kwadrilionaj" +msgstr[2] "%(value).1f kwadriliony" +msgstr[3] "%(value).1f kwadrilionow" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -132,6 +188,14 @@ msgstr[1] "%(value)s kwadrilionaj" msgstr[2] "%(value)s kwadriliony" msgstr[3] "%(value)s kwadrilionow" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f kwadriliarda" +msgstr[1] "%(value).1f kwadriliardźe" +msgstr[2] "%(value).1f kwadriliardy" +msgstr[3] "%(value).1f kwadriliardow" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -140,6 +204,14 @@ msgstr[1] "%(value)s kwadriliardźe" msgstr[2] "%(value)s kwadriliardy" msgstr[3] "%(value)s kwadriliardow" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f kwintilion" +msgstr[1] "%(value).1f kwintilionaj" +msgstr[2] "%(value).1f kwintiliony" +msgstr[3] "%(value).1f kwintilionow" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -148,6 +220,14 @@ msgstr[1] "%(value)s kwintilionaj" msgstr[2] "%(value)s kwintiliony" msgstr[3] "%(value)s kwintilionow" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f kwintiliarda" +msgstr[1] "%(value).1f kwintiliardźe" +msgstr[2] "%(value).1f kwintiliardy" +msgstr[3] "%(value).1f kwintiliardow" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -156,6 +236,14 @@ msgstr[1] "%(value)s kwintiliardźe" msgstr[2] "%(value)s kwintiliardy" msgstr[3] "%(value)s kwintiliardow" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f sedeciliarda" +msgstr[1] "%(value).1f sedeciliardźe" +msgstr[2] "%(value).1f sedeciliardy" +msgstr[3] "%(value).1f sedeciliardow" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -278,110 +366,110 @@ msgstr "%(delta)s wotnětka" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)d lěće" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d lětom" +msgstr[1] "%d lětomaj" +msgstr[2] "%d lětami" +msgstr[3] "%d lětami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsac" -msgstr[1] "%(num)d měsacaj" -msgstr[2] "%(num)d měsacy" -msgstr[3] "%(num)d měsacow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsacom" +msgstr[1] "%d měsacomaj" +msgstr[2] "%d měsacami" +msgstr[3] "%d měsacami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydźeń" -msgstr[1] "%(num)d njedźeli" -msgstr[2] "%(num)d njedźele" -msgstr[3] "%(num)d njedźel" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydźenjom" +msgstr[1] "%d tydźenjomaj" +msgstr[2] "%d tydźenjemi" +msgstr[3] "%d tydźenjemi" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dźeń" -msgstr[1] "%(num)d dnjej" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dnjom" +msgstr[1] "%d dnjomaj" +msgstr[2] "%d dnjemi" +msgstr[3] "%d dnjemi" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodźina" -msgstr[1] "%(num)d hodźinje" -msgstr[2] "%(num)d hodźiny" -msgstr[3] "%(num)d hodźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodźinu" +msgstr[1] "%d hodźinomaj" +msgstr[2] "%d hodźinami" +msgstr[3] "%d hodźinami" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d mjeńšina" -msgstr[1] "%(num)d mjeńšinje" -msgstr[2] "%(num)d mjeńšiny" -msgstr[3] "%(num)d mjeńšin" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d mjeńšinu" +msgstr[1] "%d mjeńšinomaj" +msgstr[2] "%d mjeńšinami" +msgstr[3] "%d mjeńšinami" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d lěto" -msgstr[1] "%(num)d lěće" -msgstr[2] "%(num)d lěta" -msgstr[3] "%(num)d lět" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d lěto" +msgstr[1] "%d lěće" +msgstr[2] "%d lěta" +msgstr[3] "%d lět" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d měsac" -msgstr[1] "%(num)d měsacaj" -msgstr[2] "%(num)d měsacy" -msgstr[3] "%(num)d měsacow" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d měsac" +msgstr[1] "%d měsacaj" +msgstr[2] "%d měsacy" +msgstr[3] "%d měsacow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydźeń" -msgstr[1] "%(num)d njedźeli" -msgstr[2] "%(num)d njedźele" -msgstr[3] "%(num)d njedźel" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydźeń" +msgstr[1] "%d njedźeli" +msgstr[2] "%d njedźele" +msgstr[3] "%d njedźel" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dźeń" -msgstr[1] "%(num)d dnjej" -msgstr[2] "%(num)d dny" -msgstr[3] "%(num)d dnjow" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d źeń" +msgstr[1] "%d dnjej" +msgstr[2] "%d dny" +msgstr[3] "%d dnjow" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d hodźina" -msgstr[1] "%(num)d hodźinje" -msgstr[2] "%(num)d hodźiny" -msgstr[3] "%(num)d hodźin" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d hodźina" +msgstr[1] "%d hodźinje" +msgstr[2] "%d hodźiny" +msgstr[3] "%d hodźin" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d mjeńšina" -msgstr[1] "%(num)d mjeńšinje" -msgstr[2] "%(num)d mjeńšiny" -msgstr[3] "%(num)d mjeńšin" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d mjeńšina" +msgstr[1] "%d mjeńšinje" +msgstr[2] "%d mjeńšiny" +msgstr[3] "%d mjeńšin" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.mo index c464dc7..578cf5a 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.po index 8acb99d..71ff784 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/id/LC_MESSAGES/django.po @@ -1,19 +1,19 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Fery Setiawan <gembelweb@gmail.com>, 2016,2018,2021 +# Fery Setiawan <gembelweb@gmail.com>, 2016,2018 # Jannis Leidel <jannis@leidel.info>, 2011 # rodin <romihardiyanto@gmail.com>, 2011-2012 # rodin <romihardiyanto@gmail.com>, 2014 -# sag᠎e <laymonage@gmail.com>, 2019 +# sage <laymonage@gmail.com>, 2019 # Sutrisno Efendi <kangfend@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-06 09:17+0000\n" -"Last-Translator: Fery Setiawan <gembelweb@gmail.com>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-09-28 05:43+0000\n" +"Last-Translator: sage <laymonage@gmail.com>\n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" "MIME-Version: 1.0\n" @@ -80,56 +80,111 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "ke-{}" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f juta" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s juta" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliar" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s miliar" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f triliun" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s triliun" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f kuadriliun" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s kuadriliun" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f kuintiliun" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s kuintiliun" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sekstiliun" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s sekstiliun" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septiliun" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septiliun" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f oktiliun" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s oktiliun" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f noniliun" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s noniliun" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f desiliun" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s desiliun" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -231,74 +286,74 @@ msgstr "%(delta)s dari sekarang" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d tahun" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d tahun" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d bulan" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d bulan" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d minggu" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d minggu" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d hari" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d hari" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d jam" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d jam" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d menit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d menit" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d tahun" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d tahun" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d bulan" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d bulan" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d minggu" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d minggu" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d hari" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d hari" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d jam" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d jam" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d menit" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d menit" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.mo index 3860b12..b30b616 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.po index 8c1f9e5..b27d8f2 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/it/LC_MESSAGES/django.po @@ -1,9 +1,8 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Carlo Miron <carlo@miron.it>, 2014 -# Carlo Miron <carlo@miron.it>, 2018 -# Davide Targa <davide.targa@gmail.com>, 2021 +# Carlo Miron <C8E@miron.it>, 2014 +# Carlo Miron <C8E@miron.it>, 2018 # Federico Capoano <federico.capoano@teletu.it>, 2011 # Jannis Leidel <jannis@leidel.info>, 2011 # Luca Manlio De Lisi <lukefiltroman@gmail.com>, 2011 @@ -16,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-12 12:01+0000\n" -"Last-Translator: Davide Targa <davide.targa@gmail.com>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-07-30 21:09+0000\n" +"Last-Translator: Carlo Miron <C8E@miron.it>\n" "Language-Team: Italian (http://www.transifex.com/django/django/language/" "it/)\n" "MIME-Version: 1.0\n" @@ -85,66 +84,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}esimo" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milione" +msgstr[1] "%(value).1f milioni" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s milione" msgstr[1] "%(value)s milioni" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliardo" +msgstr[1] "%(value).1f miliardi" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s miliardo" msgstr[1] "%(value)s miliardi" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f migliaio di miliardi" +msgstr[1] "%(value).1f migliaia di miliardi" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s migliaio di miliardi" msgstr[1] "%(value)s migliaia di miliardi" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f milione di miliardi" +msgstr[1] "%(value).1f milioni di miliardi" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s milione di miliardi" msgstr[1] "%(value)s milioni di miliardi" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f miliardo di miliardi" +msgstr[1] "%(value).1f miliardi di miliardi" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s miliardo di miliardi" msgstr[1] "%(value)s miliardi di miliardi" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f migliaio di miliardi di miliardi" +msgstr[1] "%(value).1f migliaia di miliardi di miliardi" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s migliaio di miliardi di miliardi" msgstr[1] "%(value)s migliaia di miliardi di miliardi" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f milione di miliardi di miliardi" +msgstr[1] "%(value).1f milioni di miliardi di miliardi" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s milione di miliardi di miliardi" msgstr[1] "%(value)s milioni di miliardi di miliardi" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f miliardo di miliardi di miliardi" +msgstr[1] "%(value).1f miliardi di miliardi di miliardi" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s miliardo di miliardi di miliardi" msgstr[1] "%(value)s miliardi di miliardi di miliardi" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f migliaio di miliardi di miliardi di miliardi" +msgstr[1] "%(value).1f migliaia di miliardi di miliardi di miliardi" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s migliaio di miliardi di miliardi di miliardi" msgstr[1] "%(value)s migliaia di miliardi di miliardi di miliardi" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f milione di miliardi di miliardi di miliardi" +msgstr[1] "%(value).1f milioni di miliardi di miliardi di miliardi" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s milione di miliardi di miliardi di miliardi" msgstr[1] "%(value)s milioni di miliardi di miliardi di miliardi" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f googol" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -253,86 +318,86 @@ msgstr "%(delta)s da adesso" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d anno" -msgstr[1] "%(num)d anni" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d anni" +msgstr[1] "%d anni" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mese" -msgstr[1] "%(num)d mesi" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mesi" +msgstr[1] "%d mesi" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d settimana" -msgstr[1] "%(num)d settimane" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d settimane" +msgstr[1] "%d settimane" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d giorno" -msgstr[1] "%(num)d giorni" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d giorni" +msgstr[1] "%d giorni" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ora" -msgstr[1] "%(num)d ore" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ore" +msgstr[1] "%d ore" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minuti" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuti" +msgstr[1] "%d minuti" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d anno" -msgstr[1] "%(num)d anni" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d anni" +msgstr[1] "%d anni" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mese" -msgstr[1] "%(num)d mesi" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mesi" +msgstr[1] "%d mesi" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d settimana" -msgstr[1] "%(num)d settimane" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d settimane" +msgstr[1] "%d settimane" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d giorni" -msgstr[1] "%(num)d giorni" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d giorni" +msgstr[1] "%d giorni" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d ora" -msgstr[1] "%(num)d ore" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d ore" +msgstr[1] "%d ore" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minuto" -msgstr[1] "%(num)d minuti" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minuti" +msgstr[1] "%d minuti" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo index 0e043b9..3a76fd3 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.po index f077d0a..b1b9822 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/ja/LC_MESSAGES/django.po @@ -3,13 +3,13 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # Jonas Obrist <ojiidotch@gmail.com>, 2012 -# Shinya Okano <tokibito@gmail.com>, 2012-2014,2018,2021 +# Shinya Okano <tokibito@gmail.com>, 2012-2014,2018 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-13 11:44+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-23 03:33+0000\n" "Last-Translator: Shinya Okano <tokibito@gmail.com>\n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" @@ -77,56 +77,111 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}番目" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f ミリオン" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s ミリオン" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f ビリオン" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s ビリオン" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f トリリオン" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s トリリオン" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f クァドリリオン" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] " %(value)s クァドリリオン" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f クインテリオン" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s クインテリオン" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f セクスティリオン" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s セクスティリオン" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f セプティリオン" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s セプティリオン" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f オクティリオン" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s オクティリオン" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f ノニリオン" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s ノニリオン" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f デシリオン" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s デシリオン" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f グーゴル" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -228,74 +283,74 @@ msgstr "今から%(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)dヶ月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ヶ月" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d週間" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 週間" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d 日" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d時間" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 時間" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d分" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)dヶ月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ヶ月" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d週間" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 週間" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d 日" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d時間" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 時間" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d分" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/kn/LC_MESSAGES/django.po index 9e7eeb9..b38dd8c 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/kn/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Humanize" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo index 669a321..9d598d5 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.po index 1308815..32ed39f 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/ky/LC_MESSAGES/django.po @@ -1,14 +1,13 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>, 2021 # Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>, 2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-27 14:11+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2020-05-23 06:04+0000\n" "Last-Translator: Soyuzbek Orozbek uulu <soyuzbek196.kg@gmail.com>\n" "Language-Team: Kyrgyz (http://www.transifex.com/django/django/language/ky/)\n" "MIME-Version: 1.0\n" @@ -75,56 +74,111 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}унчу" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1fмиллион" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)sмиллион" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1fмиллиард" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)sмиллиард" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1fтриллион" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)sтриллион" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1fквадриллион" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)sквадриллион" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1fквинтиллион" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)sквинтиллион" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1fсекстиллион" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)sсекстиллион" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1fсептиллион" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)sсептиллион" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1fоктиллион" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)sоктиллион" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1fнонтиллион" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)sнонтиллион" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1fдесилион" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)sдесилион" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1fгугл" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -226,74 +280,74 @@ msgstr "мындан %(delta)s мурда" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d жыл" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%dжыл" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ай" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%dай" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d апта" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%dжума" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d күн" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%dкүн" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d саат" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%dсаат" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d мүнөт" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%dмүнөт" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d жыл" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%dжыл" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ай" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%dай" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d апта" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%dжума" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d күн" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%dкүн" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d саат" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%dсаат" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d мүнөт" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%dмүнөт" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo index 29d2e78..eae919c 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.po index 50569e5..0bac68b 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/lv/LC_MESSAGES/django.po @@ -4,15 +4,14 @@ # NullIsNot0 <nullisnot0@inbox.lv>, 2017 # NullIsNot0 <nullisnot0@inbox.lv>, 2017-2018 # Jannis Leidel <jannis@leidel.info>, 2011 -# NullIsNot0 <nullisnot0@inbox.lv>, 2021 # peterisb <pb@sungis.lv>, 2016 # peterisb <pb@sungis.lv>, 2016 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-06 05:15+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-23 16:47+0000\n" "Last-Translator: NullIsNot0 <nullisnot0@inbox.lv>\n" "Language-Team: Latvian (http://www.transifex.com/django/django/language/" "lv/)\n" @@ -81,6 +80,13 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f miljons" +msgstr[1] "%(value).1f miljoni" +msgstr[2] "%(value).1f miljonu" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -88,6 +94,13 @@ msgstr[0] "%(value)s miljonu" msgstr[1] "%(value)s miljons" msgstr[2] "%(value)s miljonu" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miljards" +msgstr[1] "%(value).1f miljardi" +msgstr[2] "%(value).1f miljardu" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -95,6 +108,13 @@ msgstr[0] "%(value)s miljardu" msgstr[1] "%(value)s miljards" msgstr[2] "%(value)s miljardu" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f triljons" +msgstr[1] "%(value).1f triljoni" +msgstr[2] "%(value).1f triljonu" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -102,6 +122,13 @@ msgstr[0] "%(value)s triljonu" msgstr[1] "%(value)s triljons" msgstr[2] "%(value)s triljonu" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f kvadriljons" +msgstr[1] "%(value).1f kvadriljoni" +msgstr[2] "%(value).1f kvadriljonu" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -109,6 +136,13 @@ msgstr[0] "%(value)s kvadriljonu" msgstr[1] "%(value)s kvadriljons" msgstr[2] "%(value)s kvadriljonu" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f kvintiljons" +msgstr[1] "%(value).1f kvintiljoni" +msgstr[2] "%(value).1f kvintiljonu" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -116,6 +150,13 @@ msgstr[0] "%(value)s kvintiljonu" msgstr[1] "%(value)s kvintiljons" msgstr[2] "%(value)s kvintiljonu" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sekstiljons" +msgstr[1] "%(value).1f sekstiljoni" +msgstr[2] "%(value).1f sekstiljonu" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -123,6 +164,13 @@ msgstr[0] "%(value)s sekstiljonu" msgstr[1] "%(value)s sekstiljons" msgstr[2] "%(value)s sekstiljonu" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septiljons" +msgstr[1] "%(value).1f septiljoni" +msgstr[2] "%(value).1f septiljonu" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -130,6 +178,13 @@ msgstr[0] "%(value)s septiljonu" msgstr[1] "%(value)s septiljons" msgstr[2] "%(value)s septiljonu" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f oktiljons" +msgstr[1] "%(value).1f oktiljoni" +msgstr[2] "%(value).1f oktiljonu" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -137,6 +192,13 @@ msgstr[0] "%(value)s oktiljonu" msgstr[1] "%(value)s oktiljons" msgstr[2] "%(value)s oktiljonu" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f noniljons" +msgstr[1] "%(value).1f noniljoni" +msgstr[2] "%(value).1f noniljonu" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -144,6 +206,13 @@ msgstr[0] "%(value)s noniljonu" msgstr[1] "%(value)s noniljons" msgstr[2] "%(value)s noniljonu" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f dekaljons" +msgstr[1] "%(value).1f dekaljoni" +msgstr[2] "%(value).1f dekaljonu" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -151,6 +220,13 @@ msgstr[0] "%(value)s dekaljonu" msgstr[1] "%(value)s dekaljons" msgstr[2] "%(value)s dekaljonu" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f gugols" +msgstr[1] "%(value).1f gugoli" +msgstr[2] "%(value).1f gugolu" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -266,98 +342,98 @@ msgstr "pēc %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d gadi" -msgstr[1] "%(num)d gads" -msgstr[2] "%(num)d gadi" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d gada" +msgstr[1] "%d gada" +msgstr[2] "%d gadiem" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mēneši" -msgstr[1] "%(num)d mēnesis" -msgstr[2] "%(num)d mēneši" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mēneša" +msgstr[1] "%d mēneša" +msgstr[2] "%d mēnešiem" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nedēļas" -msgstr[1] "%(num)d nedēļa" -msgstr[2] "%(num)d nedēļas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nedēļas" +msgstr[1] "%d nedēļas" +msgstr[2] "%d nedēļām" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dienas" -msgstr[1] "%(num)d diena" -msgstr[2] "%(num)d dienas" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dienas" +msgstr[1] "%d dienas" +msgstr[2] "%d dienām" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d stundas" -msgstr[1] "%(num)d stunda" -msgstr[2] "%(num)d stundas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d stundas" +msgstr[1] "%d stundas" +msgstr[2] "%d stundām" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minūtes" -msgstr[1] "%(num)d minūte" -msgstr[2] "%(num)d minūtes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minūtes" +msgstr[1] "%d minūtes" +msgstr[2] "%d minūtēm" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d gadi" -msgstr[1] "%(num)d gads" -msgstr[2] "%(num)d gadi" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d gada" +msgstr[1] "%d gada" +msgstr[2] "%d gadiem" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d mēneši" -msgstr[1] "%(num)d mēnesis" -msgstr[2] "%(num)d mēneši" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mēneša" +msgstr[1] "%d mēneša" +msgstr[2] "%d mēnešiem" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d nedēļas" -msgstr[1] "%(num)d nedēļa" -msgstr[2] "%(num)d nedēļas" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d nedēļas" +msgstr[1] "%d nedēļas" +msgstr[2] "%d nedēļām" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dienas" -msgstr[1] "%(num)d diena" -msgstr[2] "%(num)d dienas" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dienas" +msgstr[1] "%d dienas" +msgstr[2] "%d dienām" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d stundas" -msgstr[1] "%(num)d stunda" -msgstr[2] "%(num)d stundas" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d stundas" +msgstr[1] "%d stundas" +msgstr[2] "%d stundām" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minūtes" -msgstr[1] "%(num)d minūte" -msgstr[2] "%(num)d minūtes" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minūtes" +msgstr[1] "%d minūtes" +msgstr[2] "%d minūtēm" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo index 4053b2c..35956dd 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.po index dcbc237..ca73983 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/nn/LC_MESSAGES/django.po @@ -2,15 +2,14 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 # velmont <odin.omdal@gmail.com>, 2012 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-10-26 12:33+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -20,62 +19,25 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Humanize" -msgstr "Humanize" +msgstr "" -#. Translators: Ordinal format for 11 (11th), 12 (12th), and 13 (13th). -msgctxt "ordinal 11, 12, 13" -msgid "{}th" -msgstr "{}." +msgid "th" +msgstr "." -#. Translators: Ordinal format when value ends with 0, e.g. 80th. -msgctxt "ordinal 0" -msgid "{}th" -msgstr "{}." +msgid "st" +msgstr "." -#. Translators: Ordinal format when value ends with 1, e.g. 81st, except 11. -msgctxt "ordinal 1" -msgid "{}st" -msgstr "{}." +msgid "nd" +msgstr "." -#. Translators: Ordinal format when value ends with 2, e.g. 82nd, except 12. -msgctxt "ordinal 2" -msgid "{}nd" -msgstr "{}." +msgid "rd" +msgstr "." -#. Translators: Ordinal format when value ends with 3, e.g. 83th, except 13. -msgctxt "ordinal 3" -msgid "{}rd" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 4, e.g. 84th. -msgctxt "ordinal 4" -msgid "{}th" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 5, e.g. 85th. -msgctxt "ordinal 5" -msgid "{}th" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 6, e.g. 86th. -msgctxt "ordinal 6" -msgid "{}th" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 7, e.g. 87th. -msgctxt "ordinal 7" -msgid "{}th" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 8, e.g. 88th. -msgctxt "ordinal 8" -msgid "{}th" -msgstr "{}." - -#. Translators: Ordinal format when value ends with 9, e.g. 89th. -msgctxt "ordinal 9" -msgid "{}th" -msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f million" +msgstr[1] "%(value).1f millionar" #, python-format msgid "%(value)s million" @@ -83,60 +45,120 @@ msgid_plural "%(value)s million" msgstr[0] "%(value)s million" msgstr[1] "%(value)s millionar" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f milliard" +msgstr[1] "%(value).1f milliardar" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s milliard" msgstr[1] "%(value)s milliardar" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f billion" +msgstr[1] "%(value).1f billionar" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s billion" msgstr[1] "%(value)s billionar" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f kvadrillion" +msgstr[1] "%(value).1f kvadrillionar" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s kvadrillion" msgstr[1] "%(value)s kvadrillionar" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f kvintillion" +msgstr[1] "%(value).1f kvintillionar" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s kvintillion" msgstr[1] "%(value)s kvintillionar" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f sekstillion" +msgstr[1] "%(value).1f sekstilionar" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s sekstillion" msgstr[1] "%(value)s sekstillionar" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septillion" +msgstr[1] "%(value).1f septillionar" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septillion" msgstr[1] "%(value)s septillionar" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f oktillion" +msgstr[1] "%(value).1f oktillionar" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s oktillion" msgstr[1] "%(value)s oktillionar" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f nonillion" +msgstr[1] "%(value).1f nonillionar" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s nonillion" msgstr[1] "%(value)s nonillionar" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f desillion" +msgstr[1] "%(value).1f desillionar" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s desillion" msgstr[1] "%(value)s desillionar" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f googolar" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -179,152 +201,63 @@ msgstr "i morgon" msgid "yesterday" msgstr "i går" -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' #, python-format +msgctxt "naturaltime" msgid "%(delta)s ago" msgstr "%(delta)s sidan" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "an hour ago" -msgid_plural "%(count)s hours ago" -msgstr[0] "ein time sidan" -msgstr[1] "%(count)s timar sidan" - -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "a minute ago" -msgid_plural "%(count)s minutes ago" -msgstr[0] "eitt minutt sidan" -msgstr[1] "%(count)s minutt frå no" - -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. -#, python-format -msgid "a second ago" -msgid_plural "%(count)s seconds ago" -msgstr[0] "eitt sekund sidan" -msgstr[1] "%(count)s sekund sidan" - msgid "now" msgstr "no" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgid "a second from now" -msgid_plural "%(count)s seconds from now" -msgstr[0] "eitt sekund frå no" -msgstr[1] "%(count)s sekund frå no" +msgid "a second ago" +msgid_plural "%(count)s seconds ago" +msgstr[0] "" +msgstr[1] "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgid "a minute from now" -msgid_plural "%(count)s minutes from now" -msgstr[0] "eitt minutt frå no" -msgstr[1] "%(count)s minutt frå no" +msgid "a minute ago" +msgid_plural "%(count)s minutes ago" +msgstr[0] "" +msgstr[1] "" -#. Translators: please keep a non-breaking space (U+00A0) between count -#. and time unit. +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgid "an hour from now" -msgid_plural "%(count)s hours from now" -msgstr[0] "ein time frå no" -msgstr[1] "%(count)s timar frå no" +msgid "an hour ago" +msgid_plural "%(count)s hours ago" +msgstr[0] "" +msgstr[1] "" -#. Translators: delta will contain a string like '2 months' or '1 month, 2 -#. weeks' #, python-format +msgctxt "naturaltime" msgid "%(delta)s from now" msgstr "%(delta)s frå no" -#. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" +msgid "a second from now" +msgid_plural "%(count)s seconds from now" +msgstr[0] "" +msgstr[1] "" +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d månad" -msgstr[1] "%(num)d månader" +msgid "a minute from now" +msgid_plural "%(count)s minutes from now" +msgstr[0] "" +msgstr[1] "" +#. Translators: please keep a non-breaking space (U+00A0) +#. between count and time unit. #, python-format -msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d veke" -msgstr[1] "%(num)d veker" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dagar" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timar" - -#, python-format -msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutt" -msgstr[1] "%(num)d minutt" - -#. Translators: 'naturaltime-future' strings will be included in '%(delta)s -#. from now' -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d år" -msgstr[1] "%(num)d år" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d månad" -msgstr[1] "%(num)d månader" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d veke" -msgstr[1] "%(num)d veker" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dag" -msgstr[1] "%(num)d dagar" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d time" -msgstr[1] "%(num)d timar" - -#, python-format -msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutt" -msgstr[1] "%(num)d minutt" +msgid "an hour from now" +msgid_plural "%(count)s hours from now" +msgstr[0] "" +msgstr[1] "" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo index 9e8495f..eb3a2fd 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.po index ab0ab92..45667b8 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/pl/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # angularcircle, 2011 # angularcircle, 2014 # Jannis Leidel <jannis@leidel.info>, 2011 -# m_aciek <maciej.olko@gmail.com>, 2018,2021 +# m_aciek <maciej.olko@gmail.com>, 2018 # Piotr Meuś <piotr.meus@gmail.com>, 2014 # Roman Barczyński, 2012 # Tomasz Kajtoch <tomekkaj@tomekkaj.pl>, 2016 @@ -13,8 +13,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-23 19:40+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-19 00:03+0000\n" "Last-Translator: m_aciek <maciej.olko@gmail.com>\n" "Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" "MIME-Version: 1.0\n" @@ -83,6 +83,14 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milion" +msgstr[1] "%(value).1f miliony" +msgstr[2] "%(value).1f milionów" +msgstr[3] "%(value).1f milionów" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" @@ -91,6 +99,14 @@ msgstr[1] "%(value)s miliony" msgstr[2] "%(value)s milionów" msgstr[3] "%(value)s milionów" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f miliard" +msgstr[1] "%(value).1f miliardy" +msgstr[2] "%(value).1f miliardów" +msgstr[3] "%(value).1f miliardów" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" @@ -99,6 +115,14 @@ msgstr[1] "%(value)s miliardy" msgstr[2] "%(value)s miliardów" msgstr[3] "%(value)s miliardów" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f kwintylion" +msgstr[1] "%(value).1f biliony" +msgstr[2] "%(value).1f kwintylionów" +msgstr[3] "%(value).1f kwintylionów" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" @@ -107,6 +131,14 @@ msgstr[1] "%(value)s biliony" msgstr[2] "%(value)s kwintylionów" msgstr[3] "%(value)s kwintylionów" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f kwadrylion" +msgstr[1] "%(value).1f biliardy" +msgstr[2] "%(value).1f kwadrylionów" +msgstr[3] "%(value).1f kwadrylionów" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" @@ -115,6 +147,14 @@ msgstr[1] "%(value)s biliardy" msgstr[2] "%(value)s kwadrylionów" msgstr[3] "%(value)s kwadrylionów" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f trylion" +msgstr[1] "%(value).1f tryliony" +msgstr[2] "%(value).1f trylionów" +msgstr[3] "%(value).1f trylionów" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" @@ -123,6 +163,14 @@ msgstr[1] "%(value)s tryliony" msgstr[2] "%(value)s trylionyów" msgstr[3] "%(value)s trylionyów" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f tryliard" +msgstr[1] "%(value).1f tryliardy" +msgstr[2] "%(value).1f tryliardów" +msgstr[3] "%(value).1f tryliardów" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" @@ -131,6 +179,14 @@ msgstr[1] "%(value)s tryliardy" msgstr[2] "%(value)s tryliardów" msgstr[3] "%(value)s tryliardów" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septylion" +msgstr[1] "%(value).1f septyliony" +msgstr[2] "%(value).1f septylionów" +msgstr[3] "%(value).1f septylionów" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" @@ -139,6 +195,14 @@ msgstr[1] "%(value)s septyliony" msgstr[2] "%(value)s septylionów" msgstr[3] "%(value)s septylionów" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f kwadryliard" +msgstr[1] "%(value).1f kwardyliardy" +msgstr[2] "%(value).1f kwadryliardów" +msgstr[3] "%(value).1f kwadryliardów" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" @@ -147,6 +211,14 @@ msgstr[1] "%(value)s kwardyliardy" msgstr[2] "%(value)s kwadryliardów" msgstr[3] "%(value)s kwadryliardów" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f kwintylion" +msgstr[1] "%(value).1f kwintyliony" +msgstr[2] "%(value).1f kwintylionów" +msgstr[3] "%(value).1f kwintylionów" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" @@ -155,6 +227,14 @@ msgstr[1] "%(value)s kwintyliony" msgstr[2] "%(value)s kwintylionów" msgstr[3] "%(value)s kwintylionów" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f kwintyliard" +msgstr[1] "%(value).1f kwintyliardy" +msgstr[2] "%(value).1f kwintyliardów" +msgstr[3] "%(value).1f kwintyliardów" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" @@ -163,6 +243,14 @@ msgstr[1] "%(value)s kwintyliardy" msgstr[2] "%(value)s kwintyliardów" msgstr[3] "%(value)s kwintyliardów" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f googole" +msgstr[2] "%(value).1f googolów" +msgstr[3] "%(value).1f googolów" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -285,110 +373,110 @@ msgstr "za %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rok" -msgstr[1] "%(num)d lata" -msgstr[2] "%(num)d lat" -msgstr[3] "%(num)d roku" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d lata" +msgstr[2] "%d lat" +msgstr[3] "%d lat" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d miesiąc" -msgstr[1] "%(num)d miesiące" -msgstr[2] "%(num)d miesięcy" -msgstr[3] "%(num)d miesiąca" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d miesiąc" +msgstr[1] "%d miesiące" +msgstr[2] "%d miesięcy" +msgstr[3] "%d miesięcy" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydzień" -msgstr[1] "%(num)d tygodnie" -msgstr[2] "%(num)d tygodni" -msgstr[3] "%(num)d tygodnia" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydzień" +msgstr[1] "%d tygodnie" +msgstr[2] "%d tygodni" +msgstr[3] "%d tygodni" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dzień" -msgstr[1] "%(num)d dni" -msgstr[2] "%(num)d dni" -msgstr[3] "%(num)d dnia" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dzień" +msgstr[1] "%d dni" +msgstr[2] "%d dni" +msgstr[3] "%d dni" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d godzinę" -msgstr[1] "%(num)d godziny" -msgstr[2] "%(num)d godzin" -msgstr[3] "%(num)d godziny" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d godzinę" +msgstr[1] "%d godziny" +msgstr[2] "%d godzin" +msgstr[3] "%d godzin" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutę" -msgstr[1] "%(num)d minuty" -msgstr[2] "%(num)d minut" -msgstr[3] "%(num)d minuty" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutę" +msgstr[1] "%d minuty" +msgstr[2] "%d minut" +msgstr[3] "%d minut" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d rok" -msgstr[1] "%(num)d lata" -msgstr[2] "%(num)d lat" -msgstr[3] "%(num)d roku" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d rok" +msgstr[1] "%d lata" +msgstr[2] "%d lat" +msgstr[3] "%d lat" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d miesiąc" -msgstr[1] "%(num)d miesiące" -msgstr[2] "%(num)d miesięcy" -msgstr[3] "%(num)dmiesiąca" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d miesiąc" +msgstr[1] "%d miesiące" +msgstr[2] "%d miesięcy" +msgstr[3] "%d miesięcy" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d tydzień" -msgstr[1] "%(num)d tygodnie" -msgstr[2] "%(num)d tygodni" -msgstr[3] "%(num)d tygodnia" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d tydzień" +msgstr[1] "%d tygodnie" +msgstr[2] "%d tygodni" +msgstr[3] "%d tygodni" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d dzień" -msgstr[1] "%(num)d dni" -msgstr[2] "%(num)d dni" -msgstr[3] "%(num)d dnia" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d dzień" +msgstr[1] "%d dni" +msgstr[2] "%d dni" +msgstr[3] "%d dni" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d godzinę" -msgstr[1] "%(num)d godziny" -msgstr[2] "%(num)d godzin" -msgstr[3] "%(num)d godziny" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d godzinę" +msgstr[1] "%d godziny" +msgstr[2] "%d godzin" +msgstr[3] "%d godzin" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d minutę" -msgstr[1] "%(num)d minuty" -msgstr[2] "%(num)d minut" -msgstr[3] "%(num)d minuty" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minutę" +msgstr[1] "%d minuty" +msgstr[2] "%d minut" +msgstr[3] "%d minut" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo index 348e992..b6d4927 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po index ef2b605..0419f3a 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ # # Translators: # Ahmet Emre Aladağ <emre.aladag@isik.edu.tr>, 2013 -# BouRock, 2018,2021 +# BouRock, 2018 # BouRock, 2014 # Jannis Leidel <jannis@leidel.info>, 2011 # Metin Amiroff <amiroff@gmail.com>, 2012 @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-09-22 17:36+0000\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2018-05-18 09:57+0000\n" "Last-Translator: BouRock\n" "Language-Team: Turkish (http://www.transifex.com/django/django/language/" "tr/)\n" @@ -80,66 +80,132 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "{}." +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milyon" +msgstr[1] "%(value).1f milyon" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s milyon" msgstr[1] "%(value)s milyon" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f milyar" +msgstr[1] "%(value).1f milyar" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s milyar" msgstr[1] "%(value)s milyar" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f trilyon" +msgstr[1] "%(value).1f trilyon" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s trilyon" msgstr[1] "%(value)s trilyon" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f katrilyon" +msgstr[1] "%(value).1f katrilyon" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s katrilyon" msgstr[1] "%(value)s katrilyon" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f kentilyon" +msgstr[1] "%(value).1f kentilyon" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s kentilyon" msgstr[1] "%(value)s kentilyon" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f seksilyon" +msgstr[1] "%(value).1f seksilyon" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s seksilyon" msgstr[1] "%(value)s seksilyon" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f septilyon" +msgstr[1] "%(value).1f septilyon" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s septilyon" msgstr[1] "%(value)s septilyon" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f oktilyon" +msgstr[1] "%(value).1f oktilyon" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s oktilyon" msgstr[1] "%(value)s oktilyon" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f nonilyon" +msgstr[1] "%(value).1f nonilyon" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s nonilyon" msgstr[1] "%(value)s nonilyon" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f desilyon" +msgstr[1] "%(value).1f desilyon" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s desilyon" msgstr[1] "%(value)s desilyon" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f googol" +msgstr[1] "%(value).1f googol" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -248,86 +314,86 @@ msgstr "şu andan itibaren %(delta)s" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d yıl" -msgstr[1] "%(num)d yıl" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d yıl" +msgstr[1] "%d yıl" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ay" -msgstr[1] "%(num)d ay" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ay" +msgstr[1] "%d ay" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d hafta" -msgstr[1] "%(num)d hafta" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d hafta" +msgstr[1] "%d hafta" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d gün" -msgstr[1] "%(num)d gün" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d gün" +msgstr[1] "%d gün" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d saat" -msgstr[1] "%(num)d saat" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d saat" +msgstr[1] "%d saat" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d dakika" -msgstr[1] "%(num)d dakika" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d dakika" +msgstr[1] "%d dakika" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d yıl" -msgstr[1] "%(num)d yıl" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d yıl" +msgstr[1] "%d yıl" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d ay" -msgstr[1] "%(num)d ay" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d ay" +msgstr[1] "%d ay" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d hafta" -msgstr[1] "%(num)d hafta" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d hafta" +msgstr[1] "%d hafta" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d gün" -msgstr[1] "%(num)d gün" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d gün" +msgstr[1] "%d gün" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d saat" -msgstr[1] "%(num)d saat" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d saat" +msgstr[1] "%d saat" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d dakika" -msgstr[1] "%(num)d dakika" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d dakika" +msgstr[1] "%d dakika" diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo index e0e6617..6b5a01b 100644 Binary files a/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po index abbb1b0..d35f2bc 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/humanize/locale/zh_Hans/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# lanbla <lanlinwen@buaa.edu.cn>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 # Jenny Qian <jqian@tendenci.com>, 2018 # Lele Long <schemacs@gmail.com>, 2015 @@ -15,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-07 14:40+0200\n" -"PO-Revision-Date: 2021-11-22 03:16+0000\n" -"Last-Translator: lanbla <lanlinwen@buaa.edu.cn>\n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-02-01 07:30+0000\n" +"Last-Translator: Suntravel Chris <chrisfromcn@gmail.com>\n" "Language-Team: Chinese (China) (http://www.transifex.com/django/django/" "language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -84,56 +83,111 @@ msgctxt "ordinal 9" msgid "{}th" msgstr "第{}" +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f 百万" + #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" msgstr[0] "%(value)s 百万" +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f 十亿" + #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" msgstr[0] "%(value)s 十亿" +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f 万亿" + #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" msgstr[0] "%(value)s 万亿" +#, python-format +msgid "%(value).1f quadrillion" +msgid_plural "%(value).1f quadrillion" +msgstr[0] "%(value).1f 1000的5次方" + #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" msgstr[0] "%(value)s 1000的5次方" +#, python-format +msgid "%(value).1f quintillion" +msgid_plural "%(value).1f quintillion" +msgstr[0] "%(value).1f 1000的4次方" + #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" msgstr[0] "%(value)s 1000的4次方" +#, python-format +msgid "%(value).1f sextillion" +msgid_plural "%(value).1f sextillion" +msgstr[0] "%(value).1f 1000的7次方" + #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" msgstr[0] "%(value)s 1000的7次方" +#, python-format +msgid "%(value).1f septillion" +msgid_plural "%(value).1f septillion" +msgstr[0] "%(value).1f 1000的8次方" + #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" msgstr[0] "%(value)s 1000的8次方" +#, python-format +msgid "%(value).1f octillion" +msgid_plural "%(value).1f octillion" +msgstr[0] "%(value).1f 1000的9次方" + #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" msgstr[0] "%(value)s 1000的9次方" +#, python-format +msgid "%(value).1f nonillion" +msgid_plural "%(value).1f nonillion" +msgstr[0] "%(value).1f 1000的10次方" + #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" msgstr[0] "%(value)s 1000的10次方" +#, python-format +msgid "%(value).1f decillion" +msgid_plural "%(value).1f decillion" +msgstr[0] "%(value).1f 1000的11次方" + #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" msgstr[0] "%(value)s 1000的11次方" +#, python-format +msgid "%(value).1f googol" +msgid_plural "%(value).1f googol" +msgstr[0] "%(value).1f 10的100次方" + #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" @@ -235,74 +289,74 @@ msgstr "%(delta)s之后" #. Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' #, python-format msgctxt "naturaltime-past" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d 年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d 月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d月" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d 周" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 周" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d 日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d天" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d 小时" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 小时" #, python-format msgctxt "naturaltime-past" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d 分钟" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分" #. Translators: 'naturaltime-future' strings will be included in '%(delta)s #. from now' #, python-format msgctxt "naturaltime-future" -msgid "%(num)d year" -msgid_plural "%(num)d years" -msgstr[0] "%(num)d 年" +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d 年" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d month" -msgid_plural "%(num)d months" -msgstr[0] "%(num)d 月" +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d 月" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d week" -msgid_plural "%(num)d weeks" -msgstr[0] "%(num)d 周" +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d 周" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d day" -msgid_plural "%(num)d days" -msgstr[0] "%(num)d 日" +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d 天" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d hour" -msgid_plural "%(num)d hours" -msgstr[0] "%(num)d 小时" +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d 小时" #, python-format msgctxt "naturaltime-future" -msgid "%(num)d minute" -msgid_plural "%(num)d minutes" -msgstr[0] "%(num)d 分钟" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d 分" diff --git a/venv/Lib/site-packages/django/contrib/humanize/templatetags/humanize.py b/venv/Lib/site-packages/django/contrib/humanize/templatetags/humanize.py index 07bb70b..753a0d9 100644 --- a/venv/Lib/site-packages/django/contrib/humanize/templatetags/humanize.py +++ b/venv/Lib/site-packages/django/contrib/humanize/templatetags/humanize.py @@ -7,14 +7,9 @@ from django.template import defaultfilters from django.utils.formats import number_format from django.utils.safestring import mark_safe from django.utils.timezone import is_aware, utc -from django.utils.translation import gettext as _ from django.utils.translation import ( - gettext_lazy, - ngettext, - ngettext_lazy, - npgettext_lazy, - pgettext, - round_away_from_one, + gettext as _, gettext_lazy, ngettext, ngettext_lazy, npgettext_lazy, + pgettext, round_away_from_one, ) register = template.Library() @@ -32,29 +27,29 @@ def ordinal(value): return value if value % 100 in (11, 12, 13): # Translators: Ordinal format for 11 (11th), 12 (12th), and 13 (13th). - value = pgettext("ordinal 11, 12, 13", "{}th").format(value) + value = pgettext('ordinal 11, 12, 13', '{}th').format(value) else: templates = ( # Translators: Ordinal format when value ends with 0, e.g. 80th. - pgettext("ordinal 0", "{}th"), + pgettext('ordinal 0', '{}th'), # Translators: Ordinal format when value ends with 1, e.g. 81st, except 11. - pgettext("ordinal 1", "{}st"), + pgettext('ordinal 1', '{}st'), # Translators: Ordinal format when value ends with 2, e.g. 82nd, except 12. - pgettext("ordinal 2", "{}nd"), + pgettext('ordinal 2', '{}nd'), # Translators: Ordinal format when value ends with 3, e.g. 83th, except 13. - pgettext("ordinal 3", "{}rd"), + pgettext('ordinal 3', '{}rd'), # Translators: Ordinal format when value ends with 4, e.g. 84th. - pgettext("ordinal 4", "{}th"), + pgettext('ordinal 4', '{}th'), # Translators: Ordinal format when value ends with 5, e.g. 85th. - pgettext("ordinal 5", "{}th"), + pgettext('ordinal 5', '{}th'), # Translators: Ordinal format when value ends with 6, e.g. 86th. - pgettext("ordinal 6", "{}th"), + pgettext('ordinal 6', '{}th'), # Translators: Ordinal format when value ends with 7, e.g. 87th. - pgettext("ordinal 7", "{}th"), + pgettext('ordinal 7', '{}th'), # Translators: Ordinal format when value ends with 8, e.g. 88th. - pgettext("ordinal 8", "{}th"), + pgettext('ordinal 8', '{}th'), # Translators: Ordinal format when value ends with 9, e.g. 89th. - pgettext("ordinal 9", "{}th"), + pgettext('ordinal 9', '{}th'), ) value = templates[value % 10].format(value) # Mark value safe so i18n does not break with <sup> or <sub> see #19988 @@ -76,7 +71,7 @@ def intcomma(value, use_l10n=True): else: return number_format(value, use_l10n=True, force_grouping=True) orig = str(value) - new = re.sub(r"^(-?\d+)(\d{3})", r"\g<1>,\g<2>", orig) + new = re.sub(r"^(-?\d+)(\d{3})", r'\g<1>,\g<2>', orig) if orig == new: return new else: @@ -85,33 +80,17 @@ def intcomma(value, use_l10n=True): # A tuple of standard large number to their converters intword_converters = ( - (6, lambda number: ngettext("%(value)s million", "%(value)s million", number)), - (9, lambda number: ngettext("%(value)s billion", "%(value)s billion", number)), - (12, lambda number: ngettext("%(value)s trillion", "%(value)s trillion", number)), - ( - 15, - lambda number: ngettext( - "%(value)s quadrillion", "%(value)s quadrillion", number - ), - ), - ( - 18, - lambda number: ngettext( - "%(value)s quintillion", "%(value)s quintillion", number - ), - ), - ( - 21, - lambda number: ngettext("%(value)s sextillion", "%(value)s sextillion", number), - ), - ( - 24, - lambda number: ngettext("%(value)s septillion", "%(value)s septillion", number), - ), - (27, lambda number: ngettext("%(value)s octillion", "%(value)s octillion", number)), - (30, lambda number: ngettext("%(value)s nonillion", "%(value)s nonillion", number)), - (33, lambda number: ngettext("%(value)s decillion", "%(value)s decillion", number)), - (100, lambda number: ngettext("%(value)s googol", "%(value)s googol", number)), + (6, lambda number: ngettext('%(value)s million', '%(value)s million', number)), + (9, lambda number: ngettext('%(value)s billion', '%(value)s billion', number)), + (12, lambda number: ngettext('%(value)s trillion', '%(value)s trillion', number)), + (15, lambda number: ngettext('%(value)s quadrillion', '%(value)s quadrillion', number)), + (18, lambda number: ngettext('%(value)s quintillion', '%(value)s quintillion', number)), + (21, lambda number: ngettext('%(value)s sextillion', '%(value)s sextillion', number)), + (24, lambda number: ngettext('%(value)s septillion', '%(value)s septillion', number)), + (27, lambda number: ngettext('%(value)s octillion', '%(value)s octillion', number)), + (30, lambda number: ngettext('%(value)s nonillion', '%(value)s nonillion', number)), + (33, lambda number: ngettext('%(value)s decillion', '%(value)s decillion', number)), + (100, lambda number: ngettext('%(value)s googol', '%(value)s googol', number)), ) @@ -132,12 +111,12 @@ def intword(value): return value for exponent, converter in intword_converters: - large_number = 10**exponent + large_number = 10 ** exponent if abs_value < large_number * 1000: new_value = value / large_number rounded_value = round_away_from_one(new_value) return converter(abs(rounded_value)) % { - "value": defaultfilters.floatformat(new_value, 1), + 'value': defaultfilters.floatformat(new_value, 1), } return value @@ -154,17 +133,8 @@ def apnumber(value): return value if not 0 < value < 10: return value - return ( - _("one"), - _("two"), - _("three"), - _("four"), - _("five"), - _("six"), - _("seven"), - _("eight"), - _("nine"), - )[value - 1] + return (_('one'), _('two'), _('three'), _('four'), _('five'), + _('six'), _('seven'), _('eight'), _('nine'))[value - 1] # Perform the comparison in the default time zone when USE_TZ = True @@ -176,7 +146,7 @@ def naturalday(value, arg=None): present day return representing string. Otherwise, return a string formatted according to settings.DATE_FORMAT. """ - tzinfo = getattr(value, "tzinfo", None) + tzinfo = getattr(value, 'tzinfo', None) try: value = date(value.year, value.month, value.day) except AttributeError: @@ -185,11 +155,11 @@ def naturalday(value, arg=None): today = datetime.now(tzinfo).date() delta = value - today if delta.days == 0: - return _("today") + return _('today') elif delta.days == 1: - return _("tomorrow") + return _('tomorrow') elif delta.days == -1: - return _("yesterday") + return _('yesterday') return defaultfilters.date(value, arg) @@ -207,75 +177,46 @@ def naturaltime(value): class NaturalTimeFormatter: time_strings = { # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' - "past-day": gettext_lazy("%(delta)s ago"), + 'past-day': gettext_lazy('%(delta)s ago'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "past-hour": ngettext_lazy("an hour ago", "%(count)s hours ago", "count"), + 'past-hour': ngettext_lazy('an hour ago', '%(count)s hours ago', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "past-minute": ngettext_lazy("a minute ago", "%(count)s minutes ago", "count"), + 'past-minute': ngettext_lazy('a minute ago', '%(count)s minutes ago', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "past-second": ngettext_lazy("a second ago", "%(count)s seconds ago", "count"), - "now": gettext_lazy("now"), + 'past-second': ngettext_lazy('a second ago', '%(count)s seconds ago', 'count'), + 'now': gettext_lazy('now'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "future-second": ngettext_lazy( - "a second from now", "%(count)s seconds from now", "count" - ), + 'future-second': ngettext_lazy('a second from now', '%(count)s seconds from now', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "future-minute": ngettext_lazy( - "a minute from now", "%(count)s minutes from now", "count" - ), + 'future-minute': ngettext_lazy('a minute from now', '%(count)s minutes from now', 'count'), # Translators: please keep a non-breaking space (U+00A0) between count # and time unit. - "future-hour": ngettext_lazy( - "an hour from now", "%(count)s hours from now", "count" - ), + 'future-hour': ngettext_lazy('an hour from now', '%(count)s hours from now', 'count'), # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' - "future-day": gettext_lazy("%(delta)s from now"), + 'future-day': gettext_lazy('%(delta)s from now'), } past_substrings = { # Translators: 'naturaltime-past' strings will be included in '%(delta)s ago' - "year": npgettext_lazy( - "naturaltime-past", "%(num)d year", "%(num)d years", "num" - ), - "month": npgettext_lazy( - "naturaltime-past", "%(num)d month", "%(num)d months", "num" - ), - "week": npgettext_lazy( - "naturaltime-past", "%(num)d week", "%(num)d weeks", "num" - ), - "day": npgettext_lazy("naturaltime-past", "%(num)d day", "%(num)d days", "num"), - "hour": npgettext_lazy( - "naturaltime-past", "%(num)d hour", "%(num)d hours", "num" - ), - "minute": npgettext_lazy( - "naturaltime-past", "%(num)d minute", "%(num)d minutes", "num" - ), + 'year': npgettext_lazy('naturaltime-past', '%d year', '%d years'), + 'month': npgettext_lazy('naturaltime-past', '%d month', '%d months'), + 'week': npgettext_lazy('naturaltime-past', '%d week', '%d weeks'), + 'day': npgettext_lazy('naturaltime-past', '%d day', '%d days'), + 'hour': npgettext_lazy('naturaltime-past', '%d hour', '%d hours'), + 'minute': npgettext_lazy('naturaltime-past', '%d minute', '%d minutes'), } future_substrings = { - # Translators: 'naturaltime-future' strings will be included in - # '%(delta)s from now'. - "year": npgettext_lazy( - "naturaltime-future", "%(num)d year", "%(num)d years", "num" - ), - "month": npgettext_lazy( - "naturaltime-future", "%(num)d month", "%(num)d months", "num" - ), - "week": npgettext_lazy( - "naturaltime-future", "%(num)d week", "%(num)d weeks", "num" - ), - "day": npgettext_lazy( - "naturaltime-future", "%(num)d day", "%(num)d days", "num" - ), - "hour": npgettext_lazy( - "naturaltime-future", "%(num)d hour", "%(num)d hours", "num" - ), - "minute": npgettext_lazy( - "naturaltime-future", "%(num)d minute", "%(num)d minutes", "num" - ), + # Translators: 'naturaltime-future' strings will be included in '%(delta)s from now' + 'year': npgettext_lazy('naturaltime-future', '%d year', '%d years'), + 'month': npgettext_lazy('naturaltime-future', '%d month', '%d months'), + 'week': npgettext_lazy('naturaltime-future', '%d week', '%d weeks'), + 'day': npgettext_lazy('naturaltime-future', '%d day', '%d days'), + 'hour': npgettext_lazy('naturaltime-future', '%d hour', '%d hours'), + 'minute': npgettext_lazy('naturaltime-future', '%d minute', '%d minutes'), } @classmethod @@ -287,36 +228,32 @@ class NaturalTimeFormatter: if value < now: delta = now - value if delta.days != 0: - return cls.time_strings["past-day"] % { - "delta": defaultfilters.timesince( - value, now, time_strings=cls.past_substrings - ), + return cls.time_strings['past-day'] % { + 'delta': defaultfilters.timesince(value, now, time_strings=cls.past_substrings), } elif delta.seconds == 0: - return cls.time_strings["now"] + return cls.time_strings['now'] elif delta.seconds < 60: - return cls.time_strings["past-second"] % {"count": delta.seconds} + return cls.time_strings['past-second'] % {'count': delta.seconds} elif delta.seconds // 60 < 60: count = delta.seconds // 60 - return cls.time_strings["past-minute"] % {"count": count} + return cls.time_strings['past-minute'] % {'count': count} else: count = delta.seconds // 60 // 60 - return cls.time_strings["past-hour"] % {"count": count} + return cls.time_strings['past-hour'] % {'count': count} else: delta = value - now if delta.days != 0: - return cls.time_strings["future-day"] % { - "delta": defaultfilters.timeuntil( - value, now, time_strings=cls.future_substrings - ), + return cls.time_strings['future-day'] % { + 'delta': defaultfilters.timeuntil(value, now, time_strings=cls.future_substrings), } elif delta.seconds == 0: - return cls.time_strings["now"] + return cls.time_strings['now'] elif delta.seconds < 60: - return cls.time_strings["future-second"] % {"count": delta.seconds} + return cls.time_strings['future-second'] % {'count': delta.seconds} elif delta.seconds // 60 < 60: count = delta.seconds // 60 - return cls.time_strings["future-minute"] % {"count": count} + return cls.time_strings['future-minute'] % {'count': count} else: count = delta.seconds // 60 // 60 - return cls.time_strings["future-hour"] % {"count": count} + return cls.time_strings['future-hour'] % {'count': count} diff --git a/venv/Lib/site-packages/django/contrib/messages/api.py b/venv/Lib/site-packages/django/contrib/messages/api.py index 7a67e8b..f0da168 100644 --- a/venv/Lib/site-packages/django/contrib/messages/api.py +++ b/venv/Lib/site-packages/django/contrib/messages/api.py @@ -2,16 +2,10 @@ from django.contrib.messages import constants from django.contrib.messages.storage import default_storage __all__ = ( - "add_message", - "get_messages", - "get_level", - "set_level", - "debug", - "info", - "success", - "warning", - "error", - "MessageFailure", + 'add_message', 'get_messages', + 'get_level', 'set_level', + 'debug', 'info', 'success', 'warning', 'error', + 'MessageFailure', ) @@ -19,22 +13,22 @@ class MessageFailure(Exception): pass -def add_message(request, level, message, extra_tags="", fail_silently=False): +def add_message(request, level, message, extra_tags='', fail_silently=False): """ Attempt to add a message to the request using the 'messages' app. """ try: messages = request._messages except AttributeError: - if not hasattr(request, "META"): + if not hasattr(request, 'META'): raise TypeError( "add_message() argument must be an HttpRequest object, not " "'%s'." % request.__class__.__name__ ) if not fail_silently: raise MessageFailure( - "You cannot add messages without installing " - "django.contrib.messages.middleware.MessageMiddleware" + 'You cannot add messages without installing ' + 'django.contrib.messages.middleware.MessageMiddleware' ) else: return messages.add(level, message, extra_tags) @@ -45,7 +39,7 @@ def get_messages(request): Return the message storage on the request if it exists, otherwise return an empty list. """ - return getattr(request, "_messages", []) + return getattr(request, '_messages', []) def get_level(request): @@ -55,7 +49,7 @@ def get_level(request): The default level is the ``MESSAGE_LEVEL`` setting. If this is not found, use the ``INFO`` level. """ - storage = getattr(request, "_messages", default_storage(request)) + storage = getattr(request, '_messages', default_storage(request)) return storage.level @@ -66,62 +60,37 @@ def set_level(request, level): If set to ``None``, use the default level (see the get_level() function). """ - if not hasattr(request, "_messages"): + if not hasattr(request, '_messages'): return False request._messages.level = level return True -def debug(request, message, extra_tags="", fail_silently=False): +def debug(request, message, extra_tags='', fail_silently=False): """Add a message with the ``DEBUG`` level.""" - add_message( - request, - constants.DEBUG, - message, - extra_tags=extra_tags, - fail_silently=fail_silently, - ) + add_message(request, constants.DEBUG, message, extra_tags=extra_tags, + fail_silently=fail_silently) -def info(request, message, extra_tags="", fail_silently=False): +def info(request, message, extra_tags='', fail_silently=False): """Add a message with the ``INFO`` level.""" - add_message( - request, - constants.INFO, - message, - extra_tags=extra_tags, - fail_silently=fail_silently, - ) + add_message(request, constants.INFO, message, extra_tags=extra_tags, + fail_silently=fail_silently) -def success(request, message, extra_tags="", fail_silently=False): +def success(request, message, extra_tags='', fail_silently=False): """Add a message with the ``SUCCESS`` level.""" - add_message( - request, - constants.SUCCESS, - message, - extra_tags=extra_tags, - fail_silently=fail_silently, - ) + add_message(request, constants.SUCCESS, message, extra_tags=extra_tags, + fail_silently=fail_silently) -def warning(request, message, extra_tags="", fail_silently=False): +def warning(request, message, extra_tags='', fail_silently=False): """Add a message with the ``WARNING`` level.""" - add_message( - request, - constants.WARNING, - message, - extra_tags=extra_tags, - fail_silently=fail_silently, - ) + add_message(request, constants.WARNING, message, extra_tags=extra_tags, + fail_silently=fail_silently) -def error(request, message, extra_tags="", fail_silently=False): +def error(request, message, extra_tags='', fail_silently=False): """Add a message with the ``ERROR`` level.""" - add_message( - request, - constants.ERROR, - message, - extra_tags=extra_tags, - fail_silently=fail_silently, - ) + add_message(request, constants.ERROR, message, extra_tags=extra_tags, + fail_silently=fail_silently) diff --git a/venv/Lib/site-packages/django/contrib/messages/apps.py b/venv/Lib/site-packages/django/contrib/messages/apps.py index 957fbda..0ff25d3 100644 --- a/venv/Lib/site-packages/django/contrib/messages/apps.py +++ b/venv/Lib/site-packages/django/contrib/messages/apps.py @@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _ class MessagesConfig(AppConfig): - name = "django.contrib.messages" + name = 'django.contrib.messages' verbose_name = _("Messages") diff --git a/venv/Lib/site-packages/django/contrib/messages/constants.py b/venv/Lib/site-packages/django/contrib/messages/constants.py index 92c9242..b431448 100644 --- a/venv/Lib/site-packages/django/contrib/messages/constants.py +++ b/venv/Lib/site-packages/django/contrib/messages/constants.py @@ -5,17 +5,17 @@ WARNING = 30 ERROR = 40 DEFAULT_TAGS = { - DEBUG: "debug", - INFO: "info", - SUCCESS: "success", - WARNING: "warning", - ERROR: "error", + DEBUG: 'debug', + INFO: 'info', + SUCCESS: 'success', + WARNING: 'warning', + ERROR: 'error', } DEFAULT_LEVELS = { - "DEBUG": DEBUG, - "INFO": INFO, - "SUCCESS": SUCCESS, - "WARNING": WARNING, - "ERROR": ERROR, + 'DEBUG': DEBUG, + 'INFO': INFO, + 'SUCCESS': SUCCESS, + 'WARNING': WARNING, + 'ERROR': ERROR, } diff --git a/venv/Lib/site-packages/django/contrib/messages/context_processors.py b/venv/Lib/site-packages/django/contrib/messages/context_processors.py index e01cc31..b4b2095 100644 --- a/venv/Lib/site-packages/django/contrib/messages/context_processors.py +++ b/venv/Lib/site-packages/django/contrib/messages/context_processors.py @@ -8,6 +8,6 @@ def messages(request): 'DEFAULT_MESSAGE_LEVELS'. """ return { - "messages": get_messages(request), - "DEFAULT_MESSAGE_LEVELS": DEFAULT_LEVELS, + 'messages': get_messages(request), + 'DEFAULT_MESSAGE_LEVELS': DEFAULT_LEVELS, } diff --git a/venv/Lib/site-packages/django/contrib/messages/middleware.py b/venv/Lib/site-packages/django/contrib/messages/middleware.py index 0087031..d5b787c 100644 --- a/venv/Lib/site-packages/django/contrib/messages/middleware.py +++ b/venv/Lib/site-packages/django/contrib/messages/middleware.py @@ -19,8 +19,8 @@ class MessageMiddleware(MiddlewareMixin): """ # A higher middleware layer may return a request which does not contain # messages storage, so make no assumption that it will be there. - if hasattr(request, "_messages"): + if hasattr(request, '_messages'): unstored_messages = request._messages.update(response) if unstored_messages and settings.DEBUG: - raise ValueError("Not all temporary messages could be stored.") + raise ValueError('Not all temporary messages could be stored.') return response diff --git a/venv/Lib/site-packages/django/contrib/messages/storage/base.py b/venv/Lib/site-packages/django/contrib/messages/storage/base.py index 61c5758..b2eeac7 100644 --- a/venv/Lib/site-packages/django/contrib/messages/storage/base.py +++ b/venv/Lib/site-packages/django/contrib/messages/storage/base.py @@ -34,11 +34,11 @@ class Message: @property def tags(self): - return " ".join(tag for tag in [self.extra_tags, self.level_tag] if tag) + return ' '.join(tag for tag in [self.extra_tags, self.level_tag] if tag) @property def level_tag(self): - return LEVEL_TAGS.get(self.level, "") + return LEVEL_TAGS.get(self.level, '') class BaseStorage: @@ -69,16 +69,13 @@ class BaseStorage: def __contains__(self, item): return item in self._loaded_messages or item in self._queued_messages - def __repr__(self): - return f"<{self.__class__.__qualname__}: request={self.request!r}>" - @property def _loaded_messages(self): """ Return a list of loaded messages, retrieving them first if they have not been loaded yet. """ - if not hasattr(self, "_loaded_data"): + if not hasattr(self, '_loaded_data'): messages, all_retrieved = self._get() self._loaded_data = messages or [] return self._loaded_data @@ -96,9 +93,7 @@ class BaseStorage: just containing no messages) then ``None`` should be returned in place of ``messages``. """ - raise NotImplementedError( - "subclasses of BaseStorage must provide a _get() method" - ) + raise NotImplementedError('subclasses of BaseStorage must provide a _get() method') def _store(self, messages, response, *args, **kwargs): """ @@ -109,9 +104,7 @@ class BaseStorage: **This method must be implemented by a subclass.** """ - raise NotImplementedError( - "subclasses of BaseStorage must provide a _store() method" - ) + raise NotImplementedError('subclasses of BaseStorage must provide a _store() method') def _prepare_messages(self, messages): """ @@ -134,7 +127,7 @@ class BaseStorage: messages = self._loaded_messages + self._queued_messages return self._store(messages, response) - def add(self, level, message, extra_tags=""): + def add(self, level, message, extra_tags=''): """ Queue a message to be stored. @@ -159,8 +152,8 @@ class BaseStorage: The default level is the ``MESSAGE_LEVEL`` setting. If this is not found, the ``INFO`` level is used. """ - if not hasattr(self, "_level"): - self._level = getattr(settings, "MESSAGE_LEVEL", constants.INFO) + if not hasattr(self, '_level'): + self._level = getattr(settings, 'MESSAGE_LEVEL', constants.INFO) return self._level def _set_level(self, value=None): @@ -170,7 +163,7 @@ class BaseStorage: If set to ``None``, the default level will be used (see the ``_get_level`` method). """ - if value is None and hasattr(self, "_level"): + if value is None and hasattr(self, '_level'): del self._level else: self._level = int(value) diff --git a/venv/Lib/site-packages/django/contrib/messages/storage/cookie.py b/venv/Lib/site-packages/django/contrib/messages/storage/cookie.py index 8251a01..17bed82 100644 --- a/venv/Lib/site-packages/django/contrib/messages/storage/cookie.py +++ b/venv/Lib/site-packages/django/contrib/messages/storage/cookie.py @@ -5,6 +5,7 @@ from django.conf import settings from django.contrib.messages.storage.base import BaseStorage, Message from django.core import signing from django.http import SimpleCookie +from django.utils.crypto import constant_time_compare, salted_hmac from django.utils.safestring import SafeData, mark_safe @@ -12,8 +13,7 @@ class MessageEncoder(json.JSONEncoder): """ Compactly serialize instances of the ``Message`` class as JSON. """ - - message_key = "__json_message" + message_key = '__json_message' def default(self, obj): if isinstance(obj, Message): @@ -39,7 +39,8 @@ class MessageDecoder(json.JSONDecoder): return Message(*obj[2:]) return [self.process_messages(item) for item in obj] if isinstance(obj, dict): - return {key: self.process_messages(value) for key, value in obj.items()} + return {key: self.process_messages(value) + for key, value in obj.items()} return obj def decode(self, s, **kwargs): @@ -51,26 +52,25 @@ class MessageSerializer: def dumps(self, obj): return json.dumps( obj, - separators=(",", ":"), + separators=(',', ':'), cls=MessageEncoder, - ).encode("latin-1") + ).encode('latin-1') def loads(self, data): - return json.loads(data.decode("latin-1"), cls=MessageDecoder) + return json.loads(data.decode('latin-1'), cls=MessageDecoder) class CookieStorage(BaseStorage): """ Store messages in a cookie. """ - - cookie_name = "messages" + cookie_name = 'messages' # uwsgi's default configuration enforces a maximum size of 4kb for all the # HTTP headers. In order to leave some room for other cookies and headers, # restrict the session cookie to 1/2 of 4kb. See #18781. max_cookie_size = 2048 - not_finished = "__messagesnotfinished__" - key_salt = "django.contrib.messages" + not_finished = '__messagesnotfinished__' + key_salt = 'django.contrib.messages' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -98,8 +98,7 @@ class CookieStorage(BaseStorage): """ if encoded_data: response.set_cookie( - self.cookie_name, - encoded_data, + self.cookie_name, encoded_data, domain=settings.SESSION_COOKIE_DOMAIN, secure=settings.SESSION_COOKIE_SECURE or None, httponly=settings.SESSION_COOKIE_HTTPONLY or None, @@ -136,12 +135,23 @@ class CookieStorage(BaseStorage): unstored_messages.append(messages.pop(0)) else: unstored_messages.insert(0, messages.pop()) - encoded_data = self._encode( - messages + [self.not_finished], encode_empty=unstored_messages - ) + encoded_data = self._encode(messages + [self.not_finished], + encode_empty=unstored_messages) self._update_cookie(encoded_data, response) return unstored_messages + def _legacy_hash(self, value): + """ + # RemovedInDjango40Warning: pre-Django 3.1 hashes will be invalid. + Create an HMAC/SHA1 hash based on the value and the project setting's + SECRET_KEY, modified to make it unique for the present purpose. + """ + # The class wide key salt is not reused here since older Django + # versions had it fixed and making it dynamic would break old hashes if + # self.key_salt is changed. + key_salt = 'django.contrib.messages' + return salted_hmac(key_salt, value).hexdigest() + def _encode(self, messages, encode_empty=False): """ Return an encoded version of the messages list which can be stored as @@ -151,9 +161,7 @@ class CookieStorage(BaseStorage): also contains a hash to ensure that the data was not tampered with. """ if messages or encode_empty: - return self.signer.sign_object( - messages, serializer=MessageSerializer, compress=True - ) + return self.signer.sign_object(messages, serializer=MessageSerializer, compress=True) def _decode(self, data): """ @@ -171,7 +179,10 @@ class CookieStorage(BaseStorage): # except (signing.BadSignature, json.JSONDecodeError): # pass except signing.BadSignature: - decoded = None + # RemovedInDjango40Warning: when the deprecation ends, replace + # with: + # decoded = None. + decoded = self._legacy_decode(data) except (binascii.Error, json.JSONDecodeError): decoded = self.signer.unsign(data) @@ -185,3 +196,12 @@ class CookieStorage(BaseStorage): # with the data. self.used = True return None + + def _legacy_decode(self, data): + # RemovedInDjango40Warning: pre-Django 3.1 hashes will be invalid. + bits = data.split('$', 1) + if len(bits) == 2: + hash_, value = bits + if constant_time_compare(hash_, self._legacy_hash(value)): + return value + return None diff --git a/venv/Lib/site-packages/django/contrib/messages/storage/fallback.py b/venv/Lib/site-packages/django/contrib/messages/storage/fallback.py index 44e26fa..39df6f3 100644 --- a/venv/Lib/site-packages/django/contrib/messages/storage/fallback.py +++ b/venv/Lib/site-packages/django/contrib/messages/storage/fallback.py @@ -8,14 +8,12 @@ class FallbackStorage(BaseStorage): Try to store all messages in the first backend. Store any unstored messages in each subsequent backend. """ - storage_classes = (CookieStorage, SessionStorage) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.storages = [ - storage_class(*args, **kwargs) for storage_class in self.storage_classes - ] + self.storages = [storage_class(*args, **kwargs) + for storage_class in self.storage_classes] self._used_storages = set() def _get(self, *args, **kwargs): diff --git a/venv/Lib/site-packages/django/contrib/messages/storage/session.py b/venv/Lib/site-packages/django/contrib/messages/storage/session.py index 3271c92..b5291f1 100644 --- a/venv/Lib/site-packages/django/contrib/messages/storage/session.py +++ b/venv/Lib/site-packages/django/contrib/messages/storage/session.py @@ -1,24 +1,22 @@ import json from django.contrib.messages.storage.base import BaseStorage -from django.contrib.messages.storage.cookie import MessageDecoder, MessageEncoder -from django.core.exceptions import ImproperlyConfigured +from django.contrib.messages.storage.cookie import ( + MessageDecoder, MessageEncoder, +) class SessionStorage(BaseStorage): """ Store messages in the session (that is, django.contrib.sessions). """ - - session_key = "_messages" + session_key = '_messages' def __init__(self, request, *args, **kwargs): - if not hasattr(request, "session"): - raise ImproperlyConfigured( - "The session-based temporary message storage requires session " - "middleware to be installed, and come before the message " - "middleware in the MIDDLEWARE list." - ) + assert hasattr(request, 'session'), "The session-based temporary "\ + "message storage requires session middleware to be installed, "\ + "and come before the message middleware in the "\ + "MIDDLEWARE list." super().__init__(request, *args, **kwargs) def _get(self, *args, **kwargs): @@ -27,10 +25,7 @@ class SessionStorage(BaseStorage): always stores everything it is given, so return True for the all_retrieved flag. """ - return ( - self.deserialize_messages(self.request.session.get(self.session_key)), - True, - ) + return self.deserialize_messages(self.request.session.get(self.session_key)), True def _store(self, messages, response, *args, **kwargs): """ diff --git a/venv/Lib/site-packages/django/contrib/messages/utils.py b/venv/Lib/site-packages/django/contrib/messages/utils.py index 0dd8873..9013044 100644 --- a/venv/Lib/site-packages/django/contrib/messages/utils.py +++ b/venv/Lib/site-packages/django/contrib/messages/utils.py @@ -8,5 +8,5 @@ def get_level_tags(): """ return { **constants.DEFAULT_TAGS, - **getattr(settings, "MESSAGE_TAGS", {}), + **getattr(settings, 'MESSAGE_TAGS', {}), } diff --git a/venv/Lib/site-packages/django/contrib/messages/views.py b/venv/Lib/site-packages/django/contrib/messages/views.py index 38dee9b..eaa1bee 100644 --- a/venv/Lib/site-packages/django/contrib/messages/views.py +++ b/venv/Lib/site-packages/django/contrib/messages/views.py @@ -5,8 +5,7 @@ class SuccessMessageMixin: """ Add a success message on successful form submission. """ - - success_message = "" + success_message = '' def form_valid(self, form): response = super().form_valid(form) diff --git a/venv/Lib/site-packages/django/contrib/postgres/aggregates/general.py b/venv/Lib/site-packages/django/contrib/postgres/aggregates/general.py index 026ea18..27cfe31 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/aggregates/general.py +++ b/venv/Lib/site-packages/django/contrib/postgres/aggregates/general.py @@ -1,109 +1,68 @@ -import warnings - from django.contrib.postgres.fields import ArrayField from django.db.models import Aggregate, BooleanField, JSONField, Value -from django.utils.deprecation import RemovedInDjango50Warning from .mixins import OrderableAggMixin __all__ = [ - "ArrayAgg", - "BitAnd", - "BitOr", - "BoolAnd", - "BoolOr", - "JSONBAgg", - "StringAgg", + 'ArrayAgg', 'BitAnd', 'BitOr', 'BoolAnd', 'BoolOr', 'JSONBAgg', 'StringAgg', ] -# RemovedInDjango50Warning -NOT_PROVIDED = object() - - -class DeprecatedConvertValueMixin: - def __init__(self, *expressions, default=NOT_PROVIDED, **extra): - if default is NOT_PROVIDED: - default = None - self._default_provided = False - else: - self._default_provided = True - super().__init__(*expressions, default=default, **extra) - - def convert_value(self, value, expression, connection): - if value is None and not self._default_provided: - warnings.warn(self.deprecation_msg, category=RemovedInDjango50Warning) - return self.deprecation_value - return value - - -class ArrayAgg(DeprecatedConvertValueMixin, OrderableAggMixin, Aggregate): - function = "ARRAY_AGG" - template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)" +class ArrayAgg(OrderableAggMixin, Aggregate): + function = 'ARRAY_AGG' + template = '%(function)s(%(distinct)s%(expressions)s %(ordering)s)' allow_distinct = True - # RemovedInDjango50Warning - deprecation_value = property(lambda self: []) - deprecation_msg = ( - "In Django 5.0, ArrayAgg() will return None instead of an empty list " - "if there are no rows. Pass default=None to opt into the new behavior " - "and silence this warning or default=Value([]) to keep the previous " - "behavior." - ) - @property def output_field(self): return ArrayField(self.source_expressions[0].output_field) + def convert_value(self, value, expression, connection): + if not value: + return [] + return value + class BitAnd(Aggregate): - function = "BIT_AND" + function = 'BIT_AND' class BitOr(Aggregate): - function = "BIT_OR" + function = 'BIT_OR' class BoolAnd(Aggregate): - function = "BOOL_AND" + function = 'BOOL_AND' output_field = BooleanField() class BoolOr(Aggregate): - function = "BOOL_OR" + function = 'BOOL_OR' output_field = BooleanField() -class JSONBAgg(DeprecatedConvertValueMixin, OrderableAggMixin, Aggregate): - function = "JSONB_AGG" - template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)" +class JSONBAgg(OrderableAggMixin, Aggregate): + function = 'JSONB_AGG' + template = '%(function)s(%(distinct)s%(expressions)s %(ordering)s)' allow_distinct = True output_field = JSONField() - # RemovedInDjango50Warning - deprecation_value = "[]" - deprecation_msg = ( - "In Django 5.0, JSONBAgg() will return None instead of an empty list " - "if there are no rows. Pass default=None to opt into the new behavior " - "and silence this warning or default=Value('[]') to keep the previous " - "behavior." - ) + def convert_value(self, value, expression, connection): + if not value: + return '[]' + return value -class StringAgg(DeprecatedConvertValueMixin, OrderableAggMixin, Aggregate): - function = "STRING_AGG" - template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)" +class StringAgg(OrderableAggMixin, Aggregate): + function = 'STRING_AGG' + template = '%(function)s(%(distinct)s%(expressions)s %(ordering)s)' allow_distinct = True - # RemovedInDjango50Warning - deprecation_value = "" - deprecation_msg = ( - "In Django 5.0, StringAgg() will return None instead of an empty " - "string if there are no rows. Pass default=None to opt into the new " - "behavior and silence this warning or default=Value('') to keep the " - "previous behavior." - ) - def __init__(self, expression, delimiter, **extra): delimiter_expr = Value(str(delimiter)) super().__init__(expression, delimiter_expr, **extra) + + def convert_value(self, value, expression, connection): + if not value: + return '' + return value diff --git a/venv/Lib/site-packages/django/contrib/postgres/aggregates/mixins.py b/venv/Lib/site-packages/django/contrib/postgres/aggregates/mixins.py index ca0f7c9..f2ba5c4 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/aggregates/mixins.py +++ b/venv/Lib/site-packages/django/contrib/postgres/aggregates/mixins.py @@ -2,26 +2,21 @@ from django.db.models import F, OrderBy class OrderableAggMixin: + def __init__(self, *expressions, ordering=(), **extra): if not isinstance(ordering, (list, tuple)): ordering = [ordering] ordering = ordering or [] # Transform minus sign prefixed strings into an OrderBy() expression. ordering = ( - ( - OrderBy(F(o[1:]), descending=True) - if isinstance(o, str) and o[0] == "-" - else o - ) + (OrderBy(F(o[1:]), descending=True) if isinstance(o, str) and o[0] == '-' else o) for o in ordering ) super().__init__(*expressions, **extra) self.ordering = self._parse_expressions(*ordering) def resolve_expression(self, *args, **kwargs): - self.ordering = [ - expr.resolve_expression(*args, **kwargs) for expr in self.ordering - ] + self.ordering = [expr.resolve_expression(*args, **kwargs) for expr in self.ordering] return super().resolve_expression(*args, **kwargs) def as_sql(self, compiler, connection): @@ -32,21 +27,17 @@ class OrderableAggMixin: expr_sql, expr_params = compiler.compile(expr) ordering_expr_sql.append(expr_sql) ordering_params.extend(expr_params) - sql, sql_params = super().as_sql( - compiler, - connection, - ordering=("ORDER BY " + ", ".join(ordering_expr_sql)), - ) + sql, sql_params = super().as_sql(compiler, connection, ordering=( + 'ORDER BY ' + ', '.join(ordering_expr_sql) + )) return sql, sql_params + ordering_params - return super().as_sql(compiler, connection, ordering="") + return super().as_sql(compiler, connection, ordering='') def set_source_expressions(self, exprs): # Extract the ordering expressions because ORDER BY clause is handled # in a custom way. - self.ordering = exprs[self._get_ordering_expressions_index() :] - return super().set_source_expressions( - exprs[: self._get_ordering_expressions_index()] - ) + self.ordering = exprs[self._get_ordering_expressions_index():] + return super().set_source_expressions(exprs[:self._get_ordering_expressions_index()]) def get_source_expressions(self): return super().get_source_expressions() + self.ordering diff --git a/venv/Lib/site-packages/django/contrib/postgres/aggregates/statistics.py b/venv/Lib/site-packages/django/contrib/postgres/aggregates/statistics.py index 3dc442b..c3254e3 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/aggregates/statistics.py +++ b/venv/Lib/site-packages/django/contrib/postgres/aggregates/statistics.py @@ -1,75 +1,65 @@ from django.db.models import Aggregate, FloatField, IntegerField __all__ = [ - "CovarPop", - "Corr", - "RegrAvgX", - "RegrAvgY", - "RegrCount", - "RegrIntercept", - "RegrR2", - "RegrSlope", - "RegrSXX", - "RegrSXY", - "RegrSYY", - "StatAggregate", + 'CovarPop', 'Corr', 'RegrAvgX', 'RegrAvgY', 'RegrCount', 'RegrIntercept', + 'RegrR2', 'RegrSlope', 'RegrSXX', 'RegrSXY', 'RegrSYY', 'StatAggregate', ] class StatAggregate(Aggregate): output_field = FloatField() - def __init__(self, y, x, output_field=None, filter=None, default=None): + def __init__(self, y, x, output_field=None, filter=None): if not x or not y: - raise ValueError("Both y and x must be provided.") - super().__init__( - y, x, output_field=output_field, filter=filter, default=default - ) + raise ValueError('Both y and x must be provided.') + super().__init__(y, x, output_field=output_field, filter=filter) class Corr(StatAggregate): - function = "CORR" + function = 'CORR' class CovarPop(StatAggregate): - def __init__(self, y, x, sample=False, filter=None, default=None): - self.function = "COVAR_SAMP" if sample else "COVAR_POP" - super().__init__(y, x, filter=filter, default=default) + def __init__(self, y, x, sample=False, filter=None): + self.function = 'COVAR_SAMP' if sample else 'COVAR_POP' + super().__init__(y, x, filter=filter) class RegrAvgX(StatAggregate): - function = "REGR_AVGX" + function = 'REGR_AVGX' class RegrAvgY(StatAggregate): - function = "REGR_AVGY" + function = 'REGR_AVGY' class RegrCount(StatAggregate): - function = "REGR_COUNT" + function = 'REGR_COUNT' output_field = IntegerField() - empty_result_set_value = 0 + + def convert_value(self, value, expression, connection): + return 0 if value is None else value class RegrIntercept(StatAggregate): - function = "REGR_INTERCEPT" + function = 'REGR_INTERCEPT' class RegrR2(StatAggregate): - function = "REGR_R2" + function = 'REGR_R2' class RegrSlope(StatAggregate): - function = "REGR_SLOPE" + function = 'REGR_SLOPE' class RegrSXX(StatAggregate): - function = "REGR_SXX" + function = 'REGR_SXX' class RegrSXY(StatAggregate): - function = "REGR_SXY" + function = 'REGR_SXY' class RegrSYY(StatAggregate): - function = "REGR_SYY" + function = 'REGR_SYY' diff --git a/venv/Lib/site-packages/django/contrib/postgres/apps.py b/venv/Lib/site-packages/django/contrib/postgres/apps.py index 3371b0b..781c872 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/apps.py +++ b/venv/Lib/site-packages/django/contrib/postgres/apps.py @@ -1,4 +1,6 @@ -from psycopg2.extras import DateRange, DateTimeRange, DateTimeTZRange, NumericRange +from psycopg2.extras import ( + DateRange, DateTimeRange, DateTimeTZRange, NumericRange, +) from django.apps import AppConfig from django.db import connections @@ -11,7 +13,7 @@ from django.test.signals import setting_changed from django.utils.translation import gettext_lazy as _ from .indexes import OpClass -from .lookups import SearchLookup, TrigramSimilar, TrigramWordSimilar, Unaccent +from .lookups import SearchLookup, TrigramSimilar, Unaccent from .serializers import RangeSerializer from .signals import register_type_handlers @@ -23,11 +25,7 @@ def uninstall_if_needed(setting, value, enter, **kwargs): Undo the effects of PostgresConfig.ready() when django.contrib.postgres is "uninstalled" by override_settings(). """ - if ( - not enter - and setting == "INSTALLED_APPS" - and "django.contrib.postgres" not in set(value) - ): + if not enter and setting == 'INSTALLED_APPS' and 'django.contrib.postgres' not in set(value): connection_created.disconnect(register_type_handlers) CharField._unregister_lookup(Unaccent) TextField._unregister_lookup(Unaccent) @@ -35,8 +33,6 @@ def uninstall_if_needed(setting, value, enter, **kwargs): TextField._unregister_lookup(SearchLookup) CharField._unregister_lookup(TrigramSimilar) TextField._unregister_lookup(TrigramSimilar) - CharField._unregister_lookup(TrigramWordSimilar) - TextField._unregister_lookup(TrigramWordSimilar) # Disconnect this receiver until the next time this app is installed # and ready() connects it again to prevent unnecessary processing on # each setting change. @@ -45,23 +41,21 @@ def uninstall_if_needed(setting, value, enter, **kwargs): class PostgresConfig(AppConfig): - name = "django.contrib.postgres" - verbose_name = _("PostgreSQL extensions") + name = 'django.contrib.postgres' + verbose_name = _('PostgreSQL extensions') def ready(self): setting_changed.connect(uninstall_if_needed) # Connections may already exist before we are called. for conn in connections.all(): - if conn.vendor == "postgresql": - conn.introspection.data_types_reverse.update( - { - 3904: "django.contrib.postgres.fields.IntegerRangeField", - 3906: "django.contrib.postgres.fields.DecimalRangeField", - 3910: "django.contrib.postgres.fields.DateTimeRangeField", - 3912: "django.contrib.postgres.fields.DateRangeField", - 3926: "django.contrib.postgres.fields.BigIntegerRangeField", - } - ) + if conn.vendor == 'postgresql': + conn.introspection.data_types_reverse.update({ + 3904: 'django.contrib.postgres.fields.IntegerRangeField', + 3906: 'django.contrib.postgres.fields.DecimalRangeField', + 3910: 'django.contrib.postgres.fields.DateTimeRangeField', + 3912: 'django.contrib.postgres.fields.DateRangeField', + 3926: 'django.contrib.postgres.fields.BigIntegerRangeField', + }) if conn.connection is not None: register_type_handlers(conn) connection_created.connect(register_type_handlers) @@ -71,7 +65,5 @@ class PostgresConfig(AppConfig): TextField.register_lookup(SearchLookup) CharField.register_lookup(TrigramSimilar) TextField.register_lookup(TrigramSimilar) - CharField.register_lookup(TrigramWordSimilar) - TextField.register_lookup(TrigramWordSimilar) MigrationWriter.register_serializer(RANGE_TYPES, RangeSerializer) IndexExpression.register_wrappers(OrderBy, OpClass, Collate) diff --git a/venv/Lib/site-packages/django/contrib/postgres/constraints.py b/venv/Lib/site-packages/django/contrib/postgres/constraints.py index c136de4..a844b89 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/constraints.py +++ b/venv/Lib/site-packages/django/contrib/postgres/constraints.py @@ -2,66 +2,64 @@ from django.db import NotSupportedError from django.db.backends.ddl_references import Statement, Table from django.db.models import Deferrable, F, Q from django.db.models.constraints import BaseConstraint -from django.db.models.expressions import Col from django.db.models.sql import Query -__all__ = ["ExclusionConstraint"] +__all__ = ['ExclusionConstraint'] class ExclusionConstraint(BaseConstraint): - template = ( - "CONSTRAINT %(name)s EXCLUDE USING %(index_type)s " - "(%(expressions)s)%(include)s%(where)s%(deferrable)s" - ) + template = 'CONSTRAINT %(name)s EXCLUDE USING %(index_type)s (%(expressions)s)%(include)s%(where)s%(deferrable)s' def __init__( - self, - *, - name, - expressions, - index_type=None, - condition=None, - deferrable=None, - include=None, - opclasses=(), + self, *, name, expressions, index_type=None, condition=None, + deferrable=None, include=None, opclasses=(), ): - if index_type and index_type.lower() not in {"gist", "spgist"}: + if index_type and index_type.lower() not in {'gist', 'spgist'}: raise ValueError( - "Exclusion constraints only support GiST or SP-GiST indexes." + 'Exclusion constraints only support GiST or SP-GiST indexes.' ) if not expressions: raise ValueError( - "At least one expression is required to define an exclusion " - "constraint." + 'At least one expression is required to define an exclusion ' + 'constraint.' ) if not all( - isinstance(expr, (list, tuple)) and len(expr) == 2 for expr in expressions + isinstance(expr, (list, tuple)) and len(expr) == 2 + for expr in expressions ): - raise ValueError("The expressions must be a list of 2-tuples.") + raise ValueError('The expressions must be a list of 2-tuples.') if not isinstance(condition, (type(None), Q)): - raise ValueError("ExclusionConstraint.condition must be a Q instance.") + raise ValueError( + 'ExclusionConstraint.condition must be a Q instance.' + ) if condition and deferrable: - raise ValueError("ExclusionConstraint with conditions cannot be deferred.") + raise ValueError( + 'ExclusionConstraint with conditions cannot be deferred.' + ) if not isinstance(deferrable, (type(None), Deferrable)): raise ValueError( - "ExclusionConstraint.deferrable must be a Deferrable instance." + 'ExclusionConstraint.deferrable must be a Deferrable instance.' ) if not isinstance(include, (type(None), list, tuple)): - raise ValueError("ExclusionConstraint.include must be a list or tuple.") - if include and index_type and index_type.lower() != "gist": raise ValueError( - "Covering exclusion constraints only support GiST indexes." + 'ExclusionConstraint.include must be a list or tuple.' + ) + if include and index_type and index_type.lower() != 'gist': + raise ValueError( + 'Covering exclusion constraints only support GiST indexes.' ) if not isinstance(opclasses, (list, tuple)): - raise ValueError("ExclusionConstraint.opclasses must be a list or tuple.") + raise ValueError( + 'ExclusionConstraint.opclasses must be a list or tuple.' + ) if opclasses and len(expressions) != len(opclasses): raise ValueError( - "ExclusionConstraint.expressions and " - "ExclusionConstraint.opclasses must have the same number of " - "elements." + 'ExclusionConstraint.expressions and ' + 'ExclusionConstraint.opclasses must have the same number of ' + 'elements.' ) self.expressions = expressions - self.index_type = index_type or "GIST" + self.index_type = index_type or 'GIST' self.condition = condition self.deferrable = deferrable self.include = tuple(include) if include else () @@ -75,16 +73,14 @@ class ExclusionConstraint(BaseConstraint): expression = F(expression) expression = expression.resolve_expression(query=query) sql, params = compiler.compile(expression) - if not isinstance(expression, Col): - sql = f"({sql})" try: opclass = self.opclasses[idx] if opclass: - sql = "%s %s" % (sql, opclass) + sql = '%s %s' % (sql, opclass) except IndexError: pass sql = sql % tuple(schema_editor.quote_value(p) for p in params) - expressions.append("%s WITH %s" % (sql, operator)) + expressions.append('%s WITH %s' % (sql, operator)) return expressions def _get_condition_sql(self, compiler, schema_editor, query): @@ -99,22 +95,20 @@ class ExclusionConstraint(BaseConstraint): compiler = query.get_compiler(connection=schema_editor.connection) expressions = self._get_expression_sql(compiler, schema_editor, query) condition = self._get_condition_sql(compiler, schema_editor, query) - include = [ - model._meta.get_field(field_name).column for field_name in self.include - ] + include = [model._meta.get_field(field_name).column for field_name in self.include] return self.template % { - "name": schema_editor.quote_name(self.name), - "index_type": self.index_type, - "expressions": ", ".join(expressions), - "include": schema_editor._index_include_sql(model, include), - "where": " WHERE (%s)" % condition if condition else "", - "deferrable": schema_editor._deferrable_constraint_sql(self.deferrable), + 'name': schema_editor.quote_name(self.name), + 'index_type': self.index_type, + 'expressions': ', '.join(expressions), + 'include': schema_editor._index_include_sql(model, include), + 'where': ' WHERE (%s)' % condition if condition else '', + 'deferrable': schema_editor._deferrable_constraint_sql(self.deferrable), } def create_sql(self, model, schema_editor): self.check_supported(schema_editor) return Statement( - "ALTER TABLE %(table)s ADD %(constraint)s", + 'ALTER TABLE %(table)s ADD %(constraint)s', table=Table(model._meta.db_table, schema_editor.quote_name), constraint=self.constraint_sql(model, schema_editor), ) @@ -127,50 +121,46 @@ class ExclusionConstraint(BaseConstraint): ) def check_supported(self, schema_editor): - if ( - self.include - and not schema_editor.connection.features.supports_covering_gist_indexes - ): + if self.include and not schema_editor.connection.features.supports_covering_gist_indexes: raise NotSupportedError( - "Covering exclusion constraints requires PostgreSQL 12+." + 'Covering exclusion constraints requires PostgreSQL 12+.' ) def deconstruct(self): path, args, kwargs = super().deconstruct() - kwargs["expressions"] = self.expressions + kwargs['expressions'] = self.expressions if self.condition is not None: - kwargs["condition"] = self.condition - if self.index_type.lower() != "gist": - kwargs["index_type"] = self.index_type + kwargs['condition'] = self.condition + if self.index_type.lower() != 'gist': + kwargs['index_type'] = self.index_type if self.deferrable: - kwargs["deferrable"] = self.deferrable + kwargs['deferrable'] = self.deferrable if self.include: - kwargs["include"] = self.include + kwargs['include'] = self.include if self.opclasses: - kwargs["opclasses"] = self.opclasses + kwargs['opclasses'] = self.opclasses return path, args, kwargs def __eq__(self, other): if isinstance(other, self.__class__): return ( - self.name == other.name - and self.index_type == other.index_type - and self.expressions == other.expressions - and self.condition == other.condition - and self.deferrable == other.deferrable - and self.include == other.include - and self.opclasses == other.opclasses + self.name == other.name and + self.index_type == other.index_type and + self.expressions == other.expressions and + self.condition == other.condition and + self.deferrable == other.deferrable and + self.include == other.include and + self.opclasses == other.opclasses ) return super().__eq__(other) def __repr__(self): - return "<%s: index_type=%s expressions=%s name=%s%s%s%s%s>" % ( + return '<%s: index_type=%s, expressions=%s%s%s%s%s>' % ( self.__class__.__qualname__, - repr(self.index_type), - repr(self.expressions), - repr(self.name), - "" if self.condition is None else " condition=%s" % self.condition, - "" if self.deferrable is None else " deferrable=%r" % self.deferrable, - "" if not self.include else " include=%s" % repr(self.include), - "" if not self.opclasses else " opclasses=%s" % repr(self.opclasses), + self.index_type, + self.expressions, + '' if self.condition is None else ', condition=%s' % self.condition, + '' if self.deferrable is None else ', deferrable=%s' % self.deferrable, + '' if not self.include else ', include=%s' % repr(self.include), + '' if not self.opclasses else ', opclasses=%s' % repr(self.opclasses), ) diff --git a/venv/Lib/site-packages/django/contrib/postgres/expressions.py b/venv/Lib/site-packages/django/contrib/postgres/expressions.py deleted file mode 100644 index 469f4e9..0000000 --- a/venv/Lib/site-packages/django/contrib/postgres/expressions.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.contrib.postgres.fields import ArrayField -from django.db.models import Subquery -from django.utils.functional import cached_property - - -class ArraySubquery(Subquery): - template = "ARRAY(%(subquery)s)" - - def __init__(self, queryset, **kwargs): - super().__init__(queryset, **kwargs) - - @cached_property - def output_field(self): - return ArrayField(self.query.output_field) diff --git a/venv/Lib/site-packages/django/contrib/postgres/fields/array.py b/venv/Lib/site-packages/django/contrib/postgres/fields/array.py index 7269198..9c1bb96 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/fields/array.py +++ b/venv/Lib/site-packages/django/contrib/postgres/fields/array.py @@ -12,43 +12,38 @@ from django.utils.translation import gettext_lazy as _ from ..utils import prefix_validation_error from .utils import AttributeSetter -__all__ = ["ArrayField"] +__all__ = ['ArrayField'] class ArrayField(CheckFieldDefaultMixin, Field): empty_strings_allowed = False default_error_messages = { - "item_invalid": _("Item %(nth)s in the array did not validate:"), - "nested_array_mismatch": _("Nested arrays must have the same length."), + 'item_invalid': _('Item %(nth)s in the array did not validate:'), + 'nested_array_mismatch': _('Nested arrays must have the same length.'), } - _default_hint = ("list", "[]") + _default_hint = ('list', '[]') def __init__(self, base_field, size=None, **kwargs): self.base_field = base_field self.size = size if self.size: - self.default_validators = [ - *self.default_validators, - ArrayMaxLengthValidator(self.size), - ] + self.default_validators = [*self.default_validators, ArrayMaxLengthValidator(self.size)] # For performance, only add a from_db_value() method if the base field # implements it. - if hasattr(self.base_field, "from_db_value"): + if hasattr(self.base_field, 'from_db_value'): self.from_db_value = self._from_db_value super().__init__(**kwargs) @property def model(self): try: - return self.__dict__["model"] + return self.__dict__['model'] except KeyError: - raise AttributeError( - "'%s' object has no attribute 'model'" % self.__class__.__name__ - ) + raise AttributeError("'%s' object has no attribute 'model'" % self.__class__.__name__) @model.setter def model(self, model): - self.__dict__["model"] = model + self.__dict__['model'] = model self.base_field.model = model @classmethod @@ -60,23 +55,21 @@ class ArrayField(CheckFieldDefaultMixin, Field): if self.base_field.remote_field: errors.append( checks.Error( - "Base field for array cannot be a related field.", + 'Base field for array cannot be a related field.', obj=self, - id="postgres.E002", + id='postgres.E002' ) ) else: # Remove the field name checks as they are not needed here. base_errors = self.base_field.check() if base_errors: - messages = "\n ".join( - "%s (%s)" % (error.msg, error.id) for error in base_errors - ) + messages = '\n '.join('%s (%s)' % (error.msg, error.id) for error in base_errors) errors.append( checks.Error( - "Base field for array has errors:\n %s" % messages, + 'Base field for array has errors:\n %s' % messages, obj=self, - id="postgres.E001", + id='postgres.E001' ) ) return errors @@ -87,37 +80,32 @@ class ArrayField(CheckFieldDefaultMixin, Field): @property def description(self): - return "Array of %s" % self.base_field.description + return 'Array of %s' % self.base_field.description def db_type(self, connection): - size = self.size or "" - return "%s[%s]" % (self.base_field.db_type(connection), size) + size = self.size or '' + return '%s[%s]' % (self.base_field.db_type(connection), size) def cast_db_type(self, connection): - size = self.size or "" - return "%s[%s]" % (self.base_field.cast_db_type(connection), size) + size = self.size or '' + return '%s[%s]' % (self.base_field.cast_db_type(connection), size) def get_placeholder(self, value, compiler, connection): - return "%s::{}".format(self.db_type(connection)) + return '%s::{}'.format(self.db_type(connection)) def get_db_prep_value(self, value, connection, prepared=False): if isinstance(value, (list, tuple)): - return [ - self.base_field.get_db_prep_value(i, connection, prepared=False) - for i in value - ] + return [self.base_field.get_db_prep_value(i, connection, prepared=False) for i in value] return value def deconstruct(self): name, path, args, kwargs = super().deconstruct() - if path == "django.contrib.postgres.fields.array.ArrayField": - path = "django.contrib.postgres.fields.ArrayField" - kwargs.update( - { - "base_field": self.base_field.clone(), - "size": self.size, - } - ) + if path == 'django.contrib.postgres.fields.array.ArrayField': + path = 'django.contrib.postgres.fields.ArrayField' + kwargs.update({ + 'base_field': self.base_field.clone(), + 'size': self.size, + }) return name, path, args, kwargs def to_python(self, value): @@ -152,7 +140,7 @@ class ArrayField(CheckFieldDefaultMixin, Field): transform = super().get_transform(name) if transform: return transform - if "_" not in name: + if '_' not in name: try: index = int(name) except ValueError: @@ -161,7 +149,7 @@ class ArrayField(CheckFieldDefaultMixin, Field): index += 1 # postgres uses 1-indexing return IndexTransformFactory(index, self.base_field) try: - start, end = name.split("_") + start, end = name.split('_') start = int(start) + 1 end = int(end) # don't add one here because postgres slices are weird except ValueError: @@ -177,15 +165,15 @@ class ArrayField(CheckFieldDefaultMixin, Field): except exceptions.ValidationError as error: raise prefix_validation_error( error, - prefix=self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, + prefix=self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, ) if isinstance(self.base_field, ArrayField): if len({len(i) for i in value}) > 1: raise exceptions.ValidationError( - self.error_messages["nested_array_mismatch"], - code="nested_array_mismatch", + self.error_messages['nested_array_mismatch'], + code='nested_array_mismatch', ) def run_validators(self, value): @@ -196,20 +184,18 @@ class ArrayField(CheckFieldDefaultMixin, Field): except exceptions.ValidationError as error: raise prefix_validation_error( error, - prefix=self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, + prefix=self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, ) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": SimpleArrayField, - "base_field": self.base_field.formfield(), - "max_length": self.size, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': SimpleArrayField, + 'base_field': self.base_field.formfield(), + 'max_length': self.size, + **kwargs, + }) class ArrayRHSMixin: @@ -217,21 +203,21 @@ class ArrayRHSMixin: if isinstance(rhs, (tuple, list)): expressions = [] for value in rhs: - if not hasattr(value, "resolve_expression"): + if not hasattr(value, 'resolve_expression'): field = lhs.output_field value = Value(field.base_field.get_prep_value(value)) expressions.append(value) rhs = Func( *expressions, - function="ARRAY", - template="%(function)s[%(expressions)s]", + function='ARRAY', + template='%(function)s[%(expressions)s]', ) super().__init__(lhs, rhs) def process_rhs(self, compiler, connection): rhs, rhs_params = super().process_rhs(compiler, connection) cast_type = self.lhs.output_field.cast_db_type(connection) - return "%s::%s" % (rhs, cast_type), rhs_params + return '%s::%s' % (rhs, cast_type), rhs_params @ArrayField.register_lookup @@ -256,29 +242,29 @@ class ArrayOverlap(ArrayRHSMixin, lookups.Overlap): @ArrayField.register_lookup class ArrayLenTransform(Transform): - lookup_name = "len" + lookup_name = 'len' output_field = IntegerField() def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) # Distinguish NULL and empty arrays return ( - "CASE WHEN %(lhs)s IS NULL THEN NULL ELSE " - "coalesce(array_length(%(lhs)s, 1), 0) END" - ) % {"lhs": lhs}, params + 'CASE WHEN %(lhs)s IS NULL THEN NULL ELSE ' + 'coalesce(array_length(%(lhs)s, 1), 0) END' + ) % {'lhs': lhs}, params @ArrayField.register_lookup class ArrayInLookup(In): def get_prep_lookup(self): values = super().get_prep_lookup() - if hasattr(values, "resolve_expression"): + if hasattr(values, 'resolve_expression'): return values # In.process_rhs() expects values to be hashable, so convert lists # to tuples. prepared_values = [] for value in values: - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): prepared_values.append(value) else: prepared_values.append(tuple(value)) @@ -286,6 +272,7 @@ class ArrayInLookup(In): class IndexTransform(Transform): + def __init__(self, index, base_field, *args, **kwargs): super().__init__(*args, **kwargs) self.index = index @@ -293,7 +280,7 @@ class IndexTransform(Transform): def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) - return "%s[%%s]" % lhs, params + [self.index] + return '%s[%%s]' % lhs, params + [self.index] @property def output_field(self): @@ -301,6 +288,7 @@ class IndexTransform(Transform): class IndexTransformFactory: + def __init__(self, index, base_field): self.index = index self.base_field = base_field @@ -310,6 +298,7 @@ class IndexTransformFactory: class SliceTransform(Transform): + def __init__(self, start, end, *args, **kwargs): super().__init__(*args, **kwargs) self.start = start @@ -317,10 +306,11 @@ class SliceTransform(Transform): def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) - return "%s[%%s:%%s]" % lhs, params + [self.start, self.end] + return '%s[%%s:%%s]' % lhs, params + [self.start, self.end] class SliceTransformFactory: + def __init__(self, start, end): self.start = start self.end = end diff --git a/venv/Lib/site-packages/django/contrib/postgres/fields/citext.py b/venv/Lib/site-packages/django/contrib/postgres/fields/citext.py index 2b94361..46f6d3d 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/fields/citext.py +++ b/venv/Lib/site-packages/django/contrib/postgres/fields/citext.py @@ -1,14 +1,15 @@ from django.db.models import CharField, EmailField, TextField -__all__ = ["CICharField", "CIEmailField", "CIText", "CITextField"] +__all__ = ['CICharField', 'CIEmailField', 'CIText', 'CITextField'] class CIText: + def get_internal_type(self): - return "CI" + super().get_internal_type() + return 'CI' + super().get_internal_type() def db_type(self, connection): - return "citext" + return 'citext' class CICharField(CIText, CharField): diff --git a/venv/Lib/site-packages/django/contrib/postgres/fields/hstore.py b/venv/Lib/site-packages/django/contrib/postgres/fields/hstore.py index cfc156a..2ec5766 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/fields/hstore.py +++ b/venv/Lib/site-packages/django/contrib/postgres/fields/hstore.py @@ -7,19 +7,19 @@ from django.db.models import Field, TextField, Transform from django.db.models.fields.mixins import CheckFieldDefaultMixin from django.utils.translation import gettext_lazy as _ -__all__ = ["HStoreField"] +__all__ = ['HStoreField'] class HStoreField(CheckFieldDefaultMixin, Field): empty_strings_allowed = False - description = _("Map of strings to strings/nulls") + description = _('Map of strings to strings/nulls') default_error_messages = { - "not_a_string": _("The value of “%(key)s” is not a string or null."), + 'not_a_string': _('The value of “%(key)s” is not a string or null.'), } - _default_hint = ("dict", "{}") + _default_hint = ('dict', '{}') def db_type(self, connection): - return "hstore" + return 'hstore' def get_transform(self, name): transform = super().get_transform(name) @@ -32,9 +32,9 @@ class HStoreField(CheckFieldDefaultMixin, Field): for key, val in value.items(): if not isinstance(val, str) and val is not None: raise exceptions.ValidationError( - self.error_messages["not_a_string"], - code="not_a_string", - params={"key": key}, + self.error_messages['not_a_string'], + code='not_a_string', + params={'key': key}, ) def to_python(self, value): @@ -46,12 +46,10 @@ class HStoreField(CheckFieldDefaultMixin, Field): return json.dumps(self.value_from_object(obj)) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.HStoreField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.HStoreField, + **kwargs, + }) def get_prep_value(self, value): value = super().get_prep_value(value) @@ -87,10 +85,11 @@ class KeyTransform(Transform): def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) - return "(%s -> %%s)" % lhs, tuple(params) + (self.key_name,) + return '(%s -> %%s)' % lhs, tuple(params) + (self.key_name,) class KeyTransformFactory: + def __init__(self, key_name): self.key_name = key_name @@ -100,13 +99,13 @@ class KeyTransformFactory: @HStoreField.register_lookup class KeysTransform(Transform): - lookup_name = "keys" - function = "akeys" + lookup_name = 'keys' + function = 'akeys' output_field = ArrayField(TextField()) @HStoreField.register_lookup class ValuesTransform(Transform): - lookup_name = "values" - function = "avals" + lookup_name = 'values' + function = 'avals' output_field = ArrayField(TextField()) diff --git a/venv/Lib/site-packages/django/contrib/postgres/fields/jsonb.py b/venv/Lib/site-packages/django/contrib/postgres/fields/jsonb.py index 760b5d8..7f76b29 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/fields/jsonb.py +++ b/venv/Lib/site-packages/django/contrib/postgres/fields/jsonb.py @@ -1,14 +1,43 @@ -from django.db.models import JSONField as BuiltinJSONField +import warnings -__all__ = ["JSONField"] +from django.db.models import JSONField as BuiltinJSONField +from django.db.models.fields.json import ( + KeyTextTransform as BuiltinKeyTextTransform, + KeyTransform as BuiltinKeyTransform, +) +from django.utils.deprecation import RemovedInDjango40Warning + +__all__ = ['JSONField'] class JSONField(BuiltinJSONField): - system_check_removed_details = { - "msg": ( - "django.contrib.postgres.fields.JSONField is removed except for " - "support in historical migrations." + system_check_deprecated_details = { + 'msg': ( + 'django.contrib.postgres.fields.JSONField is deprecated. Support ' + 'for it (except in historical migrations) will be removed in ' + 'Django 4.0.' ), - "hint": "Use django.db.models.JSONField instead.", - "id": "fields.E904", + 'hint': 'Use django.db.models.JSONField instead.', + 'id': 'fields.W904', } + + +class KeyTransform(BuiltinKeyTransform): + def __init__(self, *args, **kwargs): + warnings.warn( + 'django.contrib.postgres.fields.jsonb.KeyTransform is deprecated ' + 'in favor of django.db.models.fields.json.KeyTransform.', + RemovedInDjango40Warning, stacklevel=2, + ) + super().__init__(*args, **kwargs) + + +class KeyTextTransform(BuiltinKeyTextTransform): + def __init__(self, *args, **kwargs): + warnings.warn( + 'django.contrib.postgres.fields.jsonb.KeyTextTransform is ' + 'deprecated in favor of ' + 'django.db.models.fields.json.KeyTextTransform.', + RemovedInDjango40Warning, stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/django/contrib/postgres/fields/ranges.py b/venv/Lib/site-packages/django/contrib/postgres/fields/ranges.py index db3ca32..8eab2cd 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/fields/ranges.py +++ b/venv/Lib/site-packages/django/contrib/postgres/fields/ranges.py @@ -10,23 +10,17 @@ from django.db.models.lookups import PostgresOperatorLookup from .utils import AttributeSetter __all__ = [ - "RangeField", - "IntegerRangeField", - "BigIntegerRangeField", - "DecimalRangeField", - "DateTimeRangeField", - "DateRangeField", - "RangeBoundary", - "RangeOperators", + 'RangeField', 'IntegerRangeField', 'BigIntegerRangeField', + 'DecimalRangeField', 'DateTimeRangeField', 'DateRangeField', + 'RangeBoundary', 'RangeOperators', ] class RangeBoundary(models.Expression): """A class that represents range boundaries.""" - def __init__(self, inclusive_lower=True, inclusive_upper=False): - self.lower = "[" if inclusive_lower else "(" - self.upper = "]" if inclusive_upper else ")" + self.lower = '[' if inclusive_lower else '(' + self.upper = ']' if inclusive_upper else ')' def as_sql(self, compiler, connection): return "'%s%s'" % (self.lower, self.upper), [] @@ -34,40 +28,37 @@ class RangeBoundary(models.Expression): class RangeOperators: # https://www.postgresql.org/docs/current/functions-range.html#RANGE-OPERATORS-TABLE - EQUAL = "=" - NOT_EQUAL = "<>" - CONTAINS = "@>" - CONTAINED_BY = "<@" - OVERLAPS = "&&" - FULLY_LT = "<<" - FULLY_GT = ">>" - NOT_LT = "&>" - NOT_GT = "&<" - ADJACENT_TO = "-|-" + EQUAL = '=' + NOT_EQUAL = '<>' + CONTAINS = '@>' + CONTAINED_BY = '<@' + OVERLAPS = '&&' + FULLY_LT = '<<' + FULLY_GT = '>>' + NOT_LT = '&>' + NOT_GT = '&<' + ADJACENT_TO = '-|-' class RangeField(models.Field): empty_strings_allowed = False def __init__(self, *args, **kwargs): - # Initializing base_field here ensures that its model matches the model - # for self. - if hasattr(self, "base_field"): + # Initializing base_field here ensures that its model matches the model for self. + if hasattr(self, 'base_field'): self.base_field = self.base_field() super().__init__(*args, **kwargs) @property def model(self): try: - return self.__dict__["model"] + return self.__dict__['model'] except KeyError: - raise AttributeError( - "'%s' object has no attribute 'model'" % self.__class__.__name__ - ) + raise AttributeError("'%s' object has no attribute 'model'" % self.__class__.__name__) @model.setter def model(self, model): - self.__dict__["model"] = model + self.__dict__['model'] = model self.base_field.model = model @classmethod @@ -87,7 +78,7 @@ class RangeField(models.Field): if isinstance(value, str): # Assume we're deserializing vals = json.loads(value) - for end in ("lower", "upper"): + for end in ('lower', 'upper'): if end in vals: vals[end] = self.base_field.to_python(vals[end]) value = self.range_type(**vals) @@ -107,7 +98,7 @@ class RangeField(models.Field): return json.dumps({"empty": True}) base_field = self.base_field result = {"bounds": value._bounds} - for end in ("lower", "upper"): + for end in ('lower', 'upper'): val = getattr(value, end) if val is None: result[end] = None @@ -117,7 +108,7 @@ class RangeField(models.Field): return json.dumps(result) def formfield(self, **kwargs): - kwargs.setdefault("form_class", self.form_field) + kwargs.setdefault('form_class', self.form_field) return super().formfield(**kwargs) @@ -127,7 +118,7 @@ class IntegerRangeField(RangeField): form_field = forms.IntegerRangeField def db_type(self, connection): - return "int4range" + return 'int4range' class BigIntegerRangeField(RangeField): @@ -136,7 +127,7 @@ class BigIntegerRangeField(RangeField): form_field = forms.IntegerRangeField def db_type(self, connection): - return "int8range" + return 'int8range' class DecimalRangeField(RangeField): @@ -145,7 +136,7 @@ class DecimalRangeField(RangeField): form_field = forms.DecimalRangeField def db_type(self, connection): - return "numrange" + return 'numrange' class DateTimeRangeField(RangeField): @@ -154,7 +145,7 @@ class DateTimeRangeField(RangeField): form_field = forms.DateTimeRangeField def db_type(self, connection): - return "tstzrange" + return 'tstzrange' class DateRangeField(RangeField): @@ -163,7 +154,7 @@ class DateRangeField(RangeField): form_field = forms.DateRangeField def db_type(self, connection): - return "daterange" + return 'daterange' RangeField.register_lookup(lookups.DataContains) @@ -176,8 +167,7 @@ class DateTimeRangeContains(PostgresOperatorLookup): Lookup for Date/DateTimeRange containment to cast the rhs to the correct type. """ - - lookup_name = "contains" + lookup_name = 'contains' postgres_operator = RangeOperators.CONTAINS def process_rhs(self, compiler, connection): @@ -190,19 +180,16 @@ class DateTimeRangeContains(PostgresOperatorLookup): def as_postgresql(self, compiler, connection): sql, params = super().as_postgresql(compiler, connection) # Cast the rhs if needed. - cast_sql = "" + cast_sql = '' if ( - isinstance(self.rhs, models.Expression) - and self.rhs._output_field_or_none - and + isinstance(self.rhs, models.Expression) and + self.rhs._output_field_or_none and # Skip cast if rhs has a matching range type. - not isinstance( - self.rhs._output_field_or_none, self.lhs.output_field.__class__ - ) + not isinstance(self.rhs._output_field_or_none, self.lhs.output_field.__class__) ): cast_internal_type = self.lhs.output_field.base_field.get_internal_type() - cast_sql = "::{}".format(connection.data_types.get(cast_internal_type)) - return "%s%s" % (sql, cast_sql), params + cast_sql = '::{}'.format(connection.data_types.get(cast_internal_type)) + return '%s%s' % (sql, cast_sql), params DateRangeField.register_lookup(DateTimeRangeContains) @@ -210,31 +197,31 @@ DateTimeRangeField.register_lookup(DateTimeRangeContains) class RangeContainedBy(PostgresOperatorLookup): - lookup_name = "contained_by" + lookup_name = 'contained_by' type_mapping = { - "smallint": "int4range", - "integer": "int4range", - "bigint": "int8range", - "double precision": "numrange", - "numeric": "numrange", - "date": "daterange", - "timestamp with time zone": "tstzrange", + 'smallint': 'int4range', + 'integer': 'int4range', + 'bigint': 'int8range', + 'double precision': 'numrange', + 'numeric': 'numrange', + 'date': 'daterange', + 'timestamp with time zone': 'tstzrange', } postgres_operator = RangeOperators.CONTAINED_BY def process_rhs(self, compiler, connection): rhs, rhs_params = super().process_rhs(compiler, connection) # Ignore precision for DecimalFields. - db_type = self.lhs.output_field.cast_db_type(connection).split("(")[0] + db_type = self.lhs.output_field.cast_db_type(connection).split('(')[0] cast_type = self.type_mapping[db_type] - return "%s::%s" % (rhs, cast_type), rhs_params + return '%s::%s' % (rhs, cast_type), rhs_params def process_lhs(self, compiler, connection): lhs, lhs_params = super().process_lhs(compiler, connection) if isinstance(self.lhs.output_field, models.FloatField): - lhs = "%s::numeric" % lhs + lhs = '%s::numeric' % lhs elif isinstance(self.lhs.output_field, models.SmallIntegerField): - lhs = "%s::integer" % lhs + lhs = '%s::integer' % lhs return lhs, lhs_params def get_prep_lookup(self): @@ -250,38 +237,38 @@ models.DecimalField.register_lookup(RangeContainedBy) @RangeField.register_lookup class FullyLessThan(PostgresOperatorLookup): - lookup_name = "fully_lt" + lookup_name = 'fully_lt' postgres_operator = RangeOperators.FULLY_LT @RangeField.register_lookup class FullGreaterThan(PostgresOperatorLookup): - lookup_name = "fully_gt" + lookup_name = 'fully_gt' postgres_operator = RangeOperators.FULLY_GT @RangeField.register_lookup class NotLessThan(PostgresOperatorLookup): - lookup_name = "not_lt" + lookup_name = 'not_lt' postgres_operator = RangeOperators.NOT_LT @RangeField.register_lookup class NotGreaterThan(PostgresOperatorLookup): - lookup_name = "not_gt" + lookup_name = 'not_gt' postgres_operator = RangeOperators.NOT_GT @RangeField.register_lookup class AdjacentToLookup(PostgresOperatorLookup): - lookup_name = "adjacent_to" + lookup_name = 'adjacent_to' postgres_operator = RangeOperators.ADJACENT_TO @RangeField.register_lookup class RangeStartsWith(models.Transform): - lookup_name = "startswith" - function = "lower" + lookup_name = 'startswith' + function = 'lower' @property def output_field(self): @@ -290,8 +277,8 @@ class RangeStartsWith(models.Transform): @RangeField.register_lookup class RangeEndsWith(models.Transform): - lookup_name = "endswith" - function = "upper" + lookup_name = 'endswith' + function = 'upper' @property def output_field(self): @@ -300,34 +287,34 @@ class RangeEndsWith(models.Transform): @RangeField.register_lookup class IsEmpty(models.Transform): - lookup_name = "isempty" - function = "isempty" + lookup_name = 'isempty' + function = 'isempty' output_field = models.BooleanField() @RangeField.register_lookup class LowerInclusive(models.Transform): - lookup_name = "lower_inc" - function = "LOWER_INC" + lookup_name = 'lower_inc' + function = 'LOWER_INC' output_field = models.BooleanField() @RangeField.register_lookup class LowerInfinite(models.Transform): - lookup_name = "lower_inf" - function = "LOWER_INF" + lookup_name = 'lower_inf' + function = 'LOWER_INF' output_field = models.BooleanField() @RangeField.register_lookup class UpperInclusive(models.Transform): - lookup_name = "upper_inc" - function = "UPPER_INC" + lookup_name = 'upper_inc' + function = 'UPPER_INC' output_field = models.BooleanField() @RangeField.register_lookup class UpperInfinite(models.Transform): - lookup_name = "upper_inf" - function = "UPPER_INF" + lookup_name = 'upper_inf' + function = 'UPPER_INF' output_field = models.BooleanField() diff --git a/venv/Lib/site-packages/django/contrib/postgres/forms/__init__.py b/venv/Lib/site-packages/django/contrib/postgres/forms/__init__.py index bb2685e..9158f1e 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/forms/__init__.py +++ b/venv/Lib/site-packages/django/contrib/postgres/forms/__init__.py @@ -1,3 +1,4 @@ from .array import * # NOQA from .hstore import * # NOQA +from .jsonb import * # NOQA from .ranges import * # NOQA diff --git a/venv/Lib/site-packages/django/contrib/postgres/forms/array.py b/venv/Lib/site-packages/django/contrib/postgres/forms/array.py index ddb022a..2e19cd5 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/forms/array.py +++ b/venv/Lib/site-packages/django/contrib/postgres/forms/array.py @@ -3,8 +3,7 @@ from itertools import chain from django import forms from django.contrib.postgres.validators import ( - ArrayMaxLengthValidator, - ArrayMinLengthValidator, + ArrayMaxLengthValidator, ArrayMinLengthValidator, ) from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ @@ -14,12 +13,10 @@ from ..utils import prefix_validation_error class SimpleArrayField(forms.CharField): default_error_messages = { - "item_invalid": _("Item %(nth)s in the array did not validate:"), + 'item_invalid': _('Item %(nth)s in the array did not validate:'), } - def __init__( - self, base_field, *, delimiter=",", max_length=None, min_length=None, **kwargs - ): + def __init__(self, base_field, *, delimiter=',', max_length=None, min_length=None, **kwargs): self.base_field = base_field self.delimiter = delimiter super().__init__(**kwargs) @@ -36,9 +33,7 @@ class SimpleArrayField(forms.CharField): def prepare_value(self, value): if isinstance(value, list): - return self.delimiter.join( - str(self.base_field.prepare_value(v)) for v in value - ) + return self.delimiter.join(str(self.base_field.prepare_value(v)) for v in value) return value def to_python(self, value): @@ -54,14 +49,12 @@ class SimpleArrayField(forms.CharField): try: values.append(self.base_field.to_python(item)) except ValidationError as error: - errors.append( - prefix_validation_error( - error, - prefix=self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, - ) - ) + errors.append(prefix_validation_error( + error, + prefix=self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, + )) if errors: raise ValidationError(errors) return values @@ -73,14 +66,12 @@ class SimpleArrayField(forms.CharField): try: self.base_field.validate(item) except ValidationError as error: - errors.append( - prefix_validation_error( - error, - prefix=self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, - ) - ) + errors.append(prefix_validation_error( + error, + prefix=self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, + )) if errors: raise ValidationError(errors) @@ -91,14 +82,12 @@ class SimpleArrayField(forms.CharField): try: self.base_field.run_validators(item) except ValidationError as error: - errors.append( - prefix_validation_error( - error, - prefix=self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, - ) - ) + errors.append(prefix_validation_error( + error, + prefix=self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, + )) if errors: raise ValidationError(errors) @@ -114,7 +103,7 @@ class SimpleArrayField(forms.CharField): class SplitArrayWidget(forms.Widget): - template_name = "postgres/widgets/split_array.html" + template_name = 'postgres/widgets/split_array.html' def __init__(self, widget, size, **kwargs): self.widget = widget() if isinstance(widget, type) else widget @@ -126,21 +115,19 @@ class SplitArrayWidget(forms.Widget): return self.widget.is_hidden def value_from_datadict(self, data, files, name): - return [ - self.widget.value_from_datadict(data, files, "%s_%s" % (name, index)) - for index in range(self.size) - ] + return [self.widget.value_from_datadict(data, files, '%s_%s' % (name, index)) + for index in range(self.size)] def value_omitted_from_data(self, data, files, name): return all( - self.widget.value_omitted_from_data(data, files, "%s_%s" % (name, index)) + self.widget.value_omitted_from_data(data, files, '%s_%s' % (name, index)) for index in range(self.size) ) def id_for_label(self, id_): # See the comment for RadioSelect.id_for_label() if id_: - id_ += "_0" + id_ += '_0' return id_ def get_context(self, name, value, attrs=None): @@ -149,20 +136,18 @@ class SplitArrayWidget(forms.Widget): if self.is_localized: self.widget.is_localized = self.is_localized value = value or [] - context["widget"]["subwidgets"] = [] + context['widget']['subwidgets'] = [] final_attrs = self.build_attrs(attrs) - id_ = final_attrs.get("id") + id_ = final_attrs.get('id') for i in range(max(len(value), self.size)): try: widget_value = value[i] except IndexError: widget_value = None if id_: - final_attrs = {**final_attrs, "id": "%s_%s" % (id_, i)} - context["widget"]["subwidgets"].append( - self.widget.get_context(name + "_%s" % i, widget_value, final_attrs)[ - "widget" - ] + final_attrs = {**final_attrs, 'id': '%s_%s' % (id_, i)} + context['widget']['subwidgets'].append( + self.widget.get_context(name + '_%s' % i, widget_value, final_attrs)['widget'] ) return context @@ -182,7 +167,7 @@ class SplitArrayWidget(forms.Widget): class SplitArrayField(forms.Field): default_error_messages = { - "item_invalid": _("Item %(nth)s in the array did not validate:"), + 'item_invalid': _('Item %(nth)s in the array did not validate:'), } def __init__(self, base_field, size, *, remove_trailing_nulls=False, **kwargs): @@ -190,7 +175,7 @@ class SplitArrayField(forms.Field): self.size = size self.remove_trailing_nulls = remove_trailing_nulls widget = SplitArrayWidget(widget=base_field.widget, size=size) - kwargs.setdefault("widget", widget) + kwargs.setdefault('widget', widget) super().__init__(**kwargs) def _remove_trailing_nulls(self, values): @@ -213,21 +198,19 @@ class SplitArrayField(forms.Field): cleaned_data = [] errors = [] if not any(value) and self.required: - raise ValidationError(self.error_messages["required"]) + raise ValidationError(self.error_messages['required']) max_size = max(self.size, len(value)) for index in range(max_size): item = value[index] try: cleaned_data.append(self.base_field.clean(item)) except ValidationError as error: - errors.append( - prefix_validation_error( - error, - self.error_messages["item_invalid"], - code="item_invalid", - params={"nth": index + 1}, - ) - ) + errors.append(prefix_validation_error( + error, + self.error_messages['item_invalid'], + code='item_invalid', + params={'nth': index + 1}, + )) cleaned_data.append(None) else: errors.append(None) diff --git a/venv/Lib/site-packages/django/contrib/postgres/forms/hstore.py b/venv/Lib/site-packages/django/contrib/postgres/forms/hstore.py index 6a20f7b..f5af8f1 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/forms/hstore.py +++ b/venv/Lib/site-packages/django/contrib/postgres/forms/hstore.py @@ -4,18 +4,17 @@ from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -__all__ = ["HStoreField"] +__all__ = ['HStoreField'] class HStoreField(forms.CharField): """ A field for HStore data which accepts dictionary JSON input. """ - widget = forms.Textarea default_error_messages = { - "invalid_json": _("Could not load JSON data."), - "invalid_format": _("Input must be a JSON dictionary."), + 'invalid_json': _('Could not load JSON data.'), + 'invalid_format': _('Input must be a JSON dictionary.'), } def prepare_value(self, value): @@ -31,14 +30,14 @@ class HStoreField(forms.CharField): value = json.loads(value) except json.JSONDecodeError: raise ValidationError( - self.error_messages["invalid_json"], - code="invalid_json", + self.error_messages['invalid_json'], + code='invalid_json', ) if not isinstance(value, dict): raise ValidationError( - self.error_messages["invalid_format"], - code="invalid_format", + self.error_messages['invalid_format'], + code='invalid_format', ) # Cast everything to strings for ease. diff --git a/venv/Lib/site-packages/django/contrib/postgres/forms/jsonb.py b/venv/Lib/site-packages/django/contrib/postgres/forms/jsonb.py new file mode 100644 index 0000000..ebc85ef --- /dev/null +++ b/venv/Lib/site-packages/django/contrib/postgres/forms/jsonb.py @@ -0,0 +1,16 @@ +import warnings + +from django.forms import JSONField as BuiltinJSONField +from django.utils.deprecation import RemovedInDjango40Warning + +__all__ = ['JSONField'] + + +class JSONField(BuiltinJSONField): + def __init__(self, *args, **kwargs): + warnings.warn( + 'django.contrib.postgres.forms.JSONField is deprecated in favor ' + 'of django.forms.JSONField.', + RemovedInDjango40Warning, stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/django/contrib/postgres/forms/ranges.py b/venv/Lib/site-packages/django/contrib/postgres/forms/ranges.py index 915c0b5..5a20975 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/forms/ranges.py +++ b/venv/Lib/site-packages/django/contrib/postgres/forms/ranges.py @@ -6,13 +6,8 @@ from django.forms.widgets import HiddenInput, MultiWidget from django.utils.translation import gettext_lazy as _ __all__ = [ - "BaseRangeField", - "IntegerRangeField", - "DecimalRangeField", - "DateTimeRangeField", - "DateRangeField", - "HiddenRangeWidget", - "RangeWidget", + 'BaseRangeField', 'IntegerRangeField', 'DecimalRangeField', + 'DateTimeRangeField', 'DateRangeField', 'HiddenRangeWidget', 'RangeWidget', ] @@ -29,30 +24,24 @@ class RangeWidget(MultiWidget): class HiddenRangeWidget(RangeWidget): """A widget that splits input into two <input type="hidden"> inputs.""" - def __init__(self, attrs=None): super().__init__(HiddenInput, attrs) class BaseRangeField(forms.MultiValueField): default_error_messages = { - "invalid": _("Enter two valid values."), - "bound_ordering": _( - "The start of the range must not exceed the end of the range." - ), + 'invalid': _('Enter two valid values.'), + 'bound_ordering': _('The start of the range must not exceed the end of the range.'), } hidden_widget = HiddenRangeWidget def __init__(self, **kwargs): - if "widget" not in kwargs: - kwargs["widget"] = RangeWidget(self.base_field.widget) - if "fields" not in kwargs: - kwargs["fields"] = [ - self.base_field(required=False), - self.base_field(required=False), - ] - kwargs.setdefault("required", False) - kwargs.setdefault("require_all_fields", False) + if 'widget' not in kwargs: + kwargs['widget'] = RangeWidget(self.base_field.widget) + if 'fields' not in kwargs: + kwargs['fields'] = [self.base_field(required=False), self.base_field(required=False)] + kwargs.setdefault('required', False) + kwargs.setdefault('require_all_fields', False) super().__init__(**kwargs) def prepare_value(self, value): @@ -75,39 +64,39 @@ class BaseRangeField(forms.MultiValueField): lower, upper = values if lower is not None and upper is not None and lower > upper: raise exceptions.ValidationError( - self.error_messages["bound_ordering"], - code="bound_ordering", + self.error_messages['bound_ordering'], + code='bound_ordering', ) try: range_value = self.range_type(lower, upper) except TypeError: raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", + self.error_messages['invalid'], + code='invalid', ) else: return range_value class IntegerRangeField(BaseRangeField): - default_error_messages = {"invalid": _("Enter two whole numbers.")} + default_error_messages = {'invalid': _('Enter two whole numbers.')} base_field = forms.IntegerField range_type = NumericRange class DecimalRangeField(BaseRangeField): - default_error_messages = {"invalid": _("Enter two numbers.")} + default_error_messages = {'invalid': _('Enter two numbers.')} base_field = forms.DecimalField range_type = NumericRange class DateTimeRangeField(BaseRangeField): - default_error_messages = {"invalid": _("Enter two valid date/times.")} + default_error_messages = {'invalid': _('Enter two valid date/times.')} base_field = forms.DateTimeField range_type = DateTimeTZRange class DateRangeField(BaseRangeField): - default_error_messages = {"invalid": _("Enter two valid dates.")} + default_error_messages = {'invalid': _('Enter two valid dates.')} base_field = forms.DateField range_type = DateRange diff --git a/venv/Lib/site-packages/django/contrib/postgres/functions.py b/venv/Lib/site-packages/django/contrib/postgres/functions.py index f001a04..819ce05 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/functions.py +++ b/venv/Lib/site-packages/django/contrib/postgres/functions.py @@ -2,10 +2,10 @@ from django.db.models import DateTimeField, Func, UUIDField class RandomUUID(Func): - template = "GEN_RANDOM_UUID()" + template = 'GEN_RANDOM_UUID()' output_field = UUIDField() class TransactionNow(Func): - template = "CURRENT_TIMESTAMP" + template = 'CURRENT_TIMESTAMP' output_field = DateTimeField() diff --git a/venv/Lib/site-packages/django/contrib/postgres/indexes.py b/venv/Lib/site-packages/django/contrib/postgres/indexes.py index 524cdad..af9de75 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/indexes.py +++ b/venv/Lib/site-packages/django/contrib/postgres/indexes.py @@ -3,17 +3,13 @@ from django.db.models import Func, Index from django.utils.functional import cached_property __all__ = [ - "BloomIndex", - "BrinIndex", - "BTreeIndex", - "GinIndex", - "GistIndex", - "HashIndex", - "SpGistIndex", + 'BloomIndex', 'BrinIndex', 'BTreeIndex', 'GinIndex', 'GistIndex', + 'HashIndex', 'SpGistIndex', ] class PostgresIndex(Index): + @cached_property def max_name_length(self): # Allow an index name longer than 30 characters when the suffix is @@ -22,16 +18,14 @@ class PostgresIndex(Index): # indexes. return Index.max_name_length - len(Index.suffix) + len(self.suffix) - def create_sql(self, model, schema_editor, using="", **kwargs): + def create_sql(self, model, schema_editor, using='', **kwargs): self.check_supported(schema_editor) - statement = super().create_sql( - model, schema_editor, using=" USING %s" % self.suffix, **kwargs - ) + statement = super().create_sql(model, schema_editor, using=' USING %s' % self.suffix, **kwargs) with_params = self.get_with_params() if with_params: - statement.parts["extra"] = "WITH (%s) %s" % ( - ", ".join(with_params), - statement.parts["extra"], + statement.parts['extra'] = 'WITH (%s) %s' % ( + ', '.join(with_params), + statement.parts['extra'], ) return statement @@ -43,23 +37,25 @@ class PostgresIndex(Index): class BloomIndex(PostgresIndex): - suffix = "bloom" + suffix = 'bloom' def __init__(self, *expressions, length=None, columns=(), **kwargs): super().__init__(*expressions, **kwargs) if len(self.fields) > 32: - raise ValueError("Bloom indexes support a maximum of 32 fields.") + raise ValueError('Bloom indexes support a maximum of 32 fields.') if not isinstance(columns, (list, tuple)): - raise ValueError("BloomIndex.columns must be a list or tuple.") + raise ValueError('BloomIndex.columns must be a list or tuple.') if len(columns) > len(self.fields): - raise ValueError("BloomIndex.columns cannot have more values than fields.") + raise ValueError( + 'BloomIndex.columns cannot have more values than fields.' + ) if not all(0 < col <= 4095 for col in columns): raise ValueError( - "BloomIndex.columns must contain integers from 1 to 4095.", + 'BloomIndex.columns must contain integers from 1 to 4095.', ) if length is not None and not 0 < length <= 4096: raise ValueError( - "BloomIndex.length must be None or an integer from 1 to 4096.", + 'BloomIndex.length must be None or an integer from 1 to 4096.', ) self.length = length self.columns = columns @@ -67,30 +63,29 @@ class BloomIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.length is not None: - kwargs["length"] = self.length + kwargs['length'] = self.length if self.columns: - kwargs["columns"] = self.columns + kwargs['columns'] = self.columns return path, args, kwargs def get_with_params(self): with_params = [] if self.length is not None: - with_params.append("length = %d" % self.length) + with_params.append('length = %d' % self.length) if self.columns: with_params.extend( - "col%d = %d" % (i, v) for i, v in enumerate(self.columns, start=1) + 'col%d = %d' % (i, v) + for i, v in enumerate(self.columns, start=1) ) return with_params class BrinIndex(PostgresIndex): - suffix = "brin" + suffix = 'brin' - def __init__( - self, *expressions, autosummarize=None, pages_per_range=None, **kwargs - ): + def __init__(self, *expressions, autosummarize=None, pages_per_range=None, **kwargs): if pages_per_range is not None and pages_per_range <= 0: - raise ValueError("pages_per_range must be None or a positive integer") + raise ValueError('pages_per_range must be None or a positive integer') self.autosummarize = autosummarize self.pages_per_range = pages_per_range super().__init__(*expressions, **kwargs) @@ -98,24 +93,26 @@ class BrinIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.autosummarize is not None: - kwargs["autosummarize"] = self.autosummarize + kwargs['autosummarize'] = self.autosummarize if self.pages_per_range is not None: - kwargs["pages_per_range"] = self.pages_per_range + kwargs['pages_per_range'] = self.pages_per_range return path, args, kwargs + def check_supported(self, schema_editor): + if self.autosummarize and not schema_editor.connection.features.has_brin_autosummarize: + raise NotSupportedError('BRIN option autosummarize requires PostgreSQL 10+.') + def get_with_params(self): with_params = [] if self.autosummarize is not None: - with_params.append( - "autosummarize = %s" % ("on" if self.autosummarize else "off") - ) + with_params.append('autosummarize = %s' % ('on' if self.autosummarize else 'off')) if self.pages_per_range is not None: - with_params.append("pages_per_range = %d" % self.pages_per_range) + with_params.append('pages_per_range = %d' % self.pages_per_range) return with_params class BTreeIndex(PostgresIndex): - suffix = "btree" + suffix = 'btree' def __init__(self, *expressions, fillfactor=None, **kwargs): self.fillfactor = fillfactor @@ -124,22 +121,20 @@ class BTreeIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.fillfactor is not None: - kwargs["fillfactor"] = self.fillfactor + kwargs['fillfactor'] = self.fillfactor return path, args, kwargs def get_with_params(self): with_params = [] if self.fillfactor is not None: - with_params.append("fillfactor = %d" % self.fillfactor) + with_params.append('fillfactor = %d' % self.fillfactor) return with_params class GinIndex(PostgresIndex): - suffix = "gin" + suffix = 'gin' - def __init__( - self, *expressions, fastupdate=None, gin_pending_list_limit=None, **kwargs - ): + def __init__(self, *expressions, fastupdate=None, gin_pending_list_limit=None, **kwargs): self.fastupdate = fastupdate self.gin_pending_list_limit = gin_pending_list_limit super().__init__(*expressions, **kwargs) @@ -147,24 +142,22 @@ class GinIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.fastupdate is not None: - kwargs["fastupdate"] = self.fastupdate + kwargs['fastupdate'] = self.fastupdate if self.gin_pending_list_limit is not None: - kwargs["gin_pending_list_limit"] = self.gin_pending_list_limit + kwargs['gin_pending_list_limit'] = self.gin_pending_list_limit return path, args, kwargs def get_with_params(self): with_params = [] if self.gin_pending_list_limit is not None: - with_params.append( - "gin_pending_list_limit = %d" % self.gin_pending_list_limit - ) + with_params.append('gin_pending_list_limit = %d' % self.gin_pending_list_limit) if self.fastupdate is not None: - with_params.append("fastupdate = %s" % ("on" if self.fastupdate else "off")) + with_params.append('fastupdate = %s' % ('on' if self.fastupdate else 'off')) return with_params class GistIndex(PostgresIndex): - suffix = "gist" + suffix = 'gist' def __init__(self, *expressions, buffering=None, fillfactor=None, **kwargs): self.buffering = buffering @@ -174,29 +167,26 @@ class GistIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.buffering is not None: - kwargs["buffering"] = self.buffering + kwargs['buffering'] = self.buffering if self.fillfactor is not None: - kwargs["fillfactor"] = self.fillfactor + kwargs['fillfactor'] = self.fillfactor return path, args, kwargs def get_with_params(self): with_params = [] if self.buffering is not None: - with_params.append("buffering = %s" % ("on" if self.buffering else "off")) + with_params.append('buffering = %s' % ('on' if self.buffering else 'off')) if self.fillfactor is not None: - with_params.append("fillfactor = %d" % self.fillfactor) + with_params.append('fillfactor = %d' % self.fillfactor) return with_params def check_supported(self, schema_editor): - if ( - self.include - and not schema_editor.connection.features.supports_covering_gist_indexes - ): - raise NotSupportedError("Covering GiST indexes requires PostgreSQL 12+.") + if self.include and not schema_editor.connection.features.supports_covering_gist_indexes: + raise NotSupportedError('Covering GiST indexes requires PostgreSQL 12+.') class HashIndex(PostgresIndex): - suffix = "hash" + suffix = 'hash' def __init__(self, *expressions, fillfactor=None, **kwargs): self.fillfactor = fillfactor @@ -205,18 +195,18 @@ class HashIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.fillfactor is not None: - kwargs["fillfactor"] = self.fillfactor + kwargs['fillfactor'] = self.fillfactor return path, args, kwargs def get_with_params(self): with_params = [] if self.fillfactor is not None: - with_params.append("fillfactor = %d" % self.fillfactor) + with_params.append('fillfactor = %d' % self.fillfactor) return with_params class SpGistIndex(PostgresIndex): - suffix = "spgist" + suffix = 'spgist' def __init__(self, *expressions, fillfactor=None, **kwargs): self.fillfactor = fillfactor @@ -225,18 +215,18 @@ class SpGistIndex(PostgresIndex): def deconstruct(self): path, args, kwargs = super().deconstruct() if self.fillfactor is not None: - kwargs["fillfactor"] = self.fillfactor + kwargs['fillfactor'] = self.fillfactor return path, args, kwargs def get_with_params(self): with_params = [] if self.fillfactor is not None: - with_params.append("fillfactor = %d" % self.fillfactor) + with_params.append('fillfactor = %d' % self.fillfactor) return with_params class OpClass(Func): - template = "%(expressions)s %(name)s" + template = '%(expressions)s %(name)s' def __init__(self, expression, name): super().__init__(expression, name=name) diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo index 57979ba..f4aceb9 100644 Binary files a/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.po index c4a0dbf..4ee210e 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/postgres/locale/bg/LC_MESSAGES/django.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 -# Todor Lubenov <tlubenov@gmail.com>, 2015 +# Todor Lubenov <tgl.sysdev@gmail.com>, 2015 # Venelin Stoykov <vkstoykov@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-05-11 20:56+0200\n" -"PO-Revision-Date: 2022-01-14 11:54+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"PO-Revision-Date: 2020-05-12 20:01+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -24,23 +23,23 @@ msgstr "PostgreSQL разширения" #, python-format msgid "Item %(nth)s in the array did not validate:" -msgstr "Елемент %(nth)s в масива не се валидира:" +msgstr "" msgid "Nested arrays must have the same length." -msgstr "Вложените масиви трябва да имат еднаква дължина." +msgstr "Вложените масиви, трябва да имат същата дължина." msgid "Map of strings to strings/nulls" -msgstr "Мап от стрингове към стрингове/null-ове" +msgstr "" #, python-format msgid "The value of “%(key)s” is not a string or null." -msgstr "Стойността на “%(key)s” не е стринг или null." +msgstr "" msgid "Could not load JSON data." -msgstr "Не можа да зареди JSON данни." +msgstr "Не можа да зареди JSON данни ." msgid "Input must be a JSON dictionary." -msgstr "Входните данни трябва да са JSON речник." +msgstr "" msgid "Enter two valid values." msgstr "Въведете две валидни стойности." @@ -49,7 +48,7 @@ msgid "The start of the range must not exceed the end of the range." msgstr "Началото на обхвата не трябва да превишава края му." msgid "Enter two whole numbers." -msgstr "Въведете две цели числа." +msgstr "Въведете две цели числа" msgid "Enter two numbers." msgstr "Въведете две числа." @@ -107,4 +106,4 @@ msgid "" "Ensure that this range is completely greater than or equal to " "%(limit_value)s." msgstr "" -"Уверете се, че интервалът е изцяло по-голям от или равен на %(limit_value)s." +"Уверете се че интервала е изцяло по-голям от или равен на %(limit_value)s." diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.mo index baa62ce..9caa279 100644 Binary files a/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.po index 5996c4e..cd73dd7 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/postgres/locale/el/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Fotis Athineos <fotis@transifex.com>, 2021 # Giannis Meletakis <meletakis@gmail.com>, 2015 # Nick Mavrakis <mavrakis.n@gmail.com>, 2017-2018 # Nick Mavrakis <mavrakis.n@gmail.com>, 2016 @@ -11,8 +10,8 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-05-11 20:56+0200\n" -"PO-Revision-Date: 2021-08-04 06:26+0000\n" -"Last-Translator: Fotis Athineos <fotis@transifex.com>\n" +"PO-Revision-Date: 2020-05-12 20:01+0000\n" +"Last-Translator: Transifex Bot <>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -35,7 +34,7 @@ msgstr "Αντιστοίχιση strings σε strings/nulls" #, python-format msgid "The value of “%(key)s” is not a string or null." -msgstr "Η τιμή του “%(key)s“ δεν είναι string ή null." +msgstr "" msgid "Could not load JSON data." msgstr "Αδύνατη η φόρτωση των δεδομένων JSON." diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.mo deleted file mode 100644 index 1ac0ada..0000000 Binary files a/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.po deleted file mode 100644 index cdae855..0000000 --- a/venv/Lib/site-packages/django/contrib/postgres/locale/en_AU/LC_MESSAGES/django.po +++ /dev/null @@ -1,109 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Tom Fifield <tom@tomfifield.net>, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-11 20:56+0200\n" -"PO-Revision-Date: 2021-04-11 13:16+0000\n" -"Last-Translator: Tom Fifield <tom@tomfifield.net>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: en_AU\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgid "PostgreSQL extensions" -msgstr "PostgreSQL extensions" - -#, python-format -msgid "Item %(nth)s in the array did not validate:" -msgstr "Item %(nth)s in the array did not validate:" - -msgid "Nested arrays must have the same length." -msgstr "Nested arrays must have the same length." - -msgid "Map of strings to strings/nulls" -msgstr "Map of strings to strings/nulls" - -#, python-format -msgid "The value of “%(key)s” is not a string or null." -msgstr "The value of “%(key)s” is not a string or null." - -msgid "Could not load JSON data." -msgstr "Could not load JSON data." - -msgid "Input must be a JSON dictionary." -msgstr "Input must be a JSON dictionary." - -msgid "Enter two valid values." -msgstr "Enter two valid values." - -msgid "The start of the range must not exceed the end of the range." -msgstr "The start of the range must not exceed the end of the range." - -msgid "Enter two whole numbers." -msgstr "Enter two whole numbers." - -msgid "Enter two numbers." -msgstr "Enter two numbers." - -msgid "Enter two valid date/times." -msgstr "Enter two valid date/times." - -msgid "Enter two valid dates." -msgstr "Enter two valid dates." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no more than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no more than " -"%(limit_value)d." -msgstr[0] "" -"List contains %(show_value)d item, it should contain no more than " -"%(limit_value)d." -msgstr[1] "" -"List contains %(show_value)d items, it should contain no more than " -"%(limit_value)d." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no fewer than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no fewer than " -"%(limit_value)d." -msgstr[0] "" -"List contains %(show_value)d item, it should contain no fewer than " -"%(limit_value)d." -msgstr[1] "" -"List contains %(show_value)d items, it should contain no fewer than " -"%(limit_value)d." - -#, python-format -msgid "Some keys were missing: %(keys)s" -msgstr "Some keys were missing: %(keys)s" - -#, python-format -msgid "Some unknown keys were provided: %(keys)s" -msgstr "Some unknown keys were provided: %(keys)s" - -#, python-format -msgid "" -"Ensure that this range is completely less than or equal to %(limit_value)s." -msgstr "" -"Ensure that this range is completely less than or equal to %(limit_value)s." - -#, python-format -msgid "" -"Ensure that this range is completely greater than or equal to " -"%(limit_value)s." -msgstr "" -"Ensure that this range is completely greater than or equal to " -"%(limit_value)s." diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 51c00af..0000000 Binary files a/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 422eb13..0000000 --- a/venv/Lib/site-packages/django/contrib/postgres/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,100 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-11 20:56+0200\n" -"PO-Revision-Date: 2021-11-16 12:53+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "PostgreSQL extensions" -msgstr "Sambungan PoestgreSQL" - -#, python-format -msgid "Item %(nth)s in the array did not validate:" -msgstr "item %(nth)s didalam tatasusunan tidak disahkan:" - -msgid "Nested arrays must have the same length." -msgstr "Tatasusunan bersarang haruslah sama panjang." - -msgid "Map of strings to strings/nulls" -msgstr "Suaian rentetan ke rentetan/nulls" - -#, python-format -msgid "The value of “%(key)s” is not a string or null." -msgstr "Nilai \"%(key)s\" bukan rentetan atau adalah null." - -msgid "Could not load JSON data." -msgstr "Tidak dapat memuatkan data JSON." - -msgid "Input must be a JSON dictionary." -msgstr "Input mestilah dalam bentuk kamus JSON." - -msgid "Enter two valid values." -msgstr "Masukkan dua nilai yang sah." - -msgid "The start of the range must not exceed the end of the range." -msgstr "Permulaan julat tidak boleh melebihi akhir julat." - -msgid "Enter two whole numbers." -msgstr "Masukkan dua nombor bulat." - -msgid "Enter two numbers." -msgstr "Masukkan duan nombor." - -msgid "Enter two valid date/times." -msgstr "Masukkan dua tarikh.masa yang sah." - -msgid "Enter two valid dates." -msgstr "Masukkan dua tarikh yang sah." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no more than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no more than " -"%(limit_value)d." -msgstr[0] "" -"Senarai mempunyai %(show_value)d perkara, tetapi sepatutnya mempunyai lebih " -"daripada %(limit_value)d." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no fewer than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no fewer than " -"%(limit_value)d." -msgstr[0] "" -"Senarai mempunyai %(show_value)d perkara, tetapi ia sepatutnya mempunyai " -"tidak kurang daripaada %(limit_value)d." - -#, python-format -msgid "Some keys were missing: %(keys)s" -msgstr "Sesetengah kunci hilang: %(keys)s" - -#, python-format -msgid "Some unknown keys were provided: %(keys)s" -msgstr "Sesetengah kunci yang diberikan tidak diketahui: %(keys)s" - -#, python-format -msgid "" -"Ensure that this range is completely less than or equal to %(limit_value)s." -msgstr "" -"Pastikan julat ini adalah kurang daripada atau sama dengan %(limit_value)s." - -#, python-format -msgid "" -"Ensure that this range is completely greater than or equal to " -"%(limit_value)s." -msgstr "Pastikan julat ini lebih daripada atau sama dengan %(limit_value)s." diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.mo deleted file mode 100644 index 52ea3d1..0000000 Binary files a/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.po deleted file mode 100644 index aff9967..0000000 --- a/venv/Lib/site-packages/django/contrib/postgres/locale/nn/LC_MESSAGES/django.po +++ /dev/null @@ -1,106 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Sivert Olstad, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-11 20:56+0200\n" -"PO-Revision-Date: 2021-11-16 22:21+0000\n" -"Last-Translator: Sivert Olstad\n" -"Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" -"language/nn/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: nn\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgid "PostgreSQL extensions" -msgstr "PostgreSQL-utvidingar" - -#, python-format -msgid "Item %(nth)s in the array did not validate:" -msgstr "Element %(nth)s i arrayen validerte ikkje:" - -msgid "Nested arrays must have the same length." -msgstr "Nysta arrayar må ha same lengde." - -msgid "Map of strings to strings/nulls" -msgstr "Oversyn over strenger til strenger/nulls" - -#, python-format -msgid "The value of “%(key)s” is not a string or null." -msgstr "Verdien til “%(key)s” er ikkje ein streng eller null." - -msgid "Could not load JSON data." -msgstr "Kunne ikkje laste JSON-data." - -msgid "Input must be a JSON dictionary." -msgstr "Inndata må vere ein JSON-dictionary." - -msgid "Enter two valid values." -msgstr "Oppgje to gyldige verdiar." - -msgid "The start of the range must not exceed the end of the range." -msgstr "Starten på serien må ikkje overstige enden av serien." - -msgid "Enter two whole numbers." -msgstr "Oppgje to heiltal." - -msgid "Enter two numbers." -msgstr "Oppgje to tal." - -msgid "Enter two valid date/times." -msgstr "Oppgje to gyldige datoar/tidspunkt." - -msgid "Enter two valid dates." -msgstr "Oppgje to gyldige datoar." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no more than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no more than " -"%(limit_value)d." -msgstr[0] "" -"Lista inneheld %(show_value)d element, den bør ikkje innehalde fleire enn " -"%(limit_value)d." -msgstr[1] "" -"Lista inneheld %(show_value)d element, den bør ikkje innehalde fleire enn " -"%(limit_value)d." - -#, python-format -msgid "" -"List contains %(show_value)d item, it should contain no fewer than " -"%(limit_value)d." -msgid_plural "" -"List contains %(show_value)d items, it should contain no fewer than " -"%(limit_value)d." -msgstr[0] "" -"Lista inneheld %(show_value)d element, den bør ikkje innehalde færre enn " -"%(limit_value)d." -msgstr[1] "" -"Lista inneheld %(show_value)d element, den bør ikkje innehalde færre enn " -"%(limit_value)d." - -#, python-format -msgid "Some keys were missing: %(keys)s" -msgstr "Nokon nyklar mangla: %(keys)s" - -#, python-format -msgid "Some unknown keys were provided: %(keys)s" -msgstr "Nokon ukjende nyklar vart oppgjeve: %(keys)s" - -#, python-format -msgid "" -"Ensure that this range is completely less than or equal to %(limit_value)s." -msgstr "Syrg for at serien er heilt mindre enn eller lik %(limit_value)s." - -#, python-format -msgid "" -"Ensure that this range is completely greater than or equal to " -"%(limit_value)s." -msgstr "Syrg for at serien er heilt større enn eller lik %(limit_value)s." diff --git a/venv/Lib/site-packages/django/contrib/postgres/lookups.py b/venv/Lib/site-packages/django/contrib/postgres/lookups.py index 9fed0ee..28d8590 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/lookups.py +++ b/venv/Lib/site-packages/django/contrib/postgres/lookups.py @@ -5,61 +5,56 @@ from .search import SearchVector, SearchVectorExact, SearchVectorField class DataContains(PostgresOperatorLookup): - lookup_name = "contains" - postgres_operator = "@>" + lookup_name = 'contains' + postgres_operator = '@>' class ContainedBy(PostgresOperatorLookup): - lookup_name = "contained_by" - postgres_operator = "<@" + lookup_name = 'contained_by' + postgres_operator = '<@' class Overlap(PostgresOperatorLookup): - lookup_name = "overlap" - postgres_operator = "&&" + lookup_name = 'overlap' + postgres_operator = '&&' class HasKey(PostgresOperatorLookup): - lookup_name = "has_key" - postgres_operator = "?" + lookup_name = 'has_key' + postgres_operator = '?' prepare_rhs = False class HasKeys(PostgresOperatorLookup): - lookup_name = "has_keys" - postgres_operator = "?&" + lookup_name = 'has_keys' + postgres_operator = '?&' def get_prep_lookup(self): return [str(item) for item in self.rhs] class HasAnyKeys(HasKeys): - lookup_name = "has_any_keys" - postgres_operator = "?|" + lookup_name = 'has_any_keys' + postgres_operator = '?|' class Unaccent(Transform): bilateral = True - lookup_name = "unaccent" - function = "UNACCENT" + lookup_name = 'unaccent' + function = 'UNACCENT' class SearchLookup(SearchVectorExact): - lookup_name = "search" + lookup_name = 'search' def process_lhs(self, qn, connection): if not isinstance(self.lhs.output_field, SearchVectorField): - config = getattr(self.rhs, "config", None) + config = getattr(self.rhs, 'config', None) self.lhs = SearchVector(self.lhs, config=config) lhs, lhs_params = super().process_lhs(qn, connection) return lhs, lhs_params class TrigramSimilar(PostgresOperatorLookup): - lookup_name = "trigram_similar" - postgres_operator = "%%" - - -class TrigramWordSimilar(PostgresOperatorLookup): - lookup_name = "trigram_word_similar" - postgres_operator = "%%>" + lookup_name = 'trigram_similar' + postgres_operator = '%%' diff --git a/venv/Lib/site-packages/django/contrib/postgres/operations.py b/venv/Lib/site-packages/django/contrib/postgres/operations.py index b2d6d9f..a8092ec 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/operations.py +++ b/venv/Lib/site-packages/django/contrib/postgres/operations.py @@ -1,12 +1,9 @@ from django.contrib.postgres.signals import ( - get_citext_oids, - get_hstore_oids, - register_type_handlers, + get_citext_oids, get_hstore_oids, register_type_handlers, ) from django.db import NotSupportedError, router -from django.db.migrations import AddConstraint, AddIndex, RemoveIndex +from django.db.migrations import AddIndex, RemoveIndex from django.db.migrations.operations.base import Operation -from django.db.models.constraints import CheckConstraint class CreateExtension(Operation): @@ -19,14 +16,14 @@ class CreateExtension(Operation): pass def database_forwards(self, app_label, schema_editor, from_state, to_state): - if schema_editor.connection.vendor != "postgresql" or not router.allow_migrate( - schema_editor.connection.alias, app_label + if ( + schema_editor.connection.vendor != 'postgresql' or + not router.allow_migrate(schema_editor.connection.alias, app_label) ): return if not self.extension_exists(schema_editor, self.name): schema_editor.execute( - "CREATE EXTENSION IF NOT EXISTS %s" - % schema_editor.quote_name(self.name) + 'CREATE EXTENSION IF NOT EXISTS %s' % schema_editor.quote_name(self.name) ) # Clear cached, stale oids. get_hstore_oids.cache_clear() @@ -41,7 +38,7 @@ class CreateExtension(Operation): return if self.extension_exists(schema_editor, self.name): schema_editor.execute( - "DROP EXTENSION IF EXISTS %s" % schema_editor.quote_name(self.name) + 'DROP EXTENSION IF EXISTS %s' % schema_editor.quote_name(self.name) ) # Clear cached, stale oids. get_hstore_oids.cache_clear() @@ -50,7 +47,7 @@ class CreateExtension(Operation): def extension_exists(self, schema_editor, extension): with schema_editor.connection.cursor() as cursor: cursor.execute( - "SELECT 1 FROM pg_extension WHERE extname = %s", + 'SELECT 1 FROM pg_extension WHERE extname = %s', [extension], ) return bool(cursor.fetchone()) @@ -60,67 +57,75 @@ class CreateExtension(Operation): @property def migration_name_fragment(self): - return "create_extension_%s" % self.name + return 'create_extension_%s' % self.name class BloomExtension(CreateExtension): + def __init__(self): - self.name = "bloom" + self.name = 'bloom' class BtreeGinExtension(CreateExtension): + def __init__(self): - self.name = "btree_gin" + self.name = 'btree_gin' class BtreeGistExtension(CreateExtension): + def __init__(self): - self.name = "btree_gist" + self.name = 'btree_gist' class CITextExtension(CreateExtension): + def __init__(self): - self.name = "citext" + self.name = 'citext' class CryptoExtension(CreateExtension): + def __init__(self): - self.name = "pgcrypto" + self.name = 'pgcrypto' class HStoreExtension(CreateExtension): + def __init__(self): - self.name = "hstore" + self.name = 'hstore' class TrigramExtension(CreateExtension): + def __init__(self): - self.name = "pg_trgm" + self.name = 'pg_trgm' class UnaccentExtension(CreateExtension): + def __init__(self): - self.name = "unaccent" + self.name = 'unaccent' class NotInTransactionMixin: def _ensure_not_in_transaction(self, schema_editor): if schema_editor.connection.in_atomic_block: raise NotSupportedError( - "The %s operation cannot be executed inside a transaction " - "(set atomic = False on the migration)." % self.__class__.__name__ + 'The %s operation cannot be executed inside a transaction ' + '(set atomic = False on the migration).' + % self.__class__.__name__ ) class AddIndexConcurrently(NotInTransactionMixin, AddIndex): """Create an index using PostgreSQL's CREATE INDEX CONCURRENTLY syntax.""" - atomic = False def describe(self): - return "Concurrently create index %s on field(s) %s of model %s" % ( + return 'Concurrently create index %s on field(s) %s of model %s' % ( self.index.name, - ", ".join(self.index.fields), + ', '.join(self.index.fields), self.model_name, ) @@ -139,11 +144,10 @@ class AddIndexConcurrently(NotInTransactionMixin, AddIndex): class RemoveIndexConcurrently(NotInTransactionMixin, RemoveIndex): """Remove an index using PostgreSQL's DROP INDEX CONCURRENTLY syntax.""" - atomic = False def describe(self): - return "Concurrently remove index %s from %s" % (self.name, self.model_name) + return 'Concurrently remove index %s from %s' % (self.name, self.model_name) def database_forwards(self, app_label, schema_editor, from_state, to_state): self._ensure_not_in_transaction(schema_editor) @@ -163,7 +167,7 @@ class RemoveIndexConcurrently(NotInTransactionMixin, RemoveIndex): class CollationOperation(Operation): - def __init__(self, name, locale, *, provider="libc", deterministic=True): + def __init__(self, name, locale, *, provider='libc', deterministic=True): self.name = name self.locale = locale self.provider = provider @@ -173,11 +177,11 @@ class CollationOperation(Operation): pass def deconstruct(self): - kwargs = {"name": self.name, "locale": self.locale} - if self.provider and self.provider != "libc": - kwargs["provider"] = self.provider + kwargs = {'name': self.name, 'locale': self.locale} + if self.provider and self.provider != 'libc': + kwargs['provider'] = self.provider if self.deterministic is False: - kwargs["deterministic"] = self.deterministic + kwargs['deterministic'] = self.deterministic return ( self.__class__.__qualname__, [], @@ -185,39 +189,40 @@ class CollationOperation(Operation): ) def create_collation(self, schema_editor): - if self.deterministic is False and not ( - schema_editor.connection.features.supports_non_deterministic_collations + if ( + self.deterministic is False and + not schema_editor.connection.features.supports_non_deterministic_collations ): raise NotSupportedError( - "Non-deterministic collations require PostgreSQL 12+." + 'Non-deterministic collations require PostgreSQL 12+.' ) - args = {"locale": schema_editor.quote_name(self.locale)} - if self.provider != "libc": - args["provider"] = schema_editor.quote_name(self.provider) + if ( + self.provider != 'libc' and + not schema_editor.connection.features.supports_alternate_collation_providers + ): + raise NotSupportedError('Non-libc providers require PostgreSQL 10+.') + args = {'locale': schema_editor.quote_name(self.locale)} + if self.provider != 'libc': + args['provider'] = schema_editor.quote_name(self.provider) if self.deterministic is False: - args["deterministic"] = "false" - schema_editor.execute( - "CREATE COLLATION %(name)s (%(args)s)" - % { - "name": schema_editor.quote_name(self.name), - "args": ", ".join( - f"{option}={value}" for option, value in args.items() - ), - } - ) + args['deterministic'] = 'false' + schema_editor.execute('CREATE COLLATION %(name)s (%(args)s)' % { + 'name': schema_editor.quote_name(self.name), + 'args': ', '.join(f'{option}={value}' for option, value in args.items()), + }) def remove_collation(self, schema_editor): schema_editor.execute( - "DROP COLLATION %s" % schema_editor.quote_name(self.name), + 'DROP COLLATION %s' % schema_editor.quote_name(self.name), ) class CreateCollation(CollationOperation): """Create a collation.""" - def database_forwards(self, app_label, schema_editor, from_state, to_state): - if schema_editor.connection.vendor != "postgresql" or not router.allow_migrate( - schema_editor.connection.alias, app_label + if ( + schema_editor.connection.vendor != 'postgresql' or + not router.allow_migrate(schema_editor.connection.alias, app_label) ): return self.create_collation(schema_editor) @@ -228,19 +233,19 @@ class CreateCollation(CollationOperation): self.remove_collation(schema_editor) def describe(self): - return f"Create collation {self.name}" + return f'Create collation {self.name}' @property def migration_name_fragment(self): - return "create_collation_%s" % self.name.lower() + return 'create_collation_%s' % self.name.lower() class RemoveCollation(CollationOperation): """Remove a collation.""" - def database_forwards(self, app_label, schema_editor, from_state, to_state): - if schema_editor.connection.vendor != "postgresql" or not router.allow_migrate( - schema_editor.connection.alias, app_label + if ( + schema_editor.connection.vendor != 'postgresql' or + not router.allow_migrate(schema_editor.connection.alias, app_label) ): return self.remove_collation(schema_editor) @@ -251,85 +256,8 @@ class RemoveCollation(CollationOperation): self.create_collation(schema_editor) def describe(self): - return f"Remove collation {self.name}" + return f'Remove collation {self.name}' @property def migration_name_fragment(self): - return "remove_collation_%s" % self.name.lower() - - -class AddConstraintNotValid(AddConstraint): - """ - Add a table constraint without enforcing validation, using PostgreSQL's - NOT VALID syntax. - """ - - def __init__(self, model_name, constraint): - if not isinstance(constraint, CheckConstraint): - raise TypeError( - "AddConstraintNotValid.constraint must be a check constraint." - ) - super().__init__(model_name, constraint) - - def describe(self): - return "Create not valid constraint %s on model %s" % ( - self.constraint.name, - self.model_name, - ) - - def database_forwards(self, app_label, schema_editor, from_state, to_state): - model = from_state.apps.get_model(app_label, self.model_name) - if self.allow_migrate_model(schema_editor.connection.alias, model): - constraint_sql = self.constraint.create_sql(model, schema_editor) - if constraint_sql: - # Constraint.create_sql returns interpolated SQL which makes - # params=None a necessity to avoid escaping attempts on - # execution. - schema_editor.execute(str(constraint_sql) + " NOT VALID", params=None) - - @property - def migration_name_fragment(self): - return super().migration_name_fragment + "_not_valid" - - -class ValidateConstraint(Operation): - """Validate a table NOT VALID constraint.""" - - def __init__(self, model_name, name): - self.model_name = model_name - self.name = name - - def describe(self): - return "Validate constraint %s on model %s" % (self.name, self.model_name) - - def database_forwards(self, app_label, schema_editor, from_state, to_state): - model = from_state.apps.get_model(app_label, self.model_name) - if self.allow_migrate_model(schema_editor.connection.alias, model): - schema_editor.execute( - "ALTER TABLE %s VALIDATE CONSTRAINT %s" - % ( - schema_editor.quote_name(model._meta.db_table), - schema_editor.quote_name(self.name), - ) - ) - - def database_backwards(self, app_label, schema_editor, from_state, to_state): - # PostgreSQL does not provide a way to make a constraint invalid. - pass - - def state_forwards(self, app_label, state): - pass - - @property - def migration_name_fragment(self): - return "%s_validate_%s" % (self.model_name.lower(), self.name.lower()) - - def deconstruct(self): - return ( - self.__class__.__name__, - [], - { - "model_name": self.model_name, - "name": self.name, - }, - ) + return 'remove_collation_%s' % self.name.lower() diff --git a/venv/Lib/site-packages/django/contrib/postgres/search.py b/venv/Lib/site-packages/django/contrib/postgres/search.py index f652c1d..f1640d8 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/search.py +++ b/venv/Lib/site-packages/django/contrib/postgres/search.py @@ -1,25 +1,18 @@ import psycopg2 from django.db.models import ( - CharField, - Expression, - Field, - FloatField, - Func, - Lookup, - TextField, - Value, + CharField, Expression, Field, FloatField, Func, Lookup, TextField, Value, ) from django.db.models.expressions import CombinedExpression from django.db.models.functions import Cast, Coalesce class SearchVectorExact(Lookup): - lookup_name = "exact" + lookup_name = 'exact' def process_rhs(self, qn, connection): if not isinstance(self.rhs, (SearchQuery, CombinedSearchQuery)): - config = getattr(self.lhs, "config", None) + config = getattr(self.lhs, 'config', None) self.rhs = SearchQuery(self.rhs, config=config) rhs, rhs_params = super().process_rhs(qn, connection) return rhs, rhs_params @@ -28,23 +21,25 @@ class SearchVectorExact(Lookup): lhs, lhs_params = self.process_lhs(qn, connection) rhs, rhs_params = self.process_rhs(qn, connection) params = lhs_params + rhs_params - return "%s @@ %s" % (lhs, rhs), params + return '%s @@ %s' % (lhs, rhs), params class SearchVectorField(Field): + def db_type(self, connection): - return "tsvector" + return 'tsvector' class SearchQueryField(Field): + def db_type(self, connection): - return "tsquery" + return 'tsquery' class SearchConfig(Expression): def __init__(self, config): super().__init__() - if not hasattr(config, "resolve_expression"): + if not hasattr(config, 'resolve_expression'): config = Value(config) self.config = config @@ -58,21 +53,21 @@ class SearchConfig(Expression): return [self.config] def set_source_expressions(self, exprs): - (self.config,) = exprs + self.config, = exprs def as_sql(self, compiler, connection): sql, params = compiler.compile(self.config) - return "%s::regconfig" % sql, params + return '%s::regconfig' % sql, params class SearchVectorCombinable: - ADD = "||" + ADD = '||' def _combine(self, other, connector, reversed): if not isinstance(other, SearchVectorCombinable): raise TypeError( - "SearchVector can only be combined with other SearchVector " - "instances, got %s." % type(other).__name__ + 'SearchVector can only be combined with other SearchVector ' + 'instances, got %s.' % type(other).__name__ ) if reversed: return CombinedSearchVector(other, connector, self, self.config) @@ -80,61 +75,49 @@ class SearchVectorCombinable: class SearchVector(SearchVectorCombinable, Func): - function = "to_tsvector" + function = 'to_tsvector' arg_joiner = " || ' ' || " output_field = SearchVectorField() def __init__(self, *expressions, config=None, weight=None): super().__init__(*expressions) self.config = SearchConfig.from_parameter(config) - if weight is not None and not hasattr(weight, "resolve_expression"): + if weight is not None and not hasattr(weight, 'resolve_expression'): weight = Value(weight) self.weight = weight - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): - resolved = super().resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): + resolved = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) if self.config: - resolved.config = self.config.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + resolved.config = self.config.resolve_expression(query, allow_joins, reuse, summarize, for_save) return resolved def as_sql(self, compiler, connection, function=None, template=None): clone = self.copy() - clone.set_source_expressions( - [ - Coalesce( - expression - if isinstance(expression.output_field, (CharField, TextField)) - else Cast(expression, TextField()), - Value(""), - ) - for expression in clone.get_source_expressions() - ] - ) + clone.set_source_expressions([ + Coalesce( + expression + if isinstance(expression.output_field, (CharField, TextField)) + else Cast(expression, TextField()), + Value('') + ) for expression in clone.get_source_expressions() + ]) config_sql = None config_params = [] if template is None: if clone.config: config_sql, config_params = compiler.compile(clone.config) - template = "%(function)s(%(config)s, %(expressions)s)" + template = '%(function)s(%(config)s, %(expressions)s)' else: template = clone.template sql, params = super(SearchVector, clone).as_sql( - compiler, - connection, - function=function, - template=template, + compiler, connection, function=function, template=template, config=config_sql, ) extra_params = [] if clone.weight: weight_sql, extra_params = compiler.compile(clone.weight) - sql = "setweight({}, {})".format(sql, weight_sql) + sql = 'setweight({}, {})'.format(sql, weight_sql) return sql, config_params + params + extra_params @@ -145,14 +128,14 @@ class CombinedSearchVector(SearchVectorCombinable, CombinedExpression): class SearchQueryCombinable: - BITAND = "&&" - BITOR = "||" + BITAND = '&&' + BITOR = '||' def _combine(self, other, connector, reversed): if not isinstance(other, SearchQueryCombinable): raise TypeError( - "SearchQuery can only be combined with other SearchQuery " - "instances, got %s." % type(other).__name__ + 'SearchQuery can only be combined with other SearchQuery ' + 'instances, got %s.' % type(other).__name__ ) if reversed: return CombinedSearchQuery(other, connector, self, self.config) @@ -177,25 +160,17 @@ class SearchQueryCombinable: class SearchQuery(SearchQueryCombinable, Func): output_field = SearchQueryField() SEARCH_TYPES = { - "plain": "plainto_tsquery", - "phrase": "phraseto_tsquery", - "raw": "to_tsquery", - "websearch": "websearch_to_tsquery", + 'plain': 'plainto_tsquery', + 'phrase': 'phraseto_tsquery', + 'raw': 'to_tsquery', + 'websearch': 'websearch_to_tsquery', } - def __init__( - self, - value, - output_field=None, - *, - config=None, - invert=False, - search_type="plain", - ): + def __init__(self, value, output_field=None, *, config=None, invert=False, search_type='plain'): self.function = self.SEARCH_TYPES.get(search_type) if self.function is None: raise ValueError("Unknown search_type argument '%s'." % search_type) - if not hasattr(value, "resolve_expression"): + if not hasattr(value, 'resolve_expression'): value = Value(value) expressions = (value,) self.config = SearchConfig.from_parameter(config) @@ -207,7 +182,7 @@ class SearchQuery(SearchQueryCombinable, Func): def as_sql(self, compiler, connection, function=None, template=None): sql, params = super().as_sql(compiler, connection, function, template) if self.invert: - sql = "!!(%s)" % sql + sql = '!!(%s)' % sql return sql, params def __invert__(self): @@ -217,7 +192,7 @@ class SearchQuery(SearchQueryCombinable, Func): def __str__(self): result = super().__str__() - return ("~%s" % result) if self.invert else result + return ('~%s' % result) if self.invert else result class CombinedSearchQuery(SearchQueryCombinable, CombinedExpression): @@ -226,73 +201,60 @@ class CombinedSearchQuery(SearchQueryCombinable, CombinedExpression): super().__init__(lhs, connector, rhs, output_field) def __str__(self): - return "(%s)" % super().__str__() + return '(%s)' % super().__str__() class SearchRank(Func): - function = "ts_rank" + function = 'ts_rank' output_field = FloatField() def __init__( - self, - vector, - query, - weights=None, - normalization=None, + self, vector, query, weights=None, normalization=None, cover_density=False, ): - if not hasattr(vector, "resolve_expression"): + if not hasattr(vector, 'resolve_expression'): vector = SearchVector(vector) - if not hasattr(query, "resolve_expression"): + if not hasattr(query, 'resolve_expression'): query = SearchQuery(query) expressions = (vector, query) if weights is not None: - if not hasattr(weights, "resolve_expression"): + if not hasattr(weights, 'resolve_expression'): weights = Value(weights) expressions = (weights,) + expressions if normalization is not None: - if not hasattr(normalization, "resolve_expression"): + if not hasattr(normalization, 'resolve_expression'): normalization = Value(normalization) expressions += (normalization,) if cover_density: - self.function = "ts_rank_cd" + self.function = 'ts_rank_cd' super().__init__(*expressions) class SearchHeadline(Func): - function = "ts_headline" - template = "%(function)s(%(expressions)s%(options)s)" + function = 'ts_headline' + template = '%(function)s(%(expressions)s%(options)s)' output_field = TextField() def __init__( - self, - expression, - query, - *, - config=None, - start_sel=None, - stop_sel=None, - max_words=None, - min_words=None, - short_word=None, - highlight_all=None, - max_fragments=None, - fragment_delimiter=None, + self, expression, query, *, config=None, start_sel=None, stop_sel=None, + max_words=None, min_words=None, short_word=None, highlight_all=None, + max_fragments=None, fragment_delimiter=None, ): - if not hasattr(query, "resolve_expression"): + if not hasattr(query, 'resolve_expression'): query = SearchQuery(query) options = { - "StartSel": start_sel, - "StopSel": stop_sel, - "MaxWords": max_words, - "MinWords": min_words, - "ShortWord": short_word, - "HighlightAll": highlight_all, - "MaxFragments": max_fragments, - "FragmentDelimiter": fragment_delimiter, + 'StartSel': start_sel, + 'StopSel': stop_sel, + 'MaxWords': max_words, + 'MinWords': min_words, + 'ShortWord': short_word, + 'HighlightAll': highlight_all, + 'MaxFragments': max_fragments, + 'FragmentDelimiter': fragment_delimiter, } self.options = { - option: value for option, value in options.items() if value is not None + option: value + for option, value in options.items() if value is not None } expressions = (expression, query) if config is not None: @@ -301,26 +263,19 @@ class SearchHeadline(Func): super().__init__(*expressions) def as_sql(self, compiler, connection, function=None, template=None): - options_sql = "" + options_sql = '' options_params = [] if self.options: # getquoted() returns a quoted bytestring of the adapted value. - options_params.append( - ", ".join( - "%s=%s" - % ( - option, - psycopg2.extensions.adapt(value).getquoted().decode(), - ) - for option, value in self.options.items() - ) - ) - options_sql = ", %s" + options_params.append(', '.join( + '%s=%s' % ( + option, + psycopg2.extensions.adapt(value).getquoted().decode(), + ) for option, value in self.options.items() + )) + options_sql = ', %s' sql, params = super().as_sql( - compiler, - connection, - function=function, - template=template, + compiler, connection, function=function, template=template, options=options_sql, ) return sql, params + options_params @@ -333,33 +288,15 @@ class TrigramBase(Func): output_field = FloatField() def __init__(self, expression, string, **extra): - if not hasattr(string, "resolve_expression"): + if not hasattr(string, 'resolve_expression'): string = Value(string) super().__init__(expression, string, **extra) -class TrigramWordBase(Func): - output_field = FloatField() - - def __init__(self, string, expression, **extra): - if not hasattr(string, "resolve_expression"): - string = Value(string) - super().__init__(string, expression, **extra) - - class TrigramSimilarity(TrigramBase): - function = "SIMILARITY" + function = 'SIMILARITY' class TrigramDistance(TrigramBase): - function = "" - arg_joiner = " <-> " - - -class TrigramWordDistance(TrigramWordBase): - function = "" - arg_joiner = " <<-> " - - -class TrigramWordSimilarity(TrigramWordBase): - function = "WORD_SIMILARITY" + function = '' + arg_joiner = ' <-> ' diff --git a/venv/Lib/site-packages/django/contrib/postgres/serializers.py b/venv/Lib/site-packages/django/contrib/postgres/serializers.py index d04bfdb..1b1c2f1 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/serializers.py +++ b/venv/Lib/site-packages/django/contrib/postgres/serializers.py @@ -6,5 +6,5 @@ class RangeSerializer(BaseSerializer): module = self.value.__class__.__module__ # Ranges are implemented in psycopg2._range but the public import # location is psycopg2.extras. - module = "psycopg2.extras" if module == "psycopg2._range" else module - return "%s.%r" % (module, self.value), {"import %s" % module} + module = 'psycopg2.extras' if module == 'psycopg2._range' else module + return '%s.%r' % (module, self.value), {'import %s' % module} diff --git a/venv/Lib/site-packages/django/contrib/postgres/signals.py b/venv/Lib/site-packages/django/contrib/postgres/signals.py index a7da7b6..abfd890 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/signals.py +++ b/venv/Lib/site-packages/django/contrib/postgres/signals.py @@ -35,14 +35,12 @@ def get_citext_oids(connection_alias): def register_type_handlers(connection, **kwargs): - if connection.vendor != "postgresql" or connection.alias == NO_DB_ALIAS: + if connection.vendor != 'postgresql' or connection.alias == NO_DB_ALIAS: return try: oids, array_oids = get_hstore_oids(connection.alias) - register_hstore( - connection.connection, globally=True, oid=oids, array_oid=array_oids - ) + register_hstore(connection.connection, globally=True, oid=oids, array_oid=array_oids) except ProgrammingError: # Hstore is not available on the database. # @@ -56,9 +54,7 @@ def register_type_handlers(connection, **kwargs): try: citext_oids = get_citext_oids(connection.alias) - array_type = psycopg2.extensions.new_array_type( - citext_oids, "citext[]", psycopg2.STRING - ) + array_type = psycopg2.extensions.new_array_type(citext_oids, 'citext[]', psycopg2.STRING) psycopg2.extensions.register_type(array_type, None) except ProgrammingError: # citext is not available on the database. diff --git a/venv/Lib/site-packages/django/contrib/postgres/utils.py b/venv/Lib/site-packages/django/contrib/postgres/utils.py index e4f4d81..f3c022f 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/utils.py +++ b/venv/Lib/site-packages/django/contrib/postgres/utils.py @@ -17,13 +17,13 @@ def prefix_validation_error(error, prefix, code, params): # ngettext calls require a count parameter and are converted # to an empty string if they are missing it. message=format_lazy( - "{} {}", + '{} {}', SimpleLazyObject(lambda: prefix % params), SimpleLazyObject(lambda: error.message % error_params), ), code=code, params={**error_params, **params}, ) - return ValidationError( - [prefix_validation_error(e, prefix, code, params) for e in error.error_list] - ) + return ValidationError([ + prefix_validation_error(e, prefix, code, params) for e in error.error_list + ]) diff --git a/venv/Lib/site-packages/django/contrib/postgres/validators.py b/venv/Lib/site-packages/django/contrib/postgres/validators.py index 47bb7bc..db6205f 100644 --- a/venv/Lib/site-packages/django/contrib/postgres/validators.py +++ b/venv/Lib/site-packages/django/contrib/postgres/validators.py @@ -1,33 +1,24 @@ from django.core.exceptions import ValidationError from django.core.validators import ( - MaxLengthValidator, - MaxValueValidator, - MinLengthValidator, + MaxLengthValidator, MaxValueValidator, MinLengthValidator, MinValueValidator, ) from django.utils.deconstruct import deconstructible -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import ngettext_lazy +from django.utils.translation import gettext_lazy as _, ngettext_lazy class ArrayMaxLengthValidator(MaxLengthValidator): message = ngettext_lazy( - "List contains %(show_value)d item, it should contain no more than " - "%(limit_value)d.", - "List contains %(show_value)d items, it should contain no more than " - "%(limit_value)d.", - "limit_value", - ) + 'List contains %(show_value)d item, it should contain no more than %(limit_value)d.', + 'List contains %(show_value)d items, it should contain no more than %(limit_value)d.', + 'limit_value') class ArrayMinLengthValidator(MinLengthValidator): message = ngettext_lazy( - "List contains %(show_value)d item, it should contain no fewer than " - "%(limit_value)d.", - "List contains %(show_value)d items, it should contain no fewer than " - "%(limit_value)d.", - "limit_value", - ) + 'List contains %(show_value)d item, it should contain no fewer than %(limit_value)d.', + 'List contains %(show_value)d items, it should contain no fewer than %(limit_value)d.', + 'limit_value') @deconstructible @@ -35,8 +26,8 @@ class KeysValidator: """A validator designed for HStore to require/restrict keys.""" messages = { - "missing_keys": _("Some keys were missing: %(keys)s"), - "extra_keys": _("Some unknown keys were provided: %(keys)s"), + 'missing_keys': _('Some keys were missing: %(keys)s'), + 'extra_keys': _('Some unknown keys were provided: %(keys)s'), } strict = False @@ -51,41 +42,35 @@ class KeysValidator: missing_keys = self.keys - keys if missing_keys: raise ValidationError( - self.messages["missing_keys"], - code="missing_keys", - params={"keys": ", ".join(missing_keys)}, + self.messages['missing_keys'], + code='missing_keys', + params={'keys': ', '.join(missing_keys)}, ) if self.strict: extra_keys = keys - self.keys if extra_keys: raise ValidationError( - self.messages["extra_keys"], - code="extra_keys", - params={"keys": ", ".join(extra_keys)}, + self.messages['extra_keys'], + code='extra_keys', + params={'keys': ', '.join(extra_keys)}, ) def __eq__(self, other): return ( - isinstance(other, self.__class__) - and self.keys == other.keys - and self.messages == other.messages - and self.strict == other.strict + isinstance(other, self.__class__) and + self.keys == other.keys and + self.messages == other.messages and + self.strict == other.strict ) class RangeMaxValueValidator(MaxValueValidator): def compare(self, a, b): return a.upper is None or a.upper > b - - message = _( - "Ensure that this range is completely less than or equal to %(limit_value)s." - ) + message = _('Ensure that this range is completely less than or equal to %(limit_value)s.') class RangeMinValueValidator(MinValueValidator): def compare(self, a, b): return a.lower is None or a.lower < b - - message = _( - "Ensure that this range is completely greater than or equal to %(limit_value)s." - ) + message = _('Ensure that this range is completely greater than or equal to %(limit_value)s.') diff --git a/venv/Lib/site-packages/django/contrib/redirects/admin.py b/venv/Lib/site-packages/django/contrib/redirects/admin.py index 39400ad..f828747 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/admin.py +++ b/venv/Lib/site-packages/django/contrib/redirects/admin.py @@ -4,7 +4,7 @@ from django.contrib.redirects.models import Redirect @admin.register(Redirect) class RedirectAdmin(admin.ModelAdmin): - list_display = ("old_path", "new_path") - list_filter = ("site",) - search_fields = ("old_path", "new_path") - radio_fields = {"site": admin.VERTICAL} + list_display = ('old_path', 'new_path') + list_filter = ('site',) + search_fields = ('old_path', 'new_path') + radio_fields = {'site': admin.VERTICAL} diff --git a/venv/Lib/site-packages/django/contrib/redirects/apps.py b/venv/Lib/site-packages/django/contrib/redirects/apps.py index d770671..c1d80ee 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/apps.py +++ b/venv/Lib/site-packages/django/contrib/redirects/apps.py @@ -3,6 +3,6 @@ from django.utils.translation import gettext_lazy as _ class RedirectsConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.redirects" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.redirects' verbose_name = _("Redirects") diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.mo index b88e37d..1c66c35 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.po index a484b84..4557cf9 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/af/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Charl du Plessis <cjdupless@gmail.com>, 2021 # F Wolff <friedel@translate.org.za>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-08-09 13:09+0000\n" -"Last-Translator: Charl du Plessis <cjdupless@gmail.com>\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2019-01-04 18:46+0000\n" +"Last-Translator: F Wolff <friedel@translate.org.za>\n" "Language-Team: Afrikaans (http://www.transifex.com/django/django/language/" "af/)\n" "MIME-Version: 1.0\n" @@ -28,21 +27,21 @@ msgid "redirect from" msgstr "aanstuurvorm" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"Hierdie moet ’n absolute pad wees met die domeinnaam uitgesluit. Soos " -"byvoorbeeld: “/events/search/”." +"Hierdie moet ’n absolute pad wees, die domeinnaam uitgesluit. Voorbeeld: “/" +"events/search/”." msgid "redirect to" msgstr "stuur aan na" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" -"Hierdie kan òf 'n absolute pad wees (soos bo) òf 'n volledige URL wees wat " -"begin met 'n skema soos \"https://\"." +"Hierdie kan óf ’n absolute pad wees (soos bo) of 'n volle URL beginnende met " +"“http://”." msgid "redirect" msgstr "aanstuur" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo index 97afd31..a5a1f52 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.po index 523327a..af51c55 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/ar/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Bashar Al-Abdulhadi, 2016,2021 +# Bashar Al-Abdulhadi, 2016 # Bashar Al-Abdulhadi, 2014 # Jannis Leidel <jannis@leidel.info>, 2011 # Muaaz Alsaied, 2020 @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-15 21:27+0000\n" -"Last-Translator: Bashar Al-Abdulhadi\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-04-02 11:37+0000\n" +"Last-Translator: Muaaz Alsaied\n" "Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -38,11 +38,11 @@ msgid "redirect to" msgstr "إعادة التوجيه إلى" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" "يجب أن يكون هذا مسارا مطلقا (كما هو أعلاه) أو عنوانا كاملا يبدأ بالمقطع " -"“https://”." +"“http://”." msgid "redirect" msgstr "إعادة التوجيه" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo index f47e604..d3e35e4 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.po index 0fc628b..12ffedd 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/bg/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Jannis Leidel <jannis@leidel.info>, 2011 # Venelin Stoykov <vkstoykov@gmail.com>, 2015 # vestimir <vestimir@gmail.com>, 2014 @@ -9,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2022-01-14 11:55+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Venelin Stoykov <vkstoykov@gmail.com>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -30,21 +29,21 @@ msgid "redirect from" msgstr "препратка от" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"Това трябва да бъде абсолютен път, без името на домейна. Пример: \"/events/" -"search/\"." +"Това трябва да бъде абсолютен път, без името на домейна. Пример: '/events/" +"search/'." msgid "redirect to" msgstr "препратка към" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" -"Това може да бъде или абсолютен път (като горното) или пълен URL, започващ " -"със схема, например \"https://\"." +"Това може да бъде или абсолютен път (като горното) или пълен URL, започващ с " +"'http://'." msgid "redirect" msgstr "препратка" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo index fedd26a..5b0ce04 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.po index e94f9c8..ee06386 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/ca/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Antoni Aloy <aaloy@apsl.net>, 2021 # Carles Barrobés <carles@barrobes.com>, 2014 # Jannis Leidel <jannis@leidel.info>, 2011 # Manel Clos <manelclos@gmail.com>, 2020 @@ -10,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-10-27 09:06+0000\n" -"Last-Translator: Antoni Aloy <aaloy@apsl.net>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-04-28 20:20+0000\n" +"Last-Translator: Manel Clos <manelclos@gmail.com>\n" "Language-Team: Catalan (http://www.transifex.com/django/django/language/" "ca/)\n" "MIME-Version: 1.0\n" @@ -41,11 +40,11 @@ msgid "redirect to" msgstr "redirigir a" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" "Això pot ser bé una ruta absoluta (com a sobre) o una URL completa que " -"comenci per un esquema com 'https://'." +"comenci per 'http://'." msgid "redirect" msgstr "redirecció" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.mo index 2db36dc..9ec99a9 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.po index 78f928a..586cadd 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/de/LC_MESSAGES/django.po @@ -2,15 +2,14 @@ # # Translators: # André Hagenbruch, 2015 -# Florian Apolloner <florian@apolloner.eu>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011,2013-2017,2020 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-28 17:21+0000\n" -"Last-Translator: Raphael Michel <mail@raphaelmichel.de>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-01-17 23:02+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -38,11 +37,11 @@ msgid "redirect to" msgstr "Umleitung nach" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" -"Hier muss entweder ein absoluter Pfad (wie darüber) oder eine komplette URL " -"mit einem Schema, wie zum Beispiel „https://“, stehen." +"Hier muss entweder ein absoluter Pfad oder eine komplette URL mit „http://“ " +"am Anfang stehen." msgid "redirect" msgstr "Umleitung" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.mo index 89b919c..d1baec4 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.po index c1d13e7..bef80de 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/el/LC_MESSAGES/django.po @@ -4,14 +4,13 @@ # Jannis Leidel <jannis@leidel.info>, 2011 # Nick Mavrakis <mavrakis.n@gmail.com>, 2016 # Pãnoș <panos.laganakos@gmail.com>, 2014 -# Serafeim Papastefanos <spapas@gmail.com>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-06-03 18:27+0000\n" -"Last-Translator: Serafeim Papastefanos <spapas@gmail.com>\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Nick Mavrakis <mavrakis.n@gmail.com>\n" "Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -29,21 +28,21 @@ msgid "redirect from" msgstr "ανακατεύθυνση από" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"Πρέπει να είναι ένα απόλυτο μονοπάτι χωρίς το όνομα τομέα. Για παράδειγμα \"/" -"events/search\"." +"Αυτό πρέπει να είναι ένα απόλυτο μονοπάτι, με εξαίρεση το όνομα χώρου. " +"Παράδειγμα: '/events/search/'." msgid "redirect to" msgstr "ανακατεύθυνση προς" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" -"Μπορεί να είναι είτε ένα απόλυτο μονοπάτι (όπως πιο πάνω) είτε ένα πλήρες " -"URL που ξεκινά με κατάλληλο σχημα πχ \"https://\"." +"Αυτό πρέπει να είναι ένα απόλυτο μονοπάτι (όπως παραπάνω), ή ένα πλήρες URL " +"που αρχίζει με 'http://'." msgid "redirect" msgstr "ανακατεύθυνση" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo index f8b5bf1..fcc2fc5 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po index 13be8ad..f2eb088 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/en_AU/LC_MESSAGES/django.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield <tom@tomfifield.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-04-11 13:16+0000\n" -"Last-Translator: Tom Fifield <tom@tomfifield.net>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" +"POT-Creation-Date: 2015-01-17 11:07+0100\n" +"PO-Revision-Date: 2014-10-05 20:11+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: English (Australia) (http://www.transifex.com/projects/p/" +"django/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,33 +17,26 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Redirects" -msgstr "Redirects" - -msgid "site" -msgstr "site" +msgstr "" msgid "redirect from" -msgstr "redirect from" +msgstr "" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." msgid "redirect to" -msgstr "redirect to" +msgstr "" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." msgid "redirect" -msgstr "redirect" +msgstr "" msgid "redirects" -msgstr "redirects" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.mo index b9b37ba..f36d36e 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.po index 43ed079..280a7b7 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/et/LC_MESSAGES/django.po @@ -3,16 +3,15 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # Janno Liivak <jannolii@gmail.com>, 2015 -# Martin <martinpajuste@gmail.com>, 2021 # Marti Raudsepp <marti@juffo.org>, 2014 # Ragnar Rebase <rrebase@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-22 11:23+0000\n" -"Last-Translator: Martin <martinpajuste@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-12-28 02:30+0000\n" +"Last-Translator: Ragnar Rebase <rrebase@gmail.com>\n" "Language-Team: Estonian (http://www.transifex.com/django/django/language/" "et/)\n" "MIME-Version: 1.0\n" @@ -41,11 +40,11 @@ msgid "redirect to" msgstr "suuna aadressile" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" -"See võib olla kas absoluutne asukoht (nagu ülemine) või täielik URL, mis " -"algab skeemiga nagu “https://”." +"See võib olla kas absoluutne asukoht (nagu ülemine) või täielik URL algusega " +"“http://”." msgid "redirect" msgstr "suunamine" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo index e611c50..0ecb68e 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.po index 232e298..9d1eb9a 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/gd/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-15 10:46+0000\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-12-13 12:52+0000\n" "Last-Translator: GunChleoc\n" "Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" "language/gd/)\n" @@ -39,11 +39,11 @@ msgid "redirect to" msgstr "ath-stiùireadh gu" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" "Faodaidh seo a bhith ’na shlighe absaloideach (mar a tha gu h-àrd) no ’na " -"URL slàn a thòisicheas le sgeama, can “https://”." +"URL slàn a thòisicheas le “http://”." msgid "redirect" msgstr "ath-stiùireadh" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.mo index e253951..4cfb598 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.po index 4ef7ed1..7e519f2 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/id/LC_MESSAGES/django.po @@ -1,17 +1,17 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Fery Setiawan <gembelweb@gmail.com>, 2015,2021 +# Fery Setiawan <gembelweb@gmail.com>, 2015 # rodin <romihardiyanto@gmail.com>, 2011 # rodin <romihardiyanto@gmail.com>, 2015 -# sag᠎e <laymonage@gmail.com>, 2019 +# sage <laymonage@gmail.com>, 2019 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-09-14 00:29+0000\n" -"Last-Translator: Fery Setiawan <gembelweb@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2019-09-28 05:54+0000\n" +"Last-Translator: sage <laymonage@gmail.com>\n" "Language-Team: Indonesian (http://www.transifex.com/django/django/language/" "id/)\n" "MIME-Version: 1.0\n" @@ -39,11 +39,11 @@ msgid "redirect to" msgstr "pengalihan ke" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." msgstr "" -"Ini dapat berupa jalur mutlak (seperti diatas) atau URL lengkap diawali " -"dengan skema seperti “https://”." +"Dapat berupa lokasi absolut (seperti di atas) atau URL lengkap yang dimulai " +"dengan “http://”." msgid "redirect" msgstr "pengalihan" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo index 3b223b6..23d80e5 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.po index 8c6ab2b..c821de1 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/ja/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Goto Hayato <habita.gh@gmail.com>, 2021 # Jannis Leidel <jannis@leidel.info>, 2011 # Shinya Okano <tokibito@gmail.com>, 2014-2015 # Takuya N <takninnovationresearch@gmail.com>, 2020 @@ -9,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-02 13:57+0000\n" -"Last-Translator: Goto Hayato <habita.gh@gmail.com>\n" +"POT-Creation-Date: 2019-09-08 17:27+0200\n" +"PO-Revision-Date: 2020-02-07 16:04+0000\n" +"Last-Translator: Takuya N <takninnovationresearch@gmail.com>\n" "Language-Team: Japanese (http://www.transifex.com/django/django/language/" "ja/)\n" "MIME-Version: 1.0\n" @@ -38,11 +37,9 @@ msgid "redirect to" msgstr "リダイレクト先" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." -msgstr "" -"これは上のような絶対パスにも「 https:// 」のようなスキームで始まる完全な URL " -"にもすることができます。" +"This can be either an absolute path (as above) or a full URL starting with " +"“http://”." +msgstr "上記のような絶対パスか、 “http://” で始まる完全な URL にします。" msgid "redirect" msgstr "リダイレクト" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/ka/LC_MESSAGES/django.po index bf76c7a..a168d07 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/ka/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Redirects" msgstr "გადამისამართებები" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/kk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/kk/LC_MESSAGES/django.po index 8c6af97..91d2a9c 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/kk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/kk/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kk\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Redirects" msgstr "Қайта бағыттаулар" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo index 24a289a..a497d71 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.po index bfeadbe..214edc4 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/kn/LC_MESSAGES/django.po @@ -2,43 +2,44 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Rohith PR, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-08-03 10:56+0000\n" -"Last-Translator: Rohith PR\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Kannada (http://www.transifex.com/django/django/language/" "kn/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Redirects" msgstr "" msgid "site" -msgstr "ತಾಣ" +msgstr "" msgid "redirect from" msgstr "ಪುನರ್ನಿರ್ದೇಶನ ಇಲ್ಲಿಂದ->" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." -msgstr "" +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." +msgstr "ಇದು ಡೊಮೈನ್ ಹೊರತುಪಡಿಸಿದ ಸಂಪೂರ್ಣ ಪಥವಾಗಿರಬೇಕು ಉದಾ.'/events/search/'." msgid "redirect to" msgstr "ಪುನರ್ನಿರ್ದೇಶನ ಇಲ್ಲಿಗೆ->" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" +"ಇದು ಮೇಲಿನಂತೆ ಸಂಪೂರ್ಣ ಪಥವಾದರೂ ಆಗಿರಬಹುದು ಅಥವಾ 'http://'ದಿಂದ ಆರಂಭವಾಗುವ ಸಂಪೂರ್ಣ " +"URL ಆಗಿರಬಹುದು." msgid "redirect" msgstr "ಪುನರ್ನಿರ್ದೇಶನ" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/lt/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/lt/LC_MESSAGES/django.po index 811c248..2d79101 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/lt/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/lt/LC_MESSAGES/django.po @@ -16,9 +16,8 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lt\n" -"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < " -"11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? " -"1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n" +"%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Redirects" msgstr "Nukreipimai" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 54eddda..0000000 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index ef67357..0000000 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,49 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-16 12:45+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Redirects" -msgstr "Lencongan-lencongan" - -msgid "site" -msgstr "laman" - -msgid "redirect from" -msgstr "borang lencongan" - -msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." -msgstr "" -"Ini sepatutnya laluan mutlak, tidak termasuk nama domain. Contoh: \"/" -"peristiwa/cari/\"" - -msgid "redirect to" -msgstr "Lencong ke" - -msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." -msgstr "" -"Ini boleh menjadi laluan mutlak (seperti diatas) atau URL penuh bermula " -"dengan skema seperti \"https://\"." - -msgid "redirect" -msgstr "lencongan" - -msgid "redirects" -msgstr "lencongan-lencongan" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo index 8eadeb0..239487c 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.po index 363e0ff..73337b7 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/nn/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-11-11 10:05+0000\n" -"Last-Translator: Sivert Olstad\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -19,29 +18,29 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Redirects" -msgstr "Omadresseringar" +msgstr "" msgid "site" -msgstr "nettstad" +msgstr "" msgid "redirect from" msgstr "omadresser frå" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"Dette bør vere ein fullstendig sti utan domenenavn. Døme: “/hendingar/finn/”." +"Dette bør vere ein fullstendig sti utan domenenavn. Døme: '/hendingar/finn/" msgid "redirect to" msgstr "omadresser til" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" "Dette kan enten vere ein fullstendig sti (som over), eller ei fullstendig " -"nettadresse som startar med eit skjema som “https://”." +"nettadresse som startar med 'http://'" msgid "redirect" msgstr "omadressering" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo index 113b2eb..def3045 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.po index 9afa71c..035b945 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/pl/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-09-06 21:50+0000\n" +"PO-Revision-Date: 2021-04-01 19:12+0000\n" "Last-Translator: m_aciek <maciej.olko@gmail.com>\n" "Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" "MIME-Version: 1.0\n" @@ -48,7 +48,7 @@ msgstr "" "się schematem takim jak \"https://\"." msgid "redirect" -msgstr "przekierowanie" +msgstr "przekieruj" msgid "redirects" msgstr "przekierowania" diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo index 4fe1e0c..92ad19f 100644 Binary files a/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.po index 45af9d1..0517b05 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/redirects/locale/sk/LC_MESSAGES/django.po @@ -3,22 +3,20 @@ # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 # Martin Tóth <ezimir@gmail.com>, 2017 -# Peter Kuma, 2021 # supowski <supowski@gmail.com>, 2015 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-15 09:00+0100\n" -"PO-Revision-Date: 2021-07-24 20:57+0000\n" -"Last-Translator: Peter Kuma\n" +"POT-Creation-Date: 2015-10-09 17:42+0200\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Martin Tóth <ezimir@gmail.com>\n" "Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sk\n" -"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " -">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgid "Redirects" msgstr "Presmerovania" @@ -30,21 +28,21 @@ msgid "redirect from" msgstr "presmerovať z" msgid "" -"This should be an absolute path, excluding the domain name. Example: “/" -"events/search/”." +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." msgstr "" -"Toto by sa mala byť absolútna cesta bez názvu domény. Napríklad: “/events/" -"search/”." +"Tu by sa mala použiť absolútna cesta bez názvu domény. Napríklad: '/events/" +"search/'." msgid "redirect to" msgstr "presmerovať na" msgid "" -"This can be either an absolute path (as above) or a full URL starting with a " -"scheme such as “https://”." +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." msgstr "" -"Toto môže byť buď absolútna cesta (ako vyššie), alebo úplné URL začínajúce " -"schémou ako “http://”." +"Toto môže byť buď absolútna cesta (ako vyššie) alebo úplné URL začínajúce na " +"'http://'." msgid "redirect" msgstr "presmerovanie" diff --git a/venv/Lib/site-packages/django/contrib/redirects/middleware.py b/venv/Lib/site-packages/django/contrib/redirects/middleware.py index bc4fe9c..26a49f3 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/middleware.py +++ b/venv/Lib/site-packages/django/contrib/redirects/middleware.py @@ -12,8 +12,8 @@ class RedirectFallbackMiddleware(MiddlewareMixin): response_gone_class = HttpResponseGone response_redirect_class = HttpResponsePermanentRedirect - def __init__(self, get_response): - if not apps.is_installed("django.contrib.sites"): + def __init__(self, get_response=None): + if not apps.is_installed('django.contrib.sites'): raise ImproperlyConfigured( "You cannot use RedirectFallbackMiddleware when " "django.contrib.sites is not installed." @@ -33,7 +33,7 @@ class RedirectFallbackMiddleware(MiddlewareMixin): r = Redirect.objects.get(site=current_site, old_path=full_path) except Redirect.DoesNotExist: pass - if r is None and settings.APPEND_SLASH and not request.path.endswith("/"): + if r is None and settings.APPEND_SLASH and not request.path.endswith('/'): try: r = Redirect.objects.get( site=current_site, @@ -42,7 +42,7 @@ class RedirectFallbackMiddleware(MiddlewareMixin): except Redirect.DoesNotExist: pass if r is not None: - if r.new_path == "": + if r.new_path == '': return self.response_gone_class() return self.response_redirect_class(r.new_path) diff --git a/venv/Lib/site-packages/django/contrib/redirects/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/redirects/migrations/0001_initial.py index 3b3b879..baacd1c 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/redirects/migrations/0001_initial.py @@ -4,61 +4,36 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("sites", "0001_initial"), + ('sites', '0001_initial'), ] operations = [ migrations.CreateModel( - name="Redirect", + name='Redirect', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ( - "site", - models.ForeignKey( - to="sites.Site", - on_delete=models.CASCADE, - verbose_name="site", - ), - ), - ( - "old_path", - models.CharField( - help_text=( - "This should be an absolute path, excluding the domain " - "name. Example: “/events/search/”." - ), - max_length=200, - verbose_name="redirect from", - db_index=True, - ), - ), - ( - "new_path", - models.CharField( - help_text=( - "This can be either an absolute path (as above) or a full " - "URL starting with “http://”." - ), - max_length=200, - verbose_name="redirect to", - blank=True, - ), - ), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('site', models.ForeignKey( + to='sites.Site', + to_field='id', + on_delete=models.CASCADE, + verbose_name='site', + )), + ('old_path', models.CharField( + help_text=( + 'This should be an absolute path, excluding the domain name. Example: “/events/search/”.' + ), max_length=200, verbose_name='redirect from', db_index=True + )), + ('new_path', models.CharField( + help_text='This can be either an absolute path (as above) or a full URL starting with “http://”.', + max_length=200, verbose_name='redirect to', blank=True + )), ], options={ - "ordering": ["old_path"], - "unique_together": {("site", "old_path")}, - "db_table": "django_redirect", - "verbose_name": "redirect", - "verbose_name_plural": "redirects", + 'ordering': ['old_path'], + 'unique_together': {('site', 'old_path')}, + 'db_table': 'django_redirect', + 'verbose_name': 'redirect', + 'verbose_name_plural': 'redirects', }, bases=(models.Model,), ), diff --git a/venv/Lib/site-packages/django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py b/venv/Lib/site-packages/django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py index 84f67e1..afbd19e 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py +++ b/venv/Lib/site-packages/django/contrib/redirects/migrations/0002_alter_redirect_new_path_help_text.py @@ -4,21 +4,21 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("redirects", "0001_initial"), + ('redirects', '0001_initial'), ] operations = [ migrations.AlterField( - model_name="redirect", - name="new_path", + model_name='redirect', + name='new_path', field=models.CharField( blank=True, help_text=( - "This can be either an absolute path (as above) or a full " - "URL starting with a scheme such as “https://”." + 'This can be either an absolute path (as above) or a full ' + 'URL starting with a scheme such as “https://”.' ), max_length=200, - verbose_name="redirect to", + verbose_name='redirect to', ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/redirects/models.py b/venv/Lib/site-packages/django/contrib/redirects/models.py index 1552414..a200b88 100644 --- a/venv/Lib/site-packages/django/contrib/redirects/models.py +++ b/venv/Lib/site-packages/django/contrib/redirects/models.py @@ -4,32 +4,29 @@ from django.utils.translation import gettext_lazy as _ class Redirect(models.Model): - site = models.ForeignKey(Site, models.CASCADE, verbose_name=_("site")) + site = models.ForeignKey(Site, models.CASCADE, verbose_name=_('site')) old_path = models.CharField( - _("redirect from"), + _('redirect from'), max_length=200, db_index=True, - help_text=_( - "This should be an absolute path, excluding the domain name. Example: " - "“/events/search/”." - ), + help_text=_('This should be an absolute path, excluding the domain name. Example: “/events/search/”.'), ) new_path = models.CharField( - _("redirect to"), + _('redirect to'), max_length=200, blank=True, help_text=_( - "This can be either an absolute path (as above) or a full URL " - "starting with a scheme such as “https://”." + 'This can be either an absolute path (as above) or a full URL ' + 'starting with a scheme such as “https://”.' ), ) class Meta: - verbose_name = _("redirect") - verbose_name_plural = _("redirects") - db_table = "django_redirect" - unique_together = [["site", "old_path"]] - ordering = ["old_path"] + verbose_name = _('redirect') + verbose_name_plural = _('redirects') + db_table = 'django_redirect' + unique_together = [['site', 'old_path']] + ordering = ['old_path'] def __str__(self): return "%s ---> %s" % (self.old_path, self.new_path) diff --git a/venv/Lib/site-packages/django/contrib/sessions/apps.py b/venv/Lib/site-packages/django/contrib/sessions/apps.py index 83f0cef..8b778d1 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/apps.py +++ b/venv/Lib/site-packages/django/contrib/sessions/apps.py @@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _ class SessionsConfig(AppConfig): - name = "django.contrib.sessions" + name = 'django.contrib.sessions' verbose_name = _("Sessions") diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/base.py b/venv/Lib/site-packages/django/contrib/sessions/backends/base.py index 4b17932..0f06b23 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/base.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/base.py @@ -1,12 +1,20 @@ +import base64 import logging import string +import warnings from datetime import datetime, timedelta from django.conf import settings +from django.contrib.sessions.exceptions import SuspiciousSession from django.core import signing +from django.core.exceptions import SuspiciousOperation from django.utils import timezone -from django.utils.crypto import get_random_string +from django.utils.crypto import ( + constant_time_compare, get_random_string, salted_hmac, +) +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.module_loading import import_string +from django.utils.translation import LANGUAGE_SESSION_KEY # session_key should not be case sensitive because some backends can store it # on case insensitive file systems. @@ -18,7 +26,6 @@ class CreateError(Exception): Used internally as a consistent exception type to catch from save (see the docstring for SessionBase.save() for details). """ - pass @@ -26,7 +33,6 @@ class UpdateError(Exception): """ Occurs if Django tries to update a session that was deleted. """ - pass @@ -34,9 +40,8 @@ class SessionBase: """ Base class for all Session classes. """ - - TEST_COOKIE_NAME = "testcookie" - TEST_COOKIE_VALUE = "worked" + TEST_COOKIE_NAME = 'testcookie' + TEST_COOKIE_VALUE = 'worked' __not_given = object() @@ -50,6 +55,13 @@ class SessionBase: return key in self._session def __getitem__(self, key): + if key == LANGUAGE_SESSION_KEY: + warnings.warn( + 'The user language will no longer be stored in ' + 'request.session in Django 4.0. Read it from ' + 'request.COOKIES[settings.LANGUAGE_COOKIE_NAME] instead.', + RemovedInDjango40Warning, stacklevel=2, + ) return self._session[key] def __setitem__(self, key, value): @@ -62,7 +74,7 @@ class SessionBase: @property def key_salt(self): - return "django.contrib.sessions." + self.__class__.__qualname__ + return 'django.contrib.sessions.' + self.__class__.__qualname__ def get(self, key, default=None): return self._session.get(key, default) @@ -89,28 +101,62 @@ class SessionBase: def delete_test_cookie(self): del self[self.TEST_COOKIE_NAME] + def _hash(self, value): + # RemovedInDjango40Warning: pre-Django 3.1 format will be invalid. + key_salt = "django.contrib.sessions" + self.__class__.__name__ + return salted_hmac(key_salt, value).hexdigest() + def encode(self, session_dict): "Return the given session dictionary serialized and encoded as a string." + # RemovedInDjango40Warning: DEFAULT_HASHING_ALGORITHM will be removed. + if settings.DEFAULT_HASHING_ALGORITHM == 'sha1': + return self._legacy_encode(session_dict) return signing.dumps( - session_dict, - salt=self.key_salt, - serializer=self.serializer, + session_dict, salt=self.key_salt, serializer=self.serializer, compress=True, ) def decode(self, session_data): try: - return signing.loads( - session_data, salt=self.key_salt, serializer=self.serializer - ) + return signing.loads(session_data, salt=self.key_salt, serializer=self.serializer) + # RemovedInDjango40Warning: when the deprecation ends, handle here + # exceptions similar to what _legacy_decode() does now. except signing.BadSignature: - logger = logging.getLogger("django.security.SuspiciousSession") - logger.warning("Session data corrupted") + try: + # Return an empty session if data is not in the pre-Django 3.1 + # format. + return self._legacy_decode(session_data) + except Exception: + logger = logging.getLogger('django.security.SuspiciousSession') + logger.warning('Session data corrupted') + return {} except Exception: - # ValueError, unpickling exceptions. If any of these happen, just - # return an empty dictionary (an empty session). - pass - return {} + return self._legacy_decode(session_data) + + def _legacy_encode(self, session_dict): + # RemovedInDjango40Warning. + serialized = self.serializer().dumps(session_dict) + hash = self._hash(serialized) + return base64.b64encode(hash.encode() + b':' + serialized).decode('ascii') + + def _legacy_decode(self, session_data): + # RemovedInDjango40Warning: pre-Django 3.1 format will be invalid. + encoded_data = base64.b64decode(session_data.encode('ascii')) + try: + # could produce ValueError if there is no ':' + hash, serialized = encoded_data.split(b':', 1) + expected_hash = self._hash(serialized) + if not constant_time_compare(hash.decode(), expected_hash): + raise SuspiciousSession("Session data corrupted") + else: + return self.serializer().loads(serialized) + except Exception as e: + # ValueError, SuspiciousOperation, unpickling exceptions. If any of + # these happen, just return an empty dictionary (an empty session). + if isinstance(e, SuspiciousOperation): + logger = logging.getLogger('django.security.%s' % e.__class__.__name__) + logger.warning(str(e)) + return {} def update(self, dict_): self._session.update(dict_) @@ -204,18 +250,18 @@ class SessionBase: arguments specifying the modification and expiry of the session. """ try: - modification = kwargs["modification"] + modification = kwargs['modification'] except KeyError: modification = timezone.now() # Make the difference between "expiry=None passed in kwargs" and # "expiry not passed in kwargs", in order to guarantee not to trigger # self.load() when expiry is provided. try: - expiry = kwargs["expiry"] + expiry = kwargs['expiry'] except KeyError: - expiry = self.get("_session_expiry") + expiry = self.get('_session_expiry') - if not expiry: # Checks both None and 0 cases + if not expiry: # Checks both None and 0 cases return self.get_session_cookie_age() if not isinstance(expiry, datetime): return expiry @@ -229,14 +275,14 @@ class SessionBase: arguments specifying the modification and expiry of the session. """ try: - modification = kwargs["modification"] + modification = kwargs['modification'] except KeyError: modification = timezone.now() # Same comment as in get_expiry_age try: - expiry = kwargs["expiry"] + expiry = kwargs['expiry'] except KeyError: - expiry = self.get("_session_expiry") + expiry = self.get('_session_expiry') if isinstance(expiry, datetime): return expiry @@ -261,13 +307,13 @@ class SessionBase: if value is None: # Remove any custom expiration for this session. try: - del self["_session_expiry"] + del self['_session_expiry'] except KeyError: pass return if isinstance(value, timedelta): value = timezone.now() + value - self["_session_expiry"] = value + self['_session_expiry'] = value def get_expire_at_browser_close(self): """ @@ -276,9 +322,9 @@ class SessionBase: ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry date/age, if there is one. """ - if self.get("_session_expiry") is None: + if self.get('_session_expiry') is None: return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE - return self.get("_session_expiry") == 0 + return self.get('_session_expiry') == 0 def flush(self): """ @@ -306,9 +352,7 @@ class SessionBase: """ Return True if the given session_key already exists. """ - raise NotImplementedError( - "subclasses of SessionBase must provide an exists() method" - ) + raise NotImplementedError('subclasses of SessionBase must provide an exists() method') def create(self): """ @@ -316,9 +360,7 @@ class SessionBase: a unique key and will have saved the result once (with empty data) before the method returns. """ - raise NotImplementedError( - "subclasses of SessionBase must provide a create() method" - ) + raise NotImplementedError('subclasses of SessionBase must provide a create() method') def save(self, must_create=False): """ @@ -326,26 +368,20 @@ class SessionBase: object (or raise CreateError). Otherwise, only update an existing object and don't create one (raise UpdateError if needed). """ - raise NotImplementedError( - "subclasses of SessionBase must provide a save() method" - ) + raise NotImplementedError('subclasses of SessionBase must provide a save() method') def delete(self, session_key=None): """ Delete the session data under this key. If the key is None, use the current session key value. """ - raise NotImplementedError( - "subclasses of SessionBase must provide a delete() method" - ) + raise NotImplementedError('subclasses of SessionBase must provide a delete() method') def load(self): """ Load the session data and return a dictionary. """ - raise NotImplementedError( - "subclasses of SessionBase must provide a load() method" - ) + raise NotImplementedError('subclasses of SessionBase must provide a load() method') @classmethod def clear_expired(cls): @@ -356,4 +392,4 @@ class SessionBase: NotImplementedError. If it isn't necessary, because the backend has a built-in expiration mechanism, it should be a no-op. """ - raise NotImplementedError("This backend does not support clear_expired().") + raise NotImplementedError('This backend does not support clear_expired().') diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/cache.py b/venv/Lib/site-packages/django/contrib/sessions/backends/cache.py index 0c9d244..860d3a4 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/cache.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/cache.py @@ -1,5 +1,7 @@ from django.conf import settings -from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError +from django.contrib.sessions.backends.base import ( + CreateError, SessionBase, UpdateError, +) from django.core.cache import caches KEY_PREFIX = "django.contrib.sessions.cache" @@ -9,7 +11,6 @@ class SessionStore(SessionBase): """ A cache-based session store. """ - cache_key_prefix = KEY_PREFIX def __init__(self, session_key=None): @@ -48,8 +49,7 @@ class SessionStore(SessionBase): return raise RuntimeError( "Unable to create a new session key. " - "It is likely that the cache is unavailable." - ) + "It is likely that the cache is unavailable.") def save(self, must_create=False): if self.session_key is None: @@ -60,18 +60,14 @@ class SessionStore(SessionBase): func = self._cache.set else: raise UpdateError - result = func( - self.cache_key, - self._get_session(no_load=must_create), - self.get_expiry_age(), - ) + result = func(self.cache_key, + self._get_session(no_load=must_create), + self.get_expiry_age()) if must_create and not result: raise CreateError def exists(self, session_key): - return ( - bool(session_key) and (self.cache_key_prefix + session_key) in self._cache - ) + return bool(session_key) and (self.cache_key_prefix + session_key) in self._cache def delete(self, session_key=None): if session_key is None: diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/cached_db.py b/venv/Lib/site-packages/django/contrib/sessions/backends/cached_db.py index 3125a71..453d390 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/cached_db.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/cached_db.py @@ -13,7 +13,6 @@ class SessionStore(DBStore): """ Implement cached, database backed sessions. """ - cache_key_prefix = KEY_PREFIX def __init__(self, session_key=None): @@ -36,19 +35,13 @@ class SessionStore(DBStore): s = self._get_session_from_db() if s: data = self.decode(s.session_data) - self._cache.set( - self.cache_key, data, self.get_expiry_age(expiry=s.expire_date) - ) + self._cache.set(self.cache_key, data, self.get_expiry_age(expiry=s.expire_date)) else: data = {} return data def exists(self, session_key): - return ( - session_key - and (self.cache_key_prefix + session_key) in self._cache - or super().exists(session_key) - ) + return session_key and (self.cache_key_prefix + session_key) in self._cache or super().exists(session_key) def save(self, must_create=False): super().save(must_create) diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/db.py b/venv/Lib/site-packages/django/contrib/sessions/backends/db.py index e1f6b69..7c905a2 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/db.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/db.py @@ -1,6 +1,8 @@ import logging -from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError +from django.contrib.sessions.backends.base import ( + CreateError, SessionBase, UpdateError, +) from django.core.exceptions import SuspiciousOperation from django.db import DatabaseError, IntegrityError, router, transaction from django.utils import timezone @@ -11,7 +13,6 @@ class SessionStore(SessionBase): """ Implement database session store. """ - def __init__(self, session_key=None): super().__init__(session_key) @@ -20,7 +21,6 @@ class SessionStore(SessionBase): # Avoids a circular import and allows importing SessionStore when # django.contrib.sessions is not in INSTALLED_APPS. from django.contrib.sessions.models import Session - return Session @cached_property @@ -30,11 +30,12 @@ class SessionStore(SessionBase): def _get_session_from_db(self): try: return self.model.objects.get( - session_key=self.session_key, expire_date__gt=timezone.now() + session_key=self.session_key, + expire_date__gt=timezone.now() ) except (self.model.DoesNotExist, SuspiciousOperation) as e: if isinstance(e, SuspiciousOperation): - logger = logging.getLogger("django.security.%s" % e.__class__.__name__) + logger = logging.getLogger('django.security.%s' % e.__class__.__name__) logger.warning(str(e)) self._session_key = None @@ -83,9 +84,7 @@ class SessionStore(SessionBase): using = router.db_for_write(self.model, instance=obj) try: with transaction.atomic(using=using): - obj.save( - force_insert=must_create, force_update=not must_create, using=using - ) + obj.save(force_insert=must_create, force_update=not must_create, using=using) except IntegrityError: if must_create: raise CreateError diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/file.py b/venv/Lib/site-packages/django/contrib/sessions/backends/file.py index 9473883..cc5f93a 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/file.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/file.py @@ -6,10 +6,7 @@ import tempfile from django.conf import settings from django.contrib.sessions.backends.base import ( - VALID_KEY_CHARS, - CreateError, - SessionBase, - UpdateError, + VALID_KEY_CHARS, CreateError, SessionBase, UpdateError, ) from django.contrib.sessions.exceptions import InvalidSessionKey from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation @@ -20,7 +17,6 @@ class SessionStore(SessionBase): """ Implement a file based session store. """ - def __init__(self, session_key=None): self.storage_path = self._get_storage_path() self.file_prefix = settings.SESSION_COOKIE_NAME @@ -31,16 +27,13 @@ class SessionStore(SessionBase): try: return cls._storage_path except AttributeError: - storage_path = ( - getattr(settings, "SESSION_FILE_PATH", None) or tempfile.gettempdir() - ) + storage_path = getattr(settings, 'SESSION_FILE_PATH', None) or tempfile.gettempdir() # Make sure the storage path is valid. if not os.path.isdir(storage_path): raise ImproperlyConfigured( "The session storage path %r doesn't exist. Please set your" " SESSION_FILE_PATH setting to an existing directory in which" - " Django can store session data." % storage_path - ) + " Django can store session data." % storage_path) cls._storage_path = storage_path return storage_path @@ -56,7 +49,8 @@ class SessionStore(SessionBase): # should always be md5s, so they should never contain directory # components. if not set(session_key).issubset(VALID_KEY_CHARS): - raise InvalidSessionKey("Invalid characters in session key") + raise InvalidSessionKey( + "Invalid characters in session key") return os.path.join(self.storage_path, self.file_prefix + session_key) @@ -65,22 +59,23 @@ class SessionStore(SessionBase): Return the modification time of the file storing the session's content. """ modification = os.stat(self._key_to_file()).st_mtime - tz = timezone.utc if settings.USE_TZ else None - return datetime.datetime.fromtimestamp(modification, tz=tz) + if settings.USE_TZ: + modification = datetime.datetime.utcfromtimestamp(modification) + return modification.replace(tzinfo=timezone.utc) + return datetime.datetime.fromtimestamp(modification) def _expiry_date(self, session_data): """ Return the expiry time of the file storing the session's content. """ - return session_data.get("_session_expiry") or ( - self._last_modification() - + datetime.timedelta(seconds=self.get_session_cookie_age()) + return session_data.get('_session_expiry') or ( + self._last_modification() + datetime.timedelta(seconds=self.get_session_cookie_age()) ) def load(self): session_data = {} try: - with open(self._key_to_file(), encoding="ascii") as session_file: + with open(self._key_to_file(), encoding='ascii') as session_file: file_data = session_file.read() # Don't fail if there is no data in the session file. # We may have opened the empty placeholder file. @@ -89,9 +84,7 @@ class SessionStore(SessionBase): session_data = self.decode(file_data) except (EOFError, SuspiciousOperation) as e: if isinstance(e, SuspiciousOperation): - logger = logging.getLogger( - "django.security.%s" % e.__class__.__name__ - ) + logger = logging.getLogger('django.security.%s' % e.__class__.__name__) logger.warning(str(e)) self.create() @@ -127,7 +120,7 @@ class SessionStore(SessionBase): try: # Make sure the file exists. If it does not already exist, an # empty placeholder file is created. - flags = os.O_WRONLY | getattr(os, "O_BINARY", 0) + flags = os.O_WRONLY | getattr(os, 'O_BINARY', 0) if must_create: flags |= os.O_EXCL | os.O_CREAT fd = os.open(session_file_name, flags) @@ -157,9 +150,7 @@ class SessionStore(SessionBase): dir, prefix = os.path.split(session_file_name) try: - output_file_fd, output_file_name = tempfile.mkstemp( - dir=dir, prefix=prefix + "_out_" - ) + output_file_fd, output_file_name = tempfile.mkstemp(dir=dir, prefix=prefix + '_out_') renamed = False try: try: @@ -202,7 +193,7 @@ class SessionStore(SessionBase): for session_file in os.listdir(storage_path): if not session_file.startswith(file_prefix): continue - session_key = session_file[len(file_prefix) :] + session_key = session_file[len(file_prefix):] session = cls(session_key) # When an expired session is loaded, its file is removed, and a # new file is immediately created. Prevent this by disabling diff --git a/venv/Lib/site-packages/django/contrib/sessions/backends/signed_cookies.py b/venv/Lib/site-packages/django/contrib/sessions/backends/signed_cookies.py index dc41c6f..8942df1 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/backends/signed_cookies.py +++ b/venv/Lib/site-packages/django/contrib/sessions/backends/signed_cookies.py @@ -3,6 +3,7 @@ from django.core import signing class SessionStore(SessionBase): + def load(self): """ Load the data from the key itself instead of fetching from some @@ -15,7 +16,7 @@ class SessionStore(SessionBase): serializer=self.serializer, # This doesn't handle non-default expiry dates, see #19201 max_age=self.get_session_cookie_age(), - salt="django.contrib.sessions.backends.signed_cookies", + salt='django.contrib.sessions.backends.signed_cookies', ) except Exception: # BadSignature, ValueError, or unpickling exceptions. If any of @@ -53,7 +54,7 @@ class SessionStore(SessionBase): and set the modified flag so that the cookie is set on the client for the current request. """ - self._session_key = "" + self._session_key = '' self._session_cache = {} self.modified = True @@ -70,9 +71,8 @@ class SessionStore(SessionBase): base64-encoded string of data as our session key. """ return signing.dumps( - self._session, - compress=True, - salt="django.contrib.sessions.backends.signed_cookies", + self._session, compress=True, + salt='django.contrib.sessions.backends.signed_cookies', serializer=self.serializer, ) diff --git a/venv/Lib/site-packages/django/contrib/sessions/base_session.py b/venv/Lib/site-packages/django/contrib/sessions/base_session.py index 603d2fe..1d653b5 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/base_session.py +++ b/venv/Lib/site-packages/django/contrib/sessions/base_session.py @@ -24,16 +24,16 @@ class BaseSessionManager(models.Manager): class AbstractBaseSession(models.Model): - session_key = models.CharField(_("session key"), max_length=40, primary_key=True) - session_data = models.TextField(_("session data")) - expire_date = models.DateTimeField(_("expire date"), db_index=True) + session_key = models.CharField(_('session key'), max_length=40, primary_key=True) + session_data = models.TextField(_('session data')) + expire_date = models.DateTimeField(_('expire date'), db_index=True) objects = BaseSessionManager() class Meta: abstract = True - verbose_name = _("session") - verbose_name_plural = _("sessions") + verbose_name = _('session') + verbose_name_plural = _('sessions') def __str__(self): return self.session_key diff --git a/venv/Lib/site-packages/django/contrib/sessions/exceptions.py b/venv/Lib/site-packages/django/contrib/sessions/exceptions.py index 8a4853c..8dbca3d 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/exceptions.py +++ b/venv/Lib/site-packages/django/contrib/sessions/exceptions.py @@ -3,17 +3,14 @@ from django.core.exceptions import BadRequest, SuspiciousOperation class InvalidSessionKey(SuspiciousOperation): """Invalid characters in session key""" - pass class SuspiciousSession(SuspiciousOperation): """The session may be tampered with""" - pass class SessionInterrupted(BadRequest): """The session was interrupted.""" - pass diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo index 68e4b05..574320d 100644 Binary files a/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.po index aeab9b8..d4187bc 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/bg/LC_MESSAGES/django.po @@ -1,7 +1,6 @@ # This file is distributed under the same license as the Django package. # # Translators: -# arneatec <arneatec@gmail.com>, 2022 # Jannis Leidel <jannis@leidel.info>, 2011 # vestimir <vestimir@gmail.com>, 2014 msgid "" @@ -9,8 +8,8 @@ msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2022-01-14 11:56+0000\n" -"Last-Translator: arneatec <arneatec@gmail.com>\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" "bg/)\n" "MIME-Version: 1.0\n" @@ -29,7 +28,7 @@ msgid "session data" msgstr "данни от сесията" msgid "expire date" -msgstr "дата на изтичане на валидност" +msgstr "дата на валидност" msgid "session" msgstr "сесия" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo index fbc756b..fcc2fc5 100644 Binary files a/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po index da9a8d4..a38346c 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/en_AU/LC_MESSAGES/django.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield <tom@tomfifield.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-04-11 13:15+0000\n" -"Last-Translator: Tom Fifield <tom@tomfifield.net>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" +"PO-Revision-Date: 2014-10-05 20:11+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: English (Australia) (http://www.transifex.com/projects/p/" +"django/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,19 +17,19 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sessions" -msgstr "Sessions" +msgstr "" msgid "session key" -msgstr "session key" +msgstr "" msgid "session data" -msgstr "session data" +msgstr "" msgid "expire date" -msgstr "expire date" +msgstr "" msgid "session" -msgstr "session" +msgstr "" msgid "sessions" -msgstr "sessions" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/fa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/fa/LC_MESSAGES/django.po index 276d2de..5e53fab 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/fa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/fa/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fa\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sessions" msgstr "نشست‌ها" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/he/LC_MESSAGES/django.po index e8bd934..85206d4 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/he/LC_MESSAGES/django.po @@ -15,8 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: he\n" -"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " -"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sessions" msgstr "התחברויות" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/ka/LC_MESSAGES/django.po index bcdcb5f..b42fbf1 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/ka/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sessions" msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/kk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/kk/LC_MESSAGES/django.po index 5777dc2..9fd740b 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/kk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/kk/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kk\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sessions" msgstr "Сессиялар" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo index 23b8948..1240807 100644 Binary files a/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.po index 0811ad8..cf9e7bf 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/kn/LC_MESSAGES/django.po @@ -2,24 +2,23 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Rohith PR, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-08-03 10:50+0000\n" -"Last-Translator: Rohith PR\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Kannada (http://www.transifex.com/django/django/language/" "kn/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sessions" -msgstr "ಅಧಿವೇಶನಗಳು" +msgstr "" msgid "session key" msgstr "ಅಧಿವೇಶನದ ಕೀಲಿಕೈ" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/lt/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/lt/LC_MESSAGES/django.po index 0051e30..5307182 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/lt/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/lt/LC_MESSAGES/django.po @@ -16,9 +16,8 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lt\n" -"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < " -"11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? " -"1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n" +"%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Sessions" msgstr "Sesijos" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index f46c07b..0000000 Binary files a/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 4e3b6aa..0000000 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,35 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-11-16 12:39+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Sessions" -msgstr "Sesi-sesi" - -msgid "session key" -msgstr "Kunci sesi" - -msgid "session data" -msgstr "Data sesi" - -msgid "expire date" -msgstr "Tarikh tamat tempoh" - -msgid "session" -msgstr "sesi" - -msgid "sessions" -msgstr "sesi-sesi" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo index 285d386..eb15625 100644 Binary files a/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.po index 802d36d..d9ac000 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/nn/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-10-26 12:34+0000\n" -"Last-Translator: Sivert Olstad\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -19,7 +18,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sessions" -msgstr "Sesjonar" +msgstr "" msgid "session key" msgstr "sesjonsnøkkel" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/sk/LC_MESSAGES/django.po index ca01c84..84c591a 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/sk/LC_MESSAGES/django.po @@ -15,8 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sk\n" -"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " -">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgid "Sessions" msgstr "Relácie" diff --git a/venv/Lib/site-packages/django/contrib/sessions/locale/uk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sessions/locale/uk/LC_MESSAGES/django.po index befabf0..6a5aef7 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/locale/uk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sessions/locale/uk/LC_MESSAGES/django.po @@ -16,10 +16,8 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: uk\n" -"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " -"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " -"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " -"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Sessions" msgstr "Сесії" diff --git a/venv/Lib/site-packages/django/contrib/sessions/management/commands/clearsessions.py b/venv/Lib/site-packages/django/contrib/sessions/management/commands/clearsessions.py index 899f537..2d62247 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/management/commands/clearsessions.py +++ b/venv/Lib/site-packages/django/contrib/sessions/management/commands/clearsessions.py @@ -7,7 +7,7 @@ from django.core.management.base import BaseCommand, CommandError class Command(BaseCommand): help = ( "Can be run as a cronjob or directly to clean out expired sessions " - "when the backend supports it." + "(only with the database backend at the moment)." ) def handle(self, **options): diff --git a/venv/Lib/site-packages/django/contrib/sessions/middleware.py b/venv/Lib/site-packages/django/contrib/sessions/middleware.py index 2fcd7d5..50627a6 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/middleware.py +++ b/venv/Lib/site-packages/django/contrib/sessions/middleware.py @@ -10,7 +10,9 @@ from django.utils.http import http_date class SessionMiddleware(MiddlewareMixin): - def __init__(self, get_response): + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response): + def __init__(self, get_response=None): super().__init__(get_response) engine = import_module(settings.SESSION_ENGINE) self.SessionStore = engine.SessionStore @@ -40,10 +42,10 @@ class SessionMiddleware(MiddlewareMixin): domain=settings.SESSION_COOKIE_DOMAIN, samesite=settings.SESSION_COOKIE_SAMESITE, ) - patch_vary_headers(response, ("Cookie",)) + patch_vary_headers(response, ('Cookie',)) else: if accessed: - patch_vary_headers(response, ("Cookie",)) + patch_vary_headers(response, ('Cookie',)) if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: if request.session.get_expire_at_browser_close(): max_age = None @@ -65,10 +67,8 @@ class SessionMiddleware(MiddlewareMixin): ) response.set_cookie( settings.SESSION_COOKIE_NAME, - request.session.session_key, - max_age=max_age, - expires=expires, - domain=settings.SESSION_COOKIE_DOMAIN, + request.session.session_key, max_age=max_age, + expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, path=settings.SESSION_COOKIE_PATH, secure=settings.SESSION_COOKIE_SECURE or None, httponly=settings.SESSION_COOKIE_HTTPONLY or None, diff --git a/venv/Lib/site-packages/django/contrib/sessions/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/sessions/migrations/0001_initial.py index 83b0bbc..39eaa6d 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/sessions/migrations/0001_initial.py @@ -4,35 +4,27 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [] + dependencies = [ + ] operations = [ migrations.CreateModel( - name="Session", + name='Session', fields=[ - ( - "session_key", - models.CharField( - max_length=40, - serialize=False, - verbose_name="session key", - primary_key=True, - ), - ), - ("session_data", models.TextField(verbose_name="session data")), - ( - "expire_date", - models.DateTimeField(verbose_name="expire date", db_index=True), - ), + ('session_key', models.CharField( + max_length=40, serialize=False, verbose_name='session key', primary_key=True + )), + ('session_data', models.TextField(verbose_name='session data')), + ('expire_date', models.DateTimeField(verbose_name='expire date', db_index=True)), ], options={ - "abstract": False, - "db_table": "django_session", - "verbose_name": "session", - "verbose_name_plural": "sessions", + 'abstract': False, + 'db_table': 'django_session', + 'verbose_name': 'session', + 'verbose_name_plural': 'sessions', }, managers=[ - ("objects", django.contrib.sessions.models.SessionManager()), + ('objects', django.contrib.sessions.models.SessionManager()), ], ), ] diff --git a/venv/Lib/site-packages/django/contrib/sessions/models.py b/venv/Lib/site-packages/django/contrib/sessions/models.py index e786ab4..a744267 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/models.py +++ b/venv/Lib/site-packages/django/contrib/sessions/models.py @@ -1,4 +1,6 @@ -from django.contrib.sessions.base_session import AbstractBaseSession, BaseSessionManager +from django.contrib.sessions.base_session import ( + AbstractBaseSession, BaseSessionManager, +) class SessionManager(BaseSessionManager): @@ -20,16 +22,14 @@ class Session(AbstractBaseSession): For complete documentation on using Sessions in your code, consult the sessions documentation that is shipped with Django (also available - on the Django web site). + on the Django Web site). """ - objects = SessionManager() @classmethod def get_session_store_class(cls): from django.contrib.sessions.backends.db import SessionStore - return SessionStore class Meta(AbstractBaseSession.Meta): - db_table = "django_session" + db_table = 'django_session' diff --git a/venv/Lib/site-packages/django/contrib/sessions/serializers.py b/venv/Lib/site-packages/django/contrib/sessions/serializers.py index 06b147e..6ac4aab 100644 --- a/venv/Lib/site-packages/django/contrib/sessions/serializers.py +++ b/venv/Lib/site-packages/django/contrib/sessions/serializers.py @@ -1,5 +1,20 @@ -from django.core.serializers.base import PickleSerializer as BasePickleSerializer +import pickle + from django.core.signing import JSONSerializer as BaseJSONSerializer + +class PickleSerializer: + """ + Simple wrapper around pickle to be used in signing.dumps and + signing.loads. + """ + protocol = pickle.HIGHEST_PROTOCOL + + def dumps(self, obj): + return pickle.dumps(obj, self.protocol) + + def loads(self, data): + return pickle.loads(data) + + JSONSerializer = BaseJSONSerializer -PickleSerializer = BasePickleSerializer diff --git a/venv/Lib/site-packages/django/contrib/sitemaps/__init__.py b/venv/Lib/site-packages/django/contrib/sitemaps/__init__.py index 8dc6b58..46a54ec 100644 --- a/venv/Lib/site-packages/django/contrib/sitemaps/__init__.py +++ b/venv/Lib/site-packages/django/contrib/sitemaps/__init__.py @@ -1,4 +1,3 @@ -import warnings from urllib.parse import urlencode from urllib.request import urlopen @@ -8,7 +7,6 @@ from django.core import paginator from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch, reverse from django.utils import translation -from django.utils.deprecation import RemovedInDjango50Warning PING_URL = "https://www.google.com/webmasters/tools/ping" @@ -25,37 +23,32 @@ def ping_google(sitemap_url=None, ping_url=PING_URL, sitemap_uses_https=True): function will attempt to deduce it by using urls.reverse(). """ sitemap_full_url = _get_sitemap_full_url(sitemap_url, sitemap_uses_https) - params = urlencode({"sitemap": sitemap_full_url}) - urlopen("%s?%s" % (ping_url, params)) + params = urlencode({'sitemap': sitemap_full_url}) + urlopen('%s?%s' % (ping_url, params)) def _get_sitemap_full_url(sitemap_url, sitemap_uses_https=True): - if not django_apps.is_installed("django.contrib.sites"): - raise ImproperlyConfigured( - "ping_google requires django.contrib.sites, which isn't installed." - ) + if not django_apps.is_installed('django.contrib.sites'): + raise ImproperlyConfigured("ping_google requires django.contrib.sites, which isn't installed.") if sitemap_url is None: try: # First, try to get the "index" sitemap URL. - sitemap_url = reverse("django.contrib.sitemaps.views.index") + sitemap_url = reverse('django.contrib.sitemaps.views.index') except NoReverseMatch: try: # Next, try for the "global" sitemap URL. - sitemap_url = reverse("django.contrib.sitemaps.views.sitemap") + sitemap_url = reverse('django.contrib.sitemaps.views.sitemap') except NoReverseMatch: pass if sitemap_url is None: - raise SitemapNotFound( - "You didn't provide a sitemap_url, and the sitemap URL couldn't be " - "auto-detected." - ) + raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.") - Site = django_apps.get_model("sites.Site") + Site = django_apps.get_model('sites.Site') current_site = Site.objects.get_current() - scheme = "https" if sitemap_uses_https else "http" - return "%s://%s%s" % (scheme, current_site.domain, sitemap_url) + scheme = 'https' if sitemap_uses_https else 'http' + return '%s://%s%s' % (scheme, current_site.domain, sitemap_url) class Sitemap: @@ -114,8 +107,8 @@ class Sitemap: obj, lang_code = item # Activate language from item-tuple or forced one before calling location. with translation.override(force_lang_code or lang_code): - return self._get("location", item) - return self._get("location", item) + return self._get('location', item) + return self._get('location', item) @property def paginator(self): @@ -129,23 +122,13 @@ class Sitemap: def get_protocol(self, protocol=None): # Determine protocol - if self.protocol is None and protocol is None: - warnings.warn( - "The default sitemap protocol will be changed from 'http' to " - "'https' in Django 5.0. Set Sitemap.protocol to silence this " - "warning.", - category=RemovedInDjango50Warning, - stacklevel=2, - ) - # RemovedInDjango50Warning: when the deprecation ends, replace 'http' - # with 'https'. - return self.protocol or protocol or "http" + return self.protocol or protocol or 'http' def get_domain(self, site=None): # Determine domain if site is None: - if django_apps.is_installed("django.contrib.sites"): - Site = django_apps.get_model("sites.Site") + if django_apps.is_installed('django.contrib.sites'): + Site = django_apps.get_model('sites.Site') try: site = Site.objects.get_current() except Site.DoesNotExist: @@ -169,45 +152,40 @@ class Sitemap: paginator_page = self.paginator.page(page) for item in paginator_page.object_list: - loc = f"{protocol}://{domain}{self._location(item)}" - priority = self._get("priority", item) - lastmod = self._get("lastmod", item) + loc = f'{protocol}://{domain}{self._location(item)}' + priority = self._get('priority', item) + lastmod = self._get('lastmod', item) if all_items_lastmod: all_items_lastmod = lastmod is not None - if all_items_lastmod and ( - latest_lastmod is None or lastmod > latest_lastmod - ): + if (all_items_lastmod and + (latest_lastmod is None or lastmod > latest_lastmod)): latest_lastmod = lastmod url_info = { - "item": item, - "location": loc, - "lastmod": lastmod, - "changefreq": self._get("changefreq", item), - "priority": str(priority if priority is not None else ""), - "alternates": [], + 'item': item, + 'location': loc, + 'lastmod': lastmod, + 'changefreq': self._get('changefreq', item), + 'priority': str(priority if priority is not None else ''), + 'alternates': [], } if self.i18n and self.alternates: for lang_code in self._languages(): - loc = f"{protocol}://{domain}{self._location(item, lang_code)}" - url_info["alternates"].append( - { - "location": loc, - "lang_code": lang_code, - } - ) + loc = f'{protocol}://{domain}{self._location(item, lang_code)}' + url_info['alternates'].append({ + 'location': loc, + 'lang_code': lang_code, + }) if self.x_default: lang_code = settings.LANGUAGE_CODE - loc = f"{protocol}://{domain}{self._location(item, lang_code)}" - loc = loc.replace(f"/{lang_code}/", "/", 1) - url_info["alternates"].append( - { - "location": loc, - "lang_code": "x-default", - } - ) + loc = f'{protocol}://{domain}{self._location(item, lang_code)}' + loc = loc.replace(f'/{lang_code}/', '/', 1) + url_info['alternates'].append({ + 'location': loc, + 'lang_code': 'x-default', + }) urls.append(url_info) @@ -222,8 +200,8 @@ class GenericSitemap(Sitemap): changefreq = None def __init__(self, info_dict, priority=None, changefreq=None, protocol=None): - self.queryset = info_dict["queryset"] - self.date_field = info_dict.get("date_field") + self.queryset = info_dict['queryset'] + self.date_field = info_dict.get('date_field') self.priority = self.priority or priority self.changefreq = self.changefreq or changefreq self.protocol = self.protocol or protocol diff --git a/venv/Lib/site-packages/django/contrib/sitemaps/apps.py b/venv/Lib/site-packages/django/contrib/sitemaps/apps.py index 70c200c..ec795ea 100644 --- a/venv/Lib/site-packages/django/contrib/sitemaps/apps.py +++ b/venv/Lib/site-packages/django/contrib/sitemaps/apps.py @@ -3,6 +3,6 @@ from django.utils.translation import gettext_lazy as _ class SiteMapsConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.sitemaps" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.sitemaps' verbose_name = _("Site Maps") diff --git a/venv/Lib/site-packages/django/contrib/sitemaps/management/commands/ping_google.py b/venv/Lib/site-packages/django/contrib/sitemaps/management/commands/ping_google.py index 3db071e..b2d8f84 100644 --- a/venv/Lib/site-packages/django/contrib/sitemaps/management/commands/ping_google.py +++ b/venv/Lib/site-packages/django/contrib/sitemaps/management/commands/ping_google.py @@ -6,11 +6,11 @@ class Command(BaseCommand): help = "Ping Google with an updated sitemap, pass optional url of sitemap" def add_arguments(self, parser): - parser.add_argument("sitemap_url", nargs="?") - parser.add_argument("--sitemap-uses-http", action="store_true") + parser.add_argument('sitemap_url', nargs='?') + parser.add_argument('--sitemap-uses-http', action='store_true') def handle(self, *args, **options): ping_google( - sitemap_url=options["sitemap_url"], - sitemap_uses_https=not options["sitemap_uses_http"], + sitemap_url=options['sitemap_url'], + sitemap_uses_https=not options['sitemap_uses_http'], ) diff --git a/venv/Lib/site-packages/django/contrib/sitemaps/views.py b/venv/Lib/site-packages/django/contrib/sitemaps/views.py index cd315fa..bffdebb 100644 --- a/venv/Lib/site-packages/django/contrib/sitemaps/views.py +++ b/venv/Lib/site-packages/django/contrib/sitemaps/views.py @@ -1,4 +1,5 @@ import datetime +from calendar import timegm from functools import wraps from django.contrib.sites.shortcuts import get_current_site @@ -6,7 +7,6 @@ from django.core.paginator import EmptyPage, PageNotAnInteger from django.http import Http404 from django.template.response import TemplateResponse from django.urls import reverse -from django.utils import timezone from django.utils.http import http_date @@ -14,20 +14,15 @@ def x_robots_tag(func): @wraps(func) def inner(request, *args, **kwargs): response = func(request, *args, **kwargs) - response.headers["X-Robots-Tag"] = "noindex, noodp, noarchive" + response.headers['X-Robots-Tag'] = 'noindex, noodp, noarchive' return response - return inner @x_robots_tag -def index( - request, - sitemaps, - template_name="sitemap_index.xml", - content_type="application/xml", - sitemap_url_name="django.contrib.sitemaps.views.sitemap", -): +def index(request, sitemaps, + template_name='sitemap_index.xml', content_type='application/xml', + sitemap_url_name='django.contrib.sitemaps.views.sitemap'): req_protocol = request.scheme req_site = get_current_site(request) @@ -39,26 +34,20 @@ def index( if callable(site): site = site() protocol = req_protocol if site.protocol is None else site.protocol - sitemap_url = reverse(sitemap_url_name, kwargs={"section": section}) - absolute_url = "%s://%s%s" % (protocol, req_site.domain, sitemap_url) + sitemap_url = reverse(sitemap_url_name, kwargs={'section': section}) + absolute_url = '%s://%s%s' % (protocol, req_site.domain, sitemap_url) sites.append(absolute_url) # Add links to all pages of the sitemap. for page in range(2, site.paginator.num_pages + 1): - sites.append("%s?p=%s" % (absolute_url, page)) + sites.append('%s?p=%s' % (absolute_url, page)) - return TemplateResponse( - request, template_name, {"sitemaps": sites}, content_type=content_type - ) + return TemplateResponse(request, template_name, {'sitemaps': sites}, + content_type=content_type) @x_robots_tag -def sitemap( - request, - sitemaps, - section=None, - template_name="sitemap.xml", - content_type="application/xml", -): +def sitemap(request, sitemaps, section=None, + template_name='sitemap.xml', content_type='application/xml'): req_protocol = request.scheme req_site = get_current_site(request) @@ -78,30 +67,26 @@ def sitemap( try: if callable(site): site = site() - urls.extend(site.get_urls(page=page, site=req_site, protocol=req_protocol)) + urls.extend(site.get_urls(page=page, site=req_site, + protocol=req_protocol)) if all_sites_lastmod: - site_lastmod = getattr(site, "latest_lastmod", None) + site_lastmod = getattr(site, 'latest_lastmod', None) if site_lastmod is not None: - if not isinstance(site_lastmod, datetime.datetime): - site_lastmod = datetime.datetime.combine( - site_lastmod, datetime.time.min - ) - if timezone.is_naive(site_lastmod): - site_lastmod = timezone.make_aware(site_lastmod, timezone.utc) - lastmod = ( - site_lastmod if lastmod is None else max(lastmod, site_lastmod) + site_lastmod = ( + site_lastmod.utctimetuple() if isinstance(site_lastmod, datetime.datetime) + else site_lastmod.timetuple() ) + lastmod = site_lastmod if lastmod is None else max(lastmod, site_lastmod) else: all_sites_lastmod = False except EmptyPage: raise Http404("Page %s empty" % page) except PageNotAnInteger: raise Http404("No page '%s'" % page) - response = TemplateResponse( - request, template_name, {"urlset": urls}, content_type=content_type - ) + response = TemplateResponse(request, template_name, {'urlset': urls}, + content_type=content_type) if all_sites_lastmod and lastmod is not None: # if lastmod is defined for all sites, set header so as # ConditionalGetMiddleware is able to send 304 NOT MODIFIED - response.headers["Last-Modified"] = http_date(lastmod.timestamp()) + response.headers['Last-Modified'] = http_date(timegm(lastmod)) return response diff --git a/venv/Lib/site-packages/django/contrib/sites/admin.py b/venv/Lib/site-packages/django/contrib/sites/admin.py index 53ad53d..2b167fe 100644 --- a/venv/Lib/site-packages/django/contrib/sites/admin.py +++ b/venv/Lib/site-packages/django/contrib/sites/admin.py @@ -4,5 +4,5 @@ from django.contrib.sites.models import Site @admin.register(Site) class SiteAdmin(admin.ModelAdmin): - list_display = ("domain", "name") - search_fields = ("domain", "name") + list_display = ('domain', 'name') + search_fields = ('domain', 'name') diff --git a/venv/Lib/site-packages/django/contrib/sites/apps.py b/venv/Lib/site-packages/django/contrib/sites/apps.py index ac51a84..7f820dc 100644 --- a/venv/Lib/site-packages/django/contrib/sites/apps.py +++ b/venv/Lib/site-packages/django/contrib/sites/apps.py @@ -8,8 +8,8 @@ from .management import create_default_site class SitesConfig(AppConfig): - default_auto_field = "django.db.models.AutoField" - name = "django.contrib.sites" + default_auto_field = 'django.db.models.AutoField' + name = 'django.contrib.sites' verbose_name = _("Sites") def ready(self): diff --git a/venv/Lib/site-packages/django/contrib/sites/checks.py b/venv/Lib/site-packages/django/contrib/sites/checks.py index 6db039f..c7dfe9e 100644 --- a/venv/Lib/site-packages/django/contrib/sites/checks.py +++ b/venv/Lib/site-packages/django/contrib/sites/checks.py @@ -3,10 +3,11 @@ from django.core.checks import Error def check_site_id(app_configs, **kwargs): - if hasattr(settings, "SITE_ID") and not isinstance( - settings.SITE_ID, (type(None), int) + if ( + hasattr(settings, 'SITE_ID') and + not isinstance(settings.SITE_ID, (type(None), int)) ): return [ - Error("The SITE_ID setting must be an integer", id="sites.E101"), + Error('The SITE_ID setting must be an integer', id='sites.E101'), ] return [] diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo index e6e4917..fcc2fc5 100644 Binary files a/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po index 0659ec5..747882c 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/en_AU/LC_MESSAGES/django.po @@ -1,16 +1,15 @@ # This file is distributed under the same license as the Django package. # # Translators: -# Tom Fifield <tom@tomfifield.net>, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-04-11 13:15+0000\n" -"Last-Translator: Tom Fifield <tom@tomfifield.net>\n" -"Language-Team: English (Australia) (http://www.transifex.com/django/django/" -"language/en_AU/)\n" +"PO-Revision-Date: 2014-10-05 20:11+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: English (Australia) (http://www.transifex.com/projects/p/" +"django/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,19 +17,19 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sites" -msgstr "Sites" +msgstr "" msgid "The domain name cannot contain any spaces or tabs." -msgstr "The domain name cannot contain any spaces or tabs." +msgstr "" msgid "domain name" -msgstr "domain name" +msgstr "" msgid "display name" -msgstr "display name" +msgstr "" msgid "site" -msgstr "site" +msgstr "" msgid "sites" -msgstr "sites" +msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/fa/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/fa/LC_MESSAGES/django.po index ba91492..4aab223 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/fa/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/fa/LC_MESSAGES/django.po @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fa\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sites" msgstr "وب‌گاه‌ها" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/he/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/he/LC_MESSAGES/django.po index 62de695..6bc277d 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/he/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/he/LC_MESSAGES/django.po @@ -15,8 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: he\n" -"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " -"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sites" msgstr "אתרים" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/ka/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/ka/LC_MESSAGES/django.po index 5d05019..4a2ac86 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/ka/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/ka/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sites" msgstr "საიტები" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/kk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/kk/LC_MESSAGES/django.po index 23c0f00..b986095 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/kk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/kk/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kk\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sites" msgstr "Сайттар" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.mo index 0d41465..234ed90 100644 Binary files a/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.po index 8ff543a..1f9b3b9 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/kn/LC_MESSAGES/django.po @@ -2,24 +2,23 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Rohith PR, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-08-03 10:54+0000\n" -"Last-Translator: Rohith PR\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Kannada (http://www.transifex.com/django/django/language/" "kn/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: kn\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=1; plural=0;\n" msgid "Sites" -msgstr "ತಾಣಗಳು" +msgstr "" msgid "The domain name cannot contain any spaces or tabs." msgstr "" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/lt/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/lt/LC_MESSAGES/django.po index afcb5ac..f919c3b 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/lt/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/lt/LC_MESSAGES/django.po @@ -18,9 +18,8 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lt\n" -"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < " -"11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? " -"1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n" +"%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Sites" msgstr "Tinklalapiai" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.mo deleted file mode 100644 index 57289a7..0000000 Binary files a/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.mo and /dev/null differ diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.po deleted file mode 100644 index 250a2da..0000000 --- a/venv/Lib/site-packages/django/contrib/sites/locale/ms/LC_MESSAGES/django.po +++ /dev/null @@ -1,35 +0,0 @@ -# This file is distributed under the same license as the Django package. -# -# Translators: -# Jafry Hisham, 2021 -msgid "" -msgstr "" -"Project-Id-Version: django\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-11-16 12:40+0000\n" -"Last-Translator: Jafry Hisham\n" -"Language-Team: Malay (http://www.transifex.com/django/django/language/ms/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: ms\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -msgid "Sites" -msgstr "Laman-laman" - -msgid "The domain name cannot contain any spaces or tabs." -msgstr "Nama domain tidak boleh mengandungi ruang kosong atau tab." - -msgid "domain name" -msgstr "nama domain" - -msgid "display name" -msgstr "nama paparan" - -msgid "site" -msgstr "laman" - -msgid "sites" -msgstr "laman-laman" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.mo index 9f44459..f5e3f7e 100644 Binary files a/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.mo and b/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.po index 22e6ec6..c433ef2 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/nn/LC_MESSAGES/django.po @@ -2,14 +2,13 @@ # # Translators: # Jannis Leidel <jannis@leidel.info>, 2011 -# Sivert Olstad, 2021 msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-17 11:07+0100\n" -"PO-Revision-Date: 2021-11-11 00:23+0000\n" -"Last-Translator: Sivert Olstad\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django/django/" "language/nn/)\n" "MIME-Version: 1.0\n" @@ -19,10 +18,10 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Sites" -msgstr "Nettstader" +msgstr "" msgid "The domain name cannot contain any spaces or tabs." -msgstr "Domenenamnet kan ikkje innehalde mellomrom." +msgstr "" msgid "domain name" msgstr "domenenamn" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/sk/LC_MESSAGES/django.po index 0f48ec9..9b3f318 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/sk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/sk/LC_MESSAGES/django.po @@ -16,8 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sk\n" -"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " -">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgid "Sites" msgstr "Sídla" diff --git a/venv/Lib/site-packages/django/contrib/sites/locale/uk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django/contrib/sites/locale/uk/LC_MESSAGES/django.po index f98454c..232940a 100644 --- a/venv/Lib/site-packages/django/contrib/sites/locale/uk/LC_MESSAGES/django.po +++ b/venv/Lib/site-packages/django/contrib/sites/locale/uk/LC_MESSAGES/django.po @@ -17,10 +17,8 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: uk\n" -"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " -"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " -"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " -"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Sites" msgstr "Сайти" diff --git a/venv/Lib/site-packages/django/contrib/sites/management.py b/venv/Lib/site-packages/django/contrib/sites/management.py index dd75bc1..3426233 100644 --- a/venv/Lib/site-packages/django/contrib/sites/management.py +++ b/venv/Lib/site-packages/django/contrib/sites/management.py @@ -8,16 +8,9 @@ from django.core.management.color import no_style from django.db import DEFAULT_DB_ALIAS, connections, router -def create_default_site( - app_config, - verbosity=2, - interactive=True, - using=DEFAULT_DB_ALIAS, - apps=global_apps, - **kwargs, -): +def create_default_site(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs): try: - Site = apps.get_model("sites", "Site") + Site = apps.get_model('sites', 'Site') except LookupError: return @@ -32,9 +25,7 @@ def create_default_site( # can also crop up outside of tests - see #15346. if verbosity >= 2: print("Creating example.com Site object") - Site( - pk=getattr(settings, "SITE_ID", 1), domain="example.com", name="example.com" - ).save(using=using) + Site(pk=getattr(settings, 'SITE_ID', 1), domain="example.com", name="example.com").save(using=using) # We set an explicit pk instead of relying on auto-incrementation, # so we need to reset the database sequence. See #17415. diff --git a/venv/Lib/site-packages/django/contrib/sites/managers.py b/venv/Lib/site-packages/django/contrib/sites/managers.py index ec28826..91c034e 100644 --- a/venv/Lib/site-packages/django/contrib/sites/managers.py +++ b/venv/Lib/site-packages/django/contrib/sites/managers.py @@ -25,41 +25,36 @@ class CurrentSiteManager(models.Manager): except FieldDoesNotExist: return [ checks.Error( - "CurrentSiteManager could not find a field named '%s'." - % field_name, + "CurrentSiteManager could not find a field named '%s'." % field_name, obj=self, - id="sites.E001", + id='sites.E001', ) ] if not field.many_to_many and not isinstance(field, (models.ForeignKey)): return [ checks.Error( - "CurrentSiteManager cannot use '%s.%s' as it is not a foreign key " - "or a many-to-many field." - % (self.model._meta.object_name, field_name), + "CurrentSiteManager cannot use '%s.%s' as it is not a foreign key or a many-to-many field." % ( + self.model._meta.object_name, field_name + ), obj=self, - id="sites.E002", + id='sites.E002', ) ] return [] def _get_field_name(self): - """Return self.__field_name or 'site' or 'sites'.""" + """ Return self.__field_name or 'site' or 'sites'. """ if not self.__field_name: try: - self.model._meta.get_field("site") + self.model._meta.get_field('site') except FieldDoesNotExist: - self.__field_name = "sites" + self.__field_name = 'sites' else: - self.__field_name = "site" + self.__field_name = 'site' return self.__field_name def get_queryset(self): - return ( - super() - .get_queryset() - .filter(**{self._get_field_name() + "__id": settings.SITE_ID}) - ) + return super().get_queryset().filter(**{self._get_field_name() + '__id': settings.SITE_ID}) diff --git a/venv/Lib/site-packages/django/contrib/sites/migrations/0001_initial.py b/venv/Lib/site-packages/django/contrib/sites/migrations/0001_initial.py index 181cf47..9b26190 100644 --- a/venv/Lib/site-packages/django/contrib/sites/migrations/0001_initial.py +++ b/venv/Lib/site-packages/django/contrib/sites/migrations/0001_initial.py @@ -9,36 +9,23 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name="Site", + name='Site', fields=[ - ( - "id", - models.AutoField( - verbose_name="ID", - serialize=False, - auto_created=True, - primary_key=True, - ), - ), - ( - "domain", - models.CharField( - max_length=100, - verbose_name="domain name", - validators=[_simple_domain_name_validator], - ), - ), - ("name", models.CharField(max_length=50, verbose_name="display name")), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('domain', models.CharField( + max_length=100, verbose_name='domain name', validators=[_simple_domain_name_validator] + )), + ('name', models.CharField(max_length=50, verbose_name='display name')), ], options={ - "ordering": ["domain"], - "db_table": "django_site", - "verbose_name": "site", - "verbose_name_plural": "sites", + 'ordering': ['domain'], + 'db_table': 'django_site', + 'verbose_name': 'site', + 'verbose_name_plural': 'sites', }, bases=(models.Model,), managers=[ - ("objects", django.contrib.sites.models.SiteManager()), + ('objects', django.contrib.sites.models.SiteManager()), ], ), ] diff --git a/venv/Lib/site-packages/django/contrib/sites/migrations/0002_alter_domain_unique.py b/venv/Lib/site-packages/django/contrib/sites/migrations/0002_alter_domain_unique.py index ccc7bfc..6a26ebc 100644 --- a/venv/Lib/site-packages/django/contrib/sites/migrations/0002_alter_domain_unique.py +++ b/venv/Lib/site-packages/django/contrib/sites/migrations/0002_alter_domain_unique.py @@ -5,18 +5,16 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("sites", "0001_initial"), + ('sites', '0001_initial'), ] operations = [ migrations.AlterField( - model_name="site", - name="domain", + model_name='site', + name='domain', field=models.CharField( - max_length=100, - unique=True, - validators=[django.contrib.sites.models._simple_domain_name_validator], - verbose_name="domain name", + max_length=100, unique=True, validators=[django.contrib.sites.models._simple_domain_name_validator], + verbose_name='domain name' ), ), ] diff --git a/venv/Lib/site-packages/django/contrib/sites/models.py b/venv/Lib/site-packages/django/contrib/sites/models.py index e1544f1..3dc4c25 100644 --- a/venv/Lib/site-packages/django/contrib/sites/models.py +++ b/venv/Lib/site-packages/django/contrib/sites/models.py @@ -18,7 +18,7 @@ def _simple_domain_name_validator(value): if any(checks): raise ValidationError( _("The domain name cannot contain any spaces or tabs."), - code="invalid", + code='invalid', ) @@ -53,15 +53,14 @@ class SiteManager(models.Manager): retrieved from the database. """ from django.conf import settings - - if getattr(settings, "SITE_ID", ""): + if getattr(settings, 'SITE_ID', ''): site_id = settings.SITE_ID return self._get_site_by_id(site_id) elif request: return self._get_site_by_request(request) raise ImproperlyConfigured( - 'You\'re using the Django "sites framework" without having ' + "You're using the Django \"sites framework\" without having " "set the SITE_ID setting. Create a site in your database and " "set the SITE_ID setting or pass a request to " "Site.objects.get_current() to fix this error." @@ -79,20 +78,20 @@ class SiteManager(models.Manager): class Site(models.Model): domain = models.CharField( - _("domain name"), + _('domain name'), max_length=100, validators=[_simple_domain_name_validator], unique=True, ) - name = models.CharField(_("display name"), max_length=50) + name = models.CharField(_('display name'), max_length=50) objects = SiteManager() class Meta: - db_table = "django_site" - verbose_name = _("site") - verbose_name_plural = _("sites") - ordering = ["domain"] + db_table = 'django_site' + verbose_name = _('site') + verbose_name_plural = _('sites') + ordering = ['domain'] def __str__(self): return self.domain @@ -105,8 +104,8 @@ def clear_site_cache(sender, **kwargs): """ Clear the cache (if primed) each time a site is saved or deleted. """ - instance = kwargs["instance"] - using = kwargs["using"] + instance = kwargs['instance'] + using = kwargs['using'] try: del SITE_CACHE[instance.pk] except KeyError: diff --git a/venv/Lib/site-packages/django/contrib/sites/requests.py b/venv/Lib/site-packages/django/contrib/sites/requests.py index a0c9c18..f6f0270 100644 --- a/venv/Lib/site-packages/django/contrib/sites/requests.py +++ b/venv/Lib/site-packages/django/contrib/sites/requests.py @@ -6,7 +6,6 @@ class RequestSite: The save() and delete() methods raise NotImplementedError. """ - def __init__(self, request): self.domain = self.name = request.get_host() @@ -14,7 +13,7 @@ class RequestSite: return self.domain def save(self, force_insert=False, force_update=False): - raise NotImplementedError("RequestSite cannot be saved.") + raise NotImplementedError('RequestSite cannot be saved.') def delete(self): - raise NotImplementedError("RequestSite cannot be deleted.") + raise NotImplementedError('RequestSite cannot be deleted.') diff --git a/venv/Lib/site-packages/django/contrib/sites/shortcuts.py b/venv/Lib/site-packages/django/contrib/sites/shortcuts.py index ed232e1..1a2ee5c 100644 --- a/venv/Lib/site-packages/django/contrib/sites/shortcuts.py +++ b/venv/Lib/site-packages/django/contrib/sites/shortcuts.py @@ -8,11 +8,9 @@ def get_current_site(request): """ # Imports are inside the function because its point is to avoid importing # the Site models when django.contrib.sites isn't installed. - if apps.is_installed("django.contrib.sites"): + if apps.is_installed('django.contrib.sites'): from .models import Site - return Site.objects.get_current(request) else: from .requests import RequestSite - return RequestSite(request) diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/apps.py b/venv/Lib/site-packages/django/contrib/staticfiles/apps.py index 67acf04..2fbc829 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/apps.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/apps.py @@ -5,9 +5,9 @@ from django.utils.translation import gettext_lazy as _ class StaticFilesConfig(AppConfig): - name = "django.contrib.staticfiles" + name = 'django.contrib.staticfiles' verbose_name = _("Static Files") - ignore_patterns = ["CVS", ".*", "*~"] + ignore_patterns = ['CVS', '.*', '*~'] def ready(self): checks.register(check_finders, checks.Tags.staticfiles) diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/finders.py b/venv/Lib/site-packages/django/contrib/staticfiles/finders.py index 184d297..7f75af3 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/finders.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/finders.py @@ -4,9 +4,11 @@ import os from django.apps import apps from django.conf import settings from django.contrib.staticfiles import utils -from django.core.checks import Error, Warning +from django.core.checks import Error from django.core.exceptions import ImproperlyConfigured -from django.core.files.storage import FileSystemStorage, Storage, default_storage +from django.core.files.storage import ( + FileSystemStorage, Storage, default_storage, +) from django.utils._os import safe_join from django.utils.functional import LazyObject, empty from django.utils.module_loading import import_string @@ -19,11 +21,10 @@ class BaseFinder: """ A base file finder to be used for custom staticfiles finder classes. """ - def check(self, **kwargs): raise NotImplementedError( - "subclasses may provide a check() method to verify the finder is " - "configured correctly." + 'subclasses may provide a check() method to verify the finder is ' + 'configured correctly.' ) def find(self, path, all=False): @@ -33,18 +34,14 @@ class BaseFinder: If the ``all`` parameter is False (default) return only the first found file path; if True, return a list of all found files paths. """ - raise NotImplementedError( - "subclasses of BaseFinder must provide a find() method" - ) + raise NotImplementedError('subclasses of BaseFinder must provide a find() method') def list(self, ignore_patterns): """ Given an optional list of paths to ignore, return a two item iterable consisting of the relative path and storage instance. """ - raise NotImplementedError( - "subclasses of BaseFinder must provide a list() method" - ) + raise NotImplementedError('subclasses of BaseFinder must provide a list() method') class FileSystemFinder(BaseFinder): @@ -52,7 +49,6 @@ class FileSystemFinder(BaseFinder): A static files finder that uses the ``STATICFILES_DIRS`` setting to locate files. """ - def __init__(self, app_names=None, *args, **kwargs): # List of locations with static files self.locations = [] @@ -62,7 +58,7 @@ class FileSystemFinder(BaseFinder): if isinstance(root, (list, tuple)): prefix, root = root else: - prefix = "" + prefix = '' if (prefix, root) not in self.locations: self.locations.append((prefix, root)) for prefix, root in self.locations: @@ -74,43 +70,26 @@ class FileSystemFinder(BaseFinder): def check(self, **kwargs): errors = [] if not isinstance(settings.STATICFILES_DIRS, (list, tuple)): - errors.append( - Error( - "The STATICFILES_DIRS setting is not a tuple or list.", - hint="Perhaps you forgot a trailing comma?", - id="staticfiles.E001", - ) - ) - return errors + errors.append(Error( + 'The STATICFILES_DIRS setting is not a tuple or list.', + hint='Perhaps you forgot a trailing comma?', + id='staticfiles.E001', + )) for root in settings.STATICFILES_DIRS: if isinstance(root, (list, tuple)): prefix, root = root - if prefix.endswith("/"): - errors.append( - Error( - "The prefix %r in the STATICFILES_DIRS setting must " - "not end with a slash." % prefix, - id="staticfiles.E003", - ) - ) - if settings.STATIC_ROOT and os.path.abspath( - settings.STATIC_ROOT - ) == os.path.abspath(root): - errors.append( - Error( - "The STATICFILES_DIRS setting should not contain the " - "STATIC_ROOT setting.", - id="staticfiles.E002", - ) - ) - if not os.path.isdir(root): - errors.append( - Warning( - f"The directory '{root}' in the STATICFILES_DIRS setting " - f"does not exist.", - id="staticfiles.W004", - ) - ) + if prefix.endswith('/'): + errors.append(Error( + 'The prefix %r in the STATICFILES_DIRS setting must ' + 'not end with a slash.' % prefix, + id='staticfiles.E003', + )) + if settings.STATIC_ROOT and os.path.abspath(settings.STATIC_ROOT) == os.path.abspath(root): + errors.append(Error( + 'The STATICFILES_DIRS setting should not contain the ' + 'STATIC_ROOT setting.', + id='staticfiles.E002', + )) return errors def find(self, path, all=False): @@ -134,10 +113,10 @@ class FileSystemFinder(BaseFinder): absolute path (or ``None`` if no match). """ if prefix: - prefix = "%s%s" % (prefix, os.sep) + prefix = '%s%s' % (prefix, os.sep) if not path.startswith(prefix): return None - path = path[len(prefix) :] + path = path[len(prefix):] path = safe_join(root, path) if os.path.exists(path): return path @@ -147,11 +126,9 @@ class FileSystemFinder(BaseFinder): List all files in all locations. """ for prefix, root in self.locations: - # Skip nonexistent directories. - if os.path.isdir(root): - storage = self.storages[root] - for path in utils.get_files(storage, ignore_patterns): - yield path, storage + storage = self.storages[root] + for path in utils.get_files(storage, ignore_patterns): + yield path, storage class AppDirectoriesFinder(BaseFinder): @@ -159,9 +136,8 @@ class AppDirectoriesFinder(BaseFinder): A static files finder that looks in the directory of each app as specified in the source_dir attribute. """ - storage_class = FileSystemStorage - source_dir = "static" + source_dir = 'static' def __init__(self, app_names=None, *args, **kwargs): # The list of apps that are handled @@ -174,8 +150,7 @@ class AppDirectoriesFinder(BaseFinder): app_configs = [ac for ac in app_configs if ac.name in app_names] for app_config in app_configs: app_storage = self.storage_class( - os.path.join(app_config.path, self.source_dir) - ) + os.path.join(app_config.path, self.source_dir)) if os.path.isdir(app_storage.location): self.storages[app_config.name] = app_storage if app_config.name not in self.apps: @@ -187,7 +162,7 @@ class AppDirectoriesFinder(BaseFinder): List all files in all app storages. """ for storage in self.storages.values(): - if storage.exists(""): # check if storage location exists + if storage.exists(''): # check if storage location exists for path in utils.get_files(storage, ignore_patterns): yield path, storage @@ -224,18 +199,15 @@ class BaseStorageFinder(BaseFinder): A base static files finder to be used to extended with an own storage class. """ - storage = None def __init__(self, storage=None, *args, **kwargs): if storage is not None: self.storage = storage if self.storage is None: - raise ImproperlyConfigured( - "The staticfiles storage finder %r " - "doesn't have a storage class " - "assigned." % self.__class__ - ) + raise ImproperlyConfigured("The staticfiles storage finder %r " + "doesn't have a storage class " + "assigned." % self.__class__) # Make sure we have a storage instance here. if not isinstance(self.storage, (Storage, LazyObject)): self.storage = self.storage() @@ -246,7 +218,7 @@ class BaseStorageFinder(BaseFinder): Look for files in the default file storage, if it's local. """ try: - self.storage.path("") + self.storage.path('') except NotImplementedError: pass else: @@ -271,18 +243,15 @@ class DefaultStorageFinder(BaseStorageFinder): """ A static files finder that uses the default storage backend. """ - storage = default_storage def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - base_location = getattr(self.storage, "base_location", empty) + base_location = getattr(self.storage, 'base_location', empty) if not base_location: - raise ImproperlyConfigured( - "The storage backend of the " - "staticfiles finder %r doesn't have " - "a valid location." % self.__class__ - ) + raise ImproperlyConfigured("The storage backend of the " + "staticfiles finder %r doesn't have " + "a valid location." % self.__class__) def find(path, all=False): @@ -320,7 +289,6 @@ def get_finder(import_path): """ Finder = import_string(import_path) if not issubclass(Finder, BaseFinder): - raise ImproperlyConfigured( - 'Finder "%s" is not a subclass of "%s"' % (Finder, BaseFinder) - ) + raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' % + (Finder, BaseFinder)) return Finder() diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/handlers.py b/venv/Lib/site-packages/django/contrib/staticfiles/handlers.py index da53149..44fef07 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/handlers.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/handlers.py @@ -16,7 +16,6 @@ class StaticFilesHandlerMixin: """ Common methods used by WSGI and ASGI handlers. """ - # May be used to differentiate between handler types (e.g. in a # request_finished signal) handles_files = True @@ -42,7 +41,7 @@ class StaticFilesHandlerMixin: """ Return the relative path to the media file on disk for the given URL. """ - relative_url = url[len(self.base_url[2]) :] + relative_url = url[len(self.base_url[2]):] return url2pathname(relative_url) def serve(self, request): @@ -59,9 +58,7 @@ class StaticFilesHandlerMixin: try: return await sync_to_async(self.serve, thread_sensitive=False)(request) except Http404 as e: - return await sync_to_async(response_for_exception, thread_sensitive=False)( - request, e - ) + return await sync_to_async(response_for_exception, thread_sensitive=False)(request, e) class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler): @@ -69,7 +66,6 @@ class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler): WSGI middleware that intercepts calls to the static files directory, as defined by the STATIC_URL setting, and serves those files. """ - def __init__(self, application): self.application = application self.base_url = urlparse(self.get_base_url()) @@ -86,14 +82,13 @@ class ASGIStaticFilesHandler(StaticFilesHandlerMixin, ASGIHandler): ASGI application which wraps another and intercepts requests for static files, passing them off to Django's static file serving. """ - def __init__(self, application): self.application = application self.base_url = urlparse(self.get_base_url()) async def __call__(self, scope, receive, send): # Only even look at HTTP requests - if scope["type"] == "http" and self._should_handle(scope["path"]): + if scope['type'] == 'http' and self._should_handle(scope['path']): # Serve static content # (the one thing super() doesn't do is __call__, apparently) return await super().__call__(scope, receive, send) diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py index c346038..f7e069b 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py @@ -15,7 +15,6 @@ class Command(BaseCommand): Copies or symlinks static files from different locations to the settings.STATIC_ROOT. """ - help = "Collect static files in a single location." requires_system_checks = [Tags.staticfiles] @@ -31,78 +30,58 @@ class Command(BaseCommand): @cached_property def local(self): try: - self.storage.path("") + self.storage.path('') except NotImplementedError: return False return True def add_arguments(self, parser): parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", + '--noinput', '--no-input', action='store_false', dest='interactive', help="Do NOT prompt the user for input of any kind.", ) parser.add_argument( - "--no-post-process", - action="store_false", - dest="post_process", + '--no-post-process', action='store_false', dest='post_process', help="Do NOT post process collected files.", ) parser.add_argument( - "-i", - "--ignore", - action="append", - default=[], - dest="ignore_patterns", - metavar="PATTERN", + '-i', '--ignore', action='append', default=[], + dest='ignore_patterns', metavar='PATTERN', help="Ignore files or directories matching this glob-style " - "pattern. Use multiple times to ignore more.", + "pattern. Use multiple times to ignore more.", ) parser.add_argument( - "-n", - "--dry-run", - action="store_true", + '-n', '--dry-run', action='store_true', help="Do everything except modify the filesystem.", ) parser.add_argument( - "-c", - "--clear", - action="store_true", + '-c', '--clear', action='store_true', help="Clear the existing files using the storage " - "before trying to copy or link the original file.", + "before trying to copy or link the original file.", ) parser.add_argument( - "-l", - "--link", - action="store_true", + '-l', '--link', action='store_true', help="Create a symbolic link to each file instead of copying.", ) parser.add_argument( - "--no-default-ignore", - action="store_false", - dest="use_default_ignore_patterns", - help=( - "Don't ignore the common private glob-style patterns (defaults to " - "'CVS', '.*' and '*~')." - ), + '--no-default-ignore', action='store_false', dest='use_default_ignore_patterns', + help="Don't ignore the common private glob-style patterns (defaults to 'CVS', '.*' and '*~').", ) def set_options(self, **options): """ Set instance variables based on an options dict """ - self.interactive = options["interactive"] - self.verbosity = options["verbosity"] - self.symlink = options["link"] - self.clear = options["clear"] - self.dry_run = options["dry_run"] - ignore_patterns = options["ignore_patterns"] - if options["use_default_ignore_patterns"]: - ignore_patterns += apps.get_app_config("staticfiles").ignore_patterns + self.interactive = options['interactive'] + self.verbosity = options['verbosity'] + self.symlink = options['link'] + self.clear = options['clear'] + self.dry_run = options['dry_run'] + ignore_patterns = options['ignore_patterns'] + if options['use_default_ignore_patterns']: + ignore_patterns += apps.get_app_config('staticfiles').ignore_patterns self.ignore_patterns = list({os.path.normpath(p) for p in ignore_patterns}) - self.post_process = options["post_process"] + self.post_process = options['post_process'] def collect(self): """ @@ -114,7 +93,7 @@ class Command(BaseCommand): raise CommandError("Can't symlink to a remote destination.") if self.clear: - self.clear_dir("") + self.clear_dir('') if self.symlink: handler = self.link_file @@ -125,7 +104,7 @@ class Command(BaseCommand): for finder in get_finders(): for path, storage in finder.list(self.ignore_patterns): # Prefix the relative path if the source storage contains it - if getattr(storage, "prefix", None): + if getattr(storage, 'prefix', None): prefixed_path = os.path.join(storage.prefix, path) else: prefixed_path = path @@ -143,8 +122,9 @@ class Command(BaseCommand): ) # Storage backends may define a post_process() method. - if self.post_process and hasattr(self.storage, "post_process"): - processor = self.storage.post_process(found_files, dry_run=self.dry_run) + if self.post_process and hasattr(self.storage, 'post_process'): + processor = self.storage.post_process(found_files, + dry_run=self.dry_run) for original_path, processed_path, processed in processor: if isinstance(processed, Exception): self.stderr.write("Post-processing '%s' failed!" % original_path) @@ -153,85 +133,75 @@ class Command(BaseCommand): self.stderr.write() raise processed if processed: - self.log( - "Post-processed '%s' as '%s'" % (original_path, processed_path), - level=2, - ) + self.log("Post-processed '%s' as '%s'" % + (original_path, processed_path), level=2) self.post_processed_files.append(original_path) else: self.log("Skipped post-processing '%s'" % original_path) return { - "modified": self.copied_files + self.symlinked_files, - "unmodified": self.unmodified_files, - "post_processed": self.post_processed_files, + 'modified': self.copied_files + self.symlinked_files, + 'unmodified': self.unmodified_files, + 'post_processed': self.post_processed_files, } def handle(self, **options): self.set_options(**options) - message = ["\n"] + message = ['\n'] if self.dry_run: message.append( - "You have activated the --dry-run option so no files will be " - "modified.\n\n" + 'You have activated the --dry-run option so no files will be modified.\n\n' ) message.append( - "You have requested to collect static files at the destination\n" - "location as specified in your settings" + 'You have requested to collect static files at the destination\n' + 'location as specified in your settings' ) if self.is_local_storage() and self.storage.location: destination_path = self.storage.location - message.append(":\n\n %s\n\n" % destination_path) - should_warn_user = self.storage.exists(destination_path) and any( - self.storage.listdir(destination_path) + message.append(':\n\n %s\n\n' % destination_path) + should_warn_user = ( + self.storage.exists(destination_path) and + any(self.storage.listdir(destination_path)) ) else: destination_path = None - message.append(".\n\n") + message.append('.\n\n') # Destination files existence not checked; play it safe and warn. should_warn_user = True if self.interactive and should_warn_user: if self.clear: - message.append("This will DELETE ALL FILES in this location!\n") + message.append('This will DELETE ALL FILES in this location!\n') else: - message.append("This will overwrite existing files!\n") + message.append('This will overwrite existing files!\n') message.append( - "Are you sure you want to do this?\n\n" + 'Are you sure you want to do this?\n\n' "Type 'yes' to continue, or 'no' to cancel: " ) - if input("".join(message)) != "yes": + if input(''.join(message)) != 'yes': raise CommandError("Collecting static files cancelled.") collected = self.collect() if self.verbosity >= 1: - modified_count = len(collected["modified"]) - unmodified_count = len(collected["unmodified"]) - post_processed_count = len(collected["post_processed"]) + modified_count = len(collected['modified']) + unmodified_count = len(collected['unmodified']) + post_processed_count = len(collected['post_processed']) return ( "\n%(modified_count)s %(identifier)s %(action)s" "%(destination)s%(unmodified)s%(post_processed)s." ) % { - "modified_count": modified_count, - "identifier": "static file" + ("" if modified_count == 1 else "s"), - "action": "symlinked" if self.symlink else "copied", - "destination": ( - " to '%s'" % destination_path if destination_path else "" - ), - "unmodified": ( - ", %s unmodified" % unmodified_count - if collected["unmodified"] - else "" - ), - "post_processed": ( - collected["post_processed"] - and ", %s post-processed" % post_processed_count - or "" - ), + 'modified_count': modified_count, + 'identifier': 'static file' + ('' if modified_count == 1 else 's'), + 'action': 'symlinked' if self.symlink else 'copied', + 'destination': (" to '%s'" % destination_path if destination_path else ''), + 'unmodified': (', %s unmodified' % unmodified_count if collected['unmodified'] else ''), + 'post_processed': (collected['post_processed'] and + ', %s post-processed' + % post_processed_count or ''), } def log(self, msg, level=2): @@ -298,17 +268,16 @@ class Command(BaseCommand): # previous collectstatic was with --link), the old # links/files must be deleted so it's not safe to skip # unmodified files. - can_skip_unmodified_files = not ( - self.symlink ^ os.path.islink(full_path) - ) + can_skip_unmodified_files = not (self.symlink ^ os.path.islink(full_path)) else: # In remote storages, skipping is only based on the # modified times since symlinks aren't relevant. can_skip_unmodified_files = True # Avoid sub-second precision (see #14665, #19540) - file_is_unmodified = target_last_modified.replace( - microsecond=0 - ) >= source_last_modified.replace(microsecond=0) + file_is_unmodified = ( + target_last_modified.replace(microsecond=0) >= + source_last_modified.replace(microsecond=0) + ) if file_is_unmodified and can_skip_unmodified_files: if prefixed_path not in self.unmodified_files: self.unmodified_files.append(prefixed_path) @@ -345,13 +314,14 @@ class Command(BaseCommand): if os.path.lexists(full_path): os.unlink(full_path) os.symlink(source_path, full_path) + except AttributeError: + import platform + raise CommandError("Symlinking is not supported by Python %s." % + platform.python_version()) except NotImplementedError: import platform - - raise CommandError( - "Symlinking is not supported in this " - "platform (%s)." % platform.platform() - ) + raise CommandError("Symlinking is not supported in this " + "platform (%s)." % platform.platform()) except OSError as e: raise CommandError(e) if prefixed_path not in self.symlinked_files: diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/findstatic.py b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/findstatic.py index 97413a6..fe3b53c 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/findstatic.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/findstatic.py @@ -6,43 +6,38 @@ from django.core.management.base import LabelCommand class Command(LabelCommand): help = "Finds the absolute paths for the given static file(s)." - label = "staticfile" + label = 'staticfile' def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - "--first", - action="store_false", - dest="all", + '--first', action='store_false', dest='all', help="Only return the first match for each static file.", ) def handle_label(self, path, **options): - verbosity = options["verbosity"] - result = finders.find(path, all=options["all"]) + verbosity = options['verbosity'] + result = finders.find(path, all=options['all']) if verbosity >= 2: searched_locations = ( - "\nLooking in the following locations:\n %s" - % "\n ".join([str(loc) for loc in finders.searched_locations]) + "\nLooking in the following locations:\n %s" % + "\n ".join([str(loc) for loc in finders.searched_locations]) ) else: - searched_locations = "" + searched_locations = '' if result: if not isinstance(result, (list, tuple)): result = [result] result = (os.path.realpath(path) for path in result) if verbosity >= 1: - file_list = "\n ".join(result) - return "Found '%s' here:\n %s%s" % ( - path, - file_list, - searched_locations, - ) + file_list = '\n '.join(result) + return ("Found '%s' here:\n %s%s" % + (path, file_list, searched_locations)) else: - return "\n".join(result) + return '\n'.join(result) else: message = ["No matching file found for '%s'." % path] if verbosity >= 2: message.append(searched_locations) if verbosity >= 1: - self.stderr.write("\n".join(message)) + self.stderr.write('\n'.join(message)) diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/runserver.py b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/runserver.py index fd9ddb1..fe050f4 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/runserver.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/management/commands/runserver.py @@ -1,26 +1,22 @@ from django.conf import settings from django.contrib.staticfiles.handlers import StaticFilesHandler -from django.core.management.commands.runserver import Command as RunserverCommand +from django.core.management.commands.runserver import ( + Command as RunserverCommand, +) class Command(RunserverCommand): - help = ( - "Starts a lightweight web server for development and also serves static files." - ) + help = "Starts a lightweight Web server for development and also serves static files." def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - "--nostatic", - action="store_false", - dest="use_static_handler", - help="Tells Django to NOT automatically serve static files at STATIC_URL.", + '--nostatic', action="store_false", dest='use_static_handler', + help='Tells Django to NOT automatically serve static files at STATIC_URL.', ) parser.add_argument( - "--insecure", - action="store_true", - dest="insecure_serving", - help="Allows serving static files even if DEBUG is False.", + '--insecure', action="store_true", dest='insecure_serving', + help='Allows serving static files even if DEBUG is False.', ) def get_handler(self, *args, **options): @@ -29,8 +25,8 @@ class Command(RunserverCommand): if static files should be served. Otherwise return the default handler. """ handler = super().get_handler(*args, **options) - use_static_handler = options["use_static_handler"] - insecure_serving = options["insecure_serving"] + use_static_handler = options['use_static_handler'] + insecure_serving = options['insecure_serving'] if use_static_handler and (settings.DEBUG or insecure_serving): return StaticFilesHandler(handler) return handler diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/storage.py b/venv/Lib/site-packages/django/contrib/staticfiles/storage.py index 38d2dfb..494890c 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/storage.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/storage.py @@ -20,7 +20,6 @@ class StaticFilesStorage(FileSystemStorage): The defaults for ``location`` and ``base_url`` are ``STATIC_ROOT`` and ``STATIC_URL``. """ - def __init__(self, location=None, base_url=None, *args, **kwargs): if location is None: location = settings.STATIC_ROOT @@ -36,37 +35,20 @@ class StaticFilesStorage(FileSystemStorage): def path(self, name): if not self.location: - raise ImproperlyConfigured( - "You're using the staticfiles app " - "without having set the STATIC_ROOT " - "setting to a filesystem path." - ) + raise ImproperlyConfigured("You're using the staticfiles app " + "without having set the STATIC_ROOT " + "setting to a filesystem path.") return super().path(name) class HashedFilesMixin: - default_template = """url("%(url)s")""" + default_template = """url("%s")""" max_post_process_passes = 5 patterns = ( - ( - "*.css", - ( - r"""(?P<matched>url\(['"]{0,1}\s*(?P<url>.*?)["']{0,1}\))""", - ( - r"""(?P<matched>@import\s*["']\s*(?P<url>.*?)["'])""", - """@import url("%(url)s")""", - ), - ), - ), - ( - "*.js", - ( - ( - r"(?m)(?P<matched>)^(//# (?-i:sourceMappingURL)=(?P<url>.*))$", - "//# sourceMappingURL=%(url)s", - ), - ), - ), + ("*.css", ( + r"""(url\(['"]{0,1}\s*(.*?)["']{0,1}\))""", + (r"""(@import\s*["']\s*(.*?)["'])""", """@import url("%s")"""), + )), ) keep_intermediate_files = True @@ -103,9 +85,7 @@ class HashedFilesMixin: opened = content is None if opened: if not self.exists(filename): - raise ValueError( - "The file '%s' could not be found with %r." % (filename, self) - ) + raise ValueError("The file '%s' could not be found with %r." % (filename, self)) try: content = self.open(filename) except OSError: @@ -118,14 +98,15 @@ class HashedFilesMixin: content.close() path, filename = os.path.split(clean_name) root, ext = os.path.splitext(filename) - file_hash = (".%s" % file_hash) if file_hash else "" - hashed_name = os.path.join(path, "%s%s%s" % (root, file_hash, ext)) + file_hash = ('.%s' % file_hash) if file_hash else '' + hashed_name = os.path.join(path, "%s%s%s" % + (root, file_hash, ext)) unparsed_name = list(parsed_name) unparsed_name[2] = hashed_name # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax - if "?#" in name and not unparsed_name[3]: - unparsed_name[2] += "?" + if '?#' in name and not unparsed_name[3]: + unparsed_name[2] += '?' return urlunsplit(unparsed_name) def _url(self, hashed_name_func, name, force=False, hashed_files=None): @@ -133,10 +114,10 @@ class HashedFilesMixin: Return the non-hashed URL in DEBUG mode. """ if settings.DEBUG and not force: - hashed_name, fragment = name, "" + hashed_name, fragment = name, '' else: clean_name, fragment = urldefrag(name) - if urlsplit(clean_name).path.endswith("/"): # don't hash paths + if urlsplit(clean_name).path.endswith('/'): # don't hash paths hashed_name = name else: args = (clean_name,) @@ -148,13 +129,13 @@ class HashedFilesMixin: # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax - query_fragment = "?#" in name # [sic!] + query_fragment = '?#' in name # [sic!] if fragment or query_fragment: urlparts = list(urlsplit(final_url)) if fragment and not urlparts[4]: urlparts[4] = fragment if query_fragment and not urlparts[3]: - urlparts[2] += "?" + urlparts[2] += '?' final_url = urlunsplit(urlparts) return unquote(final_url) @@ -179,50 +160,43 @@ class HashedFilesMixin: This requires figuring out which files the matched URL resolves to and calling the url() method of the storage. """ - matches = matchobj.groupdict() - matched = matches["matched"] - url = matches["url"] + matched, url = matchobj.groups() # Ignore absolute/protocol-relative and data-uri URLs. - if re.match(r"^[a-z]+:", url): + if re.match(r'^[a-z]+:', url): return matched # Ignore absolute URLs that don't point to a static file (dynamic # CSS / JS?). Note that STATIC_URL cannot be empty. - if url.startswith("/") and not url.startswith(settings.STATIC_URL): + if url.startswith('/') and not url.startswith(settings.STATIC_URL): return matched # Strip off the fragment so a path-like fragment won't interfere. url_path, fragment = urldefrag(url) - if url_path.startswith("/"): + if url_path.startswith('/'): # Otherwise the condition above would have returned prematurely. assert url_path.startswith(settings.STATIC_URL) - target_name = url_path[len(settings.STATIC_URL) :] + target_name = url_path[len(settings.STATIC_URL):] else: # We're using the posixpath module to mix paths and URLs conveniently. - source_name = name if os.sep == "/" else name.replace(os.sep, "/") + source_name = name if os.sep == '/' else name.replace(os.sep, '/') target_name = posixpath.join(posixpath.dirname(source_name), url_path) # Determine the hashed name of the target file with the storage backend. hashed_url = self._url( - self._stored_name, - unquote(target_name), - force=True, - hashed_files=hashed_files, + self._stored_name, unquote(target_name), + force=True, hashed_files=hashed_files, ) - transformed_url = "/".join( - url_path.split("/")[:-1] + hashed_url.split("/")[-1:] - ) + transformed_url = '/'.join(url_path.split('/')[:-1] + hashed_url.split('/')[-1:]) # Restore the fragment that was stripped off earlier. if fragment: - transformed_url += ("?#" if "?#" in url else "#") + fragment + transformed_url += ('?#' if '?#' in url else '#') + fragment # Return the hashed version to the file - matches["url"] = unquote(transformed_url) - return template % matches + return template % unquote(transformed_url) return converter @@ -249,46 +223,31 @@ class HashedFilesMixin: # build a list of adjustable files adjustable_paths = [ - path for path in paths if matches_patterns(path, self._patterns) + path for path in paths + if matches_patterns(path, self._patterns) ] - - # Adjustable files to yield at end, keyed by the original path. - processed_adjustable_paths = {} - - # Do a single pass first. Post-process all files once, yielding not - # adjustable files and exceptions, and collecting adjustable files. - for name, hashed_name, processed, _ in self._post_process( - paths, adjustable_paths, hashed_files - ): - if name not in adjustable_paths or isinstance(processed, Exception): - yield name, hashed_name, processed - else: - processed_adjustable_paths[name] = (name, hashed_name, processed) + # Do a single pass first. Post-process all files once, then repeat for + # adjustable files. + for name, hashed_name, processed, _ in self._post_process(paths, adjustable_paths, hashed_files): + yield name, hashed_name, processed paths = {path: paths[path] for path in adjustable_paths} - substitutions = False for i in range(self.max_post_process_passes): substitutions = False - for name, hashed_name, processed, subst in self._post_process( - paths, adjustable_paths, hashed_files - ): - # Overwrite since hashed_name may be newer. - processed_adjustable_paths[name] = (name, hashed_name, processed) + for name, hashed_name, processed, subst in self._post_process(paths, adjustable_paths, hashed_files): + yield name, hashed_name, processed substitutions = substitutions or subst if not substitutions: break if substitutions: - yield "All", None, RuntimeError("Max post-process passes exceeded.") + yield 'All', None, RuntimeError('Max post-process passes exceeded.') # Store the processed paths self.hashed_files.update(hashed_files) - # Yield adjustable files with final, hashed name. - yield from processed_adjustable_paths.values() - def _post_process(self, paths, adjustable_paths, hashed_files): # Sort the files by directory level def path_level(name): @@ -311,7 +270,7 @@ class HashedFilesMixin: hashed_name = hashed_files[hash_key] # then get the original's file content.. - if hasattr(original_file, "seek"): + if hasattr(original_file, 'seek'): original_file.seek(0) hashed_file_exists = self.exists(hashed_name) @@ -320,13 +279,11 @@ class HashedFilesMixin: # ..to apply each replacement pattern to the content if name in adjustable_paths: old_hashed_name = hashed_name - content = original_file.read().decode("utf-8") + content = original_file.read().decode('utf-8') for extension, patterns in self._patterns.items(): if matches_patterns(path, (extension,)): for pattern, template in patterns: - converter = self.url_converter( - name, hashed_files, template - ) + converter = self.url_converter(name, hashed_files, template) try: content = pattern.sub(converter, content) except ValueError as exc: @@ -364,7 +321,7 @@ class HashedFilesMixin: yield name, hashed_name, processed, substitutions def clean_name(self, name): - return name.replace("\\", "/") + return name.replace('\\', '/') def hash_key(self, name): return name @@ -406,21 +363,18 @@ class HashedFilesMixin: class ManifestFilesMixin(HashedFilesMixin): - manifest_version = "1.0" # the manifest format standard - manifest_name = "staticfiles.json" + manifest_version = '1.0' # the manifest format standard + manifest_name = 'staticfiles.json' manifest_strict = True keep_intermediate_files = False - def __init__(self, *args, manifest_storage=None, **kwargs): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if manifest_storage is None: - manifest_storage = self - self.manifest_storage = manifest_storage self.hashed_files = self.load_manifest() def read_manifest(self): try: - with self.manifest_storage.open(self.manifest_name) as manifest: + with self.open(self.manifest_name) as manifest: return manifest.read().decode() except FileNotFoundError: return None @@ -434,26 +388,24 @@ class ManifestFilesMixin(HashedFilesMixin): except json.JSONDecodeError: pass else: - version = stored.get("version") - if version == "1.0": - return stored.get("paths", {}) - raise ValueError( - "Couldn't load manifest '%s' (version %s)" - % (self.manifest_name, self.manifest_version) - ) + version = stored.get('version') + if version == '1.0': + return stored.get('paths', {}) + raise ValueError("Couldn't load manifest '%s' (version %s)" % + (self.manifest_name, self.manifest_version)) def post_process(self, *args, **kwargs): self.hashed_files = {} yield from super().post_process(*args, **kwargs) - if not kwargs.get("dry_run"): + if not kwargs.get('dry_run'): self.save_manifest() def save_manifest(self): - payload = {"paths": self.hashed_files, "version": self.manifest_version} - if self.manifest_storage.exists(self.manifest_name): - self.manifest_storage.delete(self.manifest_name) + payload = {'paths': self.hashed_files, 'version': self.manifest_version} + if self.exists(self.manifest_name): + self.delete(self.manifest_name) contents = json.dumps(payload).encode() - self.manifest_storage._save(self.manifest_name, ContentFile(contents)) + self._save(self.manifest_name, ContentFile(contents)) def stored_name(self, name): parsed_name = urlsplit(unquote(name)) @@ -462,16 +414,14 @@ class ManifestFilesMixin(HashedFilesMixin): cache_name = self.hashed_files.get(hash_key) if cache_name is None: if self.manifest_strict: - raise ValueError( - "Missing staticfiles manifest entry for '%s'" % clean_name - ) + raise ValueError("Missing staticfiles manifest entry for '%s'" % clean_name) cache_name = self.clean_name(self.hashed_name(name)) unparsed_name = list(parsed_name) unparsed_name[2] = cache_name # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax - if "?#" in name and not unparsed_name[3]: - unparsed_name[2] += "?" + if '?#' in name and not unparsed_name[3]: + unparsed_name[2] += '?' return urlunsplit(unparsed_name) @@ -480,7 +430,6 @@ class ManifestStaticFilesStorage(ManifestFilesMixin, StaticFilesStorage): A static file system storage backend which also saves hashed copies of the files it saves. """ - pass diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/utils.py b/venv/Lib/site-packages/django/contrib/staticfiles/utils.py index efd67ac..5c0a85a 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/utils.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/utils.py @@ -13,7 +13,7 @@ def matches_patterns(path, patterns): return any(fnmatch.fnmatchcase(path, pattern) for pattern in patterns) -def get_files(storage, ignore_patterns=None, location=""): +def get_files(storage, ignore_patterns=None, location=''): """ Recursively walk the storage directories yielding the paths of all files that should be copied. @@ -48,24 +48,16 @@ def check_settings(base_url=None): if not base_url: raise ImproperlyConfigured( "You're using the staticfiles app " - "without having set the required STATIC_URL setting." - ) + "without having set the required STATIC_URL setting.") if settings.MEDIA_URL == base_url: - raise ImproperlyConfigured( - "The MEDIA_URL and STATIC_URL settings must have different values" - ) - if ( - settings.DEBUG - and settings.MEDIA_URL - and settings.STATIC_URL - and settings.MEDIA_URL.startswith(settings.STATIC_URL) - ): + raise ImproperlyConfigured("The MEDIA_URL and STATIC_URL " + "settings must have different values") + if (settings.DEBUG and settings.MEDIA_URL and settings.STATIC_URL and + settings.MEDIA_URL.startswith(settings.STATIC_URL)): raise ImproperlyConfigured( "runserver can't serve media if MEDIA_URL is within STATIC_URL." ) - if (settings.MEDIA_ROOT and settings.STATIC_ROOT) and ( - settings.MEDIA_ROOT == settings.STATIC_ROOT - ): - raise ImproperlyConfigured( - "The MEDIA_ROOT and STATIC_ROOT settings must have different values" - ) + if ((settings.MEDIA_ROOT and settings.STATIC_ROOT) and + (settings.MEDIA_ROOT == settings.STATIC_ROOT)): + raise ImproperlyConfigured("The MEDIA_ROOT and STATIC_ROOT " + "settings must have different values") diff --git a/venv/Lib/site-packages/django/contrib/staticfiles/views.py b/venv/Lib/site-packages/django/contrib/staticfiles/views.py index 83d04d4..6c36e7a 100644 --- a/venv/Lib/site-packages/django/contrib/staticfiles/views.py +++ b/venv/Lib/site-packages/django/contrib/staticfiles/views.py @@ -29,10 +29,10 @@ def serve(request, path, insecure=False, **kwargs): """ if not settings.DEBUG and not insecure: raise Http404 - normalized_path = posixpath.normpath(path).lstrip("/") + normalized_path = posixpath.normpath(path).lstrip('/') absolute_path = finders.find(normalized_path) if not absolute_path: - if path.endswith("/") or path == "": + if path.endswith('/') or path == '': raise Http404("Directory indexes are not allowed here.") raise Http404("'%s' could not be found" % path) document_root, path = os.path.split(absolute_path) diff --git a/venv/Lib/site-packages/django/contrib/syndication/apps.py b/venv/Lib/site-packages/django/contrib/syndication/apps.py index bb0f86a..b3f7c6c 100644 --- a/venv/Lib/site-packages/django/contrib/syndication/apps.py +++ b/venv/Lib/site-packages/django/contrib/syndication/apps.py @@ -3,5 +3,5 @@ from django.utils.translation import gettext_lazy as _ class SyndicationConfig(AppConfig): - name = "django.contrib.syndication" + name = 'django.contrib.syndication' verbose_name = _("Syndication") diff --git a/venv/Lib/site-packages/django/contrib/syndication/views.py b/venv/Lib/site-packages/django/contrib/syndication/views.py index a9d1bff..6d567dd 100644 --- a/venv/Lib/site-packages/django/contrib/syndication/views.py +++ b/venv/Lib/site-packages/django/contrib/syndication/views.py @@ -1,3 +1,5 @@ +from calendar import timegm + from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.http import Http404, HttpResponse @@ -11,12 +13,12 @@ from django.utils.translation import get_language def add_domain(domain, url, secure=False): - protocol = "https" if secure else "http" - if url.startswith("//"): + protocol = 'https' if secure else 'http' + if url.startswith('//'): # Support network-path reference (see #16753) - RSS requires a protocol - url = "%s:%s" % (protocol, url) - elif not url.startswith(("http://", "https://", "mailto:")): - url = iri_to_uri("%s://%s%s" % (protocol, domain, url)) + url = '%s:%s' % (protocol, url) + elif not url.startswith(('http://', 'https://', 'mailto:')): + url = iri_to_uri('%s://%s%s' % (protocol, domain, url)) return url @@ -34,16 +36,15 @@ class Feed: try: obj = self.get_object(request, *args, **kwargs) except ObjectDoesNotExist: - raise Http404("Feed object does not exist.") + raise Http404('Feed object does not exist.') feedgen = self.get_feed(obj, request) response = HttpResponse(content_type=feedgen.content_type) - if hasattr(self, "item_pubdate") or hasattr(self, "item_updateddate"): + if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'): # if item_pubdate or item_updateddate is defined for the feed, set # header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED - response.headers["Last-Modified"] = http_date( - feedgen.latest_post_date().timestamp() - ) - feedgen.write(response, "utf-8") + response.headers['Last-Modified'] = http_date( + timegm(feedgen.latest_post_date().utctimetuple())) + feedgen.write(response, 'utf-8') return response def item_title(self, item): @@ -58,17 +59,17 @@ class Feed: return item.get_absolute_url() except AttributeError: raise ImproperlyConfigured( - "Give your %s class a get_absolute_url() method, or define an " - "item_link() method in your Feed class." % item.__class__.__name__ + 'Give your %s class a get_absolute_url() method, or define an ' + 'item_link() method in your Feed class.' % item.__class__.__name__ ) def item_enclosures(self, item): - enc_url = self._get_dynamic_attr("item_enclosure_url", item) + enc_url = self._get_dynamic_attr('item_enclosure_url', item) if enc_url: enc = feedgenerator.Enclosure( url=str(enc_url), - length=str(self._get_dynamic_attr("item_enclosure_length", item)), - mime_type=str(self._get_dynamic_attr("item_enclosure_mime_type", item)), + length=str(self._get_dynamic_attr('item_enclosure_length', item)), + mime_type=str(self._get_dynamic_attr('item_enclosure_mime_type', item)), ) return [enc] return [] @@ -86,7 +87,7 @@ class Feed: code = attr.__code__ except AttributeError: code = attr.__call__.__code__ - if code.co_argcount == 2: # one argument is 'self' + if code.co_argcount == 2: # one argument is 'self' return attr(obj) else: return attr() @@ -117,7 +118,7 @@ class Feed: Default implementation preserves the old behavior of using {'obj': item, 'site': current_site} as the context. """ - return {"obj": kwargs.get("item"), "site": kwargs.get("site")} + return {'obj': kwargs.get('item'), 'site': kwargs.get('site')} def get_feed(self, obj, request): """ @@ -126,28 +127,28 @@ class Feed: """ current_site = get_current_site(request) - link = self._get_dynamic_attr("link", obj) + link = self._get_dynamic_attr('link', obj) link = add_domain(current_site.domain, link, request.is_secure()) feed = self.feed_type( - title=self._get_dynamic_attr("title", obj), - subtitle=self._get_dynamic_attr("subtitle", obj), + title=self._get_dynamic_attr('title', obj), + subtitle=self._get_dynamic_attr('subtitle', obj), link=link, - description=self._get_dynamic_attr("description", obj), + description=self._get_dynamic_attr('description', obj), language=self.language or get_language(), feed_url=add_domain( current_site.domain, - self._get_dynamic_attr("feed_url", obj) or request.path, + self._get_dynamic_attr('feed_url', obj) or request.path, request.is_secure(), ), - author_name=self._get_dynamic_attr("author_name", obj), - author_link=self._get_dynamic_attr("author_link", obj), - author_email=self._get_dynamic_attr("author_email", obj), - categories=self._get_dynamic_attr("categories", obj), - feed_copyright=self._get_dynamic_attr("feed_copyright", obj), - feed_guid=self._get_dynamic_attr("feed_guid", obj), - ttl=self._get_dynamic_attr("ttl", obj), - **self.feed_extra_kwargs(obj), + author_name=self._get_dynamic_attr('author_name', obj), + author_link=self._get_dynamic_attr('author_link', obj), + author_email=self._get_dynamic_attr('author_email', obj), + categories=self._get_dynamic_attr('categories', obj), + feed_copyright=self._get_dynamic_attr('feed_copyright', obj), + feed_guid=self._get_dynamic_attr('feed_guid', obj), + ttl=self._get_dynamic_attr('ttl', obj), + **self.feed_extra_kwargs(obj) ) title_tmp = None @@ -164,38 +165,37 @@ class Feed: except TemplateDoesNotExist: pass - for item in self._get_dynamic_attr("items", obj): - context = self.get_context_data( - item=item, site=current_site, obj=obj, request=request - ) + for item in self._get_dynamic_attr('items', obj): + context = self.get_context_data(item=item, site=current_site, + obj=obj, request=request) if title_tmp is not None: title = title_tmp.render(context, request) else: - title = self._get_dynamic_attr("item_title", item) + title = self._get_dynamic_attr('item_title', item) if description_tmp is not None: description = description_tmp.render(context, request) else: - description = self._get_dynamic_attr("item_description", item) + description = self._get_dynamic_attr('item_description', item) link = add_domain( current_site.domain, - self._get_dynamic_attr("item_link", item), + self._get_dynamic_attr('item_link', item), request.is_secure(), ) - enclosures = self._get_dynamic_attr("item_enclosures", item) - author_name = self._get_dynamic_attr("item_author_name", item) + enclosures = self._get_dynamic_attr('item_enclosures', item) + author_name = self._get_dynamic_attr('item_author_name', item) if author_name is not None: - author_email = self._get_dynamic_attr("item_author_email", item) - author_link = self._get_dynamic_attr("item_author_link", item) + author_email = self._get_dynamic_attr('item_author_email', item) + author_link = self._get_dynamic_attr('item_author_link', item) else: author_email = author_link = None tz = get_default_timezone() - pubdate = self._get_dynamic_attr("item_pubdate", item) + pubdate = self._get_dynamic_attr('item_pubdate', item) if pubdate and is_naive(pubdate): pubdate = make_aware(pubdate, tz) - updateddate = self._get_dynamic_attr("item_updateddate", item) + updateddate = self._get_dynamic_attr('item_updateddate', item) if updateddate and is_naive(updateddate): updateddate = make_aware(updateddate, tz) @@ -203,19 +203,18 @@ class Feed: title=title, link=link, description=description, - unique_id=self._get_dynamic_attr("item_guid", item, link), + unique_id=self._get_dynamic_attr('item_guid', item, link), unique_id_is_permalink=self._get_dynamic_attr( - "item_guid_is_permalink", item - ), + 'item_guid_is_permalink', item), enclosures=enclosures, pubdate=pubdate, updateddate=updateddate, author_name=author_name, author_email=author_email, author_link=author_link, - comments=self._get_dynamic_attr("item_comments", item), - categories=self._get_dynamic_attr("item_categories", item), - item_copyright=self._get_dynamic_attr("item_copyright", item), - **self.item_extra_kwargs(item), + comments=self._get_dynamic_attr('item_comments', item), + categories=self._get_dynamic_attr('item_categories', item), + item_copyright=self._get_dynamic_attr('item_copyright', item), + **self.item_extra_kwargs(item) ) return feed diff --git a/venv/Lib/site-packages/django/core/cache/__init__.py b/venv/Lib/site-packages/django/core/cache/__init__.py index f09c9ec..a311b50 100644 --- a/venv/Lib/site-packages/django/core/cache/__init__.py +++ b/venv/Lib/site-packages/django/core/cache/__init__.py @@ -14,35 +14,27 @@ See docs/topics/cache.txt for information on the public API. """ from django.core import signals from django.core.cache.backends.base import ( - BaseCache, - CacheKeyWarning, - InvalidCacheBackendError, - InvalidCacheKey, + BaseCache, CacheKeyWarning, InvalidCacheBackendError, InvalidCacheKey, ) from django.utils.connection import BaseConnectionHandler, ConnectionProxy from django.utils.module_loading import import_string __all__ = [ - "cache", - "caches", - "DEFAULT_CACHE_ALIAS", - "InvalidCacheBackendError", - "CacheKeyWarning", - "BaseCache", - "InvalidCacheKey", + 'cache', 'caches', 'DEFAULT_CACHE_ALIAS', 'InvalidCacheBackendError', + 'CacheKeyWarning', 'BaseCache', 'InvalidCacheKey', ] -DEFAULT_CACHE_ALIAS = "default" +DEFAULT_CACHE_ALIAS = 'default' class CacheHandler(BaseConnectionHandler): - settings_name = "CACHES" + settings_name = 'CACHES' exception_class = InvalidCacheBackendError def create_connection(self, alias): params = self.settings[alias].copy() - backend = params.pop("BACKEND") - location = params.pop("LOCATION", "") + backend = params.pop('BACKEND') + location = params.pop('LOCATION', '') try: backend_cls = import_string(backend) except ImportError as e: @@ -53,8 +45,7 @@ class CacheHandler(BaseConnectionHandler): def all(self, initialized_only=False): return [ - self[alias] - for alias in self + self[alias] for alias in self # If initialized_only is True, return only initialized caches. if not initialized_only or hasattr(self._connections, alias) ] diff --git a/venv/Lib/site-packages/django/core/cache/backends/base.py b/venv/Lib/site-packages/django/core/cache/backends/base.py index eb4b3ea..1e2c7c9 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/base.py +++ b/venv/Lib/site-packages/django/core/cache/backends/base.py @@ -2,8 +2,6 @@ import time import warnings -from asgiref.sync import sync_to_async - from django.core.exceptions import ImproperlyConfigured from django.utils.module_loading import import_string @@ -36,7 +34,7 @@ def default_key_func(key, key_prefix, version): the `key_prefix`. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ - return "%s:%s:%s" % (key_prefix, version, key) + return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): @@ -57,7 +55,7 @@ class BaseCache: _missing_key = object() def __init__(self, params): - timeout = params.get("timeout", params.get("TIMEOUT", 300)) + timeout = params.get('timeout', params.get('TIMEOUT', 300)) if timeout is not None: try: timeout = int(timeout) @@ -65,22 +63,22 @@ class BaseCache: timeout = 300 self.default_timeout = timeout - options = params.get("OPTIONS", {}) - max_entries = params.get("max_entries", options.get("MAX_ENTRIES", 300)) + options = params.get('OPTIONS', {}) + max_entries = params.get('max_entries', options.get('MAX_ENTRIES', 300)) try: self._max_entries = int(max_entries) except (ValueError, TypeError): self._max_entries = 300 - cull_frequency = params.get("cull_frequency", options.get("CULL_FREQUENCY", 3)) + cull_frequency = params.get('cull_frequency', options.get('CULL_FREQUENCY', 3)) try: self._cull_frequency = int(cull_frequency) except (ValueError, TypeError): self._cull_frequency = 3 - self.key_prefix = params.get("KEY_PREFIX", "") - self.version = params.get("VERSION", 1) - self.key_func = get_key_func(params.get("KEY_FUNCTION")) + self.key_prefix = params.get('KEY_PREFIX', '') + self.version = params.get('VERSION', 1) + self.key_func = get_key_func(params.get('KEY_FUNCTION')) def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT): """ @@ -107,21 +105,6 @@ class BaseCache: return self.key_func(key, self.key_prefix, version) - def validate_key(self, key): - """ - Warn about keys that would not be portable to the memcached - backend. This encourages (but does not force) writing backend-portable - cache code. - """ - for warning in memcache_key_warnings(key): - warnings.warn(warning, CacheKeyWarning) - - def make_and_validate_key(self, key, version=None): - """Helper to make and validate keys.""" - key = self.make_key(key, version=version) - self.validate_key(key) - return key - def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): """ Set a value in the cache if the key does not already exist. If @@ -130,64 +113,35 @@ class BaseCache: Return True if the value was stored, False otherwise. """ - raise NotImplementedError( - "subclasses of BaseCache must provide an add() method" - ) - - async def aadd(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - return await sync_to_async(self.add, thread_sensitive=True)( - key, value, timeout, version - ) + raise NotImplementedError('subclasses of BaseCache must provide an add() method') def get(self, key, default=None, version=None): """ Fetch a given key from the cache. If the key does not exist, return default, which itself defaults to None. """ - raise NotImplementedError("subclasses of BaseCache must provide a get() method") - - async def aget(self, key, default=None, version=None): - return await sync_to_async(self.get, thread_sensitive=True)( - key, default, version - ) + raise NotImplementedError('subclasses of BaseCache must provide a get() method') def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): """ Set a value in the cache. If timeout is given, use that timeout for the key; otherwise use the default cache timeout. """ - raise NotImplementedError("subclasses of BaseCache must provide a set() method") - - async def aset(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - return await sync_to_async(self.set, thread_sensitive=True)( - key, value, timeout, version - ) + raise NotImplementedError('subclasses of BaseCache must provide a set() method') def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): """ Update the key's expiry time using timeout. Return True if successful or False if the key does not exist. """ - raise NotImplementedError( - "subclasses of BaseCache must provide a touch() method" - ) - - async def atouch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - return await sync_to_async(self.touch, thread_sensitive=True)( - key, timeout, version - ) + raise NotImplementedError('subclasses of BaseCache must provide a touch() method') def delete(self, key, version=None): """ Delete a key from the cache and return whether it succeeded, failing silently. """ - raise NotImplementedError( - "subclasses of BaseCache must provide a delete() method" - ) - - async def adelete(self, key, version=None): - return await sync_to_async(self.delete, thread_sensitive=True)(key, version) + raise NotImplementedError('subclasses of BaseCache must provide a delete() method') def get_many(self, keys, version=None): """ @@ -204,15 +158,6 @@ class BaseCache: d[k] = val return d - async def aget_many(self, keys, version=None): - """See get_many().""" - d = {} - for k in keys: - val = await self.aget(k, self._missing_key, version=version) - if val is not self._missing_key: - d[k] = val - return d - def get_or_set(self, key, default, timeout=DEFAULT_TIMEOUT, version=None): """ Fetch a given key from the cache. If the key does not exist, @@ -232,31 +177,11 @@ class BaseCache: return self.get(key, default, version=version) return val - async def aget_or_set(self, key, default, timeout=DEFAULT_TIMEOUT, version=None): - """See get_or_set().""" - val = await self.aget(key, self._missing_key, version=version) - if val is self._missing_key: - if callable(default): - default = default() - await self.aadd(key, default, timeout=timeout, version=version) - # Fetch the value again to avoid a race condition if another caller - # added a value between the first aget() and the aadd() above. - return await self.aget(key, default, version=version) - return val - def has_key(self, key, version=None): """ Return True if the key is in the cache and has not expired. """ - return ( - self.get(key, self._missing_key, version=version) is not self._missing_key - ) - - async def ahas_key(self, key, version=None): - return ( - await self.aget(key, self._missing_key, version=version) - is not self._missing_key - ) + return self.get(key, self._missing_key, version=version) is not self._missing_key def incr(self, key, delta=1, version=None): """ @@ -270,15 +195,6 @@ class BaseCache: self.set(key, new_value, version=version) return new_value - async def aincr(self, key, delta=1, version=None): - """See incr().""" - value = await self.aget(key, self._missing_key, version=version) - if value is self._missing_key: - raise ValueError("Key '%s' not found" % key) - new_value = value + delta - await self.aset(key, new_value, version=version) - return new_value - def decr(self, key, delta=1, version=None): """ Subtract delta from value in the cache. If the key does not exist, raise @@ -286,9 +202,6 @@ class BaseCache: """ return self.incr(key, -delta, version=version) - async def adecr(self, key, delta=1, version=None): - return await self.aincr(key, -delta, version=version) - def __contains__(self, key): """ Return True if the key is in the cache and has not expired. @@ -314,11 +227,6 @@ class BaseCache: self.set(key, value, timeout=timeout, version=version) return [] - async def aset_many(self, data, timeout=DEFAULT_TIMEOUT, version=None): - for key, value in data.items(): - await self.aset(key, value, timeout=timeout, version=version) - return [] - def delete_many(self, keys, version=None): """ Delete a bunch of values in the cache at once. For certain backends @@ -328,18 +236,18 @@ class BaseCache: for key in keys: self.delete(key, version=version) - async def adelete_many(self, keys, version=None): - for key in keys: - await self.adelete(key, version=version) - def clear(self): """Remove *all* values from the cache at once.""" - raise NotImplementedError( - "subclasses of BaseCache must provide a clear() method" - ) + raise NotImplementedError('subclasses of BaseCache must provide a clear() method') - async def aclear(self): - return await sync_to_async(self.clear, thread_sensitive=True)() + def validate_key(self, key): + """ + Warn about keys that would not be portable to the memcached + backend. This encourages (but does not force) writing backend-portable + cache code. + """ + for warning in memcache_key_warnings(key): + warnings.warn(warning, CacheKeyWarning) def incr_version(self, key, delta=1, version=None): """ @@ -357,19 +265,6 @@ class BaseCache: self.delete(key, version=version) return version + delta - async def aincr_version(self, key, delta=1, version=None): - """See incr_version().""" - if version is None: - version = self.version - - value = await self.aget(key, self._missing_key, version=version) - if value is self._missing_key: - raise ValueError("Key '%s' not found" % key) - - await self.aset(key, value, version=version + delta) - await self.adelete(key, version=version) - return version + delta - def decr_version(self, key, delta=1, version=None): """ Subtract delta from the cache version for the supplied key. Return the @@ -377,27 +272,21 @@ class BaseCache: """ return self.incr_version(key, -delta, version) - async def adecr_version(self, key, delta=1, version=None): - return await self.aincr_version(key, -delta, version) - def close(self, **kwargs): """Close the cache connection""" pass - async def aclose(self, **kwargs): - pass - def memcache_key_warnings(key): if len(key) > MEMCACHE_MAX_KEY_LENGTH: yield ( - "Cache key will cause errors if used with memcached: %r " - "(longer than %s)" % (key, MEMCACHE_MAX_KEY_LENGTH) + 'Cache key will cause errors if used with memcached: %r ' + '(longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH) ) for char in key: if ord(char) < 33 or ord(char) == 127: yield ( - "Cache key contains characters that will cause errors if " - "used with memcached: %r" % key + 'Cache key contains characters that will cause errors if ' + 'used with memcached: %r' % key ) break diff --git a/venv/Lib/site-packages/django/core/cache/backends/db.py b/venv/Lib/site-packages/django/core/cache/backends/db.py index c6bdb45..acbe702 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/db.py +++ b/venv/Lib/site-packages/django/core/cache/backends/db.py @@ -14,14 +14,13 @@ class Options: This allows cache operations to be controlled by the router """ - def __init__(self, table): self.db_table = table - self.app_label = "django_cache" - self.model_name = "cacheentry" - self.verbose_name = "cache entry" - self.verbose_name_plural = "cache entries" - self.object_name = "CacheEntry" + self.app_label = 'django_cache' + self.model_name = 'cacheentry' + self.verbose_name = 'cache entry' + self.verbose_name_plural = 'cache entries' + self.object_name = 'CacheEntry' self.abstract = False self.managed = True self.proxy = False @@ -35,7 +34,6 @@ class BaseDatabaseCache(BaseCache): class CacheEntry: _meta = Options(table) - self.cache_model_class = CacheEntry @@ -56,9 +54,10 @@ class DatabaseCache(BaseDatabaseCache): if not keys: return {} - key_map = { - self.make_and_validate_key(key, version=version): key for key in keys - } + key_map = {} + for key in keys: + self.validate_key(key) + key_map[self.make_key(key, version)] = key db = router.db_for_read(self.cache_model_class) connection = connections[db] @@ -67,14 +66,13 @@ class DatabaseCache(BaseDatabaseCache): with connection.cursor() as cursor: cursor.execute( - "SELECT %s, %s, %s FROM %s WHERE %s IN (%s)" - % ( - quote_name("cache_key"), - quote_name("value"), - quote_name("expires"), + 'SELECT %s, %s, %s FROM %s WHERE %s IN (%s)' % ( + quote_name('cache_key'), + quote_name('value'), + quote_name('expires'), table, - quote_name("cache_key"), - ", ".join(["%s"] * len(key_map)), + quote_name('cache_key'), + ', '.join(['%s'] * len(key_map)), ), list(key_map), ) @@ -83,9 +81,7 @@ class DatabaseCache(BaseDatabaseCache): result = {} expired_keys = [] expression = models.Expression(output_field=models.DateTimeField()) - converters = connection.ops.get_db_converters( - expression - ) + expression.get_db_converters(connection) + converters = (connection.ops.get_db_converters(expression) + expression.get_db_converters(connection)) for key, value, expires in rows: for converter in converters: expires = converter(expires, expression, connection) @@ -99,16 +95,19 @@ class DatabaseCache(BaseDatabaseCache): return result def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - self._base_set("set", key, value, timeout) + key = self.make_key(key, version=version) + self.validate_key(key) + self._base_set('set', key, value, timeout) def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - return self._base_set("add", key, value, timeout) + key = self.make_key(key, version=version) + self.validate_key(key) + return self._base_set('add', key, value, timeout) def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - return self._base_set("touch", key, None, timeout) + key = self.make_key(key, version=version) + self.validate_key(key) + return self._base_set('touch', key, None, timeout) def _base_set(self, mode, key, value, timeout=DEFAULT_TIMEOUT): timeout = self.get_backend_timeout(timeout) @@ -124,16 +123,17 @@ class DatabaseCache(BaseDatabaseCache): now = now.replace(microsecond=0) if timeout is None: exp = datetime.max + elif settings.USE_TZ: + exp = datetime.utcfromtimestamp(timeout) else: - tz = timezone.utc if settings.USE_TZ else None - exp = datetime.fromtimestamp(timeout, tz=tz) + exp = datetime.fromtimestamp(timeout) exp = exp.replace(microsecond=0) if num > self._max_entries: - self._cull(db, cursor, now, num) + self._cull(db, cursor, now) pickled = pickle.dumps(value, self.pickle_protocol) # The DB column is expecting a string, so make sure the value is a # string, not bytes. Refs #19274. - b64encoded = base64.b64encode(pickled).decode("latin1") + b64encoded = base64.b64encode(pickled).decode('latin1') try: # Note: typecasting for datetimes is needed by some 3rd party # database backends. All core backends work without typecasting, @@ -141,59 +141,52 @@ class DatabaseCache(BaseDatabaseCache): # regressions. with transaction.atomic(using=db): cursor.execute( - "SELECT %s, %s FROM %s WHERE %s = %%s" - % ( - quote_name("cache_key"), - quote_name("expires"), + 'SELECT %s, %s FROM %s WHERE %s = %%s' % ( + quote_name('cache_key'), + quote_name('expires'), table, - quote_name("cache_key"), + quote_name('cache_key'), ), - [key], + [key] ) result = cursor.fetchone() if result: current_expires = result[1] - expression = models.Expression( - output_field=models.DateTimeField() - ) - for converter in connection.ops.get_db_converters( - expression - ) + expression.get_db_converters(connection): - current_expires = converter( - current_expires, expression, connection - ) + expression = models.Expression(output_field=models.DateTimeField()) + for converter in (connection.ops.get_db_converters(expression) + + expression.get_db_converters(connection)): + current_expires = converter(current_expires, expression, connection) exp = connection.ops.adapt_datetimefield_value(exp) - if result and mode == "touch": + if result and mode == 'touch': cursor.execute( - "UPDATE %s SET %s = %%s WHERE %s = %%s" - % (table, quote_name("expires"), quote_name("cache_key")), - [exp, key], - ) - elif result and ( - mode == "set" or (mode == "add" and current_expires < now) - ): - cursor.execute( - "UPDATE %s SET %s = %%s, %s = %%s WHERE %s = %%s" - % ( + 'UPDATE %s SET %s = %%s WHERE %s = %%s' % ( table, - quote_name("value"), - quote_name("expires"), - quote_name("cache_key"), + quote_name('expires'), + quote_name('cache_key') ), - [b64encoded, exp, key], + [exp, key] ) - elif mode != "touch": + elif result and (mode == 'set' or (mode == 'add' and current_expires < now)): cursor.execute( - "INSERT INTO %s (%s, %s, %s) VALUES (%%s, %%s, %%s)" - % ( + 'UPDATE %s SET %s = %%s, %s = %%s WHERE %s = %%s' % ( table, - quote_name("cache_key"), - quote_name("value"), - quote_name("expires"), + quote_name('value'), + quote_name('expires'), + quote_name('cache_key'), ), - [key, b64encoded, exp], + [b64encoded, exp, key] + ) + elif mode != 'touch': + cursor.execute( + 'INSERT INTO %s (%s, %s, %s) VALUES (%%s, %%s, %%s)' % ( + table, + quote_name('cache_key'), + quote_name('value'), + quote_name('expires'), + ), + [key, b64encoded, exp] ) else: return False # touch failed. @@ -204,12 +197,15 @@ class DatabaseCache(BaseDatabaseCache): return True def delete(self, key, version=None): - key = self.make_and_validate_key(key, version=version) - return self._base_delete_many([key]) + self.validate_key(key) + return self._base_delete_many([self.make_key(key, version)]) def delete_many(self, keys, version=None): - keys = [self.make_and_validate_key(key, version=version) for key in keys] - self._base_delete_many(keys) + key_list = [] + for key in keys: + self.validate_key(key) + key_list.append(self.make_key(key, version)) + self._base_delete_many(key_list) def _base_delete_many(self, keys): if not keys: @@ -222,58 +218,59 @@ class DatabaseCache(BaseDatabaseCache): with connection.cursor() as cursor: cursor.execute( - "DELETE FROM %s WHERE %s IN (%s)" - % ( + 'DELETE FROM %s WHERE %s IN (%s)' % ( table, - quote_name("cache_key"), - ", ".join(["%s"] * len(keys)), + quote_name('cache_key'), + ', '.join(['%s'] * len(keys)), ), keys, ) - return bool(cursor.rowcount) + return bool(cursor.rowcount) def has_key(self, key, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) db = router.db_for_read(self.cache_model_class) connection = connections[db] quote_name = connection.ops.quote_name - now = timezone.now().replace(microsecond=0, tzinfo=None) + if settings.USE_TZ: + now = datetime.utcnow() + else: + now = datetime.now() + now = now.replace(microsecond=0) with connection.cursor() as cursor: cursor.execute( - "SELECT %s FROM %s WHERE %s = %%s and expires > %%s" - % ( - quote_name("cache_key"), + 'SELECT %s FROM %s WHERE %s = %%s and expires > %%s' % ( + quote_name('cache_key'), quote_name(self._table), - quote_name("cache_key"), + quote_name('cache_key'), ), - [key, connection.ops.adapt_datetimefield_value(now)], + [key, connection.ops.adapt_datetimefield_value(now)] ) return cursor.fetchone() is not None - def _cull(self, db, cursor, now, num): + def _cull(self, db, cursor, now): if self._cull_frequency == 0: self.clear() else: connection = connections[db] table = connection.ops.quote_name(self._table) - cursor.execute( - "DELETE FROM %s WHERE expires < %%s" % table, - [connection.ops.adapt_datetimefield_value(now)], - ) - deleted_count = cursor.rowcount - remaining_num = num - deleted_count - if remaining_num > self._max_entries: - cull_num = remaining_num // self._cull_frequency + cursor.execute("DELETE FROM %s WHERE expires < %%s" % table, + [connection.ops.adapt_datetimefield_value(now)]) + cursor.execute("SELECT COUNT(*) FROM %s" % table) + num = cursor.fetchone()[0] + if num > self._max_entries: + cull_num = num // self._cull_frequency cursor.execute( - connection.ops.cache_key_culling_sql() % table, [cull_num] - ) + connection.ops.cache_key_culling_sql() % table, + [cull_num]) last_cache_key = cursor.fetchone() if last_cache_key: cursor.execute( - "DELETE FROM %s WHERE cache_key < %%s" % table, + 'DELETE FROM %s WHERE cache_key < %%s' % table, [last_cache_key[0]], ) @@ -282,4 +279,4 @@ class DatabaseCache(BaseDatabaseCache): connection = connections[db] table = connection.ops.quote_name(self._table) with connection.cursor() as cursor: - cursor.execute("DELETE FROM %s" % table) + cursor.execute('DELETE FROM %s' % table) diff --git a/venv/Lib/site-packages/django/core/cache/backends/dummy.py b/venv/Lib/site-packages/django/core/cache/backends/dummy.py index 7b4d339..3913746 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/dummy.py +++ b/venv/Lib/site-packages/django/core/cache/backends/dummy.py @@ -8,26 +8,31 @@ class DummyCache(BaseCache): super().__init__(*args, **kwargs) def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return True def get(self, key, default=None, version=None): - self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return default def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - self.make_and_validate_key(key, version=version) + self.validate_key(key) return False def delete(self, key, version=None): - self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return False def has_key(self, key, version=None): - self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return False def clear(self): diff --git a/venv/Lib/site-packages/django/core/cache/backends/filebased.py b/venv/Lib/site-packages/django/core/cache/backends/filebased.py index 6484b44..10779c5 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/filebased.py +++ b/venv/Lib/site-packages/django/core/cache/backends/filebased.py @@ -14,7 +14,7 @@ from django.core.files.move import file_move_safe class FileBasedCache(BaseCache): - cache_suffix = ".djcache" + cache_suffix = '.djcache' pickle_protocol = pickle.HIGHEST_PROTOCOL def __init__(self, dir, params): @@ -31,7 +31,7 @@ class FileBasedCache(BaseCache): def get(self, key, default=None, version=None): fname = self._key_to_file(key, version) try: - with open(fname, "rb") as f: + with open(fname, 'rb') as f: if not self._is_expired(f): return pickle.loads(zlib.decompress(f.read())) except FileNotFoundError: @@ -50,7 +50,7 @@ class FileBasedCache(BaseCache): fd, tmp_path = tempfile.mkstemp(dir=self._dir) renamed = False try: - with open(fd, "wb") as f: + with open(fd, 'wb') as f: self._write_content(f, timeout, value) file_move_safe(tmp_path, fname, allow_overwrite=True) renamed = True @@ -60,7 +60,7 @@ class FileBasedCache(BaseCache): def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): try: - with open(self._key_to_file(key, version), "r+b") as f: + with open(self._key_to_file(key, version), 'r+b') as f: try: locks.lock(f, locks.LOCK_EX) if self._is_expired(f): @@ -91,7 +91,7 @@ class FileBasedCache(BaseCache): def has_key(self, key, version=None): fname = self._key_to_file(key, version) if os.path.exists(fname): - with open(fname, "rb") as f: + with open(fname, 'rb') as f: return not self._is_expired(f) return False @@ -108,7 +108,8 @@ class FileBasedCache(BaseCache): if self._cull_frequency == 0: return self.clear() # Clear the cache when CULL_FREQUENCY = 0 # Delete a random selection of entries - filelist = random.sample(filelist, int(num_entries / self._cull_frequency)) + filelist = random.sample(filelist, + int(num_entries / self._cull_frequency)) for fname in filelist: self._delete(fname) @@ -126,11 +127,10 @@ class FileBasedCache(BaseCache): Convert a key into a cache file path. Basically this is the root cache path joined with the md5sum of the key and a suffix. """ - key = self.make_and_validate_key(key, version=version) - return os.path.join( - self._dir, - "".join([hashlib.md5(key.encode()).hexdigest(), self.cache_suffix]), - ) + key = self.make_key(key, version=version) + self.validate_key(key) + return os.path.join(self._dir, ''.join( + [hashlib.md5(key.encode()).hexdigest(), self.cache_suffix])) def clear(self): """ @@ -160,5 +160,5 @@ class FileBasedCache(BaseCache): """ return [ os.path.join(self._dir, fname) - for fname in glob.glob1(self._dir, "*%s" % self.cache_suffix) + for fname in glob.glob1(self._dir, '*%s' % self.cache_suffix) ] diff --git a/venv/Lib/site-packages/django/core/cache/backends/locmem.py b/venv/Lib/site-packages/django/core/cache/backends/locmem.py index cbc8dba..72cbc9c 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/locmem.py +++ b/venv/Lib/site-packages/django/core/cache/backends/locmem.py @@ -23,7 +23,8 @@ class LocMemCache(BaseCache): self._lock = _locks.setdefault(name, Lock()) def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) pickled = pickle.dumps(value, self.pickle_protocol) with self._lock: if self._has_expired(key): @@ -32,7 +33,8 @@ class LocMemCache(BaseCache): return False def get(self, key, default=None, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) @@ -49,13 +51,15 @@ class LocMemCache(BaseCache): self._expire_info[key] = self.get_backend_timeout(timeout) def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) pickled = pickle.dumps(value, self.pickle_protocol) with self._lock: self._set(key, pickled, timeout) def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) with self._lock: if self._has_expired(key): return False @@ -63,7 +67,8 @@ class LocMemCache(BaseCache): return True def incr(self, key, delta=1, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) @@ -77,7 +82,8 @@ class LocMemCache(BaseCache): return new_value def has_key(self, key, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) @@ -107,7 +113,8 @@ class LocMemCache(BaseCache): return True def delete(self, key, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) with self._lock: return self._delete(key) diff --git a/venv/Lib/site-packages/django/core/cache/backends/memcached.py b/venv/Lib/site-packages/django/core/cache/backends/memcached.py index 2575380..112dbdd 100644 --- a/venv/Lib/site-packages/django/core/cache/backends/memcached.py +++ b/venv/Lib/site-packages/django/core/cache/backends/memcached.py @@ -6,10 +6,7 @@ import time import warnings from django.core.cache.backends.base import ( - DEFAULT_TIMEOUT, - BaseCache, - InvalidCacheKey, - memcache_key_warnings, + DEFAULT_TIMEOUT, BaseCache, InvalidCacheKey, memcache_key_warnings, ) from django.utils.deprecation import RemovedInDjango41Warning from django.utils.functional import cached_property @@ -19,7 +16,7 @@ class BaseMemcachedCache(BaseCache): def __init__(self, server, params, library, value_not_found_exception): super().__init__(params) if isinstance(server, str): - self._servers = re.split("[;,]", server) + self._servers = re.split('[;,]', server) else: self._servers = server @@ -29,7 +26,7 @@ class BaseMemcachedCache(BaseCache): self._lib = library self._class = library.Client - self._options = params.get("OPTIONS") or {} + self._options = params.get('OPTIONS') or {} @property def client_servers(self): @@ -70,32 +67,36 @@ class BaseMemcachedCache(BaseCache): return int(timeout) def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return self._cache.add(key, value, self.get_backend_timeout(timeout)) def get(self, key, default=None, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return self._cache.get(key, default) def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) if not self._cache.set(key, value, self.get_backend_timeout(timeout)): - # Make sure the key doesn't keep its old value in case of failure - # to set (memcached's 1MB limit). + # make sure the key doesn't keep its old value in case of failure to set (memcached's 1MB limit) self._cache.delete(key) def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return bool(self._cache.touch(key, self.get_backend_timeout(timeout))) def delete(self, key, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) return bool(self._cache.delete(key)) def get_many(self, keys, version=None): - key_map = { - self.make_and_validate_key(key, version=version): key for key in keys - } + key_map = {self.make_key(key, version=version): key for key in keys} + for key in key_map: + self.validate_key(key) ret = self._cache.get_multi(key_map.keys()) return {key_map[k]: v for k, v in ret.items()} @@ -104,16 +105,33 @@ class BaseMemcachedCache(BaseCache): self._cache.disconnect_all() def incr(self, key, delta=1, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) + # memcached doesn't support a negative delta + if delta < 0: + return self._cache.decr(key, -delta) try: - # Memcached doesn't support negative delta. - if delta < 0: - val = self._cache.decr(key, -delta) - else: - val = self._cache.incr(key, delta) + val = self._cache.incr(key, delta) + # Normalize an exception raised by the underlying client library to - # ValueError in the event of a nonexistent key when calling - # incr()/decr(). + # ValueError in the event of a nonexistent key when calling incr(). + except self.LibraryValueNotFoundException: + val = None + if val is None: + raise ValueError("Key '%s' not found" % key) + return val + + def decr(self, key, delta=1, version=None): + key = self.make_key(key, version=version) + self.validate_key(key) + # memcached doesn't support a negative delta + if delta < 0: + return self._cache.incr(key, -delta) + try: + val = self._cache.decr(key, delta) + + # Normalize an exception raised by the underlying client library to + # ValueError in the event of a nonexistent key when calling decr(). except self.LibraryValueNotFoundException: val = None if val is None: @@ -124,16 +142,17 @@ class BaseMemcachedCache(BaseCache): safe_data = {} original_keys = {} for key, value in data.items(): - safe_key = self.make_and_validate_key(key, version=version) + safe_key = self.make_key(key, version=version) + self.validate_key(safe_key) safe_data[safe_key] = value original_keys[safe_key] = key - failed_keys = self._cache.set_multi( - safe_data, self.get_backend_timeout(timeout) - ) + failed_keys = self._cache.set_multi(safe_data, self.get_backend_timeout(timeout)) return [original_keys[k] for k in failed_keys] def delete_many(self, keys, version=None): - keys = [self.make_and_validate_key(key, version=version) for key in keys] + keys = [self.make_key(key, version=version) for key in keys] + for key in keys: + self.validate_key(key) self._cache.delete_multi(keys) def clear(self): @@ -153,22 +172,19 @@ class MemcachedCache(BaseMemcachedCache): def __init__(self, server, params): warnings.warn( - "MemcachedCache is deprecated in favor of PyMemcacheCache and " - "PyLibMCCache.", - RemovedInDjango41Warning, - stacklevel=2, + 'MemcachedCache is deprecated in favor of PyMemcacheCache and ' + 'PyLibMCCache.', + RemovedInDjango41Warning, stacklevel=2, ) # python-memcached ≥ 1.45 returns None for a nonexistent key in # incr/decr(), python-memcached < 1.45 raises ValueError. import memcache - - super().__init__( - server, params, library=memcache, value_not_found_exception=ValueError - ) - self._options = {"pickleProtocol": pickle.HIGHEST_PROTOCOL, **self._options} + super().__init__(server, params, library=memcache, value_not_found_exception=ValueError) + self._options = {'pickleProtocol': pickle.HIGHEST_PROTOCOL, **self._options} def get(self, key, default=None, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) val = self._cache.get(key) # python-memcached doesn't support default values in get(). # https://github.com/linsomniac/python-memcached/issues/159 @@ -181,29 +197,27 @@ class MemcachedCache(BaseMemcachedCache): # python-memcached's delete() returns True when key doesn't exist. # https://github.com/linsomniac/python-memcached/issues/170 # Call _deletetouch() without the NOT_FOUND in expected results. - key = self.make_and_validate_key(key, version=version) - return bool(self._cache._deletetouch([b"DELETED"], "delete", key)) + key = self.make_key(key, version=version) + self.validate_key(key) + return bool(self._cache._deletetouch([b'DELETED'], 'delete', key)) class PyLibMCCache(BaseMemcachedCache): "An implementation of a cache binding using pylibmc" - def __init__(self, server, params): import pylibmc - - super().__init__( - server, params, library=pylibmc, value_not_found_exception=pylibmc.NotFound - ) + super().__init__(server, params, library=pylibmc, value_not_found_exception=pylibmc.NotFound) @property def client_servers(self): output = [] for server in self._servers: - output.append(server[5:] if server.startswith("unix:") else server) + output.append(server[5:] if server.startswith('unix:') else server) return output def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) + key = self.make_key(key, version=version) + self.validate_key(key) if timeout == 0: return self._cache.delete(key) return self._cache.touch(key, self.get_backend_timeout(timeout)) @@ -216,17 +230,13 @@ class PyLibMCCache(BaseMemcachedCache): class PyMemcacheCache(BaseMemcachedCache): """An implementation of a cache binding using pymemcache.""" - def __init__(self, server, params): import pymemcache.serde - - super().__init__( - server, params, library=pymemcache, value_not_found_exception=KeyError - ) + super().__init__(server, params, library=pymemcache, value_not_found_exception=KeyError) self._class = self._lib.HashClient self._options = { - "allow_unicode_keys": True, - "default_noreply": False, - "serde": pymemcache.serde.pickle_serde, + 'allow_unicode_keys': True, + 'default_noreply': False, + 'serde': pymemcache.serde.pickle_serde, **self._options, } diff --git a/venv/Lib/site-packages/django/core/cache/backends/redis.py b/venv/Lib/site-packages/django/core/cache/backends/redis.py deleted file mode 100644 index 1a516ce..0000000 --- a/venv/Lib/site-packages/django/core/cache/backends/redis.py +++ /dev/null @@ -1,234 +0,0 @@ -"""Redis cache backend.""" - -import random -import re - -from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache -from django.core.serializers.base import PickleSerializer -from django.utils.functional import cached_property -from django.utils.module_loading import import_string - - -class RedisSerializer(PickleSerializer): - """ - Similar to PickSerializer, except integers are serialized as native Redis - integers for better incr() and decr() atomicity. - """ - - def dumps(self, obj): - # Only skip pickling for integers, a int subclasses as bool should be - # pickled. - if type(obj) is int: - return obj - return super().dumps(obj) - - def loads(self, data): - try: - return int(data) - except ValueError: - return super().loads(data) - - -class RedisCacheClient: - def __init__( - self, - servers, - serializer=None, - db=None, - pool_class=None, - parser_class=None, - ): - import redis - - self._lib = redis - self._servers = servers - self._pools = {} - - self._client = self._lib.Redis - - if isinstance(pool_class, str): - pool_class = import_string(pool_class) - self._pool_class = pool_class or self._lib.ConnectionPool - - if isinstance(serializer, str): - serializer = import_string(serializer) - if callable(serializer): - serializer = serializer() - self._serializer = serializer or RedisSerializer() - - if isinstance(parser_class, str): - parser_class = import_string(parser_class) - parser_class = parser_class or self._lib.connection.DefaultParser - - self._pool_options = {"parser_class": parser_class, "db": db} - - def _get_connection_pool_index(self, write): - # Write to the first server. Read from other servers if there are more, - # otherwise read from the first server. - if write or len(self._servers) == 1: - return 0 - return random.randint(1, len(self._servers) - 1) - - def _get_connection_pool(self, write): - index = self._get_connection_pool_index(write) - if index not in self._pools: - self._pools[index] = self._pool_class.from_url( - self._servers[index], - **self._pool_options, - ) - return self._pools[index] - - def get_client(self, key=None, *, write=False): - # key is used so that the method signature remains the same and custom - # cache client can be implemented which might require the key to select - # the server, e.g. sharding. - pool = self._get_connection_pool(write) - return self._client(connection_pool=pool) - - def add(self, key, value, timeout): - client = self.get_client(key, write=True) - value = self._serializer.dumps(value) - - if timeout == 0: - if ret := bool(client.set(key, value, nx=True)): - client.delete(key) - return ret - else: - return bool(client.set(key, value, ex=timeout, nx=True)) - - def get(self, key, default): - client = self.get_client(key) - value = client.get(key) - return default if value is None else self._serializer.loads(value) - - def set(self, key, value, timeout): - client = self.get_client(key, write=True) - value = self._serializer.dumps(value) - if timeout == 0: - client.delete(key) - else: - client.set(key, value, ex=timeout) - - def touch(self, key, timeout): - client = self.get_client(key, write=True) - if timeout is None: - return bool(client.persist(key)) - else: - return bool(client.expire(key, timeout)) - - def delete(self, key): - client = self.get_client(key, write=True) - return bool(client.delete(key)) - - def get_many(self, keys): - client = self.get_client(None) - ret = client.mget(keys) - return { - k: self._serializer.loads(v) for k, v in zip(keys, ret) if v is not None - } - - def has_key(self, key): - client = self.get_client(key) - return bool(client.exists(key)) - - def incr(self, key, delta): - client = self.get_client(key) - if not client.exists(key): - raise ValueError("Key '%s' not found." % key) - return client.incr(key, delta) - - def set_many(self, data, timeout): - client = self.get_client(None, write=True) - pipeline = client.pipeline() - pipeline.mset({k: self._serializer.dumps(v) for k, v in data.items()}) - - if timeout is not None: - # Setting timeout for each key as redis does not support timeout - # with mset(). - for key in data: - pipeline.expire(key, timeout) - pipeline.execute() - - def delete_many(self, keys): - client = self.get_client(None, write=True) - client.delete(*keys) - - def clear(self): - client = self.get_client(None, write=True) - return bool(client.flushdb()) - - -class RedisCache(BaseCache): - def __init__(self, server, params): - super().__init__(params) - if isinstance(server, str): - self._servers = re.split("[;,]", server) - else: - self._servers = server - - self._class = RedisCacheClient - self._options = params.get("OPTIONS", {}) - - @cached_property - def _cache(self): - return self._class(self._servers, **self._options) - - def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT): - if timeout == DEFAULT_TIMEOUT: - timeout = self.default_timeout - # The key will be made persistent if None used as a timeout. - # Non-positive values will cause the key to be deleted. - return None if timeout is None else max(0, int(timeout)) - - def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.add(key, value, self.get_backend_timeout(timeout)) - - def get(self, key, default=None, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.get(key, default) - - def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - self._cache.set(key, value, self.get_backend_timeout(timeout)) - - def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.touch(key, self.get_backend_timeout(timeout)) - - def delete(self, key, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.delete(key) - - def get_many(self, keys, version=None): - key_map = { - self.make_and_validate_key(key, version=version): key for key in keys - } - ret = self._cache.get_many(key_map.keys()) - return {key_map[k]: v for k, v in ret.items()} - - def has_key(self, key, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.has_key(key) - - def incr(self, key, delta=1, version=None): - key = self.make_and_validate_key(key, version=version) - return self._cache.incr(key, delta) - - def set_many(self, data, timeout=DEFAULT_TIMEOUT, version=None): - safe_data = {} - for key, value in data.items(): - key = self.make_and_validate_key(key, version=version) - safe_data[key] = value - self._cache.set_many(safe_data, self.get_backend_timeout(timeout)) - return [] - - def delete_many(self, keys, version=None): - safe_keys = [] - for key in keys: - key = self.make_and_validate_key(key, version=version) - safe_keys.append(key) - self._cache.delete_many(safe_keys) - - def clear(self): - return self._cache.clear() diff --git a/venv/Lib/site-packages/django/core/cache/utils.py b/venv/Lib/site-packages/django/core/cache/utils.py index 70dc818..2aead84 100644 --- a/venv/Lib/site-packages/django/core/cache/utils.py +++ b/venv/Lib/site-packages/django/core/cache/utils.py @@ -1,6 +1,6 @@ import hashlib -TEMPLATE_FRAGMENT_KEY_TEMPLATE = "template.cache.%s.%s" +TEMPLATE_FRAGMENT_KEY_TEMPLATE = 'template.cache.%s.%s' def make_template_fragment_key(fragment_name, vary_on=None): @@ -8,5 +8,5 @@ def make_template_fragment_key(fragment_name, vary_on=None): if vary_on is not None: for arg in vary_on: hasher.update(str(arg).encode()) - hasher.update(b":") + hasher.update(b':') return TEMPLATE_FRAGMENT_KEY_TEMPLATE % (fragment_name, hasher.hexdigest()) diff --git a/venv/Lib/site-packages/django/core/checks/__init__.py b/venv/Lib/site-packages/django/core/checks/__init__.py index 998ab9d..37b531f 100644 --- a/venv/Lib/site-packages/django/core/checks/__init__.py +++ b/venv/Lib/site-packages/django/core/checks/__init__.py @@ -1,24 +1,13 @@ from .messages import ( - CRITICAL, - DEBUG, - ERROR, - INFO, - WARNING, - CheckMessage, - Critical, - Debug, - Error, - Info, - Warning, + CRITICAL, DEBUG, ERROR, INFO, WARNING, CheckMessage, Critical, Debug, + Error, Info, Warning, ) from .registry import Tags, register, run_checks, tag_exists # Import these to force registration of checks import django.core.checks.async_checks # NOQA isort:skip import django.core.checks.caches # NOQA isort:skip -import django.core.checks.compatibility.django_4_0 # NOQA isort:skip import django.core.checks.database # NOQA isort:skip -import django.core.checks.files # NOQA isort:skip import django.core.checks.model_checks # NOQA isort:skip import django.core.checks.security.base # NOQA isort:skip import django.core.checks.security.csrf # NOQA isort:skip @@ -29,19 +18,8 @@ import django.core.checks.urls # NOQA isort:skip __all__ = [ - "CheckMessage", - "Debug", - "Info", - "Warning", - "Error", - "Critical", - "DEBUG", - "INFO", - "WARNING", - "ERROR", - "CRITICAL", - "register", - "run_checks", - "tag_exists", - "Tags", + 'CheckMessage', + 'Debug', 'Info', 'Warning', 'Error', 'Critical', + 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', + 'register', 'run_checks', 'tag_exists', 'Tags', ] diff --git a/venv/Lib/site-packages/django/core/checks/async_checks.py b/venv/Lib/site-packages/django/core/checks/async_checks.py index a0e0186..fbb5267 100644 --- a/venv/Lib/site-packages/django/core/checks/async_checks.py +++ b/venv/Lib/site-packages/django/core/checks/async_checks.py @@ -3,14 +3,14 @@ import os from . import Error, Tags, register E001 = Error( - "You should not set the DJANGO_ALLOW_ASYNC_UNSAFE environment variable in " - "deployment. This disables async safety protection.", - id="async.E001", + 'You should not set the DJANGO_ALLOW_ASYNC_UNSAFE environment variable in ' + 'deployment. This disables async safety protection.', + id='async.E001', ) @register(Tags.async_support, deploy=True) def check_async_unsafe(app_configs, **kwargs): - if os.environ.get("DJANGO_ALLOW_ASYNC_UNSAFE"): + if os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'): return [E001] return [] diff --git a/venv/Lib/site-packages/django/core/checks/caches.py b/venv/Lib/site-packages/django/core/checks/caches.py index c288a6f..b755e00 100644 --- a/venv/Lib/site-packages/django/core/checks/caches.py +++ b/venv/Lib/site-packages/django/core/checks/caches.py @@ -8,7 +8,7 @@ from . import Error, Tags, Warning, register E001 = Error( "You must define a '%s' cache in your CACHES setting." % DEFAULT_CACHE_ALIAS, - id="caches.E001", + id='caches.E001', ) @@ -22,11 +22,11 @@ def check_default_cache_is_configured(app_configs, **kwargs): @register(Tags.caches, deploy=True) def check_cache_location_not_exposed(app_configs, **kwargs): errors = [] - for name in ("MEDIA_ROOT", "STATIC_ROOT", "STATICFILES_DIRS"): + for name in ('MEDIA_ROOT', 'STATIC_ROOT', 'STATICFILES_DIRS'): setting = getattr(settings, name, None) if not setting: continue - if name == "STATICFILES_DIRS": + if name == 'STATICFILES_DIRS': paths = set() for staticfiles_dir in setting: if isinstance(staticfiles_dir, (list, tuple)): @@ -40,21 +40,19 @@ def check_cache_location_not_exposed(app_configs, **kwargs): continue cache_path = pathlib.Path(cache._dir).resolve() if any(path == cache_path for path in paths): - relation = "matches" + relation = 'matches' elif any(path in cache_path.parents for path in paths): - relation = "is inside" + relation = 'is inside' elif any(cache_path in path.parents for path in paths): - relation = "contains" + relation = 'contains' else: continue - errors.append( - Warning( - f"Your '{alias}' cache configuration might expose your cache " - f"or lead to corruption of your data because its LOCATION " - f"{relation} {name}.", - id="caches.W002", - ) - ) + errors.append(Warning( + f"Your '{alias}' cache configuration might expose your cache " + f"or lead to corruption of your data because its LOCATION " + f"{relation} {name}.", + id='caches.W002', + )) return errors @@ -65,12 +63,10 @@ def check_file_based_cache_is_absolute(app_configs, **kwargs): cache = caches[alias] if not isinstance(cache, FileBasedCache): continue - if not pathlib.Path(config["LOCATION"]).is_absolute(): - errors.append( - Warning( - f"Your '{alias}' cache LOCATION path is relative. Use an " - f"absolute path instead.", - id="caches.W003", - ) - ) + if not pathlib.Path(config['LOCATION']).is_absolute(): + errors.append(Warning( + f"Your '{alias}' cache LOCATION path is relative. Use an " + f"absolute path instead.", + id='caches.W003', + )) return errors diff --git a/venv/Lib/site-packages/django/core/checks/compatibility/django_4_0.py b/venv/Lib/site-packages/django/core/checks/compatibility/django_4_0.py deleted file mode 100644 index 79ee5fa..0000000 --- a/venv/Lib/site-packages/django/core/checks/compatibility/django_4_0.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.conf import settings - -from .. import Error, Tags, register - - -@register(Tags.compatibility) -def check_csrf_trusted_origins(app_configs, **kwargs): - errors = [] - for origin in settings.CSRF_TRUSTED_ORIGINS: - if "://" not in origin: - errors.append( - Error( - "As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS " - "setting must start with a scheme (usually http:// or " - "https://) but found %s. See the release notes for details." - % origin, - id="4_0.E001", - ) - ) - return errors diff --git a/venv/Lib/site-packages/django/core/checks/files.py b/venv/Lib/site-packages/django/core/checks/files.py deleted file mode 100644 index 40dc745..0000000 --- a/venv/Lib/site-packages/django/core/checks/files.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - -from django.conf import settings - -from . import Error, Tags, register - - -@register(Tags.files) -def check_setting_file_upload_temp_dir(app_configs, **kwargs): - setting = getattr(settings, "FILE_UPLOAD_TEMP_DIR", None) - if setting and not Path(setting).is_dir(): - return [ - Error( - f"The FILE_UPLOAD_TEMP_DIR setting refers to the nonexistent " - f"directory '{setting}'.", - id="files.E001", - ), - ] - return [] diff --git a/venv/Lib/site-packages/django/core/checks/messages.py b/venv/Lib/site-packages/django/core/checks/messages.py index db7aa55..aacac63 100644 --- a/venv/Lib/site-packages/django/core/checks/messages.py +++ b/venv/Lib/site-packages/django/core/checks/messages.py @@ -7,9 +7,9 @@ CRITICAL = 50 class CheckMessage: + def __init__(self, level, msg, hint=None, obj=None, id=None): - if not isinstance(level, int): - raise TypeError("The first argument should be level.") + assert isinstance(level, int), "The first argument should be level." self.level = level self.msg = msg self.hint = hint @@ -17,9 +17,10 @@ class CheckMessage: self.id = id def __eq__(self, other): - return isinstance(other, self.__class__) and all( - getattr(self, attr) == getattr(other, attr) - for attr in ["level", "msg", "hint", "obj", "id"] + return ( + isinstance(other, self.__class__) and + all(getattr(self, attr) == getattr(other, attr) + for attr in ['level', 'msg', 'hint', 'obj', 'id']) ) def __str__(self): @@ -34,25 +35,18 @@ class CheckMessage: else: obj = str(self.obj) id = "(%s) " % self.id if self.id else "" - hint = "\n\tHINT: %s" % self.hint if self.hint else "" + hint = "\n\tHINT: %s" % self.hint if self.hint else '' return "%s: %s%s%s" % (obj, id, self.msg, hint) def __repr__(self): - return "<%s: level=%r, msg=%r, hint=%r, obj=%r, id=%r>" % ( - self.__class__.__name__, - self.level, - self.msg, - self.hint, - self.obj, - self.id, - ) + return "<%s: level=%r, msg=%r, hint=%r, obj=%r, id=%r>" % \ + (self.__class__.__name__, self.level, self.msg, self.hint, self.obj, self.id) def is_serious(self, level=ERROR): return self.level >= level def is_silenced(self): from django.conf import settings - return self.id in settings.SILENCED_SYSTEM_CHECKS diff --git a/venv/Lib/site-packages/django/core/checks/model_checks.py b/venv/Lib/site-packages/django/core/checks/model_checks.py index 7a5bef9..15d9b7f 100644 --- a/venv/Lib/site-packages/django/core/checks/model_checks.py +++ b/venv/Lib/site-packages/django/core/checks/model_checks.py @@ -17,9 +17,7 @@ def check_all_models(app_configs=None, **kwargs): if app_configs is None: models = apps.get_models() else: - models = chain.from_iterable( - app_config.get_models() for app_config in app_configs - ) + models = chain.from_iterable(app_config.get_models() for app_config in app_configs) for model in models: if model._meta.managed and not model._meta.proxy: db_table_models[model._meta.db_table].append(model._meta.label) @@ -29,7 +27,7 @@ def check_all_models(app_configs=None, **kwargs): "The '%s.check()' class method is currently overridden by %r." % (model.__name__, model.check), obj=model, - id="models.E020", + id='models.E020' ) ) else: @@ -39,17 +37,17 @@ def check_all_models(app_configs=None, **kwargs): for model_constraint in model._meta.constraints: constraints[model_constraint.name].append(model._meta.label) if settings.DATABASE_ROUTERS: - error_class, error_id = Warning, "models.W035" + error_class, error_id = Warning, 'models.W035' error_hint = ( - "You have configured settings.DATABASE_ROUTERS. Verify that %s " - "are correctly routed to separate databases." + 'You have configured settings.DATABASE_ROUTERS. Verify that %s ' + 'are correctly routed to separate databases.' ) else: - error_class, error_id = Error, "models.E028" + error_class, error_id = Error, 'models.E028' error_hint = None for db_table, model_labels in db_table_models.items(): if len(model_labels) != 1: - model_labels_str = ", ".join(model_labels) + model_labels_str = ', '.join(model_labels) errors.append( error_class( "db_table '%s' is used by multiple models: %s." @@ -64,13 +62,12 @@ def check_all_models(app_configs=None, **kwargs): model_labels = set(model_labels) errors.append( Error( - "index name '%s' is not unique %s %s." - % ( + "index name '%s' is not unique %s %s." % ( index_name, - "for model" if len(model_labels) == 1 else "among models:", - ", ".join(sorted(model_labels)), + 'for model' if len(model_labels) == 1 else 'among models:', + ', '.join(sorted(model_labels)), ), - id="models.E029" if len(model_labels) == 1 else "models.E030", + id='models.E029' if len(model_labels) == 1 else 'models.E030', ), ) for constraint_name, model_labels in constraints.items(): @@ -78,13 +75,12 @@ def check_all_models(app_configs=None, **kwargs): model_labels = set(model_labels) errors.append( Error( - "constraint name '%s' is not unique %s %s." - % ( + "constraint name '%s' is not unique %s %s." % ( constraint_name, - "for model" if len(model_labels) == 1 else "among models:", - ", ".join(sorted(model_labels)), + 'for model' if len(model_labels) == 1 else 'among models:', + ', '.join(sorted(model_labels)), ), - id="models.E031" if len(model_labels) == 1 else "models.E032", + id='models.E031' if len(model_labels) == 1 else 'models.E032', ), ) return errors @@ -108,10 +104,8 @@ def _check_lazy_references(apps, ignore=None): return [] from django.db.models import signals - model_signals = { - signal: name - for name, signal in vars(signals).items() + signal: name for name, signal in vars(signals).items() if isinstance(signal, signals.ModelSignal) } @@ -126,9 +120,9 @@ def _check_lazy_references(apps, ignore=None): annotated there with a `func` attribute so as to imitate a partial. """ operation, args, keywords = obj, [], {} - while hasattr(operation, "func"): - args.extend(getattr(operation, "args", [])) - keywords.update(getattr(operation, "keywords", {})) + while hasattr(operation, 'func'): + args.extend(getattr(operation, 'args', [])) + keywords.update(getattr(operation, 'keywords', {})) operation = operation.func return operation, args, keywords @@ -152,11 +146,11 @@ def _check_lazy_references(apps, ignore=None): "to '%(model)s', but %(model_error)s." ) params = { - "model": ".".join(model_key), - "field": keywords["field"], - "model_error": app_model_error(model_key), + 'model': '.'.join(model_key), + 'field': keywords['field'], + 'model_error': app_model_error(model_key), } - return Error(error_msg % params, obj=keywords["field"], id="fields.E307") + return Error(error_msg % params, obj=keywords['field'], id='fields.E307') def signal_connect_error(model_key, func, args, keywords): error_msg = ( @@ -169,39 +163,34 @@ def _check_lazy_references(apps, ignore=None): if isinstance(receiver, types.FunctionType): description = "The function '%s'" % receiver.__name__ elif isinstance(receiver, types.MethodType): - description = "Bound method '%s.%s'" % ( - receiver.__self__.__class__.__name__, - receiver.__name__, - ) + description = "Bound method '%s.%s'" % (receiver.__self__.__class__.__name__, receiver.__name__) else: description = "An instance of class '%s'" % receiver.__class__.__name__ - signal_name = model_signals.get(func.__self__, "unknown") + signal_name = model_signals.get(func.__self__, 'unknown') params = { - "model": ".".join(model_key), - "receiver": description, - "signal": signal_name, - "model_error": app_model_error(model_key), + 'model': '.'.join(model_key), + 'receiver': description, + 'signal': signal_name, + 'model_error': app_model_error(model_key), } - return Error(error_msg % params, obj=receiver.__module__, id="signals.E001") + return Error(error_msg % params, obj=receiver.__module__, id='signals.E001') def default_error(model_key, func, args, keywords): - error_msg = ( - "%(op)s contains a lazy reference to %(model)s, but %(model_error)s." - ) + error_msg = "%(op)s contains a lazy reference to %(model)s, but %(model_error)s." params = { - "op": func, - "model": ".".join(model_key), - "model_error": app_model_error(model_key), + 'op': func, + 'model': '.'.join(model_key), + 'model_error': app_model_error(model_key), } - return Error(error_msg % params, obj=func, id="models.E022") + return Error(error_msg % params, obj=func, id='models.E022') # Maps common uses of lazy operations to corresponding error functions # defined above. If a key maps to None, no error will be produced. # default_error() will be used for usages that don't appear in this dict. known_lazy = { - ("django.db.models.fields.related", "resolve_related_class"): field_error, - ("django.db.models.fields.related", "set_managed"): None, - ("django.dispatch.dispatcher", "connect"): signal_connect_error, + ('django.db.models.fields.related', 'resolve_related_class'): field_error, + ('django.db.models.fields.related', 'set_managed'): None, + ('django.dispatch.dispatcher', 'connect'): signal_connect_error, } def build_error(model_key, func, args, keywords): @@ -209,17 +198,11 @@ def _check_lazy_references(apps, ignore=None): error_fn = known_lazy.get(key, default_error) return error_fn(model_key, func, args, keywords) if error_fn else None - return sorted( - filter( - None, - ( - build_error(model_key, *extract_operation(func)) - for model_key in pending_models - for func in apps._pending_operations[model_key] - ), - ), - key=lambda error: error.msg, - ) + return sorted(filter(None, ( + build_error(model_key, *extract_operation(func)) + for model_key in pending_models + for func in apps._pending_operations[model_key] + )), key=lambda error: error.msg) @register(Tags.models) diff --git a/venv/Lib/site-packages/django/core/checks/registry.py b/venv/Lib/site-packages/django/core/checks/registry.py index f4bdea8..46a04d7 100644 --- a/venv/Lib/site-packages/django/core/checks/registry.py +++ b/venv/Lib/site-packages/django/core/checks/registry.py @@ -8,24 +8,23 @@ class Tags: """ Built-in tags for internal checks. """ - - admin = "admin" - async_support = "async_support" - caches = "caches" - compatibility = "compatibility" - database = "database" - files = "files" - models = "models" - security = "security" - signals = "signals" - sites = "sites" - staticfiles = "staticfiles" - templates = "templates" - translation = "translation" - urls = "urls" + admin = 'admin' + async_support = 'async_support' + caches = 'caches' + compatibility = 'compatibility' + database = 'database' + models = 'models' + security = 'security' + signals = 'signals' + sites = 'sites' + staticfiles = 'staticfiles' + templates = 'templates' + translation = 'translation' + urls = 'urls' class CheckRegistry: + def __init__(self): self.registered_checks = set() self.deployment_checks = set() @@ -46,18 +45,13 @@ class CheckRegistry: # or registry.register(my_check, 'mytag', 'anothertag') """ - def inner(check): if not func_accepts_kwargs(check): raise TypeError( - "Check functions must accept keyword arguments (**kwargs)." + 'Check functions must accept keyword arguments (**kwargs).' ) check.tags = tags - checks = ( - self.deployment_checks - if kwargs.get("deploy") - else self.registered_checks - ) + checks = self.deployment_checks if kwargs.get('deploy') else self.registered_checks checks.add(check) return check @@ -68,13 +62,7 @@ class CheckRegistry: tags += (check,) return inner - def run_checks( - self, - app_configs=None, - tags=None, - include_deployment_checks=False, - databases=None, - ): + def run_checks(self, app_configs=None, tags=None, include_deployment_checks=False, databases=None): """ Run all registered checks and return list of Errors and Warnings. """ @@ -86,11 +74,9 @@ class CheckRegistry: for check in checks: new_errors = check(app_configs=app_configs, databases=databases) - if not is_iterable(new_errors): - raise TypeError( - "The function %r did not return a list. All functions " - "registered with the checks registry must return a list." % check, - ) + assert is_iterable(new_errors), ( + "The function %r did not return a list. All functions registered " + "with the checks registry must return a list." % check) errors.extend(new_errors) return errors @@ -98,11 +84,9 @@ class CheckRegistry: return tag in self.tags_available(include_deployment_checks) def tags_available(self, deployment_checks=False): - return set( - chain.from_iterable( - check.tags for check in self.get_checks(deployment_checks) - ) - ) + return set(chain.from_iterable( + check.tags for check in self.get_checks(deployment_checks) + )) def get_checks(self, include_deployment_checks=False): checks = list(self.registered_checks) diff --git a/venv/Lib/site-packages/django/core/checks/security/base.py b/venv/Lib/site-packages/django/core/checks/security/base.py index 3e3de6e..8cf3d1d 100644 --- a/venv/Lib/site-packages/django/core/checks/security/base.py +++ b/venv/Lib/site-packages/django/core/checks/security/base.py @@ -3,33 +3,23 @@ from django.core.exceptions import ImproperlyConfigured from .. import Error, Tags, Warning, register -CROSS_ORIGIN_OPENER_POLICY_VALUES = { - "same-origin", - "same-origin-allow-popups", - "unsafe-none", -} REFERRER_POLICY_VALUES = { - "no-referrer", - "no-referrer-when-downgrade", - "origin", - "origin-when-cross-origin", - "same-origin", - "strict-origin", - "strict-origin-when-cross-origin", - "unsafe-url", + 'no-referrer', 'no-referrer-when-downgrade', 'origin', + 'origin-when-cross-origin', 'same-origin', 'strict-origin', + 'strict-origin-when-cross-origin', 'unsafe-url', } -SECRET_KEY_INSECURE_PREFIX = "django-insecure-" +SECRET_KEY_INSECURE_PREFIX = 'django-insecure-' SECRET_KEY_MIN_LENGTH = 50 SECRET_KEY_MIN_UNIQUE_CHARACTERS = 5 W001 = Warning( "You do not have 'django.middleware.security.SecurityMiddleware' " "in your MIDDLEWARE so the SECURE_HSTS_SECONDS, " - "SECURE_CONTENT_TYPE_NOSNIFF, SECURE_REFERRER_POLICY, " - "SECURE_CROSS_ORIGIN_OPENER_POLICY, and SECURE_SSL_REDIRECT settings will " - "have no effect.", - id="security.W001", + "SECURE_CONTENT_TYPE_NOSNIFF, SECURE_BROWSER_XSS_FILTER, " + "SECURE_REFERRER_POLICY, and SECURE_SSL_REDIRECT settings will have no " + "effect.", + id='security.W001', ) W002 = Warning( @@ -39,7 +29,7 @@ W002 = Warning( "'x-frame-options' header. Unless there is a good reason for your " "site to be served in a frame, you should consider enabling this " "header to help prevent clickjacking attacks.", - id="security.W002", + id='security.W002', ) W004 = Warning( @@ -48,7 +38,7 @@ W004 = Warning( "setting a value and enabling HTTP Strict Transport Security. " "Be sure to read the documentation first; enabling HSTS carelessly " "can cause serious, irreversible problems.", - id="security.W004", + id='security.W004', ) W005 = Warning( @@ -57,7 +47,7 @@ W005 = Warning( "via an insecure connection to a subdomain. Only set this to True if " "you are certain that all subdomains of your domain should be served " "exclusively via SSL.", - id="security.W005", + id='security.W005', ) W006 = Warning( @@ -66,7 +56,7 @@ W006 = Warning( "'X-Content-Type-Options: nosniff' header. " "You should consider enabling this header to prevent the " "browser from identifying content types incorrectly.", - id="security.W006", + id='security.W006', ) W008 = Warning( @@ -75,7 +65,7 @@ W008 = Warning( "connections, you may want to either set this setting True " "or configure a load balancer or reverse-proxy server " "to redirect all connections to HTTPS.", - id="security.W008", + id='security.W008', ) W009 = Warning( @@ -83,18 +73,17 @@ W009 = Warning( "%(min_unique_chars)s unique characters, or it's prefixed with " "'%(insecure_prefix)s' indicating that it was generated automatically by " "Django. Please generate a long and random SECRET_KEY, otherwise many of " - "Django's security-critical features will be vulnerable to attack." - % { - "min_length": SECRET_KEY_MIN_LENGTH, - "min_unique_chars": SECRET_KEY_MIN_UNIQUE_CHARACTERS, - "insecure_prefix": SECRET_KEY_INSECURE_PREFIX, + "Django's security-critical features will be vulnerable to attack." % { + 'min_length': SECRET_KEY_MIN_LENGTH, + 'min_unique_chars': SECRET_KEY_MIN_UNIQUE_CHARACTERS, + 'insecure_prefix': SECRET_KEY_INSECURE_PREFIX, }, - id="security.W009", + id='security.W009', ) W018 = Warning( "You should not have DEBUG set to True in deployment.", - id="security.W018", + id='security.W018', ) W019 = Warning( @@ -103,51 +92,45 @@ W019 = Warning( "MIDDLEWARE, but X_FRAME_OPTIONS is not set to 'DENY'. " "Unless there is a good reason for your site to serve other parts of " "itself in a frame, you should change it to 'DENY'.", - id="security.W019", + id='security.W019', ) W020 = Warning( "ALLOWED_HOSTS must not be empty in deployment.", - id="security.W020", + id='security.W020', ) W021 = Warning( "You have not set the SECURE_HSTS_PRELOAD setting to True. Without this, " "your site cannot be submitted to the browser preload list.", - id="security.W021", + id='security.W021', ) W022 = Warning( - "You have not set the SECURE_REFERRER_POLICY setting. Without this, your " - "site will not send a Referrer-Policy header. You should consider " - "enabling this header to protect user privacy.", - id="security.W022", + 'You have not set the SECURE_REFERRER_POLICY setting. Without this, your ' + 'site will not send a Referrer-Policy header. You should consider ' + 'enabling this header to protect user privacy.', + id='security.W022', ) E023 = Error( - "You have set the SECURE_REFERRER_POLICY setting to an invalid value.", - hint="Valid values are: {}.".format(", ".join(sorted(REFERRER_POLICY_VALUES))), - id="security.E023", + 'You have set the SECURE_REFERRER_POLICY setting to an invalid value.', + hint='Valid values are: {}.'.format(', '.join(sorted(REFERRER_POLICY_VALUES))), + id='security.E023', ) -E024 = Error( - "You have set the SECURE_CROSS_ORIGIN_OPENER_POLICY setting to an invalid " - "value.", - hint="Valid values are: {}.".format( - ", ".join(sorted(CROSS_ORIGIN_OPENER_POLICY_VALUES)), - ), - id="security.E024", +E100 = Error( + "DEFAULT_HASHING_ALGORITHM must be 'sha1' or 'sha256'.", + id='security.E100', ) def _security_middleware(): - return "django.middleware.security.SecurityMiddleware" in settings.MIDDLEWARE + return 'django.middleware.security.SecurityMiddleware' in settings.MIDDLEWARE def _xframe_middleware(): - return ( - "django.middleware.clickjacking.XFrameOptionsMiddleware" in settings.MIDDLEWARE - ) + return 'django.middleware.clickjacking.XFrameOptionsMiddleware' in settings.MIDDLEWARE @register(Tags.security, deploy=True) @@ -171,9 +154,9 @@ def check_sts(app_configs, **kwargs): @register(Tags.security, deploy=True) def check_sts_include_subdomains(app_configs, **kwargs): passed_check = ( - not _security_middleware() - or not settings.SECURE_HSTS_SECONDS - or settings.SECURE_HSTS_INCLUDE_SUBDOMAINS is True + not _security_middleware() or + not settings.SECURE_HSTS_SECONDS or + settings.SECURE_HSTS_INCLUDE_SUBDOMAINS is True ) return [] if passed_check else [W005] @@ -181,9 +164,9 @@ def check_sts_include_subdomains(app_configs, **kwargs): @register(Tags.security, deploy=True) def check_sts_preload(app_configs, **kwargs): passed_check = ( - not _security_middleware() - or not settings.SECURE_HSTS_SECONDS - or settings.SECURE_HSTS_PRELOAD is True + not _security_middleware() or + not settings.SECURE_HSTS_SECONDS or + settings.SECURE_HSTS_PRELOAD is True ) return [] if passed_check else [W021] @@ -191,14 +174,18 @@ def check_sts_preload(app_configs, **kwargs): @register(Tags.security, deploy=True) def check_content_type_nosniff(app_configs, **kwargs): passed_check = ( - not _security_middleware() or settings.SECURE_CONTENT_TYPE_NOSNIFF is True + not _security_middleware() or + settings.SECURE_CONTENT_TYPE_NOSNIFF is True ) return [] if passed_check else [W006] @register(Tags.security, deploy=True) def check_ssl_redirect(app_configs, **kwargs): - passed_check = not _security_middleware() or settings.SECURE_SSL_REDIRECT is True + passed_check = ( + not _security_middleware() or + settings.SECURE_SSL_REDIRECT is True + ) return [] if passed_check else [W008] @@ -210,9 +197,9 @@ def check_secret_key(app_configs, **kwargs): passed_check = False else: passed_check = ( - len(set(secret_key)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS - and len(secret_key) >= SECRET_KEY_MIN_LENGTH - and not secret_key.startswith(SECRET_KEY_INSECURE_PREFIX) + len(set(secret_key)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS and + len(secret_key) >= SECRET_KEY_MIN_LENGTH and + not secret_key.startswith(SECRET_KEY_INSECURE_PREFIX) ) return [] if passed_check else [W009] @@ -225,7 +212,10 @@ def check_debug(app_configs, **kwargs): @register(Tags.security, deploy=True) def check_xframe_deny(app_configs, **kwargs): - passed_check = not _xframe_middleware() or settings.X_FRAME_OPTIONS == "DENY" + passed_check = ( + not _xframe_middleware() or + settings.X_FRAME_OPTIONS == 'DENY' + ) return [] if passed_check else [W019] @@ -241,7 +231,7 @@ def check_referrer_policy(app_configs, **kwargs): return [W022] # Support a comma-separated string or iterable of values to allow fallback. if isinstance(settings.SECURE_REFERRER_POLICY, str): - values = {v.strip() for v in settings.SECURE_REFERRER_POLICY.split(",")} + values = {v.strip() for v in settings.SECURE_REFERRER_POLICY.split(',')} else: values = set(settings.SECURE_REFERRER_POLICY) if not values <= REFERRER_POLICY_VALUES: @@ -249,13 +239,9 @@ def check_referrer_policy(app_configs, **kwargs): return [] -@register(Tags.security, deploy=True) -def check_cross_origin_opener_policy(app_configs, **kwargs): - if ( - _security_middleware() - and settings.SECURE_CROSS_ORIGIN_OPENER_POLICY is not None - and settings.SECURE_CROSS_ORIGIN_OPENER_POLICY - not in CROSS_ORIGIN_OPENER_POLICY_VALUES - ): - return [E024] +# RemovedInDjango40Warning +@register(Tags.security) +def check_default_hashing_algorithm(app_configs, **kwargs): + if settings.DEFAULT_HASHING_ALGORITHM not in {'sha1', 'sha256'}: + return [E100] return [] diff --git a/venv/Lib/site-packages/django/core/checks/security/csrf.py b/venv/Lib/site-packages/django/core/checks/security/csrf.py index af59589..2b70d36 100644 --- a/venv/Lib/site-packages/django/core/checks/security/csrf.py +++ b/venv/Lib/site-packages/django/core/checks/security/csrf.py @@ -10,7 +10,7 @@ W003 = Warning( "('django.middleware.csrf.CsrfViewMiddleware' is not in your " "MIDDLEWARE). Enabling the middleware is the safest approach " "to ensure you don't leave any holes.", - id="security.W003", + id='security.W003', ) W016 = Warning( @@ -18,12 +18,12 @@ W016 = Warning( "MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. " "Using a secure-only CSRF cookie makes it more difficult for network " "traffic sniffers to steal the CSRF token.", - id="security.W016", + id='security.W016', ) def _csrf_middleware(): - return "django.middleware.csrf.CsrfViewMiddleware" in settings.MIDDLEWARE + return 'django.middleware.csrf.CsrfViewMiddleware' in settings.MIDDLEWARE @register(Tags.security, deploy=True) @@ -35,9 +35,9 @@ def check_csrf_middleware(app_configs, **kwargs): @register(Tags.security, deploy=True) def check_csrf_cookie_secure(app_configs, **kwargs): passed_check = ( - settings.CSRF_USE_SESSIONS - or not _csrf_middleware() - or settings.CSRF_COOKIE_SECURE + settings.CSRF_USE_SESSIONS or + not _csrf_middleware() or + settings.CSRF_COOKIE_SECURE ) return [] if passed_check else [W016] @@ -51,17 +51,17 @@ def check_csrf_failure_view(app_configs, **kwargs): view = _get_failure_view() except ImportError: msg = ( - "The CSRF failure view '%s' could not be imported." - % settings.CSRF_FAILURE_VIEW + "The CSRF failure view '%s' could not be imported." % + settings.CSRF_FAILURE_VIEW ) - errors.append(Error(msg, id="security.E102")) + errors.append(Error(msg, id='security.E102')) else: try: inspect.signature(view).bind(None, reason=None) except TypeError: msg = ( - "The CSRF failure view '%s' does not take the correct number of " - "arguments." % settings.CSRF_FAILURE_VIEW + "The CSRF failure view '%s' does not take the correct number of arguments." % + settings.CSRF_FAILURE_VIEW ) - errors.append(Error(msg, id="security.E101")) + errors.append(Error(msg, id='security.E101')) return errors diff --git a/venv/Lib/site-packages/django/core/checks/security/sessions.py b/venv/Lib/site-packages/django/core/checks/security/sessions.py index 7c251c0..1f31a16 100644 --- a/venv/Lib/site-packages/django/core/checks/security/sessions.py +++ b/venv/Lib/site-packages/django/core/checks/security/sessions.py @@ -15,7 +15,7 @@ W010 = Warning( "You have 'django.contrib.sessions' in your INSTALLED_APPS, " "but you have not set SESSION_COOKIE_SECURE to True." ), - id="security.W010", + id='security.W010', ) W011 = Warning( @@ -24,12 +24,12 @@ W011 = Warning( "in your MIDDLEWARE, but you have not set " "SESSION_COOKIE_SECURE to True." ), - id="security.W011", + id='security.W011', ) W012 = Warning( add_session_cookie_message("SESSION_COOKIE_SECURE is not set to True."), - id="security.W012", + id='security.W012', ) @@ -45,7 +45,7 @@ W013 = Warning( "You have 'django.contrib.sessions' in your INSTALLED_APPS, " "but you have not set SESSION_COOKIE_HTTPONLY to True.", ), - id="security.W013", + id='security.W013', ) W014 = Warning( @@ -54,12 +54,12 @@ W014 = Warning( "in your MIDDLEWARE, but you have not set " "SESSION_COOKIE_HTTPONLY to True." ), - id="security.W014", + id='security.W014', ) W015 = Warning( add_httponly_message("SESSION_COOKIE_HTTPONLY is not set to True."), - id="security.W015", + id='security.W015', ) @@ -90,7 +90,7 @@ def check_session_cookie_httponly(app_configs, **kwargs): def _session_middleware(): - return "django.contrib.sessions.middleware.SessionMiddleware" in settings.MIDDLEWARE + return 'django.contrib.sessions.middleware.SessionMiddleware' in settings.MIDDLEWARE def _session_app(): diff --git a/venv/Lib/site-packages/django/core/checks/templates.py b/venv/Lib/site-packages/django/core/checks/templates.py index 1aafc99..8c4b7c1 100644 --- a/venv/Lib/site-packages/django/core/checks/templates.py +++ b/venv/Lib/site-packages/django/core/checks/templates.py @@ -7,7 +7,7 @@ from . import Error, Tags, register E001 = Error( "You have 'APP_DIRS': True in your TEMPLATES but also specify 'loaders' " "in OPTIONS. Either remove APP_DIRS or remove the 'loaders' option.", - id="templates.E001", + id='templates.E001', ) E002 = Error( "'string_if_invalid' in TEMPLATES OPTIONS must be a string but got: {} ({}).", @@ -17,25 +17,19 @@ E002 = Error( @register(Tags.templates) def check_setting_app_dirs_loaders(app_configs, **kwargs): - return ( - [E001] - if any( - conf.get("APP_DIRS") and "loaders" in conf.get("OPTIONS", {}) - for conf in settings.TEMPLATES - ) - else [] - ) + return [E001] if any( + conf.get('APP_DIRS') and 'loaders' in conf.get('OPTIONS', {}) + for conf in settings.TEMPLATES + ) else [] @register(Tags.templates) def check_string_if_invalid_is_string(app_configs, **kwargs): errors = [] for conf in settings.TEMPLATES: - string_if_invalid = conf.get("OPTIONS", {}).get("string_if_invalid", "") + string_if_invalid = conf.get('OPTIONS', {}).get('string_if_invalid', '') if not isinstance(string_if_invalid, str): error = copy.copy(E002) - error.msg = error.msg.format( - string_if_invalid, type(string_if_invalid).__name__ - ) + error.msg = error.msg.format(string_if_invalid, type(string_if_invalid).__name__) errors.append(error) return errors diff --git a/venv/Lib/site-packages/django/core/checks/translation.py b/venv/Lib/site-packages/django/core/checks/translation.py index 214e970..8457a6b 100644 --- a/venv/Lib/site-packages/django/core/checks/translation.py +++ b/venv/Lib/site-packages/django/core/checks/translation.py @@ -5,24 +5,24 @@ from django.utils.translation.trans_real import language_code_re from . import Error, Tags, register E001 = Error( - "You have provided an invalid value for the LANGUAGE_CODE setting: {!r}.", - id="translation.E001", + 'You have provided an invalid value for the LANGUAGE_CODE setting: {!r}.', + id='translation.E001', ) E002 = Error( - "You have provided an invalid language code in the LANGUAGES setting: {!r}.", - id="translation.E002", + 'You have provided an invalid language code in the LANGUAGES setting: {!r}.', + id='translation.E002', ) E003 = Error( - "You have provided an invalid language code in the LANGUAGES_BIDI setting: {!r}.", - id="translation.E003", + 'You have provided an invalid language code in the LANGUAGES_BIDI setting: {!r}.', + id='translation.E003', ) E004 = Error( - "You have provided a value for the LANGUAGE_CODE setting that is not in " - "the LANGUAGES setting.", - id="translation.E004", + 'You have provided a value for the LANGUAGE_CODE setting that is not in ' + 'the LANGUAGES setting.', + id='translation.E004', ) @@ -40,8 +40,7 @@ def check_setting_languages(app_configs, **kwargs): """Error if LANGUAGES setting is invalid.""" return [ Error(E002.msg.format(tag), id=E002.id) - for tag, _ in settings.LANGUAGES - if not isinstance(tag, str) or not language_code_re.match(tag) + for tag, _ in settings.LANGUAGES if not isinstance(tag, str) or not language_code_re.match(tag) ] @@ -50,8 +49,7 @@ def check_setting_languages_bidi(app_configs, **kwargs): """Error if LANGUAGES_BIDI setting is invalid.""" return [ Error(E003.msg.format(tag), id=E003.id) - for tag in settings.LANGUAGES_BIDI - if not isinstance(tag, str) or not language_code_re.match(tag) + for tag in settings.LANGUAGES_BIDI if not isinstance(tag, str) or not language_code_re.match(tag) ] diff --git a/venv/Lib/site-packages/django/core/checks/urls.py b/venv/Lib/site-packages/django/core/checks/urls.py index 34eff96..e51ca3f 100644 --- a/venv/Lib/site-packages/django/core/checks/urls.py +++ b/venv/Lib/site-packages/django/core/checks/urls.py @@ -7,9 +7,8 @@ from . import Error, Tags, Warning, register @register(Tags.urls) def check_url_config(app_configs, **kwargs): - if getattr(settings, "ROOT_URLCONF", None): + if getattr(settings, 'ROOT_URLCONF', None): from django.urls import get_resolver - resolver = get_resolver() return check_resolver(resolver) return [] @@ -19,10 +18,10 @@ def check_resolver(resolver): """ Recursively check the resolver. """ - check_method = getattr(resolver, "check", None) + check_method = getattr(resolver, 'check', None) if check_method is not None: return check_method() - elif not hasattr(resolver, "resolve"): + elif not hasattr(resolver, 'resolve'): return get_warning_for_invalid_pattern(resolver) else: return [] @@ -33,24 +32,21 @@ def check_url_namespaces_unique(app_configs, **kwargs): """ Warn if URL namespaces used in applications aren't unique. """ - if not getattr(settings, "ROOT_URLCONF", None): + if not getattr(settings, 'ROOT_URLCONF', None): return [] from django.urls import get_resolver - resolver = get_resolver() all_namespaces = _load_all_namespaces(resolver) counter = Counter(all_namespaces) non_unique_namespaces = [n for n, count in counter.items() if count > 1] errors = [] for namespace in non_unique_namespaces: - errors.append( - Warning( - "URL namespace '{}' isn't unique. You may not be able to reverse " - "all URLs in this namespace".format(namespace), - id="urls.W005", - ) - ) + errors.append(Warning( + "URL namespace '{}' isn't unique. You may not be able to reverse " + "all URLs in this namespace".format(namespace), + id="urls.W005", + )) return errors @@ -58,14 +54,13 @@ def _load_all_namespaces(resolver, parents=()): """ Recursively load all namespaces from URL patterns. """ - url_patterns = getattr(resolver, "url_patterns", []) + url_patterns = getattr(resolver, 'url_patterns', []) namespaces = [ - ":".join(parents + (url.namespace,)) - for url in url_patterns - if getattr(url, "namespace", None) is not None + ':'.join(parents + (url.namespace,)) for url in url_patterns + if getattr(url, 'namespace', None) is not None ] for pattern in url_patterns: - namespace = getattr(pattern, "namespace", None) + namespace = getattr(pattern, 'namespace', None) current = parents if namespace is not None: current += (namespace,) @@ -90,28 +85,26 @@ def get_warning_for_invalid_pattern(pattern): else: hint = None - return [ - Error( - "Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list " - "of path() and/or re_path() instances.".format(pattern), - hint=hint, - id="urls.E004", - ) - ] + return [Error( + "Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list " + "of path() and/or re_path() instances.".format(pattern), + hint=hint, + id="urls.E004", + )] @register(Tags.urls) def check_url_settings(app_configs, **kwargs): errors = [] - for name in ("STATIC_URL", "MEDIA_URL"): + for name in ('STATIC_URL', 'MEDIA_URL'): value = getattr(settings, name) - if value and not value.endswith("/"): + if value and not value.endswith('/'): errors.append(E006(name)) return errors def E006(name): return Error( - "The {} setting must end with a slash.".format(name), - id="urls.E006", + 'The {} setting must end with a slash.'.format(name), + id='urls.E006', ) diff --git a/venv/Lib/site-packages/django/core/exceptions.py b/venv/Lib/site-packages/django/core/exceptions.py index 7be4e16..673d004 100644 --- a/venv/Lib/site-packages/django/core/exceptions.py +++ b/venv/Lib/site-packages/django/core/exceptions.py @@ -8,25 +8,21 @@ from django.utils.hashable import make_hashable class FieldDoesNotExist(Exception): """The requested model field does not exist""" - pass class AppRegistryNotReady(Exception): """The django.apps registry is not populated yet""" - pass class ObjectDoesNotExist(Exception): """The requested object does not exist""" - silent_variable_failure = True class MultipleObjectsReturned(Exception): """The query returned multiple objects when only one was expected.""" - pass @@ -36,25 +32,21 @@ class SuspiciousOperation(Exception): class SuspiciousMultipartForm(SuspiciousOperation): """Suspect MIME request in multipart form data""" - pass class SuspiciousFileOperation(SuspiciousOperation): """A Suspicious filesystem operation was attempted""" - pass class DisallowedHost(SuspiciousOperation): """HTTP_HOST header contains invalid value""" - pass class DisallowedRedirect(SuspiciousOperation): """Redirect to scheme not in allowed list""" - pass @@ -63,7 +55,6 @@ class TooManyFieldsSent(SuspiciousOperation): The number of fields in a GET or POST request exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS. """ - pass @@ -72,58 +63,49 @@ class RequestDataTooBig(SuspiciousOperation): The size of the request (excluding any file uploads) exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE. """ - pass class RequestAborted(Exception): """The request was closed before it was completed, or timed out.""" - pass class BadRequest(Exception): """The request is malformed and cannot be processed.""" - pass class PermissionDenied(Exception): """The user did not have permission to do that""" - pass class ViewDoesNotExist(Exception): """The requested view does not exist""" - pass class MiddlewareNotUsed(Exception): """This middleware is not used in this server configuration""" - pass class ImproperlyConfigured(Exception): """Django is somehow improperly configured""" - pass class FieldError(Exception): """Some kind of problem with a model field.""" - pass -NON_FIELD_ERRORS = "__all__" +NON_FIELD_ERRORS = '__all__' class ValidationError(Exception): """An error while validating data.""" - def __init__(self, message, code=None, params=None): """ The `message` argument can be a single error, a list of errors, or a @@ -136,9 +118,9 @@ class ValidationError(Exception): super().__init__(message, code, params) if isinstance(message, ValidationError): - if hasattr(message, "error_dict"): + if hasattr(message, 'error_dict'): message = message.error_dict - elif not hasattr(message, "message"): + elif not hasattr(message, 'message'): message = message.error_list else: message, code, params = message.message, message.code, message.params @@ -156,7 +138,7 @@ class ValidationError(Exception): # Normalize plain strings to instances of ValidationError. if not isinstance(message, ValidationError): message = ValidationError(message) - if hasattr(message, "error_dict"): + if hasattr(message, 'error_dict'): self.error_list.extend(sum(message.error_dict.values(), [])) else: self.error_list.extend(message.error_list) @@ -171,18 +153,18 @@ class ValidationError(Exception): def message_dict(self): # Trigger an AttributeError if this ValidationError # doesn't have an error_dict. - getattr(self, "error_dict") + getattr(self, 'error_dict') return dict(self) @property def messages(self): - if hasattr(self, "error_dict"): + if hasattr(self, 'error_dict'): return sum(dict(self).values(), []) return list(self) def update_error_dict(self, error_dict): - if hasattr(self, "error_dict"): + if hasattr(self, 'error_dict'): for field, error_list in self.error_dict.items(): error_dict.setdefault(field, []).extend(error_list) else: @@ -190,7 +172,7 @@ class ValidationError(Exception): return error_dict def __iter__(self): - if hasattr(self, "error_dict"): + if hasattr(self, 'error_dict'): for field, errors in self.error_dict.items(): yield field, list(ValidationError(errors)) else: @@ -201,12 +183,12 @@ class ValidationError(Exception): yield str(message) def __str__(self): - if hasattr(self, "error_dict"): + if hasattr(self, 'error_dict'): return repr(dict(self)) return repr(list(self)) def __repr__(self): - return "ValidationError(%s)" % self + return 'ValidationError(%s)' % self def __eq__(self, other): if not isinstance(other, ValidationError): @@ -214,26 +196,22 @@ class ValidationError(Exception): return hash(self) == hash(other) def __hash__(self): - if hasattr(self, "message"): - return hash( - ( - self.message, - self.code, - make_hashable(self.params), - ) - ) - if hasattr(self, "error_dict"): + if hasattr(self, 'message'): + return hash(( + self.message, + self.code, + make_hashable(self.params), + )) + if hasattr(self, 'error_dict'): return hash(make_hashable(self.error_dict)) - return hash(tuple(sorted(self.error_list, key=operator.attrgetter("message")))) + return hash(tuple(sorted(self.error_list, key=operator.attrgetter('message')))) class EmptyResultSet(Exception): """A database query predicate is impossible.""" - pass class SynchronousOnlyOperation(Exception): """The user tried to call a sync-only function from an async context.""" - pass diff --git a/venv/Lib/site-packages/django/core/files/__init__.py b/venv/Lib/site-packages/django/core/files/__init__.py index d046aca..58a6fd8 100644 --- a/venv/Lib/site-packages/django/core/files/__init__.py +++ b/venv/Lib/site-packages/django/core/files/__init__.py @@ -1,3 +1,3 @@ from django.core.files.base import File -__all__ = ["File"] +__all__ = ['File'] diff --git a/venv/Lib/site-packages/django/core/files/base.py b/venv/Lib/site-packages/django/core/files/base.py index 3ca43ec..2ac662e 100644 --- a/venv/Lib/site-packages/django/core/files/base.py +++ b/venv/Lib/site-packages/django/core/files/base.py @@ -6,18 +6,18 @@ from django.utils.functional import cached_property class File(FileProxyMixin): - DEFAULT_CHUNK_SIZE = 64 * 2**10 + DEFAULT_CHUNK_SIZE = 64 * 2 ** 10 def __init__(self, file, name=None): self.file = file if name is None: - name = getattr(file, "name", None) + name = getattr(file, 'name', None) self.name = name - if hasattr(file, "mode"): + if hasattr(file, 'mode'): self.mode = file.mode def __str__(self): - return self.name or "" + return self.name or '' def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self or "None") @@ -30,14 +30,14 @@ class File(FileProxyMixin): @cached_property def size(self): - if hasattr(self.file, "size"): + if hasattr(self.file, 'size'): return self.file.size - if hasattr(self.file, "name"): + if hasattr(self.file, 'name'): try: return os.path.getsize(self.file.name) except (OSError, TypeError): pass - if hasattr(self.file, "tell") and hasattr(self.file, "seek"): + if hasattr(self.file, 'tell') and hasattr(self.file, 'seek'): pos = self.file.tell() self.file.seek(0, os.SEEK_END) size = self.file.tell() @@ -122,14 +122,13 @@ class ContentFile(File): """ A File-like object that takes just raw content, rather than an actual file. """ - def __init__(self, content, name=None): stream_class = StringIO if isinstance(content, str) else BytesIO super().__init__(stream_class(content), name=name) self.size = len(content) def __str__(self): - return "Raw content" + return 'Raw content' def __bool__(self): return True @@ -142,20 +141,20 @@ class ContentFile(File): pass def write(self, data): - self.__dict__.pop("size", None) # Clear the computed size. + self.__dict__.pop('size', None) # Clear the computed size. return self.file.write(data) def endswith_cr(line): """Return True if line (a text or bytestring) ends with '\r'.""" - return line.endswith("\r" if isinstance(line, str) else b"\r") + return line.endswith('\r' if isinstance(line, str) else b'\r') def endswith_lf(line): """Return True if line (a text or bytestring) ends with '\n'.""" - return line.endswith("\n" if isinstance(line, str) else b"\n") + return line.endswith('\n' if isinstance(line, str) else b'\n') def equals_lf(line): """Return True if line (a text or bytestring) equals '\n'.""" - return line == ("\n" if isinstance(line, str) else b"\n") + return line == ('\n' if isinstance(line, str) else b'\n') diff --git a/venv/Lib/site-packages/django/core/files/images.py b/venv/Lib/site-packages/django/core/files/images.py index 6a603f2..579c32e 100644 --- a/venv/Lib/site-packages/django/core/files/images.py +++ b/venv/Lib/site-packages/django/core/files/images.py @@ -14,7 +14,6 @@ class ImageFile(File): A mixin for use alongside django.core.files.base.File, which provides additional features for dealing with images. """ - @property def width(self): return self._get_image_dimensions()[0] @@ -24,7 +23,7 @@ class ImageFile(File): return self._get_image_dimensions()[1] def _get_image_dimensions(self): - if not hasattr(self, "_dimensions_cache"): + if not hasattr(self, '_dimensions_cache'): close = self.closed self.open() self._dimensions_cache = get_image_dimensions(self, close=close) @@ -40,15 +39,12 @@ def get_image_dimensions(file_or_path, close=False): from PIL import ImageFile as PillowImageFile p = PillowImageFile.Parser() - if hasattr(file_or_path, "read"): + if hasattr(file_or_path, 'read'): file = file_or_path file_pos = file.tell() file.seek(0) else: - try: - file = open(file_or_path, "rb") - except OSError: - return (None, None) + file = open(file_or_path, 'rb') close = True try: # Most of the time Pillow only needs a small chunk to parse the image diff --git a/venv/Lib/site-packages/django/core/files/locks.py b/venv/Lib/site-packages/django/core/files/locks.py index 6f63280..b02faaa 100644 --- a/venv/Lib/site-packages/django/core/files/locks.py +++ b/venv/Lib/site-packages/django/core/files/locks.py @@ -6,7 +6,7 @@ Cookbook [1] (licensed under the Python Software License) and a ctypes port by Anatoly Techtonik for Roundup [2] (license [3]). [1] http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65203 -[2] https://sourceforge.net/p/roundup/code/ci/default/tree/roundup/backends/portalocker.py # NOQA +[2] https://sourceforge.net/p/roundup/code/ci/default/tree/roundup/backends/portalocker.py [3] https://sourceforge.net/p/roundup/code/ci/default/tree/COPYING.txt Example Usage:: @@ -18,25 +18,18 @@ Example Usage:: """ import os -__all__ = ("LOCK_EX", "LOCK_SH", "LOCK_NB", "lock", "unlock") +__all__ = ('LOCK_EX', 'LOCK_SH', 'LOCK_NB', 'lock', 'unlock') def _fd(f): """Get a filedescriptor from something which could be a file or an fd.""" - return f.fileno() if hasattr(f, "fileno") else f + return f.fileno() if hasattr(f, 'fileno') else f -if os.name == "nt": +if os.name == 'nt': import msvcrt from ctypes import ( - POINTER, - Structure, - Union, - byref, - c_int64, - c_ulong, - c_void_p, - sizeof, + POINTER, Structure, Union, byref, c_int64, c_ulong, c_void_p, sizeof, windll, ) from ctypes.wintypes import BOOL, DWORD, HANDLE @@ -55,20 +48,23 @@ if os.name == "nt": # --- Union inside Structure by stackoverflow:3480240 --- class _OFFSET(Structure): - _fields_ = [("Offset", DWORD), ("OffsetHigh", DWORD)] + _fields_ = [ + ('Offset', DWORD), + ('OffsetHigh', DWORD)] class _OFFSET_UNION(Union): - _anonymous_ = ["_offset"] - _fields_ = [("_offset", _OFFSET), ("Pointer", PVOID)] + _anonymous_ = ['_offset'] + _fields_ = [ + ('_offset', _OFFSET), + ('Pointer', PVOID)] class OVERLAPPED(Structure): - _anonymous_ = ["_offset_union"] + _anonymous_ = ['_offset_union'] _fields_ = [ - ("Internal", ULONG_PTR), - ("InternalHigh", ULONG_PTR), - ("_offset_union", _OFFSET_UNION), - ("hEvent", HANDLE), - ] + ('Internal', ULONG_PTR), + ('InternalHigh', ULONG_PTR), + ('_offset_union', _OFFSET_UNION), + ('hEvent', HANDLE)] LPOVERLAPPED = POINTER(OVERLAPPED) @@ -91,11 +87,9 @@ if os.name == "nt": overlapped = OVERLAPPED() ret = UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped)) return bool(ret) - else: try: import fcntl - LOCK_SH = fcntl.LOCK_SH # shared lock LOCK_NB = fcntl.LOCK_NB # non-blocking LOCK_EX = fcntl.LOCK_EX @@ -111,9 +105,7 @@ else: def unlock(f): # File is unlocked return True - else: - def lock(f, flags): try: fcntl.flock(_fd(f), flags) diff --git a/venv/Lib/site-packages/django/core/files/move.py b/venv/Lib/site-packages/django/core/files/move.py index 2d71e11..2cce784 100644 --- a/venv/Lib/site-packages/django/core/files/move.py +++ b/venv/Lib/site-packages/django/core/files/move.py @@ -11,26 +11,23 @@ from shutil import copystat from django.core.files import locks -__all__ = ["file_move_safe"] +__all__ = ['file_move_safe'] def _samefile(src, dst): # Macintosh, Unix. - if hasattr(os.path, "samefile"): + if hasattr(os.path, 'samefile'): try: return os.path.samefile(src, dst) except OSError: return False # All other platforms: check for same pathname. - return os.path.normcase(os.path.abspath(src)) == os.path.normcase( - os.path.abspath(dst) - ) + return (os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst))) -def file_move_safe( - old_file_name, new_file_name, chunk_size=1024 * 64, allow_overwrite=False -): +def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_overwrite=False): """ Move a file from one location to another in the safest way possible. @@ -46,10 +43,7 @@ def file_move_safe( try: if not allow_overwrite and os.access(new_file_name, os.F_OK): - raise FileExistsError( - "Destination file %s exists and allow_overwrite is False." - % new_file_name - ) + raise FileExistsError('Destination file %s exists and allow_overwrite is False.' % new_file_name) os.rename(old_file_name, new_file_name) return @@ -59,21 +53,14 @@ def file_move_safe( pass # first open the old file, so that it won't go away - with open(old_file_name, "rb") as old_file: + with open(old_file_name, 'rb') as old_file: # now open the new file, not forgetting allow_overwrite - fd = os.open( - new_file_name, - ( - os.O_WRONLY - | os.O_CREAT - | getattr(os, "O_BINARY", 0) - | (os.O_EXCL if not allow_overwrite else 0) - ), - ) + fd = os.open(new_file_name, (os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0) | + (os.O_EXCL if not allow_overwrite else 0))) try: locks.lock(fd, locks.LOCK_EX) current_chunk = None - while current_chunk != b"": + while current_chunk != b'': current_chunk = old_file.read(chunk_size) os.write(fd, current_chunk) finally: @@ -96,5 +83,5 @@ def file_move_safe( # fail when deleting opened files, ignore it. (For the # systems where this happens, temporary files will be auto-deleted # on close anyway.) - if getattr(e, "winerror", 0) != 32: + if getattr(e, 'winerror', 0) != 32: raise diff --git a/venv/Lib/site-packages/django/core/files/storage.py b/venv/Lib/site-packages/django/core/files/storage.py index 714f9c1..3e68853 100644 --- a/venv/Lib/site-packages/django/core/files/storage.py +++ b/venv/Lib/site-packages/django/core/files/storage.py @@ -19,11 +19,8 @@ from django.utils.module_loading import import_string from django.utils.text import get_valid_filename __all__ = ( - "Storage", - "FileSystemStorage", - "DefaultStorage", - "default_storage", - "get_storage_class", + 'Storage', 'FileSystemStorage', 'DefaultStorage', 'default_storage', + 'get_storage_class', ) @@ -36,7 +33,7 @@ class Storage: # The following methods represent a public interface to private methods. # These shouldn't be overridden by subclasses unless absolutely necessary. - def open(self, name, mode="rb"): + def open(self, name, mode='rb'): """Retrieve the specified file from storage.""" return self._open(name, mode) @@ -50,14 +47,11 @@ class Storage: if name is None: name = content.name - if not hasattr(content, "chunks"): + if not hasattr(content, 'chunks'): content = File(content, name) name = self.get_available_name(name, max_length=max_length) - name = self._save(name, content) - # Ensure that the name returned from the storage system is still valid. - validate_file_name(name, allow_relative_path=True) - return name + return self._save(name, content) # These methods are part of the public API, with default implementations. @@ -74,19 +68,16 @@ class Storage: character alphanumeric string (before the file extension, if one exists) to the filename. """ - return "%s_%s%s" % (file_root, get_random_string(7), file_ext) + return '%s_%s%s' % (file_root, get_random_string(7), file_ext) def get_available_name(self, name, max_length=None): """ Return a filename that's free on the target storage system and available for new content to be written to. """ - name = str(name).replace("\\", "/") dir_name, file_name = os.path.split(name) - if ".." in pathlib.PurePath(dir_name).parts: - raise SuspiciousFileOperation( - "Detected path traversal attempt in '%s'" % dir_name - ) + if '..' in pathlib.PurePath(dir_name).parts: + raise SuspiciousFileOperation("Detected path traversal attempt in '%s'" % dir_name) validate_file_name(file_name) file_root, file_ext = os.path.splitext(file_name) # If the filename already exists, generate an alternative filename @@ -95,26 +86,21 @@ class Storage: # exceed the max_length. while self.exists(name) or (max_length and len(name) > max_length): # file_ext includes the dot. - name = os.path.join( - dir_name, self.get_alternative_name(file_root, file_ext) - ) + name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) if max_length is None: continue # Truncate file_root if max_length exceeded. truncation = len(name) - max_length if truncation > 0: file_root = file_root[:-truncation] - # Entire file_root was truncated in attempt to find an - # available filename. + # Entire file_root was truncated in attempt to find an available filename. if not file_root: raise SuspiciousFileOperation( 'Storage can not find an available filename for "%s". ' - "Please make sure that the corresponding file field " + 'Please make sure that the corresponding file field ' 'allows sufficient "max_length".' % name ) - name = os.path.join( - dir_name, self.get_alternative_name(file_root, file_ext) - ) + name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) return name def generate_filename(self, filename): @@ -122,13 +108,10 @@ class Storage: Validate the filename by calling get_valid_name() and return a filename to be passed to the save() method. """ - filename = str(filename).replace("\\", "/") # `filename` may include a path as returned by FileField.upload_to. dirname, filename = os.path.split(filename) - if ".." in pathlib.PurePath(dirname).parts: - raise SuspiciousFileOperation( - "Detected path traversal attempt in '%s'" % dirname - ) + if '..' in pathlib.PurePath(dirname).parts: + raise SuspiciousFileOperation("Detected path traversal attempt in '%s'" % dirname) return os.path.normpath(os.path.join(dirname, self.get_valid_name(filename))) def path(self, name): @@ -146,67 +129,55 @@ class Storage: """ Delete the specified file from the storage system. """ - raise NotImplementedError( - "subclasses of Storage must provide a delete() method" - ) + raise NotImplementedError('subclasses of Storage must provide a delete() method') def exists(self, name): """ Return True if a file referenced by the given name already exists in the storage system, or False if the name is available for a new file. """ - raise NotImplementedError( - "subclasses of Storage must provide an exists() method" - ) + raise NotImplementedError('subclasses of Storage must provide an exists() method') def listdir(self, path): """ List the contents of the specified path. Return a 2-tuple of lists: the first item being directories, the second item being files. """ - raise NotImplementedError( - "subclasses of Storage must provide a listdir() method" - ) + raise NotImplementedError('subclasses of Storage must provide a listdir() method') def size(self, name): """ Return the total size, in bytes, of the file specified by name. """ - raise NotImplementedError("subclasses of Storage must provide a size() method") + raise NotImplementedError('subclasses of Storage must provide a size() method') def url(self, name): """ Return an absolute URL where the file's contents can be accessed - directly by a web browser. + directly by a Web browser. """ - raise NotImplementedError("subclasses of Storage must provide a url() method") + raise NotImplementedError('subclasses of Storage must provide a url() method') def get_accessed_time(self, name): """ Return the last accessed time (as a datetime) of the file specified by name. The datetime will be timezone-aware if USE_TZ=True. """ - raise NotImplementedError( - "subclasses of Storage must provide a get_accessed_time() method" - ) + raise NotImplementedError('subclasses of Storage must provide a get_accessed_time() method') def get_created_time(self, name): """ Return the creation time (as a datetime) of the file specified by name. The datetime will be timezone-aware if USE_TZ=True. """ - raise NotImplementedError( - "subclasses of Storage must provide a get_created_time() method" - ) + raise NotImplementedError('subclasses of Storage must provide a get_created_time() method') def get_modified_time(self, name): """ Return the last modified time (as a datetime) of the file specified by name. The datetime will be timezone-aware if USE_TZ=True. """ - raise NotImplementedError( - "subclasses of Storage must provide a get_modified_time() method" - ) + raise NotImplementedError('subclasses of Storage must provide a get_modified_time() method') @deconstructible @@ -214,18 +185,12 @@ class FileSystemStorage(Storage): """ Standard filesystem storage """ - # The combination of O_CREAT and O_EXCL makes os.open() raise OSError if # the file already exists before it's opened. - OS_OPEN_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_BINARY", 0) + OS_OPEN_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0) - def __init__( - self, - location=None, - base_url=None, - file_permissions_mode=None, - directory_permissions_mode=None, - ): + def __init__(self, location=None, base_url=None, file_permissions_mode=None, + directory_permissions_mode=None): self._location = location self._base_url = base_url self._file_permissions_mode = file_permissions_mode @@ -234,15 +199,15 @@ class FileSystemStorage(Storage): def _clear_cached_properties(self, setting, **kwargs): """Reset setting based property values.""" - if setting == "MEDIA_ROOT": - self.__dict__.pop("base_location", None) - self.__dict__.pop("location", None) - elif setting == "MEDIA_URL": - self.__dict__.pop("base_url", None) - elif setting == "FILE_UPLOAD_PERMISSIONS": - self.__dict__.pop("file_permissions_mode", None) - elif setting == "FILE_UPLOAD_DIRECTORY_PERMISSIONS": - self.__dict__.pop("directory_permissions_mode", None) + if setting == 'MEDIA_ROOT': + self.__dict__.pop('base_location', None) + self.__dict__.pop('location', None) + elif setting == 'MEDIA_URL': + self.__dict__.pop('base_url', None) + elif setting == 'FILE_UPLOAD_PERMISSIONS': + self.__dict__.pop('file_permissions_mode', None) + elif setting == 'FILE_UPLOAD_DIRECTORY_PERMISSIONS': + self.__dict__.pop('directory_permissions_mode', None) def _value_or_setting(self, value, setting): return setting if value is None else value @@ -257,23 +222,19 @@ class FileSystemStorage(Storage): @cached_property def base_url(self): - if self._base_url is not None and not self._base_url.endswith("/"): - self._base_url += "/" + if self._base_url is not None and not self._base_url.endswith('/'): + self._base_url += '/' return self._value_or_setting(self._base_url, settings.MEDIA_URL) @cached_property def file_permissions_mode(self): - return self._value_or_setting( - self._file_permissions_mode, settings.FILE_UPLOAD_PERMISSIONS - ) + return self._value_or_setting(self._file_permissions_mode, settings.FILE_UPLOAD_PERMISSIONS) @cached_property def directory_permissions_mode(self): - return self._value_or_setting( - self._directory_permissions_mode, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS - ) + return self._value_or_setting(self._directory_permissions_mode, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS) - def _open(self, name, mode="rb"): + def _open(self, name, mode='rb'): return File(open(self.path(name), mode)) def _save(self, name, content): @@ -287,15 +248,13 @@ class FileSystemStorage(Storage): # argument to intermediate-level directories. old_umask = os.umask(0o777 & ~self.directory_permissions_mode) try: - os.makedirs( - directory, self.directory_permissions_mode, exist_ok=True - ) + os.makedirs(directory, self.directory_permissions_mode, exist_ok=True) finally: os.umask(old_umask) else: os.makedirs(directory, exist_ok=True) except FileExistsError: - raise FileExistsError("%s exists and is not a directory." % directory) + raise FileExistsError('%s exists and is not a directory.' % directory) # There's a potential race condition between get_available_name and # saving the file; it's possible that two threads might return the @@ -306,7 +265,7 @@ class FileSystemStorage(Storage): while True: try: # This file has a file path that we can move. - if hasattr(content, "temporary_file_path"): + if hasattr(content, 'temporary_file_path'): file_move_safe(content.temporary_file_path(), full_path) # This is a normal uploadedfile that we can stream. @@ -318,7 +277,7 @@ class FileSystemStorage(Storage): locks.lock(fd, locks.LOCK_EX) for chunk in content.chunks(): if _file is None: - mode = "wb" if isinstance(chunk, bytes) else "wt" + mode = 'wb' if isinstance(chunk, bytes) else 'wt' _file = os.fdopen(fd, mode) _file.write(chunk) finally: @@ -338,14 +297,11 @@ class FileSystemStorage(Storage): if self.file_permissions_mode is not None: os.chmod(full_path, self.file_permissions_mode) - # Ensure the saved path is always relative to the storage root. - name = os.path.relpath(full_path, self.location) # Store filenames with forward slashes, even on Windows. - return str(name).replace("\\", "/") + return str(name).replace('\\', '/') def delete(self, name): - if not name: - raise ValueError("The name must be given to delete().") + assert name, "The name argument is not allowed to be empty." name = self.path(name) # If the file or directory exists, delete it from the filesystem. try: @@ -359,17 +315,16 @@ class FileSystemStorage(Storage): pass def exists(self, name): - return os.path.lexists(self.path(name)) + return os.path.exists(self.path(name)) def listdir(self, path): path = self.path(path) directories, files = [], [] - with os.scandir(path) as entries: - for entry in entries: - if entry.is_dir(): - directories.append(entry.name) - else: - files.append(entry.name) + for entry in os.scandir(path): + if entry.is_dir(): + directories.append(entry.name) + else: + files.append(entry.name) return directories, files def path(self, name): @@ -383,7 +338,7 @@ class FileSystemStorage(Storage): raise ValueError("This file is not accessible via a URL.") url = filepath_to_uri(name) if url is not None: - url = url.lstrip("/") + url = url.lstrip('/') return urljoin(self.base_url, url) def _datetime_from_timestamp(self, ts): @@ -391,8 +346,11 @@ class FileSystemStorage(Storage): If timezone support is enabled, make an aware datetime object in UTC; otherwise make a naive one in the local timezone. """ - tz = timezone.utc if settings.USE_TZ else None - return datetime.fromtimestamp(ts, tz=tz) + if settings.USE_TZ: + # Safe to use .replace() because UTC doesn't have DST + return datetime.utcfromtimestamp(ts).replace(tzinfo=timezone.utc) + else: + return datetime.fromtimestamp(ts) def get_accessed_time(self, name): return self._datetime_from_timestamp(os.path.getatime(self.path(name))) diff --git a/venv/Lib/site-packages/django/core/files/temp.py b/venv/Lib/site-packages/django/core/files/temp.py index 5bd31dd..57a8107 100644 --- a/venv/Lib/site-packages/django/core/files/temp.py +++ b/venv/Lib/site-packages/django/core/files/temp.py @@ -21,14 +21,10 @@ import tempfile from django.core.files.utils import FileProxyMixin -__all__ = ( - "NamedTemporaryFile", - "gettempdir", -) +__all__ = ('NamedTemporaryFile', 'gettempdir',) -if os.name == "nt": - +if os.name == 'nt': class TemporaryFile(FileProxyMixin): """ Temporary file object constructor that supports reopening of the @@ -38,8 +34,7 @@ if os.name == "nt": __init__() doesn't support the 'delete', 'buffering', 'encoding', or 'newline' keyword arguments. """ - - def __init__(self, mode="w+b", bufsize=-1, suffix="", prefix="", dir=None): + def __init__(self, mode='w+b', bufsize=-1, suffix='', prefix='', dir=None): fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) self.name = name self.file = os.fdopen(fd, mode, bufsize) diff --git a/venv/Lib/site-packages/django/core/files/uploadedfile.py b/venv/Lib/site-packages/django/core/files/uploadedfile.py index efbfcac..f452bcd 100644 --- a/venv/Lib/site-packages/django/core/files/uploadedfile.py +++ b/venv/Lib/site-packages/django/core/files/uploadedfile.py @@ -10,12 +10,8 @@ from django.core.files import temp as tempfile from django.core.files.base import File from django.core.files.utils import validate_file_name -__all__ = ( - "UploadedFile", - "TemporaryUploadedFile", - "InMemoryUploadedFile", - "SimpleUploadedFile", -) +__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile', + 'SimpleUploadedFile') class UploadedFile(File): @@ -27,15 +23,7 @@ class UploadedFile(File): represents some file data that the user submitted with a form. """ - def __init__( - self, - file=None, - name=None, - content_type=None, - size=None, - charset=None, - content_type_extra=None, - ): + def __init__(self, file=None, name=None, content_type=None, size=None, charset=None, content_type_extra=None): super().__init__(file, name) self.size = size self.content_type = content_type @@ -58,7 +46,7 @@ class UploadedFile(File): if len(name) > 255: name, ext = os.path.splitext(name) ext = ext[:255] - name = name[: 255 - len(ext)] + ext + name = name[:255 - len(ext)] + ext name = validate_file_name(name) @@ -71,12 +59,9 @@ class TemporaryUploadedFile(UploadedFile): """ A file uploaded to a temporary location (i.e. stream-to-disk). """ - def __init__(self, name, content_type, size, charset, content_type_extra=None): _, ext = os.path.splitext(name) - file = tempfile.NamedTemporaryFile( - suffix=".upload" + ext, dir=settings.FILE_UPLOAD_TEMP_DIR - ) + file = tempfile.NamedTemporaryFile(suffix='.upload' + ext, dir=settings.FILE_UPLOAD_TEMP_DIR) super().__init__(file, name, content_type, size, charset, content_type_extra) def temporary_file_path(self): @@ -97,17 +82,7 @@ class InMemoryUploadedFile(UploadedFile): """ A file uploaded into memory (i.e. stream-to-memory). """ - - def __init__( - self, - file, - field_name, - name, - content_type, - size, - charset, - content_type_extra=None, - ): + def __init__(self, file, field_name, name, content_type, size, charset, content_type_extra=None): super().__init__(file, name, content_type, size, charset, content_type_extra) self.field_name = field_name @@ -128,12 +103,9 @@ class SimpleUploadedFile(InMemoryUploadedFile): """ A simple representation of a file, which just has content, size, and a name. """ - - def __init__(self, name, content, content_type="text/plain"): - content = content or b"" - super().__init__( - BytesIO(content), None, name, content_type, len(content), None, None - ) + def __init__(self, name, content, content_type='text/plain'): + content = content or b'' + super().__init__(BytesIO(content), None, name, content_type, len(content), None, None) @classmethod def from_dict(cls, file_dict): @@ -143,8 +115,6 @@ class SimpleUploadedFile(InMemoryUploadedFile): - content-type - content """ - return cls( - file_dict["filename"], - file_dict["content"], - file_dict.get("content-type", "text/plain"), - ) + return cls(file_dict['filename'], + file_dict['content'], + file_dict.get('content-type', 'text/plain')) diff --git a/venv/Lib/site-packages/django/core/files/uploadhandler.py b/venv/Lib/site-packages/django/core/files/uploadhandler.py index b6c185e..ee6bb31 100644 --- a/venv/Lib/site-packages/django/core/files/uploadhandler.py +++ b/venv/Lib/site-packages/django/core/files/uploadhandler.py @@ -5,18 +5,15 @@ import os from io import BytesIO from django.conf import settings -from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile +from django.core.files.uploadedfile import ( + InMemoryUploadedFile, TemporaryUploadedFile, +) from django.utils.module_loading import import_string __all__ = [ - "UploadFileException", - "StopUpload", - "SkipFile", - "FileUploadHandler", - "TemporaryFileUploadHandler", - "MemoryFileUploadHandler", - "load_handler", - "StopFutureHandlers", + 'UploadFileException', 'StopUpload', 'SkipFile', 'FileUploadHandler', + 'TemporaryFileUploadHandler', 'MemoryFileUploadHandler', 'load_handler', + 'StopFutureHandlers' ] @@ -24,7 +21,6 @@ class UploadFileException(Exception): """ Any error having to do with uploading files. """ - pass @@ -32,7 +28,6 @@ class StopUpload(UploadFileException): """ This exception is raised when an upload must abort. """ - def __init__(self, connection_reset=False): """ If ``connection_reset`` is ``True``, Django knows will halt the upload @@ -43,16 +38,15 @@ class StopUpload(UploadFileException): def __str__(self): if self.connection_reset: - return "StopUpload: Halt current upload." + return 'StopUpload: Halt current upload.' else: - return "StopUpload: Consume request data, then halt." + return 'StopUpload: Consume request data, then halt.' class SkipFile(UploadFileException): """ This exception is raised by an upload handler that wants to skip a given file. """ - pass @@ -61,7 +55,6 @@ class StopFutureHandlers(UploadFileException): Upload handlers that have handled a file and do not want future handlers to run should raise this exception instead of returning None. """ - pass @@ -69,8 +62,7 @@ class FileUploadHandler: """ Base class for streaming upload handlers. """ - - chunk_size = 64 * 2**10 # : The default chunk size is 64 KB. + chunk_size = 64 * 2 ** 10 # : The default chunk size is 64 KB. def __init__(self, request=None): self.file_name = None @@ -80,9 +72,7 @@ class FileUploadHandler: self.content_type_extra = None self.request = request - def handle_raw_input( - self, input_data, META, content_length, boundary, encoding=None - ): + def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): """ Handle the raw input from the client. @@ -100,15 +90,7 @@ class FileUploadHandler: """ pass - def new_file( - self, - field_name, - file_name, - content_type, - content_length, - charset=None, - content_type_extra=None, - ): + def new_file(self, field_name, file_name, content_type, content_length, charset=None, content_type_extra=None): """ Signal that a new file has been started. @@ -127,9 +109,7 @@ class FileUploadHandler: Receive data from the streamed upload parser. ``start`` is the position in the file of the chunk. """ - raise NotImplementedError( - "subclasses of FileUploadHandler must provide a receive_data_chunk() method" - ) + raise NotImplementedError('subclasses of FileUploadHandler must provide a receive_data_chunk() method') def file_complete(self, file_size): """ @@ -138,9 +118,7 @@ class FileUploadHandler: Subclasses should return a valid ``UploadedFile`` object. """ - raise NotImplementedError( - "subclasses of FileUploadHandler must provide a file_complete() method" - ) + raise NotImplementedError('subclasses of FileUploadHandler must provide a file_complete() method') def upload_complete(self): """ @@ -161,15 +139,12 @@ class TemporaryFileUploadHandler(FileUploadHandler): """ Upload handler that streams data into a temporary file. """ - def new_file(self, *args, **kwargs): """ Create the file object to append to as data is coming in. """ super().new_file(*args, **kwargs) - self.file = TemporaryUploadedFile( - self.file_name, self.content_type, 0, self.charset, self.content_type_extra - ) + self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset, self.content_type_extra) def receive_data_chunk(self, raw_data, start): self.file.write(raw_data) @@ -180,7 +155,7 @@ class TemporaryFileUploadHandler(FileUploadHandler): return self.file def upload_interrupted(self): - if hasattr(self, "file"): + if hasattr(self, 'file'): temp_location = self.file.temporary_file_path() try: self.file.close() @@ -194,9 +169,7 @@ class MemoryFileUploadHandler(FileUploadHandler): File upload handler to stream uploads into memory (used for small files). """ - def handle_raw_input( - self, input_data, META, content_length, boundary, encoding=None - ): + def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): """ Use the content_length to signal whether or not this handler should be used. @@ -231,7 +204,7 @@ class MemoryFileUploadHandler(FileUploadHandler): content_type=self.content_type, size=file_size, charset=self.charset, - content_type_extra=self.content_type_extra, + content_type_extra=self.content_type_extra ) @@ -242,10 +215,7 @@ def load_handler(path, *args, **kwargs): E.g.:: >>> from django.http import HttpRequest >>> request = HttpRequest() - >>> load_handler( - ... 'django.core.files.uploadhandler.TemporaryFileUploadHandler', - ... request, - ... ) + >>> load_handler('django.core.files.uploadhandler.TemporaryFileUploadHandler', request) <TemporaryFileUploadHandler object at 0x...> """ return import_string(path)(*args, **kwargs) diff --git a/venv/Lib/site-packages/django/core/files/utils.py b/venv/Lib/site-packages/django/core/files/utils.py index 85342b2..f28cea1 100644 --- a/venv/Lib/site-packages/django/core/files/utils.py +++ b/venv/Lib/site-packages/django/core/files/utils.py @@ -6,7 +6,7 @@ from django.core.exceptions import SuspiciousFileOperation def validate_file_name(name, allow_relative_path=False): # Remove potentially dangerous names - if os.path.basename(name) in {"", ".", ".."}: + if os.path.basename(name) in {'', '.', '..'}: raise SuspiciousFileOperation("Could not derive file name from '%s'" % name) if allow_relative_path: @@ -14,7 +14,7 @@ def validate_file_name(name, allow_relative_path=False): # FileField.generate_filename() where all file paths are expected to be # Unix style (with forward slashes). path = pathlib.PurePosixPath(name) - if path.is_absolute() or ".." in path.parts: + if path.is_absolute() or '..' in path.parts: raise SuspiciousFileOperation( "Detected path traversal attempt in '%s'" % name ) @@ -56,21 +56,21 @@ class FileProxyMixin: def readable(self): if self.closed: return False - if hasattr(self.file, "readable"): + if hasattr(self.file, 'readable'): return self.file.readable() return True def writable(self): if self.closed: return False - if hasattr(self.file, "writable"): + if hasattr(self.file, 'writable'): return self.file.writable() - return "w" in getattr(self.file, "mode", "") + return 'w' in getattr(self.file, 'mode', '') def seekable(self): if self.closed: return False - if hasattr(self.file, "seekable"): + if hasattr(self.file, 'seekable'): return self.file.seekable() return True diff --git a/venv/Lib/site-packages/django/core/handlers/asgi.py b/venv/Lib/site-packages/django/core/handlers/asgi.py index 7b17c58..7fbabe4 100644 --- a/venv/Lib/site-packages/django/core/handlers/asgi.py +++ b/venv/Lib/site-packages/django/core/handlers/asgi.py @@ -3,25 +3,20 @@ import sys import tempfile import traceback -from asgiref.sync import ThreadSensitiveContext, sync_to_async +from asgiref.sync import sync_to_async from django.conf import settings from django.core import signals from django.core.exceptions import RequestAborted, RequestDataTooBig from django.core.handlers import base from django.http import ( - FileResponse, - HttpRequest, - HttpResponse, - HttpResponseBadRequest, - HttpResponseServerError, - QueryDict, - parse_cookie, + FileResponse, HttpRequest, HttpResponse, HttpResponseBadRequest, + HttpResponseServerError, QueryDict, parse_cookie, ) from django.urls import set_script_prefix from django.utils.functional import cached_property -logger = logging.getLogger("django.request") +logger = logging.getLogger('django.request') class ASGIRequest(HttpRequest): @@ -29,7 +24,6 @@ class ASGIRequest(HttpRequest): Custom request subclass that decodes from an ASGI-standard request dict and wraps request body handling. """ - # Number of seconds until a Request gives up on trying to read a request # body and aborts. body_receive_timeout = 60 @@ -39,60 +33,60 @@ class ASGIRequest(HttpRequest): self._post_parse_error = False self._read_started = False self.resolver_match = None - self.script_name = self.scope.get("root_path", "") - if self.script_name and scope["path"].startswith(self.script_name): + self.script_name = self.scope.get('root_path', '') + if self.script_name and scope['path'].startswith(self.script_name): # TODO: Better is-prefix checking, slash handling? - self.path_info = scope["path"][len(self.script_name) :] + self.path_info = scope['path'][len(self.script_name):] else: - self.path_info = scope["path"] + self.path_info = scope['path'] # The Django path is different from ASGI scope path args, it should # combine with script name. if self.script_name: - self.path = "%s/%s" % ( - self.script_name.rstrip("/"), - self.path_info.replace("/", "", 1), + self.path = '%s/%s' % ( + self.script_name.rstrip('/'), + self.path_info.replace('/', '', 1), ) else: - self.path = scope["path"] + self.path = scope['path'] # HTTP basics. - self.method = self.scope["method"].upper() + self.method = self.scope['method'].upper() # Ensure query string is encoded correctly. - query_string = self.scope.get("query_string", "") + query_string = self.scope.get('query_string', '') if isinstance(query_string, bytes): query_string = query_string.decode() self.META = { - "REQUEST_METHOD": self.method, - "QUERY_STRING": query_string, - "SCRIPT_NAME": self.script_name, - "PATH_INFO": self.path_info, + 'REQUEST_METHOD': self.method, + 'QUERY_STRING': query_string, + 'SCRIPT_NAME': self.script_name, + 'PATH_INFO': self.path_info, # WSGI-expecting code will need these for a while - "wsgi.multithread": True, - "wsgi.multiprocess": True, + 'wsgi.multithread': True, + 'wsgi.multiprocess': True, } - if self.scope.get("client"): - self.META["REMOTE_ADDR"] = self.scope["client"][0] - self.META["REMOTE_HOST"] = self.META["REMOTE_ADDR"] - self.META["REMOTE_PORT"] = self.scope["client"][1] - if self.scope.get("server"): - self.META["SERVER_NAME"] = self.scope["server"][0] - self.META["SERVER_PORT"] = str(self.scope["server"][1]) + if self.scope.get('client'): + self.META['REMOTE_ADDR'] = self.scope['client'][0] + self.META['REMOTE_HOST'] = self.META['REMOTE_ADDR'] + self.META['REMOTE_PORT'] = self.scope['client'][1] + if self.scope.get('server'): + self.META['SERVER_NAME'] = self.scope['server'][0] + self.META['SERVER_PORT'] = str(self.scope['server'][1]) else: - self.META["SERVER_NAME"] = "unknown" - self.META["SERVER_PORT"] = "0" + self.META['SERVER_NAME'] = 'unknown' + self.META['SERVER_PORT'] = '0' # Headers go into META. - for name, value in self.scope.get("headers", []): - name = name.decode("latin1") - if name == "content-length": - corrected_name = "CONTENT_LENGTH" - elif name == "content-type": - corrected_name = "CONTENT_TYPE" + for name, value in self.scope.get('headers', []): + name = name.decode('latin1') + if name == 'content-length': + corrected_name = 'CONTENT_LENGTH' + elif name == 'content-type': + corrected_name = 'CONTENT_TYPE' else: - corrected_name = "HTTP_%s" % name.upper().replace("-", "_") + corrected_name = 'HTTP_%s' % name.upper().replace('-', '_') # HTTP/2 say only ASCII chars are allowed in headers, but decode # latin1 just in case. - value = value.decode("latin1") + value = value.decode('latin1') if corrected_name in self.META: - value = self.META[corrected_name] + "," + value + value = self.META[corrected_name] + ',' + value self.META[corrected_name] = value # Pull out request encoding, if provided. self._set_content_type_params(self.META) @@ -103,13 +97,13 @@ class ASGIRequest(HttpRequest): @cached_property def GET(self): - return QueryDict(self.META["QUERY_STRING"]) + return QueryDict(self.META['QUERY_STRING']) def _get_scheme(self): - return self.scope.get("scheme") or super()._get_scheme() + return self.scope.get('scheme') or super()._get_scheme() def _get_post(self): - if not hasattr(self, "_post"): + if not hasattr(self, '_post'): self._load_post_and_files() return self._post @@ -117,7 +111,7 @@ class ASGIRequest(HttpRequest): self._post = post def _get_files(self): - if not hasattr(self, "_files"): + if not hasattr(self, '_files'): self._load_post_and_files() return self._files @@ -126,15 +120,14 @@ class ASGIRequest(HttpRequest): @cached_property def COOKIES(self): - return parse_cookie(self.META.get("HTTP_COOKIE", "")) + return parse_cookie(self.META.get('HTTP_COOKIE', '')) class ASGIHandler(base.BaseHandler): """Handler for ASGI requests.""" - request_class = ASGIRequest # Size to chunk response bodies into for multiple response messages. - chunk_size = 2**16 + chunk_size = 2 ** 16 def __init__(self): super().__init__() @@ -146,18 +139,11 @@ class ASGIHandler(base.BaseHandler): """ # Serve only HTTP connections. # FIXME: Allow to override this. - if scope["type"] != "http": + if scope['type'] != 'http': raise ValueError( - "Django can only handle ASGI/HTTP connections, not %s." % scope["type"] + 'Django can only handle ASGI/HTTP connections, not %s.' + % scope['type'] ) - - async with ThreadSensitiveContext(): - await self.handle(scope, receive, send) - - async def handle(self, scope, receive, send): - """ - Handles the ASGI request. Called via the __call__ method. - """ # Receive the HTTP request body as a stream object. try: body_file = await self.read_body(receive) @@ -165,9 +151,7 @@ class ASGIHandler(base.BaseHandler): return # Request is complete and can be served. set_script_prefix(self.get_script_prefix(scope)) - await sync_to_async(signals.request_started.send, thread_sensitive=True)( - sender=self.__class__, scope=scope - ) + await sync_to_async(signals.request_started.send, thread_sensitive=True)(sender=self.__class__, scope=scope) # Get the request and check for basic issues. request, error_response = self.create_request(scope, body_file) if request is None: @@ -184,21 +168,19 @@ class ASGIHandler(base.BaseHandler): await self.send_response(response, send) async def read_body(self, receive): - """Reads an HTTP body from an ASGI connection.""" + """Reads a HTTP body from an ASGI connection.""" # Use the tempfile that auto rolls-over to a disk file as it fills up. - body_file = tempfile.SpooledTemporaryFile( - max_size=settings.FILE_UPLOAD_MAX_MEMORY_SIZE, mode="w+b" - ) + body_file = tempfile.SpooledTemporaryFile(max_size=settings.FILE_UPLOAD_MAX_MEMORY_SIZE, mode='w+b') while True: message = await receive() - if message["type"] == "http.disconnect": + if message['type'] == 'http.disconnect': # Early client disconnect. raise RequestAborted() # Add a body chunk from the message, if provided. - if "body" in message: - body_file.write(message["body"]) + if 'body' in message: + body_file.write(message['body']) # Quit out if that's the end. - if not message.get("more_body", False): + if not message.get('more_body', False): break body_file.seek(0) return body_file @@ -212,13 +194,13 @@ class ASGIHandler(base.BaseHandler): return self.request_class(scope, body_file), None except UnicodeDecodeError: logger.warning( - "Bad Request (UnicodeDecodeError)", + 'Bad Request (UnicodeDecodeError)', exc_info=sys.exc_info(), - extra={"status_code": 400}, + extra={'status_code': 400}, ) return None, HttpResponseBadRequest() except RequestDataTooBig: - return None, HttpResponse("413 Payload too large", status=413) + return None, HttpResponse('413 Payload too large', status=413) def handle_uncaught_exception(self, request, resolver, exc_info): """Last-chance handler for exceptions.""" @@ -228,8 +210,8 @@ class ASGIHandler(base.BaseHandler): return super().handle_uncaught_exception(request, resolver, exc_info) except Exception: return HttpResponseServerError( - traceback.format_exc() if settings.DEBUG else "Internal Server Error", - content_type="text/plain", + traceback.format_exc() if settings.DEBUG else 'Internal Server Error', + content_type='text/plain', ) async def send_response(self, response, send): @@ -239,50 +221,44 @@ class ASGIHandler(base.BaseHandler): response_headers = [] for header, value in response.items(): if isinstance(header, str): - header = header.encode("ascii") + header = header.encode('ascii') if isinstance(value, str): - value = value.encode("latin1") + value = value.encode('latin1') response_headers.append((bytes(header), bytes(value))) for c in response.cookies.values(): response_headers.append( - (b"Set-Cookie", c.output(header="").encode("ascii").strip()) + (b'Set-Cookie', c.output(header='').encode('ascii').strip()) ) # Initial response message. - await send( - { - "type": "http.response.start", - "status": response.status_code, - "headers": response_headers, - } - ) + await send({ + 'type': 'http.response.start', + 'status': response.status_code, + 'headers': response_headers, + }) # Streaming responses need to be pinned to their iterator. if response.streaming: # Access `__iter__` and not `streaming_content` directly in case # it has been overridden in a subclass. for part in response: for chunk, _ in self.chunk_bytes(part): - await send( - { - "type": "http.response.body", - "body": chunk, - # Ignore "more" as there may be more parts; instead, - # use an empty final closing message with False. - "more_body": True, - } - ) + await send({ + 'type': 'http.response.body', + 'body': chunk, + # Ignore "more" as there may be more parts; instead, + # use an empty final closing message with False. + 'more_body': True, + }) # Final closing message. - await send({"type": "http.response.body"}) + await send({'type': 'http.response.body'}) # Other responses just need chunking. else: # Yield chunks of response. for chunk, last in self.chunk_bytes(response.content): - await send( - { - "type": "http.response.body", - "body": chunk, - "more_body": not last, - } - ) + await send({ + 'type': 'http.response.body', + 'body': chunk, + 'more_body': not last, + }) await sync_to_async(response.close, thread_sensitive=True)() @classmethod @@ -297,7 +273,7 @@ class ASGIHandler(base.BaseHandler): return while position < len(data): yield ( - data[position : position + cls.chunk_size], + data[position:position + cls.chunk_size], (position + cls.chunk_size) >= len(data), ) position += cls.chunk_size @@ -308,4 +284,4 @@ class ASGIHandler(base.BaseHandler): """ if settings.FORCE_SCRIPT_NAME: return settings.FORCE_SCRIPT_NAME - return scope.get("root_path", "") or "" + return scope.get('root_path', '') or '' diff --git a/venv/Lib/site-packages/django/core/handlers/base.py b/venv/Lib/site-packages/django/core/handlers/base.py index 0f341a5..728e449 100644 --- a/venv/Lib/site-packages/django/core/handlers/base.py +++ b/venv/Lib/site-packages/django/core/handlers/base.py @@ -14,7 +14,7 @@ from django.utils.module_loading import import_string from .exception import convert_exception_to_response -logger = logging.getLogger("django.request") +logger = logging.getLogger('django.request') class BaseHandler: @@ -38,12 +38,12 @@ class BaseHandler: handler_is_async = is_async for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) - middleware_can_sync = getattr(middleware, "sync_capable", True) - middleware_can_async = getattr(middleware, "async_capable", False) + middleware_can_sync = getattr(middleware, 'sync_capable', True) + middleware_can_async = getattr(middleware, 'async_capable', False) if not middleware_can_sync and not middleware_can_async: raise RuntimeError( - "Middleware %s must have at least one of " - "sync_capable/async_capable set to True." % middleware_path + 'Middleware %s must have at least one of ' + 'sync_capable/async_capable set to True.' % middleware_path ) elif not handler_is_async and middleware_can_sync: middleware_is_async = False @@ -52,40 +52,35 @@ class BaseHandler: try: # Adapt handler, if needed. adapted_handler = self.adapt_method_mode( - middleware_is_async, - handler, - handler_is_async, - debug=settings.DEBUG, - name="middleware %s" % middleware_path, + middleware_is_async, handler, handler_is_async, + debug=settings.DEBUG, name='middleware %s' % middleware_path, ) mw_instance = middleware(adapted_handler) except MiddlewareNotUsed as exc: if settings.DEBUG: if str(exc): - logger.debug("MiddlewareNotUsed(%r): %s", middleware_path, exc) + logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) else: - logger.debug("MiddlewareNotUsed: %r", middleware_path) + logger.debug('MiddlewareNotUsed: %r', middleware_path) continue else: handler = adapted_handler if mw_instance is None: raise ImproperlyConfigured( - "Middleware factory %s returned None." % middleware_path + 'Middleware factory %s returned None.' % middleware_path ) - if hasattr(mw_instance, "process_view"): + if hasattr(mw_instance, 'process_view'): self._view_middleware.insert( 0, self.adapt_method_mode(is_async, mw_instance.process_view), ) - if hasattr(mw_instance, "process_template_response"): + if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.append( - self.adapt_method_mode( - is_async, mw_instance.process_template_response - ), + self.adapt_method_mode(is_async, mw_instance.process_template_response), ) - if hasattr(mw_instance, "process_exception"): + if hasattr(mw_instance, 'process_exception'): # The exception-handling stack is still always synchronous for # now, so adapt that way. self._exception_middleware.append( @@ -102,12 +97,7 @@ class BaseHandler: self._middleware_chain = handler def adapt_method_mode( - self, - is_async, - method, - method_is_async=None, - debug=False, - name=None, + self, is_async, method, method_is_async=None, debug=False, name=None, ): """ Adapt a method to be in the correct "mode": @@ -121,15 +111,15 @@ class BaseHandler: if method_is_async is None: method_is_async = asyncio.iscoroutinefunction(method) if debug and not name: - name = name or "method %s()" % method.__qualname__ + name = name or 'method %s()' % method.__qualname__ if is_async: if not method_is_async: if debug: - logger.debug("Synchronous %s adapted.", name) + logger.debug('Synchronous %s adapted.', name) return sync_to_async(method, thread_sensitive=True) elif method_is_async: if debug: - logger.debug("Asynchronous %s adapted.", name) + logger.debug('Asynchronous %s adapted.', name) return async_to_sync(method) return method @@ -141,9 +131,7 @@ class BaseHandler: response._resource_closers.append(request.close) if response.status_code >= 400: log_response( - "%s: %s", - response.reason_phrase, - request.path, + '%s: %s', response.reason_phrase, request.path, response=response, request=request, ) @@ -163,9 +151,7 @@ class BaseHandler: response._resource_closers.append(request.close) if response.status_code >= 400: await sync_to_async(log_response, thread_sensitive=False)( - "%s: %s", - response.reason_phrase, - request.path, + '%s: %s', response.reason_phrase, request.path, response=response, request=request, ) @@ -182,9 +168,7 @@ class BaseHandler: # Apply view middleware for middleware_method in self._view_middleware: - response = middleware_method( - request, callback, callback_args, callback_kwargs - ) + response = middleware_method(request, callback, callback_args, callback_kwargs) if response: break @@ -205,16 +189,16 @@ class BaseHandler: # If the response supports deferred rendering, apply template # response middleware and then render the response - if hasattr(response, "render") and callable(response.render): + if hasattr(response, 'render') and callable(response.render): for middleware_method in self._template_response_middleware: response = middleware_method(request, response) - # Complain if the template response middleware returned None - # (a common error). + # Complain if the template response middleware returned None (a common error). self.check_response( response, middleware_method, - name="%s.process_template_response" - % (middleware_method.__self__.__class__.__name__,), + name='%s.process_template_response' % ( + middleware_method.__self__.__class__.__name__, + ) ) try: response = response.render() @@ -236,9 +220,7 @@ class BaseHandler: # Apply view middleware. for middleware_method in self._view_middleware: - response = await middleware_method( - request, callback, callback_args, callback_kwargs - ) + response = await middleware_method(request, callback, callback_args, callback_kwargs) if response: break @@ -246,13 +228,9 @@ class BaseHandler: wrapped_callback = self.make_view_atomic(callback) # If it is a synchronous view, run it in a subthread if not asyncio.iscoroutinefunction(wrapped_callback): - wrapped_callback = sync_to_async( - wrapped_callback, thread_sensitive=True - ) + wrapped_callback = sync_to_async(wrapped_callback, thread_sensitive=True) try: - response = await wrapped_callback( - request, *callback_args, **callback_kwargs - ) + response = await wrapped_callback(request, *callback_args, **callback_kwargs) except Exception as e: response = await sync_to_async( self.process_exception_by_middleware, @@ -266,7 +244,7 @@ class BaseHandler: # If the response supports deferred rendering, apply template # response middleware and then render the response - if hasattr(response, "render") and callable(response.render): + if hasattr(response, 'render') and callable(response.render): for middleware_method in self._template_response_middleware: response = await middleware_method(request, response) # Complain if the template response middleware returned None or @@ -274,16 +252,15 @@ class BaseHandler: self.check_response( response, middleware_method, - name="%s.process_template_response" - % (middleware_method.__self__.__class__.__name__,), + name='%s.process_template_response' % ( + middleware_method.__self__.__class__.__name__, + ) ) try: if asyncio.iscoroutinefunction(response.render): response = await response.render() else: - response = await sync_to_async( - response.render, thread_sensitive=True - )() + response = await sync_to_async(response.render, thread_sensitive=True)() except Exception as e: response = await sync_to_async( self.process_exception_by_middleware, @@ -294,7 +271,7 @@ class BaseHandler: # Make sure the response is not a coroutine if asyncio.iscoroutine(response): - raise RuntimeError("Response is still a coroutine.") + raise RuntimeError('Response is still a coroutine.') return response def resolve_request(self, request): @@ -303,7 +280,7 @@ class BaseHandler: with its args and kwargs. """ # Work out the resolver. - if hasattr(request, "urlconf"): + if hasattr(request, 'urlconf'): urlconf = request.urlconf set_urlconf(urlconf) resolver = get_resolver(urlconf) @@ -318,13 +295,13 @@ class BaseHandler: """ Raise an error if the view returned None or an uncalled coroutine. """ - if not (response is None or asyncio.iscoroutine(response)): + if not(response is None or asyncio.iscoroutine(response)): return if not name: if isinstance(callback, types.FunctionType): # FBV - name = "The view %s.%s" % (callback.__module__, callback.__name__) + name = 'The view %s.%s' % (callback.__module__, callback.__name__) else: # CBV - name = "The view %s.%s.__call__" % ( + name = 'The view %s.%s.__call__' % ( callback.__module__, callback.__class__.__name__, ) @@ -343,15 +320,12 @@ class BaseHandler: # Other utility methods. def make_view_atomic(self, view): - non_atomic_requests = getattr(view, "_non_atomic_requests", set()) + non_atomic_requests = getattr(view, '_non_atomic_requests', set()) for db in connections.all(): - if ( - db.settings_dict["ATOMIC_REQUESTS"] - and db.alias not in non_atomic_requests - ): + if db.settings_dict['ATOMIC_REQUESTS'] and db.alias not in non_atomic_requests: if asyncio.iscoroutinefunction(view): raise RuntimeError( - "You cannot use ATOMIC_REQUESTS with async views." + 'You cannot use ATOMIC_REQUESTS with async views.' ) view = transaction.atomic(using=db.alias)(view) return view diff --git a/venv/Lib/site-packages/django/core/handlers/exception.py b/venv/Lib/site-packages/django/core/handlers/exception.py index 622c531..3005a5e 100644 --- a/venv/Lib/site-packages/django/core/handlers/exception.py +++ b/venv/Lib/site-packages/django/core/handlers/exception.py @@ -8,10 +8,7 @@ from asgiref.sync import sync_to_async from django.conf import settings from django.core import signals from django.core.exceptions import ( - BadRequest, - PermissionDenied, - RequestDataTooBig, - SuspiciousOperation, + BadRequest, PermissionDenied, RequestDataTooBig, SuspiciousOperation, TooManyFieldsSent, ) from django.http import Http404 @@ -35,20 +32,15 @@ def convert_exception_to_response(get_response): can rely on getting a response instead of an exception. """ if asyncio.iscoroutinefunction(get_response): - @wraps(get_response) async def inner(request): try: response = await get_response(request) except Exception as exc: - response = await sync_to_async( - response_for_exception, thread_sensitive=False - )(request, exc) + response = await sync_to_async(response_for_exception, thread_sensitive=False)(request, exc) return response - return inner else: - @wraps(get_response) def inner(request): try: @@ -56,7 +48,6 @@ def convert_exception_to_response(get_response): except Exception as exc: response = response_for_exception(request, exc) return response - return inner @@ -65,29 +56,21 @@ def response_for_exception(request, exc): if settings.DEBUG: response = debug.technical_404_response(request, exc) else: - response = get_exception_response( - request, get_resolver(get_urlconf()), 404, exc - ) + response = get_exception_response(request, get_resolver(get_urlconf()), 404, exc) elif isinstance(exc, PermissionDenied): - response = get_exception_response( - request, get_resolver(get_urlconf()), 403, exc - ) + response = get_exception_response(request, get_resolver(get_urlconf()), 403, exc) log_response( - "Forbidden (Permission denied): %s", - request.path, + 'Forbidden (Permission denied): %s', request.path, response=response, request=request, exc_info=sys.exc_info(), ) elif isinstance(exc, MultiPartParserError): - response = get_exception_response( - request, get_resolver(get_urlconf()), 400, exc - ) + response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc) log_response( - "Bad request (Unable to parse request body): %s", - request.path, + 'Bad request (Unable to parse request body): %s', request.path, response=response, request=request, exc_info=sys.exc_info(), @@ -95,17 +78,11 @@ def response_for_exception(request, exc): elif isinstance(exc, BadRequest): if settings.DEBUG: - response = debug.technical_500_response( - request, *sys.exc_info(), status_code=400 - ) + response = debug.technical_500_response(request, *sys.exc_info(), status_code=400) else: - response = get_exception_response( - request, get_resolver(get_urlconf()), 400, exc - ) + response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc) log_response( - "%s: %s", - str(exc), - request.path, + '%s: %s', str(exc), request.path, response=response, request=request, exc_info=sys.exc_info(), @@ -118,40 +95,32 @@ def response_for_exception(request, exc): # The request logger receives events for any problematic request # The security logger receives events for all SuspiciousOperations - security_logger = logging.getLogger( - "django.security.%s" % exc.__class__.__name__ - ) + security_logger = logging.getLogger('django.security.%s' % exc.__class__.__name__) security_logger.error( str(exc), - extra={"status_code": 400, "request": request}, + extra={'status_code': 400, 'request': request}, ) if settings.DEBUG: - response = debug.technical_500_response( - request, *sys.exc_info(), status_code=400 - ) + response = debug.technical_500_response(request, *sys.exc_info(), status_code=400) else: - response = get_exception_response( - request, get_resolver(get_urlconf()), 400, exc - ) + response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc) + + elif isinstance(exc, SystemExit): + # Allow sys.exit() to actually exit. See tickets #1023 and #4701 + raise else: signals.got_request_exception.send(sender=None, request=request) - response = handle_uncaught_exception( - request, get_resolver(get_urlconf()), sys.exc_info() - ) + response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) log_response( - "%s: %s", - response.reason_phrase, - request.path, + '%s: %s', response.reason_phrase, request.path, response=response, request=request, exc_info=sys.exc_info(), ) # Force a TemplateResponse to be rendered. - if not getattr(response, "is_rendered", True) and callable( - getattr(response, "render", None) - ): + if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)): response = response.render() return response diff --git a/venv/Lib/site-packages/django/core/handlers/wsgi.py b/venv/Lib/site-packages/django/core/handlers/wsgi.py index 621130b..2c1c4db 100644 --- a/venv/Lib/site-packages/django/core/handlers/wsgi.py +++ b/venv/Lib/site-packages/django/core/handlers/wsgi.py @@ -9,23 +9,22 @@ from django.utils.encoding import repercent_broken_unicode from django.utils.functional import cached_property from django.utils.regex_helper import _lazy_re_compile -_slashes_re = _lazy_re_compile(rb"/+") +_slashes_re = _lazy_re_compile(br'/+') class LimitedStream: """Wrap another stream to disallow reading it past a number of bytes.""" - def __init__(self, stream, limit, buf_size=64 * 1024 * 1024): self.stream = stream self.remaining = limit - self.buffer = b"" + self.buffer = b'' self.buf_size = buf_size def _read_limited(self, size=None): if size is None or size > self.remaining: size = self.remaining if size == 0: - return b"" + return b'' result = self.stream.read(size) self.remaining -= len(result) return result @@ -33,17 +32,18 @@ class LimitedStream: def read(self, size=None): if size is None: result = self.buffer + self._read_limited() - self.buffer = b"" + self.buffer = b'' elif size < len(self.buffer): result = self.buffer[:size] self.buffer = self.buffer[size:] else: # size >= len(self.buffer) result = self.buffer + self._read_limited(size - len(self.buffer)) - self.buffer = b"" + self.buffer = b'' return result def readline(self, size=None): - while b"\n" not in self.buffer and (size is None or len(self.buffer) < size): + while b'\n' not in self.buffer and \ + (size is None or len(self.buffer) < size): if size: # since size is not None here, len(self.buffer) < size chunk = self._read_limited(size - len(self.buffer)) @@ -66,38 +66,39 @@ class WSGIRequest(HttpRequest): script_name = get_script_name(environ) # If PATH_INFO is empty (e.g. accessing the SCRIPT_NAME URL without a # trailing slash), operate as if '/' was requested. - path_info = get_path_info(environ) or "/" + path_info = get_path_info(environ) or '/' self.environ = environ self.path_info = path_info # be careful to only replace the first slash in the path because of # http://test/something and http://test//something being different as # stated in https://www.ietf.org/rfc/rfc2396.txt - self.path = "%s/%s" % (script_name.rstrip("/"), path_info.replace("/", "", 1)) + self.path = '%s/%s' % (script_name.rstrip('/'), + path_info.replace('/', '', 1)) self.META = environ - self.META["PATH_INFO"] = path_info - self.META["SCRIPT_NAME"] = script_name - self.method = environ["REQUEST_METHOD"].upper() + self.META['PATH_INFO'] = path_info + self.META['SCRIPT_NAME'] = script_name + self.method = environ['REQUEST_METHOD'].upper() # Set content_type, content_params, and encoding. self._set_content_type_params(environ) try: - content_length = int(environ.get("CONTENT_LENGTH")) + content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 - self._stream = LimitedStream(self.environ["wsgi.input"], content_length) + self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None def _get_scheme(self): - return self.environ.get("wsgi.url_scheme") + return self.environ.get('wsgi.url_scheme') @cached_property def GET(self): # The WSGI spec says 'QUERY_STRING' may be absent. - raw_query_string = get_bytes_from_wsgi(self.environ, "QUERY_STRING", "") + raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '') return QueryDict(raw_query_string, encoding=self._encoding) def _get_post(self): - if not hasattr(self, "_post"): + if not hasattr(self, '_post'): self._load_post_and_files() return self._post @@ -106,12 +107,12 @@ class WSGIRequest(HttpRequest): @cached_property def COOKIES(self): - raw_cookie = get_str_from_wsgi(self.environ, "HTTP_COOKIE", "") + raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') return parse_cookie(raw_cookie) @property def FILES(self): - if not hasattr(self, "_files"): + if not hasattr(self, '_files'): self._load_post_and_files() return self._files @@ -133,28 +134,24 @@ class WSGIHandler(base.BaseHandler): response._handler_class = self.__class__ - status = "%d %s" % (response.status_code, response.reason_phrase) + status = '%d %s' % (response.status_code, response.reason_phrase) response_headers = [ *response.items(), - *(("Set-Cookie", c.output(header="")) for c in response.cookies.values()), + *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()), ] start_response(status, response_headers) - if getattr(response, "file_to_stream", None) is not None and environ.get( - "wsgi.file_wrapper" - ): + if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): # If `wsgi.file_wrapper` is used the WSGI server does not call # .close on the response, but on the file wrapper. Patch it to use # response.close instead which takes care of closing all files. response.file_to_stream.close = response.close - response = environ["wsgi.file_wrapper"]( - response.file_to_stream, response.block_size - ) + response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size) return response def get_path_info(environ): """Return the HTTP request's PATH_INFO as a string.""" - path_info = get_bytes_from_wsgi(environ, "PATH_INFO", "/") + path_info = get_bytes_from_wsgi(environ, 'PATH_INFO', '/') return repercent_broken_unicode(path_info).decode() @@ -172,22 +169,20 @@ def get_script_name(environ): # If Apache's mod_rewrite had a whack at the URL, Apache set either # SCRIPT_URL or REDIRECT_URL to the full resource URL before applying any - # rewrites. Unfortunately not every web server (lighttpd!) passes this + # rewrites. Unfortunately not every Web server (lighttpd!) passes this # information through all the time, so FORCE_SCRIPT_NAME, above, is still # needed. - script_url = get_bytes_from_wsgi(environ, "SCRIPT_URL", "") or get_bytes_from_wsgi( - environ, "REDIRECT_URL", "" - ) + script_url = get_bytes_from_wsgi(environ, 'SCRIPT_URL', '') or get_bytes_from_wsgi(environ, 'REDIRECT_URL', '') if script_url: - if b"//" in script_url: + if b'//' in script_url: # mod_wsgi squashes multiple successive slashes in PATH_INFO, # do the same with script_url before manipulating paths (#17133). - script_url = _slashes_re.sub(b"/", script_url) - path_info = get_bytes_from_wsgi(environ, "PATH_INFO", "") - script_name = script_url[: -len(path_info)] if path_info else script_url + script_url = _slashes_re.sub(b'/', script_url) + path_info = get_bytes_from_wsgi(environ, 'PATH_INFO', '') + script_name = script_url[:-len(path_info)] if path_info else script_url else: - script_name = get_bytes_from_wsgi(environ, "SCRIPT_NAME", "") + script_name = get_bytes_from_wsgi(environ, 'SCRIPT_NAME', '') return script_name.decode() @@ -202,7 +197,7 @@ def get_bytes_from_wsgi(environ, key, default): # Non-ASCII values in the WSGI environ are arbitrarily decoded with # ISO-8859-1. This is wrong for Django websites where UTF-8 is the default. # Re-encode to recover the original bytestring. - return value.encode("iso-8859-1") + return value.encode('iso-8859-1') def get_str_from_wsgi(environ, key, default): @@ -212,4 +207,4 @@ def get_str_from_wsgi(environ, key, default): key and default should be str objects. """ value = get_bytes_from_wsgi(environ, key, default) - return value.decode(errors="replace") + return value.decode(errors='replace') diff --git a/venv/Lib/site-packages/django/core/mail/__init__.py b/venv/Lib/site-packages/django/core/mail/__init__.py index dc63e87..f49cd07 100644 --- a/venv/Lib/site-packages/django/core/mail/__init__.py +++ b/venv/Lib/site-packages/django/core/mail/__init__.py @@ -2,40 +2,24 @@ Tools for sending email. """ from django.conf import settings - # Imported for backwards compatibility and for the sake # of a cleaner namespace. These symbols used to be in # django/core/mail.py before the introduction of email # backends and the subsequent reorganization (See #10355) from django.core.mail.message import ( - DEFAULT_ATTACHMENT_MIME_TYPE, - BadHeaderError, - EmailMessage, - EmailMultiAlternatives, - SafeMIMEMultipart, - SafeMIMEText, - forbid_multi_line_headers, - make_msgid, + DEFAULT_ATTACHMENT_MIME_TYPE, BadHeaderError, EmailMessage, + EmailMultiAlternatives, SafeMIMEMultipart, SafeMIMEText, + forbid_multi_line_headers, make_msgid, ) from django.core.mail.utils import DNS_NAME, CachedDnsName from django.utils.module_loading import import_string __all__ = [ - "CachedDnsName", - "DNS_NAME", - "EmailMessage", - "EmailMultiAlternatives", - "SafeMIMEText", - "SafeMIMEMultipart", - "DEFAULT_ATTACHMENT_MIME_TYPE", - "make_msgid", - "BadHeaderError", - "forbid_multi_line_headers", - "get_connection", - "send_mail", - "send_mass_mail", - "mail_admins", - "mail_managers", + 'CachedDnsName', 'DNS_NAME', 'EmailMessage', 'EmailMultiAlternatives', + 'SafeMIMEText', 'SafeMIMEMultipart', 'DEFAULT_ATTACHMENT_MIME_TYPE', + 'make_msgid', 'BadHeaderError', 'forbid_multi_line_headers', + 'get_connection', 'send_mail', 'send_mass_mail', 'mail_admins', + 'mail_managers', ] @@ -51,17 +35,9 @@ def get_connection(backend=None, fail_silently=False, **kwds): return klass(fail_silently=fail_silently, **kwds) -def send_mail( - subject, - message, - from_email, - recipient_list, - fail_silently=False, - auth_user=None, - auth_password=None, - connection=None, - html_message=None, -): +def send_mail(subject, message, from_email, recipient_list, + fail_silently=False, auth_user=None, auth_password=None, + connection=None, html_message=None): """ Easy wrapper for sending a single message to a recipient list. All members of the recipient list will see the other recipients in the 'To' field. @@ -78,18 +54,15 @@ def send_mail( password=auth_password, fail_silently=fail_silently, ) - mail = EmailMultiAlternatives( - subject, message, from_email, recipient_list, connection=connection - ) + mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, connection=connection) if html_message: - mail.attach_alternative(html_message, "text/html") + mail.attach_alternative(html_message, 'text/html') return mail.send() -def send_mass_mail( - datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None -): +def send_mass_mail(datatuple, fail_silently=False, auth_user=None, + auth_password=None, connection=None): """ Given a datatuple of (subject, message, from_email, recipient_list), send each message to each recipient list. Return the number of emails sent. @@ -114,41 +87,35 @@ def send_mass_mail( return connection.send_messages(messages) -def mail_admins( - subject, message, fail_silently=False, connection=None, html_message=None -): +def mail_admins(subject, message, fail_silently=False, connection=None, + html_message=None): """Send a message to the admins, as defined by the ADMINS setting.""" if not settings.ADMINS: return if not all(isinstance(a, (list, tuple)) and len(a) == 2 for a in settings.ADMINS): - raise ValueError("The ADMINS setting must be a list of 2-tuples.") + raise ValueError('The ADMINS setting must be a list of 2-tuples.') mail = EmailMultiAlternatives( - "%s%s" % (settings.EMAIL_SUBJECT_PREFIX, subject), - message, - settings.SERVER_EMAIL, - [a[1] for a in settings.ADMINS], + '%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message, + settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], connection=connection, ) if html_message: - mail.attach_alternative(html_message, "text/html") + mail.attach_alternative(html_message, 'text/html') mail.send(fail_silently=fail_silently) -def mail_managers( - subject, message, fail_silently=False, connection=None, html_message=None -): +def mail_managers(subject, message, fail_silently=False, connection=None, + html_message=None): """Send a message to the managers, as defined by the MANAGERS setting.""" if not settings.MANAGERS: return if not all(isinstance(a, (list, tuple)) and len(a) == 2 for a in settings.MANAGERS): - raise ValueError("The MANAGERS setting must be a list of 2-tuples.") + raise ValueError('The MANAGERS setting must be a list of 2-tuples.') mail = EmailMultiAlternatives( - "%s%s" % (settings.EMAIL_SUBJECT_PREFIX, subject), - message, - settings.SERVER_EMAIL, - [a[1] for a in settings.MANAGERS], + '%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject), message, + settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS], connection=connection, ) if html_message: - mail.attach_alternative(html_message, "text/html") + mail.attach_alternative(html_message, 'text/html') mail.send(fail_silently=fail_silently) diff --git a/venv/Lib/site-packages/django/core/mail/backends/base.py b/venv/Lib/site-packages/django/core/mail/backends/base.py index b35b964..d687703 100644 --- a/venv/Lib/site-packages/django/core/mail/backends/base.py +++ b/venv/Lib/site-packages/django/core/mail/backends/base.py @@ -14,7 +14,6 @@ class BaseEmailBackend: # do something with connection pass """ - def __init__(self, fail_silently=False, **kwargs): self.fail_silently = fail_silently @@ -57,6 +56,4 @@ class BaseEmailBackend: Send one or more EmailMessage objects and return the number of email messages sent. """ - raise NotImplementedError( - "subclasses of BaseEmailBackend must override send_messages() method" - ) + raise NotImplementedError('subclasses of BaseEmailBackend must override send_messages() method') diff --git a/venv/Lib/site-packages/django/core/mail/backends/console.py b/venv/Lib/site-packages/django/core/mail/backends/console.py index ee5dd28..a8bdcbd 100644 --- a/venv/Lib/site-packages/django/core/mail/backends/console.py +++ b/venv/Lib/site-packages/django/core/mail/backends/console.py @@ -9,20 +9,18 @@ from django.core.mail.backends.base import BaseEmailBackend class EmailBackend(BaseEmailBackend): def __init__(self, *args, **kwargs): - self.stream = kwargs.pop("stream", sys.stdout) + self.stream = kwargs.pop('stream', sys.stdout) self._lock = threading.RLock() super().__init__(*args, **kwargs) def write_message(self, message): msg = message.message() msg_data = msg.as_bytes() - charset = ( - msg.get_charset().get_output_charset() if msg.get_charset() else "utf-8" - ) + charset = msg.get_charset().get_output_charset() if msg.get_charset() else 'utf-8' msg_data = msg_data.decode(charset) - self.stream.write("%s\n" % msg_data) - self.stream.write("-" * 79) - self.stream.write("\n") + self.stream.write('%s\n' % msg_data) + self.stream.write('-' * 79) + self.stream.write('\n') def send_messages(self, email_messages): """Write all messages to the stream in a thread-safe way.""" diff --git a/venv/Lib/site-packages/django/core/mail/backends/filebased.py b/venv/Lib/site-packages/django/core/mail/backends/filebased.py index 3b2b037..498d86f 100644 --- a/venv/Lib/site-packages/django/core/mail/backends/filebased.py +++ b/venv/Lib/site-packages/django/core/mail/backends/filebased.py @@ -5,7 +5,9 @@ import os from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.core.mail.backends.console import EmailBackend as ConsoleEmailBackend +from django.core.mail.backends.console import ( + EmailBackend as ConsoleEmailBackend, +) class EmailBackend(ConsoleEmailBackend): @@ -14,35 +16,31 @@ class EmailBackend(ConsoleEmailBackend): if file_path is not None: self.file_path = file_path else: - self.file_path = getattr(settings, "EMAIL_FILE_PATH", None) + self.file_path = getattr(settings, 'EMAIL_FILE_PATH', None) self.file_path = os.path.abspath(self.file_path) try: os.makedirs(self.file_path, exist_ok=True) except FileExistsError: raise ImproperlyConfigured( - "Path for saving email messages exists, but is not a directory: %s" - % self.file_path + 'Path for saving email messages exists, but is not a directory: %s' % self.file_path ) except OSError as err: raise ImproperlyConfigured( - "Could not create directory for saving email messages: %s (%s)" - % (self.file_path, err) + 'Could not create directory for saving email messages: %s (%s)' % (self.file_path, err) ) # Make sure that self.file_path is writable. if not os.access(self.file_path, os.W_OK): - raise ImproperlyConfigured( - "Could not write to directory: %s" % self.file_path - ) + raise ImproperlyConfigured('Could not write to directory: %s' % self.file_path) # Finally, call super(). # Since we're using the console-based backend as a base, # force the stream to be None, so we don't default to stdout - kwargs["stream"] = None + kwargs['stream'] = None super().__init__(*args, **kwargs) def write_message(self, message): - self.stream.write(message.message().as_bytes() + b"\n") - self.stream.write(b"-" * 79) - self.stream.write(b"\n") + self.stream.write(message.message().as_bytes() + b'\n') + self.stream.write(b'-' * 79) + self.stream.write(b'\n') def _get_filename(self): """Return a unique file name.""" @@ -54,7 +52,7 @@ class EmailBackend(ConsoleEmailBackend): def open(self): if self.stream is None: - self.stream = open(self._get_filename(), "ab") + self.stream = open(self._get_filename(), 'ab') return True return False diff --git a/venv/Lib/site-packages/django/core/mail/backends/locmem.py b/venv/Lib/site-packages/django/core/mail/backends/locmem.py index 7667697..84732e9 100644 --- a/venv/Lib/site-packages/django/core/mail/backends/locmem.py +++ b/venv/Lib/site-packages/django/core/mail/backends/locmem.py @@ -15,10 +15,9 @@ class EmailBackend(BaseEmailBackend): The dummy outbox is accessible through the outbox instance attribute. """ - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if not hasattr(mail, "outbox"): + if not hasattr(mail, 'outbox'): mail.outbox = [] def send_messages(self, messages): diff --git a/venv/Lib/site-packages/django/core/mail/backends/smtp.py b/venv/Lib/site-packages/django/core/mail/backends/smtp.py index 5df7c20..13ed4a2 100644 --- a/venv/Lib/site-packages/django/core/mail/backends/smtp.py +++ b/venv/Lib/site-packages/django/core/mail/backends/smtp.py @@ -13,21 +13,10 @@ class EmailBackend(BaseEmailBackend): """ A wrapper that manages the SMTP network connection. """ - - def __init__( - self, - host=None, - port=None, - username=None, - password=None, - use_tls=None, - fail_silently=False, - use_ssl=None, - timeout=None, - ssl_keyfile=None, - ssl_certfile=None, - **kwargs, - ): + def __init__(self, host=None, port=None, username=None, password=None, + use_tls=None, fail_silently=False, use_ssl=None, timeout=None, + ssl_keyfile=None, ssl_certfile=None, + **kwargs): super().__init__(fail_silently=fail_silently) self.host = host or settings.EMAIL_HOST self.port = port or settings.EMAIL_PORT @@ -36,17 +25,12 @@ class EmailBackend(BaseEmailBackend): self.use_tls = settings.EMAIL_USE_TLS if use_tls is None else use_tls self.use_ssl = settings.EMAIL_USE_SSL if use_ssl is None else use_ssl self.timeout = settings.EMAIL_TIMEOUT if timeout is None else timeout - self.ssl_keyfile = ( - settings.EMAIL_SSL_KEYFILE if ssl_keyfile is None else ssl_keyfile - ) - self.ssl_certfile = ( - settings.EMAIL_SSL_CERTFILE if ssl_certfile is None else ssl_certfile - ) + self.ssl_keyfile = settings.EMAIL_SSL_KEYFILE if ssl_keyfile is None else ssl_keyfile + self.ssl_certfile = settings.EMAIL_SSL_CERTFILE if ssl_certfile is None else ssl_certfile if self.use_ssl and self.use_tls: raise ValueError( "EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set " - "one of those settings to True." - ) + "one of those settings to True.") self.connection = None self._lock = threading.RLock() @@ -66,27 +50,21 @@ class EmailBackend(BaseEmailBackend): # If local_hostname is not specified, socket.getfqdn() gets used. # For performance, we use the cached FQDN for local_hostname. - connection_params = {"local_hostname": DNS_NAME.get_fqdn()} + connection_params = {'local_hostname': DNS_NAME.get_fqdn()} if self.timeout is not None: - connection_params["timeout"] = self.timeout + connection_params['timeout'] = self.timeout if self.use_ssl: - connection_params.update( - { - "keyfile": self.ssl_keyfile, - "certfile": self.ssl_certfile, - } - ) + connection_params.update({ + 'keyfile': self.ssl_keyfile, + 'certfile': self.ssl_certfile, + }) try: - self.connection = self.connection_class( - self.host, self.port, **connection_params - ) + self.connection = self.connection_class(self.host, self.port, **connection_params) # TLS/SSL are mutually exclusive, so only attempt TLS over # non-secure connections. if not self.use_ssl and self.use_tls: - self.connection.starttls( - keyfile=self.ssl_keyfile, certfile=self.ssl_certfile - ) + self.connection.starttls(keyfile=self.ssl_keyfile, certfile=self.ssl_certfile) if self.username and self.password: self.connection.login(self.username, self.password) return True @@ -141,14 +119,10 @@ class EmailBackend(BaseEmailBackend): return False encoding = email_message.encoding or settings.DEFAULT_CHARSET from_email = sanitize_address(email_message.from_email, encoding) - recipients = [ - sanitize_address(addr, encoding) for addr in email_message.recipients() - ] + recipients = [sanitize_address(addr, encoding) for addr in email_message.recipients()] message = email_message.message() try: - self.connection.sendmail( - from_email, recipients, message.as_bytes(linesep="\r\n") - ) + self.connection.sendmail(from_email, recipients, message.as_bytes(linesep='\r\n')) except smtplib.SMTPException: if not self.fail_silently: raise diff --git a/venv/Lib/site-packages/django/core/mail/message.py b/venv/Lib/site-packages/django/core/mail/message.py index cd5b71a..963542c 100644 --- a/venv/Lib/site-packages/django/core/mail/message.py +++ b/venv/Lib/site-packages/django/core/mail/message.py @@ -1,7 +1,7 @@ import mimetypes -from email import charset as Charset -from email import encoders as Encoders -from email import generator, message_from_string +from email import ( + charset as Charset, encoders as Encoders, generator, message_from_string, +) from email.errors import HeaderParseError from email.header import Header from email.headerregistry import Address, parser @@ -20,14 +20,14 @@ from django.utils.encoding import force_str, punycode # Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from # some spam filters. -utf8_charset = Charset.Charset("utf-8") +utf8_charset = Charset.Charset('utf-8') utf8_charset.body_encoding = None # Python defaults to BASE64 -utf8_charset_qp = Charset.Charset("utf-8") +utf8_charset_qp = Charset.Charset('utf-8') utf8_charset_qp.body_encoding = Charset.QP # Default MIME type to use on attachments (if it is not explicitly given # and cannot be guessed). -DEFAULT_ATTACHMENT_MIME_TYPE = "application/octet-stream" +DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream' RFC5322_EMAIL_LINE_LENGTH_LIMIT = 998 @@ -38,17 +38,17 @@ class BadHeaderError(ValueError): # Header names that contain structured address data (RFC #5322) ADDRESS_HEADERS = { - "from", - "sender", - "reply-to", - "to", - "cc", - "bcc", - "resent-from", - "resent-sender", - "resent-to", - "resent-cc", - "resent-bcc", + 'from', + 'sender', + 'reply-to', + 'to', + 'cc', + 'bcc', + 'resent-from', + 'resent-sender', + 'resent-to', + 'resent-cc', + 'resent-bcc', } @@ -56,21 +56,17 @@ def forbid_multi_line_headers(name, val, encoding): """Forbid multi-line headers to prevent header injection.""" encoding = encoding or settings.DEFAULT_CHARSET val = str(val) # val may be lazy - if "\n" in val or "\r" in val: - raise BadHeaderError( - "Header values can't contain newlines (got %r for header %r)" % (val, name) - ) + if '\n' in val or '\r' in val: + raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name)) try: - val.encode("ascii") + val.encode('ascii') except UnicodeEncodeError: if name.lower() in ADDRESS_HEADERS: - val = ", ".join( - sanitize_address(addr, encoding) for addr in getaddresses((val,)) - ) + val = ', '.join(sanitize_address(addr, encoding) for addr in getaddresses((val,))) else: val = Header(val, encoding).encode() else: - if name.lower() == "subject": + if name.lower() == 'subject': val = Header(val).encode() return name, val @@ -90,27 +86,28 @@ def sanitize_address(addr, encoding): if rest: # The entire email address must be parsed. raise ValueError( - 'Invalid address; only %s could be parsed from "%s"' % (token, addr) + 'Invalid address; only %s could be parsed from "%s"' + % (token, addr) ) - nm = token.display_name or "" + nm = token.display_name or '' localpart = token.local_part - domain = token.domain or "" + domain = token.domain or '' else: nm, address = addr - localpart, domain = address.rsplit("@", 1) + localpart, domain = address.rsplit('@', 1) address_parts = nm + localpart + domain - if "\n" in address_parts or "\r" in address_parts: - raise ValueError("Invalid address; address parts cannot contain newlines.") + if '\n' in address_parts or '\r' in address_parts: + raise ValueError('Invalid address; address parts cannot contain newlines.') # Avoid UTF-8 encode, if it's possible. try: - nm.encode("ascii") + nm.encode('ascii') nm = Header(nm).encode() except UnicodeEncodeError: nm = Header(nm, encoding).encode() try: - localpart.encode("ascii") + localpart.encode('ascii') except UnicodeEncodeError: localpart = Header(localpart, encoding).encode() domain = punycode(domain) @@ -120,7 +117,7 @@ def sanitize_address(addr, encoding): class MIMEMixin: - def as_string(self, unixfrom=False, linesep="\n"): + def as_string(self, unixfrom=False, linesep='\n'): """Return the entire formatted message as a string. Optional `unixfrom' when True, means include the Unix From_ envelope header. @@ -133,7 +130,7 @@ class MIMEMixin: g.flatten(self, unixfrom=unixfrom, linesep=linesep) return fp.getvalue() - def as_bytes(self, unixfrom=False, linesep="\n"): + def as_bytes(self, unixfrom=False, linesep='\n'): """Return the entire formatted message as bytes. Optional `unixfrom' when True, means include the Unix From_ envelope header. @@ -148,14 +145,16 @@ class MIMEMixin: class SafeMIMEMessage(MIMEMixin, MIMEMessage): + def __setitem__(self, name, val): # message/rfc822 attachments must be ASCII - name, val = forbid_multi_line_headers(name, val, "ascii") + name, val = forbid_multi_line_headers(name, val, 'ascii') MIMEMessage.__setitem__(self, name, val) class SafeMIMEText(MIMEMixin, MIMEText): - def __init__(self, _text, _subtype="plain", _charset=None): + + def __init__(self, _text, _subtype='plain', _charset=None): self.encoding = _charset MIMEText.__init__(self, _text, _subtype=_subtype, _charset=_charset) @@ -164,7 +163,7 @@ class SafeMIMEText(MIMEMixin, MIMEText): MIMEText.__setitem__(self, name, val) def set_payload(self, payload, charset=None): - if charset == "utf-8" and not isinstance(charset, Charset.Charset): + if charset == 'utf-8' and not isinstance(charset, Charset.Charset): has_long_lines = any( len(line.encode()) > RFC5322_EMAIL_LINE_LENGTH_LIMIT for line in payload.splitlines() @@ -176,9 +175,8 @@ class SafeMIMEText(MIMEMixin, MIMEText): class SafeMIMEMultipart(MIMEMixin, MIMEMultipart): - def __init__( - self, _subtype="mixed", boundary=None, _subparts=None, encoding=None, **_params - ): + + def __init__(self, _subtype='mixed', boundary=None, _subparts=None, encoding=None, **_params): self.encoding = encoding MIMEMultipart.__init__(self, _subtype, boundary, _subparts, **_params) @@ -189,24 +187,13 @@ class SafeMIMEMultipart(MIMEMixin, MIMEMultipart): class EmailMessage: """A container for email information.""" + content_subtype = 'plain' + mixed_subtype = 'mixed' + encoding = None # None => use settings default - content_subtype = "plain" - mixed_subtype = "mixed" - encoding = None # None => use settings default - - def __init__( - self, - subject="", - body="", - from_email=None, - to=None, - bcc=None, - connection=None, - attachments=None, - headers=None, - cc=None, - reply_to=None, - ): + def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, + connection=None, attachments=None, headers=None, cc=None, + reply_to=None): """ Initialize a single email message (which can be sent to multiple recipients). @@ -237,7 +224,7 @@ class EmailMessage: self.reply_to = [] self.from_email = from_email or settings.DEFAULT_FROM_EMAIL self.subject = subject - self.body = body or "" + self.body = body or '' self.attachments = [] if attachments: for attachment in attachments: @@ -250,7 +237,6 @@ class EmailMessage: def get_connection(self, fail_silently=False): from django.core.mail import get_connection - if not self.connection: self.connection = get_connection(fail_silently=fail_silently) return self.connection @@ -259,26 +245,26 @@ class EmailMessage: encoding = self.encoding or settings.DEFAULT_CHARSET msg = SafeMIMEText(self.body, self.content_subtype, encoding) msg = self._create_message(msg) - msg["Subject"] = self.subject - msg["From"] = self.extra_headers.get("From", self.from_email) - self._set_list_header_if_not_empty(msg, "To", self.to) - self._set_list_header_if_not_empty(msg, "Cc", self.cc) - self._set_list_header_if_not_empty(msg, "Reply-To", self.reply_to) + msg['Subject'] = self.subject + msg['From'] = self.extra_headers.get('From', self.from_email) + self._set_list_header_if_not_empty(msg, 'To', self.to) + self._set_list_header_if_not_empty(msg, 'Cc', self.cc) + self._set_list_header_if_not_empty(msg, 'Reply-To', self.reply_to) # Email header names are case-insensitive (RFC 2045), so we have to # accommodate that when doing comparisons. header_names = [key.lower() for key in self.extra_headers] - if "date" not in header_names: + if 'date' not in header_names: # formatdate() uses stdlib methods to format the date, which use # the stdlib/OS concept of a timezone, however, Django sets the # TZ environment variable based on the TIME_ZONE setting which # will get picked up by formatdate(). - msg["Date"] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME) - if "message-id" not in header_names: + msg['Date'] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME) + if 'message-id' not in header_names: # Use cached DNS_NAME for performance - msg["Message-ID"] = make_msgid(domain=DNS_NAME) + msg['Message-ID'] = make_msgid(domain=DNS_NAME) for name, value in self.extra_headers.items(): - if name.lower() != "from": # From is already handled + if name.lower() != 'from': # From is already handled msg[name] = value return msg @@ -310,23 +296,15 @@ class EmailMessage: mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. """ if isinstance(filename, MIMEBase): - if content is not None or mimetype is not None: - raise ValueError( - "content and mimetype must not be given when a MIMEBase " - "instance is provided." - ) + assert content is None + assert mimetype is None self.attachments.append(filename) - elif content is None: - raise ValueError("content must be provided.") else: - mimetype = ( - mimetype - or mimetypes.guess_type(filename)[0] - or DEFAULT_ATTACHMENT_MIME_TYPE - ) - basetype, subtype = mimetype.split("/", 1) + assert content is not None + mimetype = mimetype or mimetypes.guess_type(filename)[0] or DEFAULT_ATTACHMENT_MIME_TYPE + basetype, subtype = mimetype.split('/', 1) - if basetype == "text": + if basetype == 'text': if isinstance(content, bytes): try: content = content.decode() @@ -349,7 +327,7 @@ class EmailMessage: DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. """ path = Path(path) - with path.open("rb") as file: + with path.open('rb') as file: content = file.read() self.attach(path.name, content, mimetype) @@ -377,11 +355,11 @@ class EmailMessage: If the mimetype is message/rfc822, content may be an email.Message or EmailMessage object, as well as a str. """ - basetype, subtype = mimetype.split("/", 1) - if basetype == "text": + basetype, subtype = mimetype.split('/', 1) + if basetype == 'text': encoding = self.encoding or settings.DEFAULT_CHARSET attachment = SafeMIMEText(content, subtype, encoding) - elif basetype == "message" and subtype == "rfc822": + elif basetype == 'message' and subtype == 'rfc822': # Bug #18967: per RFC2046 s5.2.1, message/rfc822 attachments # must not be base64 encoded. if isinstance(content, EmailMessage): @@ -408,12 +386,10 @@ class EmailMessage: attachment = self._create_mime_attachment(content, mimetype) if filename: try: - filename.encode("ascii") + filename.encode('ascii') except UnicodeEncodeError: - filename = ("utf-8", "", filename) - attachment.add_header( - "Content-Disposition", "attachment", filename=filename - ) + filename = ('utf-8', '', filename) + attachment.add_header('Content-Disposition', 'attachment', filename=filename) return attachment def _set_list_header_if_not_empty(self, msg, header, values): @@ -425,7 +401,7 @@ class EmailMessage: try: value = self.extra_headers[header] except KeyError: - value = ", ".join(str(v) for v in values) + value = ', '.join(str(v) for v in values) msg[header] = value @@ -435,45 +411,25 @@ class EmailMultiAlternatives(EmailMessage): messages. For example, including text and HTML versions of the text is made easier. """ + alternative_subtype = 'alternative' - alternative_subtype = "alternative" - - def __init__( - self, - subject="", - body="", - from_email=None, - to=None, - bcc=None, - connection=None, - attachments=None, - headers=None, - alternatives=None, - cc=None, - reply_to=None, - ): + def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, + connection=None, attachments=None, headers=None, alternatives=None, + cc=None, reply_to=None): """ Initialize a single email message (which can be sent to multiple recipients). """ super().__init__( - subject, - body, - from_email, - to, - bcc, - connection, - attachments, - headers, - cc, - reply_to, + subject, body, from_email, to, bcc, connection, attachments, + headers, cc, reply_to, ) self.alternatives = alternatives or [] def attach_alternative(self, content, mimetype): """Attach an alternative content representation.""" - if content is None or mimetype is None: - raise ValueError("Both content and mimetype must be provided.") + assert content is not None + assert mimetype is not None self.alternatives.append((content, mimetype)) def _create_message(self, msg): @@ -483,9 +439,7 @@ class EmailMultiAlternatives(EmailMessage): encoding = self.encoding or settings.DEFAULT_CHARSET if self.alternatives: body_msg = msg - msg = SafeMIMEMultipart( - _subtype=self.alternative_subtype, encoding=encoding - ) + msg = SafeMIMEMultipart(_subtype=self.alternative_subtype, encoding=encoding) if self.body: msg.attach(body_msg) for alternative in self.alternatives: diff --git a/venv/Lib/site-packages/django/core/mail/utils.py b/venv/Lib/site-packages/django/core/mail/utils.py index 8143c23..1e48faa 100644 --- a/venv/Lib/site-packages/django/core/mail/utils.py +++ b/venv/Lib/site-packages/django/core/mail/utils.py @@ -14,7 +14,7 @@ class CachedDnsName: return self.get_fqdn() def get_fqdn(self): - if not hasattr(self, "_fqdn"): + if not hasattr(self, '_fqdn'): self._fqdn = punycode(socket.getfqdn()) return self._fqdn diff --git a/venv/Lib/site-packages/django/core/management/__init__.py b/venv/Lib/site-packages/django/core/management/__init__.py index 64af1b8..4e30a28 100644 --- a/venv/Lib/site-packages/django/core/management/__init__.py +++ b/venv/Lib/site-packages/django/core/management/__init__.py @@ -3,10 +3,7 @@ import os import pkgutil import sys from argparse import ( - _AppendConstAction, - _CountAction, - _StoreConstAction, - _SubParsersAction, + _AppendConstAction, _CountAction, _StoreConstAction, _SubParsersAction, ) from collections import defaultdict from difflib import get_close_matches @@ -17,10 +14,7 @@ from django.apps import apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.management.base import ( - BaseCommand, - CommandError, - CommandParser, - handle_default_options, + BaseCommand, CommandError, CommandParser, handle_default_options, ) from django.core.management.color import color_style from django.utils import autoreload @@ -31,12 +25,9 @@ def find_commands(management_dir): Given a path to a management directory, return a list of all the command names that are available. """ - command_dir = os.path.join(management_dir, "commands") - return [ - name - for _, name, is_pkg in pkgutil.iter_modules([command_dir]) - if not is_pkg and not name.startswith("_") - ] + command_dir = os.path.join(management_dir, 'commands') + return [name for _, name, is_pkg in pkgutil.iter_modules([command_dir]) + if not is_pkg and not name.startswith('_')] def load_command_class(app_name, name): @@ -45,7 +36,7 @@ def load_command_class(app_name, name): class instance. Allow all errors raised by the import process (ImportError, AttributeError) to propagate. """ - module = import_module("%s.management.commands.%s" % (app_name, name)) + module = import_module('%s.management.commands.%s' % (app_name, name)) return module.Command() @@ -72,13 +63,13 @@ def get_commands(): The dictionary is cached on the first call and reused on subsequent calls. """ - commands = {name: "django.core" for name in find_commands(__path__[0])} + commands = {name: 'django.core' for name in find_commands(__path__[0])} if not settings.configured: return commands for app_config in reversed(list(apps.get_app_configs())): - path = os.path.join(app_config.path, "management") + path = os.path.join(app_config.path, 'management') commands.update({name: app_config.name for name in find_commands(path)}) return commands @@ -107,7 +98,7 @@ def call_command(command_name, *args, **options): if isinstance(command_name, BaseCommand): # Command object passed in. command = command_name - command_name = command.__class__.__module__.split(".")[-1] + command_name = command.__class__.__module__.split('.')[-1] else: # Load the command object by name. try: @@ -122,12 +113,11 @@ def call_command(command_name, *args, **options): command = load_command_class(app_name, command_name) # Simulate argument parsing to get the option defaults (see #10080 for details). - parser = command.create_parser("", command_name) + parser = command.create_parser('', command_name) # Use the `dest` option name from the parser option opt_mapping = { - min(s_opt.option_strings).lstrip("-").replace("-", "_"): s_opt.dest - for s_opt in parser._actions - if s_opt.option_strings + min(s_opt.option_strings).lstrip('-').replace('-', '_'): s_opt.dest + for s_opt in parser._actions if s_opt.option_strings } arg_options = {opt_mapping.get(key, key): value for key, value in options.items()} parse_args = [] @@ -150,21 +140,15 @@ def call_command(command_name, *args, **options): mutually_exclusive_required_options = { opt for group in parser._mutually_exclusive_groups - for opt in group._group_actions - if group.required + for opt in group._group_actions if group.required } # Any required arguments which are passed in via **options must be passed # to parse_args(). for opt in parser_actions: - if opt.dest in options and ( - opt.required or opt in mutually_exclusive_required_options + if ( + opt.dest in options and + (opt.required or opt in mutually_exclusive_required_options) ): - opt_dest_count = sum(v == opt.dest for v in opt_mapping.values()) - if opt_dest_count > 1: - raise TypeError( - f"Cannot pass the dest {opt.dest!r} that matches multiple " - f"arguments via **options." - ) parse_args.append(min(opt.option_strings)) if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction)): continue @@ -183,17 +167,16 @@ def call_command(command_name, *args, **options): if unknown_options: raise TypeError( "Unknown option(s) for %s command: %s. " - "Valid options are: %s." - % ( + "Valid options are: %s." % ( command_name, - ", ".join(sorted(unknown_options)), - ", ".join(sorted(valid_options)), + ', '.join(sorted(unknown_options)), + ', '.join(sorted(valid_options)), ) ) # Move positional args out of options to mimic legacy optparse - args = defaults.pop("args", ()) - if "skip_checks" not in options: - defaults["skip_checks"] = True + args = defaults.pop('args', ()) + if 'skip_checks' not in options: + defaults['skip_checks'] = True return command.execute(*args, **defaults) @@ -202,12 +185,11 @@ class ManagementUtility: """ Encapsulate the logic of the django-admin and manage.py utilities. """ - def __init__(self, argv=None): self.argv = argv or sys.argv[:] self.prog_name = os.path.basename(self.argv[0]) - if self.prog_name == "__main__.py": - self.prog_name = "python -m django" + if self.prog_name == '__main__.py': + self.prog_name = 'python -m django' self.settings_exception = None def main_help_text(self, commands_only=False): @@ -217,17 +199,16 @@ class ManagementUtility: else: usage = [ "", - "Type '%s help <subcommand>' for help on a specific subcommand." - % self.prog_name, + "Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name, "", "Available subcommands:", ] commands_dict = defaultdict(lambda: []) for name, app in get_commands().items(): - if app == "django.core": - app = "django" + if app == 'django.core': + app = 'django' else: - app = app.rpartition(".")[-1] + app = app.rpartition('.')[-1] commands_dict[app].append(name) style = color_style() for app in sorted(commands_dict): @@ -237,15 +218,12 @@ class ManagementUtility: usage.append(" %s" % name) # Output an extra note if settings are not properly configured if self.settings_exception is not None: - usage.append( - style.NOTICE( - "Note that only Django core commands are listed " - "as settings are not properly configured (error: %s)." - % self.settings_exception - ) - ) + usage.append(style.NOTICE( + "Note that only Django core commands are listed " + "as settings are not properly configured (error: %s)." + % self.settings_exception)) - return "\n".join(usage) + return '\n'.join(usage) def fetch_command(self, subcommand): """ @@ -258,7 +236,7 @@ class ManagementUtility: try: app_name = commands[subcommand] except KeyError: - if os.environ.get("DJANGO_SETTINGS_MODULE"): + if os.environ.get('DJANGO_SETTINGS_MODULE'): # If `subcommand` is missing due to misconfigured settings, the # following line will retrigger an ImproperlyConfigured exception # (get_commands() swallows the original one) so the user is @@ -267,9 +245,9 @@ class ManagementUtility: elif not settings.configured: sys.stderr.write("No Django settings specified.\n") possible_matches = get_close_matches(subcommand, commands) - sys.stderr.write("Unknown command: %r" % subcommand) + sys.stderr.write('Unknown command: %r' % subcommand) if possible_matches: - sys.stderr.write(". Did you mean %s?" % possible_matches[0]) + sys.stderr.write('. Did you mean %s?' % possible_matches[0]) sys.stderr.write("\nType '%s help' for usage.\n" % self.prog_name) sys.exit(1) if isinstance(app_name, BaseCommand): @@ -301,29 +279,29 @@ class ManagementUtility: and formatted as potential completion suggestions. """ # Don't complete if user hasn't sourced bash_completion file. - if "DJANGO_AUTO_COMPLETE" not in os.environ: + if 'DJANGO_AUTO_COMPLETE' not in os.environ: return - cwords = os.environ["COMP_WORDS"].split()[1:] - cword = int(os.environ["COMP_CWORD"]) + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) try: curr = cwords[cword - 1] except IndexError: - curr = "" + curr = '' - subcommands = [*get_commands(), "help"] - options = [("--help", False)] + subcommands = [*get_commands(), 'help'] + options = [('--help', False)] # subcommand if cword == 1: - print(" ".join(sorted(filter(lambda x: x.startswith(curr), subcommands)))) + print(' '.join(sorted(filter(lambda x: x.startswith(curr), subcommands)))) # subcommand options # special case: the 'help' subcommand has no options - elif cwords[0] in subcommands and cwords[0] != "help": + elif cwords[0] in subcommands and cwords[0] != 'help': subcommand_cls = self.fetch_command(cwords[0]) # special case: add the names of installed apps to options - if cwords[0] in ("dumpdata", "sqlmigrate", "sqlsequencereset", "test"): + if cwords[0] in ('dumpdata', 'sqlmigrate', 'sqlsequencereset', 'test'): try: app_configs = apps.get_app_configs() # Get the last part of the dotted path as the app name. @@ -332,14 +310,13 @@ class ManagementUtility: # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The # user will find out once they execute the command. pass - parser = subcommand_cls.create_parser("", cwords[0]) + parser = subcommand_cls.create_parser('', cwords[0]) options.extend( (min(s_opt.option_strings), s_opt.nargs != 0) - for s_opt in parser._actions - if s_opt.option_strings + for s_opt in parser._actions if s_opt.option_strings ) # filter out previously specified options from available options - prev_opts = {x.split("=")[0] for x in cwords[1 : cword - 1]} + prev_opts = {x.split('=')[0] for x in cwords[1:cword - 1]} options = (opt for opt in options if opt[0] not in prev_opts) # filter options by current input @@ -347,7 +324,7 @@ class ManagementUtility: for opt_label, require_arg in options: # append '=' to options which require args if require_arg: - opt_label += "=" + opt_label += '=' print(opt_label) # Exit code of the bash completion function is never passed back to # the user, so it's safe to always exit with 0. @@ -362,20 +339,20 @@ class ManagementUtility: try: subcommand = self.argv[1] except IndexError: - subcommand = "help" # Display help if no arguments were given. + subcommand = 'help' # Display help if no arguments were given. # Preprocess options to extract --settings and --pythonpath. # These options could affect the commands that are available, so they # must be processed early. parser = CommandParser( prog=self.prog_name, - usage="%(prog)s subcommand [options] [args]", + usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False, ) - parser.add_argument("--settings") - parser.add_argument("--pythonpath") - parser.add_argument("args", nargs="*") # catch-all + parser.add_argument('--settings') + parser.add_argument('--pythonpath') + parser.add_argument('args', nargs='*') # catch-all try: options, args = parser.parse_known_args(self.argv[2:]) handle_default_options(options) @@ -393,7 +370,7 @@ class ManagementUtility: # Start the auto-reloading dev server even if the code is broken. # The hardcoded condition is a code smell but we can't rely on a # flag on the command class because we haven't located it yet. - if subcommand == "runserver" and "--noreload" not in self.argv: + if subcommand == 'runserver' and '--noreload' not in self.argv: try: autoreload.check_errors(django.setup)() except Exception: @@ -408,9 +385,7 @@ class ManagementUtility: # (e.g. options for the contrib.staticfiles' runserver). # Changes here require manually testing as described in # #27522. - _parser = self.fetch_command("runserver").create_parser( - "django", "runserver" - ) + _parser = self.fetch_command('runserver').create_parser('django', 'runserver') _options, _args = _parser.parse_known_args(self.argv[2:]) for _arg in _args: self.argv.remove(_arg) @@ -421,21 +396,19 @@ class ManagementUtility: self.autocomplete() - if subcommand == "help": - if "--commands" in args: - sys.stdout.write(self.main_help_text(commands_only=True) + "\n") + if subcommand == 'help': + if '--commands' in args: + sys.stdout.write(self.main_help_text(commands_only=True) + '\n') elif not options.args: - sys.stdout.write(self.main_help_text() + "\n") + sys.stdout.write(self.main_help_text() + '\n') else: - self.fetch_command(options.args[0]).print_help( - self.prog_name, options.args[0] - ) + self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0]) # Special-cases: We want 'django-admin --version' and # 'django-admin --help' to work, for backwards compatibility. - elif subcommand == "version" or self.argv[1:] == ["--version"]: - sys.stdout.write(django.get_version() + "\n") - elif self.argv[1:] in (["--help"], ["-h"]): - sys.stdout.write(self.main_help_text() + "\n") + elif subcommand == 'version' or self.argv[1:] == ['--version']: + sys.stdout.write(django.get_version() + '\n') + elif self.argv[1:] in (['--help'], ['-h']): + sys.stdout.write(self.main_help_text() + '\n') else: self.fetch_command(subcommand).run_from_argv(self.argv) diff --git a/venv/Lib/site-packages/django/core/management/base.py b/venv/Lib/site-packages/django/core/management/base.py index 834d1b2..463e4e8 100644 --- a/venv/Lib/site-packages/django/core/management/base.py +++ b/venv/Lib/site-packages/django/core/management/base.py @@ -2,7 +2,6 @@ Base classes for writing management commands (named commands which can be executed through ``django-admin`` or ``manage.py``). """ -import argparse import os import sys import warnings @@ -16,7 +15,7 @@ from django.core.management.color import color_style, no_style from django.db import DEFAULT_DB_ALIAS, connections from django.utils.deprecation import RemovedInDjango41Warning -ALL_CHECKS = "__all__" +ALL_CHECKS = '__all__' class CommandError(Exception): @@ -31,7 +30,6 @@ class CommandError(Exception): error) is the preferred way to indicate that something has gone wrong in the execution of a command. """ - def __init__(self, *args, returncode=1, **kwargs): self.returncode = returncode super().__init__(*args, **kwargs) @@ -41,7 +39,6 @@ class SystemCheckError(CommandError): """ The system check framework detected unrecoverable errors. """ - pass @@ -51,19 +48,15 @@ class CommandParser(ArgumentParser): SystemExit in several occasions, as SystemExit is unacceptable when a command is called programmatically. """ - - def __init__( - self, *, missing_args_message=None, called_from_command_line=None, **kwargs - ): + def __init__(self, *, missing_args_message=None, called_from_command_line=None, **kwargs): self.missing_args_message = missing_args_message self.called_from_command_line = called_from_command_line super().__init__(**kwargs) def parse_args(self, args=None, namespace=None): # Catch missing argument for a better error message - if self.missing_args_message and not ( - args or any(not arg.startswith("-") for arg in args) - ): + if (self.missing_args_message and + not (args or any(not arg.startswith('-') for arg in args))): self.error(self.missing_args_message) return super().parse_args(args, namespace) @@ -81,17 +74,15 @@ def handle_default_options(options): user commands. """ if options.settings: - os.environ["DJANGO_SETTINGS_MODULE"] = options.settings + os.environ['DJANGO_SETTINGS_MODULE'] = options.settings if options.pythonpath: sys.path.insert(0, options.pythonpath) def no_translations(handle_func): """Decorator that forces a command to run with translations deactivated.""" - def wrapped(*args, **kwargs): from django.utils import translation - saved_locale = translation.get_language() translation.deactivate_all() try: @@ -100,7 +91,6 @@ def no_translations(handle_func): if saved_locale is not None: translation.activate(saved_locale) return res - return wrapped @@ -109,21 +99,15 @@ class DjangoHelpFormatter(HelpFormatter): Customized formatter so that command-specific arguments appear in the --help output before arguments common to all commands. """ - show_last = { - "--version", - "--verbosity", - "--traceback", - "--settings", - "--pythonpath", - "--no-color", - "--force-color", - "--skip-checks", + '--version', '--verbosity', '--traceback', '--settings', '--pythonpath', + '--no-color', '--force-color', '--skip-checks', } def _reordered_actions(self, actions): return sorted( - actions, key=lambda a: set(a.option_strings) & self.show_last != set() + actions, + key=lambda a: set(a.option_strings) & self.show_last != set() ) def add_usage(self, usage, actions, *args, **kwargs): @@ -137,7 +121,6 @@ class OutputWrapper(TextIOBase): """ Wrapper around stdout/stderr """ - @property def style_func(self): return self._style_func @@ -149,7 +132,7 @@ class OutputWrapper(TextIOBase): else: self._style_func = lambda x: x - def __init__(self, out, ending="\n"): + def __init__(self, out, ending='\n'): self._out = out self.style_func = None self.ending = ending @@ -158,13 +141,13 @@ class OutputWrapper(TextIOBase): return getattr(self._out, name) def flush(self): - if hasattr(self._out, "flush"): + if hasattr(self._out, 'flush'): self._out.flush() def isatty(self): - return hasattr(self._out, "isatty") and self._out.isatty() + return hasattr(self._out, 'isatty') and self._out.isatty() - def write(self, msg="", style_func=None, ending=None): + def write(self, msg='', style_func=None, ending=None): ending = self.ending if ending is None else ending if ending and not msg.endswith(ending): msg += ending @@ -243,21 +226,19 @@ class BaseCommand: A tuple of any options the command uses which aren't defined by the argument parser. """ - # Metadata about this command. - help = "" + help = '' # Configuration shortcuts that alter various logic. _called_from_command_line = False output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;" requires_migrations_checks = False - requires_system_checks = "__all__" + requires_system_checks = '__all__' # Arguments, common to all commands, which aren't defined by the argument # parser. - base_stealth_options = ("stderr", "stdout") + base_stealth_options = ('stderr', 'stdout') # Command-specific options not defined by the argument parser. stealth_options = () - suppressed_base_arguments = set() def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False): self.stdout = OutputWrapper(stdout or sys.stdout) @@ -276,14 +257,12 @@ class BaseCommand: "list) instead of False.", RemovedInDjango41Warning, ) - self.requires_system_checks = ( - ALL_CHECKS if self.requires_system_checks else [] - ) + self.requires_system_checks = ALL_CHECKS if self.requires_system_checks else [] if ( - not isinstance(self.requires_system_checks, (list, tuple)) - and self.requires_system_checks != ALL_CHECKS + not isinstance(self.requires_system_checks, (list, tuple)) and + self.requires_system_checks != ALL_CHECKS ): - raise TypeError("requires_system_checks must be a list or tuple.") + raise TypeError('requires_system_checks must be a list or tuple.') def get_version(self): """ @@ -299,72 +278,44 @@ class BaseCommand: parse the arguments to this command. """ parser = CommandParser( - prog="%s %s" % (os.path.basename(prog_name), subcommand), + prog='%s %s' % (os.path.basename(prog_name), subcommand), description=self.help or None, formatter_class=DjangoHelpFormatter, - missing_args_message=getattr(self, "missing_args_message", None), - called_from_command_line=getattr(self, "_called_from_command_line", None), - **kwargs, + missing_args_message=getattr(self, 'missing_args_message', None), + called_from_command_line=getattr(self, '_called_from_command_line', None), + **kwargs ) - self.add_base_argument( - parser, - "--version", - action="version", - version=self.get_version(), - help="Show program's version number and exit.", + parser.add_argument('--version', action='version', version=self.get_version()) + parser.add_argument( + '-v', '--verbosity', default=1, + type=int, choices=[0, 1, 2, 3], + help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output', ) - self.add_base_argument( - parser, - "-v", - "--verbosity", - default=1, - type=int, - choices=[0, 1, 2, 3], + parser.add_argument( + '--settings', help=( - "Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, " - "3=very verbose output" - ), - ) - self.add_base_argument( - parser, - "--settings", - help=( - "The Python path to a settings module, e.g. " + 'The Python path to a settings module, e.g. ' '"myproject.settings.main". If this isn\'t provided, the ' - "DJANGO_SETTINGS_MODULE environment variable will be used." + 'DJANGO_SETTINGS_MODULE environment variable will be used.' ), ) - self.add_base_argument( - parser, - "--pythonpath", - help=( - "A directory to add to the Python path, e.g. " - '"/home/djangoprojects/myproject".' - ), + parser.add_argument( + '--pythonpath', + help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".', ) - self.add_base_argument( - parser, - "--traceback", - action="store_true", - help="Raise on CommandError exceptions.", - ) - self.add_base_argument( - parser, - "--no-color", - action="store_true", + parser.add_argument('--traceback', action='store_true', help='Raise on CommandError exceptions') + parser.add_argument( + '--no-color', action='store_true', help="Don't colorize the command output.", ) - self.add_base_argument( - parser, - "--force-color", - action="store_true", - help="Force colorization of the command output.", + parser.add_argument( + '--force-color', action='store_true', + help='Force colorization of the command output.', ) if self.requires_system_checks: parser.add_argument( - "--skip-checks", - action="store_true", - help="Skip system checks.", + '--skip-checks', action='store_true', + help='Skip system checks.', ) self.add_arguments(parser) return parser @@ -375,17 +326,6 @@ class BaseCommand: """ pass - def add_base_argument(self, parser, *args, **kwargs): - """ - Call the parser's add_argument() method, suppressing the help text - according to BaseCommand.suppressed_base_arguments. - """ - for arg in args: - if arg in self.suppressed_base_arguments: - kwargs["help"] = argparse.SUPPRESS - break - parser.add_argument(*args, **kwargs) - def print_help(self, prog_name, subcommand): """ Print the help message for this command, derived from @@ -408,7 +348,7 @@ class BaseCommand: options = parser.parse_args(argv[2:]) cmd_options = vars(options) # Move positional args out of options to mimic legacy optparse - args = cmd_options.pop("args", ()) + args = cmd_options.pop('args', ()) handle_default_options(options) try: self.execute(*args, **cmd_options) @@ -420,7 +360,7 @@ class BaseCommand: if isinstance(e, SystemCheckError): self.stderr.write(str(e), lambda x: x) else: - self.stderr.write("%s: %s" % (e.__class__.__name__, e)) + self.stderr.write('%s: %s' % (e.__class__.__name__, e)) sys.exit(e.returncode) finally: try: @@ -436,21 +376,19 @@ class BaseCommand: controlled by the ``requires_system_checks`` attribute, except if force-skipped). """ - if options["force_color"] and options["no_color"]: - raise CommandError( - "The --no-color and --force-color options can't be used together." - ) - if options["force_color"]: + if options['force_color'] and options['no_color']: + raise CommandError("The --no-color and --force-color options can't be used together.") + if options['force_color']: self.style = color_style(force_color=True) - elif options["no_color"]: + elif options['no_color']: self.style = no_style() self.stderr.style_func = None - if options.get("stdout"): - self.stdout = OutputWrapper(options["stdout"]) - if options.get("stderr"): - self.stderr = OutputWrapper(options["stderr"]) + if options.get('stdout'): + self.stdout = OutputWrapper(options['stdout']) + if options.get('stderr'): + self.stderr = OutputWrapper(options['stderr']) - if self.requires_system_checks and not options["skip_checks"]: + if self.requires_system_checks and not options['skip_checks']: if self.requires_system_checks == ALL_CHECKS: self.check() else: @@ -460,8 +398,8 @@ class BaseCommand: output = self.handle(*args, **options) if output: if self.output_transaction: - connection = connections[options.get("database", DEFAULT_DB_ALIAS)] - output = "%s\n%s\n%s" % ( + connection = connections[options.get('database', DEFAULT_DB_ALIAS)] + output = '%s\n%s\n%s' % ( self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()), output, self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()), @@ -469,15 +407,9 @@ class BaseCommand: self.stdout.write(output) return output - def check( - self, - app_configs=None, - tags=None, - display_num_errors=False, - include_deployment_checks=False, - fail_level=checks.ERROR, - databases=None, - ): + def check(self, app_configs=None, tags=None, display_num_errors=False, + include_deployment_checks=False, fail_level=checks.ERROR, + databases=None): """ Use the system check framework to validate entire Django project. Raise CommandError for any serious message (error or critical errors). @@ -495,35 +427,17 @@ class BaseCommand: visible_issue_count = 0 # excludes silenced warnings if all_issues: - debugs = [ - e for e in all_issues if e.level < checks.INFO and not e.is_silenced() - ] - infos = [ - e - for e in all_issues - if checks.INFO <= e.level < checks.WARNING and not e.is_silenced() - ] - warnings = [ - e - for e in all_issues - if checks.WARNING <= e.level < checks.ERROR and not e.is_silenced() - ] - errors = [ - e - for e in all_issues - if checks.ERROR <= e.level < checks.CRITICAL and not e.is_silenced() - ] - criticals = [ - e - for e in all_issues - if checks.CRITICAL <= e.level and not e.is_silenced() - ] + debugs = [e for e in all_issues if e.level < checks.INFO and not e.is_silenced()] + infos = [e for e in all_issues if checks.INFO <= e.level < checks.WARNING and not e.is_silenced()] + warnings = [e for e in all_issues if checks.WARNING <= e.level < checks.ERROR and not e.is_silenced()] + errors = [e for e in all_issues if checks.ERROR <= e.level < checks.CRITICAL and not e.is_silenced()] + criticals = [e for e in all_issues if checks.CRITICAL <= e.level and not e.is_silenced()] sorted_issues = [ - (criticals, "CRITICALS"), - (errors, "ERRORS"), - (warnings, "WARNINGS"), - (infos, "INFOS"), - (debugs, "DEBUGS"), + (criticals, 'CRITICALS'), + (errors, 'ERRORS'), + (warnings, 'WARNINGS'), + (infos, 'INFOS'), + (debugs, 'DEBUGS'), ] for issues, group_name in sorted_issues: @@ -533,23 +447,20 @@ class BaseCommand: self.style.ERROR(str(e)) if e.is_serious() else self.style.WARNING(str(e)) - for e in issues - ) + for e in issues) formatted = "\n".join(sorted(formatted)) - body += "\n%s:\n%s\n" % (group_name, formatted) + body += '\n%s:\n%s\n' % (group_name, formatted) if visible_issue_count: header = "System check identified some issues:\n" if display_num_errors: if visible_issue_count: - footer += "\n" + footer += '\n' footer += "System check identified %s (%s silenced)." % ( - "no issues" - if visible_issue_count == 0 - else "1 issue" - if visible_issue_count == 1 - else "%s issues" % visible_issue_count, + "no issues" if visible_issue_count == 0 else + "1 issue" if visible_issue_count == 1 else + "%s issues" % visible_issue_count, len(all_issues) - visible_issue_count, ) @@ -571,7 +482,6 @@ class BaseCommand: migrations in the database. """ from django.db.migrations.executor import MigrationExecutor - try: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) except ImproperlyConfigured: @@ -580,32 +490,25 @@ class BaseCommand: plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if plan: - apps_waiting_migration = sorted( - {migration.app_label for migration, backwards in plan} - ) + apps_waiting_migration = sorted({migration.app_label for migration, backwards in plan}) self.stdout.write( self.style.NOTICE( "\nYou have %(unapplied_migration_count)s unapplied migration(s). " "Your project may not work properly until you apply the " - "migrations for app(s): %(apps_waiting_migration)s." - % { + "migrations for app(s): %(apps_waiting_migration)s." % { "unapplied_migration_count": len(plan), "apps_waiting_migration": ", ".join(apps_waiting_migration), } ) ) - self.stdout.write( - self.style.NOTICE("Run 'python manage.py migrate' to apply them.") - ) + self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.")) def handle(self, *args, **options): """ The actual logic of the command. Subclasses must implement this method. """ - raise NotImplementedError( - "subclasses of BaseCommand must provide a handle() method" - ) + raise NotImplementedError('subclasses of BaseCommand must provide a handle() method') class AppCommand(BaseCommand): @@ -616,32 +519,23 @@ class AppCommand(BaseCommand): Rather than implementing ``handle()``, subclasses must implement ``handle_app_config()``, which will be called once for each application. """ - missing_args_message = "Enter at least one application label." def add_arguments(self, parser): - parser.add_argument( - "args", - metavar="app_label", - nargs="+", - help="One or more application label.", - ) + parser.add_argument('args', metavar='app_label', nargs='+', help='One or more application label.') def handle(self, *app_labels, **options): from django.apps import apps - try: app_configs = [apps.get_app_config(app_label) for app_label in app_labels] except (LookupError, ImportError) as e: - raise CommandError( - "%s. Are you sure your INSTALLED_APPS setting is correct?" % e - ) + raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e) output = [] for app_config in app_configs: app_output = self.handle_app_config(app_config, **options) if app_output: output.append(app_output) - return "\n".join(output) + return '\n'.join(output) def handle_app_config(self, app_config, **options): """ @@ -649,8 +543,8 @@ class AppCommand(BaseCommand): corresponding to an application label given on the command line. """ raise NotImplementedError( - "Subclasses of AppCommand must provide a handle_app_config() method." - ) + "Subclasses of AppCommand must provide" + "a handle_app_config() method.") class LabelCommand(BaseCommand): @@ -665,12 +559,11 @@ class LabelCommand(BaseCommand): If the arguments should be names of installed applications, use ``AppCommand`` instead. """ - - label = "label" + label = 'label' missing_args_message = "Enter at least one %s." % label def add_arguments(self, parser): - parser.add_argument("args", metavar=self.label, nargs="+") + parser.add_argument('args', metavar=self.label, nargs='+') def handle(self, *labels, **options): output = [] @@ -678,13 +571,11 @@ class LabelCommand(BaseCommand): label_output = self.handle_label(label, **options) if label_output: output.append(label_output) - return "\n".join(output) + return '\n'.join(output) def handle_label(self, label, **options): """ Perform the command's actions for ``label``, which will be the string as given on the command line. """ - raise NotImplementedError( - "subclasses of LabelCommand must provide a handle_label() method" - ) + raise NotImplementedError('subclasses of LabelCommand must provide a handle_label() method') diff --git a/venv/Lib/site-packages/django/core/management/color.py b/venv/Lib/site-packages/django/core/management/color.py index d2255d2..be8c31b 100644 --- a/venv/Lib/site-packages/django/core/management/color.py +++ b/venv/Lib/site-packages/django/core/management/color.py @@ -10,7 +10,6 @@ from django.utils import termcolors try: import colorama - colorama.init() except (ImportError, OSError): HAS_COLORAMA = False @@ -23,7 +22,6 @@ def supports_color(): Return True if the running system's terminal supports color, and False otherwise. """ - def vt_codes_enabled_in_windows_registry(): """ Check the Windows Registry to see if VT code handling has been enabled @@ -35,28 +33,26 @@ def supports_color(): except ImportError: return False else: - reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Console") + reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Console') try: - reg_key_value, _ = winreg.QueryValueEx(reg_key, "VirtualTerminalLevel") + reg_key_value, _ = winreg.QueryValueEx(reg_key, 'VirtualTerminalLevel') except FileNotFoundError: return False else: return reg_key_value == 1 # isatty is not always implemented, #6223. - is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() + is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() return is_a_tty and ( - sys.platform != "win32" - or HAS_COLORAMA - or "ANSICON" in os.environ - or + sys.platform != 'win32' or + HAS_COLORAMA or + 'ANSICON' in os.environ or # Windows Terminal supports VT codes. - "WT_SESSION" in os.environ - or + 'WT_SESSION' in os.environ or # Microsoft Visual Studio Code's built-in terminal supports colors. - os.environ.get("TERM_PROGRAM") == "vscode" - or vt_codes_enabled_in_windows_registry() + os.environ.get('TERM_PROGRAM') == 'vscode' or + vt_codes_enabled_in_windows_registry() ) @@ -64,7 +60,7 @@ class Style: pass -def make_style(config_string=""): +def make_style(config_string=''): """ Create a Style object from the given config_string. @@ -83,10 +79,8 @@ def make_style(config_string=""): format = color_settings.get(role, {}) style_func = termcolors.make_style(**format) else: - def style_func(x): return x - setattr(style, role, style_func) # For backwards compatibility, @@ -101,7 +95,7 @@ def no_style(): """ Return a Style object with no color scheme. """ - return make_style("nocolor") + return make_style('nocolor') def color_style(force_color=False): @@ -110,4 +104,4 @@ def color_style(force_color=False): """ if not force_color and not supports_color(): return no_style() - return make_style(os.environ.get("DJANGO_COLORS", "")) + return make_style(os.environ.get('DJANGO_COLORS', '')) diff --git a/venv/Lib/site-packages/django/core/management/commands/check.py b/venv/Lib/site-packages/django/core/management/commands/check.py index 7624b85..a925636 100644 --- a/venv/Lib/site-packages/django/core/management/commands/check.py +++ b/venv/Lib/site-packages/django/core/management/commands/check.py @@ -10,46 +10,37 @@ class Command(BaseCommand): requires_system_checks = [] def add_arguments(self, parser): - parser.add_argument("args", metavar="app_label", nargs="*") + parser.add_argument('args', metavar='app_label', nargs='*') parser.add_argument( - "--tag", - "-t", - action="append", - dest="tags", - help="Run only checks labeled with given tag.", + '--tag', '-t', action='append', dest='tags', + help='Run only checks labeled with given tag.', ) parser.add_argument( - "--list-tags", - action="store_true", - help="List available tags.", + '--list-tags', action='store_true', + help='List available tags.', ) parser.add_argument( - "--deploy", - action="store_true", - help="Check deployment settings.", + '--deploy', action='store_true', + help='Check deployment settings.', ) parser.add_argument( - "--fail-level", - default="ERROR", - choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], + '--fail-level', + default='ERROR', + choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'], help=( - "Message level that will cause the command to exit with a " - "non-zero status. Default is ERROR." + 'Message level that will cause the command to exit with a ' + 'non-zero status. Default is ERROR.' ), ) parser.add_argument( - "--database", - action="append", - dest="databases", - help="Run database related checks against these aliases.", + '--database', action='append', dest='databases', + help='Run database related checks against these aliases.', ) def handle(self, *app_labels, **options): - include_deployment_checks = options["deploy"] - if options["list_tags"]: - self.stdout.write( - "\n".join(sorted(registry.tags_available(include_deployment_checks))) - ) + include_deployment_checks = options['deploy'] + if options['list_tags']: + self.stdout.write('\n'.join(sorted(registry.tags_available(include_deployment_checks)))) return if app_labels: @@ -57,27 +48,23 @@ class Command(BaseCommand): else: app_configs = None - tags = options["tags"] + tags = options['tags'] if tags: try: invalid_tag = next( - tag - for tag in tags - if not checks.tag_exists(tag, include_deployment_checks) + tag for tag in tags if not checks.tag_exists(tag, include_deployment_checks) ) except StopIteration: # no invalid tags pass else: - raise CommandError( - 'There is no system check with the "%s" tag.' % invalid_tag - ) + raise CommandError('There is no system check with the "%s" tag.' % invalid_tag) self.check( app_configs=app_configs, tags=tags, display_num_errors=True, include_deployment_checks=include_deployment_checks, - fail_level=getattr(checks, options["fail_level"]), - databases=options["databases"], + fail_level=getattr(checks, options['fail_level']), + databases=options['databases'], ) diff --git a/venv/Lib/site-packages/django/core/management/commands/compilemessages.py b/venv/Lib/site-packages/django/core/management/commands/compilemessages.py index 9ed3ef7..cad24f8 100644 --- a/venv/Lib/site-packages/django/core/management/commands/compilemessages.py +++ b/venv/Lib/site-packages/django/core/management/commands/compilemessages.py @@ -5,22 +5,22 @@ import os from pathlib import Path from django.core.management.base import BaseCommand, CommandError -from django.core.management.utils import find_command, is_ignored_path, popen_wrapper +from django.core.management.utils import ( + find_command, is_ignored_path, popen_wrapper, +) def has_bom(fn): - with fn.open("rb") as f: + with fn.open('rb') as f: sample = f.read(4) - return sample.startswith( - (codecs.BOM_UTF8, codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE) - ) + return sample.startswith((codecs.BOM_UTF8, codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)) def is_writable(path): # Known side effect: updating file access/modified time to current time if # it is writable. try: - with open(path, "a"): + with open(path, 'a'): os.utime(path, None) except OSError: return False @@ -28,91 +28,71 @@ def is_writable(path): class Command(BaseCommand): - help = "Compiles .po files to .mo files for use with builtin gettext support." + help = 'Compiles .po files to .mo files for use with builtin gettext support.' requires_system_checks = [] - program = "msgfmt" - program_options = ["--check-format"] + program = 'msgfmt' + program_options = ['--check-format'] def add_arguments(self, parser): parser.add_argument( - "--locale", - "-l", - action="append", - default=[], - help="Locale(s) to process (e.g. de_AT). Default is to process all. " - "Can be used multiple times.", + '--locale', '-l', action='append', default=[], + help='Locale(s) to process (e.g. de_AT). Default is to process all. ' + 'Can be used multiple times.', ) parser.add_argument( - "--exclude", - "-x", - action="append", - default=[], - help="Locales to exclude. Default is none. Can be used multiple times.", + '--exclude', '-x', action='append', default=[], + help='Locales to exclude. Default is none. Can be used multiple times.', ) parser.add_argument( - "--use-fuzzy", - "-f", - dest="fuzzy", - action="store_true", - help="Use fuzzy translations.", + '--use-fuzzy', '-f', dest='fuzzy', action='store_true', + help='Use fuzzy translations.', ) parser.add_argument( - "--ignore", - "-i", - action="append", - dest="ignore_patterns", - default=[], - metavar="PATTERN", - help="Ignore directories matching this glob-style pattern. " - "Use multiple times to ignore more.", + '--ignore', '-i', action='append', dest='ignore_patterns', + default=[], metavar='PATTERN', + help='Ignore directories matching this glob-style pattern. ' + 'Use multiple times to ignore more.', ) def handle(self, **options): - locale = options["locale"] - exclude = options["exclude"] - ignore_patterns = set(options["ignore_patterns"]) - self.verbosity = options["verbosity"] - if options["fuzzy"]: - self.program_options = self.program_options + ["-f"] + locale = options['locale'] + exclude = options['exclude'] + ignore_patterns = set(options['ignore_patterns']) + self.verbosity = options['verbosity'] + if options['fuzzy']: + self.program_options = self.program_options + ['-f'] if find_command(self.program) is None: - raise CommandError( - "Can't find %s. Make sure you have GNU gettext " - "tools 0.15 or newer installed." % self.program - ) + raise CommandError("Can't find %s. Make sure you have GNU gettext " + "tools 0.15 or newer installed." % self.program) - basedirs = [os.path.join("conf", "locale"), "locale"] - if os.environ.get("DJANGO_SETTINGS_MODULE"): + basedirs = [os.path.join('conf', 'locale'), 'locale'] + if os.environ.get('DJANGO_SETTINGS_MODULE'): from django.conf import settings - basedirs.extend(settings.LOCALE_PATHS) # Walk entire tree, looking for locale directories - for dirpath, dirnames, filenames in os.walk(".", topdown=True): + for dirpath, dirnames, filenames in os.walk('.', topdown=True): for dirname in dirnames: - if is_ignored_path( - os.path.normpath(os.path.join(dirpath, dirname)), ignore_patterns - ): + if is_ignored_path(os.path.normpath(os.path.join(dirpath, dirname)), ignore_patterns): dirnames.remove(dirname) - elif dirname == "locale": + elif dirname == 'locale': basedirs.append(os.path.join(dirpath, dirname)) # Gather existing directories. basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs))) if not basedirs: - raise CommandError( - "This script should be run from the Django Git " - "checkout or your project or app tree, or with " - "the settings module specified." - ) + raise CommandError("This script should be run from the Django Git " + "checkout or your project or app tree, or with " + "the settings module specified.") # Build locale list all_locales = [] for basedir in basedirs: - locale_dirs = filter(os.path.isdir, glob.glob("%s/*" % basedir)) + locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % basedir)) all_locales.extend(map(os.path.basename, locale_dirs)) # Account for excluded locales @@ -122,22 +102,18 @@ class Command(BaseCommand): self.has_errors = False for basedir in basedirs: if locales: - dirs = [ - os.path.join(basedir, locale, "LC_MESSAGES") for locale in locales - ] + dirs = [os.path.join(basedir, locale, 'LC_MESSAGES') for locale in locales] else: dirs = [basedir] locations = [] for ldir in dirs: for dirpath, dirnames, filenames in os.walk(ldir): - locations.extend( - (dirpath, f) for f in filenames if f.endswith(".po") - ) + locations.extend((dirpath, f) for f in filenames if f.endswith('.po')) if locations: self.compile_messages(locations) if self.has_errors: - raise CommandError("compilemessages generated one or more errors.") + raise CommandError('compilemessages generated one or more errors.') def compile_messages(self, locations): """ @@ -147,25 +123,24 @@ class Command(BaseCommand): futures = [] for i, (dirpath, f) in enumerate(locations): po_path = Path(dirpath) / f - mo_path = po_path.with_suffix(".mo") + mo_path = po_path.with_suffix('.mo') try: if mo_path.stat().st_mtime >= po_path.stat().st_mtime: if self.verbosity > 0: self.stdout.write( - "File “%s” is already compiled and up to date." + 'File “%s” is already compiled and up to date.' % po_path ) continue except FileNotFoundError: pass if self.verbosity > 0: - self.stdout.write("processing file %s in %s" % (f, dirpath)) + self.stdout.write('processing file %s in %s' % (f, dirpath)) if has_bom(po_path): self.stderr.write( - "The %s file has a BOM (Byte Order Mark). Django only " - "supports .po files encoded in UTF-8 and without any BOM." - % po_path + 'The %s file has a BOM (Byte Order Mark). Django only ' + 'supports .po files encoded in UTF-8 and without any BOM.' % po_path ) self.has_errors = True continue @@ -173,13 +148,15 @@ class Command(BaseCommand): # Check writability on first location if i == 0 and not is_writable(mo_path): self.stderr.write( - "The po files under %s are in a seemingly not writable " - "location. mo files will not be updated/created." % dirpath + 'The po files under %s are in a seemingly not writable location. ' + 'mo files will not be updated/created.' % dirpath ) self.has_errors = True return - args = [self.program, *self.program_options, "-o", mo_path, po_path] + # PY37: Remove str() when dropping support for PY37. + # https://bugs.python.org/issue31961 + args = [self.program, *self.program_options, '-o', str(mo_path), str(po_path)] futures.append(executor.submit(popen_wrapper, args)) for future in concurrent.futures.as_completed(futures): @@ -187,9 +164,7 @@ class Command(BaseCommand): if status: if self.verbosity > 0: if errors: - self.stderr.write( - "Execution of %s failed: %s" % (self.program, errors) - ) + self.stderr.write("Execution of %s failed: %s" % (self.program, errors)) else: self.stderr.write("Execution of %s failed" % self.program) self.has_errors = True diff --git a/venv/Lib/site-packages/django/core/management/commands/createcachetable.py b/venv/Lib/site-packages/django/core/management/commands/createcachetable.py index 65ed168..84f6104 100644 --- a/venv/Lib/site-packages/django/core/management/commands/createcachetable.py +++ b/venv/Lib/site-packages/django/core/management/commands/createcachetable.py @@ -3,12 +3,7 @@ from django.core.cache import caches from django.core.cache.backends.db import BaseDatabaseCache from django.core.management.base import BaseCommand, CommandError from django.db import ( - DEFAULT_DB_ALIAS, - DatabaseError, - connections, - models, - router, - transaction, + DEFAULT_DB_ALIAS, DatabaseError, connections, models, router, transaction, ) @@ -19,30 +14,24 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "args", - metavar="table_name", - nargs="*", - help=( - "Optional table names. Otherwise, settings.CACHES is used to find " - "cache tables." - ), + 'args', metavar='table_name', nargs='*', + help='Optional table names. Otherwise, settings.CACHES is used to find cache tables.', ) parser.add_argument( - "--database", + '--database', default=DEFAULT_DB_ALIAS, - help="Nominates a database onto which the cache tables will be " - 'installed. Defaults to the "default" database.', + help='Nominates a database onto which the cache tables will be ' + 'installed. Defaults to the "default" database.', ) parser.add_argument( - "--dry-run", - action="store_true", - help="Does not create the table, just prints the SQL that would be run.", + '--dry-run', action='store_true', + help='Does not create the table, just prints the SQL that would be run.', ) def handle(self, *tablenames, **options): - db = options["database"] - self.verbosity = options["verbosity"] - dry_run = options["dry_run"] + db = options['database'] + self.verbosity = options['verbosity'] + dry_run = options['dry_run'] if tablenames: # Legacy behavior, tablename specified as argument for tablename in tablenames: @@ -66,11 +55,9 @@ class Command(BaseCommand): fields = ( # "key" is a reserved word in MySQL, so use "cache_key" instead. - models.CharField( - name="cache_key", max_length=255, unique=True, primary_key=True - ), - models.TextField(name="value"), - models.DateTimeField(name="expires", db_index=True), + models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True), + models.TextField(name='value'), + models.DateTimeField(name='expires', db_index=True), ) table_output = [] index_output = [] @@ -79,7 +66,7 @@ class Command(BaseCommand): field_output = [ qn(f.name), f.db_type(connection=connection), - "%sNULL" % ("NOT " if not f.null else ""), + '%sNULL' % ('NOT ' if not f.null else ''), ] if f.primary_key: field_output.append("PRIMARY KEY") @@ -88,21 +75,14 @@ class Command(BaseCommand): if f.db_index: unique = "UNIQUE " if f.unique else "" index_output.append( - "CREATE %sINDEX %s ON %s (%s);" - % ( - unique, - qn("%s_%s" % (tablename, f.name)), - qn(tablename), - qn(f.name), - ) + "CREATE %sINDEX %s ON %s (%s);" % + (unique, qn('%s_%s' % (tablename, f.name)), qn(tablename), qn(f.name)) ) table_output.append(" ".join(field_output)) full_statement = ["CREATE TABLE %s (" % qn(tablename)] for i, line in enumerate(table_output): - full_statement.append( - " %s%s" % (line, "," if i < len(table_output) - 1 else "") - ) - full_statement.append(");") + full_statement.append(' %s%s' % (line, ',' if i < len(table_output) - 1 else '')) + full_statement.append(');') full_statement = "\n".join(full_statement) @@ -112,17 +92,14 @@ class Command(BaseCommand): self.stdout.write(statement) return - with transaction.atomic( - using=database, savepoint=connection.features.can_rollback_ddl - ): + with transaction.atomic(using=database, savepoint=connection.features.can_rollback_ddl): with connection.cursor() as curs: try: curs.execute(full_statement) except DatabaseError as e: raise CommandError( - "Cache table '%s' could not be created.\nThe error was: %s." - % (tablename, e) - ) + "Cache table '%s' could not be created.\nThe error was: %s." % + (tablename, e)) for statement in index_output: curs.execute(statement) diff --git a/venv/Lib/site-packages/django/core/management/commands/dbshell.py b/venv/Lib/site-packages/django/core/management/commands/dbshell.py index 30d2765..cd94787 100644 --- a/venv/Lib/site-packages/django/core/management/commands/dbshell.py +++ b/venv/Lib/site-packages/django/core/management/commands/dbshell.py @@ -14,34 +14,29 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - "Nominates a database onto which to open a shell. Defaults to the " - '"default" database.' - ), + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database onto which to open a shell. Defaults to the "default" database.', ) - parameters = parser.add_argument_group("parameters", prefix_chars="--") - parameters.add_argument("parameters", nargs="*") + parameters = parser.add_argument_group('parameters', prefix_chars='--') + parameters.add_argument('parameters', nargs='*') def handle(self, **options): - connection = connections[options["database"]] + connection = connections[options['database']] try: - connection.client.runshell(options["parameters"]) + connection.client.runshell(options['parameters']) except FileNotFoundError: # Note that we're assuming the FileNotFoundError relates to the # command missing. It could be raised for some other reason, in # which case this error message would be inaccurate. Still, this # message catches the common case. raise CommandError( - "You appear not to have the %r program installed or on your path." - % connection.client.executable_name + 'You appear not to have the %r program installed or on your path.' % + connection.client.executable_name ) except subprocess.CalledProcessError as e: raise CommandError( - '"%s" returned non-zero exit status %s.' - % ( - " ".join(e.cmd), + '"%s" returned non-zero exit status %s.' % ( + ' '.join(e.cmd), e.returncode, ), returncode=e.returncode, diff --git a/venv/Lib/site-packages/django/core/management/commands/diffsettings.py b/venv/Lib/site-packages/django/core/management/commands/diffsettings.py index 047e476..5adf35e 100644 --- a/venv/Lib/site-packages/django/core/management/commands/diffsettings.py +++ b/venv/Lib/site-packages/django/core/management/commands/diffsettings.py @@ -1,7 +1,7 @@ from django.core.management.base import BaseCommand -def module_to_dict(module, omittable=lambda k: k.startswith("_") or not k.isupper()): +def module_to_dict(module, omittable=lambda k: k.startswith('_') or not k.isupper()): """Convert a module namespace to a Python dictionary.""" return {k: repr(getattr(module, k)) for k in dir(module) if not omittable(k)} @@ -14,25 +14,21 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "--all", - action="store_true", + '--all', action='store_true', help=( 'Display all settings, regardless of their value. In "hash" ' 'mode, default values are prefixed by "###".' ), ) parser.add_argument( - "--default", - metavar="MODULE", + '--default', metavar='MODULE', help=( - "The settings module to compare the current settings against. Leave " - "empty to compare against Django's default settings." + "The settings module to compare the current settings against. Leave empty to " + "compare against Django's default settings." ), ) parser.add_argument( - "--output", - default="hash", - choices=("hash", "unified"), + '--output', default='hash', choices=('hash', 'unified'), help=( "Selects the output format. 'hash' mode displays each changed " "setting, with the settings that don't appear in the defaults " @@ -50,15 +46,13 @@ class Command(BaseCommand): settings._setup() user_settings = module_to_dict(settings._wrapped) - default = options["default"] - default_settings = module_to_dict( - Settings(default) if default else global_settings - ) + default = options['default'] + default_settings = module_to_dict(Settings(default) if default else global_settings) output_func = { - "hash": self.output_hash, - "unified": self.output_unified, - }[options["output"]] - return "\n".join(output_func(user_settings, default_settings, **options)) + 'hash': self.output_hash, + 'unified': self.output_unified, + }[options['output']] + return '\n'.join(output_func(user_settings, default_settings, **options)) def output_hash(self, user_settings, default_settings, **options): # Inspired by Postfix's "postconf -n". @@ -68,7 +62,7 @@ class Command(BaseCommand): output.append("%s = %s ###" % (key, user_settings[key])) elif user_settings[key] != default_settings[key]: output.append("%s = %s" % (key, user_settings[key])) - elif options["all"]: + elif options['all']: output.append("### %s = %s" % (key, user_settings[key])) return output @@ -76,16 +70,10 @@ class Command(BaseCommand): output = [] for key in sorted(user_settings): if key not in default_settings: - output.append( - self.style.SUCCESS("+ %s = %s" % (key, user_settings[key])) - ) + output.append(self.style.SUCCESS("+ %s = %s" % (key, user_settings[key]))) elif user_settings[key] != default_settings[key]: - output.append( - self.style.ERROR("- %s = %s" % (key, default_settings[key])) - ) - output.append( - self.style.SUCCESS("+ %s = %s" % (key, user_settings[key])) - ) - elif options["all"]: + output.append(self.style.ERROR("- %s = %s" % (key, default_settings[key]))) + output.append(self.style.SUCCESS("+ %s = %s" % (key, user_settings[key]))) + elif options['all']: output.append(" %s = %s" % (key, user_settings[key])) return output diff --git a/venv/Lib/site-packages/django/core/management/commands/dumpdata.py b/venv/Lib/site-packages/django/core/management/commands/dumpdata.py index 038b826..925a23a 100644 --- a/venv/Lib/site-packages/django/core/management/commands/dumpdata.py +++ b/venv/Lib/site-packages/django/core/management/commands/dumpdata.py @@ -10,14 +10,12 @@ from django.db import DEFAULT_DB_ALIAS, router try: import bz2 - has_bz2 = True except ImportError: has_bz2 = False try: import lzma - has_lzma = True except ImportError: has_lzma = False @@ -35,85 +33,65 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "args", - metavar="app_label[.ModelName]", - nargs="*", - help=( - "Restricts dumped data to the specified app_label or " - "app_label.ModelName." - ), + 'args', metavar='app_label[.ModelName]', nargs='*', + help='Restricts dumped data to the specified app_label or app_label.ModelName.', ) parser.add_argument( - "--format", - default="json", - help="Specifies the output serialization format for fixtures.", + '--format', default='json', + help='Specifies the output serialization format for fixtures.', ) parser.add_argument( - "--indent", - type=int, - help="Specifies the indent level to use when pretty-printing output.", + '--indent', type=int, + help='Specifies the indent level to use when pretty-printing output.', ) parser.add_argument( - "--database", + '--database', default=DEFAULT_DB_ALIAS, - help="Nominates a specific database to dump fixtures from. " - 'Defaults to the "default" database.', + help='Nominates a specific database to dump fixtures from. ' + 'Defaults to the "default" database.', ) parser.add_argument( - "-e", - "--exclude", - action="append", - default=[], - help="An app_label or app_label.ModelName to exclude " - "(use multiple --exclude to exclude multiple apps/models).", + '-e', '--exclude', action='append', default=[], + help='An app_label or app_label.ModelName to exclude ' + '(use multiple --exclude to exclude multiple apps/models).', ) parser.add_argument( - "--natural-foreign", - action="store_true", - dest="use_natural_foreign_keys", - help="Use natural foreign keys if they are available.", + '--natural-foreign', action='store_true', dest='use_natural_foreign_keys', + help='Use natural foreign keys if they are available.', ) parser.add_argument( - "--natural-primary", - action="store_true", - dest="use_natural_primary_keys", - help="Use natural primary keys if they are available.", + '--natural-primary', action='store_true', dest='use_natural_primary_keys', + help='Use natural primary keys if they are available.', ) parser.add_argument( - "-a", - "--all", - action="store_true", - dest="use_base_manager", - help=( - "Use Django's base manager to dump all models stored in the database, " - "including those that would otherwise be filtered or modified by a " - "custom manager." - ), + '-a', '--all', action='store_true', dest='use_base_manager', + help="Use Django's base manager to dump all models stored in the database, " + "including those that would otherwise be filtered or modified by a custom manager.", ) parser.add_argument( - "--pks", - dest="primary_keys", + '--pks', dest='primary_keys', help="Only dump objects with given primary keys. Accepts a comma-separated " - "list of keys. This option only works when you specify one model.", + "list of keys. This option only works when you specify one model.", ) parser.add_argument( - "-o", "--output", help="Specifies file to which the output is written." + '-o', '--output', + help='Specifies file to which the output is written.' ) def handle(self, *app_labels, **options): - format = options["format"] - indent = options["indent"] - using = options["database"] - excludes = options["exclude"] - output = options["output"] - show_traceback = options["traceback"] - use_natural_foreign_keys = options["use_natural_foreign_keys"] - use_natural_primary_keys = options["use_natural_primary_keys"] - use_base_manager = options["use_base_manager"] - pks = options["primary_keys"] + format = options['format'] + indent = options['indent'] + using = options['database'] + excludes = options['exclude'] + output = options['output'] + show_traceback = options['traceback'] + use_natural_foreign_keys = options['use_natural_foreign_keys'] + use_natural_primary_keys = options['use_natural_primary_keys'] + use_base_manager = options['use_base_manager'] + pks = options['primary_keys'] if pks: - primary_keys = [pk.strip() for pk in pks.split(",")] + primary_keys = [pk.strip() for pk in pks.split(',')] else: primary_keys = [] @@ -123,10 +101,8 @@ class Command(BaseCommand): if primary_keys: raise CommandError("You can only use --pks option with one model") app_list = dict.fromkeys( - app_config - for app_config in apps.get_app_configs() - if app_config.models_module is not None - and app_config not in excluded_apps + app_config for app_config in apps.get_app_configs() + if app_config.models_module is not None and app_config not in excluded_apps ) else: if len(app_labels) > 1 and primary_keys: @@ -134,7 +110,7 @@ class Command(BaseCommand): app_list = {} for label in app_labels: try: - app_label, model_label = label.split(".") + app_label, model_label = label.split('.') try: app_config = apps.get_app_config(app_label) except LookupError as e: @@ -144,9 +120,7 @@ class Command(BaseCommand): try: model = app_config.get_model(model_label) except LookupError: - raise CommandError( - "Unknown model: %s.%s" % (app_label, model_label) - ) + raise CommandError("Unknown model: %s.%s" % (app_label, model_label)) app_list_value = app_list.setdefault(app_config, []) @@ -157,9 +131,7 @@ class Command(BaseCommand): app_list_value.append(model) except ValueError: if primary_keys: - raise CommandError( - "You can only use --pks option with one model" - ) + raise CommandError("You can only use --pks option with one model") # This is just an app - no model qualifier app_label = label try: @@ -186,9 +158,7 @@ class Command(BaseCommand): count the number of objects to be serialized. """ if use_natural_foreign_keys: - models = serializers.sort_dependencies( - app_list.items(), allow_cycles=True - ) + models = serializers.sort_dependencies(app_list.items(), allow_cycles=True) else: # There is no need to sort dependencies when natural foreign # keys are not used. @@ -203,8 +173,7 @@ class Command(BaseCommand): continue if model._meta.proxy and model._meta.proxy_for_model not in models: warnings.warn( - "%s is a proxy model and won't be serialized." - % model._meta.label, + "%s is a proxy model and won't be serialized." % model._meta.label, category=ProxyModelWarning, ) if not model._meta.proxy and router.allow_migrate_model(using, model): @@ -226,27 +195,25 @@ class Command(BaseCommand): progress_output = None object_count = 0 # If dumpdata is outputting to stdout, there is no way to display progress - if output and self.stdout.isatty() and options["verbosity"] > 0: + if output and self.stdout.isatty() and options['verbosity'] > 0: progress_output = self.stdout object_count = sum(get_objects(count_only=True)) if output: file_root, file_ext = os.path.splitext(output) compression_formats = { - ".bz2": (open, {}, file_root), - ".gz": (gzip.open, {}, output), - ".lzma": (open, {}, file_root), - ".xz": (open, {}, file_root), - ".zip": (open, {}, file_root), + '.bz2': (open, {}, file_root), + '.gz': (gzip.open, {}, output), + '.lzma': (open, {}, file_root), + '.xz': (open, {}, file_root), + '.zip': (open, {}, file_root), } if has_bz2: - compression_formats[".bz2"] = (bz2.open, {}, output) + compression_formats['.bz2'] = (bz2.open, {}, output) if has_lzma: - compression_formats[".lzma"] = ( - lzma.open, - {"format": lzma.FORMAT_ALONE}, - output, + compression_formats['.lzma'] = ( + lzma.open, {'format': lzma.FORMAT_ALONE}, output ) - compression_formats[".xz"] = (lzma.open, {}, output) + compression_formats['.xz'] = (lzma.open, {}, output) try: open_method, kwargs, file_path = compression_formats[file_ext] except KeyError: @@ -258,18 +225,15 @@ class Command(BaseCommand): f"Fixtures saved in '{file_name}'.", RuntimeWarning, ) - stream = open_method(file_path, "wt", **kwargs) + stream = open_method(file_path, 'wt', **kwargs) else: stream = None try: serializers.serialize( - format, - get_objects(), - indent=indent, + format, get_objects(), indent=indent, use_natural_foreign_keys=use_natural_foreign_keys, use_natural_primary_keys=use_natural_primary_keys, - stream=stream or self.stdout, - progress_output=progress_output, + stream=stream or self.stdout, progress_output=progress_output, object_count=object_count, ) finally: diff --git a/venv/Lib/site-packages/django/core/management/commands/flush.py b/venv/Lib/site-packages/django/core/management/commands/flush.py index e9d440d..6737b9b 100644 --- a/venv/Lib/site-packages/django/core/management/commands/flush.py +++ b/venv/Lib/site-packages/django/core/management/commands/flush.py @@ -9,34 +9,30 @@ from django.db import DEFAULT_DB_ALIAS, connections class Command(BaseCommand): help = ( - "Removes ALL DATA from the database, including data added during " + 'Removes ALL DATA from the database, including data added during ' 'migrations. Does not achieve a "fresh install" state.' ) - stealth_options = ("reset_sequences", "allow_cascade", "inhibit_post_migrate") + stealth_options = ('reset_sequences', 'allow_cascade', 'inhibit_post_migrate') def add_arguments(self, parser): parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, + '--database', default=DEFAULT_DB_ALIAS, help='Nominates a database to flush. Defaults to the "default" database.', ) def handle(self, **options): - database = options["database"] + database = options['database'] connection = connections[database] - verbosity = options["verbosity"] - interactive = options["interactive"] + verbosity = options['verbosity'] + interactive = options['interactive'] # The following are stealth options used by Django's internals. - reset_sequences = options.get("reset_sequences", True) - allow_cascade = options.get("allow_cascade", False) - inhibit_post_migrate = options.get("inhibit_post_migrate", False) + reset_sequences = options.get('reset_sequences', True) + allow_cascade = options.get('allow_cascade', False) + inhibit_post_migrate = options.get('inhibit_post_migrate', False) self.style = no_style() @@ -44,31 +40,25 @@ class Command(BaseCommand): # dispatcher events. for app_config in apps.get_app_configs(): try: - import_module(".management", app_config.name) + import_module('.management', app_config.name) except ImportError: pass - sql_list = sql_flush( - self.style, - connection, - reset_sequences=reset_sequences, - allow_cascade=allow_cascade, - ) + sql_list = sql_flush(self.style, connection, + reset_sequences=reset_sequences, + allow_cascade=allow_cascade) if interactive: - confirm = input( - """You have requested a flush of the database. + confirm = input("""You have requested a flush of the database. This will IRREVERSIBLY DESTROY all data currently in the "%s" database, and return each table to an empty state. Are you sure you want to do this? - Type 'yes' to continue, or 'no' to cancel: """ - % connection.settings_dict["NAME"] - ) + Type 'yes' to continue, or 'no' to cancel: """ % connection.settings_dict['NAME']) else: - confirm = "yes" + confirm = 'yes' - if confirm == "yes": + if confirm == 'yes': try: connection.ops.execute_sql_flush(sql_list) except Exception as exc: @@ -78,15 +68,15 @@ Are you sure you want to do this? " * At least one of the expected database tables doesn't exist.\n" " * The SQL was invalid.\n" "Hint: Look at the output of 'django-admin sqlflush'. " - "That's the SQL this command wasn't able to run." - % (connection.settings_dict["NAME"],) + "That's the SQL this command wasn't able to run." % ( + connection.settings_dict['NAME'], + ) ) from exc - # Empty sql_list may signify an empty database and post_migrate - # would then crash. + # Empty sql_list may signify an empty database and post_migrate would then crash if sql_list and not inhibit_post_migrate: # Emit the post migrate signal. This allows individual applications to # respond as if the database had been migrated from scratch. emit_post_migrate_signal(verbosity, interactive, database) else: - self.stdout.write("Flush cancelled.") + self.stdout.write('Flush cancelled.') diff --git a/venv/Lib/site-packages/django/core/management/commands/inspectdb.py b/venv/Lib/site-packages/django/core/management/commands/inspectdb.py index 8f1a310..f9b4bfb 100644 --- a/venv/Lib/site-packages/django/core/management/commands/inspectdb.py +++ b/venv/Lib/site-packages/django/core/management/commands/inspectdb.py @@ -7,38 +7,25 @@ from django.db.models.constants import LOOKUP_SEP class Command(BaseCommand): - help = ( - "Introspects the database tables in the given database and outputs a Django " - "model module." - ) + help = "Introspects the database tables in the given database and outputs a Django model module." requires_system_checks = [] - stealth_options = ("table_name_filter",) - db_module = "django.db" + stealth_options = ('table_name_filter',) + db_module = 'django.db' def add_arguments(self, parser): parser.add_argument( - "table", - nargs="*", - type=str, - help="Selects what tables or views should be introspected.", + 'table', nargs='*', type=str, + help='Selects what tables or views should be introspected.', ) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - 'Nominates a database to introspect. Defaults to using the "default" ' - "database." - ), + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database to introspect. Defaults to using the "default" database.', ) parser.add_argument( - "--include-partitions", - action="store_true", - help="Also output models for partition tables.", + '--include-partitions', action='store_true', help='Also output models for partition tables.', ) parser.add_argument( - "--include-views", - action="store_true", - help="Also output models for database views.", + '--include-views', action='store_true', help='Also output models for database views.', ) def handle(self, **options): @@ -46,101 +33,76 @@ class Command(BaseCommand): for line in self.handle_inspection(options): self.stdout.write(line) except NotImplementedError: - raise CommandError( - "Database inspection isn't supported for the currently selected " - "database backend." - ) + raise CommandError("Database inspection isn't supported for the currently selected database backend.") def handle_inspection(self, options): - connection = connections[options["database"]] + connection = connections[options['database']] # 'table_name_filter' is a stealth option - table_name_filter = options.get("table_name_filter") + table_name_filter = options.get('table_name_filter') def table2model(table_name): - return re.sub(r"[^a-zA-Z0-9]", "", table_name.title()) + return re.sub(r'[^a-zA-Z0-9]', '', table_name.title()) with connection.cursor() as cursor: yield "# This is an auto-generated Django model module." yield "# You'll have to do the following manually to clean this up:" yield "# * Rearrange models' order" yield "# * Make sure each model has one field with primary_key=True" - yield ( - "# * Make sure each ForeignKey and OneToOneField has `on_delete` set " - "to the desired behavior" - ) + yield "# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior" yield ( "# * Remove `managed = False` lines if you wish to allow " "Django to create, modify, and delete the table" ) - yield ( - "# Feel free to rename the models, but don't rename db_table values or " - "field names." - ) - yield "from %s import models" % self.db_module + yield "# Feel free to rename the models, but don't rename db_table values or field names." + yield 'from %s import models' % self.db_module known_models = [] table_info = connection.introspection.get_table_list(cursor) # Determine types of tables and/or views to be introspected. - types = {"t"} - if options["include_partitions"]: - types.add("p") - if options["include_views"]: - types.add("v") + types = {'t'} + if options['include_partitions']: + types.add('p') + if options['include_views']: + types.add('v') - for table_name in options["table"] or sorted( - info.name for info in table_info if info.type in types - ): + for table_name in (options['table'] or sorted(info.name for info in table_info if info.type in types)): if table_name_filter is not None and callable(table_name_filter): if not table_name_filter(table_name): continue try: try: - relations = connection.introspection.get_relations( - cursor, table_name - ) + relations = connection.introspection.get_relations(cursor, table_name) except NotImplementedError: relations = {} try: - constraints = connection.introspection.get_constraints( - cursor, table_name - ) + constraints = connection.introspection.get_constraints(cursor, table_name) except NotImplementedError: constraints = {} - primary_key_column = ( - connection.introspection.get_primary_key_column( - cursor, table_name - ) - ) + primary_key_column = connection.introspection.get_primary_key_column(cursor, table_name) unique_columns = [ - c["columns"][0] - for c in constraints.values() - if c["unique"] and len(c["columns"]) == 1 + c['columns'][0] for c in constraints.values() + if c['unique'] and len(c['columns']) == 1 ] - table_description = connection.introspection.get_table_description( - cursor, table_name - ) + table_description = connection.introspection.get_table_description(cursor, table_name) except Exception as e: yield "# Unable to inspect table '%s'" % table_name yield "# The error was: %s" % e continue - yield "" - yield "" - yield "class %s(models.Model):" % table2model(table_name) + yield '' + yield '' + yield 'class %s(models.Model):' % table2model(table_name) known_models.append(table2model(table_name)) used_column_names = [] # Holds column names used in the table so far column_to_field_name = {} # Maps column names to names of model fields for row in table_description: - comment_notes = ( - [] - ) # Holds Field notes, to be displayed in a Python comment. + comment_notes = [] # Holds Field notes, to be displayed in a Python comment. extra_params = {} # Holds Field parameters such as 'db_column'. column_name = row.name is_relation = column_name in relations att_name, params, notes = self.normalize_col_name( - column_name, used_column_names, is_relation - ) + column_name, used_column_names, is_relation) extra_params.update(params) comment_notes.extend(notes) @@ -149,83 +111,66 @@ class Command(BaseCommand): # Add primary_key and unique, if necessary. if column_name == primary_key_column: - extra_params["primary_key"] = True + extra_params['primary_key'] = True elif column_name in unique_columns: - extra_params["unique"] = True + extra_params['unique'] = True if is_relation: - if extra_params.pop("unique", False) or extra_params.get( - "primary_key" - ): - rel_type = "OneToOneField" + if extra_params.pop('unique', False) or extra_params.get('primary_key'): + rel_type = 'OneToOneField' else: - rel_type = "ForeignKey" + rel_type = 'ForeignKey' rel_to = ( - "self" - if relations[column_name][1] == table_name + "self" if relations[column_name][1] == table_name else table2model(relations[column_name][1]) ) if rel_to in known_models: - field_type = "%s(%s" % (rel_type, rel_to) + field_type = '%s(%s' % (rel_type, rel_to) else: field_type = "%s('%s'" % (rel_type, rel_to) else: # Calling `get_field_type` to get the field type string and any # additional parameters and notes. - field_type, field_params, field_notes = self.get_field_type( - connection, table_name, row - ) + field_type, field_params, field_notes = self.get_field_type(connection, table_name, row) extra_params.update(field_params) comment_notes.extend(field_notes) - field_type += "(" + field_type += '(' # Don't output 'id = meta.AutoField(primary_key=True)', because # that's assumed if it doesn't exist. - if att_name == "id" and extra_params == {"primary_key": True}: - if field_type == "AutoField(": + if att_name == 'id' and extra_params == {'primary_key': True}: + if field_type == 'AutoField(': continue - elif ( - field_type - == connection.features.introspected_field_types["AutoField"] - + "(" - ): - comment_notes.append("AutoField?") + elif field_type == connection.features.introspected_field_types['AutoField'] + '(': + comment_notes.append('AutoField?') # Add 'null' and 'blank', if the 'null_ok' flag was present in the # table description. if row.null_ok: # If it's NULL... - extra_params["blank"] = True - extra_params["null"] = True + extra_params['blank'] = True + extra_params['null'] = True - field_desc = "%s = %s%s" % ( + field_desc = '%s = %s%s' % ( att_name, # Custom fields will have a dotted path - "" if "." in field_type else "models.", + '' if '.' in field_type else 'models.', field_type, ) - if field_type.startswith(("ForeignKey(", "OneToOneField(")): - field_desc += ", models.DO_NOTHING" + if field_type.startswith(('ForeignKey(', 'OneToOneField(')): + field_desc += ', models.DO_NOTHING' if extra_params: - if not field_desc.endswith("("): - field_desc += ", " - field_desc += ", ".join( - "%s=%r" % (k, v) for k, v in extra_params.items() - ) - field_desc += ")" + if not field_desc.endswith('('): + field_desc += ', ' + field_desc += ', '.join('%s=%r' % (k, v) for k, v in extra_params.items()) + field_desc += ')' if comment_notes: - field_desc += " # " + " ".join(comment_notes) - yield " %s" % field_desc - is_view = any( - info.name == table_name and info.type == "v" for info in table_info - ) - is_partition = any( - info.name == table_name and info.type == "p" for info in table_info - ) - yield from self.get_meta( - table_name, constraints, column_to_field_name, is_view, is_partition - ) + field_desc += ' # ' + ' '.join(comment_notes) + yield ' %s' % field_desc + is_view = any(info.name == table_name and info.type == 'v' for info in table_info) + is_partition = any(info.name == table_name and info.type == 'p' for info in table_info) + yield from self.get_meta(table_name, constraints, column_to_field_name, is_view, is_partition) def normalize_col_name(self, col_name, used_column_names, is_relation): """ @@ -236,54 +181,50 @@ class Command(BaseCommand): new_name = col_name.lower() if new_name != col_name: - field_notes.append("Field name made lowercase.") + field_notes.append('Field name made lowercase.') if is_relation: - if new_name.endswith("_id"): + if new_name.endswith('_id'): new_name = new_name[:-3] else: - field_params["db_column"] = col_name + field_params['db_column'] = col_name - new_name, num_repl = re.subn(r"\W", "_", new_name) + new_name, num_repl = re.subn(r'\W', '_', new_name) if num_repl > 0: - field_notes.append("Field renamed to remove unsuitable characters.") + field_notes.append('Field renamed to remove unsuitable characters.') if new_name.find(LOOKUP_SEP) >= 0: while new_name.find(LOOKUP_SEP) >= 0: - new_name = new_name.replace(LOOKUP_SEP, "_") + new_name = new_name.replace(LOOKUP_SEP, '_') if col_name.lower().find(LOOKUP_SEP) >= 0: # Only add the comment if the double underscore was in the original name - field_notes.append( - "Field renamed because it contained more than one '_' in a row." - ) + field_notes.append("Field renamed because it contained more than one '_' in a row.") - if new_name.startswith("_"): - new_name = "field%s" % new_name + if new_name.startswith('_'): + new_name = 'field%s' % new_name field_notes.append("Field renamed because it started with '_'.") - if new_name.endswith("_"): - new_name = "%sfield" % new_name + if new_name.endswith('_'): + new_name = '%sfield' % new_name field_notes.append("Field renamed because it ended with '_'.") if keyword.iskeyword(new_name): - new_name += "_field" - field_notes.append("Field renamed because it was a Python reserved word.") + new_name += '_field' + field_notes.append('Field renamed because it was a Python reserved word.') if new_name[0].isdigit(): - new_name = "number_%s" % new_name - field_notes.append( - "Field renamed because it wasn't a valid Python identifier." - ) + new_name = 'number_%s' % new_name + field_notes.append("Field renamed because it wasn't a valid Python identifier.") if new_name in used_column_names: num = 0 - while "%s_%d" % (new_name, num) in used_column_names: + while '%s_%d' % (new_name, num) in used_column_names: num += 1 - new_name = "%s_%d" % (new_name, num) - field_notes.append("Field renamed because of name conflict.") + new_name = '%s_%d' % (new_name, num) + field_notes.append('Field renamed because of name conflict.') if col_name != new_name and field_notes: - field_params["db_column"] = col_name + field_params['db_column'] = col_name return new_name, field_params, field_notes @@ -299,37 +240,30 @@ class Command(BaseCommand): try: field_type = connection.introspection.get_field_type(row.type_code, row) except KeyError: - field_type = "TextField" - field_notes.append("This field type is a guess.") + field_type = 'TextField' + field_notes.append('This field type is a guess.') # Add max_length for all CharFields. - if field_type == "CharField" and row.internal_size: - field_params["max_length"] = int(row.internal_size) + if field_type == 'CharField' and row.internal_size: + field_params['max_length'] = int(row.internal_size) - if field_type in {"CharField", "TextField"} and row.collation: - field_params["db_collation"] = row.collation + if field_type in {'CharField', 'TextField'} and row.collation: + field_params['db_collation'] = row.collation - if field_type == "DecimalField": + if field_type == 'DecimalField': if row.precision is None or row.scale is None: field_notes.append( - "max_digits and decimal_places have been guessed, as this " - "database handles decimal fields as float" - ) - field_params["max_digits"] = ( - row.precision if row.precision is not None else 10 - ) - field_params["decimal_places"] = ( - row.scale if row.scale is not None else 5 - ) + 'max_digits and decimal_places have been guessed, as this ' + 'database handles decimal fields as float') + field_params['max_digits'] = row.precision if row.precision is not None else 10 + field_params['decimal_places'] = row.scale if row.scale is not None else 5 else: - field_params["max_digits"] = row.precision - field_params["decimal_places"] = row.scale + field_params['max_digits'] = row.precision + field_params['decimal_places'] = row.scale return field_type, field_params, field_notes - def get_meta( - self, table_name, constraints, column_to_field_name, is_view, is_partition - ): + def get_meta(self, table_name, constraints, column_to_field_name, is_view, is_partition): """ Return a sequence comprising the lines of code necessary to construct the inner Meta class for the model corresponding @@ -338,30 +272,28 @@ class Command(BaseCommand): unique_together = [] has_unsupported_constraint = False for params in constraints.values(): - if params["unique"]: - columns = params["columns"] + if params['unique']: + columns = params['columns'] if None in columns: has_unsupported_constraint = True columns = [x for x in columns if x is not None] if len(columns) > 1: - unique_together.append( - str(tuple(column_to_field_name[c] for c in columns)) - ) + unique_together.append(str(tuple(column_to_field_name[c] for c in columns))) if is_view: managed_comment = " # Created from a view. Don't remove." elif is_partition: managed_comment = " # Created from a partition. Don't remove." else: - managed_comment = "" - meta = [""] + managed_comment = '' + meta = [''] if has_unsupported_constraint: - meta.append(" # A unique constraint could not be introspected.") + meta.append(' # A unique constraint could not be introspected.') meta += [ - " class Meta:", - " managed = False%s" % managed_comment, - " db_table = %r" % table_name, + ' class Meta:', + ' managed = False%s' % managed_comment, + ' db_table = %r' % table_name ] if unique_together: - tup = "(" + ", ".join(unique_together) + ",)" + tup = '(' + ', '.join(unique_together) + ',)' meta += [" unique_together = %s" % tup] return meta diff --git a/venv/Lib/site-packages/django/core/management/commands/loaddata.py b/venv/Lib/site-packages/django/core/management/commands/loaddata.py index ac97f13..49679d6 100644 --- a/venv/Lib/site-packages/django/core/management/commands/loaddata.py +++ b/venv/Lib/site-packages/django/core/management/commands/loaddata.py @@ -15,88 +15,64 @@ from django.core.management.base import BaseCommand, CommandError from django.core.management.color import no_style from django.core.management.utils import parse_apps_and_model_labels from django.db import ( - DEFAULT_DB_ALIAS, - DatabaseError, - IntegrityError, - connections, - router, + DEFAULT_DB_ALIAS, DatabaseError, IntegrityError, connections, router, transaction, ) from django.utils.functional import cached_property try: import bz2 - has_bz2 = True except ImportError: has_bz2 = False try: import lzma - has_lzma = True except ImportError: has_lzma = False -READ_STDIN = "-" +READ_STDIN = '-' class Command(BaseCommand): - help = "Installs the named fixture(s) in the database." + help = 'Installs the named fixture(s) in the database.' missing_args_message = ( "No database fixture specified. Please provide the path of at least " "one fixture in the command line." ) def add_arguments(self, parser): + parser.add_argument('args', metavar='fixture', nargs='+', help='Fixture labels.') parser.add_argument( - "args", metavar="fixture", nargs="+", help="Fixture labels." + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a specific database to load fixtures into. Defaults to the "default" database.', ) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - "Nominates a specific database to load fixtures into. Defaults to the " - '"default" database.' - ), + '--app', dest='app_label', + help='Only look for fixtures in the specified app.', ) parser.add_argument( - "--app", - dest="app_label", - help="Only look for fixtures in the specified app.", + '--ignorenonexistent', '-i', action='store_true', dest='ignore', + help='Ignores entries in the serialized data for fields that do not ' + 'currently exist on the model.', ) parser.add_argument( - "--ignorenonexistent", - "-i", - action="store_true", - dest="ignore", - help="Ignores entries in the serialized data for fields that do not " - "currently exist on the model.", + '-e', '--exclude', action='append', default=[], + help='An app_label or app_label.ModelName to exclude. Can be used multiple times.', ) parser.add_argument( - "-e", - "--exclude", - action="append", - default=[], - help=( - "An app_label or app_label.ModelName to exclude. Can be used multiple " - "times." - ), - ) - parser.add_argument( - "--format", - help="Format of serialized data when reading from stdin.", + '--format', + help='Format of serialized data when reading from stdin.', ) def handle(self, *fixture_labels, **options): - self.ignore = options["ignore"] - self.using = options["database"] - self.app_label = options["app_label"] - self.verbosity = options["verbosity"] - self.excluded_models, self.excluded_apps = parse_apps_and_model_labels( - options["exclude"] - ) - self.format = options["format"] + self.ignore = options['ignore'] + self.using = options['database'] + self.app_label = options['app_label'] + self.verbosity = options['verbosity'] + self.excluded_models, self.excluded_apps = parse_apps_and_model_labels(options['exclude']) + self.format = options['format'] with transaction.atomic(using=self.using): self.loaddata(fixture_labels) @@ -108,34 +84,6 @@ class Command(BaseCommand): if transaction.get_autocommit(self.using): connections[self.using].close() - @cached_property - def compression_formats(self): - """A dict mapping format names to (open function, mode arg) tuples.""" - # Forcing binary mode may be revisited after dropping Python 2 support - # (see #22399). - compression_formats = { - None: (open, "rb"), - "gz": (gzip.GzipFile, "rb"), - "zip": (SingleZipReader, "r"), - "stdin": (lambda *args: sys.stdin, None), - } - if has_bz2: - compression_formats["bz2"] = (bz2.BZ2File, "r") - if has_lzma: - compression_formats["lzma"] = (lzma.LZMAFile, "r") - compression_formats["xz"] = (lzma.LZMAFile, "r") - return compression_formats - - def reset_sequences(self, connection, models): - """Reset database sequences for the given connection and models.""" - sequence_sql = connection.ops.sequence_reset_sql(no_style(), models) - if sequence_sql: - if self.verbosity >= 2: - self.stdout.write("Resetting sequences") - with connection.cursor() as cursor: - for line in sequence_sql: - cursor.execute(line) - def loaddata(self, fixture_labels): connection = connections[self.using] @@ -146,6 +94,18 @@ class Command(BaseCommand): self.models = set() self.serialization_formats = serializers.get_public_serializer_formats() + # Forcing binary mode may be revisited after dropping Python 2 support (see #22399) + self.compression_formats = { + None: (open, 'rb'), + 'gz': (gzip.GzipFile, 'rb'), + 'zip': (SingleZipReader, 'r'), + 'stdin': (lambda *args: sys.stdin, None), + } + if has_bz2: + self.compression_formats['bz2'] = (bz2.BZ2File, 'r') + if has_lzma: + self.compression_formats['lzma'] = (lzma.LZMAFile, 'r') + self.compression_formats['xz'] = (lzma.LZMAFile, 'r') # Django's test suite repeatedly tries to load initial_data fixtures # from apps that don't have any fixtures. Because disabling constraint @@ -157,8 +117,8 @@ class Command(BaseCommand): else: return - self.objs_with_deferred_fields = [] with connection.constraint_checks_disabled(): + self.objs_with_deferred_fields = [] for fixture_label in fixture_labels: self.load_label(fixture_label) for obj in self.objs_with_deferred_fields: @@ -176,7 +136,13 @@ class Command(BaseCommand): # If we found even one object in a fixture, we need to reset the # database sequences. if self.loaded_object_count > 0: - self.reset_sequences(connection, self.models) + sequence_sql = connection.ops.sequence_reset_sql(no_style(), self.models) + if sequence_sql: + if self.verbosity >= 2: + self.stdout.write('Resetting sequences') + with connection.cursor() as cursor: + for line in sequence_sql: + cursor.execute(line) if self.verbosity >= 1: if self.fixture_object_count == self.loaded_object_count: @@ -187,130 +153,75 @@ class Command(BaseCommand): else: self.stdout.write( "Installed %d object(s) (of %d) from %d fixture(s)" - % ( - self.loaded_object_count, - self.fixture_object_count, - self.fixture_count, - ) + % (self.loaded_object_count, self.fixture_object_count, self.fixture_count) ) - def save_obj(self, obj): - """Save an object if permitted.""" - if ( - obj.object._meta.app_config in self.excluded_apps - or type(obj.object) in self.excluded_models - ): - return False - saved = False - if router.allow_migrate_model(self.using, obj.object.__class__): - saved = True - self.models.add(obj.object.__class__) - try: - obj.save(using=self.using) - # psycopg2 raises ValueError if data contains NUL chars. - except (DatabaseError, IntegrityError, ValueError) as e: - e.args = ( - "Could not load %(object_label)s(pk=%(pk)s): %(error_msg)s" - % { - "object_label": obj.object._meta.label, - "pk": obj.object.pk, - "error_msg": e, - }, - ) - raise - if obj.deferred_fields: - self.objs_with_deferred_fields.append(obj) - return saved - def load_label(self, fixture_label): """Load fixtures files for a given label.""" show_progress = self.verbosity >= 3 - for fixture_file, fixture_dir, fixture_name in self.find_fixtures( - fixture_label - ): + for fixture_file, fixture_dir, fixture_name in self.find_fixtures(fixture_label): _, ser_fmt, cmp_fmt = self.parse_name(os.path.basename(fixture_file)) open_method, mode = self.compression_formats[cmp_fmt] fixture = open_method(fixture_file, mode) - self.fixture_count += 1 - objects_in_fixture = 0 - loaded_objects_in_fixture = 0 - if self.verbosity >= 2: - self.stdout.write( - "Installing %s fixture '%s' from %s." - % (ser_fmt, fixture_name, humanize(fixture_dir)) - ) try: + self.fixture_count += 1 + objects_in_fixture = 0 + loaded_objects_in_fixture = 0 + if self.verbosity >= 2: + self.stdout.write( + "Installing %s fixture '%s' from %s." + % (ser_fmt, fixture_name, humanize(fixture_dir)) + ) + objects = serializers.deserialize( - ser_fmt, - fixture, - using=self.using, - ignorenonexistent=self.ignore, + ser_fmt, fixture, using=self.using, ignorenonexistent=self.ignore, handle_forward_references=True, ) for obj in objects: objects_in_fixture += 1 - if self.save_obj(obj): + if (obj.object._meta.app_config in self.excluded_apps or + type(obj.object) in self.excluded_models): + continue + if router.allow_migrate_model(self.using, obj.object.__class__): loaded_objects_in_fixture += 1 - if show_progress: - self.stdout.write( - "\rProcessed %i object(s)." % loaded_objects_in_fixture, - ending="", - ) + self.models.add(obj.object.__class__) + try: + obj.save(using=self.using) + if show_progress: + self.stdout.write( + '\rProcessed %i object(s).' % loaded_objects_in_fixture, + ending='' + ) + # psycopg2 raises ValueError if data contains NUL chars. + except (DatabaseError, IntegrityError, ValueError) as e: + e.args = ("Could not load %(object_label)s(pk=%(pk)s): %(error_msg)s" % { + 'object_label': obj.object._meta.label, + 'pk': obj.object.pk, + 'error_msg': e, + },) + raise + if obj.deferred_fields: + self.objs_with_deferred_fields.append(obj) + if objects and show_progress: + self.stdout.write() # Add a newline after progress indicator. + self.loaded_object_count += loaded_objects_in_fixture + self.fixture_object_count += objects_in_fixture except Exception as e: if not isinstance(e, CommandError): - e.args = ( - "Problem installing fixture '%s': %s" % (fixture_file, e), - ) + e.args = ("Problem installing fixture '%s': %s" % (fixture_file, e),) raise finally: fixture.close() - if objects_in_fixture and show_progress: - self.stdout.write() # Add a newline after progress indicator. - self.loaded_object_count += loaded_objects_in_fixture - self.fixture_object_count += objects_in_fixture # Warn if the fixture we loaded contains 0 objects. if objects_in_fixture == 0: warnings.warn( "No fixture data found for '%s'. (File format may be " "invalid.)" % fixture_name, - RuntimeWarning, + RuntimeWarning ) - def get_fixture_name_and_dirs(self, fixture_name): - dirname, basename = os.path.split(fixture_name) - if os.path.isabs(fixture_name): - fixture_dirs = [dirname] - else: - fixture_dirs = self.fixture_dirs - if os.path.sep in os.path.normpath(fixture_name): - fixture_dirs = [os.path.join(dir_, dirname) for dir_ in fixture_dirs] - return basename, fixture_dirs - - def get_targets(self, fixture_name, ser_fmt, cmp_fmt): - databases = [self.using, None] - cmp_fmts = self.compression_formats if cmp_fmt is None else [cmp_fmt] - ser_fmts = self.serialization_formats if ser_fmt is None else [ser_fmt] - return { - "%s.%s" - % ( - fixture_name, - ".".join([ext for ext in combo if ext]), - ) - for combo in product(databases, ser_fmts, cmp_fmts) - } - - def find_fixture_files_in_dir(self, fixture_dir, fixture_name, targets): - fixture_files_in_dir = [] - path = os.path.join(fixture_dir, fixture_name) - for candidate in glob.iglob(glob.escape(path) + "*"): - if os.path.basename(candidate) in targets: - # Save the fixture_dir and fixture_name for future error - # messages. - fixture_files_in_dir.append((candidate, fixture_dir, fixture_name)) - return fixture_files_in_dir - @functools.lru_cache(maxsize=None) def find_fixtures(self, fixture_label): """Find fixture files for a given label.""" @@ -318,32 +229,50 @@ class Command(BaseCommand): return [(READ_STDIN, None, READ_STDIN)] fixture_name, ser_fmt, cmp_fmt = self.parse_name(fixture_label) + databases = [self.using, None] + cmp_fmts = list(self.compression_formats) if cmp_fmt is None else [cmp_fmt] + ser_fmts = self.serialization_formats if ser_fmt is None else [ser_fmt] + if self.verbosity >= 2: self.stdout.write("Loading '%s' fixtures..." % fixture_name) - fixture_name, fixture_dirs = self.get_fixture_name_and_dirs(fixture_name) - targets = self.get_targets(fixture_name, ser_fmt, cmp_fmt) + if os.path.isabs(fixture_name): + fixture_dirs = [os.path.dirname(fixture_name)] + fixture_name = os.path.basename(fixture_name) + else: + fixture_dirs = self.fixture_dirs + if os.path.sep in os.path.normpath(fixture_name): + fixture_dirs = [os.path.join(dir_, os.path.dirname(fixture_name)) + for dir_ in fixture_dirs] + fixture_name = os.path.basename(fixture_name) + + suffixes = ( + '.'.join(ext for ext in combo if ext) + for combo in product(databases, ser_fmts, cmp_fmts) + ) + targets = {'.'.join((fixture_name, suffix)) for suffix in suffixes} + fixture_files = [] for fixture_dir in fixture_dirs: if self.verbosity >= 2: self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir)) - fixture_files_in_dir = self.find_fixture_files_in_dir( - fixture_dir, - fixture_name, - targets, - ) + fixture_files_in_dir = [] + path = os.path.join(fixture_dir, fixture_name) + for candidate in glob.iglob(glob.escape(path) + '*'): + if os.path.basename(candidate) in targets: + # Save the fixture_dir and fixture_name for future error messages. + fixture_files_in_dir.append((candidate, fixture_dir, fixture_name)) + if self.verbosity >= 2 and not fixture_files_in_dir: - self.stdout.write( - "No fixture '%s' in %s." % (fixture_name, humanize(fixture_dir)) - ) + self.stdout.write("No fixture '%s' in %s." % + (fixture_name, humanize(fixture_dir))) # Check kept for backwards-compatibility; it isn't clear why # duplicates are only allowed in different directories. if len(fixture_files_in_dir) > 1: raise CommandError( - "Multiple fixtures named '%s' in %s. Aborting." - % (fixture_name, humanize(fixture_dir)) - ) + "Multiple fixtures named '%s' in %s. Aborting." % + (fixture_name, humanize(fixture_dir))) fixture_files.extend(fixture_files_in_dir) if not fixture_files: @@ -366,12 +295,11 @@ class Command(BaseCommand): raise ImproperlyConfigured("settings.FIXTURE_DIRS contains duplicates.") for app_config in apps.get_app_configs(): app_label = app_config.label - app_dir = os.path.join(app_config.path, "fixtures") + app_dir = os.path.join(app_config.path, 'fixtures') if app_dir in fixture_dirs: raise ImproperlyConfigured( "'%s' is a default fixture directory for the '%s' app " - "and cannot be listed in settings.FIXTURE_DIRS." - % (app_dir, app_label) + "and cannot be listed in settings.FIXTURE_DIRS." % (app_dir, app_label) ) if self.app_label and app_label != self.app_label: @@ -379,7 +307,7 @@ class Command(BaseCommand): if os.path.isdir(app_dir): dirs.append(app_dir) dirs.extend(fixture_dirs) - dirs.append("") + dirs.append('') return [os.path.realpath(d) for d in dirs] def parse_name(self, fixture_name): @@ -388,12 +316,10 @@ class Command(BaseCommand): """ if fixture_name == READ_STDIN: if not self.format: - raise CommandError( - "--format must be specified when reading from stdin." - ) - return READ_STDIN, self.format, "stdin" + raise CommandError('--format must be specified when reading from stdin.') + return READ_STDIN, self.format, 'stdin' - parts = fixture_name.rsplit(".", 2) + parts = fixture_name.rsplit('.', 2) if len(parts) > 1 and parts[-1] in self.compression_formats: cmp_fmt = parts[-1] @@ -408,17 +334,17 @@ class Command(BaseCommand): else: raise CommandError( "Problem installing fixture '%s': %s is not a known " - "serialization format." % (".".join(parts[:-1]), parts[-1]) - ) + "serialization format." % ('.'.join(parts[:-1]), parts[-1])) else: ser_fmt = None - name = ".".join(parts) + name = '.'.join(parts) return name, ser_fmt, cmp_fmt class SingleZipReader(zipfile.ZipFile): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if len(self.namelist()) != 1: @@ -429,4 +355,4 @@ class SingleZipReader(zipfile.ZipFile): def humanize(dirname): - return "'%s'" % dirname if dirname else "absolute path" + return "'%s'" % dirname if dirname else 'absolute path' diff --git a/venv/Lib/site-packages/django/core/management/commands/makemessages.py b/venv/Lib/site-packages/django/core/management/commands/makemessages.py index 77c84cb..7aa184b 100644 --- a/venv/Lib/site-packages/django/core/management/commands/makemessages.py +++ b/venv/Lib/site-packages/django/core/management/commands/makemessages.py @@ -11,10 +11,7 @@ from django.core.exceptions import ImproperlyConfigured from django.core.files.temp import NamedTemporaryFile from django.core.management.base import BaseCommand, CommandError from django.core.management.utils import ( - find_command, - handle_extensions, - is_ignored_path, - popen_wrapper, + find_command, handle_extensions, is_ignored_path, popen_wrapper, ) from django.utils.encoding import DEFAULT_LOCALE_ENCODING from django.utils.functional import cached_property @@ -23,9 +20,7 @@ from django.utils.regex_helper import _lazy_re_compile from django.utils.text import get_text_list from django.utils.translation import templatize -plural_forms_re = _lazy_re_compile( - r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL -) +plural_forms_re = _lazy_re_compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL) STATUS_OK = 0 NO_LOCALE_DIR = object() @@ -67,7 +62,6 @@ class BuildFile: """ Represent the state of a translatable file during the build process. """ - def __init__(self, command, domain, translatable): self.command = command self.domain = domain @@ -75,11 +69,11 @@ class BuildFile: @cached_property def is_templatized(self): - if self.domain == "djangojs": + if self.domain == 'djangojs': return self.command.gettext_version < (0, 18, 3) - elif self.domain == "django": + elif self.domain == 'django': file_ext = os.path.splitext(self.translatable.file)[1] - return file_ext != ".py" + return file_ext != '.py' return False @cached_property @@ -95,10 +89,10 @@ class BuildFile: if not self.is_templatized: return self.path extension = { - "djangojs": "c", - "django": "py", + 'djangojs': 'c', + 'django': 'py', }.get(self.domain) - filename = "%s.%s" % (self.translatable.file, extension) + filename = '%s.%s' % (self.translatable.file, extension) return os.path.join(self.translatable.dirpath, filename) def preprocess(self): @@ -109,15 +103,15 @@ class BuildFile: if not self.is_templatized: return - with open(self.path, encoding="utf-8") as fp: + with open(self.path, encoding='utf-8') as fp: src_data = fp.read() - if self.domain == "djangojs": + if self.domain == 'djangojs': content = prepare_js_for_gettext(src_data) - elif self.domain == "django": + elif self.domain == 'django': content = templatize(src_data, origin=self.path[2:]) - with open(self.work_path, "w", encoding="utf-8") as fp: + with open(self.work_path, 'w', encoding='utf-8') as fp: fp.write(content) def postprocess_messages(self, msgs): @@ -131,7 +125,7 @@ class BuildFile: return msgs # Remove '.py' suffix - if os.name == "nt": + if os.name == 'nt': # Preserve '.\' prefix on Windows to respect gettext behavior old_path = self.work_path new_path = self.path @@ -140,10 +134,10 @@ class BuildFile: new_path = self.path[2:] return re.sub( - r"^(#: .*)(" + re.escape(old_path) + r")", + r'^(#: .*)(' + re.escape(old_path) + r')', lambda match: match[0].replace(old_path, new_path), msgs, - flags=re.MULTILINE, + flags=re.MULTILINE ) def cleanup(self): @@ -169,8 +163,8 @@ def normalize_eols(raw_contents): lines_list = raw_contents.splitlines() # Ensure last line has its EOL if lines_list and lines_list[-1]: - lines_list.append("") - return "\n".join(lines_list) + lines_list.append('') + return '\n'.join(lines_list) def write_pot_file(potfile, msgs): @@ -187,26 +181,26 @@ def write_pot_file(potfile, msgs): found, header_read = False, False for line in pot_lines: if not found and not header_read: - if "charset=CHARSET" in line: + if 'charset=CHARSET' in line: found = True - line = line.replace("charset=CHARSET", "charset=UTF-8") + line = line.replace('charset=CHARSET', 'charset=UTF-8') if not line and not found: header_read = True lines.append(line) - msgs = "\n".join(lines) + msgs = '\n'.join(lines) # Force newlines of POT files to '\n' to work around # https://savannah.gnu.org/bugs/index.php?52395 - with open(potfile, "a", encoding="utf-8", newline="\n") as fp: + with open(potfile, 'a', encoding='utf-8', newline='\n') as fp: fp.write(msgs) class Command(BaseCommand): help = ( - "Runs over the entire source tree of the current directory and pulls out all " - "strings marked for translation. It creates (or updates) a message file in the " - "conf/locale (in the django tree) or locale (for projects and applications) " - "directory.\n\nYou must run this command with one of either the --locale, " - "--exclude, or --all options." + "Runs over the entire source tree of the current directory and " + "pulls out all strings marked for translation. It creates (or updates) a message " + "file in the conf/locale (in the django tree) or locale (for projects and " + "applications) directory.\n\nYou must run this command with one of either the " + "--locale, --exclude, or --all options." ) translatable_file_class = TranslatableFile @@ -214,91 +208,61 @@ class Command(BaseCommand): requires_system_checks = [] - msgmerge_options = ["-q", "--previous"] - msguniq_options = ["--to-code=utf-8"] - msgattrib_options = ["--no-obsolete"] - xgettext_options = ["--from-code=UTF-8", "--add-comments=Translators"] + msgmerge_options = ['-q', '--previous'] + msguniq_options = ['--to-code=utf-8'] + msgattrib_options = ['--no-obsolete'] + xgettext_options = ['--from-code=UTF-8', '--add-comments=Translators'] def add_arguments(self, parser): parser.add_argument( - "--locale", - "-l", - default=[], - action="append", - help=( - "Creates or updates the message files for the given locale(s) (e.g. " - "pt_BR). Can be used multiple times." - ), + '--locale', '-l', default=[], action='append', + help='Creates or updates the message files for the given locale(s) (e.g. pt_BR). ' + 'Can be used multiple times.', ) parser.add_argument( - "--exclude", - "-x", - default=[], - action="append", - help="Locales to exclude. Default is none. Can be used multiple times.", + '--exclude', '-x', default=[], action='append', + help='Locales to exclude. Default is none. Can be used multiple times.', ) parser.add_argument( - "--domain", - "-d", - default="django", + '--domain', '-d', default='django', help='The domain of the message files (default: "django").', ) parser.add_argument( - "--all", - "-a", - action="store_true", - help="Updates the message files for all existing locales.", + '--all', '-a', action='store_true', + help='Updates the message files for all existing locales.', ) parser.add_argument( - "--extension", - "-e", - dest="extensions", - action="append", + '--extension', '-e', dest='extensions', action='append', help='The file extension(s) to examine (default: "html,txt,py", or "js" ' - 'if the domain is "djangojs"). Separate multiple extensions with ' - "commas, or use -e multiple times.", + 'if the domain is "djangojs"). Separate multiple extensions with ' + 'commas, or use -e multiple times.', ) parser.add_argument( - "--symlinks", - "-s", - action="store_true", - help="Follows symlinks to directories when examining source code " - "and templates for translation strings.", + '--symlinks', '-s', action='store_true', + help='Follows symlinks to directories when examining source code ' + 'and templates for translation strings.', ) parser.add_argument( - "--ignore", - "-i", - action="append", - dest="ignore_patterns", - default=[], - metavar="PATTERN", - help="Ignore files or directories matching this glob-style pattern. " - "Use multiple times to ignore more.", + '--ignore', '-i', action='append', dest='ignore_patterns', + default=[], metavar='PATTERN', + help='Ignore files or directories matching this glob-style pattern. ' + 'Use multiple times to ignore more.', ) parser.add_argument( - "--no-default-ignore", - action="store_false", - dest="use_default_ignore_patterns", - help=( - "Don't ignore the common glob-style patterns 'CVS', '.*', '*~' and " - "'*.pyc'." - ), + '--no-default-ignore', action='store_false', dest='use_default_ignore_patterns', + help="Don't ignore the common glob-style patterns 'CVS', '.*', '*~' and '*.pyc'.", ) parser.add_argument( - "--no-wrap", - action="store_true", + '--no-wrap', action='store_true', help="Don't break long message lines into several lines.", ) parser.add_argument( - "--no-location", - action="store_true", + '--no-location', action='store_true', help="Don't write '#: filename:line' lines.", ) parser.add_argument( - "--add-location", - choices=("full", "file", "never"), - const="full", - nargs="?", + '--add-location', + choices=('full', 'file', 'never'), const='full', nargs='?', help=( "Controls '#: filename:line' lines. If the option is 'full' " "(the default if not given), the lines include both file name " @@ -308,65 +272,61 @@ class Command(BaseCommand): ), ) parser.add_argument( - "--no-obsolete", - action="store_true", + '--no-obsolete', action='store_true', help="Remove obsolete message strings.", ) parser.add_argument( - "--keep-pot", - action="store_true", + '--keep-pot', action='store_true', help="Keep .pot file after making messages. Useful when debugging.", ) def handle(self, *args, **options): - locale = options["locale"] - exclude = options["exclude"] - self.domain = options["domain"] - self.verbosity = options["verbosity"] - process_all = options["all"] - extensions = options["extensions"] - self.symlinks = options["symlinks"] + locale = options['locale'] + exclude = options['exclude'] + self.domain = options['domain'] + self.verbosity = options['verbosity'] + process_all = options['all'] + extensions = options['extensions'] + self.symlinks = options['symlinks'] - ignore_patterns = options["ignore_patterns"] - if options["use_default_ignore_patterns"]: - ignore_patterns += ["CVS", ".*", "*~", "*.pyc"] + ignore_patterns = options['ignore_patterns'] + if options['use_default_ignore_patterns']: + ignore_patterns += ['CVS', '.*', '*~', '*.pyc'] self.ignore_patterns = list(set(ignore_patterns)) # Avoid messing with mutable class variables - if options["no_wrap"]: - self.msgmerge_options = self.msgmerge_options[:] + ["--no-wrap"] - self.msguniq_options = self.msguniq_options[:] + ["--no-wrap"] - self.msgattrib_options = self.msgattrib_options[:] + ["--no-wrap"] - self.xgettext_options = self.xgettext_options[:] + ["--no-wrap"] - if options["no_location"]: - self.msgmerge_options = self.msgmerge_options[:] + ["--no-location"] - self.msguniq_options = self.msguniq_options[:] + ["--no-location"] - self.msgattrib_options = self.msgattrib_options[:] + ["--no-location"] - self.xgettext_options = self.xgettext_options[:] + ["--no-location"] - if options["add_location"]: + if options['no_wrap']: + self.msgmerge_options = self.msgmerge_options[:] + ['--no-wrap'] + self.msguniq_options = self.msguniq_options[:] + ['--no-wrap'] + self.msgattrib_options = self.msgattrib_options[:] + ['--no-wrap'] + self.xgettext_options = self.xgettext_options[:] + ['--no-wrap'] + if options['no_location']: + self.msgmerge_options = self.msgmerge_options[:] + ['--no-location'] + self.msguniq_options = self.msguniq_options[:] + ['--no-location'] + self.msgattrib_options = self.msgattrib_options[:] + ['--no-location'] + self.xgettext_options = self.xgettext_options[:] + ['--no-location'] + if options['add_location']: if self.gettext_version < (0, 19): raise CommandError( "The --add-location option requires gettext 0.19 or later. " - "You have %s." % ".".join(str(x) for x in self.gettext_version) + "You have %s." % '.'.join(str(x) for x in self.gettext_version) ) - arg_add_location = "--add-location=%s" % options["add_location"] + arg_add_location = "--add-location=%s" % options['add_location'] self.msgmerge_options = self.msgmerge_options[:] + [arg_add_location] self.msguniq_options = self.msguniq_options[:] + [arg_add_location] self.msgattrib_options = self.msgattrib_options[:] + [arg_add_location] self.xgettext_options = self.xgettext_options[:] + [arg_add_location] - self.no_obsolete = options["no_obsolete"] - self.keep_pot = options["keep_pot"] + self.no_obsolete = options['no_obsolete'] + self.keep_pot = options['keep_pot'] - if self.domain not in ("django", "djangojs"): - raise CommandError( - "currently makemessages only supports domains " - "'django' and 'djangojs'" - ) - if self.domain == "djangojs": - exts = extensions or ["js"] + if self.domain not in ('django', 'djangojs'): + raise CommandError("currently makemessages only supports domains " + "'django' and 'djangojs'") + if self.domain == 'djangojs': + exts = extensions or ['js'] else: - exts = extensions or ["html", "txt", "py"] + exts = extensions or ['html', 'txt', 'py'] self.extensions = handle_extensions(exts) if (not locale and not exclude and not process_all) or self.domain is None: @@ -377,35 +337,32 @@ class Command(BaseCommand): if self.verbosity > 1: self.stdout.write( - "examining files with the extensions: %s" - % get_text_list(list(self.extensions), "and") + 'examining files with the extensions: %s' + % get_text_list(list(self.extensions), 'and') ) self.invoked_for_django = False self.locale_paths = [] self.default_locale_path = None - if os.path.isdir(os.path.join("conf", "locale")): - self.locale_paths = [os.path.abspath(os.path.join("conf", "locale"))] + if os.path.isdir(os.path.join('conf', 'locale')): + self.locale_paths = [os.path.abspath(os.path.join('conf', 'locale'))] self.default_locale_path = self.locale_paths[0] self.invoked_for_django = True else: if self.settings_available: self.locale_paths.extend(settings.LOCALE_PATHS) # Allow to run makemessages inside an app dir - if os.path.isdir("locale"): - self.locale_paths.append(os.path.abspath("locale")) + if os.path.isdir('locale'): + self.locale_paths.append(os.path.abspath('locale')) if self.locale_paths: self.default_locale_path = self.locale_paths[0] os.makedirs(self.default_locale_path, exist_ok=True) # Build locale list - looks_like_locale = re.compile(r"[a-z]{2}") - locale_dirs = filter( - os.path.isdir, glob.glob("%s/*" % self.default_locale_path) - ) + looks_like_locale = re.compile(r'[a-z]{2}') + locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path)) all_locales = [ - lang_code - for lang_code in map(os.path.basename, locale_dirs) + lang_code for lang_code in map(os.path.basename, locale_dirs) if looks_like_locale.match(lang_code) ] @@ -417,26 +374,25 @@ class Command(BaseCommand): locales = set(locales).difference(exclude) if locales: - check_programs("msguniq", "msgmerge", "msgattrib") + check_programs('msguniq', 'msgmerge', 'msgattrib') - check_programs("xgettext") + check_programs('xgettext') try: potfiles = self.build_potfiles() # Build po files for each selected locale for locale in locales: - if "-" in locale: + if '-' in locale: self.stdout.write( - "invalid locale %s, did you mean %s?" - % ( + 'invalid locale %s, did you mean %s?' % ( locale, - locale.replace("-", "_"), + locale.replace('-', '_'), ), ) continue if self.verbosity > 0: - self.stdout.write("processing locale %s" % locale) + self.stdout.write('processing locale %s' % locale) for potfile in potfiles: self.write_po_file(potfile, locale) finally: @@ -448,10 +404,10 @@ class Command(BaseCommand): # Gettext tools will output system-encoded bytestrings instead of UTF-8, # when looking up the version. It's especially a problem on Windows. out, err, status = popen_wrapper( - ["xgettext", "--version"], + ['xgettext', '--version'], stdout_encoding=DEFAULT_LOCALE_ENCODING, ) - m = re.search(r"(\d+)\.(\d+)\.?(\d+)?", out) + m = re.search(r'(\d+)\.(\d+)\.?(\d+)?', out) if m: return tuple(int(d) for d in m.groups() if d is not None) else: @@ -476,27 +432,26 @@ class Command(BaseCommand): self.process_files(file_list) potfiles = [] for path in self.locale_paths: - potfile = os.path.join(path, "%s.pot" % self.domain) + potfile = os.path.join(path, '%s.pot' % self.domain) if not os.path.exists(potfile): continue - args = ["msguniq"] + self.msguniq_options + [potfile] + args = ['msguniq'] + self.msguniq_options + [potfile] msgs, errors, status = popen_wrapper(args) if errors: if status != STATUS_OK: raise CommandError( - "errors happened while running msguniq\n%s" % errors - ) + "errors happened while running msguniq\n%s" % errors) elif self.verbosity > 0: self.stdout.write(errors) msgs = normalize_eols(msgs) - with open(potfile, "w", encoding="utf-8") as fp: + with open(potfile, 'w', encoding='utf-8') as fp: fp.write(msgs) potfiles.append(potfile) return potfiles def remove_potfiles(self): for path in self.locale_paths: - pot_path = os.path.join(path, "%s.pot" % self.domain) + pot_path = os.path.join(path, '%s.pot' % self.domain) if os.path.exists(pot_path): os.unlink(pot_path) @@ -508,40 +463,23 @@ class Command(BaseCommand): all_files = [] ignored_roots = [] if self.settings_available: - ignored_roots = [ - os.path.normpath(p) - for p in (settings.MEDIA_ROOT, settings.STATIC_ROOT) - if p - ] - for dirpath, dirnames, filenames in os.walk( - root, topdown=True, followlinks=self.symlinks - ): + ignored_roots = [os.path.normpath(p) for p in (settings.MEDIA_ROOT, settings.STATIC_ROOT) if p] + for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=self.symlinks): for dirname in dirnames[:]: - if ( - is_ignored_path( - os.path.normpath(os.path.join(dirpath, dirname)), - self.ignore_patterns, - ) - or os.path.join(os.path.abspath(dirpath), dirname) in ignored_roots - ): + if (is_ignored_path(os.path.normpath(os.path.join(dirpath, dirname)), self.ignore_patterns) or + os.path.join(os.path.abspath(dirpath), dirname) in ignored_roots): dirnames.remove(dirname) if self.verbosity > 1: - self.stdout.write("ignoring directory %s" % dirname) - elif dirname == "locale": + self.stdout.write('ignoring directory %s' % dirname) + elif dirname == 'locale': dirnames.remove(dirname) - self.locale_paths.insert( - 0, os.path.join(os.path.abspath(dirpath), dirname) - ) + self.locale_paths.insert(0, os.path.join(os.path.abspath(dirpath), dirname)) for filename in filenames: file_path = os.path.normpath(os.path.join(dirpath, filename)) file_ext = os.path.splitext(filename)[1] - if file_ext not in self.extensions or is_ignored_path( - file_path, self.ignore_patterns - ): + if file_ext not in self.extensions or is_ignored_path(file_path, self.ignore_patterns): if self.verbosity > 1: - self.stdout.write( - "ignoring file %s in %s" % (filename, dirpath) - ) + self.stdout.write('ignoring file %s in %s' % (filename, dirpath)) else: locale_dir = None for path in self.locale_paths: @@ -549,9 +487,7 @@ class Command(BaseCommand): locale_dir = path break locale_dir = locale_dir or self.default_locale_path or NO_LOCALE_DIR - all_files.append( - self.translatable_file_class(dirpath, filename, locale_dir) - ) + all_files.append(self.translatable_file_class(dirpath, filename, locale_dir)) return sorted(all_files) def process_files(self, file_list): @@ -576,69 +512,61 @@ class Command(BaseCommand): build_files = [] for translatable in files: if self.verbosity > 1: - self.stdout.write( - "processing file %s in %s" - % (translatable.file, translatable.dirpath) - ) - if self.domain not in ("djangojs", "django"): + self.stdout.write('processing file %s in %s' % ( + translatable.file, translatable.dirpath + )) + if self.domain not in ('djangojs', 'django'): continue build_file = self.build_file_class(self, self.domain, translatable) try: build_file.preprocess() except UnicodeDecodeError as e: self.stdout.write( - "UnicodeDecodeError: skipped file %s in %s (reason: %s)" - % ( - translatable.file, - translatable.dirpath, - e, + 'UnicodeDecodeError: skipped file %s in %s (reason: %s)' % ( + translatable.file, translatable.dirpath, e, ) ) continue - except BaseException: - # Cleanup before exit. - for build_file in build_files: - build_file.cleanup() - raise build_files.append(build_file) - if self.domain == "djangojs": + if self.domain == 'djangojs': is_templatized = build_file.is_templatized args = [ - "xgettext", - "-d", - self.domain, - "--language=%s" % ("C" if is_templatized else "JavaScript",), - "--keyword=gettext_noop", - "--keyword=gettext_lazy", - "--keyword=ngettext_lazy:1,2", - "--keyword=pgettext:1c,2", - "--keyword=npgettext:1c,2,3", - "--output=-", + 'xgettext', + '-d', self.domain, + '--language=%s' % ('C' if is_templatized else 'JavaScript',), + '--keyword=gettext_noop', + '--keyword=gettext_lazy', + '--keyword=ngettext_lazy:1,2', + '--keyword=pgettext:1c,2', + '--keyword=npgettext:1c,2,3', + '--output=-', ] - elif self.domain == "django": + elif self.domain == 'django': args = [ - "xgettext", - "-d", - self.domain, - "--language=Python", - "--keyword=gettext_noop", - "--keyword=gettext_lazy", - "--keyword=ngettext_lazy:1,2", - "--keyword=pgettext:1c,2", - "--keyword=npgettext:1c,2,3", - "--keyword=pgettext_lazy:1c,2", - "--keyword=npgettext_lazy:1c,2,3", - "--output=-", + 'xgettext', + '-d', self.domain, + '--language=Python', + '--keyword=gettext_noop', + '--keyword=gettext_lazy', + '--keyword=ngettext_lazy:1,2', + '--keyword=ugettext_noop', + '--keyword=ugettext_lazy', + '--keyword=ungettext_lazy:1,2', + '--keyword=pgettext:1c,2', + '--keyword=npgettext:1c,2,3', + '--keyword=pgettext_lazy:1c,2', + '--keyword=npgettext_lazy:1c,2,3', + '--output=-', ] else: return input_files = [bf.work_path for bf in build_files] - with NamedTemporaryFile(mode="w+") as input_files_list: - input_files_list.write("\n".join(input_files)) + with NamedTemporaryFile(mode='w+') as input_files_list: + input_files_list.write('\n'.join(input_files)) input_files_list.flush() - args.extend(["--files-from", input_files_list.name]) + args.extend(['--files-from', input_files_list.name]) args.extend(self.xgettext_options) msgs, errors, status = popen_wrapper(args) @@ -647,8 +575,8 @@ class Command(BaseCommand): for build_file in build_files: build_file.cleanup() raise CommandError( - "errors happened while running xgettext on %s\n%s" - % ("\n".join(input_files), errors) + 'errors happened while running xgettext on %s\n%s' % + ('\n'.join(input_files), errors) ) elif self.verbosity > 0: # Print warnings @@ -656,17 +584,14 @@ class Command(BaseCommand): if msgs: if locale_dir is NO_LOCALE_DIR: - for build_file in build_files: - build_file.cleanup() file_path = os.path.normpath(build_files[0].path) raise CommandError( - "Unable to find a locale path to store translations for " - "file %s. Make sure the 'locale' directory exists in an " - "app or LOCALE_PATHS setting is set." % file_path + 'Unable to find a locale path to store translations for ' + 'file %s' % file_path ) for build_file in build_files: msgs = build_file.postprocess_messages(msgs) - potfile = os.path.join(locale_dir, "%s.pot" % self.domain) + potfile = os.path.join(locale_dir, '%s.pot' % self.domain) write_pot_file(potfile, msgs) for build_file in build_files: @@ -679,40 +604,37 @@ class Command(BaseCommand): Use msgmerge and msgattrib GNU gettext utilities. """ - basedir = os.path.join(os.path.dirname(potfile), locale, "LC_MESSAGES") + basedir = os.path.join(os.path.dirname(potfile), locale, 'LC_MESSAGES') os.makedirs(basedir, exist_ok=True) - pofile = os.path.join(basedir, "%s.po" % self.domain) + pofile = os.path.join(basedir, '%s.po' % self.domain) if os.path.exists(pofile): - args = ["msgmerge"] + self.msgmerge_options + [pofile, potfile] + args = ['msgmerge'] + self.msgmerge_options + [pofile, potfile] msgs, errors, status = popen_wrapper(args) if errors: if status != STATUS_OK: raise CommandError( - "errors happened while running msgmerge\n%s" % errors - ) + "errors happened while running msgmerge\n%s" % errors) elif self.verbosity > 0: self.stdout.write(errors) else: - with open(potfile, encoding="utf-8") as fp: + with open(potfile, encoding='utf-8') as fp: msgs = fp.read() if not self.invoked_for_django: msgs = self.copy_plural_forms(msgs, locale) msgs = normalize_eols(msgs) msgs = msgs.replace( - "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "" - ) - with open(pofile, "w", encoding="utf-8") as fp: + "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "") + with open(pofile, 'w', encoding='utf-8') as fp: fp.write(msgs) if self.no_obsolete: - args = ["msgattrib"] + self.msgattrib_options + ["-o", pofile, pofile] + args = ['msgattrib'] + self.msgattrib_options + ['-o', pofile, pofile] msgs, errors, status = popen_wrapper(args) if errors: if status != STATUS_OK: raise CommandError( - "errors happened while running msgattrib\n%s" % errors - ) + "errors happened while running msgattrib\n%s" % errors) elif self.verbosity > 0: self.stdout.write(errors) @@ -723,21 +645,19 @@ class Command(BaseCommand): contents of a newly created .po file. """ django_dir = os.path.normpath(os.path.join(os.path.dirname(django.__file__))) - if self.domain == "djangojs": - domains = ("djangojs", "django") + if self.domain == 'djangojs': + domains = ('djangojs', 'django') else: - domains = ("django",) + domains = ('django',) for domain in domains: - django_po = os.path.join( - django_dir, "conf", "locale", locale, "LC_MESSAGES", "%s.po" % domain - ) + django_po = os.path.join(django_dir, 'conf', 'locale', locale, 'LC_MESSAGES', '%s.po' % domain) if os.path.exists(django_po): - with open(django_po, encoding="utf-8") as fp: + with open(django_po, encoding='utf-8') as fp: m = plural_forms_re.search(fp.read()) if m: - plural_form_line = m["value"] + plural_form_line = m['value'] if self.verbosity > 1: - self.stdout.write("copying plural forms: %s" % plural_form_line) + self.stdout.write('copying plural forms: %s' % plural_form_line) lines = [] found = False for line in msgs.splitlines(): @@ -745,6 +665,6 @@ class Command(BaseCommand): line = plural_form_line found = True lines.append(line) - msgs = "\n".join(lines) + msgs = '\n'.join(lines) break return msgs diff --git a/venv/Lib/site-packages/django/core/management/commands/makemigrations.py b/venv/Lib/site-packages/django/core/management/commands/makemigrations.py index 12352e5..5c0a782 100644 --- a/venv/Lib/site-packages/django/core/management/commands/makemigrations.py +++ b/venv/Lib/site-packages/django/core/management/commands/makemigrations.py @@ -5,14 +5,15 @@ from itertools import takewhile from django.apps import apps from django.conf import settings -from django.core.management.base import BaseCommand, CommandError, no_translations +from django.core.management.base import ( + BaseCommand, CommandError, no_translations, +) from django.db import DEFAULT_DB_ALIAS, OperationalError, connections, router from django.db.migrations import Migration from django.db.migrations.autodetector import MigrationAutodetector from django.db.migrations.loader import MigrationLoader from django.db.migrations.questioner import ( - InteractiveMigrationQuestioner, - MigrationQuestioner, + InteractiveMigrationQuestioner, MigrationQuestioner, NonInteractiveMigrationQuestioner, ) from django.db.migrations.state import ProjectState @@ -25,63 +26,50 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "args", - metavar="app_label", - nargs="*", - help="Specify the app label(s) to create migrations for.", + 'args', metavar='app_label', nargs='*', + help='Specify the app label(s) to create migrations for.', ) parser.add_argument( - "--dry-run", - action="store_true", + '--dry-run', action='store_true', help="Just show what migrations would be made; don't actually write them.", ) parser.add_argument( - "--merge", - action="store_true", + '--merge', action='store_true', help="Enable fixing of migration conflicts.", ) parser.add_argument( - "--empty", - action="store_true", + '--empty', action='store_true', help="Create an empty migration.", ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "-n", - "--name", + '-n', '--name', help="Use this name for migration file(s).", ) parser.add_argument( - "--no-header", - action="store_false", - dest="include_header", - help="Do not add header comments to new migration file(s).", + '--no-header', action='store_false', dest='include_header', + help='Do not add header comments to new migration file(s).', ) parser.add_argument( - "--check", - action="store_true", - dest="check_changes", - help="Exit with a non-zero status if model changes are missing migrations.", + '--check', action='store_true', dest='check_changes', + help='Exit with a non-zero status if model changes are missing migrations.', ) @no_translations def handle(self, *app_labels, **options): - self.verbosity = options["verbosity"] - self.interactive = options["interactive"] - self.dry_run = options["dry_run"] - self.merge = options["merge"] - self.empty = options["empty"] - self.migration_name = options["name"] + self.verbosity = options['verbosity'] + self.interactive = options['interactive'] + self.dry_run = options['dry_run'] + self.merge = options['merge'] + self.empty = options['empty'] + self.migration_name = options['name'] if self.migration_name and not self.migration_name.isidentifier(): - raise CommandError("The migration name must be a valid Python identifier.") - self.include_header = options["include_header"] - check_changes = options["check_changes"] + raise CommandError('The migration name must be a valid Python identifier.') + self.include_header = options['include_header'] + check_changes = options['check_changes'] # Make sure the app they asked for exists app_labels = set(app_labels) @@ -102,43 +90,39 @@ class Command(BaseCommand): # Raise an error if any migrations are applied before their dependencies. consistency_check_labels = {config.label for config in apps.get_app_configs()} # Non-default databases are only checked if database routers used. - aliases_to_check = ( - connections if settings.DATABASE_ROUTERS else [DEFAULT_DB_ALIAS] - ) + aliases_to_check = connections if settings.DATABASE_ROUTERS else [DEFAULT_DB_ALIAS] for alias in sorted(aliases_to_check): connection = connections[alias] - if connection.settings_dict["ENGINE"] != "django.db.backends.dummy" and any( - # At least one model must be migrated to the database. - router.allow_migrate( - connection.alias, app_label, model_name=model._meta.object_name - ) - for app_label in consistency_check_labels - for model in apps.get_app_config(app_label).get_models() - ): + if (connection.settings_dict['ENGINE'] != 'django.db.backends.dummy' and any( + # At least one model must be migrated to the database. + router.allow_migrate(connection.alias, app_label, model_name=model._meta.object_name) + for app_label in consistency_check_labels + for model in apps.get_app_config(app_label).get_models() + )): try: loader.check_consistent_history(connection) except OperationalError as error: warnings.warn( "Got an error checking a consistent migration history " - "performed for database connection '%s': %s" % (alias, error), + "performed for database connection '%s': %s" + % (alias, error), RuntimeWarning, ) # Before anything else, see if there's conflicting apps and drop out # hard if there are any and they don't want to merge conflicts = loader.detect_conflicts() - # If app_labels is specified, filter out conflicting migrations for - # unspecified apps. + # If app_labels is specified, filter out conflicting migrations for unspecified apps if app_labels: conflicts = { - app_label: conflict - for app_label, conflict in conflicts.items() + app_label: conflict for app_label, conflict in conflicts.items() if app_label in app_labels } if conflicts and not self.merge: name_str = "; ".join( - "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() + "%s in %s" % (", ".join(names), app) + for app, names in conflicts.items() ) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " @@ -157,13 +141,9 @@ class Command(BaseCommand): return self.handle_merge(loader, conflicts) if self.interactive: - questioner = InteractiveMigrationQuestioner( - specified_apps=app_labels, dry_run=self.dry_run - ) + questioner = InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run) else: - questioner = NonInteractiveMigrationQuestioner( - specified_apps=app_labels, dry_run=self.dry_run - ) + questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run) # Set up autodetector autodetector = MigrationAutodetector( loader.project_state(), @@ -174,11 +154,12 @@ class Command(BaseCommand): # If they want to make an empty migration, make one for each app if self.empty: if not app_labels: - raise CommandError( - "You must supply at least one app label when using --empty." - ) + raise CommandError("You must supply at least one app label when using --empty.") # Make a fake changes() result we can pass to arrange_for_graph - changes = {app: [Migration("custom", app)] for app in app_labels} + changes = { + app: [Migration("custom", app)] + for app in app_labels + } changes = autodetector.arrange_for_graph( changes=changes, graph=loader.graph, @@ -200,14 +181,9 @@ class Command(BaseCommand): if self.verbosity >= 1: if app_labels: if len(app_labels) == 1: - self.stdout.write( - "No changes detected in app '%s'" % app_labels.pop() - ) + self.stdout.write("No changes detected in app '%s'" % app_labels.pop()) else: - self.stdout.write( - "No changes detected in apps '%s'" - % ("', '".join(app_labels)) - ) + self.stdout.write("No changes detected in apps '%s'" % ("', '".join(app_labels))) else: self.stdout.write("No changes detected") else: @@ -222,9 +198,7 @@ class Command(BaseCommand): directory_created = {} for app_label, app_migrations in changes.items(): if self.verbosity >= 1: - self.stdout.write( - self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) - ) + self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label)) for migration in app_migrations: # Describe the migration writer = MigrationWriter(migration, self.include_header) @@ -235,13 +209,11 @@ class Command(BaseCommand): migration_string = os.path.relpath(writer.path) except ValueError: migration_string = writer.path - if migration_string.startswith(".."): + if migration_string.startswith('..'): migration_string = writer.path - self.stdout.write( - " %s\n" % self.style.MIGRATE_LABEL(migration_string) - ) + self.stdout.write(' %s\n' % self.style.MIGRATE_LABEL(migration_string)) for operation in migration.operations: - self.stdout.write(" - %s" % operation.describe()) + self.stdout.write(' - %s' % operation.describe()) if not self.dry_run: # Write the migrations file to the disk. migrations_directory = os.path.dirname(writer.path) @@ -253,17 +225,15 @@ class Command(BaseCommand): # We just do this once per app directory_created[app_label] = True migration_string = writer.as_string() - with open(writer.path, "w", encoding="utf-8") as fh: + with open(writer.path, "w", encoding='utf-8') as fh: fh.write(migration_string) elif self.verbosity == 3: # Alternatively, makemigrations --dry-run --verbosity 3 # will output the migrations to stdout rather than saving # the file to the disk. - self.stdout.write( - self.style.MIGRATE_HEADING( - "Full migrations file '%s':" % writer.filename - ) - ) + self.stdout.write(self.style.MIGRATE_HEADING( + "Full migrations file '%s':" % writer.filename + )) self.stdout.write(writer.as_string()) def handle_merge(self, loader, conflicts): @@ -274,7 +244,7 @@ class Command(BaseCommand): if self.interactive: questioner = InteractiveMigrationQuestioner() else: - questioner = MigrationQuestioner(defaults={"ask_merge": True}) + questioner = MigrationQuestioner(defaults={'ask_merge': True}) for app_label, migration_names in conflicts.items(): # Grab out the migrations in question, and work out their @@ -283,8 +253,7 @@ class Command(BaseCommand): for migration_name in migration_names: migration = loader.get_migration(app_label, migration_name) migration.ancestry = [ - mig - for mig in loader.graph.forwards_plan((app_label, migration_name)) + mig for mig in loader.graph.forwards_plan((app_label, migration_name)) if mig[0] == migration.app_label ] merge_migrations.append(migration) @@ -293,23 +262,15 @@ class Command(BaseCommand): return all(item == seq[0] for item in seq[1:]) merge_migrations_generations = zip(*(m.ancestry for m in merge_migrations)) - common_ancestor_count = sum( - 1 - for common_ancestor_generation in takewhile( - all_items_equal, merge_migrations_generations - ) - ) + common_ancestor_count = sum(1 for common_ancestor_generation + in takewhile(all_items_equal, merge_migrations_generations)) if not common_ancestor_count: - raise ValueError( - "Could not find common ancestor of %s" % migration_names - ) + raise ValueError("Could not find common ancestor of %s" % migration_names) # Now work out the operations along each divergent branch for migration in merge_migrations: migration.branch = migration.ancestry[common_ancestor_count:] - migrations_ops = ( - loader.get_migration(node_app, node_name).operations - for node_app, node_name in migration.branch - ) + migrations_ops = (loader.get_migration(node_app, node_name).operations + for node_app, node_name in migration.branch) migration.merged_operations = sum(migrations_ops, []) # In future, this could use some of the Optimizer code # (can_optimize_through) to automatically see if they're @@ -317,11 +278,9 @@ class Command(BaseCommand): if self.verbosity > 0: self.stdout.write(self.style.MIGRATE_HEADING("Merging %s" % app_label)) for migration in merge_migrations: - self.stdout.write( - self.style.MIGRATE_LABEL(" Branch %s" % migration.name) - ) + self.stdout.write(self.style.MIGRATE_LABEL(" Branch %s" % migration.name)) for operation in migration.merged_operations: - self.stdout.write(" - %s" % operation.describe()) + self.stdout.write(' - %s' % operation.describe()) if questioner.ask_merge(app_label): # If they still want to merge it, then write out an empty # file depending on the migrations needing merging. @@ -333,47 +292,34 @@ class Command(BaseCommand): biggest_number = max(x for x in numbers if x is not None) except ValueError: biggest_number = 1 - subclass = type( - "Migration", - (Migration,), - { - "dependencies": [ - (app_label, migration.name) - for migration in merge_migrations - ], - }, - ) - parts = ["%04i" % (biggest_number + 1)] + subclass = type("Migration", (Migration,), { + "dependencies": [(app_label, migration.name) for migration in merge_migrations], + }) + parts = ['%04i' % (biggest_number + 1)] if self.migration_name: parts.append(self.migration_name) else: - parts.append("merge") - leaf_names = "_".join( - sorted(migration.name for migration in merge_migrations) - ) + parts.append('merge') + leaf_names = '_'.join(sorted(migration.name for migration in merge_migrations)) if len(leaf_names) > 47: parts.append(get_migration_name_timestamp()) else: parts.append(leaf_names) - migration_name = "_".join(parts) + migration_name = '_'.join(parts) new_migration = subclass(migration_name, app_label) writer = MigrationWriter(new_migration, self.include_header) if not self.dry_run: # Write the merge migrations file to the disk - with open(writer.path, "w", encoding="utf-8") as fh: + with open(writer.path, "w", encoding='utf-8') as fh: fh.write(writer.as_string()) if self.verbosity > 0: - self.stdout.write( - "\nCreated new merge migration %s" % writer.path - ) + self.stdout.write("\nCreated new merge migration %s" % writer.path) elif self.verbosity == 3: # Alternatively, makemigrations --merge --dry-run --verbosity 3 # will output the merge migrations to stdout rather than saving # the file to the disk. - self.stdout.write( - self.style.MIGRATE_HEADING( - "Full merge migrations file '%s':" % writer.filename - ) - ) + self.stdout.write(self.style.MIGRATE_HEADING( + "Full merge migrations file '%s':" % writer.filename + )) self.stdout.write(writer.as_string()) diff --git a/venv/Lib/site-packages/django/core/management/commands/migrate.py b/venv/Lib/site-packages/django/core/management/commands/migrate.py index 228e07a..a65500d 100644 --- a/venv/Lib/site-packages/django/core/management/commands/migrate.py +++ b/venv/Lib/site-packages/django/core/management/commands/migrate.py @@ -3,8 +3,12 @@ import time from importlib import import_module from django.apps import apps -from django.core.management.base import BaseCommand, CommandError, no_translations -from django.core.management.sql import emit_post_migrate_signal, emit_pre_migrate_signal +from django.core.management.base import ( + BaseCommand, CommandError, no_translations, +) +from django.core.management.sql import ( + emit_post_migrate_signal, emit_pre_migrate_signal, +) from django.db import DEFAULT_DB_ALIAS, connections, router from django.db.migrations.autodetector import MigrationAutodetector from django.db.migrations.executor import MigrationExecutor @@ -15,89 +19,69 @@ from django.utils.text import Truncator class Command(BaseCommand): - help = ( - "Updates database schema. Manages both apps with migrations and those without." - ) + help = "Updates database schema. Manages both apps with migrations and those without." requires_system_checks = [] def add_arguments(self, parser): parser.add_argument( - "--skip-checks", - action="store_true", - help="Skip system checks.", + '--skip-checks', action='store_true', + help='Skip system checks.', ) parser.add_argument( - "app_label", - nargs="?", - help="App label of an application to synchronize the state.", + 'app_label', nargs='?', + help='App label of an application to synchronize the state.', ) parser.add_argument( - "migration_name", - nargs="?", - help="Database state will be brought to the state after that " - 'migration. Use the name "zero" to unapply all migrations.', + 'migration_name', nargs='?', + help='Database state will be brought to the state after that ' + 'migration. Use the name "zero" to unapply all migrations.', ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--database", + '--database', default=DEFAULT_DB_ALIAS, - help=( - 'Nominates a database to synchronize. Defaults to the "default" ' - "database." - ), + help='Nominates a database to synchronize. Defaults to the "default" database.', ) parser.add_argument( - "--fake", - action="store_true", - help="Mark migrations as run without actually running them.", + '--fake', action='store_true', + help='Mark migrations as run without actually running them.', ) parser.add_argument( - "--fake-initial", - action="store_true", - help=( - "Detect if tables already exist and fake-apply initial migrations if " - "so. Make sure that the current database schema matches your initial " - "migration before using this flag. Django will only check for an " - "existing table name." - ), + '--fake-initial', action='store_true', + help='Detect if tables already exist and fake-apply initial migrations if so. Make sure ' + 'that the current database schema matches your initial migration before using this ' + 'flag. Django will only check for an existing table name.', ) parser.add_argument( - "--plan", - action="store_true", - help="Shows a list of the migration actions that will be performed.", + '--plan', action='store_true', + help='Shows a list of the migration actions that will be performed.', ) parser.add_argument( - "--run-syncdb", - action="store_true", - help="Creates tables for apps without migrations.", + '--run-syncdb', action='store_true', + help='Creates tables for apps without migrations.', ) parser.add_argument( - "--check", - action="store_true", - dest="check_unapplied", - help="Exits with a non-zero status if unapplied migrations exist.", + '--check', action='store_true', dest='check_unapplied', + help='Exits with a non-zero status if unapplied migrations exist.', ) @no_translations def handle(self, *args, **options): - database = options["database"] - if not options["skip_checks"]: + database = options['database'] + if not options['skip_checks']: self.check(databases=[database]) - self.verbosity = options["verbosity"] - self.interactive = options["interactive"] + self.verbosity = options['verbosity'] + self.interactive = options['interactive'] # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): - import_module(".management", app_config.name) + import_module('.management', app_config.name) # Get the database we're operating from connection = connections[database] @@ -115,7 +99,8 @@ class Command(BaseCommand): conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join( - "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() + "%s in %s" % (", ".join(names), app) + for app, names in conflicts.items() ) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " @@ -124,74 +109,57 @@ class Command(BaseCommand): ) # If they supplied command line arguments, work out what they mean. - run_syncdb = options["run_syncdb"] + run_syncdb = options['run_syncdb'] target_app_labels_only = True - if options["app_label"]: + if options['app_label']: # Validate app_label. - app_label = options["app_label"] + app_label = options['app_label'] try: apps.get_app_config(app_label) except LookupError as err: raise CommandError(str(err)) if run_syncdb: if app_label in executor.loader.migrated_apps: - raise CommandError( - "Can't use run_syncdb with app '%s' as it has migrations." - % app_label - ) + raise CommandError("Can't use run_syncdb with app '%s' as it has migrations." % app_label) elif app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations." % app_label) - if options["app_label"] and options["migration_name"]: - migration_name = options["migration_name"] + if options['app_label'] and options['migration_name']: + migration_name = options['migration_name'] if migration_name == "zero": targets = [(app_label, None)] else: try: - migration = executor.loader.get_migration_by_prefix( - app_label, migration_name - ) + migration = executor.loader.get_migration_by_prefix(app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. " - "Please be more specific." % (migration_name, app_label) + "Please be more specific." % + (migration_name, app_label) ) except KeyError: - raise CommandError( - "Cannot find a migration matching '%s' from app '%s'." - % (migration_name, app_label) - ) - target = (app_label, migration.name) - # Partially applied squashed migrations are not included in the - # graph, use the last replacement instead. - if ( - target not in executor.loader.graph.nodes - and target in executor.loader.replacements - ): - incomplete_migration = executor.loader.replacements[target] - target = incomplete_migration.replaces[-1] - targets = [target] + raise CommandError("Cannot find a migration matching '%s' from app '%s'." % ( + migration_name, app_label)) + targets = [(app_label, migration.name)] target_app_labels_only = False - elif options["app_label"]: - targets = [ - key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label - ] + elif options['app_label']: + targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label] else: targets = executor.loader.graph.leaf_nodes() plan = executor.migration_plan(targets) - exit_dry = plan and options["check_unapplied"] + exit_dry = plan and options['check_unapplied'] - if options["plan"]: - self.stdout.write("Planned operations:", self.style.MIGRATE_LABEL) + if options['plan']: + self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL) if not plan: - self.stdout.write(" No planned migration operations.") + self.stdout.write(' No planned migration operations.') for migration, backwards in plan: self.stdout.write(str(migration), self.style.MIGRATE_HEADING) for operation in migration.operations: message, is_error = self.describe_operation(operation, backwards) style = self.style.WARNING if is_error else None - self.stdout.write(" " + message, style) + self.stdout.write(' ' + message, style) if exit_dry: sys.exit(1) return @@ -199,57 +167,48 @@ class Command(BaseCommand): sys.exit(1) # At this point, ignore run_syncdb if there aren't any apps to sync. - run_syncdb = options["run_syncdb"] and executor.loader.unmigrated_apps + run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps # Print some useful info if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb: - if options["app_label"]: + if options['app_label']: self.stdout.write( - self.style.MIGRATE_LABEL( - " Synchronize unmigrated app: %s" % app_label - ) + self.style.MIGRATE_LABEL(" Synchronize unmigrated app: %s" % app_label) ) else: self.stdout.write( - self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") - + (", ".join(sorted(executor.loader.unmigrated_apps))) + self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + + (", ".join(sorted(executor.loader.unmigrated_apps))) ) if target_app_labels_only: self.stdout.write( - self.style.MIGRATE_LABEL(" Apply all migrations: ") - + (", ".join(sorted({a for a, n in targets})) or "(none)") + self.style.MIGRATE_LABEL(" Apply all migrations: ") + + (", ".join(sorted({a for a, n in targets})) or "(none)") ) else: if targets[0][1] is None: self.stdout.write( - self.style.MIGRATE_LABEL(" Unapply all migrations: ") - + str(targets[0][0]) + self.style.MIGRATE_LABEL(' Unapply all migrations: ') + + str(targets[0][0]) ) else: - self.stdout.write( - self.style.MIGRATE_LABEL(" Target specific migration: ") - + "%s, from %s" % (targets[0][1], targets[0][0]) + self.stdout.write(self.style.MIGRATE_LABEL( + " Target specific migration: ") + "%s, from %s" + % (targets[0][1], targets[0][0]) ) pre_migrate_state = executor._create_project_state(with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps emit_pre_migrate_signal( - self.verbosity, - self.interactive, - connection.alias, - stdout=self.stdout, - apps=pre_migrate_apps, - plan=plan, + self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan, ) # Run the syncdb phase. if run_syncdb: if self.verbosity >= 1: - self.stdout.write( - self.style.MIGRATE_HEADING("Synchronizing apps without migrations:") - ) - if options["app_label"]: + self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:")) + if options['app_label']: self.sync_apps(connection, [app_label]) else: self.sync_apps(connection, executor.loader.unmigrated_apps) @@ -260,38 +219,30 @@ class Command(BaseCommand): if not plan: if self.verbosity >= 1: self.stdout.write(" No migrations to apply.") - # If there's changes that aren't in migrations yet, tell them - # how to fix it. + # If there's changes that aren't in migrations yet, tell them how to fix it. autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changes = autodetector.changes(graph=executor.loader.graph) if changes: - self.stdout.write( - self.style.NOTICE( - " Your models in app(s): %s have changes that are not " - "yet reflected in a migration, and so won't be " - "applied." % ", ".join(repr(app) for app in sorted(changes)) - ) - ) - self.stdout.write( - self.style.NOTICE( - " Run 'manage.py makemigrations' to make new " - "migrations, and then re-run 'manage.py migrate' to " - "apply them." - ) - ) + self.stdout.write(self.style.NOTICE( + " Your models in app(s): %s have changes that are not " + "yet reflected in a migration, and so won't be " + "applied." % ", ".join(repr(app) for app in sorted(changes)) + )) + self.stdout.write(self.style.NOTICE( + " Run 'manage.py makemigrations' to make new " + "migrations, and then re-run 'manage.py migrate' to " + "apply them." + )) fake = False fake_initial = False else: - fake = options["fake"] - fake_initial = options["fake_initial"] + fake = options['fake'] + fake_initial = options['fake_initial'] post_migrate_state = executor.migrate( - targets, - plan=plan, - state=pre_migrate_state.clone(), - fake=fake, + targets, plan=plan, state=pre_migrate_state.clone(), fake=fake, fake_initial=fake_initial, ) # post_migrate signals have access to all models. Ensure that all models @@ -308,19 +259,14 @@ class Command(BaseCommand): model_key = model_state.app_label, model_state.name_lower model_keys.append(model_key) post_migrate_apps.unregister_model(*model_key) - post_migrate_apps.render_multiple( - [ModelState.from_model(apps.get_model(*model)) for model in model_keys] - ) + post_migrate_apps.render_multiple([ + ModelState.from_model(apps.get_model(*model)) for model in model_keys + ]) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal( - self.verbosity, - self.interactive, - connection.alias, - stdout=self.stdout, - apps=post_migrate_apps, - plan=plan, + self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan, ) def migration_progress_callback(self, action, migration=None, fake=False): @@ -332,9 +278,7 @@ class Command(BaseCommand): self.stdout.write(" Applying %s..." % migration, ending="") self.stdout.flush() elif action == "apply_success": - elapsed = ( - " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" - ) + elapsed = " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" if fake: self.stdout.write(self.style.SUCCESS(" FAKED" + elapsed)) else: @@ -345,9 +289,7 @@ class Command(BaseCommand): self.stdout.write(" Unapplying %s..." % migration, ending="") self.stdout.flush() elif action == "unapply_success": - elapsed = ( - " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" - ) + elapsed = " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" if fake: self.stdout.write(self.style.SUCCESS(" FAKED" + elapsed)) else: @@ -358,9 +300,7 @@ class Command(BaseCommand): self.stdout.write(" Rendering model states...", ending="") self.stdout.flush() elif action == "render_success": - elapsed = ( - " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" - ) + elapsed = " (%.3fs)" % (time.monotonic() - self.start) if compute_time else "" self.stdout.write(self.style.SUCCESS(" DONE" + elapsed)) def sync_apps(self, connection, app_labels): @@ -372,9 +312,7 @@ class Command(BaseCommand): all_models = [ ( app_config.label, - router.get_migratable_models( - app_config, connection.alias, include_auto_created=False - ), + router.get_migratable_models(app_config, connection.alias, include_auto_created=False), ) for app_config in apps.get_app_configs() if app_config.models_module is not None and app_config.label in app_labels @@ -384,11 +322,8 @@ class Command(BaseCommand): opts = model._meta converter = connection.introspection.identifier_converter return not ( - (converter(opts.db_table) in tables) - or ( - opts.auto_created - and converter(opts.auto_created._meta.db_table) in tables - ) + (converter(opts.db_table) in tables) or + (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables) ) manifest = { @@ -398,7 +333,7 @@ class Command(BaseCommand): # Create the tables for each model if self.verbosity >= 1: - self.stdout.write(" Creating tables...") + self.stdout.write(' Creating tables...') with connection.schema_editor() as editor: for app_name, model_list in manifest.items(): for model in model_list: @@ -407,39 +342,36 @@ class Command(BaseCommand): continue if self.verbosity >= 3: self.stdout.write( - " Processing %s.%s model" - % (app_name, model._meta.object_name) + ' Processing %s.%s model' % (app_name, model._meta.object_name) ) if self.verbosity >= 1: - self.stdout.write( - " Creating table %s" % model._meta.db_table - ) + self.stdout.write(' Creating table %s' % model._meta.db_table) editor.create_model(model) # Deferred SQL is executed when exiting the editor's context. if self.verbosity >= 1: - self.stdout.write(" Running deferred SQL...") + self.stdout.write(' Running deferred SQL...') @staticmethod def describe_operation(operation, backwards): """Return a string that describes a migration operation for --plan.""" - prefix = "" + prefix = '' is_error = False - if hasattr(operation, "code"): + if hasattr(operation, 'code'): code = operation.reverse_code if backwards else operation.code - action = (code.__doc__ or "") if code else None - elif hasattr(operation, "sql"): + action = (code.__doc__ or '') if code else None + elif hasattr(operation, 'sql'): action = operation.reverse_sql if backwards else operation.sql else: - action = "" + action = '' if backwards: - prefix = "Undo " + prefix = 'Undo ' if action is not None: - action = str(action).replace("\n", "") + action = str(action).replace('\n', '') elif backwards: - action = "IRREVERSIBLE" + action = 'IRREVERSIBLE' is_error = True if action: - action = " -> " + action + action = ' -> ' + action truncated = Truncator(action) return prefix + operation.describe() + truncated.chars(40), is_error diff --git a/venv/Lib/site-packages/django/core/management/commands/runserver.py b/venv/Lib/site-packages/django/core/management/commands/runserver.py index 3c39f57..d9fb088 100644 --- a/venv/Lib/site-packages/django/core/management/commands/runserver.py +++ b/venv/Lib/site-packages/django/core/management/commands/runserver.py @@ -7,66 +7,53 @@ from datetime import datetime from django.conf import settings from django.core.management.base import BaseCommand, CommandError -from django.core.servers.basehttp import WSGIServer, get_internal_wsgi_application, run +from django.core.servers.basehttp import ( + WSGIServer, get_internal_wsgi_application, run, +) from django.utils import autoreload from django.utils.regex_helper import _lazy_re_compile -naiveip_re = _lazy_re_compile( - r"""^(?: +naiveip_re = _lazy_re_compile(r"""^(?: (?P<addr> (?P<ipv4>\d{1,3}(?:\.\d{1,3}){3}) | # IPv4 address (?P<ipv6>\[[a-fA-F0-9:]+\]) | # IPv6 address (?P<fqdn>[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*) # FQDN -):)?(?P<port>\d+)$""", - re.X, -) +):)?(?P<port>\d+)$""", re.X) class Command(BaseCommand): - help = "Starts a lightweight web server for development." + help = "Starts a lightweight Web server for development." # Validation is called explicitly each time the server is reloaded. requires_system_checks = [] - stealth_options = ("shutdown_message",) - suppressed_base_arguments = {"--verbosity", "--traceback"} + stealth_options = ('shutdown_message',) - default_addr = "127.0.0.1" - default_addr_ipv6 = "::1" - default_port = "8000" - protocol = "http" + default_addr = '127.0.0.1' + default_addr_ipv6 = '::1' + default_port = '8000' + protocol = 'http' server_cls = WSGIServer def add_arguments(self, parser): parser.add_argument( - "addrport", nargs="?", help="Optional port number, or ipaddr:port" + 'addrport', nargs='?', + help='Optional port number, or ipaddr:port' ) parser.add_argument( - "--ipv6", - "-6", - action="store_true", - dest="use_ipv6", - help="Tells Django to use an IPv6 address.", + '--ipv6', '-6', action='store_true', dest='use_ipv6', + help='Tells Django to use an IPv6 address.', ) parser.add_argument( - "--nothreading", - action="store_false", - dest="use_threading", - help="Tells Django to NOT use threading.", + '--nothreading', action='store_false', dest='use_threading', + help='Tells Django to NOT use threading.', ) parser.add_argument( - "--noreload", - action="store_false", - dest="use_reloader", - help="Tells Django to NOT use the auto-reloader.", - ) - parser.add_argument( - "--skip-checks", - action="store_true", - help="Skip system checks.", + '--noreload', action='store_false', dest='use_reloader', + help='Tells Django to NOT use the auto-reloader.', ) def execute(self, *args, **options): - if options["no_color"]: + if options['no_color']: # We rely on the environment because it's currently the only # way to reach WSGIRequestHandler. This seems an acceptable # compromise considering `runserver` runs indefinitely. @@ -79,22 +66,20 @@ class Command(BaseCommand): def handle(self, *args, **options): if not settings.DEBUG and not settings.ALLOWED_HOSTS: - raise CommandError("You must set settings.ALLOWED_HOSTS if DEBUG is False.") + raise CommandError('You must set settings.ALLOWED_HOSTS if DEBUG is False.') - self.use_ipv6 = options["use_ipv6"] + self.use_ipv6 = options['use_ipv6'] if self.use_ipv6 and not socket.has_ipv6: - raise CommandError("Your Python does not support IPv6.") + raise CommandError('Your Python does not support IPv6.') self._raw_ipv6 = False - if not options["addrport"]: - self.addr = "" + if not options['addrport']: + self.addr = '' self.port = self.default_port else: - m = re.match(naiveip_re, options["addrport"]) + m = re.match(naiveip_re, options['addrport']) if m is None: - raise CommandError( - '"%s" is not a valid port number ' - "or address:port pair." % options["addrport"] - ) + raise CommandError('"%s" is not a valid port number ' + 'or address:port pair.' % options['addrport']) self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups() if not self.port.isdigit(): raise CommandError("%r is not a valid port number." % self.port) @@ -112,7 +97,7 @@ class Command(BaseCommand): def run(self, **options): """Run the server, using the autoreloader if needed.""" - use_reloader = options["use_reloader"] + use_reloader = options['use_reloader'] if use_reloader: autoreload.run_with_reloader(self.inner_run, **options) @@ -124,45 +109,35 @@ class Command(BaseCommand): # to be raised in the child process, raise it now. autoreload.raise_last_exception() - threading = options["use_threading"] + threading = options['use_threading'] # 'shutdown_message' is a stealth option. - shutdown_message = options.get("shutdown_message", "") - quit_command = "CTRL-BREAK" if sys.platform == "win32" else "CONTROL-C" + shutdown_message = options.get('shutdown_message', '') + quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C' - if not options["skip_checks"]: - self.stdout.write("Performing system checks...\n\n") - self.check(display_num_errors=True) + self.stdout.write("Performing system checks...\n\n") + self.check(display_num_errors=True) # Need to check migrations here, so can't use the # requires_migrations_check attribute. self.check_migrations() - now = datetime.now().strftime("%B %d, %Y - %X") + now = datetime.now().strftime('%B %d, %Y - %X') self.stdout.write(now) - self.stdout.write( - ( - "Django version %(version)s, using settings %(settings)r\n" - "Starting development server at %(protocol)s://%(addr)s:%(port)s/\n" - "Quit the server with %(quit_command)s." - ) - % { - "version": self.get_version(), - "settings": settings.SETTINGS_MODULE, - "protocol": self.protocol, - "addr": "[%s]" % self.addr if self._raw_ipv6 else self.addr, - "port": self.port, - "quit_command": quit_command, - } - ) + self.stdout.write(( + "Django version %(version)s, using settings %(settings)r\n" + "Starting development server at %(protocol)s://%(addr)s:%(port)s/\n" + "Quit the server with %(quit_command)s." + ) % { + "version": self.get_version(), + "settings": settings.SETTINGS_MODULE, + "protocol": self.protocol, + "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr, + "port": self.port, + "quit_command": quit_command, + }) try: handler = self.get_handler(*args, **options) - run( - self.addr, - int(self.port), - handler, - ipv6=self.use_ipv6, - threading=threading, - server_cls=self.server_cls, - ) + run(self.addr, int(self.port), handler, + ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) except OSError as e: # Use helpful error messages instead of ugly tracebacks. ERRORS = { diff --git a/venv/Lib/site-packages/django/core/management/commands/sendtestemail.py b/venv/Lib/site-packages/django/core/management/commands/sendtestemail.py index fbb2a78..9ed1e96 100644 --- a/venv/Lib/site-packages/django/core/management/commands/sendtestemail.py +++ b/venv/Lib/site-packages/django/core/management/commands/sendtestemail.py @@ -7,40 +7,34 @@ from django.utils import timezone class Command(BaseCommand): help = "Sends a test email to the email addresses specified as arguments." - missing_args_message = ( - "You must specify some email recipients, or pass the --managers or --admin " - "options." - ) + missing_args_message = "You must specify some email recipients, or pass the --managers or --admin options." def add_arguments(self, parser): parser.add_argument( - "email", - nargs="*", - help="One or more email addresses to send a test email to.", + 'email', nargs='*', + help='One or more email addresses to send a test email to.', ) parser.add_argument( - "--managers", - action="store_true", - help="Send a test email to the addresses specified in settings.MANAGERS.", + '--managers', action='store_true', + help='Send a test email to the addresses specified in settings.MANAGERS.', ) parser.add_argument( - "--admins", - action="store_true", - help="Send a test email to the addresses specified in settings.ADMINS.", + '--admins', action='store_true', + help='Send a test email to the addresses specified in settings.ADMINS.', ) def handle(self, *args, **kwargs): - subject = "Test email from %s on %s" % (socket.gethostname(), timezone.now()) + subject = 'Test email from %s on %s' % (socket.gethostname(), timezone.now()) send_mail( subject=subject, - message="If you're reading this, it was successful.", + message="If you\'re reading this, it was successful.", from_email=None, - recipient_list=kwargs["email"], + recipient_list=kwargs['email'], ) - if kwargs["managers"]: + if kwargs['managers']: mail_managers(subject, "This email was sent to the site managers.") - if kwargs["admins"]: + if kwargs['admins']: mail_admins(subject, "This email was sent to the site admins.") diff --git a/venv/Lib/site-packages/django/core/management/commands/shell.py b/venv/Lib/site-packages/django/core/management/commands/shell.py index f55b346..3e8a451 100644 --- a/venv/Lib/site-packages/django/core/management/commands/shell.py +++ b/venv/Lib/site-packages/django/core/management/commands/shell.py @@ -15,57 +15,57 @@ class Command(BaseCommand): ) requires_system_checks = [] - shells = ["ipython", "bpython", "python"] + shells = ['ipython', 'bpython', 'python'] def add_arguments(self, parser): parser.add_argument( - "--no-startup", - action="store_true", - help=( - "When using plain Python, ignore the PYTHONSTARTUP environment " - "variable and ~/.pythonrc.py script." - ), + '--no-startup', action='store_true', + help='When using plain Python, ignore the PYTHONSTARTUP environment variable and ~/.pythonrc.py script.', ) parser.add_argument( - "-i", - "--interface", - choices=self.shells, - help=( - "Specify an interactive interpreter interface. Available options: " - '"ipython", "bpython", and "python"' - ), + '-i', '--interface', choices=self.shells, + help='Specify an interactive interpreter interface. Available options: "ipython", "bpython", and "python"', ) parser.add_argument( - "-c", - "--command", - help=( - "Instead of opening an interactive shell, run a command as Django and " - "exit." - ), + '-c', '--command', + help='Instead of opening an interactive shell, run a command as Django and exit.', ) def ipython(self, options): from IPython import start_ipython - start_ipython(argv=[]) def bpython(self, options): import bpython - bpython.embed() def python(self, options): import code - # Set up a dictionary to serve as the environment for the shell. + # Set up a dictionary to serve as the environment for the shell, so + # that tab completion works on objects that are imported at runtime. imported_objects = {} + try: # Try activating rlcompleter, because it's handy. + import readline + except ImportError: + pass + else: + # We don't have to wrap the following import in a 'try', because + # we already know 'readline' was imported successfully. + import rlcompleter + readline.set_completer(rlcompleter.Completer(imported_objects).complete) + # Enable tab completion on systems using libedit (e.g. macOS). + # These lines are copied from Python's Lib/site.py. + readline_doc = getattr(readline, '__doc__', '') + if readline_doc is not None and 'libedit' in readline_doc: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab:complete") # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system # conventions and get $PYTHONSTARTUP first then .pythonrc.py. - if not options["no_startup"]: - for pythonrc in OrderedSet( - [os.environ.get("PYTHONSTARTUP"), os.path.expanduser("~/.pythonrc.py")] - ): + if not options['no_startup']: + for pythonrc in OrderedSet([os.environ.get("PYTHONSTARTUP"), os.path.expanduser('~/.pythonrc.py')]): if not pythonrc: continue if not os.path.isfile(pythonrc): @@ -75,61 +75,25 @@ class Command(BaseCommand): # Match the behavior of the cpython shell where an error in # PYTHONSTARTUP prints an exception and continues. try: - exec(compile(pythonrc_code, pythonrc, "exec"), imported_objects) + exec(compile(pythonrc_code, pythonrc, 'exec'), imported_objects) except Exception: traceback.print_exc() - # By default, this will set up readline to do tab completion and to read and - # write history to the .python_history file, but this can be overridden by - # $PYTHONSTARTUP or ~/.pythonrc.py. - try: - hook = sys.__interactivehook__ - except AttributeError: - # Match the behavior of the cpython shell where a missing - # sys.__interactivehook__ is ignored. - pass - else: - try: - hook() - except Exception: - # Match the behavior of the cpython shell where an error in - # sys.__interactivehook__ prints a warning and the exception - # and continues. - print("Failed calling sys.__interactivehook__") - traceback.print_exc() - - # Set up tab completion for objects imported by $PYTHONSTARTUP or - # ~/.pythonrc.py. - try: - import readline - import rlcompleter - - readline.set_completer(rlcompleter.Completer(imported_objects).complete) - except ImportError: - pass - - # Start the interactive interpreter. code.interact(local=imported_objects) def handle(self, **options): # Execute the command and exit. - if options["command"]: - exec(options["command"], globals()) + if options['command']: + exec(options['command'], globals()) return # Execute stdin if it has anything to read and exit. # Not supported on Windows due to select.select() limitations. - if ( - sys.platform != "win32" - and not sys.stdin.isatty() - and select.select([sys.stdin], [], [], 0)[0] - ): + if sys.platform != 'win32' and not sys.stdin.isatty() and select.select([sys.stdin], [], [], 0)[0]: exec(sys.stdin.read(), globals()) return - available_shells = ( - [options["interface"]] if options["interface"] else self.shells - ) + available_shells = [options['interface']] if options['interface'] else self.shells for shell in available_shells: try: diff --git a/venv/Lib/site-packages/django/core/management/commands/showmigrations.py b/venv/Lib/site-packages/django/core/management/commands/showmigrations.py index 203f921..48bea1f 100644 --- a/venv/Lib/site-packages/django/core/management/commands/showmigrations.py +++ b/venv/Lib/site-packages/django/core/management/commands/showmigrations.py @@ -4,7 +4,6 @@ from django.apps import apps from django.core.management.base import BaseCommand from django.db import DEFAULT_DB_ALIAS, connections from django.db.migrations.loader import MigrationLoader -from django.db.migrations.recorder import MigrationRecorder class Command(BaseCommand): @@ -12,58 +11,45 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - "app_label", - nargs="*", - help="App labels of applications to limit the output to.", + 'app_label', nargs='*', + help='App labels of applications to limit the output to.', ) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - "Nominates a database to show migrations for. Defaults to the " - '"default" database.' - ), + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database to synchronize. Defaults to the "default" database.', ) formats = parser.add_mutually_exclusive_group() formats.add_argument( - "--list", - "-l", - action="store_const", - dest="format", - const="list", + '--list', '-l', action='store_const', dest='format', const='list', help=( - "Shows a list of all migrations and which are applied. " - "With a verbosity level of 2 or above, the applied datetimes " - "will be included." + 'Shows a list of all migrations and which are applied. ' + 'With a verbosity level of 2 or above, the applied datetimes ' + 'will be included.' ), ) formats.add_argument( - "--plan", - "-p", - action="store_const", - dest="format", - const="plan", + '--plan', '-p', action='store_const', dest='format', const='plan', help=( - "Shows all migrations in the order they will be applied. With a " - "verbosity level of 2 or above all direct migration dependencies and " - "reverse dependencies (run_before) will be included." - ), + 'Shows all migrations in the order they will be applied. ' + 'With a verbosity level of 2 or above all direct migration dependencies ' + 'and reverse dependencies (run_before) will be included.' + ) ) - parser.set_defaults(format="list") + parser.set_defaults(format='list') def handle(self, *args, **options): - self.verbosity = options["verbosity"] + self.verbosity = options['verbosity'] # Get the database we're operating from - db = options["database"] + db = options['database'] connection = connections[db] - if options["format"] == "plan": - return self.show_plan(connection, options["app_label"]) + if options['format'] == "plan": + return self.show_plan(connection, options['app_label']) else: - return self.show_list(connection, options["app_label"]) + return self.show_list(connection, options['app_label']) def _validate_app_names(self, loader, app_names): has_bad_names = False @@ -83,8 +69,6 @@ class Command(BaseCommand): """ # Load migrations from disk/DB loader = MigrationLoader(connection, ignore_no_migrations=True) - recorder = MigrationRecorder(connection) - recorded_migrations = recorder.applied_migrations() graph = loader.graph # If we were passed a list of apps, validate it if app_names: @@ -103,26 +87,13 @@ class Command(BaseCommand): # Give it a nice title if it's a squashed one title = plan_node[1] if graph.nodes[plan_node].replaces: - title += " (%s squashed migrations)" % len( - graph.nodes[plan_node].replaces - ) + title += " (%s squashed migrations)" % len(graph.nodes[plan_node].replaces) applied_migration = loader.applied_migrations.get(plan_node) # Mark it as applied/unapplied if applied_migration: - if plan_node in recorded_migrations: - output = " [X] %s" % title - else: - title += " Run 'manage.py migrate' to finish recording." - output = " [-] %s" % title - if self.verbosity >= 2 and hasattr( - applied_migration, "applied" - ): - output += ( - " (applied at %s)" - % applied_migration.applied.strftime( - "%Y-%m-%d %H:%M:%S" - ) - ) + output = ' [X] %s' % title + if self.verbosity >= 2: + output += ' (applied at %s)' % applied_migration.applied.strftime('%Y-%m-%d %H:%M:%S') self.stdout.write(output) else: self.stdout.write(" [ ] %s" % title) @@ -173,4 +144,4 @@ class Command(BaseCommand): else: self.stdout.write("[ ] %s.%s%s" % (node.key[0], node.key[1], deps)) if not plan: - self.stdout.write("(no migrations)", self.style.ERROR) + self.stdout.write('(no migrations)', self.style.ERROR) diff --git a/venv/Lib/site-packages/django/core/management/commands/sqlflush.py b/venv/Lib/site-packages/django/core/management/commands/sqlflush.py index bc82e1f..2978260 100644 --- a/venv/Lib/site-packages/django/core/management/commands/sqlflush.py +++ b/venv/Lib/site-packages/django/core/management/commands/sqlflush.py @@ -14,16 +14,12 @@ class Command(BaseCommand): def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - 'Nominates a database to print the SQL for. Defaults to the "default" ' - "database." - ), + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database to print the SQL for. Defaults to the "default" database.', ) def handle(self, **options): - sql_statements = sql_flush(self.style, connections[options["database"]]) - if not sql_statements and options["verbosity"] >= 1: - self.stderr.write("No tables found.") - return "\n".join(sql_statements) + sql_statements = sql_flush(self.style, connections[options['database']]) + if not sql_statements and options['verbosity'] >= 1: + self.stderr.write('No tables found.') + return '\n'.join(sql_statements) diff --git a/venv/Lib/site-packages/django/core/management/commands/sqlmigrate.py b/venv/Lib/site-packages/django/core/management/commands/sqlmigrate.py index 2f69936..aee793f 100644 --- a/venv/Lib/site-packages/django/core/management/commands/sqlmigrate.py +++ b/venv/Lib/site-packages/django/core/management/commands/sqlmigrate.py @@ -10,43 +10,34 @@ class Command(BaseCommand): output_transaction = True def add_arguments(self, parser): + parser.add_argument('app_label', help='App label of the application containing the migration.') + parser.add_argument('migration_name', help='Migration name to print the SQL for.') parser.add_argument( - "app_label", help="App label of the application containing the migration." + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database to create SQL for. Defaults to the "default" database.', ) parser.add_argument( - "migration_name", help="Migration name to print the SQL for." - ) - parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - 'Nominates a database to create SQL for. Defaults to the "default" ' - "database." - ), - ) - parser.add_argument( - "--backwards", - action="store_true", - help="Creates SQL to unapply the migration, rather than to apply it", + '--backwards', action='store_true', + help='Creates SQL to unapply the migration, rather than to apply it', ) def execute(self, *args, **options): # sqlmigrate doesn't support coloring its output but we need to force # no_color=True so that the BEGIN/COMMIT statements added by # output_transaction don't get colored either. - options["no_color"] = True + options['no_color'] = True return super().execute(*args, **options) def handle(self, *args, **options): # Get the database we're operating from - connection = connections[options["database"]] + connection = connections[options['database']] - # Load up a loader to get all the migration data, but don't replace + # Load up an loader to get all the migration data, but don't replace # migrations. loader = MigrationLoader(connection, replace_migrations=False) # Resolve command-line arguments into a migration - app_label, migration_name = options["app_label"], options["migration_name"] + app_label, migration_name = options['app_label'], options['migration_name'] # Validate app_label try: apps.get_app_config(app_label) @@ -57,27 +48,21 @@ class Command(BaseCommand): try: migration = loader.get_migration_by_prefix(app_label, migration_name) except AmbiguityError: - raise CommandError( - "More than one migration matches '%s' in app '%s'. Please be more " - "specific." % (migration_name, app_label) - ) + raise CommandError("More than one migration matches '%s' in app '%s'. Please be more specific." % ( + migration_name, app_label)) except KeyError: - raise CommandError( - "Cannot find a migration matching '%s' from app '%s'. Is it in " - "INSTALLED_APPS?" % (migration_name, app_label) - ) + raise CommandError("Cannot find a migration matching '%s' from app '%s'. Is it in INSTALLED_APPS?" % ( + migration_name, app_label)) target = (app_label, migration.name) # Show begin/end around output for atomic migrations, if the database # supports transactional DDL. - self.output_transaction = ( - migration.atomic and connection.features.can_rollback_ddl - ) + self.output_transaction = migration.atomic and connection.features.can_rollback_ddl # Make a plan that represents just the requested migrations and show SQL # for it - plan = [(loader.graph.nodes[target], options["backwards"])] + plan = [(loader.graph.nodes[target], options['backwards'])] sql_statements = loader.collect_sql(plan) - if not sql_statements and options["verbosity"] >= 1: - self.stderr.write("No operations found.") - return "\n".join(sql_statements) + if not sql_statements and options['verbosity'] >= 1: + self.stderr.write('No operations found.') + return '\n'.join(sql_statements) diff --git a/venv/Lib/site-packages/django/core/management/commands/sqlsequencereset.py b/venv/Lib/site-packages/django/core/management/commands/sqlsequencereset.py index 9653fa5..1d74ed9 100644 --- a/venv/Lib/site-packages/django/core/management/commands/sqlsequencereset.py +++ b/venv/Lib/site-packages/django/core/management/commands/sqlsequencereset.py @@ -3,29 +3,23 @@ from django.db import DEFAULT_DB_ALIAS, connections class Command(AppCommand): - help = ( - "Prints the SQL statements for resetting sequences for the given app name(s)." - ) + help = 'Prints the SQL statements for resetting sequences for the given app name(s).' output_transaction = True def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - "--database", - default=DEFAULT_DB_ALIAS, - help=( - 'Nominates a database to print the SQL for. Defaults to the "default" ' - "database." - ), + '--database', default=DEFAULT_DB_ALIAS, + help='Nominates a database to print the SQL for. Defaults to the "default" database.', ) def handle_app_config(self, app_config, **options): if app_config.models_module is None: return - connection = connections[options["database"]] + connection = connections[options['database']] models = app_config.get_models(include_auto_created=True) statements = connection.ops.sequence_reset_sql(self.style, models) - if not statements and options["verbosity"] >= 1: - self.stderr.write("No sequences found.") - return "\n".join(statements) + if not statements and options['verbosity'] >= 1: + self.stderr.write('No sequences found.') + return '\n'.join(statements) diff --git a/venv/Lib/site-packages/django/core/management/commands/squashmigrations.py b/venv/Lib/site-packages/django/core/management/commands/squashmigrations.py index 2529c4d..65da95a 100644 --- a/venv/Lib/site-packages/django/core/management/commands/squashmigrations.py +++ b/venv/Lib/site-packages/django/core/management/commands/squashmigrations.py @@ -10,68 +10,54 @@ from django.utils.version import get_docs_version class Command(BaseCommand): - help = ( - "Squashes an existing set of migrations (from first until specified) into a " - "single new one." - ) + help = "Squashes an existing set of migrations (from first until specified) into a single new one." def add_arguments(self, parser): parser.add_argument( - "app_label", - help="App label of the application to squash migrations for.", + 'app_label', + help='App label of the application to squash migrations for.', ) parser.add_argument( - "start_migration_name", - nargs="?", - help=( - "Migrations will be squashed starting from and including this " - "migration." - ), + 'start_migration_name', nargs='?', + help='Migrations will be squashed starting from and including this migration.', ) parser.add_argument( - "migration_name", - help="Migrations will be squashed until and including this migration.", + 'migration_name', + help='Migrations will be squashed until and including this migration.', ) parser.add_argument( - "--no-optimize", - action="store_true", - help="Do not try to optimize the squashed operations.", + '--no-optimize', action='store_true', + help='Do not try to optimize the squashed operations.', ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--squashed-name", - help="Sets the name of the new squashed migration.", + '--squashed-name', + help='Sets the name of the new squashed migration.', ) parser.add_argument( - "--no-header", - action="store_false", - dest="include_header", - help="Do not add a header comment to the new squashed migration.", + '--no-header', action='store_false', dest='include_header', + help='Do not add a header comment to the new squashed migration.', ) def handle(self, **options): - self.verbosity = options["verbosity"] - self.interactive = options["interactive"] - app_label = options["app_label"] - start_migration_name = options["start_migration_name"] - migration_name = options["migration_name"] - no_optimize = options["no_optimize"] - squashed_name = options["squashed_name"] - include_header = options["include_header"] + self.verbosity = options['verbosity'] + self.interactive = options['interactive'] + app_label = options['app_label'] + start_migration_name = options['start_migration_name'] + migration_name = options['migration_name'] + no_optimize = options['no_optimize'] + squashed_name = options['squashed_name'] + include_header = options['include_header'] # Validate app_label. try: apps.get_app_config(app_label) except LookupError as err: raise CommandError(str(err)) - # Load the current graph state, check the app and migration they asked - # for exists. + # Load the current graph state, check the app and migration they asked for exists loader = MigrationLoader(connections[DEFAULT_DB_ALIAS]) if app_label not in loader.migrated_apps: raise CommandError( @@ -84,19 +70,13 @@ class Command(BaseCommand): # Work out the list of predecessor migrations migrations_to_squash = [ loader.get_migration(al, mn) - for al, mn in loader.graph.forwards_plan( - (migration.app_label, migration.name) - ) + for al, mn in loader.graph.forwards_plan((migration.app_label, migration.name)) if al == migration.app_label ] if start_migration_name: - start_migration = self.find_migration( - loader, app_label, start_migration_name - ) - start = loader.get_migration( - start_migration.app_label, start_migration.name - ) + start_migration = self.find_migration(loader, app_label, start_migration_name) + start = loader.get_migration(start_migration.app_label, start_migration.name) try: start_index = migrations_to_squash.index(start) migrations_to_squash = migrations_to_squash[start_index:] @@ -111,9 +91,7 @@ class Command(BaseCommand): # Tell them what we're doing and optionally ask if we should proceed if self.verbosity > 0 or self.interactive: - self.stdout.write( - self.style.MIGRATE_HEADING("Will squash the following migrations:") - ) + self.stdout.write(self.style.MIGRATE_HEADING("Will squash the following migrations:")) for migration in migrations_to_squash: self.stdout.write(" - %s" % migration.name) @@ -140,9 +118,9 @@ class Command(BaseCommand): for smigration in migrations_to_squash: if smigration.replaces: raise CommandError( - "You cannot squash squashed migrations! Please transition it to a " - "normal migration first: https://docs.djangoproject.com/en/%s/" - "topics/migrations/#squashing-migrations" % get_docs_version() + "You cannot squash squashed migrations! Please transition " + "it to a normal migration first: " + "https://docs.djangoproject.com/en/%s/topics/migrations/#squashing-migrations" % get_docs_version() ) operations.extend(smigration.operations) for dependency in smigration.dependencies: @@ -157,9 +135,7 @@ class Command(BaseCommand): if no_optimize: if self.verbosity > 0: - self.stdout.write( - self.style.MIGRATE_HEADING("(Skipping optimization.)") - ) + self.stdout.write(self.style.MIGRATE_HEADING("(Skipping optimization.)")) new_operations = operations else: if self.verbosity > 0: @@ -173,8 +149,8 @@ class Command(BaseCommand): self.stdout.write(" No optimizations possible.") else: self.stdout.write( - " Optimized from %s operations to %s operations." - % (len(operations), len(new_operations)) + " Optimized from %s operations to %s operations." % + (len(operations), len(new_operations)) ) # Work out the value of replaces (any squashed ones we're re-squashing) @@ -187,54 +163,44 @@ class Command(BaseCommand): replaces.append((migration.app_label, migration.name)) # Make a new migration with those operations - subclass = type( - "Migration", - (migrations.Migration,), - { - "dependencies": dependencies, - "operations": new_operations, - "replaces": replaces, - }, - ) + subclass = type("Migration", (migrations.Migration,), { + "dependencies": dependencies, + "operations": new_operations, + "replaces": replaces, + }) if start_migration_name: if squashed_name: # Use the name from --squashed-name. - prefix, _ = start_migration.name.split("_", 1) - name = "%s_%s" % (prefix, squashed_name) + prefix, _ = start_migration.name.split('_', 1) + name = '%s_%s' % (prefix, squashed_name) else: # Generate a name. - name = "%s_squashed_%s" % (start_migration.name, migration.name) + name = '%s_squashed_%s' % (start_migration.name, migration.name) new_migration = subclass(name, app_label) else: - name = "0001_%s" % (squashed_name or "squashed_%s" % migration.name) + name = '0001_%s' % (squashed_name or 'squashed_%s' % migration.name) new_migration = subclass(name, app_label) new_migration.initial = True # Write out the new migration file writer = MigrationWriter(new_migration, include_header) - with open(writer.path, "w", encoding="utf-8") as fh: + with open(writer.path, "w", encoding='utf-8') as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write( - self.style.MIGRATE_HEADING( - "Created new squashed migration %s" % writer.path - ) - + "\n" - " You should commit this migration but leave the old ones in place;\n" - " the new migration will be used for new installs. Once you are sure\n" - " all instances of the codebase have applied the migrations you " - "squashed,\n" - " you can delete them." + self.style.MIGRATE_HEADING('Created new squashed migration %s' % writer.path) + '\n' + ' You should commit this migration but leave the old ones in place;\n' + ' the new migration will be used for new installs. Once you are sure\n' + ' all instances of the codebase have applied the migrations you squashed,\n' + ' you can delete them.' ) if writer.needs_manual_porting: self.stdout.write( - self.style.MIGRATE_HEADING("Manual porting required") + "\n" - " Your migrations contained functions that must be manually " - "copied over,\n" - " as we could not safely copy their implementation.\n" - " See the comment at the top of the squashed migration for " - "details." + self.style.MIGRATE_HEADING('Manual porting required') + '\n' + ' Your migrations contained functions that must be manually copied over,\n' + ' as we could not safely copy their implementation.\n' + ' See the comment at the top of the squashed migration for details.' ) def find_migration(self, loader, app_label, name): @@ -247,6 +213,6 @@ class Command(BaseCommand): ) except KeyError: raise CommandError( - "Cannot find a migration matching '%s' from app '%s'." - % (name, app_label) + "Cannot find a migration matching '%s' from app '%s'." % + (name, app_label) ) diff --git a/venv/Lib/site-packages/django/core/management/commands/startapp.py b/venv/Lib/site-packages/django/core/management/commands/startapp.py index e85833b..bba9f3d 100644 --- a/venv/Lib/site-packages/django/core/management/commands/startapp.py +++ b/venv/Lib/site-packages/django/core/management/commands/startapp.py @@ -9,6 +9,6 @@ class Command(TemplateCommand): missing_args_message = "You must provide an application name." def handle(self, **options): - app_name = options.pop("name") - target = options.pop("directory") - super().handle("app", app_name, target, **options) + app_name = options.pop('name') + target = options.pop('directory') + super().handle('app', app_name, target, **options) diff --git a/venv/Lib/site-packages/django/core/management/commands/startproject.py b/venv/Lib/site-packages/django/core/management/commands/startproject.py index ca17fa5..164ccdf 100644 --- a/venv/Lib/site-packages/django/core/management/commands/startproject.py +++ b/venv/Lib/site-packages/django/core/management/commands/startproject.py @@ -12,10 +12,10 @@ class Command(TemplateCommand): missing_args_message = "You must provide a project name." def handle(self, **options): - project_name = options.pop("name") - target = options.pop("directory") + project_name = options.pop('name') + target = options.pop('directory') # Create a random SECRET_KEY to put it in the main settings. - options["secret_key"] = SECRET_KEY_INSECURE_PREFIX + get_random_secret_key() + options['secret_key'] = SECRET_KEY_INSECURE_PREFIX + get_random_secret_key() - super().handle("project", project_name, target, **options) + super().handle('project', project_name, target, **options) diff --git a/venv/Lib/site-packages/django/core/management/commands/test.py b/venv/Lib/site-packages/django/core/management/commands/test.py index 2df6dbb..35e7b73 100644 --- a/venv/Lib/site-packages/django/core/management/commands/test.py +++ b/venv/Lib/site-packages/django/core/management/commands/test.py @@ -3,12 +3,11 @@ import sys from django.conf import settings from django.core.management.base import BaseCommand from django.core.management.utils import get_command_line_option -from django.test.runner import get_max_test_processes from django.test.utils import NullTimeKeeper, TimeKeeper, get_runner class Command(BaseCommand): - help = "Discover and run tests in the specified modules or the current directory." + help = 'Discover and run tests in the specified modules or the current directory.' # DiscoverRunner runs the checks after databases are set up. requires_system_checks = [] @@ -20,51 +19,39 @@ class Command(BaseCommand): option. This allows a test runner to define additional command line arguments. """ - self.test_runner = get_command_line_option(argv, "--testrunner") + self.test_runner = get_command_line_option(argv, '--testrunner') super().run_from_argv(argv) def add_arguments(self, parser): parser.add_argument( - "args", - metavar="test_label", - nargs="*", - help=( - "Module paths to test; can be modulename, modulename.TestCase or " - "modulename.TestCase.test_method" - ), + 'args', metavar='test_label', nargs='*', + help='Module paths to test; can be modulename, modulename.TestCase or modulename.TestCase.test_method' ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--failfast", - action="store_true", - help="Tells Django to stop running the test suite after first failed test.", + '--failfast', action='store_true', + help='Tells Django to stop running the test suite after first failed test.', ) parser.add_argument( - "--testrunner", - help="Tells Django to use specified test runner class instead of " - "the one specified by the TEST_RUNNER setting.", + '--testrunner', + help='Tells Django to use specified test runner class instead of ' + 'the one specified by the TEST_RUNNER setting.', ) test_runner_class = get_runner(settings, self.test_runner) - if hasattr(test_runner_class, "add_arguments"): + if hasattr(test_runner_class, 'add_arguments'): test_runner_class.add_arguments(parser) def handle(self, *test_labels, **options): - TestRunner = get_runner(settings, options["testrunner"]) + TestRunner = get_runner(settings, options['testrunner']) - time_keeper = TimeKeeper() if options.get("timing", False) else NullTimeKeeper() - parallel = options.get("parallel") - if parallel == "auto": - options["parallel"] = get_max_test_processes() + time_keeper = TimeKeeper() if options.get('timing', False) else NullTimeKeeper() test_runner = TestRunner(**options) - with time_keeper.timed("Total run"): + with time_keeper.timed('Total run'): failures = test_runner.run_tests(test_labels) time_keeper.print_results() if failures: diff --git a/venv/Lib/site-packages/django/core/management/commands/testserver.py b/venv/Lib/site-packages/django/core/management/commands/testserver.py index caff6c6..ee8709a 100644 --- a/venv/Lib/site-packages/django/core/management/commands/testserver.py +++ b/venv/Lib/site-packages/django/core/management/commands/testserver.py @@ -4,62 +4,51 @@ from django.db import connection class Command(BaseCommand): - help = "Runs a development server with data from the given fixture(s)." + help = 'Runs a development server with data from the given fixture(s).' requires_system_checks = [] def add_arguments(self, parser): parser.add_argument( - "args", - metavar="fixture", - nargs="*", - help="Path(s) to fixtures to load before running the server.", + 'args', metavar='fixture', nargs='*', + help='Path(s) to fixtures to load before running the server.', ) parser.add_argument( - "--noinput", - "--no-input", - action="store_false", - dest="interactive", - help="Tells Django to NOT prompt the user for input of any kind.", + '--noinput', '--no-input', action='store_false', dest='interactive', + help='Tells Django to NOT prompt the user for input of any kind.', ) parser.add_argument( - "--addrport", - default="", - help="Port number or ipaddr:port to run the server on.", + '--addrport', default='', + help='Port number or ipaddr:port to run the server on.', ) parser.add_argument( - "--ipv6", - "-6", - action="store_true", - dest="use_ipv6", - help="Tells Django to use an IPv6 address.", + '--ipv6', '-6', action='store_true', dest='use_ipv6', + help='Tells Django to use an IPv6 address.', ) def handle(self, *fixture_labels, **options): - verbosity = options["verbosity"] - interactive = options["interactive"] + verbosity = options['verbosity'] + interactive = options['interactive'] # Create a test database. - db_name = connection.creation.create_test_db( - verbosity=verbosity, autoclobber=not interactive, serialize=False - ) + db_name = connection.creation.create_test_db(verbosity=verbosity, autoclobber=not interactive, serialize=False) # Import the fixture data into the test database. - call_command("loaddata", *fixture_labels, **{"verbosity": verbosity}) + call_command('loaddata', *fixture_labels, **{'verbosity': verbosity}) # Run the development server. Turn off auto-reloading because it causes # a strange error -- it causes this handle() method to be called # multiple times. shutdown_message = ( - "\nServer stopped.\nNote that the test database, %r, has not been " - "deleted. You can explore it on your own." % db_name + '\nServer stopped.\nNote that the test database, %r, has not been ' + 'deleted. You can explore it on your own.' % db_name ) use_threading = connection.features.test_db_allows_multiple_connections call_command( - "runserver", - addrport=options["addrport"], + 'runserver', + addrport=options['addrport'], shutdown_message=shutdown_message, use_reloader=False, - use_ipv6=options["use_ipv6"], - use_threading=use_threading, + use_ipv6=options['use_ipv6'], + use_threading=use_threading ) diff --git a/venv/Lib/site-packages/django/core/management/sql.py b/venv/Lib/site-packages/django/core/management/sql.py index 2375cc2..1e55a24 100644 --- a/venv/Lib/site-packages/django/core/management/sql.py +++ b/venv/Lib/site-packages/django/core/management/sql.py @@ -1,5 +1,3 @@ -import sys - from django.apps import apps from django.db import models @@ -8,9 +6,7 @@ def sql_flush(style, connection, reset_sequences=True, allow_cascade=False): """ Return a list of the SQL statements used to flush the database. """ - tables = connection.introspection.django_table_names( - only_existing=True, include_views=False - ) + tables = connection.introspection.django_table_names(only_existing=True, include_views=False) return connection.ops.sql_flush( style, tables, @@ -25,17 +21,14 @@ def emit_pre_migrate_signal(verbosity, interactive, db, **kwargs): if app_config.models_module is None: continue if verbosity >= 2: - stdout = kwargs.get("stdout", sys.stdout) - stdout.write( - "Running pre-migrate handlers for application %s" % app_config.label - ) + print("Running pre-migrate handlers for application %s" % app_config.label) models.signals.pre_migrate.send( sender=app_config, app_config=app_config, verbosity=verbosity, interactive=interactive, using=db, - **kwargs, + **kwargs ) @@ -45,15 +38,12 @@ def emit_post_migrate_signal(verbosity, interactive, db, **kwargs): if app_config.models_module is None: continue if verbosity >= 2: - stdout = kwargs.get("stdout", sys.stdout) - stdout.write( - "Running post-migrate handlers for application %s" % app_config.label - ) + print("Running post-migrate handlers for application %s" % app_config.label) models.signals.post_migrate.send( sender=app_config, app_config=app_config, verbosity=verbosity, interactive=interactive, using=db, - **kwargs, + **kwargs ) diff --git a/venv/Lib/site-packages/django/core/management/templates.py b/venv/Lib/site-packages/django/core/management/templates.py index 32ceee8..8dc6068 100644 --- a/venv/Lib/site-packages/django/core/management/templates.py +++ b/venv/Lib/site-packages/django/core/management/templates.py @@ -1,4 +1,3 @@ -import argparse import cgi import mimetypes import os @@ -29,61 +28,38 @@ class TemplateCommand(BaseCommand): :param directory: The directory to which the template should be copied. :param options: The additional variables passed to project or app templates """ - requires_system_checks = [] # The supported URL schemes - url_schemes = ["http", "https", "ftp"] + url_schemes = ['http', 'https', 'ftp'] # Rewrite the following suffixes when determining the target filename. rewrite_template_suffixes = ( # Allow shipping invalid .py files without byte-compilation. - (".py-tpl", ".py"), + ('.py-tpl', '.py'), ) def add_arguments(self, parser): - parser.add_argument("name", help="Name of the application or project.") + parser.add_argument('name', help='Name of the application or project.') + parser.add_argument('directory', nargs='?', help='Optional destination directory') + parser.add_argument('--template', help='The path or URL to load the template from.') parser.add_argument( - "directory", nargs="?", help="Optional destination directory" - ) - parser.add_argument( - "--template", help="The path or URL to load the template from." - ) - parser.add_argument( - "--extension", - "-e", - dest="extensions", - action="append", - default=["py"], + '--extension', '-e', dest='extensions', + action='append', default=['py'], help='The file extension(s) to render (default: "py"). ' - "Separate multiple extensions with commas, or use " - "-e multiple times.", + 'Separate multiple extensions with commas, or use ' + '-e multiple times.' ) parser.add_argument( - "--name", - "-n", - dest="files", - action="append", - default=[], - help="The file name(s) to render. Separate multiple file names " - "with commas, or use -n multiple times.", - ) - parser.add_argument( - "--exclude", - "-x", - action="append", - default=argparse.SUPPRESS, - nargs="?", - const="", - help=( - "The directory name(s) to exclude, in addition to .git and " - "__pycache__. Can be used multiple times." - ), + '--name', '-n', dest='files', + action='append', default=[], + help='The file name(s) to render. Separate multiple file names ' + 'with commas, or use -n multiple times.' ) def handle(self, app_or_project, name, target=None, **options): self.app_or_project = app_or_project - self.a_or_an = "an" if app_or_project == "app" else "a" + self.a_or_an = 'an' if app_or_project == 'app' else 'a' self.paths_to_remove = [] - self.verbosity = options["verbosity"] + self.verbosity = options['verbosity'] self.validate_name(name) @@ -97,56 +73,48 @@ class TemplateCommand(BaseCommand): except OSError as e: raise CommandError(e) else: + if app_or_project == 'app': + self.validate_name(os.path.basename(target), 'directory') top_dir = os.path.abspath(os.path.expanduser(target)) - if app_or_project == "app": - self.validate_name(os.path.basename(top_dir), "directory") if not os.path.exists(top_dir): - raise CommandError( - "Destination directory '%s' does not " - "exist, please create it first." % top_dir - ) + raise CommandError("Destination directory '%s' does not " + "exist, please create it first." % top_dir) - extensions = tuple(handle_extensions(options["extensions"])) + extensions = tuple(handle_extensions(options['extensions'])) extra_files = [] - excluded_directories = [".git", "__pycache__"] - for file in options["files"]: - extra_files.extend(map(lambda x: x.strip(), file.split(","))) - if exclude := options.get("exclude"): - for directory in exclude: - excluded_directories.append(directory.strip()) + for file in options['files']: + extra_files.extend(map(lambda x: x.strip(), file.split(','))) if self.verbosity >= 2: self.stdout.write( - "Rendering %s template files with extensions: %s" - % (app_or_project, ", ".join(extensions)) + 'Rendering %s template files with extensions: %s' + % (app_or_project, ', '.join(extensions)) ) self.stdout.write( - "Rendering %s template files with filenames: %s" - % (app_or_project, ", ".join(extra_files)) + 'Rendering %s template files with filenames: %s' + % (app_or_project, ', '.join(extra_files)) ) - base_name = "%s_name" % app_or_project - base_subdir = "%s_template" % app_or_project - base_directory = "%s_directory" % app_or_project - camel_case_name = "camel_case_%s_name" % app_or_project - camel_case_value = "".join(x for x in name.title() if x != "_") + base_name = '%s_name' % app_or_project + base_subdir = '%s_template' % app_or_project + base_directory = '%s_directory' % app_or_project + camel_case_name = 'camel_case_%s_name' % app_or_project + camel_case_value = ''.join(x for x in name.title() if x != '_') - context = Context( - { - **options, - base_name: name, - base_directory: top_dir, - camel_case_name: camel_case_value, - "docs_version": get_docs_version(), - "django_version": django.__version__, - }, - autoescape=False, - ) + context = Context({ + **options, + base_name: name, + base_directory: top_dir, + camel_case_name: camel_case_value, + 'docs_version': get_docs_version(), + 'django_version': django.__version__, + }, autoescape=False) # Setup a stub settings environment for template rendering if not settings.configured: settings.configure() django.setup() - template_dir = self.handle_template(options["template"], base_subdir) + template_dir = self.handle_template(options['template'], + base_subdir) prefix_length = len(template_dir) + 1 for root, dirs, files in os.walk(template_dir): @@ -158,14 +126,11 @@ class TemplateCommand(BaseCommand): os.makedirs(target_dir, exist_ok=True) for dirname in dirs[:]: - if "exclude" not in options: - if dirname.startswith(".") or dirname == "__pycache__": - dirs.remove(dirname) - elif dirname in excluded_directories: + if dirname.startswith('.') or dirname == '__pycache__': dirs.remove(dirname) for filename in files: - if filename.endswith((".pyo", ".pyc", ".py.class")): + if filename.endswith(('.pyo', '.pyc', '.py.class')): # Ignore some files as they cause various breakages. continue old_path = os.path.join(root, filename) @@ -174,34 +139,31 @@ class TemplateCommand(BaseCommand): ) for old_suffix, new_suffix in self.rewrite_template_suffixes: if new_path.endswith(old_suffix): - new_path = new_path[: -len(old_suffix)] + new_suffix + new_path = new_path[:-len(old_suffix)] + new_suffix break # Only rewrite once if os.path.exists(new_path): raise CommandError( "%s already exists. Overlaying %s %s into an existing " - "directory won't replace conflicting files." - % ( - new_path, - self.a_or_an, - app_or_project, + "directory won't replace conflicting files." % ( + new_path, self.a_or_an, app_or_project, ) ) # Only render the Python files, as we don't want to # accidentally render Django templates files if new_path.endswith(extensions) or filename in extra_files: - with open(old_path, encoding="utf-8") as template_file: + with open(old_path, encoding='utf-8') as template_file: content = template_file.read() template = Engine().from_string(content) content = template.render(context) - with open(new_path, "w", encoding="utf-8") as new_file: + with open(new_path, 'w', encoding='utf-8') as new_file: new_file.write(content) else: shutil.copyfile(old_path, new_path) if self.verbosity >= 2: - self.stdout.write("Creating %s" % new_path) + self.stdout.write('Creating %s' % new_path) try: shutil.copymode(old_path, new_path) self.make_writeable(new_path) @@ -209,13 +171,11 @@ class TemplateCommand(BaseCommand): self.stderr.write( "Notice: Couldn't set permission bits on %s. You're " "probably using an uncommon filesystem setup. No " - "problem." % new_path, - self.style.NOTICE, - ) + "problem." % new_path, self.style.NOTICE) if self.paths_to_remove: if self.verbosity >= 2: - self.stdout.write("Cleaning up temporary files.") + self.stdout.write('Cleaning up temporary files.') for path_to_remove in self.paths_to_remove: if os.path.isfile(path_to_remove): os.remove(path_to_remove) @@ -229,9 +189,9 @@ class TemplateCommand(BaseCommand): directory isn't known. """ if template is None: - return os.path.join(django.__path__[0], "conf", subdir) + return os.path.join(django.__path__[0], 'conf', subdir) else: - if template.startswith("file://"): + if template.startswith('file://'): template = template[7:] expanded_template = os.path.expanduser(template) expanded_template = os.path.normpath(expanded_template) @@ -245,18 +205,15 @@ class TemplateCommand(BaseCommand): if os.path.exists(absolute_path): return self.extract(absolute_path) - raise CommandError( - "couldn't handle %s template %s." % (self.app_or_project, template) - ) + raise CommandError("couldn't handle %s template %s." % + (self.app_or_project, template)) - def validate_name(self, name, name_or_dir="name"): + def validate_name(self, name, name_or_dir='name'): if name is None: - raise CommandError( - "you must provide {an} {app} name".format( - an=self.a_or_an, - app=self.app_or_project, - ) - ) + raise CommandError('you must provide {an} {app} name'.format( + an=self.a_or_an, + app=self.app_or_project, + )) # Check it's a valid directory name. if not name.isidentifier(): raise CommandError( @@ -288,43 +245,41 @@ class TemplateCommand(BaseCommand): """ Download the given URL and return the file name. """ - def cleanup_url(url): - tmp = url.rstrip("/") - filename = tmp.split("/")[-1] - if url.endswith("/"): - display_url = tmp + "/" + tmp = url.rstrip('/') + filename = tmp.split('/')[-1] + if url.endswith('/'): + display_url = tmp + '/' else: display_url = url return filename, display_url - prefix = "django_%s_template_" % self.app_or_project - tempdir = tempfile.mkdtemp(prefix=prefix, suffix="_download") + prefix = 'django_%s_template_' % self.app_or_project + tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_download') self.paths_to_remove.append(tempdir) filename, display_url = cleanup_url(url) if self.verbosity >= 2: - self.stdout.write("Downloading %s" % display_url) + self.stdout.write('Downloading %s' % display_url) try: the_path, info = urlretrieve(url, os.path.join(tempdir, filename)) except OSError as e: - raise CommandError( - "couldn't download URL %s to %s: %s" % (url, filename, e) - ) + raise CommandError("couldn't download URL %s to %s: %s" % + (url, filename, e)) - used_name = the_path.split("/")[-1] + used_name = the_path.split('/')[-1] # Trying to get better name from response headers - content_disposition = info.get("content-disposition") + content_disposition = info.get('content-disposition') if content_disposition: _, params = cgi.parse_header(content_disposition) - guessed_filename = params.get("filename") or used_name + guessed_filename = params.get('filename') or used_name else: guessed_filename = used_name # Falling back to content type guessing ext = self.splitext(guessed_filename)[1] - content_type = info.get("content-type") + content_type = info.get('content-type') if not ext and content_type: ext = mimetypes.guess_extension(content_type) if ext: @@ -345,7 +300,7 @@ class TemplateCommand(BaseCommand): Like os.path.splitext, but takes off .tar, too """ base, ext = posixpath.splitext(the_path) - if base.lower().endswith(".tar"): + if base.lower().endswith('.tar'): ext = base[-4:] + ext base = base[:-4] return base, ext @@ -355,24 +310,23 @@ class TemplateCommand(BaseCommand): Extract the given file to a temporary directory and return the path of the directory with the extracted content. """ - prefix = "django_%s_template_" % self.app_or_project - tempdir = tempfile.mkdtemp(prefix=prefix, suffix="_extract") + prefix = 'django_%s_template_' % self.app_or_project + tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_extract') self.paths_to_remove.append(tempdir) if self.verbosity >= 2: - self.stdout.write("Extracting %s" % filename) + self.stdout.write('Extracting %s' % filename) try: archive.extract(filename, tempdir) return tempdir except (archive.ArchiveException, OSError) as e: - raise CommandError( - "couldn't extract file %s to %s: %s" % (filename, tempdir, e) - ) + raise CommandError("couldn't extract file %s to %s: %s" % + (filename, tempdir, e)) def is_url(self, template): """Return True if the name looks like a URL.""" - if ":" not in template: + if ':' not in template: return False - scheme = template.split(":", 1)[0].lower() + scheme = template.split(':', 1)[0].lower() return scheme in self.url_schemes def make_writeable(self, filename): diff --git a/venv/Lib/site-packages/django/core/management/utils.py b/venv/Lib/site-packages/django/core/management/utils.py index d71f4df..43addf8 100644 --- a/venv/Lib/site-packages/django/core/management/utils.py +++ b/venv/Lib/site-packages/django/core/management/utils.py @@ -10,20 +10,20 @@ from django.utils.encoding import DEFAULT_LOCALE_ENCODING from .base import CommandError, CommandParser -def popen_wrapper(args, stdout_encoding="utf-8"): +def popen_wrapper(args, stdout_encoding='utf-8'): """ Friendly wrapper around Popen. Return stdout output, stderr output, and OS status code. """ try: - p = run(args, stdout=PIPE, stderr=PIPE, close_fds=os.name != "nt") + p = run(args, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt') except OSError as err: - raise CommandError("Error executing %s" % args[0]) from err + raise CommandError('Error executing %s' % args[0]) from err return ( p.stdout.decode(stdout_encoding), - p.stderr.decode(DEFAULT_LOCALE_ENCODING, errors="replace"), - p.returncode, + p.stderr.decode(DEFAULT_LOCALE_ENCODING, errors='replace'), + p.returncode ) @@ -42,25 +42,25 @@ def handle_extensions(extensions): """ ext_list = [] for ext in extensions: - ext_list.extend(ext.replace(" ", "").split(",")) + ext_list.extend(ext.replace(' ', '').split(',')) for i, ext in enumerate(ext_list): - if not ext.startswith("."): - ext_list[i] = ".%s" % ext_list[i] + if not ext.startswith('.'): + ext_list[i] = '.%s' % ext_list[i] return set(ext_list) def find_command(cmd, path=None, pathext=None): if path is None: - path = os.environ.get("PATH", "").split(os.pathsep) + path = os.environ.get('PATH', '').split(os.pathsep) if isinstance(path, str): path = [path] # check if there are funny path extensions for executables, e.g. Windows if pathext is None: - pathext = os.environ.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(os.pathsep) + pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD').split(os.pathsep) # don't use extensions if the command ends with one of them for ext in pathext: if cmd.endswith(ext): - pathext = [""] + pathext = [''] break # check if we find the command on PATH for p in path: @@ -78,7 +78,7 @@ def get_random_secret_key(): """ Return a 50 character random string usable as a SECRET_KEY setting value. """ - chars = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" + chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' return get_random_string(50, chars) @@ -93,11 +93,11 @@ def parse_apps_and_model_labels(labels): models = set() for label in labels: - if "." in label: + if '.' in label: try: model = installed_apps.get_model(label) except LookupError: - raise CommandError("Unknown model: %s" % label) + raise CommandError('Unknown model: %s' % label) models.add(model) else: try: @@ -116,7 +116,7 @@ def get_command_line_option(argv, option): option wasn't passed or if the argument list couldn't be parsed. """ parser = CommandParser(add_help=False, allow_abbrev=False) - parser.add_argument(option, dest="value") + parser.add_argument(option, dest='value') try: options, _ = parser.parse_known_args(argv[2:]) except CommandError: @@ -128,12 +128,12 @@ def get_command_line_option(argv, option): def normalize_path_patterns(patterns): """Normalize an iterable of glob style patterns based on OS.""" patterns = [os.path.normcase(p) for p in patterns] - dir_suffixes = {"%s*" % path_sep for path_sep in {"/", os.sep}} + dir_suffixes = {'%s*' % path_sep for path_sep in {'/', os.sep}} norm_patterns = [] for pattern in patterns: for dir_suffix in dir_suffixes: if pattern.endswith(dir_suffix): - norm_patterns.append(pattern[: -len(dir_suffix)]) + norm_patterns.append(pattern[:-len(dir_suffix)]) break else: norm_patterns.append(pattern) @@ -148,8 +148,6 @@ def is_ignored_path(path, ignore_patterns): path = Path(path) def ignore(pattern): - return fnmatch.fnmatchcase(path.name, pattern) or fnmatch.fnmatchcase( - str(path), pattern - ) + return fnmatch.fnmatchcase(path.name, pattern) or fnmatch.fnmatchcase(str(path), pattern) return any(ignore(pattern) for pattern in normalize_path_patterns(ignore_patterns)) diff --git a/venv/Lib/site-packages/django/core/paginator.py b/venv/Lib/site-packages/django/core/paginator.py index 5684456..7db6491 100644 --- a/venv/Lib/site-packages/django/core/paginator.py +++ b/venv/Lib/site-packages/django/core/paginator.py @@ -27,9 +27,10 @@ class EmptyPage(InvalidPage): class Paginator: # Translators: String used to replace omitted page numbers in elided page # range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. - ELLIPSIS = _("…") + ELLIPSIS = _('…') - def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): + def __init__(self, object_list, per_page, orphans=0, + allow_empty_first_page=True): self.object_list = object_list self._check_object_list_is_ordered() self.per_page = int(per_page) @@ -47,14 +48,14 @@ class Paginator: raise ValueError number = int(number) except (TypeError, ValueError): - raise PageNotAnInteger(_("That page number is not an integer")) + raise PageNotAnInteger(_('That page number is not an integer')) if number < 1: - raise EmptyPage(_("That page number is less than 1")) + raise EmptyPage(_('That page number is less than 1')) if number > self.num_pages: if number == 1 and self.allow_empty_first_page: pass else: - raise EmptyPage(_("That page contains no results")) + raise EmptyPage(_('That page contains no results')) return number def get_page(self, number): @@ -91,7 +92,7 @@ class Paginator: @cached_property def count(self): """Return the total number of objects, across all pages.""" - c = getattr(self.object_list, "count", None) + c = getattr(self.object_list, 'count', None) if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c): return c() return len(self.object_list) @@ -116,20 +117,18 @@ class Paginator: """ Warn if self.object_list is unordered (typically a QuerySet). """ - ordered = getattr(self.object_list, "ordered", None) + ordered = getattr(self.object_list, 'ordered', None) if ordered is not None and not ordered: obj_list_repr = ( - "{} {}".format( - self.object_list.model, self.object_list.__class__.__name__ - ) - if hasattr(self.object_list, "model") - else "{!r}".format(self.object_list) + '{} {}'.format(self.object_list.model, self.object_list.__class__.__name__) + if hasattr(self.object_list, 'model') + else '{!r}'.format(self.object_list) ) warnings.warn( - "Pagination may yield inconsistent results with an unordered " - "object_list: {}.".format(obj_list_repr), + 'Pagination may yield inconsistent results with an unordered ' + 'object_list: {}.'.format(obj_list_repr), UnorderedObjectListWarning, - stacklevel=3, + stacklevel=3 ) def get_elided_page_range(self, number=1, *, on_each_side=3, on_ends=2): @@ -165,13 +164,14 @@ class Paginator: class Page(collections.abc.Sequence): + def __init__(self, object_list, number, paginator): self.object_list = object_list self.number = number self.paginator = paginator def __repr__(self): - return "<Page %s of %s>" % (self.number, self.paginator.num_pages) + return '<Page %s of %s>' % (self.number, self.paginator.num_pages) def __len__(self): return len(self.object_list) @@ -179,7 +179,7 @@ class Page(collections.abc.Sequence): def __getitem__(self, index): if not isinstance(index, (int, slice)): raise TypeError( - "Page indices must be integers or slices, not %s." + 'Page indices must be integers or slices, not %s.' % type(index).__name__ ) # The object_list is converted to a list so that if it was a QuerySet diff --git a/venv/Lib/site-packages/django/core/serializers/__init__.py b/venv/Lib/site-packages/django/core/serializers/__init__.py index 480c54b..793f6dc 100644 --- a/venv/Lib/site-packages/django/core/serializers/__init__.py +++ b/venv/Lib/site-packages/django/core/serializers/__init__.py @@ -42,7 +42,6 @@ class BadSerializer: is an error raised in the process of creating a serializer it will be raised and passed along to the caller when the serializer is used. """ - internal_use_only = False def __init__(self, exception): @@ -73,14 +72,10 @@ def register_serializer(format, serializer_module, serializers=None): except ImportError as exc: bad_serializer = BadSerializer(exc) - module = type( - "BadSerializerModule", - (), - { - "Deserializer": bad_serializer, - "Serializer": bad_serializer, - }, - ) + module = type('BadSerializerModule', (), { + 'Deserializer': bad_serializer, + 'Serializer': bad_serializer, + }) if serializers is None: _serializers[format] = module @@ -158,9 +153,7 @@ def _load_serializers(): register_serializer(format, BUILTIN_SERIALIZERS[format], serializers) if hasattr(settings, "SERIALIZATION_MODULES"): for format in settings.SERIALIZATION_MODULES: - register_serializer( - format, settings.SERIALIZATION_MODULES[format], serializers - ) + register_serializer(format, settings.SERIALIZATION_MODULES[format], serializers) _serializers = serializers @@ -184,8 +177,8 @@ def sort_dependencies(app_list, allow_cycles=False): for model in model_list: models.add(model) # Add any explicitly defined dependencies - if hasattr(model, "natural_key"): - deps = getattr(model.natural_key, "dependencies", []) + if hasattr(model, 'natural_key'): + deps = getattr(model.natural_key, 'dependencies', []) if deps: deps = [apps.get_model(dep) for dep in deps] else: @@ -196,7 +189,7 @@ def sort_dependencies(app_list, allow_cycles=False): for field in model._meta.fields: if field.remote_field: rel_model = field.remote_field.model - if hasattr(rel_model, "natural_key") and rel_model != model: + if hasattr(rel_model, 'natural_key') and rel_model != model: deps.append(rel_model) # Also add a dependency for any simple M2M relation with a model # that defines a natural key. M2M relations with explicit through @@ -204,7 +197,7 @@ def sort_dependencies(app_list, allow_cycles=False): for field in model._meta.many_to_many: if field.remote_field.through._meta.auto_created: rel_model = field.remote_field.model - if hasattr(rel_model, "natural_key") and rel_model != model: + if hasattr(rel_model, 'natural_key') and rel_model != model: deps.append(rel_model) model_dependencies.append((model, deps)) @@ -242,11 +235,9 @@ def sort_dependencies(app_list, allow_cycles=False): else: raise RuntimeError( "Can't resolve dependencies for %s in serialized app list." - % ", ".join( + % ', '.join( model._meta.label - for model, deps in sorted( - skipped, key=lambda obj: obj[0].__name__ - ) + for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__) ), ) model_dependencies = skipped diff --git a/venv/Lib/site-packages/django/core/serializers/base.py b/venv/Lib/site-packages/django/core/serializers/base.py index afdd8b4..3fee789 100644 --- a/venv/Lib/site-packages/django/core/serializers/base.py +++ b/venv/Lib/site-packages/django/core/serializers/base.py @@ -1,7 +1,6 @@ """ Module for abstract serializer/unserializer base classes. """ -import pickle from io import StringIO from django.core.exceptions import ObjectDoesNotExist @@ -10,31 +9,13 @@ from django.db import models DEFER_FIELD = object() -class PickleSerializer: - """ - Simple wrapper around pickle to be used in signing.dumps()/loads() and - cache backends. - """ - - def __init__(self, protocol=None): - self.protocol = pickle.HIGHEST_PROTOCOL if protocol is None else protocol - - def dumps(self, obj): - return pickle.dumps(obj, self.protocol) - - def loads(self, data): - return pickle.loads(data) - - class SerializerDoesNotExist(KeyError): """The requested serializer was not found.""" - pass class SerializationError(Exception): """Something bad happened during serialization.""" - pass @@ -47,15 +28,11 @@ class DeserializationError(Exception): Factory method for creating a deserialization error which has a more explanatory message. """ - return cls( - "%s: (%s:pk=%s) field_value was '%s'" - % (original_exc, model, fk, field_value) - ) + return cls("%s: (%s:pk=%s) field_value was '%s'" % (original_exc, model, fk, field_value)) class M2MDeserializationError(Exception): """Something bad happened during deserialization of a ManyToManyField.""" - def __init__(self, original_exc, pk): self.original_exc = original_exc self.pk = pk @@ -77,12 +54,10 @@ class ProgressBar: if self.prev_done >= done: return self.prev_done = done - cr = "" if self.total_count == 1 else "\r" - self.output.write( - cr + "[" + "." * done + " " * (self.progress_width - done) + "]" - ) + cr = '' if self.total_count == 1 else '\r' + self.output.write(cr + '[' + '.' * done + ' ' * (self.progress_width - done) + ']') if done == self.progress_width: - self.output.write("\n") + self.output.write('\n') self.output.flush() @@ -97,18 +72,8 @@ class Serializer: progress_class = ProgressBar stream_class = StringIO - def serialize( - self, - queryset, - *, - stream=None, - fields=None, - use_natural_foreign_keys=False, - use_natural_primary_keys=False, - progress_output=None, - object_count=0, - **options, - ): + def serialize(self, queryset, *, stream=None, fields=None, use_natural_foreign_keys=False, + use_natural_primary_keys=False, progress_output=None, object_count=0, **options): """ Serialize a queryset. """ @@ -132,31 +97,20 @@ class Serializer: # be serialized, otherwise deserialization isn't possible. if self.use_natural_primary_keys: pk = concrete_model._meta.pk - pk_parent = ( - pk if pk.remote_field and pk.remote_field.parent_link else None - ) + pk_parent = pk if pk.remote_field and pk.remote_field.parent_link else None else: pk_parent = None for field in concrete_model._meta.local_fields: if field.serialize or field is pk_parent: if field.remote_field is None: - if ( - self.selected_fields is None - or field.attname in self.selected_fields - ): + if self.selected_fields is None or field.attname in self.selected_fields: self.handle_field(obj, field) else: - if ( - self.selected_fields is None - or field.attname[:-3] in self.selected_fields - ): + if self.selected_fields is None or field.attname[:-3] in self.selected_fields: self.handle_fk_field(obj, field) for field in concrete_model._meta.local_many_to_many: if field.serialize: - if ( - self.selected_fields is None - or field.attname in self.selected_fields - ): + if self.selected_fields is None or field.attname in self.selected_fields: self.handle_m2m_field(obj, field) self.end_object(obj) progress_bar.update(count) @@ -168,9 +122,7 @@ class Serializer: """ Called when serializing of the queryset starts. """ - raise NotImplementedError( - "subclasses of Serializer must provide a start_serialization() method" - ) + raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method') def end_serialization(self): """ @@ -182,9 +134,7 @@ class Serializer: """ Called when serializing of an object starts. """ - raise NotImplementedError( - "subclasses of Serializer must provide a start_object() method" - ) + raise NotImplementedError('subclasses of Serializer must provide a start_object() method') def end_object(self, obj): """ @@ -196,32 +146,26 @@ class Serializer: """ Called to handle each individual (non-relational) field on an object. """ - raise NotImplementedError( - "subclasses of Serializer must provide a handle_field() method" - ) + raise NotImplementedError('subclasses of Serializer must provide a handle_field() method') def handle_fk_field(self, obj, field): """ Called to handle a ForeignKey field. """ - raise NotImplementedError( - "subclasses of Serializer must provide a handle_fk_field() method" - ) + raise NotImplementedError('subclasses of Serializer must provide a handle_fk_field() method') def handle_m2m_field(self, obj, field): """ Called to handle a ManyToManyField. """ - raise NotImplementedError( - "subclasses of Serializer must provide a handle_m2m_field() method" - ) + raise NotImplementedError('subclasses of Serializer must provide a handle_m2m_field() method') def getvalue(self): """ Return the fully serialized queryset (or None if the output stream is not seekable). """ - if callable(getattr(self.stream, "getvalue", None)): + if callable(getattr(self.stream, 'getvalue', None)): return self.stream.getvalue() @@ -245,9 +189,7 @@ class Deserializer: def __next__(self): """Iteration interface -- return the next item in the stream""" - raise NotImplementedError( - "subclasses of Deserializer must provide a __next__() method" - ) + raise NotImplementedError('subclasses of Deserializer must provide a __next__() method') class DeserializedObject: @@ -291,26 +233,18 @@ class DeserializedObject: self.m2m_data = {} for field, field_value in self.deferred_fields.items(): opts = self.object._meta - label = opts.app_label + "." + opts.model_name + label = opts.app_label + '.' + opts.model_name if isinstance(field.remote_field, models.ManyToManyRel): try: - values = deserialize_m2m_values( - field, field_value, using, handle_forward_references=False - ) + values = deserialize_m2m_values(field, field_value, using, handle_forward_references=False) except M2MDeserializationError as e: - raise DeserializationError.WithData( - e.original_exc, label, self.object.pk, e.pk - ) + raise DeserializationError.WithData(e.original_exc, label, self.object.pk, e.pk) self.m2m_data[field.name] = values elif isinstance(field.remote_field, models.ManyToOneRel): try: - value = deserialize_fk_value( - field, field_value, using, handle_forward_references=False - ) + value = deserialize_fk_value(field, field_value, using, handle_forward_references=False) except Exception as e: - raise DeserializationError.WithData( - e, label, self.object.pk, field_value - ) + raise DeserializationError.WithData(e, label, self.object.pk, field_value) setattr(self.object, field.attname, value) self.save() @@ -324,11 +258,8 @@ def build_instance(Model, data, db): """ default_manager = Model._meta.default_manager pk = data.get(Model._meta.pk.attname) - if ( - pk is None - and hasattr(default_manager, "get_by_natural_key") - and hasattr(Model, "natural_key") - ): + if (pk is None and hasattr(default_manager, 'get_by_natural_key') and + hasattr(Model, 'natural_key')): natural_key = Model(**data).natural_key() try: data[Model._meta.pk.attname] = Model._meta.pk.to_python( @@ -341,20 +272,13 @@ def build_instance(Model, data, db): def deserialize_m2m_values(field, field_value, using, handle_forward_references): model = field.remote_field.model - if hasattr(model._default_manager, "get_by_natural_key"): - + if hasattr(model._default_manager, 'get_by_natural_key'): def m2m_convert(value): - if hasattr(value, "__iter__") and not isinstance(value, str): - return ( - model._default_manager.db_manager(using) - .get_by_natural_key(*value) - .pk - ) + if hasattr(value, '__iter__') and not isinstance(value, str): + return model._default_manager.db_manager(using).get_by_natural_key(*value).pk else: return model._meta.pk.to_python(value) - else: - def m2m_convert(v): return model._meta.pk.to_python(v) @@ -380,11 +304,8 @@ def deserialize_fk_value(field, field_value, using, handle_forward_references): model = field.remote_field.model default_manager = model._default_manager field_name = field.remote_field.field_name - if ( - hasattr(default_manager, "get_by_natural_key") - and hasattr(field_value, "__iter__") - and not isinstance(field_value, str) - ): + if (hasattr(default_manager, 'get_by_natural_key') and + hasattr(field_value, '__iter__') and not isinstance(field_value, str)): try: obj = default_manager.db_manager(using).get_by_natural_key(*field_value) except ObjectDoesNotExist: diff --git a/venv/Lib/site-packages/django/core/serializers/json.py b/venv/Lib/site-packages/django/core/serializers/json.py index 59d7318..886e8f8 100644 --- a/venv/Lib/site-packages/django/core/serializers/json.py +++ b/venv/Lib/site-packages/django/core/serializers/json.py @@ -8,8 +8,9 @@ import json import uuid from django.core.serializers.base import DeserializationError -from django.core.serializers.python import Deserializer as PythonDeserializer -from django.core.serializers.python import Serializer as PythonSerializer +from django.core.serializers.python import ( + Deserializer as PythonDeserializer, Serializer as PythonSerializer, +) from django.utils.duration import duration_iso_string from django.utils.functional import Promise from django.utils.timezone import is_aware @@ -17,19 +18,18 @@ from django.utils.timezone import is_aware class Serializer(PythonSerializer): """Convert a queryset to JSON.""" - internal_use_only = False def _init_options(self): self._current = None self.json_kwargs = self.options.copy() - self.json_kwargs.pop("stream", None) - self.json_kwargs.pop("fields", None) - if self.options.get("indent"): + self.json_kwargs.pop('stream', None) + self.json_kwargs.pop('fields', None) + if self.options.get('indent'): # Prevent trailing spaces - self.json_kwargs["separators"] = (",", ": ") - self.json_kwargs.setdefault("cls", DjangoJSONEncoder) - self.json_kwargs.setdefault("ensure_ascii", False) + self.json_kwargs['separators'] = (',', ': ') + self.json_kwargs.setdefault('cls', DjangoJSONEncoder) + self.json_kwargs.setdefault('ensure_ascii', False) def start_serialization(self): self._init_options() @@ -79,15 +79,14 @@ class DjangoJSONEncoder(json.JSONEncoder): JSONEncoder subclass that knows how to encode date/time, decimal types, and UUIDs. """ - def default(self, o): # See "Date Time String Format" in the ECMA-262 specification. if isinstance(o, datetime.datetime): r = o.isoformat() if o.microsecond: r = r[:23] + r[26:] - if r.endswith("+00:00"): - r = r[:-6] + "Z" + if r.endswith('+00:00'): + r = r[:-6] + 'Z' return r elif isinstance(o, datetime.date): return o.isoformat() diff --git a/venv/Lib/site-packages/django/core/serializers/jsonl.py b/venv/Lib/site-packages/django/core/serializers/jsonl.py index c264c2c..4b3e46e 100644 --- a/venv/Lib/site-packages/django/core/serializers/jsonl.py +++ b/venv/Lib/site-packages/django/core/serializers/jsonl.py @@ -6,24 +6,24 @@ import json from django.core.serializers.base import DeserializationError from django.core.serializers.json import DjangoJSONEncoder -from django.core.serializers.python import Deserializer as PythonDeserializer -from django.core.serializers.python import Serializer as PythonSerializer +from django.core.serializers.python import ( + Deserializer as PythonDeserializer, Serializer as PythonSerializer, +) class Serializer(PythonSerializer): """Convert a queryset to JSON Lines.""" - internal_use_only = False def _init_options(self): self._current = None self.json_kwargs = self.options.copy() - self.json_kwargs.pop("stream", None) - self.json_kwargs.pop("fields", None) - self.json_kwargs.pop("indent", None) - self.json_kwargs["separators"] = (",", ": ") - self.json_kwargs.setdefault("cls", DjangoJSONEncoder) - self.json_kwargs.setdefault("ensure_ascii", False) + self.json_kwargs.pop('stream', None) + self.json_kwargs.pop('fields', None) + self.json_kwargs.pop('indent', None) + self.json_kwargs['separators'] = (',', ': ') + self.json_kwargs.setdefault('cls', DjangoJSONEncoder) + self.json_kwargs.setdefault('ensure_ascii', False) def start_serialization(self): self._init_options() diff --git a/venv/Lib/site-packages/django/core/serializers/python.py b/venv/Lib/site-packages/django/core/serializers/python.py index a3918bf..0ceb676 100644 --- a/venv/Lib/site-packages/django/core/serializers/python.py +++ b/venv/Lib/site-packages/django/core/serializers/python.py @@ -32,10 +32,10 @@ class Serializer(base.Serializer): self._current = None def get_dump_object(self, obj): - data = {"model": str(obj._meta)} - if not self.use_natural_primary_keys or not hasattr(obj, "natural_key"): + data = {'model': str(obj._meta)} + if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'): data["pk"] = self._value_from_field(obj, obj._meta.pk) - data["fields"] = self._current + data['fields'] = self._current return data def _value_from_field(self, obj, field): @@ -49,9 +49,7 @@ class Serializer(base.Serializer): self._current[field.name] = self._value_from_field(obj, field) def handle_fk_field(self, obj, field): - if self.use_natural_foreign_keys and hasattr( - field.remote_field.model, "natural_key" - ): + if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): related = getattr(obj, field.name) if related: value = related.natural_key() @@ -63,19 +61,13 @@ class Serializer(base.Serializer): def handle_m2m_field(self, obj, field): if field.remote_field.through._meta.auto_created: - if self.use_natural_foreign_keys and hasattr( - field.remote_field.model, "natural_key" - ): - + if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): def m2m_value(value): return value.natural_key() - else: - def m2m_value(value): return self._value_from_field(value, value._meta.pk) - - m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get( + m2m_iter = getattr(obj, '_prefetched_objects_cache', {}).get( field.name, getattr(obj, field.name).iterator(), ) @@ -85,16 +77,14 @@ class Serializer(base.Serializer): return self.objects -def Deserializer( - object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options -): +def Deserializer(object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options): """ Deserialize simple Python objects back into Django ORM instances. It's expected that you pass the Python objects themselves (instead of a stream or a string) to the constructor """ - handle_forward_references = options.pop("handle_forward_references", False) + handle_forward_references = options.pop('handle_forward_references', False) field_names_cache = {} # Model: <list of field_names> for d in object_list: @@ -107,13 +97,11 @@ def Deserializer( else: raise data = {} - if "pk" in d: + if 'pk' in d: try: - data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get("pk")) + data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get('pk')) except Exception as e: - raise base.DeserializationError.WithData( - e, d["model"], d.get("pk"), None - ) + raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), None) m2m_data = {} deferred_fields = {} @@ -131,33 +119,21 @@ def Deserializer( field = Model._meta.get_field(field_name) # Handle M2M relations - if field.remote_field and isinstance( - field.remote_field, models.ManyToManyRel - ): + if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel): try: - values = base.deserialize_m2m_values( - field, field_value, using, handle_forward_references - ) + values = base.deserialize_m2m_values(field, field_value, using, handle_forward_references) except base.M2MDeserializationError as e: - raise base.DeserializationError.WithData( - e.original_exc, d["model"], d.get("pk"), e.pk - ) + raise base.DeserializationError.WithData(e.original_exc, d['model'], d.get('pk'), e.pk) if values == base.DEFER_FIELD: deferred_fields[field] = field_value else: m2m_data[field.name] = values # Handle FK fields - elif field.remote_field and isinstance( - field.remote_field, models.ManyToOneRel - ): + elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel): try: - value = base.deserialize_fk_value( - field, field_value, using, handle_forward_references - ) + value = base.deserialize_fk_value(field, field_value, using, handle_forward_references) except Exception as e: - raise base.DeserializationError.WithData( - e, d["model"], d.get("pk"), field_value - ) + raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value) if value == base.DEFER_FIELD: deferred_fields[field] = field_value else: @@ -167,9 +143,7 @@ def Deserializer( try: data[field.name] = field.to_python(field_value) except Exception as e: - raise base.DeserializationError.WithData( - e, d["model"], d.get("pk"), field_value - ) + raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value) obj = base.build_instance(Model, data, using) yield base.DeserializedObject(obj, m2m_data, deferred_fields) @@ -180,6 +154,4 @@ def _get_model(model_identifier): try: return apps.get_model(model_identifier) except (LookupError, TypeError): - raise base.DeserializationError( - "Invalid model identifier: '%s'" % model_identifier - ) + raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier) diff --git a/venv/Lib/site-packages/django/core/serializers/pyyaml.py b/venv/Lib/site-packages/django/core/serializers/pyyaml.py index 9a20b66..9719f6e 100644 --- a/venv/Lib/site-packages/django/core/serializers/pyyaml.py +++ b/venv/Lib/site-packages/django/core/serializers/pyyaml.py @@ -11,30 +11,28 @@ from io import StringIO import yaml from django.core.serializers.base import DeserializationError -from django.core.serializers.python import Deserializer as PythonDeserializer -from django.core.serializers.python import Serializer as PythonSerializer +from django.core.serializers.python import ( + Deserializer as PythonDeserializer, Serializer as PythonSerializer, +) from django.db import models # Use the C (faster) implementation if possible try: - from yaml import CSafeDumper as SafeDumper - from yaml import CSafeLoader as SafeLoader + from yaml import CSafeDumper as SafeDumper, CSafeLoader as SafeLoader except ImportError: from yaml import SafeDumper, SafeLoader class DjangoSafeDumper(SafeDumper): def represent_decimal(self, data): - return self.represent_scalar("tag:yaml.org,2002:str", str(data)) + return self.represent_scalar('tag:yaml.org,2002:str', str(data)) def represent_ordered_dict(self, data): - return self.represent_mapping("tag:yaml.org,2002:map", data.items()) + return self.represent_mapping('tag:yaml.org,2002:map', data.items()) DjangoSafeDumper.add_representer(decimal.Decimal, DjangoSafeDumper.represent_decimal) -DjangoSafeDumper.add_representer( - collections.OrderedDict, DjangoSafeDumper.represent_ordered_dict -) +DjangoSafeDumper.add_representer(collections.OrderedDict, DjangoSafeDumper.represent_ordered_dict) # Workaround to represent dictionaries in insertion order. # See https://github.com/yaml/pyyaml/pull/143. DjangoSafeDumper.add_representer(dict, DjangoSafeDumper.represent_ordered_dict) @@ -58,7 +56,7 @@ class Serializer(PythonSerializer): super().handle_field(obj, field) def end_serialization(self): - self.options.setdefault("allow_unicode", True) + self.options.setdefault('allow_unicode', True) yaml.dump(self.objects, self.stream, Dumper=DjangoSafeDumper, **self.options) def getvalue(self): diff --git a/venv/Lib/site-packages/django/core/serializers/xml_serializer.py b/venv/Lib/site-packages/django/core/serializers/xml_serializer.py index 8d3918c..88bfa59 100644 --- a/venv/Lib/site-packages/django/core/serializers/xml_serializer.py +++ b/venv/Lib/site-packages/django/core/serializers/xml_serializer.py @@ -11,25 +11,23 @@ from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.core.serializers import base from django.db import DEFAULT_DB_ALIAS, models -from django.utils.xmlutils import SimplerXMLGenerator, UnserializableContentError +from django.utils.xmlutils import ( + SimplerXMLGenerator, UnserializableContentError, +) class Serializer(base.Serializer): """Serialize a QuerySet to XML.""" def indent(self, level): - if self.options.get("indent") is not None: - self.xml.ignorableWhitespace( - "\n" + " " * self.options.get("indent") * level - ) + if self.options.get('indent') is not None: + self.xml.ignorableWhitespace('\n' + ' ' * self.options.get('indent') * level) def start_serialization(self): """ Start serialization -- open the XML document and the root element. """ - self.xml = SimplerXMLGenerator( - self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET) - ) + self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET)) self.xml.startDocument() self.xml.startElement("django-objects", {"version": "1.0"}) @@ -46,16 +44,14 @@ class Serializer(base.Serializer): Called as each object is handled. """ if not hasattr(obj, "_meta"): - raise base.SerializationError( - "Non-model object (%s) encountered during serialization" % type(obj) - ) + raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) self.indent(1) - attrs = {"model": str(obj._meta)} - if not self.use_natural_primary_keys or not hasattr(obj, "natural_key"): + attrs = {'model': str(obj._meta)} + if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'): obj_pk = obj.pk if obj_pk is not None: - attrs["pk"] = str(obj_pk) + attrs['pk'] = str(obj_pk) self.xml.startElement("object", attrs) @@ -72,28 +68,23 @@ class Serializer(base.Serializer): ManyToManyFields). """ self.indent(2) - self.xml.startElement( - "field", - { - "name": field.name, - "type": field.get_internal_type(), - }, - ) + self.xml.startElement('field', { + 'name': field.name, + 'type': field.get_internal_type(), + }) # Get a "string version" of the object's data. if getattr(obj, field.name) is not None: value = field.value_to_string(obj) - if field.get_internal_type() == "JSONField": + if field.get_internal_type() == 'JSONField': # Dump value since JSONField.value_to_string() doesn't output # strings. value = json.dumps(value, cls=field.encoder) try: self.xml.characters(value) except UnserializableContentError: - raise ValueError( - "%s.%s (pk:%s) contains unserializable characters" - % (obj.__class__.__name__, field.name, obj.pk) - ) + raise ValueError("%s.%s (pk:%s) contains unserializable characters" % ( + obj.__class__.__name__, field.name, obj.pk)) else: self.xml.addQuickElement("None") @@ -107,9 +98,7 @@ class Serializer(base.Serializer): self._start_relational_field(field) related_att = getattr(obj, field.get_attname()) if related_att is not None: - if self.use_natural_foreign_keys and hasattr( - field.remote_field.model, "natural_key" - ): + if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): related = getattr(obj, field.name) # If related object has a natural key, use it related = related.natural_key() @@ -132,9 +121,7 @@ class Serializer(base.Serializer): """ if field.remote_field.through._meta.auto_created: self._start_relational_field(field) - if self.use_natural_foreign_keys and hasattr( - field.remote_field.model, "natural_key" - ): + if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): # If the objects in the m2m have a natural key, use it def handle_m2m(value): natural = value.natural_key() @@ -145,13 +132,12 @@ class Serializer(base.Serializer): self.xml.characters(str(key_value)) self.xml.endElement("natural") self.xml.endElement("object") - else: - def handle_m2m(value): - self.xml.addQuickElement("object", attrs={"pk": str(value.pk)}) - - m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get( + self.xml.addQuickElement("object", attrs={ + 'pk': str(value.pk) + }) + m2m_iter = getattr(obj, '_prefetched_objects_cache', {}).get( field.name, getattr(obj, field.name).iterator(), ) @@ -163,29 +149,19 @@ class Serializer(base.Serializer): def _start_relational_field(self, field): """Output the <field> element for relational fields.""" self.indent(2) - self.xml.startElement( - "field", - { - "name": field.name, - "rel": field.remote_field.__class__.__name__, - "to": str(field.remote_field.model._meta), - }, - ) + self.xml.startElement('field', { + 'name': field.name, + 'rel': field.remote_field.__class__.__name__, + 'to': str(field.remote_field.model._meta), + }) class Deserializer(base.Deserializer): """Deserialize XML.""" - def __init__( - self, - stream_or_string, - *, - using=DEFAULT_DB_ALIAS, - ignorenonexistent=False, - **options, - ): + def __init__(self, stream_or_string, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options): super().__init__(stream_or_string, **options) - self.handle_forward_references = options.pop("handle_forward_references", False) + self.handle_forward_references = options.pop('handle_forward_references', False) self.event_stream = pulldom.parse(self.stream, self._make_parser()) self.db = using self.ignore = ignorenonexistent @@ -209,10 +185,9 @@ class Deserializer(base.Deserializer): # Start building a data dictionary from the object. data = {} - if node.hasAttribute("pk"): + if node.hasAttribute('pk'): data[Model._meta.pk.attname] = Model._meta.pk.to_python( - node.getAttribute("pk") - ) + node.getAttribute('pk')) # Also start building a dict of m2m data (this is saved as # {m2m_accessor_attribute : [list_of_related_objects]}) @@ -226,9 +201,7 @@ class Deserializer(base.Deserializer): # sensing a pattern here?) field_name = field_node.getAttribute("name") if not field_name: - raise base.DeserializationError( - "<field> node is missing the 'name' attribute" - ) + raise base.DeserializationError("<field> node is missing the 'name' attribute") # Get the field from the Model. This will raise a # FieldDoesNotExist if, well, the field doesn't exist, which will @@ -238,38 +211,34 @@ class Deserializer(base.Deserializer): field = Model._meta.get_field(field_name) # As is usually the case, relation fields get the special treatment. - if field.remote_field and isinstance( - field.remote_field, models.ManyToManyRel - ): + if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel): value = self._handle_m2m_field_node(field_node, field) if value == base.DEFER_FIELD: deferred_fields[field] = [ [ getInnerText(nat_node).strip() - for nat_node in obj_node.getElementsByTagName("natural") + for nat_node in obj_node.getElementsByTagName('natural') ] - for obj_node in field_node.getElementsByTagName("object") + for obj_node in field_node.getElementsByTagName('object') ] else: m2m_data[field.name] = value - elif field.remote_field and isinstance( - field.remote_field, models.ManyToOneRel - ): + elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel): value = self._handle_fk_field_node(field_node, field) if value == base.DEFER_FIELD: deferred_fields[field] = [ getInnerText(k).strip() - for k in field_node.getElementsByTagName("natural") + for k in field_node.getElementsByTagName('natural') ] else: data[field.attname] = value else: - if field_node.getElementsByTagName("None"): + if field_node.getElementsByTagName('None'): value = None else: value = field.to_python(getInnerText(field_node).strip()) # Load value since JSONField.to_python() outputs strings. - if field.get_internal_type() == "JSONField": + if field.get_internal_type() == 'JSONField': value = json.loads(value, cls=field.decoder) data[field.name] = value @@ -283,19 +252,17 @@ class Deserializer(base.Deserializer): Handle a <field> node for a ForeignKey """ # Check if there is a child node named 'None', returning None if so. - if node.getElementsByTagName("None"): + if node.getElementsByTagName('None'): return None else: model = field.remote_field.model - if hasattr(model._default_manager, "get_by_natural_key"): - keys = node.getElementsByTagName("natural") + if hasattr(model._default_manager, 'get_by_natural_key'): + keys = node.getElementsByTagName('natural') if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] try: - obj = model._default_manager.db_manager( - self.db - ).get_by_natural_key(*field_value) + obj = model._default_manager.db_manager(self.db).get_by_natural_key(*field_value) except ObjectDoesNotExist: if self.handle_forward_references: return base.DEFER_FIELD @@ -309,15 +276,11 @@ class Deserializer(base.Deserializer): else: # Otherwise, treat like a normal PK field_value = getInnerText(node).strip() - obj_pk = model._meta.get_field( - field.remote_field.field_name - ).to_python(field_value) + obj_pk = model._meta.get_field(field.remote_field.field_name).to_python(field_value) return obj_pk else: field_value = getInnerText(node).strip() - return model._meta.get_field(field.remote_field.field_name).to_python( - field_value - ) + return model._meta.get_field(field.remote_field.field_name).to_python(field_value) def _handle_m2m_field_node(self, node, field): """ @@ -325,31 +288,23 @@ class Deserializer(base.Deserializer): """ model = field.remote_field.model default_manager = model._default_manager - if hasattr(default_manager, "get_by_natural_key"): - + if hasattr(default_manager, 'get_by_natural_key'): def m2m_convert(n): - keys = n.getElementsByTagName("natural") + keys = n.getElementsByTagName('natural') if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] - obj_pk = ( - default_manager.db_manager(self.db) - .get_by_natural_key(*field_value) - .pk - ) + obj_pk = default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk else: # Otherwise, treat like a normal PK value. - obj_pk = model._meta.pk.to_python(n.getAttribute("pk")) + obj_pk = model._meta.pk.to_python(n.getAttribute('pk')) return obj_pk - else: - def m2m_convert(n): - return model._meta.pk.to_python(n.getAttribute("pk")) - + return model._meta.pk.to_python(n.getAttribute('pk')) values = [] try: - for c in node.getElementsByTagName("object"): + for c in node.getElementsByTagName('object'): values.append(m2m_convert(c)) except Exception as e: if isinstance(e, ObjectDoesNotExist) and self.handle_forward_references: @@ -368,15 +323,13 @@ class Deserializer(base.Deserializer): if not model_identifier: raise base.DeserializationError( "<%s> node is missing the required '%s' attribute" - % (node.nodeName, attr) - ) + % (node.nodeName, attr)) try: return apps.get_model(model_identifier) except (LookupError, TypeError): raise base.DeserializationError( "<%s> node has invalid model identifier: '%s'" - % (node.nodeName, model_identifier) - ) + % (node.nodeName, model_identifier)) def getInnerText(node): @@ -384,10 +337,7 @@ def getInnerText(node): # inspired by https://mail.python.org/pipermail/xml-sig/2005-March/011022.html inner_text = [] for child in node.childNodes: - if ( - child.nodeType == child.TEXT_NODE - or child.nodeType == child.CDATA_SECTION_NODE - ): + if child.nodeType == child.TEXT_NODE or child.nodeType == child.CDATA_SECTION_NODE: inner_text.append(child.data) elif child.nodeType == child.ELEMENT_NODE: inner_text.extend(getInnerText(child)) @@ -405,7 +355,6 @@ class DefusedExpatParser(_ExpatParser): Forbid DTDs, external entity references """ - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setFeature(handler.feature_external_ges, False) @@ -414,9 +363,8 @@ class DefusedExpatParser(_ExpatParser): def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): raise DTDForbidden(name, sysid, pubid) - def entity_decl( - self, name, is_parameter_entity, value, base, sysid, pubid, notation_name - ): + def entity_decl(self, name, is_parameter_entity, value, base, + sysid, pubid, notation_name): raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): @@ -437,14 +385,12 @@ class DefusedExpatParser(_ExpatParser): class DefusedXmlException(ValueError): """Base exception.""" - def __repr__(self): return str(self) class DTDForbidden(DefusedXmlException): """Document type definition is forbidden.""" - def __init__(self, name, sysid, pubid): super().__init__() self.name = name @@ -458,7 +404,6 @@ class DTDForbidden(DefusedXmlException): class EntitiesForbidden(DefusedXmlException): """Entity definition is forbidden.""" - def __init__(self, name, value, base, sysid, pubid, notation_name): super().__init__() self.name = name @@ -475,7 +420,6 @@ class EntitiesForbidden(DefusedXmlException): class ExternalReferenceForbidden(DefusedXmlException): """Resolving an external reference is forbidden.""" - def __init__(self, context, base, sysid, pubid): super().__init__() self.context = context diff --git a/venv/Lib/site-packages/django/core/servers/basehttp.py b/venv/Lib/site-packages/django/core/servers/basehttp.py index 440e7bc..c5b020e 100644 --- a/venv/Lib/site-packages/django/core/servers/basehttp.py +++ b/venv/Lib/site-packages/django/core/servers/basehttp.py @@ -16,12 +16,11 @@ from wsgiref import simple_server from django.core.exceptions import ImproperlyConfigured from django.core.handlers.wsgi import LimitedStream from django.core.wsgi import get_wsgi_application -from django.db import connections from django.utils.module_loading import import_string -__all__ = ("WSGIServer", "WSGIRequestHandler") +__all__ = ('WSGIServer', 'WSGIRequestHandler') -logger = logging.getLogger("django.server") +logger = logging.getLogger('django.server') def get_internal_wsgi_application(): @@ -38,8 +37,7 @@ def get_internal_wsgi_application(): whatever ``django.core.wsgi.get_wsgi_application`` returns. """ from django.conf import settings - - app_path = getattr(settings, "WSGI_APPLICATION") + app_path = getattr(settings, 'WSGI_APPLICATION') if app_path is None: return get_wsgi_application() @@ -54,14 +52,11 @@ def get_internal_wsgi_application(): def is_broken_pipe_error(): exc_type, _, _ = sys.exc_info() - return issubclass( - exc_type, - ( - BrokenPipeError, - ConnectionAbortedError, - ConnectionResetError, - ), - ) + return issubclass(exc_type, ( + BrokenPipeError, + ConnectionAbortedError, + ConnectionResetError, + )) class WSGIServer(simple_server.WSGIServer): @@ -84,34 +79,11 @@ class WSGIServer(simple_server.WSGIServer): class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer): """A threaded version of the WSGIServer""" - daemon_threads = True - def __init__(self, *args, connections_override=None, **kwargs): - super().__init__(*args, **kwargs) - self.connections_override = connections_override - - # socketserver.ThreadingMixIn.process_request() passes this method as - # the target to a new Thread object. - def process_request_thread(self, request, client_address): - if self.connections_override: - # Override this thread's database connections with the ones - # provided by the parent thread. - for alias, conn in self.connections_override.items(): - connections[alias] = conn - super().process_request_thread(request, client_address) - - def _close_connections(self): - # Used for mocking in tests. - connections.close_all() - - def close_request(self, request): - self._close_connections() - super().close_request(request) - class ServerHandler(simple_server.ServerHandler): - http_version = "1.1" + http_version = '1.1' def __init__(self, stdin, stdout, stderr, environ, **kwargs): """ @@ -121,35 +93,38 @@ class ServerHandler(simple_server.ServerHandler): This fix applies only for testserver/runserver. """ try: - content_length = int(environ.get("CONTENT_LENGTH")) + content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 - super().__init__( - LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs - ) + super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs) def cleanup_headers(self): super().cleanup_headers() # HTTP/1.1 requires support for persistent connections. Send 'close' if # the content length is unknown to prevent clients from reusing the # connection. - if "Content-Length" not in self.headers: - self.headers["Connection"] = "close" + if 'Content-Length' not in self.headers: + self.headers['Connection'] = 'close' # Persistent connections require threading server. elif not isinstance(self.request_handler.server, socketserver.ThreadingMixIn): - self.headers["Connection"] = "close" + self.headers['Connection'] = 'close' # Mark the connection for closing if it's set as such above or if the # application sent the header. - if self.headers.get("Connection") == "close": + if self.headers.get('Connection') == 'close': self.request_handler.close_connection = True def close(self): self.get_stdin()._read_limited() super().close() + def handle_error(self): + # Ignore broken pipe errors, otherwise pass on + if not is_broken_pipe_error(): + super().handle_error() + class WSGIRequestHandler(simple_server.WSGIRequestHandler): - protocol_version = "HTTP/1.1" + protocol_version = 'HTTP/1.1' def address_string(self): # Short-circuit parent method to not call socket.getfqdn @@ -157,23 +132,22 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler): def log_message(self, format, *args): extra = { - "request": self.request, - "server_time": self.log_date_time_string(), + 'request': self.request, + 'server_time': self.log_date_time_string(), } - if args[1][0] == "4": + if args[1][0] == '4': # 0x16 = Handshake, 0x03 = SSL 3.0 or TLS 1.x - if args[0].startswith("\x16\x03"): - extra["status_code"] = 500 + if args[0].startswith('\x16\x03'): + extra['status_code'] = 500 logger.error( "You're accessing the development server over HTTPS, but " - "it only supports HTTP.\n", - extra=extra, + "it only supports HTTP.\n", extra=extra, ) return if args[1].isdigit() and len(args[1]) == 3: status_code = int(args[1]) - extra["status_code"] = status_code + extra['status_code'] = status_code if status_code >= 500: level = logger.error @@ -192,7 +166,7 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler): # between underscores and dashes both normalized to underscores in WSGI # env vars. Nginx and Apache 2.4+ both do this as well. for k in self.headers: - if "_" in k: + if '_' in k: del self.headers[k] return super().get_environ() @@ -211,9 +185,9 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler): """Copy of WSGIRequestHandler.handle() but with different ServerHandler""" self.raw_requestline = self.rfile.readline(65537) if len(self.raw_requestline) > 65536: - self.requestline = "" - self.request_version = "" - self.command = "" + self.requestline = '' + self.request_version = '' + self.command = '' self.send_error(414) return @@ -223,14 +197,14 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler): handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) - handler.request_handler = self # backpointer for logging & connection closing + handler.request_handler = self # backpointer for logging & connection closing handler.run(self.server.get_app()) def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): server_address = (addr, port) if threading: - httpd_cls = type("WSGIServer", (socketserver.ThreadingMixIn, server_cls), {}) + httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) diff --git a/venv/Lib/site-packages/django/core/signing.py b/venv/Lib/site-packages/django/core/signing.py index 633d6cc..a5bccfb 100644 --- a/venv/Lib/site-packages/django/core/signing.py +++ b/venv/Lib/site-packages/django/core/signing.py @@ -40,71 +40,42 @@ import time import zlib from django.conf import settings +from django.utils import baseconv from django.utils.crypto import constant_time_compare, salted_hmac from django.utils.encoding import force_bytes from django.utils.module_loading import import_string from django.utils.regex_helper import _lazy_re_compile -_SEP_UNSAFE = _lazy_re_compile(r"^[A-z0-9-_=]*$") -BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +_SEP_UNSAFE = _lazy_re_compile(r'^[A-z0-9-_=]*$') class BadSignature(Exception): """Signature does not match.""" - pass class SignatureExpired(BadSignature): """Signature timestamp is older than required max_age.""" - pass -def b62_encode(s): - if s == 0: - return "0" - sign = "-" if s < 0 else "" - s = abs(s) - encoded = "" - while s > 0: - s, remainder = divmod(s, 62) - encoded = BASE62_ALPHABET[remainder] + encoded - return sign + encoded - - -def b62_decode(s): - if s == "0": - return 0 - sign = 1 - if s[0] == "-": - s = s[1:] - sign = -1 - decoded = 0 - for digit in s: - decoded = decoded * 62 + BASE62_ALPHABET.index(digit) - return sign * decoded - - def b64_encode(s): - return base64.urlsafe_b64encode(s).strip(b"=") + return base64.urlsafe_b64encode(s).strip(b'=') def b64_decode(s): - pad = b"=" * (-len(s) % 4) + pad = b'=' * (-len(s) % 4) return base64.urlsafe_b64decode(s + pad) -def base64_hmac(salt, value, key, algorithm="sha1"): - return b64_encode( - salted_hmac(salt, value, key, algorithm=algorithm).digest() - ).decode() +def base64_hmac(salt, value, key, algorithm='sha1'): + return b64_encode(salted_hmac(salt, value, key, algorithm=algorithm).digest()).decode() -def get_cookie_signer(salt="django.core.signing.get_cookie_signer"): +def get_cookie_signer(salt='django.core.signing.get_cookie_signer'): Signer = import_string(settings.SIGNING_BACKEND) key = force_bytes(settings.SECRET_KEY) # SECRET_KEY may be str or bytes. - return Signer(b"django.http.cookies" + key, salt=salt) + return Signer(b'django.http.cookies' + key, salt=salt) class JSONSerializer: @@ -112,17 +83,14 @@ class JSONSerializer: Simple wrapper around json to be used in signing.dumps and signing.loads. """ - def dumps(self, obj): - return json.dumps(obj, separators=(",", ":")).encode("latin-1") + return json.dumps(obj, separators=(',', ':')).encode('latin-1') def loads(self, data): - return json.loads(data.decode("latin-1")) + return json.loads(data.decode('latin-1')) -def dumps( - obj, key=None, salt="django.core.signing", serializer=JSONSerializer, compress=False -): +def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False): """ Return URL-safe, hmac signed base64 compressed JSON string. If key is None, use settings.SECRET_KEY instead. The hmac algorithm is the default @@ -139,52 +107,55 @@ def dumps( The serializer is expected to return a bytestring. """ - return TimestampSigner(key, salt=salt).sign_object( - obj, serializer=serializer, compress=compress - ) + return TimestampSigner(key, salt=salt).sign_object(obj, serializer=serializer, compress=compress) -def loads( - s, key=None, salt="django.core.signing", serializer=JSONSerializer, max_age=None -): +def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None): """ Reverse of dumps(), raise BadSignature if signature fails. The serializer is expected to accept a bytestring. """ - return TimestampSigner(key, salt=salt).unsign_object( - s, serializer=serializer, max_age=max_age - ) + return TimestampSigner(key, salt=salt).unsign_object(s, serializer=serializer, max_age=max_age) class Signer: - def __init__(self, key=None, sep=":", salt=None, algorithm=None): + # RemovedInDjango40Warning. + legacy_algorithm = 'sha1' + + def __init__(self, key=None, sep=':', salt=None, algorithm=None): self.key = key or settings.SECRET_KEY self.sep = sep if _SEP_UNSAFE.match(self.sep): raise ValueError( - "Unsafe Signer separator: %r (cannot be empty or consist of " - "only A-z0-9-_=)" % sep, + 'Unsafe Signer separator: %r (cannot be empty or consist of ' + 'only A-z0-9-_=)' % sep, ) - self.salt = salt or "%s.%s" % ( - self.__class__.__module__, - self.__class__.__name__, - ) - self.algorithm = algorithm or "sha256" + self.salt = salt or '%s.%s' % (self.__class__.__module__, self.__class__.__name__) + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # self.algorithm = algorithm or 'sha256' + self.algorithm = algorithm or settings.DEFAULT_HASHING_ALGORITHM def signature(self, value): - return base64_hmac( - self.salt + "signer", value, self.key, algorithm=self.algorithm - ) + return base64_hmac(self.salt + 'signer', value, self.key, algorithm=self.algorithm) + + def _legacy_signature(self, value): + # RemovedInDjango40Warning. + return base64_hmac(self.salt + 'signer', value, self.key, algorithm=self.legacy_algorithm) def sign(self, value): - return "%s%s%s" % (value, self.sep, self.signature(value)) + return '%s%s%s' % (value, self.sep, self.signature(value)) def unsign(self, signed_value): if self.sep not in signed_value: raise BadSignature('No "%s" found in value' % self.sep) value, sig = signed_value.rsplit(self.sep, 1) - if constant_time_compare(sig, self.signature(value)): + if ( + constant_time_compare(sig, self.signature(value)) or ( + self.legacy_algorithm and + constant_time_compare(sig, self._legacy_signature(value)) + ) + ): return value raise BadSignature('Signature "%s" does not match' % sig) @@ -210,14 +181,14 @@ class Signer: is_compressed = True base64d = b64_encode(data).decode() if is_compressed: - base64d = "." + base64d + base64d = '.' + base64d return self.sign(base64d) def unsign_object(self, signed_obj, serializer=JSONSerializer, **kwargs): # Signer.unsign() returns str but base64 and zlib compression operate # on bytes. base64d = self.unsign(signed_obj, **kwargs).encode() - decompress = base64d[:1] == b"." + decompress = base64d[:1] == b'.' if decompress: # It's compressed; uncompress it first. base64d = base64d[1:] @@ -228,11 +199,12 @@ class Signer: class TimestampSigner(Signer): + def timestamp(self): - return b62_encode(int(time.time())) + return baseconv.base62.encode(int(time.time())) def sign(self, value): - value = "%s%s%s" % (value, self.sep, self.timestamp()) + value = '%s%s%s' % (value, self.sep, self.timestamp()) return super().sign(value) def unsign(self, value, max_age=None): @@ -242,12 +214,13 @@ class TimestampSigner(Signer): """ result = super().unsign(value) value, timestamp = result.rsplit(self.sep, 1) - timestamp = b62_decode(timestamp) + timestamp = baseconv.base62.decode(timestamp) if max_age is not None: if isinstance(max_age, datetime.timedelta): max_age = max_age.total_seconds() # Check timestamp is not older than max_age age = time.time() - timestamp if age > max_age: - raise SignatureExpired("Signature age %s > %s seconds" % (age, max_age)) + raise SignatureExpired( + 'Signature age %s > %s seconds' % (age, max_age)) return value diff --git a/venv/Lib/site-packages/django/core/validators.py b/venv/Lib/site-packages/django/core/validators.py index 4879c86..731ccf2 100644 --- a/venv/Lib/site-packages/django/core/validators.py +++ b/venv/Lib/site-packages/django/core/validators.py @@ -10,24 +10,21 @@ from django.utils.deprecation import RemovedInDjango41Warning from django.utils.encoding import punycode from django.utils.ipv6 import is_valid_ipv6_address from django.utils.regex_helper import _lazy_re_compile -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import ngettext_lazy +from django.utils.translation import gettext_lazy as _, ngettext_lazy # These values, if given to validate(), will trigger the self.required check. -EMPTY_VALUES = (None, "", [], (), {}) +EMPTY_VALUES = (None, '', [], (), {}) @deconstructible class RegexValidator: - regex = "" - message = _("Enter a valid value.") - code = "invalid" + regex = '' + message = _('Enter a valid value.') + code = 'invalid' inverse_match = False flags = 0 - def __init__( - self, regex=None, message=None, code=None, inverse_match=None, flags=None - ): + def __init__(self, regex=None, message=None, code=None, inverse_match=None, flags=None): if regex is not None: self.regex = regex if message is not None: @@ -39,9 +36,7 @@ class RegexValidator: if flags is not None: self.flags = flags if self.flags and not isinstance(self.regex, str): - raise TypeError( - "If the flags are set, regex must be a regular expression string." - ) + raise TypeError("If the flags are set, regex must be a regular expression string.") self.regex = _lazy_re_compile(self.regex, self.flags) @@ -53,58 +48,51 @@ class RegexValidator: regex_matches = self.regex.search(str(value)) invalid_input = regex_matches if self.inverse_match else not regex_matches if invalid_input: - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) def __eq__(self, other): return ( - isinstance(other, RegexValidator) - and self.regex.pattern == other.regex.pattern - and self.regex.flags == other.regex.flags - and (self.message == other.message) - and (self.code == other.code) - and (self.inverse_match == other.inverse_match) + isinstance(other, RegexValidator) and + self.regex.pattern == other.regex.pattern and + self.regex.flags == other.regex.flags and + (self.message == other.message) and + (self.code == other.code) and + (self.inverse_match == other.inverse_match) ) @deconstructible class URLValidator(RegexValidator): - ul = "\u00a1-\uffff" # Unicode letters range (must not be a raw string). + ul = '\u00a1-\uffff' # Unicode letters range (must not be a raw string). # IP patterns - ipv4_re = ( - r"(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)" - r"(?:\.(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)){3}" - ) - ipv6_re = r"\[[0-9a-f:.]+\]" # (simple regex, validated later) + ipv4_re = r'(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)(?:\.(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)){3}' + ipv6_re = r'\[[0-9a-f:.]+\]' # (simple regex, validated later) # Host patterns - hostname_re = ( - r"[a-z" + ul + r"0-9](?:[a-z" + ul + r"0-9-]{0,61}[a-z" + ul + r"0-9])?" - ) + hostname_re = r'[a-z' + ul + r'0-9](?:[a-z' + ul + r'0-9-]{0,61}[a-z' + ul + r'0-9])?' # Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1 - domain_re = r"(?:\.(?!-)[a-z" + ul + r"0-9-]{1,63}(?<!-))*" + domain_re = r'(?:\.(?!-)[a-z' + ul + r'0-9-]{1,63}(?<!-))*' tld_re = ( - r"\." # dot - r"(?!-)" # can't start with a dash - r"(?:[a-z" + ul + "-]{2,63}" # domain label - r"|xn--[a-z0-9]{1,59})" # or punycode label - r"(?<!-)" # can't end with a dash - r"\.?" # may have a trailing dot + r'\.' # dot + r'(?!-)' # can't start with a dash + r'(?:[a-z' + ul + '-]{2,63}' # domain label + r'|xn--[a-z0-9]{1,59})' # or punycode label + r'(?<!-)' # can't end with a dash + r'\.?' # may have a trailing dot ) - host_re = "(" + hostname_re + domain_re + tld_re + "|localhost)" + host_re = '(' + hostname_re + domain_re + tld_re + '|localhost)' regex = _lazy_re_compile( - r"^(?:[a-z0-9.+-]*)://" # scheme is validated separately - r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?" # user:pass authentication - r"(?:" + ipv4_re + "|" + ipv6_re + "|" + host_re + ")" - r"(?::\d{1,5})?" # port - r"(?:[/?#][^\s]*)?" # resource path - r"\Z", - re.IGNORECASE, - ) - message = _("Enter a valid URL.") - schemes = ["http", "https", "ftp", "ftps"] - unsafe_chars = frozenset("\t\r\n") + r'^(?:[a-z0-9.+-]*)://' # scheme is validated separately + r'(?:[^\s:@/]+(?::[^\s:@/]*)?@)?' # user:pass authentication + r'(?:' + ipv4_re + '|' + ipv6_re + '|' + host_re + ')' + r'(?::\d{2,5})?' # port + r'(?:[/?#][^\s]*)?' # resource path + r'\Z', re.IGNORECASE) + message = _('Enter a valid URL.') + schemes = ['http', 'https', 'ftp', 'ftps'] + unsafe_chars = frozenset('\t\r\n') def __init__(self, schemes=None, **kwargs): super().__init__(**kwargs) @@ -113,13 +101,13 @@ class URLValidator(RegexValidator): def __call__(self, value): if not isinstance(value, str): - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) if self.unsafe_chars.intersection(value): - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) # Check if the scheme is valid. - scheme = value.split("://")[0].lower() + scheme = value.split('://')[0].lower() if scheme not in self.schemes: - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) # Then check full URL try: @@ -130,9 +118,7 @@ class URLValidator(RegexValidator): try: scheme, netloc, path, query, fragment = urlsplit(value) except ValueError: # for example, "Invalid IPv6 URL" - raise ValidationError( - self.message, code=self.code, params={"value": value} - ) + raise ValidationError(self.message, code=self.code, params={'value': value}) try: netloc = punycode(netloc) # IDN -> ACE except UnicodeError: # invalid domain part @@ -143,28 +129,26 @@ class URLValidator(RegexValidator): raise else: # Now verify IPv6 in the netloc part - host_match = re.search(r"^\[(.+)\](?::\d{1,5})?$", urlsplit(value).netloc) + host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$', urlsplit(value).netloc) if host_match: potential_ip = host_match[1] try: validate_ipv6_address(potential_ip) except ValidationError: - raise ValidationError( - self.message, code=self.code, params={"value": value} - ) + raise ValidationError(self.message, code=self.code, params={'value': value}) # The maximum length of a full host name is 253 characters per RFC 1034 # section 3.1. It's defined to be 255 bytes or less, but this includes # one byte for the length of the name and one byte for the trailing dot # that's used to indicate absolute names in DNS. if len(urlsplit(value).hostname) > 253: - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) integer_validator = RegexValidator( - _lazy_re_compile(r"^-?\d+\Z"), - message=_("Enter a valid integer."), - code="invalid", + _lazy_re_compile(r'^-?\d+\Z'), + message=_('Enter a valid integer.'), + code='invalid', ) @@ -174,33 +158,27 @@ def validate_integer(value): @deconstructible class EmailValidator: - message = _("Enter a valid email address.") - code = "invalid" + message = _('Enter a valid email address.') + code = 'invalid' user_regex = _lazy_re_compile( - # dot-atom - r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" - # quoted-string - r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])' - r'*"\Z)', - re.IGNORECASE, - ) + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE) domain_regex = _lazy_re_compile( # max length for domain name labels is 63 characters per RFC 1034 - r"((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z", - re.IGNORECASE, - ) + r'((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z', + re.IGNORECASE) literal_regex = _lazy_re_compile( # literal form, ipv4 or ipv6 address (SMTP 4.1.3) - r"\[([A-F0-9:.]+)\]\Z", - re.IGNORECASE, - ) - domain_allowlist = ["localhost"] + r'\[([A-f0-9:.]+)\]\Z', + re.IGNORECASE) + domain_allowlist = ['localhost'] @property def domain_whitelist(self): warnings.warn( - "The domain_whitelist attribute is deprecated in favor of " - "domain_allowlist.", + 'The domain_whitelist attribute is deprecated in favor of ' + 'domain_allowlist.', RemovedInDjango41Warning, stacklevel=2, ) @@ -209,8 +187,8 @@ class EmailValidator: @domain_whitelist.setter def domain_whitelist(self, allowlist): warnings.warn( - "The domain_whitelist attribute is deprecated in favor of " - "domain_allowlist.", + 'The domain_whitelist attribute is deprecated in favor of ' + 'domain_allowlist.', RemovedInDjango41Warning, stacklevel=2, ) @@ -220,7 +198,7 @@ class EmailValidator: if whitelist is not None: allowlist = whitelist warnings.warn( - "The whitelist argument is deprecated in favor of allowlist.", + 'The whitelist argument is deprecated in favor of allowlist.', RemovedInDjango41Warning, stacklevel=2, ) @@ -232,17 +210,16 @@ class EmailValidator: self.domain_allowlist = allowlist def __call__(self, value): - if not value or "@" not in value: - raise ValidationError(self.message, code=self.code, params={"value": value}) + if not value or '@' not in value: + raise ValidationError(self.message, code=self.code, params={'value': value}) - user_part, domain_part = value.rsplit("@", 1) + user_part, domain_part = value.rsplit('@', 1) if not self.user_regex.match(user_part): - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) - if domain_part not in self.domain_allowlist and not self.validate_domain_part( - domain_part - ): + if (domain_part not in self.domain_allowlist and + not self.validate_domain_part(domain_part)): # Try for possible IDN domain-part try: domain_part = punycode(domain_part) @@ -251,7 +228,7 @@ class EmailValidator: else: if self.validate_domain_part(domain_part): return - raise ValidationError(self.message, code=self.code, params={"value": value}) + raise ValidationError(self.message, code=self.code, params={'value': value}) def validate_domain_part(self, domain_part): if self.domain_regex.match(domain_part): @@ -269,31 +246,28 @@ class EmailValidator: def __eq__(self, other): return ( - isinstance(other, EmailValidator) - and (self.domain_allowlist == other.domain_allowlist) - and (self.message == other.message) - and (self.code == other.code) + isinstance(other, EmailValidator) and + (self.domain_allowlist == other.domain_allowlist) and + (self.message == other.message) and + (self.code == other.code) ) validate_email = EmailValidator() -slug_re = _lazy_re_compile(r"^[-a-zA-Z0-9_]+\Z") +slug_re = _lazy_re_compile(r'^[-a-zA-Z0-9_]+\Z') validate_slug = RegexValidator( slug_re, # Translators: "letters" means latin letters: a-z and A-Z. - _("Enter a valid “slug” consisting of letters, numbers, underscores or hyphens."), - "invalid", + _('Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.'), + 'invalid' ) -slug_unicode_re = _lazy_re_compile(r"^[-\w]+\Z") +slug_unicode_re = _lazy_re_compile(r'^[-\w]+\Z') validate_unicode_slug = RegexValidator( slug_unicode_re, - _( - "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " - "hyphens." - ), - "invalid", + _('Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or hyphens.'), + 'invalid' ) @@ -301,26 +275,25 @@ def validate_ipv4_address(value): try: ipaddress.IPv4Address(value) except ValueError: - raise ValidationError( - _("Enter a valid IPv4 address."), code="invalid", params={"value": value} - ) + raise ValidationError(_('Enter a valid IPv4 address.'), code='invalid', params={'value': value}) else: # Leading zeros are forbidden to avoid ambiguity with the octal # notation. This restriction is included in Python 3.9.5+. # TODO: Remove when dropping support for PY39. - if any(octet != "0" and octet[0] == "0" for octet in value.split(".")): + if any( + octet != '0' and octet[0] == '0' + for octet in value.split('.') + ): raise ValidationError( - _("Enter a valid IPv4 address."), - code="invalid", - params={"value": value}, + _('Enter a valid IPv4 address.'), + code='invalid', + params={'value': value}, ) def validate_ipv6_address(value): if not is_valid_ipv6_address(value): - raise ValidationError( - _("Enter a valid IPv6 address."), code="invalid", params={"value": value} - ) + raise ValidationError(_('Enter a valid IPv6 address.'), code='invalid', params={'value': value}) def validate_ipv46_address(value): @@ -330,17 +303,13 @@ def validate_ipv46_address(value): try: validate_ipv6_address(value) except ValidationError: - raise ValidationError( - _("Enter a valid IPv4 or IPv6 address."), - code="invalid", - params={"value": value}, - ) + raise ValidationError(_('Enter a valid IPv4 or IPv6 address.'), code='invalid', params={'value': value}) ip_address_validator_map = { - "both": ([validate_ipv46_address], _("Enter a valid IPv4 or IPv6 address.")), - "ipv4": ([validate_ipv4_address], _("Enter a valid IPv4 address.")), - "ipv6": ([validate_ipv6_address], _("Enter a valid IPv6 address.")), + 'both': ([validate_ipv46_address], _('Enter a valid IPv4 or IPv6 address.')), + 'ipv4': ([validate_ipv4_address], _('Enter a valid IPv4 address.')), + 'ipv6': ([validate_ipv6_address], _('Enter a valid IPv6 address.')), } @@ -349,39 +318,33 @@ def ip_address_validators(protocol, unpack_ipv4): Depending on the given parameters, return the appropriate validators for the GenericIPAddressField. """ - if protocol != "both" and unpack_ipv4: + if protocol != 'both' and unpack_ipv4: raise ValueError( - "You can only use `unpack_ipv4` if `protocol` is set to 'both'" - ) + "You can only use `unpack_ipv4` if `protocol` is set to 'both'") try: return ip_address_validator_map[protocol.lower()] except KeyError: - raise ValueError( - "The protocol '%s' is unknown. Supported: %s" - % (protocol, list(ip_address_validator_map)) - ) + raise ValueError("The protocol '%s' is unknown. Supported: %s" + % (protocol, list(ip_address_validator_map))) -def int_list_validator(sep=",", message=None, code="invalid", allow_negative=False): - regexp = _lazy_re_compile( - r"^%(neg)s\d+(?:%(sep)s%(neg)s\d+)*\Z" - % { - "neg": "(-)?" if allow_negative else "", - "sep": re.escape(sep), - } - ) +def int_list_validator(sep=',', message=None, code='invalid', allow_negative=False): + regexp = _lazy_re_compile(r'^%(neg)s\d+(?:%(sep)s%(neg)s\d+)*\Z' % { + 'neg': '(-)?' if allow_negative else '', + 'sep': re.escape(sep), + }) return RegexValidator(regexp, message=message, code=code) validate_comma_separated_integer_list = int_list_validator( - message=_("Enter only digits separated by commas."), + message=_('Enter only digits separated by commas.'), ) @deconstructible class BaseValidator: - message = _("Ensure this value is %(limit_value)s (it is %(show_value)s).") - code = "limit_value" + message = _('Ensure this value is %(limit_value)s (it is %(show_value)s).') + code = 'limit_value' def __init__(self, limit_value, message=None): self.limit_value = limit_value @@ -390,10 +353,8 @@ class BaseValidator: def __call__(self, value): cleaned = self.clean(value) - limit_value = ( - self.limit_value() if callable(self.limit_value) else self.limit_value - ) - params = {"limit_value": limit_value, "show_value": cleaned, "value": value} + limit_value = self.limit_value() if callable(self.limit_value) else self.limit_value + params = {'limit_value': limit_value, 'show_value': cleaned, 'value': value} if self.compare(cleaned, limit_value): raise ValidationError(self.message, code=self.code, params=params) @@ -401,9 +362,9 @@ class BaseValidator: if not isinstance(other, self.__class__): return NotImplemented return ( - self.limit_value == other.limit_value - and self.message == other.message - and self.code == other.code + self.limit_value == other.limit_value and + self.message == other.message and + self.code == other.code ) def compare(self, a, b): @@ -415,8 +376,8 @@ class BaseValidator: @deconstructible class MaxValueValidator(BaseValidator): - message = _("Ensure this value is less than or equal to %(limit_value)s.") - code = "max_value" + message = _('Ensure this value is less than or equal to %(limit_value)s.') + code = 'max_value' def compare(self, a, b): return a > b @@ -424,8 +385,8 @@ class MaxValueValidator(BaseValidator): @deconstructible class MinValueValidator(BaseValidator): - message = _("Ensure this value is greater than or equal to %(limit_value)s.") - code = "min_value" + message = _('Ensure this value is greater than or equal to %(limit_value)s.') + code = 'min_value' def compare(self, a, b): return a < b @@ -434,13 +395,10 @@ class MinValueValidator(BaseValidator): @deconstructible class MinLengthValidator(BaseValidator): message = ngettext_lazy( - "Ensure this value has at least %(limit_value)d character (it has " - "%(show_value)d).", - "Ensure this value has at least %(limit_value)d characters (it has " - "%(show_value)d).", - "limit_value", - ) - code = "min_length" + 'Ensure this value has at least %(limit_value)d character (it has %(show_value)d).', + 'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).', + 'limit_value') + code = 'min_length' def compare(self, a, b): return a < b @@ -452,13 +410,10 @@ class MinLengthValidator(BaseValidator): @deconstructible class MaxLengthValidator(BaseValidator): message = ngettext_lazy( - "Ensure this value has at most %(limit_value)d character (it has " - "%(show_value)d).", - "Ensure this value has at most %(limit_value)d characters (it has " - "%(show_value)d).", - "limit_value", - ) - code = "max_length" + 'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).', + 'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).', + 'limit_value') + code = 'max_length' def compare(self, a, b): return a > b @@ -473,25 +428,22 @@ class DecimalValidator: Validate that the input does not exceed the maximum number of digits expected, otherwise raise ValidationError. """ - messages = { - "invalid": _("Enter a number."), - "max_digits": ngettext_lazy( - "Ensure that there are no more than %(max)s digit in total.", - "Ensure that there are no more than %(max)s digits in total.", - "max", + 'invalid': _('Enter a number.'), + 'max_digits': ngettext_lazy( + 'Ensure that there are no more than %(max)s digit in total.', + 'Ensure that there are no more than %(max)s digits in total.', + 'max' ), - "max_decimal_places": ngettext_lazy( - "Ensure that there are no more than %(max)s decimal place.", - "Ensure that there are no more than %(max)s decimal places.", - "max", + 'max_decimal_places': ngettext_lazy( + 'Ensure that there are no more than %(max)s decimal place.', + 'Ensure that there are no more than %(max)s decimal places.', + 'max' ), - "max_whole_digits": ngettext_lazy( - "Ensure that there are no more than %(max)s digit before the decimal " - "point.", - "Ensure that there are no more than %(max)s digits before the decimal " - "point.", - "max", + 'max_whole_digits': ngettext_lazy( + 'Ensure that there are no more than %(max)s digit before the decimal point.', + 'Ensure that there are no more than %(max)s digits before the decimal point.', + 'max' ), } @@ -501,10 +453,8 @@ class DecimalValidator: def __call__(self, value): digit_tuple, exponent = value.as_tuple()[1:] - if exponent in {"F", "n", "N"}: - raise ValidationError( - self.messages["invalid"], code="invalid", params={"value": value} - ) + if exponent in {'F', 'n', 'N'}: + raise ValidationError(self.messages['invalid'], code='invalid', params={'value': value}) if exponent >= 0: # A positive exponent adds that many trailing zeros. digits = len(digit_tuple) + exponent @@ -524,48 +474,43 @@ class DecimalValidator: if self.max_digits is not None and digits > self.max_digits: raise ValidationError( - self.messages["max_digits"], - code="max_digits", - params={"max": self.max_digits, "value": value}, + self.messages['max_digits'], + code='max_digits', + params={'max': self.max_digits, 'value': value}, ) if self.decimal_places is not None and decimals > self.decimal_places: raise ValidationError( - self.messages["max_decimal_places"], - code="max_decimal_places", - params={"max": self.decimal_places, "value": value}, + self.messages['max_decimal_places'], + code='max_decimal_places', + params={'max': self.decimal_places, 'value': value}, ) - if ( - self.max_digits is not None - and self.decimal_places is not None - and whole_digits > (self.max_digits - self.decimal_places) - ): + if (self.max_digits is not None and self.decimal_places is not None and + whole_digits > (self.max_digits - self.decimal_places)): raise ValidationError( - self.messages["max_whole_digits"], - code="max_whole_digits", - params={"max": (self.max_digits - self.decimal_places), "value": value}, + self.messages['max_whole_digits'], + code='max_whole_digits', + params={'max': (self.max_digits - self.decimal_places), 'value': value}, ) def __eq__(self, other): return ( - isinstance(other, self.__class__) - and self.max_digits == other.max_digits - and self.decimal_places == other.decimal_places + isinstance(other, self.__class__) and + self.max_digits == other.max_digits and + self.decimal_places == other.decimal_places ) @deconstructible class FileExtensionValidator: message = _( - "File extension “%(extension)s” is not allowed. " - "Allowed extensions are: %(allowed_extensions)s." + 'File extension “%(extension)s” is not allowed. ' + 'Allowed extensions are: %(allowed_extensions)s.' ) - code = "invalid_extension" + code = 'invalid_extension' def __init__(self, allowed_extensions=None, message=None, code=None): if allowed_extensions is not None: - allowed_extensions = [ - allowed_extension.lower() for allowed_extension in allowed_extensions - ] + allowed_extensions = [allowed_extension.lower() for allowed_extension in allowed_extensions] self.allowed_extensions = allowed_extensions if message is not None: self.message = message @@ -574,26 +519,23 @@ class FileExtensionValidator: def __call__(self, value): extension = Path(value.name).suffix[1:].lower() - if ( - self.allowed_extensions is not None - and extension not in self.allowed_extensions - ): + if self.allowed_extensions is not None and extension not in self.allowed_extensions: raise ValidationError( self.message, code=self.code, params={ - "extension": extension, - "allowed_extensions": ", ".join(self.allowed_extensions), - "value": value, - }, + 'extension': extension, + 'allowed_extensions': ', '.join(self.allowed_extensions), + 'value': value, + } ) def __eq__(self, other): return ( - isinstance(other, self.__class__) - and self.allowed_extensions == other.allowed_extensions - and self.message == other.message - and self.code == other.code + isinstance(other, self.__class__) and + self.allowed_extensions == other.allowed_extensions and + self.message == other.message and + self.code == other.code ) @@ -608,17 +550,14 @@ def get_available_image_extensions(): def validate_image_file_extension(value): - return FileExtensionValidator(allowed_extensions=get_available_image_extensions())( - value - ) + return FileExtensionValidator(allowed_extensions=get_available_image_extensions())(value) @deconstructible class ProhibitNullCharactersValidator: """Validate that the string doesn't contain the null character.""" - - message = _("Null characters are not allowed.") - code = "null_characters_not_allowed" + message = _('Null characters are not allowed.') + code = 'null_characters_not_allowed' def __init__(self, message=None, code=None): if message is not None: @@ -627,12 +566,12 @@ class ProhibitNullCharactersValidator: self.code = code def __call__(self, value): - if "\x00" in str(value): - raise ValidationError(self.message, code=self.code, params={"value": value}) + if '\x00' in str(value): + raise ValidationError(self.message, code=self.code, params={'value': value}) def __eq__(self, other): return ( - isinstance(other, self.__class__) - and self.message == other.message - and self.code == other.code + isinstance(other, self.__class__) and + self.message == other.message and + self.code == other.code ) diff --git a/venv/Lib/site-packages/django/db/__init__.py b/venv/Lib/site-packages/django/db/__init__.py index b0cae97..2612786 100644 --- a/venv/Lib/site-packages/django/db/__init__.py +++ b/venv/Lib/site-packages/django/db/__init__.py @@ -1,36 +1,17 @@ from django.core import signals from django.db.utils import ( - DEFAULT_DB_ALIAS, - DJANGO_VERSION_PICKLE_KEY, - ConnectionHandler, - ConnectionRouter, - DatabaseError, - DataError, - Error, - IntegrityError, - InterfaceError, - InternalError, - NotSupportedError, - OperationalError, + DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, ConnectionHandler, + ConnectionRouter, DatabaseError, DataError, Error, IntegrityError, + InterfaceError, InternalError, NotSupportedError, OperationalError, ProgrammingError, ) from django.utils.connection import ConnectionProxy __all__ = [ - "connection", - "connections", - "router", - "DatabaseError", - "IntegrityError", - "InternalError", - "ProgrammingError", - "DataError", - "NotSupportedError", - "Error", - "InterfaceError", - "OperationalError", - "DEFAULT_DB_ALIAS", - "DJANGO_VERSION_PICKLE_KEY", + 'connection', 'connections', 'router', 'DatabaseError', 'IntegrityError', + 'InternalError', 'ProgrammingError', 'DataError', 'NotSupportedError', + 'Error', 'InterfaceError', 'OperationalError', 'DEFAULT_DB_ALIAS', + 'DJANGO_VERSION_PICKLE_KEY', ] connections = ConnectionHandler() diff --git a/venv/Lib/site-packages/django/db/backends/base/base.py b/venv/Lib/site-packages/django/db/backends/base/base.py index a7f923c..42b4b17 100644 --- a/venv/Lib/site-packages/django/db/backends/base/base.py +++ b/venv/Lib/site-packages/django/db/backends/base/base.py @@ -6,10 +6,7 @@ import warnings from collections import deque from contextlib import contextmanager -try: - import zoneinfo -except ImportError: - from backports import zoneinfo +import pytz from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -23,21 +20,11 @@ from django.utils import timezone from django.utils.asyncio import async_unsafe from django.utils.functional import cached_property -NO_DB_ALIAS = "__no_db__" - - -# RemovedInDjango50Warning -def timezone_constructor(tzname): - if settings.USE_DEPRECATED_PYTZ: - import pytz - - return pytz.timezone(tzname) - return zoneinfo.ZoneInfo(tzname) +NO_DB_ALIAS = '__no_db__' class BaseDatabaseWrapper: """Represent a database connection.""" - # Mapping of Field objects to their column types. data_types = {} # Mapping of Field objects to their SQL suffix such as AUTOINCREMENT. @@ -45,8 +32,8 @@ class BaseDatabaseWrapper: # Mapping of Field objects to their SQL for CHECK constraints. data_type_check_constraints = {} ops = None - vendor = "unknown" - display_name = "unknown" + vendor = 'unknown' + display_name = 'unknown' SchemaEditorClass = None # Classes instantiated in __init__(). client_class = None @@ -145,10 +132,10 @@ class BaseDatabaseWrapper: """ if not settings.USE_TZ: return None - elif self.settings_dict["TIME_ZONE"] is None: + elif self.settings_dict['TIME_ZONE'] is None: return timezone.utc else: - return timezone_constructor(self.settings_dict["TIME_ZONE"]) + return pytz.timezone(self.settings_dict['TIME_ZONE']) @cached_property def timezone_name(self): @@ -157,10 +144,10 @@ class BaseDatabaseWrapper: """ if not settings.USE_TZ: return settings.TIME_ZONE - elif self.settings_dict["TIME_ZONE"] is None: - return "UTC" + elif self.settings_dict['TIME_ZONE'] is None: + return 'UTC' else: - return self.settings_dict["TIME_ZONE"] + return self.settings_dict['TIME_ZONE'] @property def queries_logged(self): @@ -171,38 +158,26 @@ class BaseDatabaseWrapper: if len(self.queries_log) == self.queries_log.maxlen: warnings.warn( "Limit for query logging exceeded, only the last {} queries " - "will be returned.".format(self.queries_log.maxlen) - ) + "will be returned.".format(self.queries_log.maxlen)) return list(self.queries_log) # ##### Backend-specific methods for creating connections and cursors ##### def get_connection_params(self): """Return a dict of parameters suitable for get_new_connection.""" - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require a get_connection_params() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_connection_params() method') def get_new_connection(self, conn_params): """Open a connection to the database.""" - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require a get_new_connection() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_new_connection() method') def init_connection_state(self): """Initialize the database connection settings.""" - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require an init_connection_state() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an init_connection_state() method') def create_cursor(self, name=None): """Create a cursor. Assume that a connection is established.""" - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require a create_cursor() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a create_cursor() method') # ##### Backend-specific methods for creating connections ##### @@ -216,21 +191,21 @@ class BaseDatabaseWrapper: self.savepoint_ids = [] self.needs_rollback = False # Reset parameters defining when to close the connection - max_age = self.settings_dict["CONN_MAX_AGE"] + max_age = self.settings_dict['CONN_MAX_AGE'] self.close_at = None if max_age is None else time.monotonic() + max_age self.closed_in_transaction = False self.errors_occurred = False # Establish the connection conn_params = self.get_connection_params() self.connection = self.get_new_connection(conn_params) - self.set_autocommit(self.settings_dict["AUTOCOMMIT"]) + self.set_autocommit(self.settings_dict['AUTOCOMMIT']) self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) self.run_on_commit = [] def check_settings(self): - if self.settings_dict["TIME_ZONE"] is not None and not settings.USE_TZ: + if self.settings_dict['TIME_ZONE'] is not None and not settings.USE_TZ: raise ImproperlyConfigured( "Connection '%s' cannot set TIME_ZONE because USE_TZ is False." % self.alias @@ -355,7 +330,7 @@ class BaseDatabaseWrapper: return thread_ident = _thread.get_ident() - tid = str(thread_ident).replace("-", "") + tid = str(thread_ident).replace('-', '') self.savepoint_state += 1 sid = "s%s_x%d" % (tid, self.savepoint_state) @@ -405,9 +380,7 @@ class BaseDatabaseWrapper: """ Backend-specific implementation to enable or disable autocommit. """ - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require a _set_autocommit() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _set_autocommit() method') # ##### Generic transaction management methods ##### @@ -416,9 +389,7 @@ class BaseDatabaseWrapper: self.ensure_connection() return self.autocommit - def set_autocommit( - self, autocommit, force_begin_transaction_with_broken_autocommit=False - ): + def set_autocommit(self, autocommit, force_begin_transaction_with_broken_autocommit=False): """ Enable or disable autocommit. @@ -434,9 +405,8 @@ class BaseDatabaseWrapper: self.ensure_connection() start_transaction_under_autocommit = ( - force_begin_transaction_with_broken_autocommit - and not autocommit - and hasattr(self, "_start_transaction_under_autocommit") + force_begin_transaction_with_broken_autocommit and not autocommit and + hasattr(self, '_start_transaction_under_autocommit') ) if start_transaction_under_autocommit: @@ -454,8 +424,7 @@ class BaseDatabaseWrapper: """Get the "needs rollback" flag -- for *advanced use* only.""" if not self.in_atomic_block: raise TransactionManagementError( - "The rollback flag doesn't work outside of an 'atomic' block." - ) + "The rollback flag doesn't work outside of an 'atomic' block.") return self.needs_rollback def set_rollback(self, rollback): @@ -464,23 +433,20 @@ class BaseDatabaseWrapper: """ if not self.in_atomic_block: raise TransactionManagementError( - "The rollback flag doesn't work outside of an 'atomic' block." - ) + "The rollback flag doesn't work outside of an 'atomic' block.") self.needs_rollback = rollback def validate_no_atomic_block(self): """Raise an error if an atomic block is active.""" if self.in_atomic_block: raise TransactionManagementError( - "This is forbidden when an 'atomic' block is active." - ) + "This is forbidden when an 'atomic' block is active.") def validate_no_broken_transaction(self): if self.needs_rollback: raise TransactionManagementError( "An error occurred in the current transaction. You can't " - "execute queries until the end of the 'atomic' block." - ) + "execute queries until the end of the 'atomic' block.") # ##### Foreign key constraints checks handling ##### @@ -531,8 +497,7 @@ class BaseDatabaseWrapper: as that may prevent Django from recycling unusable connections. """ raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require an is_usable() method" - ) + "subclasses of BaseDatabaseWrapper may require an is_usable() method") def close_if_unusable_or_obsolete(self): """ @@ -542,7 +507,7 @@ class BaseDatabaseWrapper: if self.connection is not None: # If the application didn't restore the original autocommit setting, # don't take chances, drop the connection. - if self.get_autocommit() != self.settings_dict["AUTOCOMMIT"]: + if self.get_autocommit() != self.settings_dict['AUTOCOMMIT']: self.close() return @@ -573,9 +538,7 @@ class BaseDatabaseWrapper: def dec_thread_sharing(self): with self._thread_sharing_lock: if self._thread_sharing_count <= 0: - raise RuntimeError( - "Cannot decrement the thread sharing count below zero." - ) + raise RuntimeError('Cannot decrement the thread sharing count below zero.') self._thread_sharing_count -= 1 def validate_thread_sharing(self): @@ -590,7 +553,8 @@ class BaseDatabaseWrapper: "DatabaseWrapper objects created in a " "thread can only be used in that same thread. The object " "with alias '%s' was created in thread id %s and this is " - "thread id %s." % (self.alias, self._thread_ident, _thread.get_ident()) + "thread id %s." + % (self.alias, self._thread_ident, _thread.get_ident()) ) # ##### Miscellaneous ##### @@ -651,7 +615,7 @@ class BaseDatabaseWrapper: being exposed to potential child threads while (or after) the test database is destroyed. Refs #10868, #17786, #16969. """ - conn = self.__class__({**self.settings_dict, "NAME": None}, alias=NO_DB_ALIAS) + conn = self.__class__({**self.settings_dict, 'NAME': None}, alias=NO_DB_ALIAS) try: with conn.cursor() as cursor: yield cursor @@ -664,8 +628,7 @@ class BaseDatabaseWrapper: """ if self.SchemaEditorClass is None: raise NotImplementedError( - "The SchemaEditorClass attribute of this database wrapper is still None" - ) + 'The SchemaEditorClass attribute of this database wrapper is still None') return self.SchemaEditorClass(self, *args, **kwargs) def on_commit(self, func): @@ -675,9 +638,7 @@ class BaseDatabaseWrapper: # Transaction in progress; save for execution on commit. self.run_on_commit.append((set(self.savepoint_ids), func)) elif not self.get_autocommit(): - raise TransactionManagementError( - "on_commit() cannot be used in manual transaction management" - ) + raise TransactionManagementError('on_commit() cannot be used in manual transaction management') else: # No transaction in progress and in autocommit mode; execute # immediately. diff --git a/venv/Lib/site-packages/django/db/backends/base/client.py b/venv/Lib/site-packages/django/db/backends/base/client.py index 0310563..8aca821 100644 --- a/venv/Lib/site-packages/django/db/backends/base/client.py +++ b/venv/Lib/site-packages/django/db/backends/base/client.py @@ -4,7 +4,6 @@ import subprocess class BaseDatabaseClient: """Encapsulate backend-specific methods for opening a client shell.""" - # This should be a string representing the name of the executable # (e.g., "psql"). Subclasses must override this. executable_name = None @@ -16,13 +15,11 @@ class BaseDatabaseClient: @classmethod def settings_to_cmd_args_env(cls, settings_dict, parameters): raise NotImplementedError( - "subclasses of BaseDatabaseClient must provide a " - "settings_to_cmd_args_env() method or override a runshell()." + 'subclasses of BaseDatabaseClient must provide a ' + 'settings_to_cmd_args_env() method or override a runshell().' ) def runshell(self, parameters): - args, env = self.settings_to_cmd_args_env( - self.connection.settings_dict, parameters - ) + args, env = self.settings_to_cmd_args_env(self.connection.settings_dict, parameters) env = {**os.environ, **env} if env else None subprocess.run(args, env=env, check=True) diff --git a/venv/Lib/site-packages/django/db/backends/base/creation.py b/venv/Lib/site-packages/django/db/backends/base/creation.py index 2132746..81cb34b 100644 --- a/venv/Lib/site-packages/django/db/backends/base/creation.py +++ b/venv/Lib/site-packages/django/db/backends/base/creation.py @@ -12,7 +12,7 @@ from django.utils.module_loading import import_string # The prefix to put on the default database name when creating # the test database. -TEST_DATABASE_PREFIX = "test_" +TEST_DATABASE_PREFIX = 'test_' class BaseDatabaseCreation: @@ -20,7 +20,6 @@ class BaseDatabaseCreation: Encapsulate backend-specific differences pertaining to creation and destruction of the test database. """ - def __init__(self, connection): self.connection = connection @@ -30,9 +29,7 @@ class BaseDatabaseCreation: def log(self, msg): sys.stderr.write(msg + os.linesep) - def create_test_db( - self, verbosity=1, autoclobber=False, serialize=True, keepdb=False - ): + def create_test_db(self, verbosity=1, autoclobber=False, serialize=True, keepdb=False): """ Create a test database, prompting the user for confirmation if the database already exists. Return the name of the test database created. @@ -43,17 +40,14 @@ class BaseDatabaseCreation: test_database_name = self._get_test_db_name() if verbosity >= 1: - action = "Creating" + action = 'Creating' if keepdb: action = "Using existing" - self.log( - "%s test database for alias %s..." - % ( - action, - self._get_database_display_str(verbosity, test_database_name), - ) - ) + self.log('%s test database for alias %s...' % ( + action, + self._get_database_display_str(verbosity, test_database_name), + )) # We could skip this call if keepdb is True, but we instead # give it the keepdb param. This is to handle the case @@ -67,24 +61,25 @@ class BaseDatabaseCreation: self.connection.settings_dict["NAME"] = test_database_name try: - if self.connection.settings_dict["TEST"]["MIGRATE"] is False: + if self.connection.settings_dict['TEST']['MIGRATE'] is False: # Disable migrations for all apps. old_migration_modules = settings.MIGRATION_MODULES settings.MIGRATION_MODULES = { - app.label: None for app in apps.get_app_configs() + app.label: None + for app in apps.get_app_configs() } # We report migrate messages at one level lower than that # requested. This ensures we don't get flooded with messages during # testing (unless you really ask to be flooded). call_command( - "migrate", + 'migrate', verbosity=max(verbosity - 1, 0), interactive=False, database=self.connection.alias, run_syncdb=True, ) finally: - if self.connection.settings_dict["TEST"]["MIGRATE"] is False: + if self.connection.settings_dict['TEST']['MIGRATE'] is False: settings.MIGRATION_MODULES = old_migration_modules # We then serialize the current state of the database into a string @@ -94,12 +89,12 @@ class BaseDatabaseCreation: if serialize: self.connection._test_serialized_contents = self.serialize_db_to_string() - call_command("createcachetable", database=self.connection.alias) + call_command('createcachetable', database=self.connection.alias) # Ensure a connection for the side effect of initializing the test database. self.connection.ensure_connection() - if os.environ.get("RUNNING_DJANGOS_TEST_SUITE") == "true": + if os.environ.get('RUNNING_DJANGOS_TEST_SUITE') == 'true': self.mark_expected_failures_and_skips() return test_database_name @@ -109,7 +104,7 @@ class BaseDatabaseCreation: Set this database up to be used in testing as a mirror of a primary database whose settings are given. """ - self.connection.settings_dict["NAME"] = primary_settings_dict["NAME"] + self.connection.settings_dict['NAME'] = primary_settings_dict['NAME'] def serialize_db_to_string(self): """ @@ -120,23 +115,22 @@ class BaseDatabaseCreation: # Iteratively return every object for all models to serialize. def get_objects(): from django.db.migrations.loader import MigrationLoader - loader = MigrationLoader(self.connection) for app_config in apps.get_app_configs(): if ( - app_config.models_module is not None - and app_config.label in loader.migrated_apps - and app_config.name not in settings.TEST_NON_SERIALIZED_APPS + app_config.models_module is not None and + app_config.label in loader.migrated_apps and + app_config.name not in settings.TEST_NON_SERIALIZED_APPS ): for model in app_config.get_models(): - if model._meta.can_migrate( - self.connection - ) and router.allow_migrate_model(self.connection.alias, model): + if ( + model._meta.can_migrate(self.connection) and + router.allow_migrate_model(self.connection.alias, model) + ): queryset = model._base_manager.using( self.connection.alias, ).order_by(model._meta.pk.name) yield from queryset.iterator() - # Serialize to a string out = StringIO() serializers.serialize("json", get_objects(), indent=None, stream=out) @@ -154,9 +148,7 @@ class BaseDatabaseCreation: # Disable constraint checks, because some databases (MySQL) doesn't # support deferred checks. with self.connection.constraint_checks_disabled(): - for obj in serializers.deserialize( - "json", data, using=self.connection.alias - ): + for obj in serializers.deserialize('json', data, using=self.connection.alias): obj.save() table_names.add(obj.object.__class__._meta.db_table) # Manually check for any invalid keys that might have been added, @@ -169,7 +161,7 @@ class BaseDatabaseCreation: """ return "'%s'%s" % ( self.connection.alias, - (" ('%s')" % database_name) if verbosity >= 2 else "", + (" ('%s')" % database_name) if verbosity >= 2 else '', ) def _get_test_db_name(self): @@ -179,12 +171,12 @@ class BaseDatabaseCreation: _create_test_db() and when no external munging is done with the 'NAME' settings. """ - if self.connection.settings_dict["TEST"]["NAME"]: - return self.connection.settings_dict["TEST"]["NAME"] - return TEST_DATABASE_PREFIX + self.connection.settings_dict["NAME"] + if self.connection.settings_dict['TEST']['NAME']: + return self.connection.settings_dict['TEST']['NAME'] + return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME'] def _execute_create_test_db(self, cursor, parameters, keepdb=False): - cursor.execute("CREATE DATABASE %(dbname)s %(suffix)s" % parameters) + cursor.execute('CREATE DATABASE %(dbname)s %(suffix)s' % parameters) def _create_test_db(self, verbosity, autoclobber, keepdb=False): """ @@ -192,8 +184,8 @@ class BaseDatabaseCreation: """ test_database_name = self._get_test_db_name() test_db_params = { - "dbname": self.connection.ops.quote_name(test_database_name), - "suffix": self.sql_table_creation_suffix(), + 'dbname': self.connection.ops.quote_name(test_database_name), + 'suffix': self.sql_table_creation_suffix(), } # Create the test database and connect to it. with self._nodb_cursor() as cursor: @@ -205,30 +197,24 @@ class BaseDatabaseCreation: if keepdb: return test_database_name - self.log("Got an error creating the test database: %s" % e) + self.log('Got an error creating the test database: %s' % e) if not autoclobber: confirm = input( "Type 'yes' if you would like to try deleting the test " - "database '%s', or 'no' to cancel: " % test_database_name - ) - if autoclobber or confirm == "yes": + "database '%s', or 'no' to cancel: " % test_database_name) + if autoclobber or confirm == 'yes': try: if verbosity >= 1: - self.log( - "Destroying old test database for alias %s..." - % ( - self._get_database_display_str( - verbosity, test_database_name - ), - ) - ) - cursor.execute("DROP DATABASE %(dbname)s" % test_db_params) + self.log('Destroying old test database for alias %s...' % ( + self._get_database_display_str(verbosity, test_database_name), + )) + cursor.execute('DROP DATABASE %(dbname)s' % test_db_params) self._execute_create_test_db(cursor, test_db_params, keepdb) except Exception as e: - self.log("Got an error recreating the test database: %s" % e) + self.log('Got an error recreating the test database: %s' % e) sys.exit(2) else: - self.log("Tests cancelled.") + self.log('Tests cancelled.') sys.exit(1) return test_database_name @@ -237,19 +223,16 @@ class BaseDatabaseCreation: """ Clone a test database. """ - source_database_name = self.connection.settings_dict["NAME"] + source_database_name = self.connection.settings_dict['NAME'] if verbosity >= 1: - action = "Cloning test database" + action = 'Cloning test database' if keepdb: - action = "Using existing clone" - self.log( - "%s for alias %s..." - % ( - action, - self._get_database_display_str(verbosity, source_database_name), - ) - ) + action = 'Using existing clone' + self.log('%s for alias %s...' % ( + action, + self._get_database_display_str(verbosity, source_database_name), + )) # We could skip this call if keepdb is True, but we instead # give it the keepdb param. See create_test_db for details. @@ -263,10 +246,7 @@ class BaseDatabaseCreation: # already and its name has been copied to settings_dict['NAME'] so # we don't need to call _get_test_db_name. orig_settings_dict = self.connection.settings_dict - return { - **orig_settings_dict, - "NAME": "{}_{}".format(orig_settings_dict["NAME"], suffix), - } + return {**orig_settings_dict, 'NAME': '{}_{}'.format(orig_settings_dict['NAME'], suffix)} def _clone_test_db(self, suffix, verbosity, keepdb=False): """ @@ -274,33 +254,27 @@ class BaseDatabaseCreation: """ raise NotImplementedError( "The database backend doesn't support cloning databases. " - "Disable the option to run tests in parallel processes." - ) + "Disable the option to run tests in parallel processes.") - def destroy_test_db( - self, old_database_name=None, verbosity=1, keepdb=False, suffix=None - ): + def destroy_test_db(self, old_database_name=None, verbosity=1, keepdb=False, suffix=None): """ Destroy a test database, prompting the user for confirmation if the database already exists. """ self.connection.close() if suffix is None: - test_database_name = self.connection.settings_dict["NAME"] + test_database_name = self.connection.settings_dict['NAME'] else: - test_database_name = self.get_test_db_clone_settings(suffix)["NAME"] + test_database_name = self.get_test_db_clone_settings(suffix)['NAME'] if verbosity >= 1: - action = "Destroying" + action = 'Destroying' if keepdb: - action = "Preserving" - self.log( - "%s test database for alias %s..." - % ( - action, - self._get_database_display_str(verbosity, test_database_name), - ) - ) + action = 'Preserving' + self.log('%s test database for alias %s...' % ( + action, + self._get_database_display_str(verbosity, test_database_name), + )) # if we want to preserve the database # skip the actual destroying piece. @@ -321,9 +295,8 @@ class BaseDatabaseCreation: # to do so, because it's not allowed to delete a database while being # connected to it. with self._nodb_cursor() as cursor: - cursor.execute( - "DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name) - ) + cursor.execute("DROP DATABASE %s" + % self.connection.ops.quote_name(test_database_name)) def mark_expected_failures_and_skips(self): """ @@ -331,8 +304,8 @@ class BaseDatabaseCreation: database and test which should be skipped on this database. """ for test_name in self.connection.features.django_test_expected_failures: - test_case_name, _, test_method_name = test_name.rpartition(".") - test_app = test_name.split(".")[0] + test_case_name, _, test_method_name = test_name.rpartition('.') + test_app = test_name.split('.')[0] # Importing a test app that isn't installed raises RuntimeError. if test_app in settings.INSTALLED_APPS: test_case = import_string(test_case_name) @@ -340,8 +313,8 @@ class BaseDatabaseCreation: setattr(test_case, test_method_name, expectedFailure(test_method)) for reason, tests in self.connection.features.django_test_skips.items(): for test_name in tests: - test_case_name, _, test_method_name = test_name.rpartition(".") - test_app = test_name.split(".")[0] + test_case_name, _, test_method_name = test_name.rpartition('.') + test_app = test_name.split('.')[0] # Importing a test app that isn't installed raises RuntimeError. if test_app in settings.INSTALLED_APPS: test_case = import_string(test_case_name) @@ -352,7 +325,7 @@ class BaseDatabaseCreation: """ SQL to append to the end of the test table creation statements. """ - return "" + return '' def test_db_signature(self): """ @@ -362,8 +335,8 @@ class BaseDatabaseCreation: """ settings_dict = self.connection.settings_dict return ( - settings_dict["HOST"], - settings_dict["PORT"], - settings_dict["ENGINE"], + settings_dict['HOST'], + settings_dict['PORT'], + settings_dict['ENGINE'], self._get_test_db_name(), ) diff --git a/venv/Lib/site-packages/django/db/backends/base/features.py b/venv/Lib/site-packages/django/db/backends/base/features.py index 7ebe361..4c89231 100644 --- a/venv/Lib/site-packages/django/db/backends/base/features.py +++ b/venv/Lib/site-packages/django/db/backends/base/features.py @@ -64,9 +64,6 @@ class BaseDatabaseFeatures: has_real_datatype = False supports_subqueries_in_group_by = True - # Does the backend ignore unnecessary ORDER BY clauses in subqueries? - ignores_unnecessary_order_by_in_subqueries = True - # Is there a true datatype for uuid? has_native_uuid_field = False @@ -133,21 +130,21 @@ class BaseDatabaseFeatures: # Map fields which some backends may not be able to differentiate to the # field it's introspected as. introspected_field_types = { - "AutoField": "AutoField", - "BigAutoField": "BigAutoField", - "BigIntegerField": "BigIntegerField", - "BinaryField": "BinaryField", - "BooleanField": "BooleanField", - "CharField": "CharField", - "DurationField": "DurationField", - "GenericIPAddressField": "GenericIPAddressField", - "IntegerField": "IntegerField", - "PositiveBigIntegerField": "PositiveBigIntegerField", - "PositiveIntegerField": "PositiveIntegerField", - "PositiveSmallIntegerField": "PositiveSmallIntegerField", - "SmallAutoField": "SmallAutoField", - "SmallIntegerField": "SmallIntegerField", - "TimeField": "TimeField", + 'AutoField': 'AutoField', + 'BigAutoField': 'BigAutoField', + 'BigIntegerField': 'BigIntegerField', + 'BinaryField': 'BinaryField', + 'BooleanField': 'BooleanField', + 'CharField': 'CharField', + 'DurationField': 'DurationField', + 'GenericIPAddressField': 'GenericIPAddressField', + 'IntegerField': 'IntegerField', + 'PositiveBigIntegerField': 'PositiveBigIntegerField', + 'PositiveIntegerField': 'PositiveIntegerField', + 'PositiveSmallIntegerField': 'PositiveSmallIntegerField', + 'SmallAutoField': 'SmallAutoField', + 'SmallIntegerField': 'SmallIntegerField', + 'TimeField': 'TimeField', } # Can the backend introspect the column order (ASC/DESC) for indexes? @@ -204,7 +201,7 @@ class BaseDatabaseFeatures: has_case_insensitive_like = True # Suffix for backends that don't support "SELECT xxx;" queries. - bare_select_suffix = "" + bare_select_suffix = '' # If NULL is implied on columns without needing to be explicitly specified implied_column_null = False @@ -324,13 +321,11 @@ class BaseDatabaseFeatures: # Collation names for use by the Django test suite. test_collations = { - "ci": None, # Case-insensitive. - "cs": None, # Case-sensitive. - "non_default": None, # Non-default. - "swedish_ci": None, # Swedish case-insensitive. + 'ci': None, # Case-insensitive. + 'cs': None, # Case-sensitive. + 'non_default': None, # Non-default. + 'swedish_ci': None # Swedish case-insensitive. } - # SQL template override for tests.aggregation.tests.NowUTC - test_now_utc_template = None # A set of dotted paths to tests in Django's test suite that are expected # to fail on this database. @@ -351,14 +346,14 @@ class BaseDatabaseFeatures: def supports_transactions(self): """Confirm support for transactions.""" with self.connection.cursor() as cursor: - cursor.execute("CREATE TABLE ROLLBACK_TEST (X INT)") + cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)') self.connection.set_autocommit(False) - cursor.execute("INSERT INTO ROLLBACK_TEST (X) VALUES (8)") + cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)') self.connection.rollback() self.connection.set_autocommit(True) - cursor.execute("SELECT COUNT(X) FROM ROLLBACK_TEST") - (count,) = cursor.fetchone() - cursor.execute("DROP TABLE ROLLBACK_TEST") + cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST') + count, = cursor.fetchone() + cursor.execute('DROP TABLE ROLLBACK_TEST') return count == 0 def allows_group_by_selected_pks_on_model(self, model): diff --git a/venv/Lib/site-packages/django/db/backends/base/introspection.py b/venv/Lib/site-packages/django/db/backends/base/introspection.py index 95e0e90..c8b0e90 100644 --- a/venv/Lib/site-packages/django/db/backends/base/introspection.py +++ b/venv/Lib/site-packages/django/db/backends/base/introspection.py @@ -1,19 +1,18 @@ from collections import namedtuple # Structure returned by DatabaseIntrospection.get_table_list() -TableInfo = namedtuple("TableInfo", ["name", "type"]) +TableInfo = namedtuple('TableInfo', ['name', 'type']) # Structure returned by the DB-API cursor.description interface (PEP 249) FieldInfo = namedtuple( - "FieldInfo", - "name type_code display_size internal_size precision scale null_ok " - "default collation", + 'FieldInfo', + 'name type_code display_size internal_size precision scale null_ok ' + 'default collation' ) class BaseDatabaseIntrospection: """Encapsulate backend-specific introspection utilities.""" - data_types_reverse = {} def __init__(self, connection): @@ -44,14 +43,9 @@ class BaseDatabaseIntrospection: the database's ORDER BY here to avoid subtle differences in sorting order between databases. """ - def get_names(cursor): - return sorted( - ti.name - for ti in self.get_table_list(cursor) - if include_views or ti.type == "t" - ) - + return sorted(ti.name for ti in self.get_table_list(cursor) + if include_views or ti.type == 't') if cursor is None: with self.connection.cursor() as cursor: return get_names(cursor) @@ -62,10 +56,7 @@ class BaseDatabaseIntrospection: Return an unsorted list of TableInfo named tuples of all tables and views that exist in the database. """ - raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a get_table_list() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_table_list() method') def get_table_description(self, cursor, table_name): """ @@ -73,14 +64,13 @@ class BaseDatabaseIntrospection: interface. """ raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a " - "get_table_description() method." + 'subclasses of BaseDatabaseIntrospection may require a ' + 'get_table_description() method.' ) def get_migratable_models(self): from django.apps import apps from django.db import router - return ( model for app_config in apps.get_app_configs() @@ -101,15 +91,16 @@ class BaseDatabaseIntrospection: continue tables.add(model._meta.db_table) tables.update( - f.m2m_db_table() - for f in model._meta.local_many_to_many + f.m2m_db_table() for f in model._meta.local_many_to_many if f.remote_field.through._meta.managed ) tables = list(tables) if only_existing: existing_tables = set(self.table_names(include_views=include_views)) tables = [ - t for t in tables if self.identifier_converter(t) in existing_tables + t + for t in tables + if self.identifier_converter(t) in existing_tables ] return tables @@ -120,8 +111,7 @@ class BaseDatabaseIntrospection: """ tables = set(map(self.identifier_converter, tables)) return { - m - for m in self.get_migratable_models() + m for m in self.get_migratable_models() if self.identifier_converter(m._meta.db_table) in tables } @@ -137,19 +127,13 @@ class BaseDatabaseIntrospection: continue if model._meta.swapped: continue - sequence_list.extend( - self.get_sequences( - cursor, model._meta.db_table, model._meta.local_fields - ) - ) + sequence_list.extend(self.get_sequences(cursor, model._meta.db_table, model._meta.local_fields)) for f in model._meta.local_many_to_many: # If this is an m2m using an intermediate table, # we don't need to reset the sequence. if f.remote_field.through._meta.auto_created: sequence = self.get_sequences(cursor, f.m2m_db_table()) - sequence_list.extend( - sequence or [{"table": f.m2m_db_table(), "column": None}] - ) + sequence_list.extend(sequence or [{'table': f.m2m_db_table(), 'column': None}]) return sequence_list def get_sequences(self, cursor, table_name, table_fields=()): @@ -158,10 +142,7 @@ class BaseDatabaseIntrospection: is a dict: {'table': <table_name>, 'column': <column_name>}. An optional 'name' key can be added if the backend supports named sequences. """ - raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a get_sequences() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_sequences() method') def get_relations(self, cursor, table_name): """ @@ -170,8 +151,8 @@ class BaseDatabaseIntrospection: relationships to the given table. """ raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a " - "get_relations() method." + 'subclasses of BaseDatabaseIntrospection may require a ' + 'get_relations() method.' ) def get_key_columns(self, cursor, table_name): @@ -180,18 +161,15 @@ class BaseDatabaseIntrospection: (column_name, referenced_table_name, referenced_column_name) for all key columns in given table. """ - raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a get_key_columns() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_key_columns() method') def get_primary_key_column(self, cursor, table_name): """ Return the name of the primary key column for the given table. """ for constraint in self.get_constraints(cursor, table_name).values(): - if constraint["primary_key"]: - return constraint["columns"][0] + if constraint['primary_key']: + return constraint['columns'][0] return None def get_constraints(self, cursor, table_name): @@ -213,7 +191,4 @@ class BaseDatabaseIntrospection: Some backends may return special constraint names that don't exist if they don't name constraints of a certain type (e.g. SQLite) """ - raise NotImplementedError( - "subclasses of BaseDatabaseIntrospection may require a get_constraints() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_constraints() method') diff --git a/venv/Lib/site-packages/django/db/backends/base/operations.py b/venv/Lib/site-packages/django/db/backends/base/operations.py index 322a89c..0fcc607 100644 --- a/venv/Lib/site-packages/django/db/backends/base/operations.py +++ b/venv/Lib/site-packages/django/db/backends/base/operations.py @@ -16,26 +16,25 @@ class BaseDatabaseOperations: Encapsulate backend-specific differences, such as the way a backend performs ordering or calculates the ID of a recently-inserted row. """ - compiler_module = "django.db.models.sql.compiler" # Integer field safe ranges by `internal_type` as documented # in docs/ref/models/fields.txt. integer_field_ranges = { - "SmallIntegerField": (-32768, 32767), - "IntegerField": (-2147483648, 2147483647), - "BigIntegerField": (-9223372036854775808, 9223372036854775807), - "PositiveBigIntegerField": (0, 9223372036854775807), - "PositiveSmallIntegerField": (0, 32767), - "PositiveIntegerField": (0, 2147483647), - "SmallAutoField": (-32768, 32767), - "AutoField": (-2147483648, 2147483647), - "BigAutoField": (-9223372036854775808, 9223372036854775807), + 'SmallIntegerField': (-32768, 32767), + 'IntegerField': (-2147483648, 2147483647), + 'BigIntegerField': (-9223372036854775808, 9223372036854775807), + 'PositiveBigIntegerField': (0, 9223372036854775807), + 'PositiveSmallIntegerField': (0, 32767), + 'PositiveIntegerField': (0, 2147483647), + 'SmallAutoField': (-32768, 32767), + 'AutoField': (-2147483648, 2147483647), + 'BigAutoField': (-9223372036854775808, 9223372036854775807), } set_operators = { - "union": "UNION", - "intersection": "INTERSECT", - "difference": "EXCEPT", + 'union': 'UNION', + 'intersection': 'INTERSECT', + 'difference': 'EXCEPT', } # Mapping of Field.get_internal_type() (typically the model field's class # name) to the data type to use for the Cast() function, if different from @@ -45,11 +44,11 @@ class BaseDatabaseOperations: cast_char_field_without_max_length = None # Start and end points for window expressions. - PRECEDING = "PRECEDING" - FOLLOWING = "FOLLOWING" - UNBOUNDED_PRECEDING = "UNBOUNDED " + PRECEDING - UNBOUNDED_FOLLOWING = "UNBOUNDED " + FOLLOWING - CURRENT_ROW = "CURRENT ROW" + PRECEDING = 'PRECEDING' + FOLLOWING = 'FOLLOWING' + UNBOUNDED_PRECEDING = 'UNBOUNDED ' + PRECEDING + UNBOUNDED_FOLLOWING = 'UNBOUNDED ' + FOLLOWING + CURRENT_ROW = 'CURRENT ROW' # Prefix for EXPLAIN queries, or None EXPLAIN isn't supported. explain_prefix = None @@ -91,17 +90,14 @@ class BaseDatabaseOperations: to that type. The resulting string should contain a '%s' placeholder for the expression being cast. """ - return "%s" + return '%s' def date_extract_sql(self, lookup_type, field_name): """ Given a lookup_type of 'year', 'month', or 'day', return the SQL that extracts a value from the given date field field_name. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a date_extract_sql() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_extract_sql() method') def date_trunc_sql(self, lookup_type, field_name, tzname=None): """ @@ -112,28 +108,22 @@ class BaseDatabaseOperations: If `tzname` is provided, the given value is truncated in a specific timezone. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a date_trunc_sql() " - "method." - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_trunc_sql() method.') def datetime_cast_date_sql(self, field_name, tzname): """ Return the SQL to cast a datetime value to date value. """ raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a " - "datetime_cast_date_sql() method." + 'subclasses of BaseDatabaseOperations may require a ' + 'datetime_cast_date_sql() method.' ) def datetime_cast_time_sql(self, field_name, tzname): """ Return the SQL to cast a datetime value to time value. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a " - "datetime_cast_time_sql() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_cast_time_sql() method') def datetime_extract_sql(self, lookup_type, field_name, tzname): """ @@ -141,10 +131,7 @@ class BaseDatabaseOperations: 'second', return the SQL that extracts a value from the given datetime field field_name. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a datetime_extract_sql() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_extract_sql() method') def datetime_trunc_sql(self, lookup_type, field_name, tzname): """ @@ -152,10 +139,7 @@ class BaseDatabaseOperations: 'second', return the SQL that truncates the given datetime field field_name to a datetime object with only the given specificity. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a datetime_trunc_sql() " - "method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_trunc_sql() method') def time_trunc_sql(self, lookup_type, field_name, tzname=None): """ @@ -166,9 +150,7 @@ class BaseDatabaseOperations: If `tzname` is provided, the given value is truncated in a specific timezone. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a time_trunc_sql() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a time_trunc_sql() method') def time_extract_sql(self, lookup_type, field_name): """ @@ -182,7 +164,7 @@ class BaseDatabaseOperations: Return the SQL to make a constraint "initially deferred" during a CREATE TABLE statement. """ - return "" + return '' def distinct_sql(self, fields, params): """ @@ -191,11 +173,9 @@ class BaseDatabaseOperations: duplicates. """ if fields: - raise NotSupportedError( - "DISTINCT ON fields is not supported by this database backend" - ) + raise NotSupportedError('DISTINCT ON fields is not supported by this database backend') else: - return ["DISTINCT"], [] + return ['DISTINCT'], [] def fetch_returned_insert_columns(self, cursor, returning_params): """ @@ -211,7 +191,7 @@ class BaseDatabaseOperations: it in a WHERE statement. The resulting string should contain a '%s' placeholder for the column being searched against. """ - return "%s" + return '%s' def force_no_ordering(self): """ @@ -224,11 +204,11 @@ class BaseDatabaseOperations: """ Return the FOR UPDATE SQL clause to lock rows for an update operation. """ - return "FOR%s UPDATE%s%s%s" % ( - " NO KEY" if no_key else "", - " OF %s" % ", ".join(of) if of else "", - " NOWAIT" if nowait else "", - " SKIP LOCKED" if skip_locked else "", + return 'FOR%s UPDATE%s%s%s' % ( + ' NO KEY' if no_key else '', + ' OF %s' % ', '.join(of) if of else '', + ' NOWAIT' if nowait else '', + ' SKIP LOCKED' if skip_locked else '', ) def _get_limit_offset_params(self, low_mark, high_mark): @@ -242,14 +222,10 @@ class BaseDatabaseOperations: def limit_offset_sql(self, low_mark, high_mark): """Return LIMIT/OFFSET SQL clause.""" limit, offset = self._get_limit_offset_params(low_mark, high_mark) - return " ".join( - sql - for sql in ( - ("LIMIT %d" % limit) if limit else None, - ("OFFSET %d" % offset) if offset else None, - ) - if sql - ) + return ' '.join(sql for sql in ( + ('LIMIT %d' % limit) if limit else None, + ('OFFSET %d' % offset) if offset else None, + ) if sql) def last_executed_query(self, cursor, sql, params): """ @@ -263,8 +239,7 @@ class BaseDatabaseOperations: """ # Convert params to contain string values. def to_string(s): - return force_str(s, strings_only=True, errors="replace") - + return force_str(s, strings_only=True, errors='replace') if isinstance(params, (list, tuple)): u_params = tuple(to_string(val) for val in params) elif params is None: @@ -310,16 +285,14 @@ class BaseDatabaseOperations: Return the value to use for the LIMIT when we are wanting "LIMIT infinity". Return None if the limit clause can be omitted in this case. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a no_limit_value() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a no_limit_value() method') def pk_default_value(self): """ Return the value to use during an INSERT statement to specify that the field should use its default value. """ - return "DEFAULT" + return 'DEFAULT' def prepare_sql_script(self, sql): """ @@ -332,8 +305,7 @@ class BaseDatabaseOperations: """ return [ sqlparse.format(statement, strip_comments=True) - for statement in sqlparse.split(sql) - if statement + for statement in sqlparse.split(sql) if statement ] def process_clob(self, value): @@ -366,9 +338,7 @@ class BaseDatabaseOperations: Return a quoted version of the given table, index, or column name. Do not quote the given name if it's already been quoted. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a quote_name() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a quote_name() method') def regex_lookup(self, lookup_type): """ @@ -379,9 +349,7 @@ class BaseDatabaseOperations: If the feature is not supported (or part of it is not supported), raise NotImplementedError. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations may require a regex_lookup() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations may require a regex_lookup() method') def savepoint_create_sql(self, sid): """ @@ -409,7 +377,7 @@ class BaseDatabaseOperations: Return '' if the backend doesn't support time zones. """ - return "" + return '' def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False): """ @@ -427,9 +395,7 @@ class BaseDatabaseOperations: to tables with foreign keys pointing the tables being truncated. PostgreSQL requires a cascade even if these tables are empty. """ - raise NotImplementedError( - "subclasses of BaseDatabaseOperations must provide an sql_flush() method" - ) + raise NotImplementedError('subclasses of BaseDatabaseOperations must provide an sql_flush() method') def execute_sql_flush(self, sql_list): """Execute a list of SQL statements to flush the database.""" @@ -480,7 +446,7 @@ class BaseDatabaseOperations: If `inline` is True, append the SQL to a row; otherwise append it to the entire CREATE TABLE or CREATE INDEX statement. """ - return "" + return '' def prep_for_like_query(self, x): """Prepare a value for use in a LIKE query.""" @@ -506,7 +472,7 @@ class BaseDatabaseOperations: cases where the target type isn't known, such as .raw() SQL queries. As a consequence it may not work perfectly in all circumstances. """ - if isinstance(value, datetime.datetime): # must be before date + if isinstance(value, datetime.datetime): # must be before date return self.adapt_datetimefield_value(value) elif isinstance(value, datetime.date): return self.adapt_datefield_value(value) @@ -560,44 +526,30 @@ class BaseDatabaseOperations: """ return value or None - def year_lookup_bounds_for_date_field(self, value, iso_year=False): + def year_lookup_bounds_for_date_field(self, value): """ Return a two-elements list with the lower and upper bound to be used with a BETWEEN operator to query a DateField value using a year lookup. `value` is an int, containing the looked-up year. - If `iso_year` is True, return bounds for ISO-8601 week-numbering years. """ - if iso_year: - first = datetime.date.fromisocalendar(value, 1, 1) - second = datetime.date.fromisocalendar( - value + 1, 1, 1 - ) - datetime.timedelta(days=1) - else: - first = datetime.date(value, 1, 1) - second = datetime.date(value, 12, 31) + first = datetime.date(value, 1, 1) + second = datetime.date(value, 12, 31) first = self.adapt_datefield_value(first) second = self.adapt_datefield_value(second) return [first, second] - def year_lookup_bounds_for_datetime_field(self, value, iso_year=False): + def year_lookup_bounds_for_datetime_field(self, value): """ Return a two-elements list with the lower and upper bound to be used with a BETWEEN operator to query a DateTimeField value using a year lookup. `value` is an int, containing the looked-up year. - If `iso_year` is True, return bounds for ISO-8601 week-numbering years. """ - if iso_year: - first = datetime.datetime.fromisocalendar(value, 1, 1) - second = datetime.datetime.fromisocalendar( - value + 1, 1, 1 - ) - datetime.timedelta(microseconds=1) - else: - first = datetime.datetime(value, 1, 1) - second = datetime.datetime(value, 12, 31, 23, 59, 59, 999999) + first = datetime.datetime(value, 1, 1) + second = datetime.datetime(value, 12, 31, 23, 59, 59, 999999) if settings.USE_TZ: tz = timezone.get_current_timezone() first = timezone.make_aware(first, tz) @@ -644,7 +596,7 @@ class BaseDatabaseOperations: can vary between backends (e.g., Oracle with %% and &) and between subexpression types (e.g., date expressions). """ - conn = " %s " % connector + conn = ' %s ' % connector return conn.join(sub_expressions) def combine_duration_expression(self, connector, sub_expressions): @@ -655,7 +607,7 @@ class BaseDatabaseOperations: Some backends require special syntax to insert binary content (MySQL for example uses '_binary %s'). """ - return "%s" + return '%s' def modify_insert_params(self, placeholder, params): """ @@ -676,76 +628,66 @@ class BaseDatabaseOperations: if self.connection.features.supports_temporal_subtraction: lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs - return "(%s - %s)" % (lhs_sql, rhs_sql), (*lhs_params, *rhs_params) - raise NotSupportedError( - "This backend does not support %s subtraction." % internal_type - ) + return '(%s - %s)' % (lhs_sql, rhs_sql), (*lhs_params, *rhs_params) + raise NotSupportedError("This backend does not support %s subtraction." % internal_type) def window_frame_start(self, start): if isinstance(start, int): if start < 0: - return "%d %s" % (abs(start), self.PRECEDING) + return '%d %s' % (abs(start), self.PRECEDING) elif start == 0: return self.CURRENT_ROW elif start is None: return self.UNBOUNDED_PRECEDING - raise ValueError( - "start argument must be a negative integer, zero, or None, but got '%s'." - % start - ) + raise ValueError("start argument must be a negative integer, zero, or None, but got '%s'." % start) def window_frame_end(self, end): if isinstance(end, int): if end == 0: return self.CURRENT_ROW elif end > 0: - return "%d %s" % (end, self.FOLLOWING) + return '%d %s' % (end, self.FOLLOWING) elif end is None: return self.UNBOUNDED_FOLLOWING - raise ValueError( - "end argument must be a positive integer, zero, or None, but got '%s'." - % end - ) + raise ValueError("end argument must be a positive integer, zero, or None, but got '%s'." % end) def window_frame_rows_start_end(self, start=None, end=None): """ Return SQL for start and end points in an OVER clause window frame. """ if not self.connection.features.supports_over_clause: - raise NotSupportedError("This backend does not support window expressions.") + raise NotSupportedError('This backend does not support window expressions.') return self.window_frame_start(start), self.window_frame_end(end) def window_frame_range_start_end(self, start=None, end=None): start_, end_ = self.window_frame_rows_start_end(start, end) - features = self.connection.features - if features.only_supports_unbounded_with_preceding_and_following and ( - (start and start < 0) or (end and end > 0) + if ( + self.connection.features.only_supports_unbounded_with_preceding_and_following and + ((start and start < 0) or (end and end > 0)) ): raise NotSupportedError( - "%s only supports UNBOUNDED together with PRECEDING and " - "FOLLOWING." % self.connection.display_name + '%s only supports UNBOUNDED together with PRECEDING and ' + 'FOLLOWING.' % self.connection.display_name ) return start_, end_ def explain_query_prefix(self, format=None, **options): if not self.connection.features.supports_explaining_query_execution: - raise NotSupportedError( - "This backend does not support explaining query execution." - ) + raise NotSupportedError('This backend does not support explaining query execution.') if format: supported_formats = self.connection.features.supported_explain_formats normalized_format = format.upper() if normalized_format not in supported_formats: - msg = "%s is not a recognized format." % normalized_format + msg = '%s is not a recognized format.' % normalized_format if supported_formats: - msg += " Allowed formats: %s" % ", ".join(sorted(supported_formats)) + msg += ' Allowed formats: %s' % ', '.join(sorted(supported_formats)) raise ValueError(msg) if options: - raise ValueError("Unknown options: %s" % ", ".join(sorted(options.keys()))) + raise ValueError('Unknown options: %s' % ', '.join(sorted(options.keys()))) return self.explain_prefix def insert_statement(self, ignore_conflicts=False): - return "INSERT INTO" + return 'INSERT INTO' def ignore_conflicts_suffix_sql(self, ignore_conflicts=None): - return "" + return '' diff --git a/venv/Lib/site-packages/django/db/backends/base/schema.py b/venv/Lib/site-packages/django/db/backends/base/schema.py index fd8211e..73b4e5c 100644 --- a/venv/Lib/site-packages/django/db/backends/base/schema.py +++ b/venv/Lib/site-packages/django/db/backends/base/schema.py @@ -2,12 +2,7 @@ import logging from datetime import datetime from django.db.backends.ddl_references import ( - Columns, - Expressions, - ForeignKeyName, - IndexName, - Statement, - Table, + Columns, Expressions, ForeignKeyName, IndexName, Statement, Table, ) from django.db.backends.utils import names_digest, split_identifier from django.db.models import Deferrable, Index @@ -15,7 +10,7 @@ from django.db.models.sql import Query from django.db.transaction import TransactionManagementError, atomic from django.utils import timezone -logger = logging.getLogger("django.db.backends.schema") +logger = logging.getLogger('django.db.backends.schema') def _is_relevant_relation(relation, altered_field): @@ -35,35 +30,16 @@ def _is_relevant_relation(relation, altered_field): def _all_related_fields(model): - return model._meta._get_fields( - forward=False, - reverse=True, - include_hidden=True, - include_parents=False, - ) + return model._meta._get_fields(forward=False, reverse=True, include_hidden=True) def _related_non_m2m_objects(old_field, new_field): # Filter out m2m objects from reverse relations. # Return (old_relation, new_relation) tuples. - related_fields = zip( - ( - obj - for obj in _all_related_fields(old_field.model) - if _is_relevant_relation(obj, old_field) - ), - ( - obj - for obj in _all_related_fields(new_field.model) - if _is_relevant_relation(obj, new_field) - ), + return zip( + (obj for obj in _all_related_fields(old_field.model) if _is_relevant_relation(obj, old_field)), + (obj for obj in _all_related_fields(new_field.model) if _is_relevant_relation(obj, new_field)), ) - for old_rel, new_rel in related_fields: - yield old_rel, new_rel - yield from _related_non_m2m_objects( - old_rel.remote_field, - new_rel.remote_field, - ) class BaseDatabaseSchemaEditor: @@ -89,12 +65,8 @@ class BaseDatabaseSchemaEditor: sql_alter_column_no_default_null = sql_alter_column_no_default sql_alter_column_collate = "ALTER COLUMN %(column)s TYPE %(type)s%(collation)s" sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s CASCADE" - sql_rename_column = ( - "ALTER TABLE %(table)s RENAME COLUMN %(old_column)s TO %(new_column)s" - ) - sql_update_with_default = ( - "UPDATE %(table)s SET %(column)s = %(default)s WHERE %(column)s IS NULL" - ) + sql_rename_column = "ALTER TABLE %(table)s RENAME COLUMN %(old_column)s TO %(new_column)s" + sql_update_with_default = "UPDATE %(table)s SET %(column)s = %(default)s WHERE %(column)s IS NULL" sql_unique_constraint = "UNIQUE (%(columns)s)%(deferrable)s" sql_check_constraint = "CHECK (%(check)s)" @@ -104,10 +76,7 @@ class BaseDatabaseSchemaEditor: sql_create_check = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s CHECK (%(check)s)" sql_delete_check = sql_delete_constraint - sql_create_unique = ( - "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s " - "UNIQUE (%(columns)s)%(deferrable)s" - ) + sql_create_unique = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE (%(columns)s)%(deferrable)s" sql_delete_unique = sql_delete_constraint sql_create_fk = ( @@ -118,22 +87,14 @@ class BaseDatabaseSchemaEditor: sql_create_column_inline_fk = None sql_delete_fk = sql_delete_constraint - sql_create_index = ( - "CREATE INDEX %(name)s ON %(table)s " - "(%(columns)s)%(include)s%(extra)s%(condition)s" - ) - sql_create_unique_index = ( - "CREATE UNIQUE INDEX %(name)s ON %(table)s " - "(%(columns)s)%(include)s%(condition)s" - ) + sql_create_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(include)s%(extra)s%(condition)s" + sql_create_unique_index = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)%(include)s%(condition)s" sql_delete_index = "DROP INDEX %(name)s" - sql_create_pk = ( - "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)" - ) + sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)" sql_delete_pk = sql_delete_constraint - sql_delete_procedure = "DROP PROCEDURE %(procedure)s" + sql_delete_procedure = 'DROP PROCEDURE %(procedure)s' def __init__(self, connection, collect_sql=False, atomic=True): self.connection = connection @@ -164,11 +125,7 @@ class BaseDatabaseSchemaEditor: """Execute the given SQL statement, with optional parameters.""" # Don't perform the transactional DDL check if SQL is being collected # as it's not going to be executed anyway. - if ( - not self.collect_sql - and self.connection.in_atomic_block - and not self.connection.features.can_rollback_ddl - ): + if not self.collect_sql and self.connection.in_atomic_block and not self.connection.features.can_rollback_ddl: raise TransactionManagementError( "Executing DDL statements while in a transaction on databases " "that can't perform a rollback is prohibited." @@ -176,15 +133,11 @@ class BaseDatabaseSchemaEditor: # Account for non-string statement objects. sql = str(sql) # Log the command we're running, then run it - logger.debug( - "%s; (params %r)", sql, params, extra={"params": params, "sql": sql} - ) + logger.debug("%s; (params %r)", sql, params, extra={'params': params, 'sql': sql}) if self.collect_sql: - ending = "" if sql.rstrip().endswith(";") else ";" + ending = "" if sql.endswith(";") else ";" if params is not None: - self.collected_sql.append( - (sql % tuple(map(self.quote_value, params))) + ending - ) + self.collected_sql.append((sql % tuple(map(self.quote_value, params))) + ending) else: self.collected_sql.append(sql + ending) else: @@ -197,10 +150,10 @@ class BaseDatabaseSchemaEditor: def table_sql(self, model): """Take a model and return its table definition.""" # Add any unique_togethers (always deferred, as some fields might be - # created afterward, like geometry fields with some backends). - for field_names in model._meta.unique_together: - fields = [model._meta.get_field(field) for field in field_names] - self.deferred_sql.append(self._create_unique_sql(model, fields)) + # created afterwards, like geometry fields with some backends). + for fields in model._meta.unique_together: + columns = [model._meta.get_field(field).column for field in fields] + self.deferred_sql.append(self._create_unique_sql(model, columns)) # Create column SQL, add FK deferreds if needed. column_sqls = [] params = [] @@ -211,82 +164,70 @@ class BaseDatabaseSchemaEditor: continue # Check constraints can go on the column SQL here. db_params = field.db_parameters(connection=self.connection) - if db_params["check"]: - definition += " " + self.sql_check_constraint % db_params + if db_params['check']: + definition += ' ' + self.sql_check_constraint % db_params # Autoincrement SQL (for backends with inline variant). col_type_suffix = field.db_type_suffix(connection=self.connection) if col_type_suffix: - definition += " %s" % col_type_suffix + definition += ' %s' % col_type_suffix params.extend(extra_params) # FK. if field.remote_field and field.db_constraint: to_table = field.remote_field.model._meta.db_table - to_column = field.remote_field.model._meta.get_field( - field.remote_field.field_name - ).column + to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column if self.sql_create_inline_fk: - definition += " " + self.sql_create_inline_fk % { - "to_table": self.quote_name(to_table), - "to_column": self.quote_name(to_column), + definition += ' ' + self.sql_create_inline_fk % { + 'to_table': self.quote_name(to_table), + 'to_column': self.quote_name(to_column), } elif self.connection.features.supports_foreign_keys: - self.deferred_sql.append( - self._create_fk_sql( - model, field, "_fk_%(to_table)s_%(to_column)s" - ) - ) + self.deferred_sql.append(self._create_fk_sql(model, field, '_fk_%(to_table)s_%(to_column)s')) # Add the SQL to our big list. - column_sqls.append( - "%s %s" - % ( - self.quote_name(field.column), - definition, - ) - ) + column_sqls.append('%s %s' % ( + self.quote_name(field.column), + definition, + )) # Autoincrement SQL (for backends with post table definition # variant). - if field.get_internal_type() in ( - "AutoField", - "BigAutoField", - "SmallAutoField", - ): - autoinc_sql = self.connection.ops.autoinc_sql( - model._meta.db_table, field.column - ) + if field.get_internal_type() in ('AutoField', 'BigAutoField', 'SmallAutoField'): + autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column) if autoinc_sql: self.deferred_sql.extend(autoinc_sql) - constraints = [ - constraint.constraint_sql(model, self) - for constraint in model._meta.constraints - ] + constraints = [constraint.constraint_sql(model, self) for constraint in model._meta.constraints] sql = self.sql_create_table % { - "table": self.quote_name(model._meta.db_table), - "definition": ", ".join( - constraint for constraint in (*column_sqls, *constraints) if constraint - ), + 'table': self.quote_name(model._meta.db_table), + 'definition': ', '.join(constraint for constraint in (*column_sqls, *constraints) if constraint), } if model._meta.db_tablespace: - tablespace_sql = self.connection.ops.tablespace_sql( - model._meta.db_tablespace - ) + tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) if tablespace_sql: - sql += " " + tablespace_sql + sql += ' ' + tablespace_sql return sql, params # Field <-> database mapping functions - def _iter_column_sql(self, column_db_type, params, model, field, include_default): - yield column_db_type - collation = getattr(field, "db_collation", None) + def column_sql(self, model, field, include_default=False): + """ + Take a field and return its column definition. + The field must already have had set_attributes_from_name() called. + """ + # Get the column's type and use that as the basis of the SQL + db_params = field.db_parameters(connection=self.connection) + sql = db_params['type'] + params = [] + # Check for fields that aren't actually columns (e.g. M2M) + if sql is None: + return None, None + # Collation. + collation = getattr(field, 'db_collation', None) if collation: - yield self._collate_sql(collation) - # Work out nullability. + sql += self._collate_sql(collation) + # Work out nullability null = field.null - # Include a default value, if requested. + # If we were told to include a default value, do so include_default = ( - include_default - and not self.skip_default(field) - and + include_default and + not self.skip_default(field) and # Don't include a default value if it's a nullable field and the # default cannot be dropped in the ALTER COLUMN statement (e.g. # MySQL longtext and longblob). @@ -294,62 +235,36 @@ class BaseDatabaseSchemaEditor: ) if include_default: default_value = self.effective_default(field) + column_default = ' DEFAULT ' + self._column_default_sql(field) if default_value is not None: - column_default = "DEFAULT " + self._column_default_sql(field) if self.connection.features.requires_literal_defaults: - # Some databases can't take defaults as a parameter (Oracle). + # Some databases can't take defaults as a parameter (oracle) # If this is the case, the individual schema backend should - # implement prepare_default(). - yield column_default % self.prepare_default(default_value) + # implement prepare_default + sql += column_default % self.prepare_default(default_value) else: - yield column_default - params.append(default_value) + sql += column_default + params += [default_value] # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. - if ( - field.empty_strings_allowed - and not field.primary_key - and self.connection.features.interprets_empty_strings_as_nulls - ): + if (field.empty_strings_allowed and not field.primary_key and + self.connection.features.interprets_empty_strings_as_nulls): null = True - if not null: - yield "NOT NULL" - elif not self.connection.features.implied_column_null: - yield "NULL" + if null and not self.connection.features.implied_column_null: + sql += " NULL" + elif not null: + sql += " NOT NULL" + # Primary key/unique outputs if field.primary_key: - yield "PRIMARY KEY" + sql += " PRIMARY KEY" elif field.unique: - yield "UNIQUE" - # Optionally add the tablespace if it's an implicitly indexed column. + sql += " UNIQUE" + # Optionally add the tablespace if it's an implicitly indexed column tablespace = field.db_tablespace or model._meta.db_tablespace - if ( - tablespace - and self.connection.features.supports_tablespaces - and field.unique - ): - yield self.connection.ops.tablespace_sql(tablespace, inline=True) - - def column_sql(self, model, field, include_default=False): - """ - Return the column definition for a field. The field must already have - had set_attributes_from_name() called. - """ - # Get the column's type and use that as the basis of the SQL. - db_params = field.db_parameters(connection=self.connection) - column_db_type = db_params["type"] - # Check for fields that aren't actually columns (e.g. M2M). - if column_db_type is None: - return None, None - params = [] - return ( - " ".join( - # This appends to the params being returned. - self._iter_column_sql( - column_db_type, params, model, field, include_default - ) - ), - params, - ) + if tablespace and self.connection.features.supports_tablespaces and field.unique: + sql += " %s" % self.connection.ops.tablespace_sql(tablespace, inline=True) + # Return the sql + return sql, params def skip_default(self, field): """ @@ -370,8 +285,8 @@ class BaseDatabaseSchemaEditor: Only used for backends which have requires_literal_defaults feature """ raise NotImplementedError( - "subclasses of BaseDatabaseSchemaEditor for backends which have " - "requires_literal_defaults must provide a prepare_default() method" + 'subclasses of BaseDatabaseSchemaEditor for backends which have ' + 'requires_literal_defaults must provide a prepare_default() method' ) def _column_default_sql(self, field): @@ -379,7 +294,7 @@ class BaseDatabaseSchemaEditor: Return the SQL to use in a DEFAULT clause. The resulting string should contain a '%s' placeholder for a default value. """ - return "%s" + return '%s' @staticmethod def _effective_default(field): @@ -388,19 +303,18 @@ class BaseDatabaseSchemaEditor: default = field.get_default() elif not field.null and field.blank and field.empty_strings_allowed: if field.get_internal_type() == "BinaryField": - default = b"" + default = b'' else: - default = "" - elif getattr(field, "auto_now", False) or getattr(field, "auto_now_add", False): + default = '' + elif getattr(field, 'auto_now', False) or getattr(field, 'auto_now_add', False): + default = datetime.now() internal_type = field.get_internal_type() - if internal_type == "DateTimeField": + if internal_type == 'DateField': + default = default.date() + elif internal_type == 'TimeField': + default = default.time() + elif internal_type == 'DateTimeField': default = timezone.now() - else: - default = datetime.now() - if internal_type == "DateField": - default = default.date() - elif internal_type == "TimeField": - default = default.time() else: default = None return default @@ -427,12 +341,10 @@ class BaseDatabaseSchemaEditor: the given `model`. """ sql, params = self.table_sql(model) - # Prevent using [] as params, in the case a literal '%' is used in the - # definition. + # Prevent using [] as params, in the case a literal '%' is used in the definition self.execute(sql, params or None) - # Add any field index and index_together's (deferred as SQLite - # _remake_table needs it). + # Add any field index and index_together's (deferred as SQLite _remake_table needs it) self.deferred_sql.extend(self._model_indexes_sql(model)) # Make M2M tables @@ -448,24 +360,19 @@ class BaseDatabaseSchemaEditor: self.delete_model(field.remote_field.through) # Delete the table - self.execute( - self.sql_delete_table - % { - "table": self.quote_name(model._meta.db_table), - } - ) + self.execute(self.sql_delete_table % { + "table": self.quote_name(model._meta.db_table), + }) # Remove all deferred statements referencing the deleted table. for sql in list(self.deferred_sql): - if isinstance(sql, Statement) and sql.references_table( - model._meta.db_table - ): + if isinstance(sql, Statement) and sql.references_table(model._meta.db_table): self.deferred_sql.remove(sql) def add_index(self, model, index): """Add an index on a model.""" if ( - index.contains_expressions - and not self.connection.features.supports_expression_indexes + index.contains_expressions and + not self.connection.features.supports_expression_indexes ): return None # Index.create_sql returns interpolated SQL which makes params=None a @@ -475,8 +382,8 @@ class BaseDatabaseSchemaEditor: def remove_index(self, model, index): """Remove an index from a model.""" if ( - index.contains_expressions - and not self.connection.features.supports_expression_indexes + index.contains_expressions and + not self.connection.features.supports_expression_indexes ): return None self.execute(index.remove_sql(model, self)) @@ -505,13 +412,11 @@ class BaseDatabaseSchemaEditor: news = {tuple(fields) for fields in new_unique_together} # Deleted uniques for fields in olds.difference(news): - self._delete_composed_index( - model, fields, {"unique": True}, self.sql_delete_unique - ) + self._delete_composed_index(model, fields, {'unique': True}, self.sql_delete_unique) # Created uniques - for field_names in news.difference(olds): - fields = [model._meta.get_field(field) for field in field_names] - self.execute(self._create_unique_sql(model, fields)) + for fields in news.difference(olds): + columns = [model._meta.get_field(field).column for field in fields] + self.execute(self._create_unique_sql(model, columns)) def alter_index_together(self, model, old_index_together, new_index_together): """ @@ -526,51 +431,40 @@ class BaseDatabaseSchemaEditor: self._delete_composed_index( model, fields, - {"index": True, "unique": False}, + {'index': True, 'unique': False}, self.sql_delete_index, ) # Created indexes for field_names in news.difference(olds): fields = [model._meta.get_field(field) for field in field_names] - self.execute(self._create_index_sql(model, fields=fields, suffix="_idx")) + self.execute(self._create_index_sql(model, fields=fields, suffix='_idx')) def _delete_composed_index(self, model, fields, constraint_kwargs, sql): - meta_constraint_names = { - constraint.name for constraint in model._meta.constraints - } + meta_constraint_names = {constraint.name for constraint in model._meta.constraints} meta_index_names = {constraint.name for constraint in model._meta.indexes} columns = [model._meta.get_field(field).column for field in fields] constraint_names = self._constraint_names( - model, - columns, - exclude=meta_constraint_names | meta_index_names, - **constraint_kwargs, + model, columns, exclude=meta_constraint_names | meta_index_names, + **constraint_kwargs ) if len(constraint_names) != 1: - raise ValueError( - "Found wrong number (%s) of constraints for %s(%s)" - % ( - len(constraint_names), - model._meta.db_table, - ", ".join(columns), - ) - ) + raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % ( + len(constraint_names), + model._meta.db_table, + ", ".join(columns), + )) self.execute(self._delete_constraint_sql(sql, model, constraint_names[0])) def alter_db_table(self, model, old_db_table, new_db_table): """Rename the table a model points to.""" - if old_db_table == new_db_table or ( - self.connection.features.ignores_table_name_case - and old_db_table.lower() == new_db_table.lower() - ): + if (old_db_table == new_db_table or + (self.connection.features.ignores_table_name_case and + old_db_table.lower() == new_db_table.lower())): return - self.execute( - self.sql_rename_table - % { - "old_table": self.quote_name(old_db_table), - "new_table": self.quote_name(new_db_table), - } - ) + self.execute(self.sql_rename_table % { + "old_table": self.quote_name(old_db_table), + "new_table": self.quote_name(new_db_table), + }) # Rename all references to the old table name. for sql in self.deferred_sql: if isinstance(sql, Statement): @@ -578,14 +472,11 @@ class BaseDatabaseSchemaEditor: def alter_db_tablespace(self, model, old_db_tablespace, new_db_tablespace): """Move a model's table between tablespaces.""" - self.execute( - self.sql_retablespace_table - % { - "table": self.quote_name(model._meta.db_table), - "old_tablespace": self.quote_name(old_db_tablespace), - "new_tablespace": self.quote_name(new_db_tablespace), - } - ) + self.execute(self.sql_retablespace_table % { + "table": self.quote_name(model._meta.db_table), + "old_tablespace": self.quote_name(old_db_tablespace), + "new_tablespace": self.quote_name(new_db_tablespace), + }) def add_field(self, model, field): """ @@ -602,36 +493,26 @@ class BaseDatabaseSchemaEditor: return # Check constraints can go on the column SQL here db_params = field.db_parameters(connection=self.connection) - if db_params["check"]: + if db_params['check']: definition += " " + self.sql_check_constraint % db_params - if ( - field.remote_field - and self.connection.features.supports_foreign_keys - and field.db_constraint - ): - constraint_suffix = "_fk_%(to_table)s_%(to_column)s" + if field.remote_field and self.connection.features.supports_foreign_keys and field.db_constraint: + constraint_suffix = '_fk_%(to_table)s_%(to_column)s' # Add FK constraint inline, if supported. if self.sql_create_column_inline_fk: to_table = field.remote_field.model._meta.db_table - to_column = field.remote_field.model._meta.get_field( - field.remote_field.field_name - ).column + to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column namespace, _ = split_identifier(model._meta.db_table) definition += " " + self.sql_create_column_inline_fk % { - "name": self._fk_constraint_name(model, field, constraint_suffix), - "namespace": "%s." % self.quote_name(namespace) - if namespace - else "", - "column": self.quote_name(field.column), - "to_table": self.quote_name(to_table), - "to_column": self.quote_name(to_column), - "deferrable": self.connection.ops.deferrable_sql(), + 'name': self._fk_constraint_name(model, field, constraint_suffix), + 'namespace': '%s.' % self.quote_name(namespace) if namespace else '', + 'column': self.quote_name(field.column), + 'to_table': self.quote_name(to_table), + 'to_column': self.quote_name(to_column), + 'deferrable': self.connection.ops.deferrable_sql() } # Otherwise, add FK constraints later. else: - self.deferred_sql.append( - self._create_fk_sql(model, field, constraint_suffix) - ) + self.deferred_sql.append(self._create_fk_sql(model, field, constraint_suffix)) # Build the SQL and run it sql = self.sql_create_column % { "table": self.quote_name(model._meta.db_table), @@ -641,13 +522,8 @@ class BaseDatabaseSchemaEditor: self.execute(sql, params) # Drop the default if we need to # (Django usually does not use in-database defaults) - if ( - not self.skip_default_on_alter(field) - and self.effective_default(field) is not None - ): - changes_sql, params = self._alter_column_default_sql( - model, None, field, drop=True - ) + if not self.skip_default_on_alter(field) and self.effective_default(field) is not None: + changes_sql, params = self._alter_column_default_sql(model, None, field, drop=True) sql = self.sql_alter_column % { "table": self.quote_name(model._meta.db_table), "changes": changes_sql, @@ -668,7 +544,7 @@ class BaseDatabaseSchemaEditor: if field.many_to_many and field.remote_field.through._meta.auto_created: return self.delete_model(field.remote_field.through) # It might not actually have a column behind it - if field.db_parameters(connection=self.connection)["type"] is None: + if field.db_parameters(connection=self.connection)['type'] is None: return # Drop any FK constraints, MySQL requires explicit deletion if field.remote_field: @@ -686,9 +562,7 @@ class BaseDatabaseSchemaEditor: self.connection.close() # Remove all deferred statements referencing the deleted column. for sql in list(self.deferred_sql): - if isinstance(sql, Statement) and sql.references_column( - model._meta.db_table, field.column - ): + if isinstance(sql, Statement) and sql.references_column(model._meta.db_table, field.column): self.deferred_sql.remove(sql) def alter_field(self, model, old_field, new_field, strict=False): @@ -703,38 +577,25 @@ class BaseDatabaseSchemaEditor: return # Ensure this field is even column-based old_db_params = old_field.db_parameters(connection=self.connection) - old_type = old_db_params["type"] + old_type = old_db_params['type'] new_db_params = new_field.db_parameters(connection=self.connection) - new_type = new_db_params["type"] - if (old_type is None and old_field.remote_field is None) or ( - new_type is None and new_field.remote_field is None - ): + new_type = new_db_params['type'] + if ((old_type is None and old_field.remote_field is None) or + (new_type is None and new_field.remote_field is None)): raise ValueError( "Cannot alter field %s into %s - they do not properly define " - "db_type (are you using a badly-written custom field?)" - % (old_field, new_field), + "db_type (are you using a badly-written custom field?)" % + (old_field, new_field), ) - elif ( - old_type is None - and new_type is None - and ( - old_field.remote_field.through - and new_field.remote_field.through - and old_field.remote_field.through._meta.auto_created - and new_field.remote_field.through._meta.auto_created - ) - ): + elif old_type is None and new_type is None and ( + old_field.remote_field.through and new_field.remote_field.through and + old_field.remote_field.through._meta.auto_created and + new_field.remote_field.through._meta.auto_created): return self._alter_many_to_many(model, old_field, new_field, strict) - elif ( - old_type is None - and new_type is None - and ( - old_field.remote_field.through - and new_field.remote_field.through - and not old_field.remote_field.through._meta.auto_created - and not new_field.remote_field.through._meta.auto_created - ) - ): + elif old_type is None and new_type is None and ( + old_field.remote_field.through and new_field.remote_field.through and + not old_field.remote_field.through._meta.auto_created and + not new_field.remote_field.through._meta.auto_created): # Both sides have through models; this is a no-op. return elif old_type is None or new_type is None: @@ -744,86 +605,52 @@ class BaseDatabaseSchemaEditor: "through= on M2M fields)" % (old_field, new_field) ) - self._alter_field( - model, - old_field, - new_field, - old_type, - new_type, - old_db_params, - new_db_params, - strict, - ) + self._alter_field(model, old_field, new_field, old_type, new_type, + old_db_params, new_db_params, strict) - def _alter_field( - self, - model, - old_field, - new_field, - old_type, - new_type, - old_db_params, - new_db_params, - strict=False, - ): + def _alter_field(self, model, old_field, new_field, old_type, new_type, + old_db_params, new_db_params, strict=False): """Perform a "physical" (non-ManyToMany) field update.""" # Drop any FK constraints, we'll remake them later fks_dropped = set() if ( - self.connection.features.supports_foreign_keys - and old_field.remote_field - and old_field.db_constraint + self.connection.features.supports_foreign_keys and + old_field.remote_field and + old_field.db_constraint ): - fk_names = self._constraint_names( - model, [old_field.column], foreign_key=True - ) + fk_names = self._constraint_names(model, [old_field.column], foreign_key=True) if strict and len(fk_names) != 1: - raise ValueError( - "Found wrong number (%s) of foreign key constraints for %s.%s" - % ( - len(fk_names), - model._meta.db_table, - old_field.column, - ) - ) + raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % ( + len(fk_names), + model._meta.db_table, + old_field.column, + )) for fk_name in fk_names: fks_dropped.add((old_field.column,)) self.execute(self._delete_fk_sql(model, fk_name)) # Has unique been removed? - if old_field.unique and ( - not new_field.unique or self._field_became_primary_key(old_field, new_field) - ): + if old_field.unique and (not new_field.unique or self._field_became_primary_key(old_field, new_field)): # Find the unique constraint for this field - meta_constraint_names = { - constraint.name for constraint in model._meta.constraints - } + meta_constraint_names = {constraint.name for constraint in model._meta.constraints} constraint_names = self._constraint_names( - model, - [old_field.column], - unique=True, - primary_key=False, + model, [old_field.column], unique=True, primary_key=False, exclude=meta_constraint_names, ) if strict and len(constraint_names) != 1: - raise ValueError( - "Found wrong number (%s) of unique constraints for %s.%s" - % ( - len(constraint_names), - model._meta.db_table, - old_field.column, - ) - ) + raise ValueError("Found wrong number (%s) of unique constraints for %s.%s" % ( + len(constraint_names), + model._meta.db_table, + old_field.column, + )) for constraint_name in constraint_names: self.execute(self._delete_unique_sql(model, constraint_name)) # Drop incoming FK constraints if the field is a primary key or unique, # which might be a to_field target, and things are going to change. drop_foreign_keys = ( - self.connection.features.supports_foreign_keys - and ( - (old_field.primary_key and new_field.primary_key) - or (old_field.unique and new_field.unique) - ) - and old_type != new_type + self.connection.features.supports_foreign_keys and ( + (old_field.primary_key and new_field.primary_key) or + (old_field.unique and new_field.unique) + ) and old_type != new_type ) if drop_foreign_keys: # '_meta.related_field' also contains M2M reverse fields, these @@ -844,20 +671,13 @@ class BaseDatabaseSchemaEditor: # True | False | False | False # True | False | False | True # True | False | True | True - if ( - old_field.db_index - and not old_field.unique - and (not new_field.db_index or new_field.unique) - ): + if old_field.db_index and not old_field.unique and (not new_field.db_index or new_field.unique): # Find the index for this field meta_index_names = {index.name for index in model._meta.indexes} # Retrieve only BTREE indexes since this is what's created with # db_index=True. index_names = self._constraint_names( - model, - [old_field.column], - index=True, - type_=Index.suffix, + model, [old_field.column], index=True, type_=Index.suffix, exclude=meta_index_names, ) for index_name in index_names: @@ -866,58 +686,41 @@ class BaseDatabaseSchemaEditor: # is to look at its name (refs #28053). self.execute(self._delete_index_sql(model, index_name)) # Change check constraints? - if old_db_params["check"] != new_db_params["check"] and old_db_params["check"]: - meta_constraint_names = { - constraint.name for constraint in model._meta.constraints - } + if old_db_params['check'] != new_db_params['check'] and old_db_params['check']: + meta_constraint_names = {constraint.name for constraint in model._meta.constraints} constraint_names = self._constraint_names( - model, - [old_field.column], - check=True, + model, [old_field.column], check=True, exclude=meta_constraint_names, ) if strict and len(constraint_names) != 1: - raise ValueError( - "Found wrong number (%s) of check constraints for %s.%s" - % ( - len(constraint_names), - model._meta.db_table, - old_field.column, - ) - ) + raise ValueError("Found wrong number (%s) of check constraints for %s.%s" % ( + len(constraint_names), + model._meta.db_table, + old_field.column, + )) for constraint_name in constraint_names: self.execute(self._delete_check_sql(model, constraint_name)) # Have they renamed the column? if old_field.column != new_field.column: - self.execute( - self._rename_field_sql( - model._meta.db_table, old_field, new_field, new_type - ) - ) + self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type)) # Rename all references to the renamed column. for sql in self.deferred_sql: if isinstance(sql, Statement): - sql.rename_column_references( - model._meta.db_table, old_field.column, new_field.column - ) + sql.rename_column_references(model._meta.db_table, old_field.column, new_field.column) # Next, start accumulating actions to do actions = [] null_actions = [] post_actions = [] # Collation change? - old_collation = getattr(old_field, "db_collation", None) - new_collation = getattr(new_field, "db_collation", None) + old_collation = getattr(old_field, 'db_collation', None) + new_collation = getattr(new_field, 'db_collation', None) if old_collation != new_collation: # Collation change handles also a type change. - fragment = self._alter_column_collation_sql( - model, new_field, new_type, new_collation - ) + fragment = self._alter_column_collation_sql(model, new_field, new_type, new_collation) actions.append(fragment) # Type change? elif old_type != new_type: - fragment, other_actions = self._alter_column_type_sql( - model, old_field, new_field, new_type - ) + fragment, other_actions = self._alter_column_type_sql(model, old_field, new_field, new_type) actions.append(fragment) post_actions.extend(other_actions) # When changing a column NULL constraint to NOT NULL with a given @@ -932,22 +735,21 @@ class BaseDatabaseSchemaEditor: old_default = self.effective_default(old_field) new_default = self.effective_default(new_field) if ( - not self.skip_default_on_alter(new_field) - and old_default != new_default - and new_default is not None + not self.skip_default_on_alter(new_field) and + old_default != new_default and + new_default is not None ): needs_database_default = True - actions.append( - self._alter_column_default_sql(model, old_field, new_field) - ) + actions.append(self._alter_column_default_sql(model, old_field, new_field)) # Nullability change? if old_field.null != new_field.null: fragment = self._alter_column_null_sql(model, old_field, new_field) if fragment: null_actions.append(fragment) # Only if we have a default and there is a change from NULL to NOT NULL - four_way_default_alteration = new_field.has_default() and ( - old_field.null and not new_field.null + four_way_default_alteration = ( + new_field.has_default() and + (old_field.null and not new_field.null) ) if actions or null_actions: if not four_way_default_alteration: @@ -961,8 +763,7 @@ class BaseDatabaseSchemaEditor: # Apply those actions for sql, params in actions: self.execute( - self.sql_alter_column - % { + self.sql_alter_column % { "table": self.quote_name(model._meta.db_table), "changes": sql, }, @@ -971,8 +772,7 @@ class BaseDatabaseSchemaEditor: if four_way_default_alteration: # Update existing rows with default value self.execute( - self.sql_update_with_default - % { + self.sql_update_with_default % { "table": self.quote_name(model._meta.db_table), "column": self.quote_name(new_field.column), "default": "%s", @@ -983,8 +783,7 @@ class BaseDatabaseSchemaEditor: # now for sql, params in null_actions: self.execute( - self.sql_alter_column - % { + self.sql_alter_column % { "table": self.quote_name(model._meta.db_table), "changes": sql, }, @@ -998,7 +797,7 @@ class BaseDatabaseSchemaEditor: self._delete_primary_key(model, strict) # Added a unique? if self._unique_should_be_added(old_field, new_field): - self.execute(self._create_unique_sql(model, [new_field])) + self.execute(self._create_unique_sql(model, [new_field.column])) # Added an index? Add an index if db_index switched to True or a unique # constraint will no longer be used in lieu of an index. The following # lines from the truth table show all True cases; the rest are False: @@ -1008,11 +807,7 @@ class BaseDatabaseSchemaEditor: # False | False | True | False # False | True | True | False # True | True | True | False - if ( - (not old_field.db_index or old_field.unique) - and new_field.db_index - and not new_field.unique - ): + if (not old_field.db_index or old_field.unique) and new_field.db_index and not new_field.unique: self.execute(self._create_index_sql(model, fields=[new_field])) # Type alteration on primary key? Then we need to alter the column # referring to us. @@ -1028,13 +823,12 @@ class BaseDatabaseSchemaEditor: # Handle our type alters on the other end of rels from the PK stuff above for old_rel, new_rel in rels_to_update: rel_db_params = new_rel.field.db_parameters(connection=self.connection) - rel_type = rel_db_params["type"] + rel_type = rel_db_params['type'] fragment, other_actions = self._alter_column_type_sql( new_rel.related_model, old_rel.field, new_rel.field, rel_type ) self.execute( - self.sql_alter_column - % { + self.sql_alter_column % { "table": self.quote_name(new_rel.related_model._meta.db_table), "changes": fragment[0], }, @@ -1043,38 +837,23 @@ class BaseDatabaseSchemaEditor: for sql, params in other_actions: self.execute(sql, params) # Does it have a foreign key? - if ( - self.connection.features.supports_foreign_keys - and new_field.remote_field - and ( - fks_dropped or not old_field.remote_field or not old_field.db_constraint - ) - and new_field.db_constraint - ): - self.execute( - self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s") - ) + if (self.connection.features.supports_foreign_keys and new_field.remote_field and + (fks_dropped or not old_field.remote_field or not old_field.db_constraint) and + new_field.db_constraint): + self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s")) # Rebuild FKs that pointed to us if we previously had to drop them if drop_foreign_keys: - for _, rel in rels_to_update: - if rel.field.db_constraint: - self.execute( - self._create_fk_sql(rel.related_model, rel.field, "_fk") - ) + for rel in new_field.model._meta.related_objects: + if _is_relevant_relation(rel, new_field) and rel.field.db_constraint: + self.execute(self._create_fk_sql(rel.related_model, rel.field, "_fk")) # Does it have check constraints we need to add? - if old_db_params["check"] != new_db_params["check"] and new_db_params["check"]: - constraint_name = self._create_index_name( - model._meta.db_table, [new_field.column], suffix="_check" - ) - self.execute( - self._create_check_sql(model, constraint_name, new_db_params["check"]) - ) + if old_db_params['check'] != new_db_params['check'] and new_db_params['check']: + constraint_name = self._create_index_name(model._meta.db_table, [new_field.column], suffix='_check') + self.execute(self._create_check_sql(model, constraint_name, new_db_params['check'])) # Drop the default if we need to # (Django usually does not use in-database defaults) if needs_database_default: - changes_sql, params = self._alter_column_default_sql( - model, old_field, new_field, drop=True - ) + changes_sql, params = self._alter_column_default_sql(model, old_field, new_field, drop=True) sql = self.sql_alter_column % { "table": self.quote_name(model._meta.db_table), "changes": changes_sql, @@ -1091,24 +870,17 @@ class BaseDatabaseSchemaEditor: Return a (sql, params) fragment to set a column to null or non-null as required by new_field, or None if no changes are required. """ - if ( - self.connection.features.interprets_empty_strings_as_nulls - and new_field.empty_strings_allowed - ): + if (self.connection.features.interprets_empty_strings_as_nulls and + new_field.get_internal_type() in ("CharField", "TextField")): # The field is nullable in the database anyway, leave it alone. return else: new_db_params = new_field.db_parameters(connection=self.connection) - sql = ( - self.sql_alter_column_null - if new_field.null - else self.sql_alter_column_not_null - ) + sql = self.sql_alter_column_null if new_field.null else self.sql_alter_column_not_null return ( - sql - % { - "column": self.quote_name(new_field.column), - "type": new_db_params["type"], + sql % { + 'column': self.quote_name(new_field.column), + 'type': new_db_params['type'], }, [], ) @@ -1142,11 +914,10 @@ class BaseDatabaseSchemaEditor: else: sql = self.sql_alter_column_default return ( - sql - % { - "column": self.quote_name(new_field.column), - "type": new_db_params["type"], - "default": default, + sql % { + 'column': self.quote_name(new_field.column), + 'type': new_db_params['type'], + 'default': default, }, params, ) @@ -1163,8 +934,7 @@ class BaseDatabaseSchemaEditor: """ return ( ( - self.sql_alter_column_type - % { + self.sql_alter_column_type % { "column": self.quote_name(new_field.column), "type": new_type, }, @@ -1175,13 +945,10 @@ class BaseDatabaseSchemaEditor: def _alter_column_collation_sql(self, model, new_field, new_type, new_collation): return ( - self.sql_alter_column_collate - % { - "column": self.quote_name(new_field.column), - "type": new_type, - "collation": " " + self._collate_sql(new_collation) - if new_collation - else "", + self.sql_alter_column_collate % { + 'column': self.quote_name(new_field.column), + 'type': new_type, + 'collation': self._collate_sql(new_collation) if new_collation else '', }, [], ) @@ -1189,27 +956,16 @@ class BaseDatabaseSchemaEditor: def _alter_many_to_many(self, model, old_field, new_field, strict): """Alter M2Ms to repoint their to= endpoints.""" # Rename the through table - if ( - old_field.remote_field.through._meta.db_table - != new_field.remote_field.through._meta.db_table - ): - self.alter_db_table( - old_field.remote_field.through, - old_field.remote_field.through._meta.db_table, - new_field.remote_field.through._meta.db_table, - ) + if old_field.remote_field.through._meta.db_table != new_field.remote_field.through._meta.db_table: + self.alter_db_table(old_field.remote_field.through, old_field.remote_field.through._meta.db_table, + new_field.remote_field.through._meta.db_table) # Repoint the FK to the other side self.alter_field( new_field.remote_field.through, - # The field that points to the target model is needed, so we can - # tell alter_field to change it - this is m2m_reverse_field_name() - # (as opposed to m2m_field_name(), which points to our model). - old_field.remote_field.through._meta.get_field( - old_field.m2m_reverse_field_name() - ), - new_field.remote_field.through._meta.get_field( - new_field.m2m_reverse_field_name() - ), + # We need the field that points to the target model, so we can tell alter_field to change it - + # this is m2m_reverse_field_name() (as opposed to m2m_field_name, which points to our model) + old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()), + new_field.remote_field.through._meta.get_field(new_field.m2m_reverse_field_name()), ) self.alter_field( new_field.remote_field.through, @@ -1226,22 +982,19 @@ class BaseDatabaseSchemaEditor: and a unique digest and suffix. """ _, table_name = split_identifier(table_name) - hash_suffix_part = "%s%s" % ( - names_digest(table_name, *column_names, length=8), - suffix, - ) + hash_suffix_part = '%s%s' % (names_digest(table_name, *column_names, length=8), suffix) max_length = self.connection.ops.max_name_length() or 200 # If everything fits into max_length, use that name. - index_name = "%s_%s_%s" % (table_name, "_".join(column_names), hash_suffix_part) + index_name = '%s_%s_%s' % (table_name, '_'.join(column_names), hash_suffix_part) if len(index_name) <= max_length: return index_name # Shorten a long suffix. if len(hash_suffix_part) > max_length / 3: - hash_suffix_part = hash_suffix_part[: max_length // 3] + hash_suffix_part = hash_suffix_part[:max_length // 3] other_length = (max_length - len(hash_suffix_part)) // 2 - 1 - index_name = "%s_%s_%s" % ( + index_name = '%s_%s_%s' % ( table_name[:other_length], - "_".join(column_names)[:other_length], + '_'.join(column_names)[:other_length], hash_suffix_part, ) # Prepend D if needed to prevent the name from starting with an @@ -1257,38 +1010,25 @@ class BaseDatabaseSchemaEditor: elif model._meta.db_tablespace: db_tablespace = model._meta.db_tablespace if db_tablespace is not None: - return " " + self.connection.ops.tablespace_sql(db_tablespace) - return "" + return ' ' + self.connection.ops.tablespace_sql(db_tablespace) + return '' def _index_condition_sql(self, condition): if condition: - return " WHERE " + condition - return "" + return ' WHERE ' + condition + return '' def _index_include_sql(self, model, columns): if not columns or not self.connection.features.supports_covering_indexes: - return "" + return '' return Statement( - " INCLUDE (%(columns)s)", + ' INCLUDE (%(columns)s)', columns=Columns(model._meta.db_table, columns, self.quote_name), ) - def _create_index_sql( - self, - model, - *, - fields=None, - name=None, - suffix="", - using="", - db_tablespace=None, - col_suffixes=(), - sql=None, - opclasses=(), - condition=None, - include=None, - expressions=None, - ): + def _create_index_sql(self, model, *, fields=None, name=None, suffix='', using='', + db_tablespace=None, col_suffixes=(), sql=None, opclasses=(), + condition=None, include=None, expressions=None): """ Return the SQL statement to create the index for one or several fields or expressions. `sql` can be specified if the syntax differs from the @@ -1299,9 +1039,7 @@ class BaseDatabaseSchemaEditor: compiler = Query(model, alias_cols=False).get_compiler( connection=self.connection, ) - tablespace_sql = self._get_index_tablespace_sql( - model, fields, db_tablespace=db_tablespace - ) + tablespace_sql = self._get_index_tablespace_sql(model, fields, db_tablespace=db_tablespace) columns = [field.column for field in fields] sql_create_index = sql or self.sql_create_index table = model._meta.db_table @@ -1350,12 +1088,12 @@ class BaseDatabaseSchemaEditor: for field_names in model._meta.index_together: fields = [model._meta.get_field(field) for field in field_names] - output.append(self._create_index_sql(model, fields=fields, suffix="_idx")) + output.append(self._create_index_sql(model, fields=fields, suffix='_idx')) for index in model._meta.indexes: if ( - not index.contains_expressions - or self.connection.features.supports_expression_indexes + not index.contains_expressions or + self.connection.features.supports_expression_indexes ): output.append(index.create_sql(model, self)) return output @@ -1377,25 +1115,26 @@ class BaseDatabaseSchemaEditor: # - changing an attribute that doesn't affect the schema # - adding only a db_column and the column name is not changed non_database_attrs = [ - "blank", - "db_column", - "editable", - "error_messages", - "help_text", - "limit_choices_to", + 'blank', + 'db_column', + 'editable', + 'error_messages', + 'help_text', + 'limit_choices_to', # Database-level options are not supported, see #21961. - "on_delete", - "related_name", - "related_query_name", - "validators", - "verbose_name", + 'on_delete', + 'related_name', + 'related_query_name', + 'validators', + 'verbose_name', ] for attr in non_database_attrs: old_kwargs.pop(attr, None) new_kwargs.pop(attr, None) - return self.quote_name(old_field.column) != self.quote_name( - new_field.column - ) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) + return ( + self.quote_name(old_field.column) != self.quote_name(new_field.column) or + (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) + ) def _field_should_be_indexed(self, model, field): return field.db_index and not field.unique @@ -1421,11 +1160,7 @@ class BaseDatabaseSchemaEditor: name = self._fk_constraint_name(model, field, suffix) column = Columns(model._meta.db_table, [field.column], self.quote_name) to_table = Table(field.target_field.model._meta.db_table, self.quote_name) - to_column = Columns( - field.target_field.model._meta.db_table, - [field.target_field.column], - self.quote_name, - ) + to_column = Columns(field.target_field.model._meta.db_table, [field.target_field.column], self.quote_name) deferrable = self.connection.ops.deferrable_sql() return Statement( self.sql_create_fk, @@ -1455,31 +1190,24 @@ class BaseDatabaseSchemaEditor: def _deferrable_constraint_sql(self, deferrable): if deferrable is None: - return "" + return '' if deferrable == Deferrable.DEFERRED: - return " DEFERRABLE INITIALLY DEFERRED" + return ' DEFERRABLE INITIALLY DEFERRED' if deferrable == Deferrable.IMMEDIATE: - return " DEFERRABLE INITIALLY IMMEDIATE" + return ' DEFERRABLE INITIALLY IMMEDIATE' def _unique_sql( - self, - model, - fields, - name, - condition=None, - deferrable=None, - include=None, - opclasses=None, - expressions=None, + self, model, fields, name, condition=None, deferrable=None, + include=None, opclasses=None, ): if ( - deferrable - and not self.connection.features.supports_deferrable_unique_constraints + deferrable and + not self.connection.features.supports_deferrable_unique_constraints ): return None - if condition or include or opclasses or expressions: - # Databases support conditional, covering, and functional unique - # constraints via a unique index. + if condition or include or opclasses: + # Databases support conditional and covering unique constraints via + # a unique index. sql = self._create_unique_sql( model, fields, @@ -1487,69 +1215,49 @@ class BaseDatabaseSchemaEditor: condition=condition, include=include, opclasses=opclasses, - expressions=expressions, ) if sql: self.deferred_sql.append(sql) return None constraint = self.sql_unique_constraint % { - "columns": ", ".join([self.quote_name(field.column) for field in fields]), - "deferrable": self._deferrable_constraint_sql(deferrable), + 'columns': ', '.join(map(self.quote_name, fields)), + 'deferrable': self._deferrable_constraint_sql(deferrable), } return self.sql_constraint % { - "name": self.quote_name(name), - "constraint": constraint, + 'name': self.quote_name(name), + 'constraint': constraint, } def _create_unique_sql( - self, - model, - fields, - name=None, - condition=None, - deferrable=None, - include=None, - opclasses=None, - expressions=None, + self, model, columns, name=None, condition=None, deferrable=None, + include=None, opclasses=None, ): if ( ( - deferrable - and not self.connection.features.supports_deferrable_unique_constraints - ) - or (condition and not self.connection.features.supports_partial_indexes) - or (include and not self.connection.features.supports_covering_indexes) - or ( - expressions and not self.connection.features.supports_expression_indexes - ) + deferrable and + not self.connection.features.supports_deferrable_unique_constraints + ) or + (condition and not self.connection.features.supports_partial_indexes) or + (include and not self.connection.features.supports_covering_indexes) ): return None def create_unique_name(*args, **kwargs): return self.quote_name(self._create_index_name(*args, **kwargs)) - compiler = Query(model, alias_cols=False).get_compiler( - connection=self.connection - ) - table = model._meta.db_table - columns = [field.column for field in fields] + table = Table(model._meta.db_table, self.quote_name) if name is None: - name = IndexName(table, columns, "_uniq", create_unique_name) + name = IndexName(model._meta.db_table, columns, '_uniq', create_unique_name) else: name = self.quote_name(name) - if condition or include or opclasses or expressions: + columns = self._index_columns(table, columns, col_suffixes=(), opclasses=opclasses) + if condition or include or opclasses: sql = self.sql_create_unique_index else: sql = self.sql_create_unique - if columns: - columns = self._index_columns( - table, columns, col_suffixes=(), opclasses=opclasses - ) - else: - columns = Expressions(table, expressions, compiler, self.quote_value) return Statement( sql, - table=Table(table, self.quote_name), + table=table, name=name, columns=columns, condition=self._index_condition_sql(condition), @@ -1558,28 +1266,19 @@ class BaseDatabaseSchemaEditor: ) def _delete_unique_sql( - self, - model, - name, - condition=None, - deferrable=None, - include=None, + self, model, name, condition=None, deferrable=None, include=None, opclasses=None, - expressions=None, ): if ( ( - deferrable - and not self.connection.features.supports_deferrable_unique_constraints - ) - or (condition and not self.connection.features.supports_partial_indexes) - or (include and not self.connection.features.supports_covering_indexes) - or ( - expressions and not self.connection.features.supports_expression_indexes - ) + deferrable and + not self.connection.features.supports_deferrable_unique_constraints + ) or + (condition and not self.connection.features.supports_partial_indexes) or + (include and not self.connection.features.supports_covering_indexes) ): return None - if condition or include or opclasses or expressions: + if condition or include or opclasses: sql = self.sql_delete_index else: sql = self.sql_delete_unique @@ -1587,8 +1286,8 @@ class BaseDatabaseSchemaEditor: def _check_sql(self, name, check): return self.sql_constraint % { - "name": self.quote_name(name), - "constraint": self.sql_check_constraint % {"check": check}, + 'name': self.quote_name(name), + 'constraint': self.sql_check_constraint % {'check': check}, } def _create_check_sql(self, model, name, check): @@ -1609,18 +1308,9 @@ class BaseDatabaseSchemaEditor: name=self.quote_name(name), ) - def _constraint_names( - self, - model, - column_names=None, - unique=None, - primary_key=None, - index=None, - foreign_key=None, - check=None, - type_=None, - exclude=None, - ): + def _constraint_names(self, model, column_names=None, unique=None, + primary_key=None, index=None, foreign_key=None, + check=None, type_=None, exclude=None): """Return all constraint names matching the columns and conditions.""" if column_names is not None: column_names = [ @@ -1628,23 +1318,21 @@ class BaseDatabaseSchemaEditor: for name in column_names ] with self.connection.cursor() as cursor: - constraints = self.connection.introspection.get_constraints( - cursor, model._meta.db_table - ) + constraints = self.connection.introspection.get_constraints(cursor, model._meta.db_table) result = [] for name, infodict in constraints.items(): - if column_names is None or column_names == infodict["columns"]: - if unique is not None and infodict["unique"] != unique: + if column_names is None or column_names == infodict['columns']: + if unique is not None and infodict['unique'] != unique: continue - if primary_key is not None and infodict["primary_key"] != primary_key: + if primary_key is not None and infodict['primary_key'] != primary_key: continue - if index is not None and infodict["index"] != index: + if index is not None and infodict['index'] != index: continue - if check is not None and infodict["check"] != check: + if check is not None and infodict['check'] != check: continue - if foreign_key is not None and not infodict["foreign_key"]: + if foreign_key is not None and not infodict['foreign_key']: continue - if type_ is not None and infodict["type"] != type_: + if type_ is not None and infodict['type'] != type_: continue if not exclude or name not in exclude: result.append(name) @@ -1653,13 +1341,10 @@ class BaseDatabaseSchemaEditor: def _delete_primary_key(self, model, strict=False): constraint_names = self._constraint_names(model, primary_key=True) if strict and len(constraint_names) != 1: - raise ValueError( - "Found wrong number (%s) of PK constraints for %s" - % ( - len(constraint_names), - model._meta.db_table, - ) - ) + raise ValueError('Found wrong number (%s) of PK constraints for %s' % ( + len(constraint_names), + model._meta.db_table, + )) for constraint_name in constraint_names: self.execute(self._delete_primary_key_sql(model, constraint_name)) @@ -1668,9 +1353,7 @@ class BaseDatabaseSchemaEditor: self.sql_create_pk, table=Table(model._meta.db_table, self.quote_name), name=self.quote_name( - self._create_index_name( - model._meta.db_table, [field.column], suffix="_pk" - ) + self._create_index_name(model._meta.db_table, [field.column], suffix="_pk") ), columns=Columns(model._meta.db_table, [field.column], self.quote_name), ) @@ -1679,11 +1362,11 @@ class BaseDatabaseSchemaEditor: return self._delete_constraint_sql(self.sql_delete_pk, model, name) def _collate_sql(self, collation): - return "COLLATE " + self.quote_name(collation) + return ' COLLATE ' + self.quote_name(collation) def remove_procedure(self, procedure_name, param_types=()): sql = self.sql_delete_procedure % { - "procedure": self.quote_name(procedure_name), - "param_types": ",".join(param_types), + 'procedure': self.quote_name(procedure_name), + 'param_types': ','.join(param_types), } self.execute(sql) diff --git a/venv/Lib/site-packages/django/db/backends/base/validation.py b/venv/Lib/site-packages/django/db/backends/base/validation.py index d0e3e21..a02780a 100644 --- a/venv/Lib/site-packages/django/db/backends/base/validation.py +++ b/venv/Lib/site-packages/django/db/backends/base/validation.py @@ -1,6 +1,5 @@ class BaseDatabaseValidation: """Encapsulate backend-specific validation.""" - def __init__(self, connection): self.connection = connection @@ -10,12 +9,9 @@ class BaseDatabaseValidation: def check_field(self, field, **kwargs): errors = [] # Backends may implement a check_field_type() method. - if ( - hasattr(self, "check_field_type") - and - # Ignore any related fields. - not getattr(field, "remote_field", None) - ): + if (hasattr(self, 'check_field_type') and + # Ignore any related fields. + not getattr(field, 'remote_field', None)): # Ignore fields with unsupported features. db_supports_all_required_features = all( getattr(self.connection.features, feature, False) diff --git a/venv/Lib/site-packages/django/db/backends/ddl_references.py b/venv/Lib/site-packages/django/db/backends/ddl_references.py index 412d07a..c06386a 100644 --- a/venv/Lib/site-packages/django/db/backends/ddl_references.py +++ b/venv/Lib/site-packages/django/db/backends/ddl_references.py @@ -33,12 +33,10 @@ class Reference: pass def __repr__(self): - return "<%s %r>" % (self.__class__.__name__, str(self)) + return '<%s %r>' % (self.__class__.__name__, str(self)) def __str__(self): - raise NotImplementedError( - "Subclasses must define how they should be converted to string." - ) + raise NotImplementedError('Subclasses must define how they should be converted to string.') class Table(Reference): @@ -90,14 +88,12 @@ class Columns(TableColumns): try: suffix = self.col_suffixes[idx] if suffix: - col = "{} {}".format(col, suffix) + col = '{} {}'.format(col, suffix) except IndexError: pass return col - return ", ".join( - col_str(column, idx) for idx, column in enumerate(self.columns) - ) + return ', '.join(col_str(column, idx) for idx, column in enumerate(self.columns)) class IndexName(TableColumns): @@ -121,49 +117,35 @@ class IndexColumns(Columns): def col_str(column, idx): # Index.__init__() guarantees that self.opclasses is the same # length as self.columns. - col = "{} {}".format(self.quote_name(column), self.opclasses[idx]) + col = '{} {}'.format(self.quote_name(column), self.opclasses[idx]) try: suffix = self.col_suffixes[idx] if suffix: - col = "{} {}".format(col, suffix) + col = '{} {}'.format(col, suffix) except IndexError: pass return col - return ", ".join( - col_str(column, idx) for idx, column in enumerate(self.columns) - ) + return ', '.join(col_str(column, idx) for idx, column in enumerate(self.columns)) class ForeignKeyName(TableColumns): """Hold a reference to a foreign key name.""" - def __init__( - self, - from_table, - from_columns, - to_table, - to_columns, - suffix_template, - create_fk_name, - ): + def __init__(self, from_table, from_columns, to_table, to_columns, suffix_template, create_fk_name): self.to_reference = TableColumns(to_table, to_columns) self.suffix_template = suffix_template self.create_fk_name = create_fk_name - super().__init__( - from_table, - from_columns, - ) + super().__init__(from_table, from_columns,) def references_table(self, table): - return super().references_table(table) or self.to_reference.references_table( - table - ) + return super().references_table(table) or self.to_reference.references_table(table) def references_column(self, table, column): - return super().references_column( - table, column - ) or self.to_reference.references_column(table, column) + return ( + super().references_column(table, column) or + self.to_reference.references_column(table, column) + ) def rename_table_references(self, old_table, new_table): super().rename_table_references(old_table, new_table) @@ -175,8 +157,8 @@ class ForeignKeyName(TableColumns): def __str__(self): suffix = self.suffix_template % { - "to_table": self.to_reference.table, - "to_column": self.to_reference.columns[0], + 'to_table': self.to_reference.table, + 'to_column': self.to_reference.columns[0], } return self.create_fk_name(self.table, self.columns, suffix) @@ -189,31 +171,30 @@ class Statement(Reference): that might have to be adjusted if they're referencing a table or column that is removed """ - def __init__(self, template, **parts): self.template = template self.parts = parts def references_table(self, table): return any( - hasattr(part, "references_table") and part.references_table(table) + hasattr(part, 'references_table') and part.references_table(table) for part in self.parts.values() ) def references_column(self, table, column): return any( - hasattr(part, "references_column") and part.references_column(table, column) + hasattr(part, 'references_column') and part.references_column(table, column) for part in self.parts.values() ) def rename_table_references(self, old_table, new_table): for part in self.parts.values(): - if hasattr(part, "rename_table_references"): + if hasattr(part, 'rename_table_references'): part.rename_table_references(old_table, new_table) def rename_column_references(self, table, old_column, new_column): for part in self.parts.values(): - if hasattr(part, "rename_column_references"): + if hasattr(part, 'rename_column_references'): part.rename_column_references(table, old_column, new_column) def __str__(self): @@ -225,16 +206,17 @@ class Expressions(TableColumns): self.compiler = compiler self.expressions = expressions self.quote_value = quote_value - columns = [ - col.target.column - for col in self.compiler.query._gen_cols([self.expressions]) - ] + columns = [col.target.column for col in self.compiler.query._gen_cols([self.expressions])] super().__init__(table, columns) def rename_table_references(self, old_table, new_table): if self.table != old_table: return - self.expressions = self.expressions.relabeled_clone({old_table: new_table}) + expressions = deepcopy(self.expressions) + self.columns = [] + for col in self.compiler.query._gen_cols([expressions]): + col.alias = new_table + self.expressions = expressions super().rename_table_references(old_table, new_table) def rename_column_references(self, table, old_column, new_column): diff --git a/venv/Lib/site-packages/django/db/backends/dummy/base.py b/venv/Lib/site-packages/django/db/backends/dummy/base.py index eed0141..c6a533e 100644 --- a/venv/Lib/site-packages/django/db/backends/dummy/base.py +++ b/venv/Lib/site-packages/django/db/backends/dummy/base.py @@ -17,11 +17,9 @@ from django.db.backends.dummy.features import DummyDatabaseFeatures def complain(*args, **kwargs): - raise ImproperlyConfigured( - "settings.DATABASES is improperly configured. " - "Please supply the ENGINE value. Check " - "settings documentation for more details." - ) + raise ImproperlyConfigured("settings.DATABASES is improperly configured. " + "Please supply the ENGINE value. Check " + "settings documentation for more details.") def ignore(*args, **kwargs): diff --git a/venv/Lib/site-packages/django/db/backends/mysql/base.py b/venv/Lib/site-packages/django/db/backends/mysql/base.py index ac90bfa..a8dcc7c 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/base.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/base.py @@ -15,7 +15,8 @@ try: import MySQLdb as Database except ImportError as err: raise ImproperlyConfigured( - "Error loading MySQLdb module.\nDid you install mysqlclient?" + 'Error loading MySQLdb module.\n' + 'Did you install mysqlclient?' ) from err from MySQLdb.constants import CLIENT, FIELD_TYPE @@ -32,9 +33,7 @@ from .validation import DatabaseValidation version = Database.version_info if version < (1, 4, 0): - raise ImproperlyConfigured( - "mysqlclient 1.4.0 or newer is required; you have %s." % Database.__version__ - ) + raise ImproperlyConfigured('mysqlclient 1.4.0 or newer is required; you have %s.' % Database.__version__) # MySQLdb returns TIME columns as timedelta -- they are more like timedelta in @@ -47,7 +46,7 @@ django_conversions = { # This should match the numerical portion of the version numbers (we can treat # versions like 5.0.24 and 5.0.24a as the same). -server_version_re = _lazy_re_compile(r"(\d{1,2})\.(\d{1,2})\.(\d{1,2})") +server_version_re = _lazy_re_compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})') class CursorWrapper: @@ -58,7 +57,6 @@ class CursorWrapper: Implemented as a wrapper, rather than a subclass, so that it isn't stuck to the particular underlying representation returned by Connection.cursor(). """ - codes_for_integrityerror = ( 1048, # Column cannot be null 1690, # BIGINT UNSIGNED value is out of range @@ -98,39 +96,40 @@ class CursorWrapper: class DatabaseWrapper(BaseDatabaseWrapper): - vendor = "mysql" + vendor = 'mysql' # This dictionary maps Field objects to their associated MySQL column # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. data_types = { - "AutoField": "integer AUTO_INCREMENT", - "BigAutoField": "bigint AUTO_INCREMENT", - "BinaryField": "longblob", - "BooleanField": "bool", - "CharField": "varchar(%(max_length)s)", - "DateField": "date", - "DateTimeField": "datetime(6)", - "DecimalField": "numeric(%(max_digits)s, %(decimal_places)s)", - "DurationField": "bigint", - "FileField": "varchar(%(max_length)s)", - "FilePathField": "varchar(%(max_length)s)", - "FloatField": "double precision", - "IntegerField": "integer", - "BigIntegerField": "bigint", - "IPAddressField": "char(15)", - "GenericIPAddressField": "char(39)", - "JSONField": "json", - "OneToOneField": "integer", - "PositiveBigIntegerField": "bigint UNSIGNED", - "PositiveIntegerField": "integer UNSIGNED", - "PositiveSmallIntegerField": "smallint UNSIGNED", - "SlugField": "varchar(%(max_length)s)", - "SmallAutoField": "smallint AUTO_INCREMENT", - "SmallIntegerField": "smallint", - "TextField": "longtext", - "TimeField": "time(6)", - "UUIDField": "char(32)", + 'AutoField': 'integer AUTO_INCREMENT', + 'BigAutoField': 'bigint AUTO_INCREMENT', + 'BinaryField': 'longblob', + 'BooleanField': 'bool', + 'CharField': 'varchar(%(max_length)s)', + 'DateField': 'date', + 'DateTimeField': 'datetime(6)', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'DurationField': 'bigint', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', + 'FloatField': 'double precision', + 'IntegerField': 'integer', + 'BigIntegerField': 'bigint', + 'IPAddressField': 'char(15)', + 'GenericIPAddressField': 'char(39)', + 'JSONField': 'json', + 'NullBooleanField': 'bool', + 'OneToOneField': 'integer', + 'PositiveBigIntegerField': 'bigint UNSIGNED', + 'PositiveIntegerField': 'integer UNSIGNED', + 'PositiveSmallIntegerField': 'smallint UNSIGNED', + 'SlugField': 'varchar(%(max_length)s)', + 'SmallAutoField': 'smallint AUTO_INCREMENT', + 'SmallIntegerField': 'smallint', + 'TextField': 'longtext', + 'TimeField': 'time(6)', + 'UUIDField': 'char(32)', } # For these data types: @@ -139,30 +138,23 @@ class DatabaseWrapper(BaseDatabaseWrapper): # - all versions of MySQL and MariaDB don't support full width database # indexes _limited_data_types = ( - "tinyblob", - "blob", - "mediumblob", - "longblob", - "tinytext", - "text", - "mediumtext", - "longtext", - "json", + 'tinyblob', 'blob', 'mediumblob', 'longblob', 'tinytext', 'text', + 'mediumtext', 'longtext', 'json', ) operators = { - "exact": "= %s", - "iexact": "LIKE %s", - "contains": "LIKE BINARY %s", - "icontains": "LIKE %s", - "gt": "> %s", - "gte": ">= %s", - "lt": "< %s", - "lte": "<= %s", - "startswith": "LIKE BINARY %s", - "endswith": "LIKE BINARY %s", - "istartswith": "LIKE %s", - "iendswith": "LIKE %s", + 'exact': '= %s', + 'iexact': 'LIKE %s', + 'contains': 'LIKE BINARY %s', + 'icontains': 'LIKE %s', + 'gt': '> %s', + 'gte': '>= %s', + 'lt': '< %s', + 'lte': '<= %s', + 'startswith': 'LIKE BINARY %s', + 'endswith': 'LIKE BINARY %s', + 'istartswith': 'LIKE %s', + 'iendswith': 'LIKE %s', } # The patterns below are used to generate SQL pattern lookup clauses when @@ -175,19 +167,19 @@ class DatabaseWrapper(BaseDatabaseWrapper): # the LIKE operator. pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\\', '\\\\'), '%%', '\%%'), '_', '\_')" pattern_ops = { - "contains": "LIKE BINARY CONCAT('%%', {}, '%%')", - "icontains": "LIKE CONCAT('%%', {}, '%%')", - "startswith": "LIKE BINARY CONCAT({}, '%%')", - "istartswith": "LIKE CONCAT({}, '%%')", - "endswith": "LIKE BINARY CONCAT('%%', {})", - "iendswith": "LIKE CONCAT('%%', {})", + 'contains': "LIKE BINARY CONCAT('%%', {}, '%%')", + 'icontains': "LIKE CONCAT('%%', {}, '%%')", + 'startswith': "LIKE BINARY CONCAT({}, '%%')", + 'istartswith': "LIKE CONCAT({}, '%%')", + 'endswith': "LIKE BINARY CONCAT('%%', {})", + 'iendswith': "LIKE CONCAT('%%', {})", } isolation_levels = { - "read uncommitted", - "read committed", - "repeatable read", - "serializable", + 'read uncommitted', + 'read committed', + 'repeatable read', + 'serializable', } Database = Database @@ -202,39 +194,37 @@ class DatabaseWrapper(BaseDatabaseWrapper): def get_connection_params(self): kwargs = { - "conv": django_conversions, - "charset": "utf8", + 'conv': django_conversions, + 'charset': 'utf8', } settings_dict = self.settings_dict - if settings_dict["USER"]: - kwargs["user"] = settings_dict["USER"] - if settings_dict["NAME"]: - kwargs["database"] = settings_dict["NAME"] - if settings_dict["PASSWORD"]: - kwargs["password"] = settings_dict["PASSWORD"] - if settings_dict["HOST"].startswith("/"): - kwargs["unix_socket"] = settings_dict["HOST"] - elif settings_dict["HOST"]: - kwargs["host"] = settings_dict["HOST"] - if settings_dict["PORT"]: - kwargs["port"] = int(settings_dict["PORT"]) + if settings_dict['USER']: + kwargs['user'] = settings_dict['USER'] + if settings_dict['NAME']: + kwargs['database'] = settings_dict['NAME'] + if settings_dict['PASSWORD']: + kwargs['password'] = settings_dict['PASSWORD'] + if settings_dict['HOST'].startswith('/'): + kwargs['unix_socket'] = settings_dict['HOST'] + elif settings_dict['HOST']: + kwargs['host'] = settings_dict['HOST'] + if settings_dict['PORT']: + kwargs['port'] = int(settings_dict['PORT']) # We need the number of potentially affected rows after an # "UPDATE", not the number of changed rows. - kwargs["client_flag"] = CLIENT.FOUND_ROWS + kwargs['client_flag'] = CLIENT.FOUND_ROWS # Validate the transaction isolation level, if specified. - options = settings_dict["OPTIONS"].copy() - isolation_level = options.pop("isolation_level", "read committed") + options = settings_dict['OPTIONS'].copy() + isolation_level = options.pop('isolation_level', 'read committed') if isolation_level: isolation_level = isolation_level.lower() if isolation_level not in self.isolation_levels: raise ImproperlyConfigured( "Invalid transaction isolation level '%s' specified.\n" - "Use one of %s, or None." - % ( + "Use one of %s, or None." % ( isolation_level, - ", ".join("'%s'" % s for s in sorted(self.isolation_levels)), - ) - ) + ', '.join("'%s'" % s for s in sorted(self.isolation_levels)) + )) self.isolation_level = isolation_level kwargs.update(options) return kwargs @@ -257,17 +247,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): # a recently inserted row will return when the field is tested # for NULL. Disabling this brings this aspect of MySQL in line # with SQL standards. - assignments.append("SET SQL_AUTO_IS_NULL = 0") + assignments.append('SET SQL_AUTO_IS_NULL = 0') if self.isolation_level: - assignments.append( - "SET SESSION TRANSACTION ISOLATION LEVEL %s" - % self.isolation_level.upper() - ) + assignments.append('SET SESSION TRANSACTION ISOLATION LEVEL %s' % self.isolation_level.upper()) if assignments: with self.cursor() as cursor: - cursor.execute("; ".join(assignments)) + cursor.execute('; '.join(assignments)) @async_unsafe def create_cursor(self, name=None): @@ -291,7 +278,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): need to be re-enabled. """ with self.cursor() as cursor: - cursor.execute("SET foreign_key_checks=0") + cursor.execute('SET foreign_key_checks=0') return True def enable_constraint_checking(self): @@ -303,7 +290,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.needs_rollback, needs_rollback = False, self.needs_rollback try: with self.cursor() as cursor: - cursor.execute("SET foreign_key_checks=1") + cursor.execute('SET foreign_key_checks=1') finally: self.needs_rollback = needs_rollback @@ -319,48 +306,31 @@ class DatabaseWrapper(BaseDatabaseWrapper): if table_names is None: table_names = self.introspection.table_names(cursor) for table_name in table_names: - primary_key_column_name = self.introspection.get_primary_key_column( - cursor, table_name - ) + primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name) if not primary_key_column_name: continue key_columns = self.introspection.get_key_columns(cursor, table_name) - for ( - column_name, - referenced_table_name, - referenced_column_name, - ) in key_columns: + for column_name, referenced_table_name, referenced_column_name in key_columns: cursor.execute( """ SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING LEFT JOIN `%s` as REFERRED ON (REFERRING.`%s` = REFERRED.`%s`) WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL - """ - % ( - primary_key_column_name, - column_name, - table_name, - referenced_table_name, - column_name, - referenced_column_name, - column_name, - referenced_column_name, + """ % ( + primary_key_column_name, column_name, table_name, + referenced_table_name, column_name, referenced_column_name, + column_name, referenced_column_name, ) ) for bad_row in cursor.fetchall(): raise IntegrityError( - "The row in table '%s' with primary key '%s' has an " - "invalid foreign key: %s.%s contains a value '%s' that " - "does not have a corresponding value in %s.%s." + "The row in table '%s' with primary key '%s' has an invalid " + "foreign key: %s.%s contains a value '%s' that does not " + "have a corresponding value in %s.%s." % ( - table_name, - bad_row[0], - table_name, - column_name, - bad_row[1], - referenced_table_name, - referenced_column_name, + table_name, bad_row[0], table_name, column_name, + bad_row[1], referenced_table_name, referenced_column_name, ) ) @@ -374,20 +344,20 @@ class DatabaseWrapper(BaseDatabaseWrapper): @cached_property def display_name(self): - return "MariaDB" if self.mysql_is_mariadb else "MySQL" + return 'MariaDB' if self.mysql_is_mariadb else 'MySQL' @cached_property def data_type_check_constraints(self): if self.features.supports_column_check_constraints: check_constraints = { - "PositiveBigIntegerField": "`%(column)s` >= 0", - "PositiveIntegerField": "`%(column)s` >= 0", - "PositiveSmallIntegerField": "`%(column)s` >= 0", + 'PositiveBigIntegerField': '`%(column)s` >= 0', + 'PositiveIntegerField': '`%(column)s` >= 0', + 'PositiveSmallIntegerField': '`%(column)s` >= 0', } if self.mysql_is_mariadb and self.mysql_version < (10, 4, 3): # MariaDB < 10.4.3 doesn't automatically use the JSON_VALID as # a check constraint. - check_constraints["JSONField"] = "JSON_VALID(`%(column)s`)" + check_constraints['JSONField'] = 'JSON_VALID(`%(column)s`)' return check_constraints return {} @@ -397,45 +367,40 @@ class DatabaseWrapper(BaseDatabaseWrapper): # Select some server variables and test if the time zone # definitions are installed. CONVERT_TZ returns NULL if 'UTC' # timezone isn't loaded into the mysql.time_zone table. - cursor.execute( - """ + cursor.execute(""" SELECT VERSION(), @@sql_mode, @@default_storage_engine, @@sql_auto_is_null, @@lower_case_table_names, CONVERT_TZ('2001-01-01 01:00:00', 'UTC', 'UTC') IS NOT NULL - """ - ) + """) row = cursor.fetchone() return { - "version": row[0], - "sql_mode": row[1], - "default_storage_engine": row[2], - "sql_auto_is_null": bool(row[3]), - "lower_case_table_names": bool(row[4]), - "has_zoneinfo_database": bool(row[5]), + 'version': row[0], + 'sql_mode': row[1], + 'default_storage_engine': row[2], + 'sql_auto_is_null': bool(row[3]), + 'lower_case_table_names': bool(row[4]), + 'has_zoneinfo_database': bool(row[5]), } @cached_property def mysql_server_info(self): - return self.mysql_server_data["version"] + return self.mysql_server_data['version'] @cached_property def mysql_version(self): match = server_version_re.match(self.mysql_server_info) if not match: - raise Exception( - "Unable to determine MySQL version from version string %r" - % self.mysql_server_info - ) + raise Exception('Unable to determine MySQL version from version string %r' % self.mysql_server_info) return tuple(int(x) for x in match.groups()) @cached_property def mysql_is_mariadb(self): - return "mariadb" in self.mysql_server_info.lower() + return 'mariadb' in self.mysql_server_info.lower() @cached_property def sql_mode(self): - sql_mode = self.mysql_server_data["sql_mode"] - return set(sql_mode.split(",") if sql_mode else ()) + sql_mode = self.mysql_server_data['sql_mode'] + return set(sql_mode.split(',') if sql_mode else ()) diff --git a/venv/Lib/site-packages/django/db/backends/mysql/client.py b/venv/Lib/site-packages/django/db/backends/mysql/client.py index 0c09a2c..7cbe314 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/client.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/client.py @@ -2,28 +2,28 @@ from django.db.backends.base.client import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): - executable_name = "mysql" + executable_name = 'mysql' @classmethod def settings_to_cmd_args_env(cls, settings_dict, parameters): args = [cls.executable_name] env = None - database = settings_dict["OPTIONS"].get( - "database", - settings_dict["OPTIONS"].get("db", settings_dict["NAME"]), + database = settings_dict['OPTIONS'].get( + 'database', + settings_dict['OPTIONS'].get('db', settings_dict['NAME']), ) - user = settings_dict["OPTIONS"].get("user", settings_dict["USER"]) - password = settings_dict["OPTIONS"].get( - "password", - settings_dict["OPTIONS"].get("passwd", settings_dict["PASSWORD"]), + user = settings_dict['OPTIONS'].get('user', settings_dict['USER']) + password = settings_dict['OPTIONS'].get( + 'password', + settings_dict['OPTIONS'].get('passwd', settings_dict['PASSWORD']) ) - host = settings_dict["OPTIONS"].get("host", settings_dict["HOST"]) - port = settings_dict["OPTIONS"].get("port", settings_dict["PORT"]) - server_ca = settings_dict["OPTIONS"].get("ssl", {}).get("ca") - client_cert = settings_dict["OPTIONS"].get("ssl", {}).get("cert") - client_key = settings_dict["OPTIONS"].get("ssl", {}).get("key") - defaults_file = settings_dict["OPTIONS"].get("read_default_file") - charset = settings_dict["OPTIONS"].get("charset") + host = settings_dict['OPTIONS'].get('host', settings_dict['HOST']) + port = settings_dict['OPTIONS'].get('port', settings_dict['PORT']) + server_ca = settings_dict['OPTIONS'].get('ssl', {}).get('ca') + client_cert = settings_dict['OPTIONS'].get('ssl', {}).get('cert') + client_key = settings_dict['OPTIONS'].get('ssl', {}).get('key') + defaults_file = settings_dict['OPTIONS'].get('read_default_file') + charset = settings_dict['OPTIONS'].get('charset') # Seems to be no good way to set sql_mode with CLI. if defaults_file: @@ -38,9 +38,9 @@ class DatabaseClient(BaseDatabaseClient): # prevents password exposure if the subprocess.run(check=True) call # raises a CalledProcessError since the string representation of # the latter includes all of the provided `args`. - env = {"MYSQL_PWD": password} + env = {'MYSQL_PWD': password} if host: - if "/" in host: + if '/' in host: args += ["--socket=%s" % host] else: args += ["--host=%s" % host] @@ -53,7 +53,7 @@ class DatabaseClient(BaseDatabaseClient): if client_key: args += ["--ssl-key=%s" % client_key] if charset: - args += ["--default-character-set=%s" % charset] + args += ['--default-character-set=%s' % charset] if database: args += [database] args.extend(parameters) diff --git a/venv/Lib/site-packages/django/db/backends/mysql/compiler.py b/venv/Lib/site-packages/django/db/backends/mysql/compiler.py index a8ab03a..49b4796 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/compiler.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/compiler.py @@ -8,14 +8,7 @@ class SQLCompiler(compiler.SQLCompiler): qn = compiler.quote_name_unless_alias qn2 = self.connection.ops.quote_name sql, params = self.as_sql() - return ( - "(%s) IN (%s)" - % ( - ", ".join("%s.%s" % (qn(alias), qn2(column)) for column in columns), - sql, - ), - params, - ) + return '(%s) IN (%s)' % (', '.join('%s.%s' % (qn(alias), qn2(column)) for column in columns), sql), params class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler): @@ -34,15 +27,16 @@ class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler): # since it doesn't allow for GROUP BY and HAVING clauses. return super().as_sql() result = [ - "DELETE %s FROM" - % self.quote_name_unless_alias(self.query.get_initial_alias()) + 'DELETE %s FROM' % self.quote_name_unless_alias( + self.query.get_initial_alias() + ) ] from_sql, from_params = self.get_from_clause() result.extend(from_sql) where_sql, where_params = self.compile(where) if where_sql: - result.append("WHERE %s" % where_sql) - return " ".join(result), tuple(from_params) + tuple(where_params) + result.append('WHERE %s' % where_sql) + return ' '.join(result), tuple(from_params) + tuple(where_params) class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler): @@ -56,15 +50,15 @@ class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler): try: for resolved, (sql, params, _) in self.get_order_by(): if ( - isinstance(resolved.expression, Col) - and resolved.expression.alias != db_table + isinstance(resolved.expression, Col) and + resolved.expression.alias != db_table ): # Ignore ordering if it contains joined fields, because # they cannot be used in the ORDER BY clause. raise FieldError order_by_sql.append(sql) order_by_params.extend(params) - update_query += " ORDER BY " + ", ".join(order_by_sql) + update_query += ' ORDER BY ' + ', '.join(order_by_sql) update_params += tuple(order_by_params) except FieldError: # Ignore ordering if it contains annotations, because they're diff --git a/venv/Lib/site-packages/django/db/backends/mysql/creation.py b/venv/Lib/site-packages/django/db/backends/mysql/creation.py index a060f41..1f0261b 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/creation.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/creation.py @@ -8,14 +8,15 @@ from .client import DatabaseClient class DatabaseCreation(BaseDatabaseCreation): + def sql_table_creation_suffix(self): suffix = [] - test_settings = self.connection.settings_dict["TEST"] - if test_settings["CHARSET"]: - suffix.append("CHARACTER SET %s" % test_settings["CHARSET"]) - if test_settings["COLLATION"]: - suffix.append("COLLATE %s" % test_settings["COLLATION"]) - return " ".join(suffix) + test_settings = self.connection.settings_dict['TEST'] + if test_settings['CHARSET']: + suffix.append('CHARACTER SET %s' % test_settings['CHARSET']) + if test_settings['COLLATION']: + suffix.append('COLLATE %s' % test_settings['COLLATION']) + return ' '.join(suffix) def _execute_create_test_db(self, cursor, parameters, keepdb=False): try: @@ -23,17 +24,17 @@ class DatabaseCreation(BaseDatabaseCreation): except Exception as e: if len(e.args) < 1 or e.args[0] != 1007: # All errors except "database exists" (1007) cancel tests. - self.log("Got an error creating the test database: %s" % e) + self.log('Got an error creating the test database: %s' % e) sys.exit(2) else: raise def _clone_test_db(self, suffix, verbosity, keepdb=False): - source_database_name = self.connection.settings_dict["NAME"] - target_database_name = self.get_test_db_clone_settings(suffix)["NAME"] + source_database_name = self.connection.settings_dict['NAME'] + target_database_name = self.get_test_db_clone_settings(suffix)['NAME'] test_db_params = { - "dbname": self.connection.ops.quote_name(target_database_name), - "suffix": self.sql_table_creation_suffix(), + 'dbname': self.connection.ops.quote_name(target_database_name), + 'suffix': self.sql_table_creation_suffix(), } with self._nodb_cursor() as cursor: try: @@ -44,44 +45,24 @@ class DatabaseCreation(BaseDatabaseCreation): return try: if verbosity >= 1: - self.log( - "Destroying old test database for alias %s..." - % ( - self._get_database_display_str( - verbosity, target_database_name - ), - ) - ) - cursor.execute("DROP DATABASE %(dbname)s" % test_db_params) + self.log('Destroying old test database for alias %s...' % ( + self._get_database_display_str(verbosity, target_database_name), + )) + cursor.execute('DROP DATABASE %(dbname)s' % test_db_params) self._execute_create_test_db(cursor, test_db_params, keepdb) except Exception as e: - self.log("Got an error recreating the test database: %s" % e) + self.log('Got an error recreating the test database: %s' % e) sys.exit(2) self._clone_db(source_database_name, target_database_name) def _clone_db(self, source_database_name, target_database_name): - cmd_args, cmd_env = DatabaseClient.settings_to_cmd_args_env( - self.connection.settings_dict, [] - ) - dump_cmd = [ - "mysqldump", - *cmd_args[1:-1], - "--routines", - "--events", - source_database_name, - ] + cmd_args, cmd_env = DatabaseClient.settings_to_cmd_args_env(self.connection.settings_dict, []) + dump_cmd = ['mysqldump', *cmd_args[1:-1], '--routines', '--events', source_database_name] dump_env = load_env = {**os.environ, **cmd_env} if cmd_env else None load_cmd = cmd_args load_cmd[-1] = target_database_name - with subprocess.Popen( - dump_cmd, stdout=subprocess.PIPE, env=dump_env - ) as dump_proc: - with subprocess.Popen( - load_cmd, - stdin=dump_proc.stdout, - stdout=subprocess.DEVNULL, - env=load_env, - ): + with subprocess.Popen(dump_cmd, stdout=subprocess.PIPE, env=dump_env) as dump_proc: + with subprocess.Popen(load_cmd, stdin=dump_proc.stdout, stdout=subprocess.DEVNULL, env=load_env): # Allow dump_proc to receive a SIGPIPE if the load process exits. dump_proc.stdout.close() diff --git a/venv/Lib/site-packages/django/db/backends/mysql/features.py b/venv/Lib/site-packages/django/db/backends/mysql/features.py index 6f8b135..419b2ba 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/features.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/features.py @@ -47,119 +47,66 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_order_by_nulls_modifier = False order_by_nulls_first = True - - @cached_property - def test_collations(self): - charset = "utf8" - if self.connection.mysql_is_mariadb and self.connection.mysql_version >= ( - 10, - 6, - ): - # utf8 is an alias for utf8mb3 in MariaDB 10.6+. - charset = "utf8mb3" - return { - "ci": f"{charset}_general_ci", - "non_default": f"{charset}_esperanto_ci", - "swedish_ci": f"{charset}_swedish_ci", - } - - test_now_utc_template = "UTC_TIMESTAMP" + test_collations = { + 'ci': 'utf8_general_ci', + 'non_default': 'utf8_esperanto_ci', + 'swedish_ci': 'utf8_swedish_ci', + } @cached_property def django_test_skips(self): skips = { "This doesn't work on MySQL.": { - "db_functions.comparison.test_greatest.GreatestTests." - "test_coalesce_workaround", - "db_functions.comparison.test_least.LeastTests." - "test_coalesce_workaround", + 'db_functions.comparison.test_greatest.GreatestTests.test_coalesce_workaround', + 'db_functions.comparison.test_least.LeastTests.test_coalesce_workaround', }, - "Running on MySQL requires utf8mb4 encoding (#18392).": { - "model_fields.test_textfield.TextFieldTests.test_emoji", - "model_fields.test_charfield.TestCharField.test_emoji", + 'Running on MySQL requires utf8mb4 encoding (#18392).': { + 'model_fields.test_textfield.TextFieldTests.test_emoji', + 'model_fields.test_charfield.TestCharField.test_emoji', }, "MySQL doesn't support functional indexes on a function that " "returns JSON": { - "schema.tests.SchemaTests.test_func_index_json_key_transform", - }, - "MySQL supports multiplying and dividing DurationFields by a " - "scalar value but it's not implemented (#25287).": { - "expressions.tests.FTimeDeltaTests.test_durationfield_multiply_divide", + 'schema.tests.SchemaTests.test_func_index_json_key_transform', }, } - if "ONLY_FULL_GROUP_BY" in self.connection.sql_mode: - skips.update( - { - "GROUP BY optimization does not work properly when " - "ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.": { - "aggregation.tests.AggregateTestCase." - "test_aggregation_subquery_annotation_multivalued", - "annotations.tests.NonAggregateAnnotationTestCase." - "test_annotation_aggregate_with_m2o", - }, - } - ) - if not self.connection.mysql_is_mariadb and self.connection.mysql_version < ( - 8, + if 'ONLY_FULL_GROUP_BY' in self.connection.sql_mode: + skips.update({ + 'GROUP BY optimization does not work properly when ' + 'ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.': { + 'aggregation.tests.AggregateTestCase.test_aggregation_subquery_annotation_multivalued', + 'annotations.tests.NonAggregateAnnotationTestCase.test_annotation_aggregate_with_m2o', + }, + }) + if ( + self.connection.mysql_is_mariadb and + (10, 4, 3) < self.connection.mysql_version < (10, 5, 2) ): - skips.update( - { - "Casting to datetime/time is not supported by MySQL < 8.0. " - "(#30224)": { - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_time_from_python", - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_datetime_from_python", - }, - "MySQL < 8.0 returns string type instead of datetime/time. " - "(#30224)": { - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_time_from_database", - "aggregation.tests.AggregateTestCase." - "test_aggregation_default_using_datetime_from_database", - }, - } - ) - if self.connection.mysql_is_mariadb and ( - 10, - 4, - 3, - ) < self.connection.mysql_version < (10, 5, 2): - skips.update( - { - "https://jira.mariadb.org/browse/MDEV-19598": { - "schema.tests.SchemaTests." - "test_alter_not_unique_field_to_primary_key", - }, - } - ) - if self.connection.mysql_is_mariadb and ( - 10, - 4, - 12, - ) < self.connection.mysql_version < (10, 5): - skips.update( - { - "https://jira.mariadb.org/browse/MDEV-22775": { - "schema.tests.SchemaTests." - "test_alter_pk_with_self_referential_field", - }, - } - ) + skips.update({ + 'https://jira.mariadb.org/browse/MDEV-19598': { + 'schema.tests.SchemaTests.test_alter_not_unique_field_to_primary_key', + }, + }) + if ( + self.connection.mysql_is_mariadb and + (10, 4, 12) < self.connection.mysql_version < (10, 5) + ): + skips.update({ + 'https://jira.mariadb.org/browse/MDEV-22775': { + 'schema.tests.SchemaTests.test_alter_pk_with_self_referential_field', + }, + }) if not self.supports_explain_analyze: - skips.update( - { - "MariaDB and MySQL >= 8.0.18 specific.": { - "queries.test_explain.ExplainTests.test_mysql_analyze", - }, - } - ) + skips.update({ + 'MariaDB and MySQL >= 8.0.18 specific.': { + 'queries.test_explain.ExplainTests.test_mysql_analyze', + }, + }) return skips @cached_property def _mysql_storage_engine(self): "Internal method used in Django tests. Don't rely on this from your code" - return self.connection.mysql_server_data["default_storage_engine"] + return self.connection.mysql_server_data['default_storage_engine'] @cached_property def allows_auto_pk_0(self): @@ -167,50 +114,40 @@ class DatabaseFeatures(BaseDatabaseFeatures): Autoincrement primary key can be set to 0 if it doesn't generate new autoincrement values. """ - return "NO_AUTO_VALUE_ON_ZERO" in self.connection.sql_mode + return 'NO_AUTO_VALUE_ON_ZERO' in self.connection.sql_mode @cached_property def update_can_self_select(self): - return self.connection.mysql_is_mariadb and self.connection.mysql_version >= ( - 10, - 3, - 2, - ) + return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 3, 2) @cached_property def can_introspect_foreign_keys(self): "Confirm support for introspected foreign keys" - return self._mysql_storage_engine != "MyISAM" + return self._mysql_storage_engine != 'MyISAM' @cached_property def introspected_field_types(self): return { **super().introspected_field_types, - "BinaryField": "TextField", - "BooleanField": "IntegerField", - "DurationField": "BigIntegerField", - "GenericIPAddressField": "CharField", + 'BinaryField': 'TextField', + 'BooleanField': 'IntegerField', + 'DurationField': 'BigIntegerField', + 'GenericIPAddressField': 'CharField', } @cached_property def can_return_columns_from_insert(self): - return self.connection.mysql_is_mariadb and self.connection.mysql_version >= ( - 10, - 5, - 0, - ) + return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 5, 0) - can_return_rows_from_bulk_insert = property( - operator.attrgetter("can_return_columns_from_insert") - ) + can_return_rows_from_bulk_insert = property(operator.attrgetter('can_return_columns_from_insert')) @cached_property def has_zoneinfo_database(self): - return self.connection.mysql_server_data["has_zoneinfo_database"] + return self.connection.mysql_server_data['has_zoneinfo_database'] @cached_property def is_sql_auto_is_null_enabled(self): - return self.connection.mysql_server_data["sql_auto_is_null"] + return self.connection.mysql_server_data['sql_auto_is_null'] @cached_property def supports_over_clause(self): @@ -218,9 +155,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): return True return self.connection.mysql_version >= (8, 0, 2) - supports_frame_range_fixed_distance = property( - operator.attrgetter("supports_over_clause") - ) + supports_frame_range_fixed_distance = property(operator.attrgetter('supports_over_clause')) @cached_property def supports_column_check_constraints(self): @@ -228,26 +163,18 @@ class DatabaseFeatures(BaseDatabaseFeatures): return self.connection.mysql_version >= (10, 2, 1) return self.connection.mysql_version >= (8, 0, 16) - supports_table_check_constraints = property( - operator.attrgetter("supports_column_check_constraints") - ) + supports_table_check_constraints = property(operator.attrgetter('supports_column_check_constraints')) @cached_property def can_introspect_check_constraints(self): if self.connection.mysql_is_mariadb: version = self.connection.mysql_version - return (version >= (10, 2, 22) and version < (10, 3)) or version >= ( - 10, - 3, - 10, - ) + return (version >= (10, 2, 22) and version < (10, 3)) or version >= (10, 3, 10) return self.connection.mysql_version >= (8, 0, 16) @cached_property def has_select_for_update_skip_locked(self): - if self.connection.mysql_is_mariadb: - return self.connection.mysql_version >= (10, 6) - return self.connection.mysql_version >= (8, 0, 1) + return not self.connection.mysql_is_mariadb and self.connection.mysql_version >= (8, 0, 1) @cached_property def has_select_for_update_nowait(self): @@ -257,30 +184,19 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def has_select_for_update_of(self): - return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version >= (8, 0, 1) - ) + return not self.connection.mysql_is_mariadb and self.connection.mysql_version >= (8, 0, 1) @cached_property def supports_explain_analyze(self): - return self.connection.mysql_is_mariadb or self.connection.mysql_version >= ( - 8, - 0, - 18, - ) + return self.connection.mysql_is_mariadb or self.connection.mysql_version >= (8, 0, 18) @cached_property def supported_explain_formats(self): # Alias MySQL's TRADITIONAL to TEXT for consistency with other # backends. - formats = {"JSON", "TEXT", "TRADITIONAL"} - if not self.connection.mysql_is_mariadb and self.connection.mysql_version >= ( - 8, - 0, - 16, - ): - formats.add("TREE") + formats = {'JSON', 'TEXT', 'TRADITIONAL'} + if not self.connection.mysql_is_mariadb and self.connection.mysql_version >= (8, 0, 16): + formats.add('TREE') return formats @cached_property @@ -288,11 +204,11 @@ class DatabaseFeatures(BaseDatabaseFeatures): """ All storage engines except MyISAM support transactions. """ - return self._mysql_storage_engine != "MyISAM" + return self._mysql_storage_engine != 'MyISAM' @cached_property def ignores_table_name_case(self): - return self.connection.mysql_server_data["lower_case_table_names"] + return self.connection.mysql_server_data['lower_case_table_names'] @cached_property def supports_default_in_lead_lag(self): @@ -314,13 +230,13 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def supports_index_column_ordering(self): return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version >= (8, 0, 1) + not self.connection.mysql_is_mariadb and + self.connection.mysql_version >= (8, 0, 1) ) @cached_property def supports_expression_indexes(self): return ( - not self.connection.mysql_is_mariadb - and self.connection.mysql_version >= (8, 0, 13) + not self.connection.mysql_is_mariadb and + self.connection.mysql_version >= (8, 0, 13) ) diff --git a/venv/Lib/site-packages/django/db/backends/mysql/introspection.py b/venv/Lib/site-packages/django/db/backends/mysql/introspection.py index 196c35a..649df5c 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/introspection.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/introspection.py @@ -3,76 +3,72 @@ from collections import namedtuple import sqlparse from MySQLdb.constants import FIELD_TYPE -from django.db.backends.base.introspection import BaseDatabaseIntrospection -from django.db.backends.base.introspection import FieldInfo as BaseFieldInfo -from django.db.backends.base.introspection import TableInfo +from django.db.backends.base.introspection import ( + BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo, +) from django.db.models import Index from django.utils.datastructures import OrderedSet -FieldInfo = namedtuple( - "FieldInfo", BaseFieldInfo._fields + ("extra", "is_unsigned", "has_json_constraint") -) +FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('extra', 'is_unsigned', 'has_json_constraint')) InfoLine = namedtuple( - "InfoLine", - "col_name data_type max_len num_prec num_scale extra column_default " - "collation is_unsigned", + 'InfoLine', + 'col_name data_type max_len num_prec num_scale extra column_default ' + 'collation is_unsigned' ) class DatabaseIntrospection(BaseDatabaseIntrospection): data_types_reverse = { - FIELD_TYPE.BLOB: "TextField", - FIELD_TYPE.CHAR: "CharField", - FIELD_TYPE.DECIMAL: "DecimalField", - FIELD_TYPE.NEWDECIMAL: "DecimalField", - FIELD_TYPE.DATE: "DateField", - FIELD_TYPE.DATETIME: "DateTimeField", - FIELD_TYPE.DOUBLE: "FloatField", - FIELD_TYPE.FLOAT: "FloatField", - FIELD_TYPE.INT24: "IntegerField", - FIELD_TYPE.JSON: "JSONField", - FIELD_TYPE.LONG: "IntegerField", - FIELD_TYPE.LONGLONG: "BigIntegerField", - FIELD_TYPE.SHORT: "SmallIntegerField", - FIELD_TYPE.STRING: "CharField", - FIELD_TYPE.TIME: "TimeField", - FIELD_TYPE.TIMESTAMP: "DateTimeField", - FIELD_TYPE.TINY: "IntegerField", - FIELD_TYPE.TINY_BLOB: "TextField", - FIELD_TYPE.MEDIUM_BLOB: "TextField", - FIELD_TYPE.LONG_BLOB: "TextField", - FIELD_TYPE.VAR_STRING: "CharField", + FIELD_TYPE.BLOB: 'TextField', + FIELD_TYPE.CHAR: 'CharField', + FIELD_TYPE.DECIMAL: 'DecimalField', + FIELD_TYPE.NEWDECIMAL: 'DecimalField', + FIELD_TYPE.DATE: 'DateField', + FIELD_TYPE.DATETIME: 'DateTimeField', + FIELD_TYPE.DOUBLE: 'FloatField', + FIELD_TYPE.FLOAT: 'FloatField', + FIELD_TYPE.INT24: 'IntegerField', + FIELD_TYPE.JSON: 'JSONField', + FIELD_TYPE.LONG: 'IntegerField', + FIELD_TYPE.LONGLONG: 'BigIntegerField', + FIELD_TYPE.SHORT: 'SmallIntegerField', + FIELD_TYPE.STRING: 'CharField', + FIELD_TYPE.TIME: 'TimeField', + FIELD_TYPE.TIMESTAMP: 'DateTimeField', + FIELD_TYPE.TINY: 'IntegerField', + FIELD_TYPE.TINY_BLOB: 'TextField', + FIELD_TYPE.MEDIUM_BLOB: 'TextField', + FIELD_TYPE.LONG_BLOB: 'TextField', + FIELD_TYPE.VAR_STRING: 'CharField', } def get_field_type(self, data_type, description): field_type = super().get_field_type(data_type, description) - if "auto_increment" in description.extra: - if field_type == "IntegerField": - return "AutoField" - elif field_type == "BigIntegerField": - return "BigAutoField" - elif field_type == "SmallIntegerField": - return "SmallAutoField" + if 'auto_increment' in description.extra: + if field_type == 'IntegerField': + return 'AutoField' + elif field_type == 'BigIntegerField': + return 'BigAutoField' + elif field_type == 'SmallIntegerField': + return 'SmallAutoField' if description.is_unsigned: - if field_type == "BigIntegerField": - return "PositiveBigIntegerField" - elif field_type == "IntegerField": - return "PositiveIntegerField" - elif field_type == "SmallIntegerField": - return "PositiveSmallIntegerField" + if field_type == 'BigIntegerField': + return 'PositiveBigIntegerField' + elif field_type == 'IntegerField': + return 'PositiveIntegerField' + elif field_type == 'SmallIntegerField': + return 'PositiveSmallIntegerField' # JSON data type is an alias for LONGTEXT in MariaDB, use check # constraints clauses to introspect JSONField. if description.has_json_constraint: - return "JSONField" + return 'JSONField' return field_type def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" cursor.execute("SHOW FULL TABLES") - return [ - TableInfo(row[0], {"BASE TABLE": "t", "VIEW": "v"}.get(row[1])) - for row in cursor.fetchall() - ] + return [TableInfo(row[0], {'BASE TABLE': 't', 'VIEW': 'v'}.get(row[1])) + for row in cursor.fetchall()] def get_table_description(self, cursor, table_name): """ @@ -80,44 +76,33 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): interface." """ json_constraints = {} - if ( - self.connection.mysql_is_mariadb - and self.connection.features.can_introspect_json_field - ): + if self.connection.mysql_is_mariadb and self.connection.features.can_introspect_json_field: # JSON data type is an alias for LONGTEXT in MariaDB, select # JSON_VALID() constraints to introspect JSONField. - cursor.execute( - """ + cursor.execute(""" SELECT c.constraint_name AS column_name FROM information_schema.check_constraints AS c WHERE c.table_name = %s AND - LOWER(c.check_clause) = - 'json_valid(`' + LOWER(c.constraint_name) + '`)' AND + LOWER(c.check_clause) = 'json_valid(`' + LOWER(c.constraint_name) + '`)' AND c.constraint_schema = DATABASE() - """, - [table_name], - ) + """, [table_name]) json_constraints = {row[0] for row in cursor.fetchall()} # A default collation for the given table. - cursor.execute( - """ + cursor.execute(""" SELECT table_collation FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = %s - """, - [table_name], - ) + """, [table_name]) row = cursor.fetchone() - default_column_collation = row[0] if row else "" + default_column_collation = row[0] if row else '' # information_schema database gives more accurate results for some figures: # - varchar length returned by cursor.description is an internal length, # not visible length (#5725) # - precision and scale (for decimal fields) (#5014) # - auto_increment is not available in cursor.description - cursor.execute( - """ + cursor.execute(""" SELECT column_name, data_type, character_maximum_length, numeric_precision, numeric_scale, extra, column_default, @@ -131,14 +116,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): END AS is_unsigned FROM information_schema.columns WHERE table_name = %s AND table_schema = DATABASE() - """, - [default_column_collation, table_name], - ) + """, [default_column_collation, table_name]) field_info = {line[0]: InfoLine(*line) for line in cursor.fetchall()} - cursor.execute( - "SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name) - ) + cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) def to_int(i): return int(i) if i is not None else i @@ -146,27 +127,25 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): fields = [] for line in cursor.description: info = field_info[line[0]] - fields.append( - FieldInfo( - *line[:3], - to_int(info.max_len) or line[3], - to_int(info.num_prec) or line[4], - to_int(info.num_scale) or line[5], - line[6], - info.column_default, - info.collation, - info.extra, - info.is_unsigned, - line[0] in json_constraints, - ) - ) + fields.append(FieldInfo( + *line[:3], + to_int(info.max_len) or line[3], + to_int(info.num_prec) or line[4], + to_int(info.num_scale) or line[5], + line[6], + info.column_default, + info.collation, + info.extra, + info.is_unsigned, + line[0] in json_constraints, + )) return fields def get_sequences(self, cursor, table_name, table_fields=()): for field_info in self.get_table_description(cursor, table_name): - if "auto_increment" in field_info.extra: + if 'auto_increment' in field_info.extra: # MySQL allows only one auto-increment column per table. - return [{"table": table_name, "column": field_info.name}] + return [{'table': table_name, 'column': field_info.name}] return [] def get_relations(self, cursor, table_name): @@ -186,17 +165,13 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): for all key columns in the given table. """ key_columns = [] - cursor.execute( - """ + cursor.execute(""" SELECT column_name, referenced_table_name, referenced_column_name FROM information_schema.key_column_usage WHERE table_name = %s AND table_schema = DATABASE() AND referenced_table_name IS NOT NULL - AND referenced_column_name IS NOT NULL - """, - [table_name], - ) + AND referenced_column_name IS NOT NULL""", [table_name]) key_columns.extend(cursor.fetchall()) return key_columns @@ -206,15 +181,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): storage engine if the table doesn't exist. """ cursor.execute( - """ - SELECT engine - FROM information_schema.tables - WHERE - table_name = %s AND - table_schema = DATABASE() - """, - [table_name], - ) + "SELECT engine " + "FROM information_schema.tables " + "WHERE table_name = %s", [table_name]) result = cursor.fetchone() if not result: return self.connection.features._mysql_storage_engine @@ -226,9 +195,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): tokens = (token for token in statement.flatten() if not token.is_whitespace) for token in tokens: if ( - token.ttype == sqlparse.tokens.Name - and self.connection.ops.quote_name(token.value) == token.value - and token.value[1:-1] in columns + token.ttype == sqlparse.tokens.Name and + self.connection.ops.quote_name(token.value) == token.value and + token.value[1:-1] in columns ): check_columns.add(token.value[1:-1]) return check_columns @@ -242,39 +211,46 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Get the actual constraint names and columns name_query = """ SELECT kc.`constraint_name`, kc.`column_name`, - kc.`referenced_table_name`, kc.`referenced_column_name`, - c.`constraint_type` - FROM - information_schema.key_column_usage AS kc, - information_schema.table_constraints AS c + kc.`referenced_table_name`, kc.`referenced_column_name` + FROM information_schema.key_column_usage AS kc WHERE kc.table_schema = DATABASE() AND - c.table_schema = kc.table_schema AND - c.constraint_name = kc.constraint_name AND - c.constraint_type != 'CHECK' AND kc.table_name = %s ORDER BY kc.`ordinal_position` """ cursor.execute(name_query, [table_name]) - for constraint, column, ref_table, ref_column, kind in cursor.fetchall(): + for constraint, column, ref_table, ref_column in cursor.fetchall(): if constraint not in constraints: constraints[constraint] = { - "columns": OrderedSet(), - "primary_key": kind == "PRIMARY KEY", - "unique": kind in {"PRIMARY KEY", "UNIQUE"}, - "index": False, - "check": False, - "foreign_key": (ref_table, ref_column) if ref_column else None, + 'columns': OrderedSet(), + 'primary_key': False, + 'unique': False, + 'index': False, + 'check': False, + 'foreign_key': (ref_table, ref_column) if ref_column else None, } if self.connection.features.supports_index_column_ordering: - constraints[constraint]["orders"] = [] - constraints[constraint]["columns"].add(column) + constraints[constraint]['orders'] = [] + constraints[constraint]['columns'].add(column) + # Now get the constraint types + type_query = """ + SELECT c.constraint_name, c.constraint_type + FROM information_schema.table_constraints AS c + WHERE + c.table_schema = DATABASE() AND + c.table_name = %s + """ + cursor.execute(type_query, [table_name]) + for constraint, kind in cursor.fetchall(): + if kind.lower() == "primary key": + constraints[constraint]['primary_key'] = True + constraints[constraint]['unique'] = True + elif kind.lower() == "unique": + constraints[constraint]['unique'] = True # Add check constraints. if self.connection.features.can_introspect_check_constraints: unnamed_constraints_index = 0 - columns = { - info.name for info in self.get_table_description(cursor, table_name) - } + columns = {info.name for info in self.get_table_description(cursor, table_name)} if self.connection.mysql_is_mariadb: type_query = """ SELECT c.constraint_name, c.check_clause @@ -298,48 +274,42 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): """ cursor.execute(type_query, [table_name]) for constraint, check_clause in cursor.fetchall(): - constraint_columns = self._parse_constraint_columns( - check_clause, columns - ) + constraint_columns = self._parse_constraint_columns(check_clause, columns) # Ensure uniqueness of unnamed constraints. Unnamed unique # and check columns constraints have the same name as # a column. if set(constraint_columns) == {constraint}: unnamed_constraints_index += 1 - constraint = "__unnamed_constraint_%s__" % unnamed_constraints_index + constraint = '__unnamed_constraint_%s__' % unnamed_constraints_index constraints[constraint] = { - "columns": constraint_columns, - "primary_key": False, - "unique": False, - "index": False, - "check": True, - "foreign_key": None, + 'columns': constraint_columns, + 'primary_key': False, + 'unique': False, + 'index': False, + 'check': True, + 'foreign_key': None, } # Now add in the indexes - cursor.execute( - "SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name) - ) + cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name)) for table, non_unique, index, colseq, column, order, type_ in [ x[:6] + (x[10],) for x in cursor.fetchall() ]: if index not in constraints: constraints[index] = { - "columns": OrderedSet(), - "primary_key": False, - "unique": not non_unique, - "check": False, - "foreign_key": None, + 'columns': OrderedSet(), + 'primary_key': False, + 'unique': False, + 'check': False, + 'foreign_key': None, } if self.connection.features.supports_index_column_ordering: - constraints[index]["orders"] = [] - constraints[index]["index"] = True - constraints[index]["type"] = ( - Index.suffix if type_ == "BTREE" else type_.lower() - ) - constraints[index]["columns"].add(column) + constraints[index]['orders'] = [] + constraints[index]['index'] = True + constraints[index]['type'] = Index.suffix if type_ == 'BTREE' else type_.lower() + constraints[index]['columns'].add(column) if self.connection.features.supports_index_column_ordering: - constraints[index]["orders"].append("DESC" if order == "D" else "ASC") + constraints[index]['orders'].append('DESC' if order == 'D' else 'ASC') # Convert the sorted sets to lists for constraint in constraints.values(): - constraint["columns"] = list(constraint["columns"]) + constraint['columns'] = list(constraint['columns']) return constraints diff --git a/venv/Lib/site-packages/django/db/backends/mysql/operations.py b/venv/Lib/site-packages/django/db/backends/mysql/operations.py index 7a57457..a646cab 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/operations.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/operations.py @@ -2,7 +2,6 @@ import uuid from django.conf import settings from django.db.backends.base.operations import BaseDatabaseOperations -from django.db.backends.utils import split_tzname_delta from django.utils import timezone from django.utils.encoding import force_str @@ -13,42 +12,42 @@ class DatabaseOperations(BaseDatabaseOperations): # MySQL stores positive fields as UNSIGNED ints. integer_field_ranges = { **BaseDatabaseOperations.integer_field_ranges, - "PositiveSmallIntegerField": (0, 65535), - "PositiveIntegerField": (0, 4294967295), - "PositiveBigIntegerField": (0, 18446744073709551615), + 'PositiveSmallIntegerField': (0, 65535), + 'PositiveIntegerField': (0, 4294967295), + 'PositiveBigIntegerField': (0, 18446744073709551615), } cast_data_types = { - "AutoField": "signed integer", - "BigAutoField": "signed integer", - "SmallAutoField": "signed integer", - "CharField": "char(%(max_length)s)", - "DecimalField": "decimal(%(max_digits)s, %(decimal_places)s)", - "TextField": "char", - "IntegerField": "signed integer", - "BigIntegerField": "signed integer", - "SmallIntegerField": "signed integer", - "PositiveBigIntegerField": "unsigned integer", - "PositiveIntegerField": "unsigned integer", - "PositiveSmallIntegerField": "unsigned integer", - "DurationField": "signed integer", + 'AutoField': 'signed integer', + 'BigAutoField': 'signed integer', + 'SmallAutoField': 'signed integer', + 'CharField': 'char(%(max_length)s)', + 'DecimalField': 'decimal(%(max_digits)s, %(decimal_places)s)', + 'TextField': 'char', + 'IntegerField': 'signed integer', + 'BigIntegerField': 'signed integer', + 'SmallIntegerField': 'signed integer', + 'PositiveBigIntegerField': 'unsigned integer', + 'PositiveIntegerField': 'unsigned integer', + 'PositiveSmallIntegerField': 'unsigned integer', + 'DurationField': 'signed integer', } - cast_char_field_without_max_length = "char" - explain_prefix = "EXPLAIN" + cast_char_field_without_max_length = 'char' + explain_prefix = 'EXPLAIN' def date_extract_sql(self, lookup_type, field_name): # https://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - if lookup_type == "week_day": + if lookup_type == 'week_day': # DAYOFWEEK() returns an integer, 1-7, Sunday=1. return "DAYOFWEEK(%s)" % field_name - elif lookup_type == "iso_week_day": + elif lookup_type == 'iso_week_day': # WEEKDAY() returns an integer, 0-6, Monday=0. return "WEEKDAY(%s) + 1" % field_name - elif lookup_type == "week": + elif lookup_type == 'week': # Override the value of default_week_format for consistency with # other database backends. # Mode 3: Monday, 1-53, with 4 or more days this year. return "WEEK(%s, 3)" % field_name - elif lookup_type == "iso_year": + elif lookup_type == 'iso_year': # Get the year part from the YEARWEEK function, which returns a # number as year * 100 + week. return "TRUNCATE(YEARWEEK(%s, 3), -2) / 100" % field_name @@ -59,26 +58,29 @@ class DatabaseOperations(BaseDatabaseOperations): def date_trunc_sql(self, lookup_type, field_name, tzname=None): field_name = self._convert_field_to_tz(field_name, tzname) fields = { - "year": "%%Y-01-01", - "month": "%%Y-%%m-01", + 'year': '%%Y-01-01', + 'month': '%%Y-%%m-01', } # Use double percents to escape. if lookup_type in fields: format_str = fields[lookup_type] return "CAST(DATE_FORMAT(%s, '%s') AS DATE)" % (field_name, format_str) - elif lookup_type == "quarter": - return ( - "MAKEDATE(YEAR(%s), 1) + " - "INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" - % (field_name, field_name) + elif lookup_type == 'quarter': + return "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" % ( + field_name, field_name + ) + elif lookup_type == 'week': + return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % ( + field_name, field_name ) - elif lookup_type == "week": - return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % (field_name, field_name) else: return "DATE(%s)" % (field_name) def _prepare_tzname_delta(self, tzname): - tzname, sign, offset = split_tzname_delta(tzname) - return f"{sign}{offset}" if offset else tzname + if '+' in tzname: + return tzname[tzname.find('+'):] + elif '-' in tzname: + return tzname[tzname.find('-'):] + return tzname def _convert_field_to_tz(self, field_name, tzname): if tzname and settings.USE_TZ and self.connection.timezone_name != tzname: @@ -103,23 +105,16 @@ class DatabaseOperations(BaseDatabaseOperations): def datetime_trunc_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - fields = ["year", "month", "day", "hour", "minute", "second"] - format = ( - "%%Y-", - "%%m", - "-%%d", - " %%H:", - "%%i", - ":%%s", - ) # Use double percents to escape. - format_def = ("0000-", "01", "-01", " 00:", "00", ":00") - if lookup_type == "quarter": + fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] + format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape. + format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') + if lookup_type == 'quarter': return ( "CAST(DATE_FORMAT(MAKEDATE(YEAR({field_name}), 1) + " - "INTERVAL QUARTER({field_name}) QUARTER - " - + "INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)" + "INTERVAL QUARTER({field_name}) QUARTER - " + + "INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)" ).format(field_name=field_name) - if lookup_type == "week": + if lookup_type == 'week': return ( "CAST(DATE_FORMAT(DATE_SUB({field_name}, " "INTERVAL WEEKDAY({field_name}) DAY), " @@ -130,16 +125,16 @@ class DatabaseOperations(BaseDatabaseOperations): except ValueError: sql = field_name else: - format_str = "".join(format[:i] + format_def[i:]) + format_str = ''.join(format[:i] + format_def[i:]) sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) return sql def time_trunc_sql(self, lookup_type, field_name, tzname=None): field_name = self._convert_field_to_tz(field_name, tzname) fields = { - "hour": "%%H:00:00", - "minute": "%%H:%%i:00", - "second": "%%H:%%i:%%s", + 'hour': '%%H:00:00', + 'minute': '%%H:%%i:00', + 'second': '%%H:%%i:%%s', } # Use double percents to escape. if lookup_type in fields: format_str = fields[lookup_type] @@ -155,7 +150,7 @@ class DatabaseOperations(BaseDatabaseOperations): return cursor.fetchall() def format_for_duration_arithmetic(self, sql): - return "INTERVAL %s MICROSECOND" % sql + return 'INTERVAL %s MICROSECOND' % sql def force_no_ordering(self): """ @@ -173,7 +168,7 @@ class DatabaseOperations(BaseDatabaseOperations): # attribute where the exact query sent to the database is saved. # See MySQLdb/cursors.py in the source distribution. # MySQLdb returns string, PyMySQL bytes. - return force_str(getattr(cursor, "_executed", None), errors="replace") + return force_str(getattr(cursor, '_executed', None), errors='replace') def no_limit_value(self): # 2**64 - 1, as recommended by the MySQL documentation @@ -188,67 +183,58 @@ class DatabaseOperations(BaseDatabaseOperations): # MySQL and MariaDB < 10.5.0 don't support an INSERT...RETURNING # statement. if not fields: - return "", () + return '', () columns = [ - "%s.%s" - % ( + '%s.%s' % ( self.quote_name(field.model._meta.db_table), self.quote_name(field.column), - ) - for field in fields + ) for field in fields ] - return "RETURNING %s" % ", ".join(columns), () + return 'RETURNING %s' % ', '.join(columns), () def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False): if not tables: return [] - sql = ["SET FOREIGN_KEY_CHECKS = 0;"] + sql = ['SET FOREIGN_KEY_CHECKS = 0;'] if reset_sequences: # It's faster to TRUNCATE tables that require a sequence reset # since ALTER TABLE AUTO_INCREMENT is slower than TRUNCATE. sql.extend( - "%s %s;" - % ( - style.SQL_KEYWORD("TRUNCATE"), + '%s %s;' % ( + style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table_name)), - ) - for table_name in tables + ) for table_name in tables ) else: # Otherwise issue a simple DELETE since it's faster than TRUNCATE # and preserves sequences. sql.extend( - "%s %s %s;" - % ( - style.SQL_KEYWORD("DELETE"), - style.SQL_KEYWORD("FROM"), + '%s %s %s;' % ( + style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), style.SQL_FIELD(self.quote_name(table_name)), - ) - for table_name in tables + ) for table_name in tables ) - sql.append("SET FOREIGN_KEY_CHECKS = 1;") + sql.append('SET FOREIGN_KEY_CHECKS = 1;') return sql def sequence_reset_by_name_sql(self, style, sequences): return [ - "%s %s %s %s = 1;" - % ( - style.SQL_KEYWORD("ALTER"), - style.SQL_KEYWORD("TABLE"), - style.SQL_FIELD(self.quote_name(sequence_info["table"])), - style.SQL_FIELD("AUTO_INCREMENT"), - ) - for sequence_info in sequences + '%s %s %s %s = 1;' % ( + style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('TABLE'), + style.SQL_FIELD(self.quote_name(sequence_info['table'])), + style.SQL_FIELD('AUTO_INCREMENT'), + ) for sequence_info in sequences ] def validate_autopk_value(self, value): # Zero in AUTO_INCREMENT field does not work without the # NO_AUTO_VALUE_ON_ZERO SQL mode. if value == 0 and not self.connection.features.allows_auto_pk_0: - raise ValueError( - "The database backend does not accept 0 as a value for AutoField." - ) + raise ValueError('The database backend does not accept 0 as a ' + 'value for AutoField.') return value def adapt_datetimefield_value(self, value): @@ -256,7 +242,7 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value # MySQL doesn't support tz-aware datetimes @@ -264,10 +250,7 @@ class DatabaseOperations(BaseDatabaseOperations): if settings.USE_TZ: value = timezone.make_naive(value, self.connection.timezone) else: - raise ValueError( - "MySQL backend does not support timezone-aware datetimes when " - "USE_TZ is False." - ) + raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.") return str(value) def adapt_timefield_value(self, value): @@ -275,20 +258,20 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value # MySQL doesn't support tz-aware times if timezone.is_aware(value): raise ValueError("MySQL backend does not support timezone-aware times.") - return value.isoformat(timespec="microseconds") + return str(value) def max_name_length(self): return 64 def pk_default_value(self): - return "NULL" + return 'NULL' def bulk_insert_sql(self, fields, placeholder_rows): placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) @@ -296,27 +279,27 @@ class DatabaseOperations(BaseDatabaseOperations): return "VALUES " + values_sql def combine_expression(self, connector, sub_expressions): - if connector == "^": - return "POW(%s)" % ",".join(sub_expressions) + if connector == '^': + return 'POW(%s)' % ','.join(sub_expressions) # Convert the result to a signed integer since MySQL's binary operators # return an unsigned integer. - elif connector in ("&", "|", "<<", "#"): - connector = "^" if connector == "#" else connector - return "CONVERT(%s, SIGNED)" % connector.join(sub_expressions) - elif connector == ">>": + elif connector in ('&', '|', '<<', '#'): + connector = '^' if connector == '#' else connector + return 'CONVERT(%s, SIGNED)' % connector.join(sub_expressions) + elif connector == '>>': lhs, rhs = sub_expressions - return "FLOOR(%(lhs)s / POW(2, %(rhs)s))" % {"lhs": lhs, "rhs": rhs} + return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs} return super().combine_expression(connector, sub_expressions) def get_db_converters(self, expression): converters = super().get_db_converters(expression) internal_type = expression.output_field.get_internal_type() - if internal_type == "BooleanField": + if internal_type in ['BooleanField', 'NullBooleanField']: converters.append(self.convert_booleanfield_value) - elif internal_type == "DateTimeField": + elif internal_type == 'DateTimeField': if settings.USE_TZ: converters.append(self.convert_datetimefield_value) - elif internal_type == "UUIDField": + elif internal_type == 'UUIDField': converters.append(self.convert_uuidfield_value) return converters @@ -336,91 +319,62 @@ class DatabaseOperations(BaseDatabaseOperations): return value def binary_placeholder_sql(self, value): - return ( - "_binary %s" if value is not None and not hasattr(value, "as_sql") else "%s" - ) + return '_binary %s' if value is not None and not hasattr(value, 'as_sql') else '%s' def subtract_temporals(self, internal_type, lhs, rhs): lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs - if internal_type == "TimeField": + if internal_type == 'TimeField': if self.connection.mysql_is_mariadb: # MariaDB includes the microsecond component in TIME_TO_SEC as # a decimal. MySQL returns an integer without microseconds. - return ( - "CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) " - "* 1000000 AS SIGNED)" - ) % { - "lhs": lhs_sql, - "rhs": rhs_sql, - }, ( - *lhs_params, - *rhs_params, - ) + return 'CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) * 1000000 AS SIGNED)' % { + 'lhs': lhs_sql, 'rhs': rhs_sql + }, (*lhs_params, *rhs_params) return ( "((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -" " (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))" - ) % {"lhs": lhs_sql, "rhs": rhs_sql}, tuple(lhs_params) * 2 + tuple( - rhs_params - ) * 2 + ) % {'lhs': lhs_sql, 'rhs': rhs_sql}, tuple(lhs_params) * 2 + tuple(rhs_params) * 2 params = (*rhs_params, *lhs_params) return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), params def explain_query_prefix(self, format=None, **options): # Alias MySQL's TRADITIONAL to TEXT for consistency with other backends. - if format and format.upper() == "TEXT": - format = "TRADITIONAL" - elif ( - not format and "TREE" in self.connection.features.supported_explain_formats - ): + if format and format.upper() == 'TEXT': + format = 'TRADITIONAL' + elif not format and 'TREE' in self.connection.features.supported_explain_formats: # Use TREE by default (if supported) as it's more informative. - format = "TREE" - analyze = options.pop("analyze", False) + format = 'TREE' + analyze = options.pop('analyze', False) prefix = super().explain_query_prefix(format, **options) if analyze and self.connection.features.supports_explain_analyze: # MariaDB uses ANALYZE instead of EXPLAIN ANALYZE. - prefix = ( - "ANALYZE" if self.connection.mysql_is_mariadb else prefix + " ANALYZE" - ) + prefix = 'ANALYZE' if self.connection.mysql_is_mariadb else prefix + ' ANALYZE' if format and not (analyze and not self.connection.mysql_is_mariadb): # Only MariaDB supports the analyze option with formats. - prefix += " FORMAT=%s" % format + prefix += ' FORMAT=%s' % format return prefix def regex_lookup(self, lookup_type): # REGEXP BINARY doesn't work correctly in MySQL 8+ and REGEXP_LIKE # doesn't exist in MySQL 5.x or in MariaDB. - if ( - self.connection.mysql_version < (8, 0, 0) - or self.connection.mysql_is_mariadb - ): - if lookup_type == "regex": - return "%s REGEXP BINARY %s" - return "%s REGEXP %s" + if self.connection.mysql_version < (8, 0, 0) or self.connection.mysql_is_mariadb: + if lookup_type == 'regex': + return '%s REGEXP BINARY %s' + return '%s REGEXP %s' - match_option = "c" if lookup_type == "regex" else "i" + match_option = 'c' if lookup_type == 'regex' else 'i' return "REGEXP_LIKE(%%s, %%s, '%s')" % match_option def insert_statement(self, ignore_conflicts=False): - return ( - "INSERT IGNORE INTO" - if ignore_conflicts - else super().insert_statement(ignore_conflicts) - ) + return 'INSERT IGNORE INTO' if ignore_conflicts else super().insert_statement(ignore_conflicts) def lookup_cast(self, lookup_type, internal_type=None): - lookup = "%s" - if internal_type == "JSONField": + lookup = '%s' + if internal_type == 'JSONField': if self.connection.mysql_is_mariadb or lookup_type in ( - "iexact", - "contains", - "icontains", - "startswith", - "istartswith", - "endswith", - "iendswith", - "regex", - "iregex", + 'iexact', 'contains', 'icontains', 'startswith', 'istartswith', + 'endswith', 'iendswith', 'regex', 'iregex', ): - lookup = "JSON_UNQUOTE(%s)" + lookup = 'JSON_UNQUOTE(%s)' return lookup diff --git a/venv/Lib/site-packages/django/db/backends/mysql/schema.py b/venv/Lib/site-packages/django/db/backends/mysql/schema.py index 7ab12f2..39450dd 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/schema.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/schema.py @@ -10,26 +10,24 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_alter_column_not_null = "MODIFY %(column)s %(type)s NOT NULL" sql_alter_column_type = "MODIFY %(column)s %(type)s" sql_alter_column_collate = "MODIFY %(column)s %(type)s%(collation)s" - sql_alter_column_no_default_null = "ALTER COLUMN %(column)s SET DEFAULT NULL" + sql_alter_column_no_default_null = 'ALTER COLUMN %(column)s SET DEFAULT NULL' # No 'CASCADE' which works as a no-op in MySQL but is undocumented sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s" sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s" sql_create_column_inline_fk = ( - ", ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) " - "REFERENCES %(to_table)s(%(to_column)s)" + ', ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) ' + 'REFERENCES %(to_table)s(%(to_column)s)' ) sql_delete_fk = "ALTER TABLE %(table)s DROP FOREIGN KEY %(name)s" sql_delete_index = "DROP INDEX %(name)s ON %(table)s" - sql_create_pk = ( - "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)" - ) + sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)" sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" - sql_create_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s" + sql_create_index = 'CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s' @property def sql_delete_check(self): @@ -37,8 +35,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # The name of the column check constraint is the same as the field # name on MariaDB. Adding IF EXISTS clause prevents migrations # crash. Constraint is removed during a "MODIFY" column statement. - return "ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(name)s" - return "ALTER TABLE %(table)s DROP CHECK %(name)s" + return 'ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(name)s' + return 'ALTER TABLE %(table)s DROP CHECK %(name)s' @property def sql_rename_column(self): @@ -49,26 +47,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): return super().sql_rename_column elif self.connection.mysql_version >= (8, 0, 4): return super().sql_rename_column - return "ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s" + return 'ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s' def quote_value(self, value): self.connection.ensure_connection() if isinstance(value, str): - value = value.replace("%", "%%") + value = value.replace('%', '%%') # MySQLdb escapes to string, PyMySQL to bytes. - quoted = self.connection.connection.escape( - value, self.connection.connection.encoders - ) + quoted = self.connection.connection.escape(value, self.connection.connection.encoders) if isinstance(value, str) and isinstance(quoted, bytes): quoted = quoted.decode() return quoted def _is_limited_data_type(self, field): db_type = field.db_type(self.connection) - return ( - db_type is not None - and db_type.lower() in self.connection._limited_data_types - ) + return db_type is not None and db_type.lower() in self.connection._limited_data_types def skip_default(self, field): if not self._supports_limited_data_type_defaults: @@ -92,13 +85,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def _column_default_sql(self, field): if ( - not self.connection.mysql_is_mariadb - and self._supports_limited_data_type_defaults - and self._is_limited_data_type(field) + not self.connection.mysql_is_mariadb and + self._supports_limited_data_type_defaults and + self._is_limited_data_type(field) ): # MySQL supports defaults for BLOB and TEXT columns only if the # default value is written as an expression i.e. in parentheses. - return "(%s)" + return '(%s)' return super()._column_default_sql(field) def add_field(self, model, field): @@ -108,32 +101,25 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # field.default may be unhashable, so a set isn't used for "in" check. if self.skip_default(field) and field.default not in (None, NOT_PROVIDED): effective_default = self.effective_default(field) - self.execute( - "UPDATE %(table)s SET %(column)s = %%s" - % { - "table": self.quote_name(model._meta.db_table), - "column": self.quote_name(field.column), - }, - [effective_default], - ) + self.execute('UPDATE %(table)s SET %(column)s = %%s' % { + 'table': self.quote_name(model._meta.db_table), + 'column': self.quote_name(field.column), + }, [effective_default]) def _field_should_be_indexed(self, model, field): - if not super()._field_should_be_indexed(model, field): - return False - + create_index = super()._field_should_be_indexed(model, field) storage = self.connection.introspection.get_storage_engine( self.connection.cursor(), model._meta.db_table ) # No need to create an index for ForeignKey fields except if # db_constraint=False because the index from that constraint won't be # created. - if ( - storage == "InnoDB" - and field.get_internal_type() == "ForeignKey" - and field.db_constraint - ): + if (storage == "InnoDB" and + create_index and + field.get_internal_type() == 'ForeignKey' and + field.db_constraint): return False - return not self._is_limited_data_type(field) + return not self._is_limited_data_type(field) and create_index def _delete_composed_index(self, model, fields, *args): """ @@ -145,13 +131,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): recreate a FK index. """ first_field = model._meta.get_field(fields[0]) - if first_field.get_internal_type() == "ForeignKey": - constraint_names = self._constraint_names( - model, [first_field.column], index=True - ) + if first_field.get_internal_type() == 'ForeignKey': + constraint_names = self._constraint_names(model, [first_field.column], index=True) if not constraint_names: self.execute( - self._create_index_sql(model, fields=[first_field], suffix="") + self._create_index_sql(model, fields=[first_field], suffix='') ) return super()._delete_composed_index(model, fields, *args) diff --git a/venv/Lib/site-packages/django/db/backends/mysql/validation.py b/venv/Lib/site-packages/django/db/backends/mysql/validation.py index fdc3809..41e600a 100644 --- a/venv/Lib/site-packages/django/db/backends/mysql/validation.py +++ b/venv/Lib/site-packages/django/db/backends/mysql/validation.py @@ -10,29 +10,24 @@ class DatabaseValidation(BaseDatabaseValidation): return issues def _check_sql_mode(self, **kwargs): - if not ( - self.connection.sql_mode & {"STRICT_TRANS_TABLES", "STRICT_ALL_TABLES"} - ): - return [ - checks.Warning( - "%s Strict Mode is not set for database connection '%s'" - % (self.connection.display_name, self.connection.alias), - hint=( - "%s's Strict Mode fixes many data integrity problems in " - "%s, such as data truncation upon insertion, by " - "escalating warnings into errors. It is strongly " - "recommended you activate it. See: " - "https://docs.djangoproject.com/en/%s/ref/databases/" - "#mysql-sql-mode" - % ( - self.connection.display_name, - self.connection.display_name, - get_docs_version(), - ), + if not (self.connection.sql_mode & {'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES'}): + return [checks.Warning( + "%s Strict Mode is not set for database connection '%s'" + % (self.connection.display_name, self.connection.alias), + hint=( + "%s's Strict Mode fixes many data integrity problems in " + "%s, such as data truncation upon insertion, by " + "escalating warnings into errors. It is strongly " + "recommended you activate it. See: " + "https://docs.djangoproject.com/en/%s/ref/databases/#mysql-sql-mode" + % ( + self.connection.display_name, + self.connection.display_name, + get_docs_version(), ), - id="mysql.W002", - ) - ] + ), + id='mysql.W002', + )] return [] def check_field_type(self, field, field_type): @@ -43,35 +38,32 @@ class DatabaseValidation(BaseDatabaseValidation): MySQL doesn't support a database index on some data types. """ errors = [] - if ( - field_type.startswith("varchar") - and field.unique - and (field.max_length is None or int(field.max_length) > 255) - ): + if (field_type.startswith('varchar') and field.unique and + (field.max_length is None or int(field.max_length) > 255)): errors.append( checks.Warning( - "%s may not allow unique CharFields to have a max_length " - "> 255." % self.connection.display_name, + '%s may not allow unique CharFields to have a max_length ' + '> 255.' % self.connection.display_name, obj=field, hint=( - "See: https://docs.djangoproject.com/en/%s/ref/" - "databases/#mysql-character-fields" % get_docs_version() + 'See: https://docs.djangoproject.com/en/%s/ref/' + 'databases/#mysql-character-fields' % get_docs_version() ), - id="mysql.W003", + id='mysql.W003', ) ) if field.db_index and field_type.lower() in self.connection._limited_data_types: errors.append( checks.Warning( - "%s does not support a database index on %s columns." + '%s does not support a database index on %s columns.' % (self.connection.display_name, field_type), hint=( "An index won't be created. Silence this warning if " "you don't care about it." ), obj=field, - id="fields.W162", + id='fields.W162', ) ) return errors diff --git a/venv/Lib/site-packages/django/db/backends/oracle/base.py b/venv/Lib/site-packages/django/db/backends/oracle/base.py index 7cbee76..d1650e7 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/base.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/base.py @@ -21,31 +21,27 @@ from django.utils.functional import cached_property def _setup_environment(environ): # Cygwin requires some special voodoo to set the environment variables # properly so that Oracle will see them. - if platform.system().upper().startswith("CYGWIN"): + if platform.system().upper().startswith('CYGWIN'): try: import ctypes except ImportError as e: - raise ImproperlyConfigured( - "Error loading ctypes: %s; " - "the Oracle backend requires ctypes to " - "operate correctly under Cygwin." % e - ) - kernel32 = ctypes.CDLL("kernel32") + raise ImproperlyConfigured("Error loading ctypes: %s; " + "the Oracle backend requires ctypes to " + "operate correctly under Cygwin." % e) + kernel32 = ctypes.CDLL('kernel32') for name, value in environ: kernel32.SetEnvironmentVariableA(name, value) else: os.environ.update(environ) -_setup_environment( - [ - # Oracle takes client-side character set encoding from the environment. - ("NLS_LANG", ".AL32UTF8"), - # This prevents Unicode from getting mangled by getting encoded into the - # potentially non-Unicode database character set. - ("ORA_NCHAR_LITERAL_REPLACE", "TRUE"), - ] -) +_setup_environment([ + # Oracle takes client-side character set encoding from the environment. + ('NLS_LANG', '.AL32UTF8'), + # This prevents Unicode from getting mangled by getting encoded into the + # potentially non-Unicode database character set. + ('ORA_NCHAR_LITERAL_REPLACE', 'TRUE'), +]) try: @@ -81,16 +77,17 @@ def wrap_oracle_errors(): # Convert that case to Django's IntegrityError exception. x = e.args[0] if ( - hasattr(x, "code") - and hasattr(x, "message") - and x.code == 2091 - and ("ORA-02291" in x.message or "ORA-00001" in x.message) + hasattr(x, 'code') and + hasattr(x, 'message') and + x.code == 2091 and + ('ORA-02291' in x.message or 'ORA-00001' in x.message) ): raise IntegrityError(*tuple(e.args)) raise class _UninitializedOperatorsDescriptor: + def __get__(self, instance, cls=None): # If connection.operators is looked up before a connection has been # created, transparently initialize connection.operators to avert an @@ -99,12 +96,12 @@ class _UninitializedOperatorsDescriptor: raise AttributeError("operators not available as class attribute") # Creating a cursor will initialize the operators. instance.cursor().close() - return instance.__dict__["operators"] + return instance.__dict__['operators'] class DatabaseWrapper(BaseDatabaseWrapper): - vendor = "oracle" - display_name = "Oracle" + vendor = 'oracle' + display_name = 'Oracle' # This dictionary maps Field objects to their associated Oracle column # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. @@ -113,86 +110,73 @@ class DatabaseWrapper(BaseDatabaseWrapper): # Any format strings starting with "qn_" are quoted before being used in the # output (the "qn_" prefix is stripped before the lookup is performed. data_types = { - "AutoField": "NUMBER(11) GENERATED BY DEFAULT ON NULL AS IDENTITY", - "BigAutoField": "NUMBER(19) GENERATED BY DEFAULT ON NULL AS IDENTITY", - "BinaryField": "BLOB", - "BooleanField": "NUMBER(1)", - "CharField": "NVARCHAR2(%(max_length)s)", - "DateField": "DATE", - "DateTimeField": "TIMESTAMP", - "DecimalField": "NUMBER(%(max_digits)s, %(decimal_places)s)", - "DurationField": "INTERVAL DAY(9) TO SECOND(6)", - "FileField": "NVARCHAR2(%(max_length)s)", - "FilePathField": "NVARCHAR2(%(max_length)s)", - "FloatField": "DOUBLE PRECISION", - "IntegerField": "NUMBER(11)", - "JSONField": "NCLOB", - "BigIntegerField": "NUMBER(19)", - "IPAddressField": "VARCHAR2(15)", - "GenericIPAddressField": "VARCHAR2(39)", - "OneToOneField": "NUMBER(11)", - "PositiveBigIntegerField": "NUMBER(19)", - "PositiveIntegerField": "NUMBER(11)", - "PositiveSmallIntegerField": "NUMBER(11)", - "SlugField": "NVARCHAR2(%(max_length)s)", - "SmallAutoField": "NUMBER(5) GENERATED BY DEFAULT ON NULL AS IDENTITY", - "SmallIntegerField": "NUMBER(11)", - "TextField": "NCLOB", - "TimeField": "TIMESTAMP", - "URLField": "VARCHAR2(%(max_length)s)", - "UUIDField": "VARCHAR2(32)", + 'AutoField': 'NUMBER(11) GENERATED BY DEFAULT ON NULL AS IDENTITY', + 'BigAutoField': 'NUMBER(19) GENERATED BY DEFAULT ON NULL AS IDENTITY', + 'BinaryField': 'BLOB', + 'BooleanField': 'NUMBER(1)', + 'CharField': 'NVARCHAR2(%(max_length)s)', + 'DateField': 'DATE', + 'DateTimeField': 'TIMESTAMP', + 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', + 'DurationField': 'INTERVAL DAY(9) TO SECOND(6)', + 'FileField': 'NVARCHAR2(%(max_length)s)', + 'FilePathField': 'NVARCHAR2(%(max_length)s)', + 'FloatField': 'DOUBLE PRECISION', + 'IntegerField': 'NUMBER(11)', + 'JSONField': 'NCLOB', + 'BigIntegerField': 'NUMBER(19)', + 'IPAddressField': 'VARCHAR2(15)', + 'GenericIPAddressField': 'VARCHAR2(39)', + 'NullBooleanField': 'NUMBER(1)', + 'OneToOneField': 'NUMBER(11)', + 'PositiveBigIntegerField': 'NUMBER(19)', + 'PositiveIntegerField': 'NUMBER(11)', + 'PositiveSmallIntegerField': 'NUMBER(11)', + 'SlugField': 'NVARCHAR2(%(max_length)s)', + 'SmallAutoField': 'NUMBER(5) GENERATED BY DEFAULT ON NULL AS IDENTITY', + 'SmallIntegerField': 'NUMBER(11)', + 'TextField': 'NCLOB', + 'TimeField': 'TIMESTAMP', + 'URLField': 'VARCHAR2(%(max_length)s)', + 'UUIDField': 'VARCHAR2(32)', } data_type_check_constraints = { - "BooleanField": "%(qn_column)s IN (0,1)", - "JSONField": "%(qn_column)s IS JSON", - "PositiveBigIntegerField": "%(qn_column)s >= 0", - "PositiveIntegerField": "%(qn_column)s >= 0", - "PositiveSmallIntegerField": "%(qn_column)s >= 0", + 'BooleanField': '%(qn_column)s IN (0,1)', + 'JSONField': '%(qn_column)s IS JSON', + 'NullBooleanField': '%(qn_column)s IN (0,1)', + 'PositiveBigIntegerField': '%(qn_column)s >= 0', + 'PositiveIntegerField': '%(qn_column)s >= 0', + 'PositiveSmallIntegerField': '%(qn_column)s >= 0', } # Oracle doesn't support a database index on these columns. - _limited_data_types = ("clob", "nclob", "blob") + _limited_data_types = ('clob', 'nclob', 'blob') operators = _UninitializedOperatorsDescriptor() _standard_operators = { - "exact": "= %s", - "iexact": "= UPPER(%s)", - "contains": ( - "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), - "icontains": ( - "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) " - "ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), - "gt": "> %s", - "gte": ">= %s", - "lt": "< %s", - "lte": "<= %s", - "startswith": ( - "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), - "endswith": ( - "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), - "istartswith": ( - "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) " - "ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), - "iendswith": ( - "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) " - "ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - ), + 'exact': '= %s', + 'iexact': '= UPPER(%s)', + 'contains': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", + 'icontains': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", + 'gt': '> %s', + 'gte': '>= %s', + 'lt': '< %s', + 'lte': '<= %s', + 'startswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", + 'endswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", + 'istartswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", + 'iendswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)", } _likec_operators = { **_standard_operators, - "contains": "LIKEC %s ESCAPE '\\'", - "icontains": "LIKEC UPPER(%s) ESCAPE '\\'", - "startswith": "LIKEC %s ESCAPE '\\'", - "endswith": "LIKEC %s ESCAPE '\\'", - "istartswith": "LIKEC UPPER(%s) ESCAPE '\\'", - "iendswith": "LIKEC UPPER(%s) ESCAPE '\\'", + 'contains': "LIKEC %s ESCAPE '\\'", + 'icontains': "LIKEC UPPER(%s) ESCAPE '\\'", + 'startswith': "LIKEC %s ESCAPE '\\'", + 'endswith': "LIKEC %s ESCAPE '\\'", + 'istartswith': "LIKEC UPPER(%s) ESCAPE '\\'", + 'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'", } # The patterns below are used to generate SQL pattern lookup clauses when @@ -205,22 +189,19 @@ class DatabaseWrapper(BaseDatabaseWrapper): # the LIKE operator. pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '\\'), '%%', '\%%'), '_', '\_')" _pattern_ops = { - "contains": "'%%' || {} || '%%'", - "icontains": "'%%' || UPPER({}) || '%%'", - "startswith": "{} || '%%'", - "istartswith": "UPPER({}) || '%%'", - "endswith": "'%%' || {}", - "iendswith": "'%%' || UPPER({})", + 'contains': "'%%' || {} || '%%'", + 'icontains': "'%%' || UPPER({}) || '%%'", + 'startswith': "{} || '%%'", + 'istartswith': "UPPER({}) || '%%'", + 'endswith': "'%%' || {}", + 'iendswith': "'%%' || UPPER({})", } - _standard_pattern_ops = { - k: "LIKE TRANSLATE( " + v + " USING NCHAR_CS)" - " ESCAPE TRANSLATE('\\' USING NCHAR_CS)" - for k, v in _pattern_ops.items() - } - _likec_pattern_ops = { - k: "LIKEC " + v + " ESCAPE '\\'" for k, v in _pattern_ops.items() - } + _standard_pattern_ops = {k: "LIKE TRANSLATE( " + v + " USING NCHAR_CS)" + " ESCAPE TRANSLATE('\\' USING NCHAR_CS)" + for k, v in _pattern_ops.items()} + _likec_pattern_ops = {k: "LIKEC " + v + " ESCAPE '\\'" + for k, v in _pattern_ops.items()} Database = Database SchemaEditorClass = DatabaseSchemaEditor @@ -234,22 +215,20 @@ class DatabaseWrapper(BaseDatabaseWrapper): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - use_returning_into = self.settings_dict["OPTIONS"].get( - "use_returning_into", True - ) + use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True) self.features.can_return_columns_from_insert = use_returning_into def get_connection_params(self): - conn_params = self.settings_dict["OPTIONS"].copy() - if "use_returning_into" in conn_params: - del conn_params["use_returning_into"] + conn_params = self.settings_dict['OPTIONS'].copy() + if 'use_returning_into' in conn_params: + del conn_params['use_returning_into'] return conn_params @async_unsafe def get_new_connection(self, conn_params): return Database.connect( - user=self.settings_dict["USER"], - password=self.settings_dict["PASSWORD"], + user=self.settings_dict['USER'], + password=self.settings_dict['PASSWORD'], dsn=dsn(self.settings_dict), **conn_params, ) @@ -267,11 +246,11 @@ class DatabaseWrapper(BaseDatabaseWrapper): # TO_CHAR(). cursor.execute( "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" - " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'" - + (" TIME_ZONE = 'UTC'" if settings.USE_TZ else "") + " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'" + + (" TIME_ZONE = 'UTC'" if settings.USE_TZ else '') ) cursor.close() - if "operators" not in self.__dict__: + if 'operators' not in self.__dict__: # Ticket #14149: Check whether our LIKE implementation will # work for this connection or we need to fall back on LIKEC. # This check is performed only once per DatabaseWrapper @@ -279,11 +258,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): # the same settings. cursor = self.create_cursor() try: - cursor.execute( - "SELECT 1 FROM DUAL WHERE DUMMY %s" - % self._standard_operators["contains"], - ["X"], - ) + cursor.execute("SELECT 1 FROM DUAL WHERE DUMMY %s" + % self._standard_operators['contains'], + ['X']) except Database.DatabaseError: self.operators = self._likec_operators self.pattern_ops = self._likec_pattern_ops @@ -309,12 +286,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): # logging is enabled to keep query counts consistent with other backends. def _savepoint_commit(self, sid): if self.queries_logged: - self.queries_log.append( - { - "sql": "-- RELEASE SAVEPOINT %s (faked)" % self.ops.quote_name(sid), - "time": "0.000", - } - ) + self.queries_log.append({ + 'sql': '-- RELEASE SAVEPOINT %s (faked)' % self.ops.quote_name(sid), + 'time': '0.000', + }) def _set_autocommit(self, autocommit): with self.wrap_database_errors: @@ -326,8 +301,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): afterward. """ with self.cursor() as cursor: - cursor.execute("SET CONSTRAINTS ALL IMMEDIATE") - cursor.execute("SET CONSTRAINTS ALL DEFERRED") + cursor.execute('SET CONSTRAINTS ALL IMMEDIATE') + cursor.execute('SET CONSTRAINTS ALL DEFERRED') def is_usable(self): try: @@ -339,12 +314,12 @@ class DatabaseWrapper(BaseDatabaseWrapper): @cached_property def cx_oracle_version(self): - return tuple(int(x) for x in Database.version.split(".")) + return tuple(int(x) for x in Database.version.split('.')) @cached_property def oracle_version(self): with self.temporary_connection(): - return tuple(int(x) for x in self.connection.version.split(".")) + return tuple(int(x) for x in self.connection.version.split('.')) class OracleParam: @@ -360,10 +335,8 @@ class OracleParam: def __init__(self, param, cursor, strings_only=False): # With raw SQL queries, datetimes can reach this function # without being converted by DateTimeField.get_db_prep_value. - if settings.USE_TZ and ( - isinstance(param, datetime.datetime) - and not isinstance(param, Oracle_datetime) - ): + if settings.USE_TZ and (isinstance(param, datetime.datetime) and + not isinstance(param, Oracle_datetime)): param = Oracle_datetime.from_datetime(param) string_size = 0 @@ -372,7 +345,7 @@ class OracleParam: param = 1 elif param is False: param = 0 - if hasattr(param, "bind_parameter"): + if hasattr(param, 'bind_parameter'): self.force_bytes = param.bind_parameter(cursor) elif isinstance(param, (Database.Binary, datetime.timedelta)): self.force_bytes = param @@ -383,7 +356,7 @@ class OracleParam: if isinstance(self.force_bytes, str): # We could optimize by only converting up to 4000 bytes here string_size = len(force_bytes(param, cursor.charset, strings_only)) - if hasattr(param, "input_size"): + if hasattr(param, 'input_size'): # If parameter has `input_size` attribute, use that. self.input_size = param.input_size elif string_size > 4000: @@ -413,7 +386,7 @@ class VariableWrapper: return getattr(self.var, key) def __setattr__(self, key, value): - if key == "var": + if key == 'var': self.__dict__[key] = value else: setattr(self.var, key, value) @@ -425,8 +398,7 @@ class FormatStylePlaceholderCursor: style. This fixes it -- but note that if you want to use a literal "%s" in a query, you'll need to use "%%s". """ - - charset = "utf-8" + charset = 'utf-8' def __init__(self, connection): self.cursor = connection.cursor() @@ -434,7 +406,7 @@ class FormatStylePlaceholderCursor: @staticmethod def _output_number_converter(value): - return decimal.Decimal(value) if "." in value else int(value) + return decimal.Decimal(value) if '.' in value else int(value) @staticmethod def _get_decimal_converter(precision, scale): @@ -464,9 +436,7 @@ class FormatStylePlaceholderCursor: elif precision > 0: # NUMBER(p,s) column: decimal-precision fixed point. # This comes from IntegerField and DecimalField columns. - outconverter = FormatStylePlaceholderCursor._get_decimal_converter( - precision, scale - ) + outconverter = FormatStylePlaceholderCursor._get_decimal_converter(precision, scale) else: # No type information. This normally comes from a # mathematical expression in the SELECT list. Guess int @@ -487,7 +457,7 @@ class FormatStylePlaceholderCursor: def _guess_input_sizes(self, params_list): # Try dict handling; if that fails, treat as sequence - if hasattr(params_list[0], "keys"): + if hasattr(params_list[0], 'keys'): sizes = {} for params in params_list: for k, value in params.items(): @@ -507,7 +477,7 @@ class FormatStylePlaceholderCursor: def _param_generator(self, params): # Try dict handling; if that fails, treat as sequence - if hasattr(params, "items"): + if hasattr(params, 'items'): return {k: v.force_bytes for k, v in params.items()} else: return [p.force_bytes for p in params] @@ -517,11 +487,11 @@ class FormatStylePlaceholderCursor: # it does want a trailing ';' but not a trailing '/'. However, these # characters must be included in the original query in case the query # is being passed to SQL*Plus. - if query.endswith(";") or query.endswith("/"): + if query.endswith(';') or query.endswith('/'): query = query[:-1] if params is None: params = [] - elif hasattr(params, "keys"): + elif hasattr(params, 'keys'): # Handle params as dict args = {k: ":%s" % k for k in params} query = query % args @@ -534,14 +504,15 @@ class FormatStylePlaceholderCursor: # args = [':arg0', ':arg1', ':arg0', ':arg2', ':arg0'] # params = {':arg0': 0.75, ':arg1': 2, ':arg2': 'sth'} params_dict = { - param: ":arg%d" % i for i, param in enumerate(dict.fromkeys(params)) + param: ':arg%d' % i + for i, param in enumerate(dict.fromkeys(params)) } args = [params_dict[param] for param in params] params = {value: key for key, value in params_dict.items()} query = query % tuple(args) else: # Handle params as sequence - args = [(":arg%d" % i) for i in range(len(params))] + args = [(':arg%d' % i) for i in range(len(params))] query = query % tuple(args) return query, self._format_params(params) @@ -563,9 +534,7 @@ class FormatStylePlaceholderCursor: formatted = [firstparams] + [self._format_params(p) for p in params_iter] self._guess_input_sizes(formatted) with wrap_oracle_errors(): - return self.cursor.executemany( - query, [self._param_generator(p) for p in formatted] - ) + return self.cursor.executemany(query, [self._param_generator(p) for p in formatted]) def close(self): try: diff --git a/venv/Lib/site-packages/django/db/backends/oracle/client.py b/venv/Lib/site-packages/django/db/backends/oracle/client.py index 365b116..9920f4c 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/client.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/client.py @@ -4,22 +4,22 @@ from django.db.backends.base.client import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): - executable_name = "sqlplus" - wrapper_name = "rlwrap" + executable_name = 'sqlplus' + wrapper_name = 'rlwrap' @staticmethod def connect_string(settings_dict): from django.db.backends.oracle.utils import dsn return '%s/"%s"@%s' % ( - settings_dict["USER"], - settings_dict["PASSWORD"], + settings_dict['USER'], + settings_dict['PASSWORD'], dsn(settings_dict), ) @classmethod def settings_to_cmd_args_env(cls, settings_dict, parameters): - args = [cls.executable_name, "-L", cls.connect_string(settings_dict)] + args = [cls.executable_name, '-L', cls.connect_string(settings_dict)] wrapper_path = shutil.which(cls.wrapper_name) if wrapper_path: args = [wrapper_path, *args] diff --git a/venv/Lib/site-packages/django/db/backends/oracle/creation.py b/venv/Lib/site-packages/django/db/backends/oracle/creation.py index df773bf..3ca3754 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/creation.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/creation.py @@ -6,10 +6,11 @@ from django.db.backends.base.creation import BaseDatabaseCreation from django.utils.crypto import get_random_string from django.utils.functional import cached_property -TEST_DATABASE_PREFIX = "test_" +TEST_DATABASE_PREFIX = 'test_' class DatabaseCreation(BaseDatabaseCreation): + @cached_property def _maindb_connection(self): """ @@ -20,9 +21,9 @@ class DatabaseCreation(BaseDatabaseCreation): is the main (non-test) connection. """ settings_dict = settings.DATABASES[self.connection.alias] - user = settings_dict.get("SAVED_USER") or settings_dict["USER"] - password = settings_dict.get("SAVED_PASSWORD") or settings_dict["PASSWORD"] - settings_dict = {**settings_dict, "USER": user, "PASSWORD": password} + user = settings_dict.get('SAVED_USER') or settings_dict['USER'] + password = settings_dict.get('SAVED_PASSWORD') or settings_dict['PASSWORD'] + settings_dict = {**settings_dict, 'USER': user, 'PASSWORD': password} DatabaseWrapper = type(self.connection) return DatabaseWrapper(settings_dict, alias=self.connection.alias) @@ -31,97 +32,72 @@ class DatabaseCreation(BaseDatabaseCreation): with self._maindb_connection.cursor() as cursor: if self._test_database_create(): try: - self._execute_test_db_creation( - cursor, parameters, verbosity, keepdb - ) + self._execute_test_db_creation(cursor, parameters, verbosity, keepdb) except Exception as e: - if "ORA-01543" not in str(e): + if 'ORA-01543' not in str(e): # All errors except "tablespace already exists" cancel tests - self.log("Got an error creating the test database: %s" % e) + self.log('Got an error creating the test database: %s' % e) sys.exit(2) if not autoclobber: confirm = input( "It appears the test database, %s, already exists. " - "Type 'yes' to delete it, or 'no' to cancel: " - % parameters["user"] - ) - if autoclobber or confirm == "yes": + "Type 'yes' to delete it, or 'no' to cancel: " % parameters['user']) + if autoclobber or confirm == 'yes': if verbosity >= 1: - self.log( - "Destroying old test database for alias '%s'..." - % self.connection.alias - ) + self.log("Destroying old test database for alias '%s'..." % self.connection.alias) try: - self._execute_test_db_destruction( - cursor, parameters, verbosity - ) + self._execute_test_db_destruction(cursor, parameters, verbosity) except DatabaseError as e: - if "ORA-29857" in str(e): - self._handle_objects_preventing_db_destruction( - cursor, parameters, verbosity, autoclobber - ) + if 'ORA-29857' in str(e): + self._handle_objects_preventing_db_destruction(cursor, parameters, + verbosity, autoclobber) else: - # Ran into a database error that isn't about - # leftover objects in the tablespace. - self.log( - "Got an error destroying the old test database: %s" - % e - ) + # Ran into a database error that isn't about leftover objects in the tablespace + self.log('Got an error destroying the old test database: %s' % e) sys.exit(2) except Exception as e: - self.log( - "Got an error destroying the old test database: %s" % e - ) + self.log('Got an error destroying the old test database: %s' % e) sys.exit(2) try: - self._execute_test_db_creation( - cursor, parameters, verbosity, keepdb - ) + self._execute_test_db_creation(cursor, parameters, verbosity, keepdb) except Exception as e: - self.log( - "Got an error recreating the test database: %s" % e - ) + self.log('Got an error recreating the test database: %s' % e) sys.exit(2) else: - self.log("Tests cancelled.") + self.log('Tests cancelled.') sys.exit(1) if self._test_user_create(): if verbosity >= 1: - self.log("Creating test user...") + self.log('Creating test user...') try: self._create_test_user(cursor, parameters, verbosity, keepdb) except Exception as e: - if "ORA-01920" not in str(e): + if 'ORA-01920' not in str(e): # All errors except "user already exists" cancel tests - self.log("Got an error creating the test user: %s" % e) + self.log('Got an error creating the test user: %s' % e) sys.exit(2) if not autoclobber: confirm = input( "It appears the test user, %s, already exists. Type " - "'yes' to delete it, or 'no' to cancel: " - % parameters["user"] - ) - if autoclobber or confirm == "yes": + "'yes' to delete it, or 'no' to cancel: " % parameters['user']) + if autoclobber or confirm == 'yes': try: if verbosity >= 1: - self.log("Destroying old test user...") + self.log('Destroying old test user...') self._destroy_test_user(cursor, parameters, verbosity) if verbosity >= 1: - self.log("Creating test user...") - self._create_test_user( - cursor, parameters, verbosity, keepdb - ) + self.log('Creating test user...') + self._create_test_user(cursor, parameters, verbosity, keepdb) except Exception as e: - self.log("Got an error recreating the test user: %s" % e) + self.log('Got an error recreating the test user: %s' % e) sys.exit(2) else: - self.log("Tests cancelled.") + self.log('Tests cancelled.') sys.exit(1) - # Done with main user -- test user and tablespaces created. - self._maindb_connection.close() + self._maindb_connection.close() # done with main user -- test user and tablespaces created self._switch_to_test_user(parameters) - return self.connection.settings_dict["NAME"] + return self.connection.settings_dict['NAME'] def _switch_to_test_user(self, parameters): """ @@ -133,71 +109,59 @@ class DatabaseCreation(BaseDatabaseCreation): credentials in the SAVED_USER/SAVED_PASSWORD key in the settings dict. """ real_settings = settings.DATABASES[self.connection.alias] - real_settings["SAVED_USER"] = self.connection.settings_dict[ - "SAVED_USER" - ] = self.connection.settings_dict["USER"] - real_settings["SAVED_PASSWORD"] = self.connection.settings_dict[ - "SAVED_PASSWORD" - ] = self.connection.settings_dict["PASSWORD"] - real_test_settings = real_settings["TEST"] - test_settings = self.connection.settings_dict["TEST"] - real_test_settings["USER"] = real_settings["USER"] = test_settings[ - "USER" - ] = self.connection.settings_dict["USER"] = parameters["user"] - real_settings["PASSWORD"] = self.connection.settings_dict[ - "PASSWORD" - ] = parameters["password"] + real_settings['SAVED_USER'] = self.connection.settings_dict['SAVED_USER'] = \ + self.connection.settings_dict['USER'] + real_settings['SAVED_PASSWORD'] = self.connection.settings_dict['SAVED_PASSWORD'] = \ + self.connection.settings_dict['PASSWORD'] + real_test_settings = real_settings['TEST'] + test_settings = self.connection.settings_dict['TEST'] + real_test_settings['USER'] = real_settings['USER'] = test_settings['USER'] = \ + self.connection.settings_dict['USER'] = parameters['user'] + real_settings['PASSWORD'] = self.connection.settings_dict['PASSWORD'] = parameters['password'] def set_as_test_mirror(self, primary_settings_dict): """ Set this database up to be used in testing as a mirror of a primary database whose settings are given. """ - self.connection.settings_dict["USER"] = primary_settings_dict["USER"] - self.connection.settings_dict["PASSWORD"] = primary_settings_dict["PASSWORD"] + self.connection.settings_dict['USER'] = primary_settings_dict['USER'] + self.connection.settings_dict['PASSWORD'] = primary_settings_dict['PASSWORD'] - def _handle_objects_preventing_db_destruction( - self, cursor, parameters, verbosity, autoclobber - ): + def _handle_objects_preventing_db_destruction(self, cursor, parameters, verbosity, autoclobber): # There are objects in the test tablespace which prevent dropping it # The easy fix is to drop the test user -- but are we allowed to do so? self.log( - "There are objects in the old test database which prevent its destruction." - "\nIf they belong to the test user, deleting the user will allow the test " - "database to be recreated.\n" - "Otherwise, you will need to find and remove each of these objects, " - "or use a different tablespace.\n" + 'There are objects in the old test database which prevent its destruction.\n' + 'If they belong to the test user, deleting the user will allow the test ' + 'database to be recreated.\n' + 'Otherwise, you will need to find and remove each of these objects, ' + 'or use a different tablespace.\n' ) if self._test_user_create(): if not autoclobber: - confirm = input("Type 'yes' to delete user %s: " % parameters["user"]) - if autoclobber or confirm == "yes": + confirm = input("Type 'yes' to delete user %s: " % parameters['user']) + if autoclobber or confirm == 'yes': try: if verbosity >= 1: - self.log("Destroying old test user...") + self.log('Destroying old test user...') self._destroy_test_user(cursor, parameters, verbosity) except Exception as e: - self.log("Got an error destroying the test user: %s" % e) + self.log('Got an error destroying the test user: %s' % e) sys.exit(2) try: if verbosity >= 1: - self.log( - "Destroying old test database for alias '%s'..." - % self.connection.alias - ) + self.log("Destroying old test database for alias '%s'..." % self.connection.alias) self._execute_test_db_destruction(cursor, parameters, verbosity) except Exception as e: - self.log("Got an error destroying the test database: %s" % e) + self.log('Got an error destroying the test database: %s' % e) sys.exit(2) else: - self.log("Tests cancelled -- test database cannot be recreated.") + self.log('Tests cancelled -- test database cannot be recreated.') sys.exit(1) else: - self.log( - "Django is configured to use pre-existing test user '%s'," - " and will not attempt to delete it." % parameters["user"] - ) - self.log("Tests cancelled -- test database cannot be recreated.") + self.log("Django is configured to use pre-existing test user '%s'," + " and will not attempt to delete it." % parameters['user']) + self.log('Tests cancelled -- test database cannot be recreated.') sys.exit(1) def _destroy_test_db(self, test_database_name, verbosity=1): @@ -205,28 +169,24 @@ class DatabaseCreation(BaseDatabaseCreation): Destroy a test database, prompting the user for confirmation if the database already exists. Return the name of the test database created. """ - self.connection.settings_dict["USER"] = self.connection.settings_dict[ - "SAVED_USER" - ] - self.connection.settings_dict["PASSWORD"] = self.connection.settings_dict[ - "SAVED_PASSWORD" - ] + self.connection.settings_dict['USER'] = self.connection.settings_dict['SAVED_USER'] + self.connection.settings_dict['PASSWORD'] = self.connection.settings_dict['SAVED_PASSWORD'] self.connection.close() parameters = self._get_test_db_params() with self._maindb_connection.cursor() as cursor: if self._test_user_create(): if verbosity >= 1: - self.log("Destroying test user...") + self.log('Destroying test user...') self._destroy_test_user(cursor, parameters, verbosity) if self._test_database_create(): if verbosity >= 1: - self.log("Destroying test database tables...") + self.log('Destroying test database tables...') self._execute_test_db_destruction(cursor, parameters, verbosity) self._maindb_connection.close() def _execute_test_db_creation(self, cursor, parameters, verbosity, keepdb=False): if verbosity >= 2: - self.log("_create_test_db(): dbname = %s" % parameters["user"]) + self.log('_create_test_db(): dbname = %s' % parameters['user']) if self._test_database_oracle_managed_files(): statements = [ """ @@ -254,14 +214,12 @@ class DatabaseCreation(BaseDatabaseCreation): """, ] # Ignore "tablespace already exists" error when keepdb is on. - acceptable_ora_err = "ORA-01543" if keepdb else None - self._execute_allow_fail_statements( - cursor, statements, parameters, verbosity, acceptable_ora_err - ) + acceptable_ora_err = 'ORA-01543' if keepdb else None + self._execute_allow_fail_statements(cursor, statements, parameters, verbosity, acceptable_ora_err) def _create_test_user(self, cursor, parameters, verbosity, keepdb=False): if verbosity >= 2: - self.log("_create_test_user(): username = %s" % parameters["user"]) + self.log('_create_test_user(): username = %s' % parameters['user']) statements = [ """CREATE USER %(user)s IDENTIFIED BY "%(password)s" @@ -277,51 +235,40 @@ class DatabaseCreation(BaseDatabaseCreation): TO %(user)s""", ] # Ignore "user already exists" error when keepdb is on - acceptable_ora_err = "ORA-01920" if keepdb else None - success = self._execute_allow_fail_statements( - cursor, statements, parameters, verbosity, acceptable_ora_err - ) + acceptable_ora_err = 'ORA-01920' if keepdb else None + success = self._execute_allow_fail_statements(cursor, statements, parameters, verbosity, acceptable_ora_err) # If the password was randomly generated, change the user accordingly. - if not success and self._test_settings_get("PASSWORD") is None: + if not success and self._test_settings_get('PASSWORD') is None: set_password = 'ALTER USER %(user)s IDENTIFIED BY "%(password)s"' self._execute_statements(cursor, [set_password], parameters, verbosity) # Most test suites can be run without "create view" and # "create materialized view" privileges. But some need it. - for object_type in ("VIEW", "MATERIALIZED VIEW"): - extra = "GRANT CREATE %(object_type)s TO %(user)s" - parameters["object_type"] = object_type - success = self._execute_allow_fail_statements( - cursor, [extra], parameters, verbosity, "ORA-01031" - ) + for object_type in ('VIEW', 'MATERIALIZED VIEW'): + extra = 'GRANT CREATE %(object_type)s TO %(user)s' + parameters['object_type'] = object_type + success = self._execute_allow_fail_statements(cursor, [extra], parameters, verbosity, 'ORA-01031') if not success and verbosity >= 2: - self.log( - "Failed to grant CREATE %s permission to test user. This may be ok." - % object_type - ) + self.log('Failed to grant CREATE %s permission to test user. This may be ok.' % object_type) def _execute_test_db_destruction(self, cursor, parameters, verbosity): if verbosity >= 2: - self.log("_execute_test_db_destruction(): dbname=%s" % parameters["user"]) + self.log('_execute_test_db_destruction(): dbname=%s' % parameters['user']) statements = [ - "DROP TABLESPACE %(tblspace)s " - "INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS", - "DROP TABLESPACE %(tblspace_temp)s " - "INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS", + 'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', + 'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', ] self._execute_statements(cursor, statements, parameters, verbosity) def _destroy_test_user(self, cursor, parameters, verbosity): if verbosity >= 2: - self.log("_destroy_test_user(): user=%s" % parameters["user"]) - self.log("Be patient. This can take some time...") + self.log('_destroy_test_user(): user=%s' % parameters['user']) + self.log('Be patient. This can take some time...') statements = [ - "DROP USER %(user)s CASCADE", + 'DROP USER %(user)s CASCADE', ] self._execute_statements(cursor, statements, parameters, verbosity) - def _execute_statements( - self, cursor, statements, parameters, verbosity, allow_quiet_fail=False - ): + def _execute_statements(self, cursor, statements, parameters, verbosity, allow_quiet_fail=False): for template in statements: stmt = template % parameters if verbosity >= 2: @@ -330,12 +277,10 @@ class DatabaseCreation(BaseDatabaseCreation): cursor.execute(stmt) except Exception as err: if (not allow_quiet_fail) or verbosity >= 2: - self.log("Failed (%s)" % (err)) + self.log('Failed (%s)' % (err)) raise - def _execute_allow_fail_statements( - self, cursor, statements, parameters, verbosity, acceptable_ora_err - ): + def _execute_allow_fail_statements(self, cursor, statements, parameters, verbosity, acceptable_ora_err): """ Execute statements which are allowed to fail silently if the Oracle error code given by `acceptable_ora_err` is raised. Return True if the @@ -343,16 +288,8 @@ class DatabaseCreation(BaseDatabaseCreation): """ try: # Statement can fail when acceptable_ora_err is not None - allow_quiet_fail = ( - acceptable_ora_err is not None and len(acceptable_ora_err) > 0 - ) - self._execute_statements( - cursor, - statements, - parameters, - verbosity, - allow_quiet_fail=allow_quiet_fail, - ) + allow_quiet_fail = acceptable_ora_err is not None and len(acceptable_ora_err) > 0 + self._execute_statements(cursor, statements, parameters, verbosity, allow_quiet_fail=allow_quiet_fail) return True except DatabaseError as err: description = str(err) @@ -362,19 +299,19 @@ class DatabaseCreation(BaseDatabaseCreation): def _get_test_db_params(self): return { - "dbname": self._test_database_name(), - "user": self._test_database_user(), - "password": self._test_database_passwd(), - "tblspace": self._test_database_tblspace(), - "tblspace_temp": self._test_database_tblspace_tmp(), - "datafile": self._test_database_tblspace_datafile(), - "datafile_tmp": self._test_database_tblspace_tmp_datafile(), - "maxsize": self._test_database_tblspace_maxsize(), - "maxsize_tmp": self._test_database_tblspace_tmp_maxsize(), - "size": self._test_database_tblspace_size(), - "size_tmp": self._test_database_tblspace_tmp_size(), - "extsize": self._test_database_tblspace_extsize(), - "extsize_tmp": self._test_database_tblspace_tmp_extsize(), + 'dbname': self._test_database_name(), + 'user': self._test_database_user(), + 'password': self._test_database_passwd(), + 'tblspace': self._test_database_tblspace(), + 'tblspace_temp': self._test_database_tblspace_tmp(), + 'datafile': self._test_database_tblspace_datafile(), + 'datafile_tmp': self._test_database_tblspace_tmp_datafile(), + 'maxsize': self._test_database_tblspace_maxsize(), + 'maxsize_tmp': self._test_database_tblspace_tmp_maxsize(), + 'size': self._test_database_tblspace_size(), + 'size_tmp': self._test_database_tblspace_tmp_size(), + 'extsize': self._test_database_tblspace_extsize(), + 'extsize_tmp': self._test_database_tblspace_tmp_extsize(), } def _test_settings_get(self, key, default=None, prefixed=None): @@ -383,67 +320,66 @@ class DatabaseCreation(BaseDatabaseCreation): prefixed entry from the main settings dict. """ settings_dict = self.connection.settings_dict - val = settings_dict["TEST"].get(key, default) + val = settings_dict['TEST'].get(key, default) if val is None and prefixed: val = TEST_DATABASE_PREFIX + settings_dict[prefixed] return val def _test_database_name(self): - return self._test_settings_get("NAME", prefixed="NAME") + return self._test_settings_get('NAME', prefixed='NAME') def _test_database_create(self): - return self._test_settings_get("CREATE_DB", default=True) + return self._test_settings_get('CREATE_DB', default=True) def _test_user_create(self): - return self._test_settings_get("CREATE_USER", default=True) + return self._test_settings_get('CREATE_USER', default=True) def _test_database_user(self): - return self._test_settings_get("USER", prefixed="USER") + return self._test_settings_get('USER', prefixed='USER') def _test_database_passwd(self): - password = self._test_settings_get("PASSWORD") + password = self._test_settings_get('PASSWORD') if password is None and self._test_user_create(): # Oracle passwords are limited to 30 chars and can't contain symbols. password = get_random_string(30) return password def _test_database_tblspace(self): - return self._test_settings_get("TBLSPACE", prefixed="USER") + return self._test_settings_get('TBLSPACE', prefixed='USER') def _test_database_tblspace_tmp(self): settings_dict = self.connection.settings_dict - return settings_dict["TEST"].get( - "TBLSPACE_TMP", TEST_DATABASE_PREFIX + settings_dict["USER"] + "_temp" - ) + return settings_dict['TEST'].get('TBLSPACE_TMP', + TEST_DATABASE_PREFIX + settings_dict['USER'] + '_temp') def _test_database_tblspace_datafile(self): - tblspace = "%s.dbf" % self._test_database_tblspace() - return self._test_settings_get("DATAFILE", default=tblspace) + tblspace = '%s.dbf' % self._test_database_tblspace() + return self._test_settings_get('DATAFILE', default=tblspace) def _test_database_tblspace_tmp_datafile(self): - tblspace = "%s.dbf" % self._test_database_tblspace_tmp() - return self._test_settings_get("DATAFILE_TMP", default=tblspace) + tblspace = '%s.dbf' % self._test_database_tblspace_tmp() + return self._test_settings_get('DATAFILE_TMP', default=tblspace) def _test_database_tblspace_maxsize(self): - return self._test_settings_get("DATAFILE_MAXSIZE", default="500M") + return self._test_settings_get('DATAFILE_MAXSIZE', default='500M') def _test_database_tblspace_tmp_maxsize(self): - return self._test_settings_get("DATAFILE_TMP_MAXSIZE", default="500M") + return self._test_settings_get('DATAFILE_TMP_MAXSIZE', default='500M') def _test_database_tblspace_size(self): - return self._test_settings_get("DATAFILE_SIZE", default="50M") + return self._test_settings_get('DATAFILE_SIZE', default='50M') def _test_database_tblspace_tmp_size(self): - return self._test_settings_get("DATAFILE_TMP_SIZE", default="50M") + return self._test_settings_get('DATAFILE_TMP_SIZE', default='50M') def _test_database_tblspace_extsize(self): - return self._test_settings_get("DATAFILE_EXTSIZE", default="25M") + return self._test_settings_get('DATAFILE_EXTSIZE', default='25M') def _test_database_tblspace_tmp_extsize(self): - return self._test_settings_get("DATAFILE_TMP_EXTSIZE", default="25M") + return self._test_settings_get('DATAFILE_TMP_EXTSIZE', default='25M') def _test_database_oracle_managed_files(self): - return self._test_settings_get("ORACLE_MANAGED_FILES", default=False) + return self._test_settings_get('ORACLE_MANAGED_FILES', default=False) def _get_test_db_name(self): """ @@ -451,14 +387,14 @@ class DatabaseCreation(BaseDatabaseCreation): to work. This isn't a great deal in this case because DB names as handled by Django don't have real counterparts in Oracle. """ - return self.connection.settings_dict["NAME"] + return self.connection.settings_dict['NAME'] def test_db_signature(self): settings_dict = self.connection.settings_dict return ( - settings_dict["HOST"], - settings_dict["PORT"], - settings_dict["ENGINE"], - settings_dict["NAME"], + settings_dict['HOST'], + settings_dict['PORT'], + settings_dict['ENGINE'], + settings_dict['NAME'], self._test_database_user(), ) diff --git a/venv/Lib/site-packages/django/db/backends/oracle/features.py b/venv/Lib/site-packages/django/db/backends/oracle/features.py index debbc8b..2570675 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/features.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/features.py @@ -15,7 +15,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): select_for_update_of_column = True can_return_columns_from_insert = True supports_subqueries_in_group_by = False - ignores_unnecessary_order_by_in_subqueries = False supports_transactions = True supports_timezones = False has_native_duration_field = True @@ -32,8 +31,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): requires_literal_defaults = True closed_cursor_error_class = InterfaceError bare_select_suffix = " FROM DUAL" - # select for update with limit can be achieved on Oracle, but not with the - # current backend. + # select for update with limit can be achieved on Oracle, but not with the current backend. supports_select_for_update_with_limit = False supports_temporal_subtraction = True # Oracle doesn't ignore quoted identifiers case but the current backend @@ -68,46 +66,44 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_json_field_contains = False supports_collation_on_textfield = False test_collations = { - "ci": "BINARY_CI", - "cs": "BINARY", - "non_default": "SWEDISH_CI", - "swedish_ci": "SWEDISH_CI", + 'ci': 'BINARY_CI', + 'cs': 'BINARY', + 'non_default': 'SWEDISH_CI', + 'swedish_ci': 'SWEDISH_CI', } - test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'" django_test_skips = { "Oracle doesn't support SHA224.": { - "db_functions.text.test_sha224.SHA224Tests.test_basic", - "db_functions.text.test_sha224.SHA224Tests.test_transform", + 'db_functions.text.test_sha224.SHA224Tests.test_basic', + 'db_functions.text.test_sha224.SHA224Tests.test_transform', }, "Oracle doesn't support bitwise XOR.": { - "expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor", - "expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor_null", + 'expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor', + 'expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor_null', }, "Oracle requires ORDER BY in row_number, ANSI:SQL doesn't.": { - "expressions_window.tests.WindowFunctionTests.test_row_number_no_ordering", + 'expressions_window.tests.WindowFunctionTests.test_row_number_no_ordering', }, - "Raises ORA-00600: internal error code.": { - "model_fields.test_jsonfield.TestQuerying.test_usage_in_subquery", + 'Raises ORA-00600: internal error code on Oracle 18.': { + 'model_fields.test_jsonfield.TestQuerying.test_usage_in_subquery', }, } django_test_expected_failures = { # A bug in Django/cx_Oracle with respect to string handling (#23843). - "annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions", - "annotations.tests.NonAggregateAnnotationTestCase." - "test_custom_functions_can_ref_other_functions", + 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions', + 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions_can_ref_other_functions', } @cached_property def introspected_field_types(self): return { **super().introspected_field_types, - "GenericIPAddressField": "CharField", - "PositiveBigIntegerField": "BigIntegerField", - "PositiveIntegerField": "IntegerField", - "PositiveSmallIntegerField": "IntegerField", - "SmallIntegerField": "IntegerField", - "TimeField": "DateTimeField", + 'GenericIPAddressField': 'CharField', + 'PositiveBigIntegerField': 'BigIntegerField', + 'PositiveIntegerField': 'IntegerField', + 'PositiveSmallIntegerField': 'IntegerField', + 'SmallIntegerField': 'IntegerField', + 'TimeField': 'DateTimeField', } @cached_property @@ -120,3 +116,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): return False raise return True + + @cached_property + def has_json_object_function(self): + # Oracle < 18 supports JSON_OBJECT() but it's not fully functional. + return self.connection.oracle_version >= (18,) diff --git a/venv/Lib/site-packages/django/db/backends/oracle/functions.py b/venv/Lib/site-packages/django/db/backends/oracle/functions.py index 936cc9e..1aeb459 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/functions.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/functions.py @@ -2,7 +2,7 @@ from django.db.models import DecimalField, DurationField, Func class IntervalToSeconds(Func): - function = "" + function = '' template = """ EXTRACT(day from %(expressions)s) * 86400 + EXTRACT(hour from %(expressions)s) * 3600 + @@ -11,16 +11,12 @@ class IntervalToSeconds(Func): """ def __init__(self, expression, *, output_field=None, **extra): - super().__init__( - expression, output_field=output_field or DecimalField(), **extra - ) + super().__init__(expression, output_field=output_field or DecimalField(), **extra) class SecondsToInterval(Func): - function = "NUMTODSINTERVAL" + function = 'NUMTODSINTERVAL' template = "%(function)s(%(expressions)s, 'SECOND')" def __init__(self, expression, *, output_field=None, **extra): - super().__init__( - expression, output_field=output_field or DurationField(), **extra - ) + super().__init__(expression, output_field=output_field or DurationField(), **extra) diff --git a/venv/Lib/site-packages/django/db/backends/oracle/introspection.py b/venv/Lib/site-packages/django/db/backends/oracle/introspection.py index adc0bc4..a5df7f1 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/introspection.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/introspection.py @@ -3,12 +3,12 @@ from collections import namedtuple import cx_Oracle from django.db import models -from django.db.backends.base.introspection import BaseDatabaseIntrospection -from django.db.backends.base.introspection import FieldInfo as BaseFieldInfo -from django.db.backends.base.introspection import TableInfo +from django.db.backends.base.introspection import ( + BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo, +) from django.utils.functional import cached_property -FieldInfo = namedtuple("FieldInfo", BaseFieldInfo._fields + ("is_autofield", "is_json")) +FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('is_autofield', 'is_json')) class DatabaseIntrospection(BaseDatabaseIntrospection): @@ -19,33 +19,33 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): def data_types_reverse(self): if self.connection.cx_oracle_version < (8,): return { - cx_Oracle.BLOB: "BinaryField", - cx_Oracle.CLOB: "TextField", - cx_Oracle.DATETIME: "DateField", - cx_Oracle.FIXED_CHAR: "CharField", - cx_Oracle.FIXED_NCHAR: "CharField", - cx_Oracle.INTERVAL: "DurationField", - cx_Oracle.NATIVE_FLOAT: "FloatField", - cx_Oracle.NCHAR: "CharField", - cx_Oracle.NCLOB: "TextField", - cx_Oracle.NUMBER: "DecimalField", - cx_Oracle.STRING: "CharField", - cx_Oracle.TIMESTAMP: "DateTimeField", + cx_Oracle.BLOB: 'BinaryField', + cx_Oracle.CLOB: 'TextField', + cx_Oracle.DATETIME: 'DateField', + cx_Oracle.FIXED_CHAR: 'CharField', + cx_Oracle.FIXED_NCHAR: 'CharField', + cx_Oracle.INTERVAL: 'DurationField', + cx_Oracle.NATIVE_FLOAT: 'FloatField', + cx_Oracle.NCHAR: 'CharField', + cx_Oracle.NCLOB: 'TextField', + cx_Oracle.NUMBER: 'DecimalField', + cx_Oracle.STRING: 'CharField', + cx_Oracle.TIMESTAMP: 'DateTimeField', } else: return { - cx_Oracle.DB_TYPE_DATE: "DateField", - cx_Oracle.DB_TYPE_BINARY_DOUBLE: "FloatField", - cx_Oracle.DB_TYPE_BLOB: "BinaryField", - cx_Oracle.DB_TYPE_CHAR: "CharField", - cx_Oracle.DB_TYPE_CLOB: "TextField", - cx_Oracle.DB_TYPE_INTERVAL_DS: "DurationField", - cx_Oracle.DB_TYPE_NCHAR: "CharField", - cx_Oracle.DB_TYPE_NCLOB: "TextField", - cx_Oracle.DB_TYPE_NVARCHAR: "CharField", - cx_Oracle.DB_TYPE_NUMBER: "DecimalField", - cx_Oracle.DB_TYPE_TIMESTAMP: "DateTimeField", - cx_Oracle.DB_TYPE_VARCHAR: "CharField", + cx_Oracle.DB_TYPE_DATE: 'DateField', + cx_Oracle.DB_TYPE_BINARY_DOUBLE: 'FloatField', + cx_Oracle.DB_TYPE_BLOB: 'BinaryField', + cx_Oracle.DB_TYPE_CHAR: 'CharField', + cx_Oracle.DB_TYPE_CLOB: 'TextField', + cx_Oracle.DB_TYPE_INTERVAL_DS: 'DurationField', + cx_Oracle.DB_TYPE_NCHAR: 'CharField', + cx_Oracle.DB_TYPE_NCLOB: 'TextField', + cx_Oracle.DB_TYPE_NVARCHAR: 'CharField', + cx_Oracle.DB_TYPE_NUMBER: 'DecimalField', + cx_Oracle.DB_TYPE_TIMESTAMP: 'DateTimeField', + cx_Oracle.DB_TYPE_VARCHAR: 'CharField', } def get_field_type(self, data_type, description): @@ -53,30 +53,25 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): precision, scale = description[4:6] if scale == 0: if precision > 11: - return ( - "BigAutoField" - if description.is_autofield - else "BigIntegerField" - ) + return 'BigAutoField' if description.is_autofield else 'BigIntegerField' elif 1 < precision < 6 and description.is_autofield: - return "SmallAutoField" + return 'SmallAutoField' elif precision == 1: - return "BooleanField" + return 'BooleanField' elif description.is_autofield: - return "AutoField" + return 'AutoField' else: - return "IntegerField" + return 'IntegerField' elif scale == -127: - return "FloatField" + return 'FloatField' elif data_type == cx_Oracle.NCLOB and description.is_json: - return "JSONField" + return 'JSONField' return super().get_field_type(data_type, description) def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" - cursor.execute( - """ + cursor.execute(""" SELECT table_name, 't' FROM user_tables WHERE @@ -89,12 +84,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): SELECT view_name, 'v' FROM user_views UNION ALL SELECT mview_name, 'v' FROM user_mviews - """ - ) - return [ - TableInfo(self.identifier_converter(row[0]), row[1]) - for row in cursor.fetchall() - ] + """) + return [TableInfo(self.identifier_converter(row[0]), row[1]) for row in cursor.fetchall()] def get_table_description(self, cursor, table_name): """ @@ -102,8 +93,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): interface. """ # user_tab_columns gives data default for columns - cursor.execute( - """ + cursor.execute(""" SELECT user_tab_cols.column_name, user_tab_cols.data_default, @@ -136,51 +126,24 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): LEFT OUTER JOIN user_tables ON user_tables.table_name = user_tab_cols.table_name WHERE user_tab_cols.table_name = UPPER(%s) - """, - [table_name], - ) + """, [table_name]) field_map = { - column: ( - internal_size, - default if default != "NULL" else None, - collation, - is_autofield, - is_json, - ) - for ( - column, - default, - collation, - internal_size, - is_autofield, - is_json, - ) in cursor.fetchall() + column: (internal_size, default if default != 'NULL' else None, collation, is_autofield, is_json) + for column, default, collation, internal_size, is_autofield, is_json in cursor.fetchall() } self.cache_bust_counter += 1 - cursor.execute( - "SELECT * FROM {} WHERE ROWNUM < 2 AND {} > 0".format( - self.connection.ops.quote_name(table_name), self.cache_bust_counter - ) - ) + cursor.execute("SELECT * FROM {} WHERE ROWNUM < 2 AND {} > 0".format( + self.connection.ops.quote_name(table_name), + self.cache_bust_counter)) description = [] for desc in cursor.description: name = desc[0] internal_size, default, collation, is_autofield, is_json = field_map[name] name = name % {} # cx_Oracle, for some reason, doubles percent signs. - description.append( - FieldInfo( - self.identifier_converter(name), - *desc[1:3], - internal_size, - desc[4] or 0, - desc[5] or 0, - *desc[6:], - default, - collation, - is_autofield, - is_json, - ) - ) + description.append(FieldInfo( + self.identifier_converter(name), *desc[1:3], internal_size, desc[4] or 0, + desc[5] or 0, *desc[6:], default, collation, is_autofield, is_json, + )) return description def identifier_converter(self, name): @@ -188,8 +151,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): return name.lower() def get_sequences(self, cursor, table_name, table_fields=()): - cursor.execute( - """ + cursor.execute(""" SELECT user_tab_identity_cols.sequence_name, user_tab_identity_cols.column_name @@ -203,24 +165,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): AND cols.column_name = user_tab_identity_cols.column_name AND user_constraints.constraint_type = 'P' AND user_tab_identity_cols.table_name = UPPER(%s) - """, - [table_name], - ) + """, [table_name]) # Oracle allows only one identity column per table. row = cursor.fetchone() if row: - return [ - { - "name": self.identifier_converter(row[0]), - "table": self.identifier_converter(table_name), - "column": self.identifier_converter(row[1]), - } - ] + return [{ + 'name': self.identifier_converter(row[0]), + 'table': self.identifier_converter(table_name), + 'column': self.identifier_converter(row[1]), + }] # To keep backward compatibility for AutoFields that aren't Oracle # identity columns. for f in table_fields: if isinstance(f, models.AutoField): - return [{"table": table_name, "column": f.column}] + return [{'table': table_name, 'column': f.column}] return [] def get_relations(self, cursor, table_name): @@ -229,48 +187,37 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): representing all relationships to the given table. """ table_name = table_name.upper() - cursor.execute( - """ + cursor.execute(""" SELECT ca.column_name, cb.table_name, cb.column_name FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb WHERE user_constraints.table_name = %s AND user_constraints.constraint_name = ca.constraint_name AND user_constraints.r_constraint_name = cb.constraint_name AND - ca.position = cb.position""", - [table_name], - ) + ca.position = cb.position""", [table_name]) return { self.identifier_converter(field_name): ( self.identifier_converter(rel_field_name), self.identifier_converter(rel_table_name), - ) - for field_name, rel_table_name, rel_field_name in cursor.fetchall() + ) for field_name, rel_table_name, rel_field_name in cursor.fetchall() } def get_key_columns(self, cursor, table_name): - cursor.execute( - """ - SELECT - ccol.column_name, - rcol.table_name AS referenced_table, - rcol.column_name AS referenced_column + cursor.execute(""" + SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column FROM user_constraints c JOIN user_cons_columns ccol ON ccol.constraint_name = c.constraint_name JOIN user_cons_columns rcol ON rcol.constraint_name = c.r_constraint_name - WHERE c.table_name = %s AND c.constraint_type = 'R'""", - [table_name.upper()], - ) + WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()]) return [ tuple(self.identifier_converter(cell) for cell in row) for row in cursor.fetchall() ] def get_primary_key_column(self, cursor, table_name): - cursor.execute( - """ + cursor.execute(""" SELECT cols.column_name FROM @@ -281,9 +228,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): user_constraints.constraint_type = 'P' AND user_constraints.table_name = UPPER(%s) AND cols.position = 1 - """, - [table_name], - ) + """, [table_name]) row = cursor.fetchone() return self.identifier_converter(row[0]) if row else None @@ -294,12 +239,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): """ constraints = {} # Loop over the constraints, getting PKs, uniques, and checks - cursor.execute( - """ + cursor.execute(""" SELECT user_constraints.constraint_name, - LISTAGG(LOWER(cols.column_name), ',') - WITHIN GROUP (ORDER BY cols.position), + LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.position), CASE user_constraints.constraint_type WHEN 'P' THEN 1 ELSE 0 @@ -315,68 +258,56 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): FROM user_constraints LEFT OUTER JOIN - user_cons_columns cols - ON user_constraints.constraint_name = cols.constraint_name + user_cons_columns cols ON user_constraints.constraint_name = cols.constraint_name WHERE user_constraints.constraint_type = ANY('P', 'U', 'C') AND user_constraints.table_name = UPPER(%s) GROUP BY user_constraints.constraint_name, user_constraints.constraint_type - """, - [table_name], - ) + """, [table_name]) for constraint, columns, pk, unique, check in cursor.fetchall(): constraint = self.identifier_converter(constraint) constraints[constraint] = { - "columns": columns.split(","), - "primary_key": pk, - "unique": unique, - "foreign_key": None, - "check": check, - "index": unique, # All uniques come with an index + 'columns': columns.split(','), + 'primary_key': pk, + 'unique': unique, + 'foreign_key': None, + 'check': check, + 'index': unique, # All uniques come with an index } # Foreign key constraints - cursor.execute( - """ + cursor.execute(""" SELECT cons.constraint_name, - LISTAGG(LOWER(cols.column_name), ',') - WITHIN GROUP (ORDER BY cols.position), + LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.position), LOWER(rcols.table_name), LOWER(rcols.column_name) FROM user_constraints cons INNER JOIN - user_cons_columns rcols - ON rcols.constraint_name = cons.r_constraint_name AND rcols.position = 1 + user_cons_columns rcols ON rcols.constraint_name = cons.r_constraint_name AND rcols.position = 1 LEFT OUTER JOIN - user_cons_columns cols - ON cons.constraint_name = cols.constraint_name + user_cons_columns cols ON cons.constraint_name = cols.constraint_name WHERE cons.constraint_type = 'R' AND cons.table_name = UPPER(%s) GROUP BY cons.constraint_name, rcols.table_name, rcols.column_name - """, - [table_name], - ) + """, [table_name]) for constraint, columns, other_table, other_column in cursor.fetchall(): constraint = self.identifier_converter(constraint) constraints[constraint] = { - "primary_key": False, - "unique": False, - "foreign_key": (other_table, other_column), - "check": False, - "index": False, - "columns": columns.split(","), + 'primary_key': False, + 'unique': False, + 'foreign_key': (other_table, other_column), + 'check': False, + 'index': False, + 'columns': columns.split(','), } # Now get indexes - cursor.execute( - """ + cursor.execute(""" SELECT ind.index_name, LOWER(ind.index_type), - LOWER(ind.uniqueness), - LISTAGG(LOWER(cols.column_name), ',') - WITHIN GROUP (ORDER BY cols.column_position), + LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.column_position), LISTAGG(cols.descend, ',') WITHIN GROUP (ORDER BY cols.column_position) FROM user_ind_columns cols, user_indexes ind @@ -387,20 +318,18 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): FROM user_constraints cons WHERE ind.index_name = cons.index_name ) AND cols.index_name = ind.index_name - GROUP BY ind.index_name, ind.index_type, ind.uniqueness - """, - [table_name], - ) - for constraint, type_, unique, columns, orders in cursor.fetchall(): + GROUP BY ind.index_name, ind.index_type + """, [table_name]) + for constraint, type_, columns, orders in cursor.fetchall(): constraint = self.identifier_converter(constraint) constraints[constraint] = { - "primary_key": False, - "unique": unique == "unique", - "foreign_key": None, - "check": False, - "index": True, - "type": "idx" if type_ == "normal" else type_, - "columns": columns.split(","), - "orders": orders.split(","), + 'primary_key': False, + 'unique': False, + 'foreign_key': None, + 'check': False, + 'index': True, + 'type': 'idx' if type_ == 'normal' else type_, + 'columns': columns.split(','), + 'orders': orders.split(','), } return constraints diff --git a/venv/Lib/site-packages/django/db/backends/oracle/operations.py b/venv/Lib/site-packages/django/db/backends/oracle/operations.py index d7f6c70..b829d2b 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/operations.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/operations.py @@ -5,8 +5,8 @@ from functools import lru_cache from django.conf import settings from django.db import DatabaseError, NotSupportedError from django.db.backends.base.operations import BaseDatabaseOperations -from django.db.backends.utils import split_tzname_delta, strip_quotes, truncate_name -from django.db.models import AutoField, Exists, ExpressionWrapper, Lookup +from django.db.backends.utils import strip_quotes, truncate_name +from django.db.models import AutoField, Exists, ExpressionWrapper from django.db.models.expressions import RawSQL from django.db.models.sql.where import WhereNode from django.utils import timezone @@ -23,17 +23,17 @@ class DatabaseOperations(BaseDatabaseOperations): # SmallIntegerField uses NUMBER(11) instead of NUMBER(5), which is used by # SmallAutoField, to preserve backward compatibility. integer_field_ranges = { - "SmallIntegerField": (-99999999999, 99999999999), - "IntegerField": (-99999999999, 99999999999), - "BigIntegerField": (-9999999999999999999, 9999999999999999999), - "PositiveBigIntegerField": (0, 9999999999999999999), - "PositiveSmallIntegerField": (0, 99999999999), - "PositiveIntegerField": (0, 99999999999), - "SmallAutoField": (-99999, 99999), - "AutoField": (-99999999999, 99999999999), - "BigAutoField": (-9999999999999999999, 9999999999999999999), + 'SmallIntegerField': (-99999999999, 99999999999), + 'IntegerField': (-99999999999, 99999999999), + 'BigIntegerField': (-9999999999999999999, 9999999999999999999), + 'PositiveBigIntegerField': (0, 9999999999999999999), + 'PositiveSmallIntegerField': (0, 99999999999), + 'PositiveIntegerField': (0, 99999999999), + 'SmallAutoField': (-99999, 99999), + 'AutoField': (-99999999999, 99999999999), + 'BigAutoField': (-9999999999999999999, 9999999999999999999), } - set_operators = {**BaseDatabaseOperations.set_operators, "difference": "MINUS"} + set_operators = {**BaseDatabaseOperations.set_operators, 'difference': 'MINUS'} # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. _sequence_reset_sql = """ @@ -61,45 +61,42 @@ END; /""" # Oracle doesn't support string without precision; use the max string size. - cast_char_field_without_max_length = "NVARCHAR2(2000)" + cast_char_field_without_max_length = 'NVARCHAR2(2000)' cast_data_types = { - "AutoField": "NUMBER(11)", - "BigAutoField": "NUMBER(19)", - "SmallAutoField": "NUMBER(5)", - "TextField": cast_char_field_without_max_length, + 'AutoField': 'NUMBER(11)', + 'BigAutoField': 'NUMBER(19)', + 'SmallAutoField': 'NUMBER(5)', + 'TextField': cast_char_field_without_max_length, } def cache_key_culling_sql(self): - return ( - "SELECT cache_key FROM %s " - "ORDER BY cache_key OFFSET %%s ROWS FETCH FIRST 1 ROWS ONLY" - ) + return 'SELECT cache_key FROM %s ORDER BY cache_key OFFSET %%s ROWS FETCH FIRST 1 ROWS ONLY' def date_extract_sql(self, lookup_type, field_name): - if lookup_type == "week_day": + if lookup_type == 'week_day': # TO_CHAR(field, 'D') returns an integer from 1-7, where 1=Sunday. return "TO_CHAR(%s, 'D')" % field_name - elif lookup_type == "iso_week_day": + elif lookup_type == 'iso_week_day': return "TO_CHAR(%s - 1, 'D')" % field_name - elif lookup_type == "week": + elif lookup_type == 'week': # IW = ISO week number return "TO_CHAR(%s, 'IW')" % field_name - elif lookup_type == "quarter": + elif lookup_type == 'quarter': return "TO_CHAR(%s, 'Q')" % field_name - elif lookup_type == "iso_year": + elif lookup_type == 'iso_year': return "TO_CHAR(%s, 'IYYY')" % field_name else: - # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/EXTRACT-datetime.html + # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/EXTRACT-datetime.html return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name) def date_trunc_sql(self, lookup_type, field_name, tzname=None): field_name = self._convert_field_to_tz(field_name, tzname) - # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ROUND-and-TRUNC-Date-Functions.html - if lookup_type in ("year", "month"): + # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/ROUND-and-TRUNC-Date-Functions.html + if lookup_type in ('year', 'month'): return "TRUNC(%s, '%s')" % (field_name, lookup_type.upper()) - elif lookup_type == "quarter": + elif lookup_type == 'quarter': return "TRUNC(%s, 'Q')" % field_name - elif lookup_type == "week": + elif lookup_type == 'week': return "TRUNC(%s, 'IW')" % field_name else: return "TRUNC(%s)" % field_name @@ -108,11 +105,14 @@ END; # if the time zone name is passed in parameter. Use interpolation instead. # https://groups.google.com/forum/#!msg/django-developers/zwQju7hbG78/9l934yelwfsJ # This regexp matches all time zone names from the zoneinfo database. - _tzname_re = _lazy_re_compile(r"^[\w/:+-]+$") + _tzname_re = _lazy_re_compile(r'^[\w/:+-]+$') def _prepare_tzname_delta(self, tzname): - tzname, sign, offset = split_tzname_delta(tzname) - return f"{sign}{offset}" if offset else tzname + if '+' in tzname: + return tzname[tzname.find('+'):] + elif '-' in tzname: + return tzname[tzname.find('-'):] + return tzname def _convert_field_to_tz(self, field_name, tzname): if not (settings.USE_TZ and tzname): @@ -132,19 +132,12 @@ END; def datetime_cast_date_sql(self, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - return "TRUNC(%s)" % field_name + return 'TRUNC(%s)' % field_name def datetime_cast_time_sql(self, field_name, tzname): - # Since `TimeField` values are stored as TIMESTAMP change to the - # default date and convert the field to the specified timezone. - convert_datetime_sql = ( - "TO_TIMESTAMP(CONCAT('1900-01-01 ', TO_CHAR(%s, 'HH24:MI:SS.FF')), " - "'YYYY-MM-DD HH24:MI:SS.FF')" - ) % self._convert_field_to_tz(field_name, tzname) - return "CASE WHEN %s IS NOT NULL THEN %s ELSE NULL END" % ( - field_name, - convert_datetime_sql, - ) + # Since `TimeField` values are stored as TIMESTAMP where only the date + # part is ignored, convert the field to the specified timezone. + return self._convert_field_to_tz(field_name, tzname) def datetime_extract_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) @@ -152,23 +145,21 @@ END; def datetime_trunc_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ROUND-and-TRUNC-Date-Functions.html - if lookup_type in ("year", "month"): + # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/ROUND-and-TRUNC-Date-Functions.html + if lookup_type in ('year', 'month'): sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper()) - elif lookup_type == "quarter": + elif lookup_type == 'quarter': sql = "TRUNC(%s, 'Q')" % field_name - elif lookup_type == "week": + elif lookup_type == 'week': sql = "TRUNC(%s, 'IW')" % field_name - elif lookup_type == "day": + elif lookup_type == 'day': sql = "TRUNC(%s)" % field_name - elif lookup_type == "hour": + elif lookup_type == 'hour': sql = "TRUNC(%s, 'HH24')" % field_name - elif lookup_type == "minute": + elif lookup_type == 'minute': sql = "TRUNC(%s, 'MI')" % field_name else: - sql = ( - "CAST(%s AS DATE)" % field_name - ) # Cast to DATE removes sub-second precision. + sql = "CAST(%s AS DATE)" % field_name # Cast to DATE removes sub-second precision. return sql def time_trunc_sql(self, lookup_type, field_name, tzname=None): @@ -176,42 +167,40 @@ END; # `DateTimeField` and `TimeField` are stored as TIMESTAMP where # the date part of the later is ignored. field_name = self._convert_field_to_tz(field_name, tzname) - if lookup_type == "hour": + if lookup_type == 'hour': sql = "TRUNC(%s, 'HH24')" % field_name - elif lookup_type == "minute": + elif lookup_type == 'minute': sql = "TRUNC(%s, 'MI')" % field_name - elif lookup_type == "second": - sql = ( - "CAST(%s AS DATE)" % field_name - ) # Cast to DATE removes sub-second precision. + elif lookup_type == 'second': + sql = "CAST(%s AS DATE)" % field_name # Cast to DATE removes sub-second precision. return sql def get_db_converters(self, expression): converters = super().get_db_converters(expression) internal_type = expression.output_field.get_internal_type() - if internal_type in ["JSONField", "TextField"]: + if internal_type in ['JSONField', 'TextField']: converters.append(self.convert_textfield_value) - elif internal_type == "BinaryField": + elif internal_type == 'BinaryField': converters.append(self.convert_binaryfield_value) - elif internal_type == "BooleanField": + elif internal_type in ['BooleanField', 'NullBooleanField']: converters.append(self.convert_booleanfield_value) - elif internal_type == "DateTimeField": + elif internal_type == 'DateTimeField': if settings.USE_TZ: converters.append(self.convert_datetimefield_value) - elif internal_type == "DateField": + elif internal_type == 'DateField': converters.append(self.convert_datefield_value) - elif internal_type == "TimeField": + elif internal_type == 'TimeField': converters.append(self.convert_timefield_value) - elif internal_type == "UUIDField": + elif internal_type == 'UUIDField': converters.append(self.convert_uuidfield_value) # Oracle stores empty strings as null. If the field accepts the empty # string, undo this to adhere to the Django convention of using # the empty string instead of null. - if expression.output_field.empty_strings_allowed: + if expression.field.empty_strings_allowed: converters.append( self.convert_empty_bytes - if internal_type == "BinaryField" - else self.convert_empty_string + if internal_type == 'BinaryField' else + self.convert_empty_string ) return converters @@ -256,11 +245,11 @@ END; @staticmethod def convert_empty_string(value, expression, connection): - return "" if value is None else value + return '' if value is None else value @staticmethod def convert_empty_bytes(value, expression, connection): - return b"" if value is None else value + return b'' if value is None else value def deferrable_sql(self): return " DEFERRABLE INITIALLY DEFERRED" @@ -269,18 +258,20 @@ END; columns = [] for param in returning_params: value = param.get_value() - if value == []: + if value is None or value == []: + # cx_Oracle < 6.3 returns None, >= 6.3 returns empty list. raise DatabaseError( - "The database did not return a new row id. Probably " + 'The database did not return a new row id. Probably ' '"ORA-1403: no data found" was raised internally but was ' - "hidden by the Oracle OCI library (see " - "https://code.djangoproject.com/ticket/28859)." + 'hidden by the Oracle OCI library (see ' + 'https://code.djangoproject.com/ticket/28859).' ) - columns.append(value[0]) + # cx_Oracle < 7 returns value, >= 7 returns list with single value. + columns.append(value[0] if isinstance(value, list) else value) return tuple(columns) def field_cast_sql(self, db_type, internal_type): - if db_type and db_type.endswith("LOB") and internal_type != "JSONField": + if db_type and db_type.endswith('LOB') and internal_type != 'JSONField': return "DBMS_LOB.SUBSTR(%s)" else: return "%s" @@ -290,14 +281,10 @@ END; def limit_offset_sql(self, low_mark, high_mark): fetch, offset = self._get_limit_offset_params(low_mark, high_mark) - return " ".join( - sql - for sql in ( - ("OFFSET %d ROWS" % offset) if offset else None, - ("FETCH FIRST %d ROWS ONLY" % fetch) if fetch else None, - ) - if sql - ) + return ' '.join(sql for sql in ( + ('OFFSET %d ROWS' % offset) if offset else None, + ('FETCH FIRST %d ROWS ONLY' % fetch) if fetch else None, + ) if sql) def last_executed_query(self, cursor, sql, params): # https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement @@ -308,14 +295,10 @@ END; # parameters manually. if isinstance(params, (tuple, list)): for i, param in enumerate(params): - statement = statement.replace( - ":arg%d" % i, force_str(param, errors="replace") - ) + statement = statement.replace(':arg%d' % i, force_str(param, errors='replace')) elif isinstance(params, dict): for key, param in params.items(): - statement = statement.replace( - ":%s" % key, force_str(param, errors="replace") - ) + statement = statement.replace(':%s' % key, force_str(param, errors='replace')) return statement def last_insert_id(self, cursor, table_name, pk_name): @@ -324,10 +307,10 @@ END; return cursor.fetchone()[0] def lookup_cast(self, lookup_type, internal_type=None): - if lookup_type in ("iexact", "icontains", "istartswith", "iendswith"): + if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'): return "UPPER(%s)" - if internal_type == "JSONField" and lookup_type == "exact": - return "DBMS_LOB.SUBSTR(%s)" + if internal_type == 'JSONField' and lookup_type == 'exact': + return 'DBMS_LOB.SUBSTR(%s)' return "%s" def max_in_list_size(self): @@ -344,7 +327,7 @@ END; def process_clob(self, value): if value is None: - return "" + return '' return value.read() def quote_name(self, name): @@ -353,69 +336,59 @@ END; # always defaults to uppercase. # We simplify things by making Oracle identifiers always uppercase. if not name.startswith('"') and not name.endswith('"'): - name = '"%s"' % truncate_name(name, self.max_name_length()) + name = '"%s"' % truncate_name(name.upper(), self.max_name_length()) # Oracle puts the query text into a (query % args) construct, so % signs # in names need to be escaped. The '%%' will be collapsed back to '%' at # that stage so we aren't really making the name longer here. - name = name.replace("%", "%%") + name = name.replace('%', '%%') return name.upper() def regex_lookup(self, lookup_type): - if lookup_type == "regex": + if lookup_type == 'regex': match_option = "'c'" else: match_option = "'i'" - return "REGEXP_LIKE(%%s, %%s, %s)" % match_option + return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option def return_insert_columns(self, fields): if not fields: - return "", () + return '', () field_names = [] params = [] for field in fields: - field_names.append( - "%s.%s" - % ( - self.quote_name(field.model._meta.db_table), - self.quote_name(field.column), - ) - ) + field_names.append('%s.%s' % ( + self.quote_name(field.model._meta.db_table), + self.quote_name(field.column), + )) params.append(InsertVar(field)) - return "RETURNING %s INTO %s" % ( - ", ".join(field_names), - ", ".join(["%s"] * len(params)), + return 'RETURNING %s INTO %s' % ( + ', '.join(field_names), + ', '.join(['%s'] * len(params)), ), tuple(params) def __foreign_key_constraints(self, table_name, recursive): with self.connection.cursor() as cursor: if recursive: - cursor.execute( - """ + cursor.execute(""" SELECT user_tables.table_name, rcons.constraint_name FROM user_tables JOIN user_constraints cons - ON (user_tables.table_name = cons.table_name - AND cons.constraint_type = ANY('P', 'U')) + ON (user_tables.table_name = cons.table_name AND cons.constraint_type = ANY('P', 'U')) LEFT JOIN user_constraints rcons - ON (user_tables.table_name = rcons.table_name - AND rcons.constraint_type = 'R') + ON (user_tables.table_name = rcons.table_name AND rcons.constraint_type = 'R') START WITH user_tables.table_name = UPPER(%s) - CONNECT BY - NOCYCLE PRIOR cons.constraint_name = rcons.r_constraint_name + CONNECT BY NOCYCLE PRIOR cons.constraint_name = rcons.r_constraint_name GROUP BY user_tables.table_name, rcons.constraint_name HAVING user_tables.table_name != UPPER(%s) ORDER BY MAX(level) DESC - """, - (table_name, table_name), - ) + """, (table_name, table_name)) else: - cursor.execute( - """ + cursor.execute(""" SELECT cons.table_name, cons.constraint_name FROM @@ -423,9 +396,7 @@ END; WHERE cons.constraint_type = 'R' AND cons.table_name = UPPER(%s) - """, - (table_name,), - ) + """, (table_name,)) return cursor.fetchall() @cached_property @@ -445,54 +416,42 @@ END; # which truncates all dependent tables by manually retrieving all # foreign key constraints and resolving dependencies. for table in tables: - for foreign_table, constraint in self._foreign_key_constraints( - table, recursive=allow_cascade - ): + for foreign_table, constraint in self._foreign_key_constraints(table, recursive=allow_cascade): if allow_cascade: truncated_tables.add(foreign_table) constraints.add((foreign_table, constraint)) - sql = ( - [ - "%s %s %s %s %s %s %s %s;" - % ( - style.SQL_KEYWORD("ALTER"), - style.SQL_KEYWORD("TABLE"), - style.SQL_FIELD(self.quote_name(table)), - style.SQL_KEYWORD("DISABLE"), - style.SQL_KEYWORD("CONSTRAINT"), - style.SQL_FIELD(self.quote_name(constraint)), - style.SQL_KEYWORD("KEEP"), - style.SQL_KEYWORD("INDEX"), - ) - for table, constraint in constraints - ] - + [ - "%s %s %s;" - % ( - style.SQL_KEYWORD("TRUNCATE"), - style.SQL_KEYWORD("TABLE"), - style.SQL_FIELD(self.quote_name(table)), - ) - for table in truncated_tables - ] - + [ - "%s %s %s %s %s %s;" - % ( - style.SQL_KEYWORD("ALTER"), - style.SQL_KEYWORD("TABLE"), - style.SQL_FIELD(self.quote_name(table)), - style.SQL_KEYWORD("ENABLE"), - style.SQL_KEYWORD("CONSTRAINT"), - style.SQL_FIELD(self.quote_name(constraint)), - ) - for table, constraint in constraints - ] - ) + sql = [ + '%s %s %s %s %s %s %s %s;' % ( + style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('TABLE'), + style.SQL_FIELD(self.quote_name(table)), + style.SQL_KEYWORD('DISABLE'), + style.SQL_KEYWORD('CONSTRAINT'), + style.SQL_FIELD(self.quote_name(constraint)), + style.SQL_KEYWORD('KEEP'), + style.SQL_KEYWORD('INDEX'), + ) for table, constraint in constraints + ] + [ + '%s %s %s;' % ( + style.SQL_KEYWORD('TRUNCATE'), + style.SQL_KEYWORD('TABLE'), + style.SQL_FIELD(self.quote_name(table)), + ) for table in truncated_tables + ] + [ + '%s %s %s %s %s %s;' % ( + style.SQL_KEYWORD('ALTER'), + style.SQL_KEYWORD('TABLE'), + style.SQL_FIELD(self.quote_name(table)), + style.SQL_KEYWORD('ENABLE'), + style.SQL_KEYWORD('CONSTRAINT'), + style.SQL_FIELD(self.quote_name(constraint)), + ) for table, constraint in constraints + ] if reset_sequences: sequences = [ sequence for sequence in self.connection.introspection.sequence_list() - if sequence["table"].upper() in truncated_tables + if sequence['table'].upper() in truncated_tables ] # Since we've just deleted all the rows, running our sequence ALTER # code will reset the sequence to 0. @@ -502,17 +461,15 @@ END; def sequence_reset_by_name_sql(self, style, sequences): sql = [] for sequence_info in sequences: - no_autofield_sequence_name = self._get_no_autofield_sequence_name( - sequence_info["table"] - ) - table = self.quote_name(sequence_info["table"]) - column = self.quote_name(sequence_info["column"] or "id") + no_autofield_sequence_name = self._get_no_autofield_sequence_name(sequence_info['table']) + table = self.quote_name(sequence_info['table']) + column = self.quote_name(sequence_info['column'] or 'id') query = self._sequence_reset_sql % { - "no_autofield_sequence_name": no_autofield_sequence_name, - "table": table, - "column": column, - "table_name": strip_quotes(table), - "column_name": strip_quotes(column), + 'no_autofield_sequence_name': no_autofield_sequence_name, + 'table': table, + 'column': column, + 'table_name': strip_quotes(table), + 'column_name': strip_quotes(column), } sql.append(query) return sql @@ -523,28 +480,23 @@ END; for model in model_list: for f in model._meta.local_fields: if isinstance(f, AutoField): - no_autofield_sequence_name = self._get_no_autofield_sequence_name( - model._meta.db_table - ) + no_autofield_sequence_name = self._get_no_autofield_sequence_name(model._meta.db_table) table = self.quote_name(model._meta.db_table) column = self.quote_name(f.column) - output.append( - query - % { - "no_autofield_sequence_name": no_autofield_sequence_name, - "table": table, - "column": column, - "table_name": strip_quotes(table), - "column_name": strip_quotes(column), - } - ) + output.append(query % { + 'no_autofield_sequence_name': no_autofield_sequence_name, + 'table': table, + 'column': column, + 'table_name': strip_quotes(table), + 'column_name': strip_quotes(column), + }) # Only one AutoField is allowed per model, so don't # continue to loop break return output def start_transaction_sql(self): - return "" + return '' def tablespace_sql(self, tablespace, inline=False): if inline: @@ -575,7 +527,7 @@ END; return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value # cx_Oracle doesn't support tz-aware datetimes @@ -583,10 +535,7 @@ END; if settings.USE_TZ: value = timezone.make_naive(value, self.connection.timezone) else: - raise ValueError( - "Oracle backend does not support timezone-aware datetimes when " - "USE_TZ is False." - ) + raise ValueError("Oracle backend does not support timezone-aware datetimes when USE_TZ is False.") return Oracle_datetime.from_datetime(value) @@ -595,39 +544,38 @@ END; return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value if isinstance(value, str): - return datetime.datetime.strptime(value, "%H:%M:%S") + return datetime.datetime.strptime(value, '%H:%M:%S') # Oracle doesn't support tz-aware times if timezone.is_aware(value): raise ValueError("Oracle backend does not support timezone-aware times.") - return Oracle_datetime( - 1900, 1, 1, value.hour, value.minute, value.second, value.microsecond - ) + return Oracle_datetime(1900, 1, 1, value.hour, value.minute, + value.second, value.microsecond) def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None): return value def combine_expression(self, connector, sub_expressions): lhs, rhs = sub_expressions - if connector == "%%": - return "MOD(%s)" % ",".join(sub_expressions) - elif connector == "&": - return "BITAND(%s)" % ",".join(sub_expressions) - elif connector == "|": - return "BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s" % {"lhs": lhs, "rhs": rhs} - elif connector == "<<": - return "(%(lhs)s * POWER(2, %(rhs)s))" % {"lhs": lhs, "rhs": rhs} - elif connector == ">>": - return "FLOOR(%(lhs)s / POWER(2, %(rhs)s))" % {"lhs": lhs, "rhs": rhs} - elif connector == "^": - return "POWER(%s)" % ",".join(sub_expressions) - elif connector == "#": - raise NotSupportedError("Bitwise XOR is not supported in Oracle.") + if connector == '%%': + return 'MOD(%s)' % ','.join(sub_expressions) + elif connector == '&': + return 'BITAND(%s)' % ','.join(sub_expressions) + elif connector == '|': + return 'BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s' % {'lhs': lhs, 'rhs': rhs} + elif connector == '<<': + return '(%(lhs)s * POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs} + elif connector == '>>': + return 'FLOOR(%(lhs)s / POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs} + elif connector == '^': + return 'POWER(%s)' % ','.join(sub_expressions) + elif connector == '#': + raise NotSupportedError('Bitwise XOR is not supported in Oracle.') return super().combine_expression(connector, sub_expressions) def _get_no_autofield_sequence_name(self, table): @@ -636,17 +584,14 @@ END; AutoFields that aren't Oracle identity columns. """ name_length = self.max_name_length() - 3 - return "%s_SQ" % truncate_name(strip_quotes(table), name_length).upper() + return '%s_SQ' % truncate_name(strip_quotes(table), name_length).upper() def _get_sequence_name(self, cursor, table, pk_name): - cursor.execute( - """ + cursor.execute(""" SELECT sequence_name FROM user_tab_identity_cols WHERE table_name = UPPER(%s) - AND column_name = UPPER(%s)""", - [table, pk_name], - ) + AND column_name = UPPER(%s)""", [table, pk_name]) row = cursor.fetchone() return self._get_no_autofield_sequence_name(table) if row is None else row[0] @@ -657,33 +602,26 @@ END; for i, placeholder in enumerate(row): # A model without any fields has fields=[None]. if fields[i]: - internal_type = getattr( - fields[i], "target_field", fields[i] - ).get_internal_type() - placeholder = ( - BulkInsertMapper.types.get(internal_type, "%s") % placeholder - ) + internal_type = getattr(fields[i], 'target_field', fields[i]).get_internal_type() + placeholder = BulkInsertMapper.types.get(internal_type, '%s') % placeholder # Add columns aliases to the first select to avoid "ORA-00918: # column ambiguously defined" when two or more columns in the # first select have the same value. if not query: - placeholder = "%s col_%s" % (placeholder, i) + placeholder = '%s col_%s' % (placeholder, i) select.append(placeholder) - query.append("SELECT %s FROM DUAL" % ", ".join(select)) + query.append('SELECT %s FROM DUAL' % ', '.join(select)) # Bulk insert to tables with Oracle identity columns causes Oracle to # add sequence.nextval to it. Sequence.nextval cannot be used with the # UNION operator. To prevent incorrect SQL, move UNION to a subquery. - return "SELECT * FROM (%s)" % " UNION ALL ".join(query) + return 'SELECT * FROM (%s)' % ' UNION ALL '.join(query) def subtract_temporals(self, internal_type, lhs, rhs): - if internal_type == "DateField": + if internal_type == 'DateField': lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs params = (*lhs_params, *rhs_params) - return ( - "NUMTODSINTERVAL(TO_NUMBER(%s - %s), 'DAY')" % (lhs_sql, rhs_sql), - params, - ) + return "NUMTODSINTERVAL(TO_NUMBER(%s - %s), 'DAY')" % (lhs_sql, rhs_sql), params return super().subtract_temporals(internal_type, lhs, rhs) def bulk_batch_size(self, fields, objs): @@ -697,12 +635,10 @@ END; Oracle supports only EXISTS(...) or filters in the WHERE clause, others must be compared with True. """ - if isinstance(expression, (Exists, Lookup, WhereNode)): + if isinstance(expression, (Exists, WhereNode)): return True if isinstance(expression, ExpressionWrapper) and expression.conditional: - return self.conditional_expression_supported_in_where_clause( - expression.expression - ) + return self.conditional_expression_supported_in_where_clause(expression.expression) if isinstance(expression, RawSQL) and expression.conditional: return True return False diff --git a/venv/Lib/site-packages/django/db/backends/oracle/schema.py b/venv/Lib/site-packages/django/db/backends/oracle/schema.py index e4178ec..cf875fc 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/schema.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/schema.py @@ -3,10 +3,7 @@ import datetime import re from django.db import DatabaseError -from django.db.backends.base.schema import ( - BaseDatabaseSchemaEditor, - _related_non_m2m_objects, -) +from django.db.backends.base.schema import BaseDatabaseSchemaEditor class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -21,9 +18,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_alter_column_collate = "MODIFY %(column)s %(type)s%(collation)s" sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s" - sql_create_column_inline_fk = ( - "CONSTRAINT %(name)s REFERENCES %(to_table)s(%(to_column)s)%(deferrable)s" - ) + sql_create_column_inline_fk = 'CONSTRAINT %(name)s REFERENCES %(to_table)s(%(to_column)s)%(deferrable)s' sql_delete_table = "DROP TABLE %(table)s CASCADE CONSTRAINTS" sql_create_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s" @@ -31,7 +26,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): if isinstance(value, (datetime.date, datetime.time, datetime.datetime)): return "'%s'" % value elif isinstance(value, str): - return "'%s'" % value.replace("'", "''").replace("%", "%%") + return "'%s'" % value.replace("\'", "\'\'").replace('%', '%%') elif isinstance(value, (bytes, bytearray, memoryview)): return "'%s'" % value.hex() elif isinstance(value, bool): @@ -50,8 +45,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # Run superclass action super().delete_model(model) # Clean up manually created sequence. - self.execute( - """ + self.execute(""" DECLARE i INTEGER; BEGIN @@ -61,13 +55,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): EXECUTE IMMEDIATE 'DROP SEQUENCE "%(sq_name)s"'; END IF; END; - /""" - % { - "sq_name": self.connection.ops._get_no_autofield_sequence_name( - model._meta.db_table - ) - } - ) + /""" % {'sq_name': self.connection.ops._get_no_autofield_sequence_name(model._meta.db_table)}) def alter_field(self, model, old_field, new_field, strict=False): try: @@ -76,16 +64,16 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): description = str(e) # If we're changing type to an unsupported type we need a # SQLite-ish workaround - if "ORA-22858" in description or "ORA-22859" in description: + if 'ORA-22858' in description or 'ORA-22859' in description: self._alter_field_type_workaround(model, old_field, new_field) # If an identity column is changing to a non-numeric type, drop the # identity first. - elif "ORA-30675" in description: + elif 'ORA-30675' in description: self._drop_identity(model._meta.db_table, old_field.column) self.alter_field(model, old_field, new_field, strict) # If a primary key column is changing to an identity column, drop # the primary key first. - elif "ORA-30673" in description and old_field.primary_key: + elif 'ORA-30673' in description and old_field.primary_key: self._delete_primary_key(model, strict=True) self._alter_field_type_workaround(model, old_field, new_field) else: @@ -105,66 +93,45 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # Make a new field that's like the new one but with a temporary # column name. new_temp_field = copy.deepcopy(new_field) - new_temp_field.null = new_field.get_internal_type() not in ( - "AutoField", - "BigAutoField", - "SmallAutoField", - ) + new_temp_field.null = (new_field.get_internal_type() not in ('AutoField', 'BigAutoField', 'SmallAutoField')) new_temp_field.column = self._generate_temp_name(new_field.column) # Add it self.add_field(model, new_temp_field) # Explicit data type conversion - # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf + # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf # /Data-Type-Comparison-Rules.html#GUID-D0C5A47E-6F93-4C2D-9E49-4F2B86B359DD new_value = self.quote_name(old_field.column) old_type = old_field.db_type(self.connection) - if re.match("^N?CLOB", old_type): + if re.match('^N?CLOB', old_type): new_value = "TO_CHAR(%s)" % new_value - old_type = "VARCHAR2" - if re.match("^N?VARCHAR2", old_type): + old_type = 'VARCHAR2' + if re.match('^N?VARCHAR2', old_type): new_internal_type = new_field.get_internal_type() - if new_internal_type == "DateField": + if new_internal_type == 'DateField': new_value = "TO_DATE(%s, 'YYYY-MM-DD')" % new_value - elif new_internal_type == "DateTimeField": + elif new_internal_type == 'DateTimeField': new_value = "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" % new_value - elif new_internal_type == "TimeField": + elif new_internal_type == 'TimeField': # TimeField are stored as TIMESTAMP with a 1900-01-01 date part. - new_value = "CONCAT('1900-01-01 ', %s)" % new_value - new_value = "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" % new_value + new_value = "TO_TIMESTAMP(CONCAT('1900-01-01 ', %s), 'YYYY-MM-DD HH24:MI:SS.FF')" % new_value # Transfer values across - self.execute( - "UPDATE %s set %s=%s" - % ( - self.quote_name(model._meta.db_table), - self.quote_name(new_temp_field.column), - new_value, - ) - ) + self.execute("UPDATE %s set %s=%s" % ( + self.quote_name(model._meta.db_table), + self.quote_name(new_temp_field.column), + new_value, + )) # Drop the old field self.remove_field(model, old_field) # Rename and possibly make the new field NOT NULL super().alter_field(model, new_temp_field, new_field) - # Recreate foreign key (if necessary) because the old field is not - # passed to the alter_field() and data types of new_temp_field and - # new_field always match. - new_type = new_field.db_type(self.connection) - if ( - (old_field.primary_key and new_field.primary_key) - or (old_field.unique and new_field.unique) - ) and old_type != new_type: - for _, rel in _related_non_m2m_objects(new_temp_field, new_field): - if rel.field.db_constraint: - self.execute( - self._create_fk_sql(rel.related_model, rel.field, "_fk") - ) def _alter_column_type_sql(self, model, old_field, new_field, new_type): - auto_field_types = {"AutoField", "BigAutoField", "SmallAutoField"} + auto_field_types = {'AutoField', 'BigAutoField', 'SmallAutoField'} # Drop the identity if migrating away from AutoField. if ( - old_field.get_internal_type() in auto_field_types - and new_field.get_internal_type() not in auto_field_types - and self._is_identity_column(model._meta.db_table, new_field.column) + old_field.get_internal_type() in auto_field_types and + new_field.get_internal_type() not in auto_field_types and + self._is_identity_column(model._meta.db_table, new_field.column) ): self._drop_identity(model._meta.db_table, new_field.column) return super()._alter_column_type_sql(model, old_field, new_field, new_type) @@ -190,55 +157,42 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def _field_should_be_indexed(self, model, field): create_index = super()._field_should_be_indexed(model, field) db_type = field.db_type(self.connection) - if ( - db_type is not None - and db_type.lower() in self.connection._limited_data_types - ): + if db_type is not None and db_type.lower() in self.connection._limited_data_types: return False return create_index def _unique_should_be_added(self, old_field, new_field): - return super()._unique_should_be_added( - old_field, new_field - ) and not self._field_became_primary_key(old_field, new_field) + return ( + super()._unique_should_be_added(old_field, new_field) and + not self._field_became_primary_key(old_field, new_field) + ) def _is_identity_column(self, table_name, column_name): with self.connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(""" SELECT CASE WHEN identity_column = 'YES' THEN 1 ELSE 0 END FROM user_tab_cols WHERE table_name = %s AND column_name = %s - """, - [self.normalize_name(table_name), self.normalize_name(column_name)], - ) + """, [self.normalize_name(table_name), self.normalize_name(column_name)]) row = cursor.fetchone() return row[0] if row else False def _drop_identity(self, table_name, column_name): - self.execute( - "ALTER TABLE %(table)s MODIFY %(column)s DROP IDENTITY" - % { - "table": self.quote_name(table_name), - "column": self.quote_name(column_name), - } - ) + self.execute('ALTER TABLE %(table)s MODIFY %(column)s DROP IDENTITY' % { + 'table': self.quote_name(table_name), + 'column': self.quote_name(column_name), + }) def _get_default_collation(self, table_name): with self.connection.cursor() as cursor: - cursor.execute( - """ + cursor.execute(""" SELECT default_collation FROM user_tables WHERE table_name = %s - """, - [self.normalize_name(table_name)], - ) + """, [self.normalize_name(table_name)]) return cursor.fetchone()[0] def _alter_column_collation_sql(self, model, new_field, new_type, new_collation): if new_collation is None: new_collation = self._get_default_collation(model._meta.db_table) - return super()._alter_column_collation_sql( - model, new_field, new_type, new_collation - ) + return super()._alter_column_collation_sql(model, new_field, new_type, new_collation) diff --git a/venv/Lib/site-packages/django/db/backends/oracle/utils.py b/venv/Lib/site-packages/django/db/backends/oracle/utils.py index bb11931..5665079 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/utils.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/utils.py @@ -9,25 +9,24 @@ class InsertVar: as a parameter, in order to receive the id of the row created by an insert statement. """ - types = { - "AutoField": int, - "BigAutoField": int, - "SmallAutoField": int, - "IntegerField": int, - "BigIntegerField": int, - "SmallIntegerField": int, - "PositiveBigIntegerField": int, - "PositiveSmallIntegerField": int, - "PositiveIntegerField": int, - "FloatField": Database.NATIVE_FLOAT, - "DateTimeField": Database.TIMESTAMP, - "DateField": Database.Date, - "DecimalField": Database.NUMBER, + 'AutoField': int, + 'BigAutoField': int, + 'SmallAutoField': int, + 'IntegerField': int, + 'BigIntegerField': int, + 'SmallIntegerField': int, + 'PositiveBigIntegerField': int, + 'PositiveSmallIntegerField': int, + 'PositiveIntegerField': int, + 'FloatField': Database.NATIVE_FLOAT, + 'DateTimeField': Database.TIMESTAMP, + 'DateField': Database.Date, + 'DecimalField': Database.NUMBER, } def __init__(self, field): - internal_type = getattr(field, "target_field", field).get_internal_type() + internal_type = getattr(field, 'target_field', field).get_internal_type() self.db_type = self.types.get(internal_type, str) self.bound_param = None @@ -44,54 +43,49 @@ class Oracle_datetime(datetime.datetime): A datetime object, with an additional class attribute to tell cx_Oracle to save the microseconds too. """ - input_size = Database.TIMESTAMP @classmethod def from_datetime(cls, dt): return Oracle_datetime( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, + dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, dt.microsecond, ) class BulkInsertMapper: - BLOB = "TO_BLOB(%s)" - CLOB = "TO_CLOB(%s)" - DATE = "TO_DATE(%s)" - INTERVAL = "CAST(%s as INTERVAL DAY(9) TO SECOND(6))" - NUMBER = "TO_NUMBER(%s)" - TIMESTAMP = "TO_TIMESTAMP(%s)" + BLOB = 'TO_BLOB(%s)' + CLOB = 'TO_CLOB(%s)' + DATE = 'TO_DATE(%s)' + INTERVAL = 'CAST(%s as INTERVAL DAY(9) TO SECOND(6))' + NUMBER = 'TO_NUMBER(%s)' + TIMESTAMP = 'TO_TIMESTAMP(%s)' types = { - "AutoField": NUMBER, - "BigAutoField": NUMBER, - "BigIntegerField": NUMBER, - "BinaryField": BLOB, - "BooleanField": NUMBER, - "DateField": DATE, - "DateTimeField": TIMESTAMP, - "DecimalField": NUMBER, - "DurationField": INTERVAL, - "FloatField": NUMBER, - "IntegerField": NUMBER, - "PositiveBigIntegerField": NUMBER, - "PositiveIntegerField": NUMBER, - "PositiveSmallIntegerField": NUMBER, - "SmallAutoField": NUMBER, - "SmallIntegerField": NUMBER, - "TextField": CLOB, - "TimeField": TIMESTAMP, + 'AutoField': NUMBER, + 'BigAutoField': NUMBER, + 'BigIntegerField': NUMBER, + 'BinaryField': BLOB, + 'BooleanField': NUMBER, + 'DateField': DATE, + 'DateTimeField': TIMESTAMP, + 'DecimalField': NUMBER, + 'DurationField': INTERVAL, + 'FloatField': NUMBER, + 'IntegerField': NUMBER, + 'NullBooleanField': NUMBER, + 'PositiveBigIntegerField': NUMBER, + 'PositiveIntegerField': NUMBER, + 'PositiveSmallIntegerField': NUMBER, + 'SmallAutoField': NUMBER, + 'SmallIntegerField': NUMBER, + 'TextField': CLOB, + 'TimeField': TIMESTAMP, } def dsn(settings_dict): - if settings_dict["PORT"]: - host = settings_dict["HOST"].strip() or "localhost" - return Database.makedsn(host, int(settings_dict["PORT"]), settings_dict["NAME"]) - return settings_dict["NAME"] + if settings_dict['PORT']: + host = settings_dict['HOST'].strip() or 'localhost' + return Database.makedsn(host, int(settings_dict['PORT']), settings_dict['NAME']) + return settings_dict['NAME'] diff --git a/venv/Lib/site-packages/django/db/backends/oracle/validation.py b/venv/Lib/site-packages/django/db/backends/oracle/validation.py index 4035b12..e5a35fd 100644 --- a/venv/Lib/site-packages/django/db/backends/oracle/validation.py +++ b/venv/Lib/site-packages/django/db/backends/oracle/validation.py @@ -9,14 +9,14 @@ class DatabaseValidation(BaseDatabaseValidation): if field.db_index and field_type.lower() in self.connection._limited_data_types: errors.append( checks.Warning( - "Oracle does not support a database index on %s columns." + 'Oracle does not support a database index on %s columns.' % field_type, hint=( "An index won't be created. Silence this warning if " "you don't care about it." ), obj=field, - id="fields.W162", + id='fields.W162', ) ) return errors diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/base.py b/venv/Lib/site-packages/django/db/backends/postgresql/base.py index 92f3932..a8efb0e 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/base.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/base.py @@ -11,10 +11,11 @@ from contextlib import contextmanager from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.db import DatabaseError as WrappedDatabaseError -from django.db import connections +from django.db import DatabaseError as WrappedDatabaseError, connections from django.db.backends.base.base import BaseDatabaseWrapper -from django.db.backends.utils import CursorDebugWrapper as BaseCursorDebugWrapper +from django.db.backends.utils import ( + CursorDebugWrapper as BaseCursorDebugWrapper, +) from django.utils.asyncio import async_unsafe from django.utils.functional import cached_property from django.utils.safestring import SafeString @@ -29,17 +30,14 @@ except ImportError as e: def psycopg2_version(): - version = psycopg2.__version__.split(" ", 1)[0] + version = psycopg2.__version__.split(' ', 1)[0] return get_version_tuple(version) PSYCOPG2_VERSION = psycopg2_version() -if PSYCOPG2_VERSION < (2, 8, 4): - raise ImproperlyConfigured( - "psycopg2 version 2.8.4 or newer is required; you have %s" - % psycopg2.__version__ - ) +if PSYCOPG2_VERSION < (2, 5, 4): + raise ImproperlyConfigured("psycopg2_version 2.5.4 or newer is required; you have %s" % psycopg2.__version__) # Some of these import psycopg2, so import them after checking if it's installed. @@ -58,68 +56,69 @@ psycopg2.extras.register_uuid() INETARRAY_OID = 1041 INETARRAY = psycopg2.extensions.new_array_type( (INETARRAY_OID,), - "INETARRAY", + 'INETARRAY', psycopg2.extensions.UNICODE, ) psycopg2.extensions.register_type(INETARRAY) class DatabaseWrapper(BaseDatabaseWrapper): - vendor = "postgresql" - display_name = "PostgreSQL" + vendor = 'postgresql' + display_name = 'PostgreSQL' # This dictionary maps Field objects to their associated PostgreSQL column # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. data_types = { - "AutoField": "serial", - "BigAutoField": "bigserial", - "BinaryField": "bytea", - "BooleanField": "boolean", - "CharField": "varchar(%(max_length)s)", - "DateField": "date", - "DateTimeField": "timestamp with time zone", - "DecimalField": "numeric(%(max_digits)s, %(decimal_places)s)", - "DurationField": "interval", - "FileField": "varchar(%(max_length)s)", - "FilePathField": "varchar(%(max_length)s)", - "FloatField": "double precision", - "IntegerField": "integer", - "BigIntegerField": "bigint", - "IPAddressField": "inet", - "GenericIPAddressField": "inet", - "JSONField": "jsonb", - "OneToOneField": "integer", - "PositiveBigIntegerField": "bigint", - "PositiveIntegerField": "integer", - "PositiveSmallIntegerField": "smallint", - "SlugField": "varchar(%(max_length)s)", - "SmallAutoField": "smallserial", - "SmallIntegerField": "smallint", - "TextField": "text", - "TimeField": "time", - "UUIDField": "uuid", + 'AutoField': 'serial', + 'BigAutoField': 'bigserial', + 'BinaryField': 'bytea', + 'BooleanField': 'boolean', + 'CharField': 'varchar(%(max_length)s)', + 'DateField': 'date', + 'DateTimeField': 'timestamp with time zone', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'DurationField': 'interval', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', + 'FloatField': 'double precision', + 'IntegerField': 'integer', + 'BigIntegerField': 'bigint', + 'IPAddressField': 'inet', + 'GenericIPAddressField': 'inet', + 'JSONField': 'jsonb', + 'NullBooleanField': 'boolean', + 'OneToOneField': 'integer', + 'PositiveBigIntegerField': 'bigint', + 'PositiveIntegerField': 'integer', + 'PositiveSmallIntegerField': 'smallint', + 'SlugField': 'varchar(%(max_length)s)', + 'SmallAutoField': 'smallserial', + 'SmallIntegerField': 'smallint', + 'TextField': 'text', + 'TimeField': 'time', + 'UUIDField': 'uuid', } data_type_check_constraints = { - "PositiveBigIntegerField": '"%(column)s" >= 0', - "PositiveIntegerField": '"%(column)s" >= 0', - "PositiveSmallIntegerField": '"%(column)s" >= 0', + 'PositiveBigIntegerField': '"%(column)s" >= 0', + 'PositiveIntegerField': '"%(column)s" >= 0', + 'PositiveSmallIntegerField': '"%(column)s" >= 0', } operators = { - "exact": "= %s", - "iexact": "= UPPER(%s)", - "contains": "LIKE %s", - "icontains": "LIKE UPPER(%s)", - "regex": "~ %s", - "iregex": "~* %s", - "gt": "> %s", - "gte": ">= %s", - "lt": "< %s", - "lte": "<= %s", - "startswith": "LIKE %s", - "endswith": "LIKE %s", - "istartswith": "LIKE UPPER(%s)", - "iendswith": "LIKE UPPER(%s)", + 'exact': '= %s', + 'iexact': '= UPPER(%s)', + 'contains': 'LIKE %s', + 'icontains': 'LIKE UPPER(%s)', + 'regex': '~ %s', + 'iregex': '~* %s', + 'gt': '> %s', + 'gte': '>= %s', + 'lt': '< %s', + 'lte': '<= %s', + 'startswith': 'LIKE %s', + 'endswith': 'LIKE %s', + 'istartswith': 'LIKE UPPER(%s)', + 'iendswith': 'LIKE UPPER(%s)', } # The patterns below are used to generate SQL pattern lookup clauses when @@ -130,16 +129,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): # # Note: we use str.format() here for readability as '%' is used as a wildcard for # the LIKE operator. - pattern_esc = ( - r"REPLACE(REPLACE(REPLACE({}, E'\\', E'\\\\'), E'%%', E'\\%%'), E'_', E'\\_')" - ) + pattern_esc = r"REPLACE(REPLACE(REPLACE({}, E'\\', E'\\\\'), E'%%', E'\\%%'), E'_', E'\\_')" pattern_ops = { - "contains": "LIKE '%%' || {} || '%%'", - "icontains": "LIKE '%%' || UPPER({}) || '%%'", - "startswith": "LIKE {} || '%%'", - "istartswith": "LIKE UPPER({}) || '%%'", - "endswith": "LIKE '%%' || {}", - "iendswith": "LIKE '%%' || UPPER({})", + 'contains': "LIKE '%%' || {} || '%%'", + 'icontains': "LIKE '%%' || UPPER({}) || '%%'", + 'startswith': "LIKE {} || '%%'", + 'istartswith': "LIKE UPPER({}) || '%%'", + 'endswith': "LIKE '%%' || {}", + 'iendswith': "LIKE '%%' || UPPER({})", } Database = Database @@ -156,46 +153,33 @@ class DatabaseWrapper(BaseDatabaseWrapper): def get_connection_params(self): settings_dict = self.settings_dict # None may be used to connect to the default 'postgres' db - if settings_dict["NAME"] == "" and not settings_dict.get("OPTIONS", {}).get( - "service" - ): + if settings_dict['NAME'] == '': raise ImproperlyConfigured( "settings.DATABASES is improperly configured. " - "Please supply the NAME or OPTIONS['service'] value." - ) - if len(settings_dict["NAME"] or "") > self.ops.max_name_length(): + "Please supply the NAME value.") + if len(settings_dict['NAME'] or '') > self.ops.max_name_length(): raise ImproperlyConfigured( "The database name '%s' (%d characters) is longer than " "PostgreSQL's limit of %d characters. Supply a shorter NAME " - "in settings.DATABASES." - % ( - settings_dict["NAME"], - len(settings_dict["NAME"]), + "in settings.DATABASES." % ( + settings_dict['NAME'], + len(settings_dict['NAME']), self.ops.max_name_length(), ) ) - conn_params = {} - if settings_dict["NAME"]: - conn_params = { - "database": settings_dict["NAME"], - **settings_dict["OPTIONS"], - } - elif settings_dict["NAME"] is None: - # Connect to the default 'postgres' db. - settings_dict.get("OPTIONS", {}).pop("service", None) - conn_params = {"database": "postgres", **settings_dict["OPTIONS"]} - else: - conn_params = {**settings_dict["OPTIONS"]} - - conn_params.pop("isolation_level", None) - if settings_dict["USER"]: - conn_params["user"] = settings_dict["USER"] - if settings_dict["PASSWORD"]: - conn_params["password"] = settings_dict["PASSWORD"] - if settings_dict["HOST"]: - conn_params["host"] = settings_dict["HOST"] - if settings_dict["PORT"]: - conn_params["port"] = settings_dict["PORT"] + conn_params = { + 'database': settings_dict['NAME'] or 'postgres', + **settings_dict['OPTIONS'], + } + conn_params.pop('isolation_level', None) + if settings_dict['USER']: + conn_params['user'] = settings_dict['USER'] + if settings_dict['PASSWORD']: + conn_params['password'] = settings_dict['PASSWORD'] + if settings_dict['HOST']: + conn_params['host'] = settings_dict['HOST'] + if settings_dict['PORT']: + conn_params['port'] = settings_dict['PORT'] return conn_params @async_unsafe @@ -207,9 +191,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): # default when no value is explicitly specified in options. # - before calling _set_autocommit() because if autocommit is on, that # will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT. - options = self.settings_dict["OPTIONS"] + options = self.settings_dict['OPTIONS'] try: - self.isolation_level = options["isolation_level"] + self.isolation_level = options['isolation_level'] except KeyError: self.isolation_level = connection.isolation_level else: @@ -219,15 +203,13 @@ class DatabaseWrapper(BaseDatabaseWrapper): # Register dummy loads() to avoid a round trip from psycopg2's decode # to json.dumps() to json.loads(), when using a custom decoder in # JSONField. - psycopg2.extras.register_default_jsonb( - conn_or_curs=connection, loads=lambda x: x - ) + psycopg2.extras.register_default_jsonb(conn_or_curs=connection, loads=lambda x: x) return connection def ensure_timezone(self): if self.connection is None: return False - conn_timezone_name = self.connection.get_parameter_status("TimeZone") + conn_timezone_name = self.connection.get_parameter_status('TimeZone') timezone_name = self.timezone_name if timezone_name and conn_timezone_name != timezone_name: with self.connection.cursor() as cursor: @@ -236,7 +218,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return False def init_connection_state(self): - self.connection.set_client_encoding("UTF8") + self.connection.set_client_encoding('UTF8') timezone_changed = self.ensure_timezone() if timezone_changed: @@ -249,9 +231,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): if name: # In autocommit mode, the cursor will be used outside of a # transaction, hence use a holdable cursor. - cursor = self.connection.cursor( - name, scrollable=False, withhold=self.connection.autocommit - ) + cursor = self.connection.cursor(name, scrollable=False, withhold=self.connection.autocommit) else: cursor = self.connection.cursor() cursor.tzinfo_factory = self.tzinfo_factory if settings.USE_TZ else None @@ -269,18 +249,22 @@ class DatabaseWrapper(BaseDatabaseWrapper): # For now, it's here so that every use of "threading" is # also async-compatible. try: - current_task = asyncio.current_task() + if hasattr(asyncio, 'current_task'): + # Python 3.7 and up + current_task = asyncio.current_task() + else: + # Python 3.6 + current_task = asyncio.Task.current_task() except RuntimeError: current_task = None # Current task can be none even if the current_task call didn't error if current_task: task_ident = str(id(current_task)) else: - task_ident = "sync" + task_ident = 'sync' # Use that and the thread ident to get a unique name return self._cursor( - name="_django_curs_%d_%s_%d" - % ( + name='_django_curs_%d_%s_%d' % ( # Avoid reusing name in other threads / tasks threading.current_thread().ident, task_ident, @@ -298,14 +282,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): afterward. """ with self.cursor() as cursor: - cursor.execute("SET CONSTRAINTS ALL IMMEDIATE") - cursor.execute("SET CONSTRAINTS ALL DEFERRED") + cursor.execute('SET CONSTRAINTS ALL IMMEDIATE') + cursor.execute('SET CONSTRAINTS ALL DEFERRED') def is_usable(self): try: # Use a psycopg cursor directly, bypassing Django's utilities. with self.connection.cursor() as cursor: - cursor.execute("SELECT 1") + cursor.execute('SELECT 1') except Database.Error: return False else: @@ -313,31 +297,22 @@ class DatabaseWrapper(BaseDatabaseWrapper): @contextmanager def _nodb_cursor(self): - cursor = None try: with super()._nodb_cursor() as cursor: yield cursor except (Database.DatabaseError, WrappedDatabaseError): - if cursor is not None: - raise warnings.warn( "Normally Django will use a connection to the 'postgres' database " "to avoid running initialization queries against the production " "database when it's not needed (for example, when running tests). " "Django was unable to create a connection to the 'postgres' database " "and will use the first PostgreSQL database instead.", - RuntimeWarning, + RuntimeWarning ) for connection in connections.all(): - if ( - connection.vendor == "postgresql" - and connection.settings_dict["NAME"] != "postgres" - ): + if connection.vendor == 'postgresql' and connection.settings_dict['NAME'] != 'postgres': conn = self.__class__( - { - **self.settings_dict, - "NAME": connection.settings_dict["NAME"], - }, + {**self.settings_dict, 'NAME': connection.settings_dict['NAME']}, alias=self.alias, ) try: @@ -364,5 +339,5 @@ class CursorDebugWrapper(BaseCursorDebugWrapper): return self.cursor.copy_expert(sql, file, *args) def copy_to(self, file, table, *args, **kwargs): - with self.debug_sql(sql="COPY %s TO STDOUT" % table): + with self.debug_sql(sql='COPY %s TO STDOUT' % table): return self.cursor.copy_to(file, table, *args, **kwargs) diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/client.py b/venv/Lib/site-packages/django/db/backends/postgresql/client.py index 4c9bd63..44f30f7 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/client.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/client.py @@ -4,53 +4,43 @@ from django.db.backends.base.client import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): - executable_name = "psql" + executable_name = 'psql' @classmethod def settings_to_cmd_args_env(cls, settings_dict, parameters): args = [cls.executable_name] - options = settings_dict.get("OPTIONS", {}) + options = settings_dict.get('OPTIONS', {}) - host = settings_dict.get("HOST") - port = settings_dict.get("PORT") - dbname = settings_dict.get("NAME") - user = settings_dict.get("USER") - passwd = settings_dict.get("PASSWORD") - passfile = options.get("passfile") - service = options.get("service") - sslmode = options.get("sslmode") - sslrootcert = options.get("sslrootcert") - sslcert = options.get("sslcert") - sslkey = options.get("sslkey") + host = settings_dict.get('HOST') + port = settings_dict.get('PORT') + dbname = settings_dict.get('NAME') or 'postgres' + user = settings_dict.get('USER') + passwd = settings_dict.get('PASSWORD') + sslmode = options.get('sslmode') + sslrootcert = options.get('sslrootcert') + sslcert = options.get('sslcert') + sslkey = options.get('sslkey') - if not dbname and not service: - # Connect to the default 'postgres' db. - dbname = "postgres" if user: - args += ["-U", user] + args += ['-U', user] if host: - args += ["-h", host] + args += ['-h', host] if port: - args += ["-p", str(port)] - if dbname: - args += [dbname] + args += ['-p', str(port)] + args += [dbname] args.extend(parameters) env = {} if passwd: - env["PGPASSWORD"] = str(passwd) - if service: - env["PGSERVICE"] = str(service) + env['PGPASSWORD'] = str(passwd) if sslmode: - env["PGSSLMODE"] = str(sslmode) + env['PGSSLMODE'] = str(sslmode) if sslrootcert: - env["PGSSLROOTCERT"] = str(sslrootcert) + env['PGSSLROOTCERT'] = str(sslrootcert) if sslcert: - env["PGSSLCERT"] = str(sslcert) + env['PGSSLCERT'] = str(sslcert) if sslkey: - env["PGSSLKEY"] = str(sslkey) - if passfile: - env["PGPASSFILE"] = str(passfile) + env['PGSSLKEY'] = str(sslkey) return args, (env or None) def runshell(self, parameters): diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/creation.py b/venv/Lib/site-packages/django/db/backends/postgresql/creation.py index 70c3eda..a609f33 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/creation.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/creation.py @@ -2,12 +2,12 @@ import sys from psycopg2 import errorcodes -from django.core.exceptions import ImproperlyConfigured from django.db.backends.base.creation import BaseDatabaseCreation from django.db.backends.utils import strip_quotes class DatabaseCreation(BaseDatabaseCreation): + def _quote_name(self, name): return self.connection.ops.quote_name(name) @@ -20,35 +20,30 @@ class DatabaseCreation(BaseDatabaseCreation): return suffix and "WITH" + suffix def sql_table_creation_suffix(self): - test_settings = self.connection.settings_dict["TEST"] - if test_settings.get("COLLATION") is not None: - raise ImproperlyConfigured( - "PostgreSQL does not support collation setting at database " - "creation time." - ) + test_settings = self.connection.settings_dict['TEST'] + assert test_settings['COLLATION'] is None, ( + "PostgreSQL does not support collation setting at database creation time." + ) return self._get_database_create_suffix( - encoding=test_settings["CHARSET"], - template=test_settings.get("TEMPLATE"), + encoding=test_settings['CHARSET'], + template=test_settings.get('TEMPLATE'), ) def _database_exists(self, cursor, database_name): - cursor.execute( - "SELECT 1 FROM pg_catalog.pg_database WHERE datname = %s", - [strip_quotes(database_name)], - ) + cursor.execute('SELECT 1 FROM pg_catalog.pg_database WHERE datname = %s', [strip_quotes(database_name)]) return cursor.fetchone() is not None def _execute_create_test_db(self, cursor, parameters, keepdb=False): try: - if keepdb and self._database_exists(cursor, parameters["dbname"]): + if keepdb and self._database_exists(cursor, parameters['dbname']): # If the database should be kept and it already exists, don't # try to create a new one. return super()._execute_create_test_db(cursor, parameters, keepdb) except Exception as e: - if getattr(e.__cause__, "pgcode", "") != errorcodes.DUPLICATE_DATABASE: + if getattr(e.__cause__, 'pgcode', '') != errorcodes.DUPLICATE_DATABASE: # All errors except "database already exists" cancel tests. - self.log("Got an error creating the test database: %s" % e) + self.log('Got an error creating the test database: %s' % e) sys.exit(2) elif not keepdb: # If the database should be kept, ignore "database already @@ -60,11 +55,11 @@ class DatabaseCreation(BaseDatabaseCreation): # to the template database. self.connection.close() - source_database_name = self.connection.settings_dict["NAME"] - target_database_name = self.get_test_db_clone_settings(suffix)["NAME"] + source_database_name = self.connection.settings_dict['NAME'] + target_database_name = self.get_test_db_clone_settings(suffix)['NAME'] test_db_params = { - "dbname": self._quote_name(target_database_name), - "suffix": self._get_database_create_suffix(template=source_database_name), + 'dbname': self._quote_name(target_database_name), + 'suffix': self._get_database_create_suffix(template=source_database_name), } with self._nodb_cursor() as cursor: try: @@ -72,16 +67,11 @@ class DatabaseCreation(BaseDatabaseCreation): except Exception: try: if verbosity >= 1: - self.log( - "Destroying old test database for alias %s..." - % ( - self._get_database_display_str( - verbosity, target_database_name - ), - ) - ) - cursor.execute("DROP DATABASE %(dbname)s" % test_db_params) + self.log('Destroying old test database for alias %s...' % ( + self._get_database_display_str(verbosity, target_database_name), + )) + cursor.execute('DROP DATABASE %(dbname)s' % test_db_params) self._execute_create_test_db(cursor, test_db_params, keepdb) except Exception as e: - self.log("Got an error cloning the test database: %s" % e) + self.log('Got an error cloning the test database: %s' % e) sys.exit(2) diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/features.py b/venv/Lib/site-packages/django/db/backends/postgresql/features.py index 6dab8d7..84259c0 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/features.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/features.py @@ -53,32 +53,41 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_over_clause = True only_supports_unbounded_with_preceding_and_following = True supports_aggregate_filter_clause = True - supported_explain_formats = {"JSON", "TEXT", "XML", "YAML"} + supported_explain_formats = {'JSON', 'TEXT', 'XML', 'YAML'} + validates_explain_options = False # A query will error on invalid options. supports_deferrable_unique_constraints = True has_json_operators = True json_key_contains_list_matching_requires_list = True - test_collations = { - "non_default": "sv-x-icu", - "swedish_ci": "sv-x-icu", - } - test_now_utc_template = "STATEMENT_TIMESTAMP() AT TIME ZONE 'UTC'" django_test_skips = { - "opclasses are PostgreSQL only.": { - "indexes.tests.SchemaIndexesNotPostgreSQLTests." - "test_create_index_ignores_opclasses", + 'opclasses are PostgreSQL only.': { + 'indexes.tests.SchemaIndexesNotPostgreSQLTests.test_create_index_ignores_opclasses', }, } + @cached_property + def test_collations(self): + # PostgreSQL < 10 doesn't support ICU collations. + if self.is_postgresql_10: + return { + 'non_default': 'sv-x-icu', + 'swedish_ci': 'sv-x-icu', + } + return {} + @cached_property def introspected_field_types(self): return { **super().introspected_field_types, - "PositiveBigIntegerField": "BigIntegerField", - "PositiveIntegerField": "IntegerField", - "PositiveSmallIntegerField": "SmallIntegerField", + 'PositiveBigIntegerField': 'BigIntegerField', + 'PositiveIntegerField': 'IntegerField', + 'PositiveSmallIntegerField': 'SmallIntegerField', } + @cached_property + def is_postgresql_10(self): + return self.connection.pg_version >= 100000 + @cached_property def is_postgresql_11(self): return self.connection.pg_version >= 110000 @@ -91,9 +100,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): def is_postgresql_13(self): return self.connection.pg_version >= 130000 - has_websearch_to_tsquery = property(operator.attrgetter("is_postgresql_11")) - supports_covering_indexes = property(operator.attrgetter("is_postgresql_11")) - supports_covering_gist_indexes = property(operator.attrgetter("is_postgresql_12")) - supports_non_deterministic_collations = property( - operator.attrgetter("is_postgresql_12") - ) + has_brin_autosummarize = property(operator.attrgetter('is_postgresql_10')) + has_websearch_to_tsquery = property(operator.attrgetter('is_postgresql_11')) + supports_table_partitions = property(operator.attrgetter('is_postgresql_10')) + supports_covering_indexes = property(operator.attrgetter('is_postgresql_11')) + supports_covering_gist_indexes = property(operator.attrgetter('is_postgresql_12')) + supports_non_deterministic_collations = property(operator.attrgetter('is_postgresql_12')) + supports_alternate_collation_providers = property(operator.attrgetter('is_postgresql_10')) diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/introspection.py b/venv/Lib/site-packages/django/db/backends/postgresql/introspection.py index 6706cc5..a0e49c8 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/introspection.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/introspection.py @@ -1,7 +1,5 @@ from django.db.backends.base.introspection import ( - BaseDatabaseIntrospection, - FieldInfo, - TableInfo, + BaseDatabaseIntrospection, FieldInfo, TableInfo, ) from django.db.models import Index @@ -9,66 +7,55 @@ from django.db.models import Index class DatabaseIntrospection(BaseDatabaseIntrospection): # Maps type codes to Django Field types. data_types_reverse = { - 16: "BooleanField", - 17: "BinaryField", - 20: "BigIntegerField", - 21: "SmallIntegerField", - 23: "IntegerField", - 25: "TextField", - 700: "FloatField", - 701: "FloatField", - 869: "GenericIPAddressField", - 1042: "CharField", # blank-padded - 1043: "CharField", - 1082: "DateField", - 1083: "TimeField", - 1114: "DateTimeField", - 1184: "DateTimeField", - 1186: "DurationField", - 1266: "TimeField", - 1700: "DecimalField", - 2950: "UUIDField", - 3802: "JSONField", + 16: 'BooleanField', + 17: 'BinaryField', + 20: 'BigIntegerField', + 21: 'SmallIntegerField', + 23: 'IntegerField', + 25: 'TextField', + 700: 'FloatField', + 701: 'FloatField', + 869: 'GenericIPAddressField', + 1042: 'CharField', # blank-padded + 1043: 'CharField', + 1082: 'DateField', + 1083: 'TimeField', + 1114: 'DateTimeField', + 1184: 'DateTimeField', + 1186: 'DurationField', + 1266: 'TimeField', + 1700: 'DecimalField', + 2950: 'UUIDField', + 3802: 'JSONField', } # A hook for subclasses. - index_default_access_method = "btree" + index_default_access_method = 'btree' ignored_tables = [] def get_field_type(self, data_type, description): field_type = super().get_field_type(data_type, description) - if description.default and "nextval" in description.default: - if field_type == "IntegerField": - return "AutoField" - elif field_type == "BigIntegerField": - return "BigAutoField" - elif field_type == "SmallIntegerField": - return "SmallAutoField" + if description.default and 'nextval' in description.default: + if field_type == 'IntegerField': + return 'AutoField' + elif field_type == 'BigIntegerField': + return 'BigAutoField' + elif field_type == 'SmallIntegerField': + return 'SmallAutoField' return field_type def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" - cursor.execute( - """ - SELECT - c.relname, - CASE - WHEN c.relispartition THEN 'p' - WHEN c.relkind IN ('m', 'v') THEN 'v' - ELSE 't' - END + cursor.execute(""" + SELECT c.relname, + CASE WHEN {} THEN 'p' WHEN c.relkind IN ('m', 'v') THEN 'v' ELSE 't' END FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('f', 'm', 'p', 'r', 'v') AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_catalog.pg_table_is_visible(c.oid) - """ - ) - return [ - TableInfo(*row) - for row in cursor.fetchall() - if row[0] not in self.ignored_tables - ] + """.format('c.relispartition' if self.connection.features.supports_table_partitions else 'FALSE')) + return [TableInfo(*row) for row in cursor.fetchall() if row[0] not in self.ignored_tables] def get_table_description(self, cursor, table_name): """ @@ -78,8 +65,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Query the pg_catalog tables as cursor.description does not reliably # return the nullable property and information_schema.columns does not # contain details of materialized views. - cursor.execute( - """ + cursor.execute(""" SELECT a.attname AS column_name, NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS is_nullable, @@ -95,13 +81,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): AND c.relname = %s AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_catalog.pg_table_is_visible(c.oid) - """, - [table_name], - ) + """, [table_name]) field_map = {line[0]: line[1:] for line in cursor.fetchall()} - cursor.execute( - "SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name) - ) + cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) return [ FieldInfo( line.name, @@ -116,30 +98,21 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ] def get_sequences(self, cursor, table_name, table_fields=()): - cursor.execute( - """ + cursor.execute(""" SELECT s.relname as sequence_name, col.attname FROM pg_class s JOIN pg_namespace sn ON sn.oid = s.relnamespace - JOIN - pg_depend d ON d.refobjid = s.oid - AND d.refclassid = 'pg_class'::regclass - JOIN - pg_attrdef ad ON ad.oid = d.objid - AND d.classid = 'pg_attrdef'::regclass - JOIN - pg_attribute col ON col.attrelid = ad.adrelid - AND col.attnum = ad.adnum + JOIN pg_depend d ON d.refobjid = s.oid AND d.refclassid = 'pg_class'::regclass + JOIN pg_attrdef ad ON ad.oid = d.objid AND d.classid = 'pg_attrdef'::regclass + JOIN pg_attribute col ON col.attrelid = ad.adrelid AND col.attnum = ad.adnum JOIN pg_class tbl ON tbl.oid = ad.adrelid WHERE s.relkind = 'S' AND d.deptype in ('a', 'n') AND pg_catalog.pg_table_is_visible(tbl.oid) AND tbl.relname = %s - """, - [table_name], - ) + """, [table_name]) return [ - {"name": row[0], "table": table_name, "column": row[1]} + {'name': row[0], 'table': table_name, 'column': row[1]} for row in cursor.fetchall() ] @@ -148,29 +121,22 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): Return a dictionary of {field_name: (field_name_other_table, other_table)} representing all relationships to the given table. """ - return { - row[0]: (row[2], row[1]) for row in self.get_key_columns(cursor, table_name) - } + return {row[0]: (row[2], row[1]) for row in self.get_key_columns(cursor, table_name)} def get_key_columns(self, cursor, table_name): - cursor.execute( - """ + cursor.execute(""" SELECT a1.attname, c2.relname, a2.attname FROM pg_constraint con LEFT JOIN pg_class c1 ON con.conrelid = c1.oid LEFT JOIN pg_class c2 ON con.confrelid = c2.oid - LEFT JOIN - pg_attribute a1 ON c1.oid = a1.attrelid AND a1.attnum = con.conkey[1] - LEFT JOIN - pg_attribute a2 ON c2.oid = a2.attrelid AND a2.attnum = con.confkey[1] + LEFT JOIN pg_attribute a1 ON c1.oid = a1.attrelid AND a1.attnum = con.conkey[1] + LEFT JOIN pg_attribute a2 ON c2.oid = a2.attrelid AND a2.attnum = con.confkey[1] WHERE c1.relname = %s AND con.contype = 'f' AND c1.relnamespace = c2.relnamespace AND pg_catalog.pg_table_is_visible(c1.oid) - """, - [table_name], - ) + """, [table_name]) return cursor.fetchall() def get_constraints(self, cursor, table_name): @@ -183,8 +149,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Loop over the key table, collecting things as constraints. The column # array must return column names in the same order in which they were # created. - cursor.execute( - """ + cursor.execute(""" SELECT c.conname, array( @@ -203,9 +168,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): FROM pg_constraint AS c JOIN pg_class AS cl ON c.conrelid = cl.oid WHERE cl.relname = %s AND pg_catalog.pg_table_is_visible(cl.oid) - """, - [table_name], - ) + """, [table_name]) for constraint, columns, kind, used_cols, options in cursor.fetchall(): constraints[constraint] = { "columns": columns, @@ -218,17 +181,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "options": options, } # Now get indexes - cursor.execute( - """ + cursor.execute(""" SELECT - indexname, - array_agg(attname ORDER BY arridx), - indisunique, - indisprimary, - array_agg(ordering ORDER BY arridx), - amname, - exprdef, - s2.attoptions + indexname, array_agg(attname ORDER BY arridx), indisunique, indisprimary, + array_agg(ordering ORDER BY arridx), amname, exprdef, s2.attoptions FROM ( SELECT c2.relname as indexname, idx.*, attr.attname, am.amname, @@ -245,40 +201,23 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): c2.reloptions as attoptions FROM ( SELECT * - FROM - pg_index i, - unnest(i.indkey, i.indoption) - WITH ORDINALITY koi(key, option, arridx) + FROM pg_index i, unnest(i.indkey, i.indoption) WITH ORDINALITY koi(key, option, arridx) ) idx LEFT JOIN pg_class c ON idx.indrelid = c.oid LEFT JOIN pg_class c2 ON idx.indexrelid = c2.oid LEFT JOIN pg_am am ON c2.relam = am.oid - LEFT JOIN - pg_attribute attr ON attr.attrelid = c.oid AND attr.attnum = idx.key + LEFT JOIN pg_attribute attr ON attr.attrelid = c.oid AND attr.attnum = idx.key WHERE c.relname = %s AND pg_catalog.pg_table_is_visible(c.oid) ) s2 GROUP BY indexname, indisunique, indisprimary, amname, exprdef, attoptions; - """, - [self.index_default_access_method, table_name], - ) - for ( - index, - columns, - unique, - primary, - orders, - type_, - definition, - options, - ) in cursor.fetchall(): + """, [self.index_default_access_method, table_name]) + for index, columns, unique, primary, orders, type_, definition, options in cursor.fetchall(): if index not in constraints: basic_index = ( - type_ == self.index_default_access_method - and + type_ == self.index_default_access_method and # '_btree' references # django.contrib.postgres.indexes.BTreeIndex.suffix. - not index.endswith("_btree") - and options is None + not index.endswith('_btree') and options is None ) constraints[index] = { "columns": columns if columns != [None] else [], diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/operations.py b/venv/Lib/site-packages/django/db/backends/postgresql/operations.py index f2d4510..8d19872 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/operations.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/operations.py @@ -2,38 +2,20 @@ from psycopg2.extras import Inet from django.conf import settings from django.db.backends.base.operations import BaseDatabaseOperations -from django.db.backends.utils import split_tzname_delta class DatabaseOperations(BaseDatabaseOperations): - cast_char_field_without_max_length = "varchar" - explain_prefix = "EXPLAIN" - explain_options = frozenset( - [ - "ANALYZE", - "BUFFERS", - "COSTS", - "SETTINGS", - "SUMMARY", - "TIMING", - "VERBOSE", - "WAL", - ] - ) + cast_char_field_without_max_length = 'varchar' + explain_prefix = 'EXPLAIN' cast_data_types = { - "AutoField": "integer", - "BigAutoField": "bigint", - "SmallAutoField": "smallint", + 'AutoField': 'integer', + 'BigAutoField': 'bigint', + 'SmallAutoField': 'smallint', } def unification_cast_sql(self, output_field): internal_type = output_field.get_internal_type() - if internal_type in ( - "GenericIPAddressField", - "IPAddressField", - "TimeField", - "UUIDField", - ): + if internal_type in ("GenericIPAddressField", "IPAddressField", "TimeField", "UUIDField"): # PostgreSQL will resolve a union as type 'text' if input types are # 'unknown'. # https://www.postgresql.org/docs/current/typeconv-union-case.html @@ -41,19 +23,17 @@ class DatabaseOperations(BaseDatabaseOperations): # PostgreSQL configuration so we need to explicitly cast them. # We must also remove components of the type within brackets: # varchar(255) -> varchar. - return ( - "CAST(%%s AS %s)" % output_field.db_type(self.connection).split("(")[0] - ) - return "%s" + return 'CAST(%%s AS %s)' % output_field.db_type(self.connection).split('(')[0] + return '%s' def date_extract_sql(self, lookup_type, field_name): # https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT - if lookup_type == "week_day": + if lookup_type == 'week_day': # For consistency across backends, we return Sunday=1, Saturday=7. return "EXTRACT('dow' FROM %s) + 1" % field_name - elif lookup_type == "iso_week_day": + elif lookup_type == 'iso_week_day': return "EXTRACT('isodow' FROM %s)" % field_name - elif lookup_type == "iso_year": + elif lookup_type == 'iso_year': return "EXTRACT('isoyear' FROM %s)" % field_name else: return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) @@ -64,27 +44,24 @@ class DatabaseOperations(BaseDatabaseOperations): return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) def _prepare_tzname_delta(self, tzname): - tzname, sign, offset = split_tzname_delta(tzname) - if offset: - sign = "-" if sign == "+" else "+" - return f"{tzname}{sign}{offset}" + if '+' in tzname: + return tzname.replace('+', '-') + elif '-' in tzname: + return tzname.replace('-', '+') return tzname def _convert_field_to_tz(self, field_name, tzname): if tzname and settings.USE_TZ: - field_name = "%s AT TIME ZONE '%s'" % ( - field_name, - self._prepare_tzname_delta(tzname), - ) + field_name = "%s AT TIME ZONE '%s'" % (field_name, self._prepare_tzname_delta(tzname)) return field_name def datetime_cast_date_sql(self, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - return "(%s)::date" % field_name + return '(%s)::date' % field_name def datetime_cast_time_sql(self, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) - return "(%s)::time" % field_name + return '(%s)::time' % field_name def datetime_extract_sql(self, lookup_type, field_name, tzname): field_name = self._convert_field_to_tz(field_name, tzname) @@ -110,30 +87,21 @@ class DatabaseOperations(BaseDatabaseOperations): return cursor.fetchall() def lookup_cast(self, lookup_type, internal_type=None): - lookup = "%s" + lookup = '%s' # Cast text lookups to text to allow things like filter(x__contains=4) - if lookup_type in ( - "iexact", - "contains", - "icontains", - "startswith", - "istartswith", - "endswith", - "iendswith", - "regex", - "iregex", - ): - if internal_type in ("IPAddressField", "GenericIPAddressField"): + if lookup_type in ('iexact', 'contains', 'icontains', 'startswith', + 'istartswith', 'endswith', 'iendswith', 'regex', 'iregex'): + if internal_type in ('IPAddressField', 'GenericIPAddressField'): lookup = "HOST(%s)" - elif internal_type in ("CICharField", "CIEmailField", "CITextField"): - lookup = "%s::citext" + elif internal_type in ('CICharField', 'CIEmailField', 'CITextField'): + lookup = '%s::citext' else: lookup = "%s::text" # Use UPPER(x) for case-insensitive lookups; it's faster. - if lookup_type in ("iexact", "icontains", "istartswith", "iendswith"): - lookup = "UPPER(%s)" % lookup + if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'): + lookup = 'UPPER(%s)' % lookup return lookup @@ -158,32 +126,29 @@ class DatabaseOperations(BaseDatabaseOperations): # Perform a single SQL 'TRUNCATE x, y, z...;' statement. It allows us # to truncate tables referenced by a foreign key in any other table. sql_parts = [ - style.SQL_KEYWORD("TRUNCATE"), - ", ".join(style.SQL_FIELD(self.quote_name(table)) for table in tables), + style.SQL_KEYWORD('TRUNCATE'), + ', '.join(style.SQL_FIELD(self.quote_name(table)) for table in tables), ] if reset_sequences: - sql_parts.append(style.SQL_KEYWORD("RESTART IDENTITY")) + sql_parts.append(style.SQL_KEYWORD('RESTART IDENTITY')) if allow_cascade: - sql_parts.append(style.SQL_KEYWORD("CASCADE")) - return ["%s;" % " ".join(sql_parts)] + sql_parts.append(style.SQL_KEYWORD('CASCADE')) + return ['%s;' % ' '.join(sql_parts)] def sequence_reset_by_name_sql(self, style, sequences): # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements # to reset sequence indices sql = [] for sequence_info in sequences: - table_name = sequence_info["table"] + table_name = sequence_info['table'] # 'id' will be the case if it's an m2m using an autogenerated # intermediate table (see BaseDatabaseIntrospection.sequence_list). - column_name = sequence_info["column"] or "id" - sql.append( - "%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" - % ( - style.SQL_KEYWORD("SELECT"), - style.SQL_TABLE(self.quote_name(table_name)), - style.SQL_FIELD(column_name), - ) - ) + column_name = sequence_info['column'] or 'id' + sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % ( + style.SQL_KEYWORD('SELECT'), + style.SQL_TABLE(self.quote_name(table_name)), + style.SQL_FIELD(column_name), + )) return sql def tablespace_sql(self, tablespace, inline=False): @@ -194,36 +159,31 @@ class DatabaseOperations(BaseDatabaseOperations): def sequence_reset_sql(self, style, model_list): from django.db import models - output = [] qn = self.quote_name for model in model_list: - # Use `coalesce` to set the sequence for each model to the max pk - # value if there are records, or 1 if there are none. Set the - # `is_called` property (the third argument to `setval`) to true if - # there are records (as the max pk value is already in use), - # otherwise set it to false. Use pg_get_serial_sequence to get the - # underlying sequence name from the table name and column name. + # Use `coalesce` to set the sequence for each model to the max pk value if there are records, + # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true + # if there are records (as the max pk value is already in use), otherwise set it to false. + # Use pg_get_serial_sequence to get the underlying sequence name from the table name + # and column name (available since PostgreSQL 8) for f in model._meta.local_fields: if isinstance(f, models.AutoField): output.append( "%s setval(pg_get_serial_sequence('%s','%s'), " - "coalesce(max(%s), 1), max(%s) %s null) %s %s;" - % ( - style.SQL_KEYWORD("SELECT"), + "coalesce(max(%s), 1), max(%s) %s null) %s %s;" % ( + style.SQL_KEYWORD('SELECT'), style.SQL_TABLE(qn(model._meta.db_table)), style.SQL_FIELD(f.column), style.SQL_FIELD(qn(f.column)), style.SQL_FIELD(qn(f.column)), - style.SQL_KEYWORD("IS NOT"), - style.SQL_KEYWORD("FROM"), + style.SQL_KEYWORD('IS NOT'), + style.SQL_KEYWORD('FROM'), style.SQL_TABLE(qn(model._meta.db_table)), ) ) - # Only one AutoField is allowed per model, so don't bother - # continuing. - break + break # Only one AutoField is allowed per model, so don't bother continuing. return output def prep_for_iexact_query(self, x): @@ -245,9 +205,9 @@ class DatabaseOperations(BaseDatabaseOperations): def distinct_sql(self, fields, params): if fields: params = [param for param_list in params for param in param_list] - return (["DISTINCT ON (%s)" % ", ".join(fields)], params) + return (['DISTINCT ON (%s)' % ', '.join(fields)], params) else: - return ["DISTINCT"], [] + return ['DISTINCT'], [] def last_executed_query(self, cursor, sql, params): # https://www.psycopg.org/docs/cursor.html#cursor.query @@ -258,16 +218,14 @@ class DatabaseOperations(BaseDatabaseOperations): def return_insert_columns(self, fields): if not fields: - return "", () + return '', () columns = [ - "%s.%s" - % ( + '%s.%s' % ( self.quote_name(field.model._meta.db_table), self.quote_name(field.column), - ) - for field in fields + ) for field in fields ] - return "RETURNING %s" % ", ".join(columns), () + return 'RETURNING %s' % ', '.join(columns), () def bulk_insert_sql(self, fields, placeholder_rows): placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) @@ -292,7 +250,7 @@ class DatabaseOperations(BaseDatabaseOperations): return None def subtract_temporals(self, internal_type, lhs, rhs): - if internal_type == "DateField": + if internal_type == 'DateField': lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs params = (*lhs_params, *rhs_params) @@ -300,27 +258,18 @@ class DatabaseOperations(BaseDatabaseOperations): return super().subtract_temporals(internal_type, lhs, rhs) def explain_query_prefix(self, format=None, **options): + prefix = super().explain_query_prefix(format) extra = {} - # Normalize options. - if options: - options = { - name.upper(): "true" if value else "false" - for name, value in options.items() - } - for valid_option in self.explain_options: - value = options.pop(valid_option, None) - if value is not None: - extra[valid_option.upper()] = value - prefix = super().explain_query_prefix(format, **options) if format: - extra["FORMAT"] = format + extra['FORMAT'] = format + if options: + extra.update({ + name.upper(): 'true' if value else 'false' + for name, value in options.items() + }) if extra: - prefix += " (%s)" % ", ".join("%s %s" % i for i in extra.items()) + prefix += ' (%s)' % ', '.join('%s %s' % i for i in extra.items()) return prefix def ignore_conflicts_suffix_sql(self, ignore_conflicts=None): - return ( - "ON CONFLICT DO NOTHING" - if ignore_conflicts - else super().ignore_conflicts_suffix_sql(ignore_conflicts) - ) + return 'ON CONFLICT DO NOTHING' if ignore_conflicts else super().ignore_conflicts_suffix_sql(ignore_conflicts) diff --git a/venv/Lib/site-packages/django/db/backends/postgresql/schema.py b/venv/Lib/site-packages/django/db/backends/postgresql/schema.py index 73e2749..f3b5bae 100644 --- a/venv/Lib/site-packages/django/db/backends/postgresql/schema.py +++ b/venv/Lib/site-packages/django/db/backends/postgresql/schema.py @@ -9,18 +9,16 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_create_sequence = "CREATE SEQUENCE %(sequence)s" sql_delete_sequence = "DROP SEQUENCE IF EXISTS %(sequence)s CASCADE" - sql_set_sequence_max = ( - "SELECT setval('%(sequence)s', MAX(%(column)s)) FROM %(table)s" - ) - sql_set_sequence_owner = "ALTER SEQUENCE %(sequence)s OWNED BY %(table)s.%(column)s" + sql_set_sequence_max = "SELECT setval('%(sequence)s', MAX(%(column)s)) FROM %(table)s" + sql_set_sequence_owner = 'ALTER SEQUENCE %(sequence)s OWNED BY %(table)s.%(column)s' sql_create_index = ( - "CREATE INDEX %(name)s ON %(table)s%(using)s " - "(%(columns)s)%(include)s%(extra)s%(condition)s" + 'CREATE INDEX %(name)s ON %(table)s%(using)s ' + '(%(columns)s)%(include)s%(extra)s%(condition)s' ) sql_create_index_concurrently = ( - "CREATE INDEX CONCURRENTLY %(name)s ON %(table)s%(using)s " - "(%(columns)s)%(include)s%(extra)s%(condition)s" + 'CREATE INDEX CONCURRENTLY %(name)s ON %(table)s%(using)s ' + '(%(columns)s)%(include)s%(extra)s%(condition)s' ) sql_delete_index = "DROP INDEX IF EXISTS %(name)s" sql_delete_index_concurrently = "DROP INDEX CONCURRENTLY IF EXISTS %(name)s" @@ -28,23 +26,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # Setting the constraint to IMMEDIATE to allow changing data in the same # transaction. sql_create_column_inline_fk = ( - "CONSTRAINT %(name)s REFERENCES %(to_table)s(%(to_column)s)%(deferrable)s" - "; SET CONSTRAINTS %(namespace)s%(name)s IMMEDIATE" + 'CONSTRAINT %(name)s REFERENCES %(to_table)s(%(to_column)s)%(deferrable)s' + '; SET CONSTRAINTS %(namespace)s%(name)s IMMEDIATE' ) # Setting the constraint to IMMEDIATE runs any deferred checks to allow # dropping it in the same transaction. - sql_delete_fk = ( - "SET CONSTRAINTS %(name)s IMMEDIATE; " - "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" - ) - sql_delete_procedure = "DROP FUNCTION %(procedure)s(%(param_types)s)" + sql_delete_fk = "SET CONSTRAINTS %(name)s IMMEDIATE; ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" + + sql_delete_procedure = 'DROP FUNCTION %(procedure)s(%(param_types)s)' def quote_value(self, value): if isinstance(value, str): - value = value.replace("%", "%%") + value = value.replace('%', '%%') adapted = psycopg2.extensions.adapt(value) - if hasattr(adapted, "encoding"): - adapted.encoding = "utf8" + if hasattr(adapted, 'encoding'): + adapted.encoding = 'utf8' # getquoted() returns a quoted bytestring of the adapted value. return adapted.getquoted().decode() @@ -65,7 +61,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def _field_base_data_types(self, field): # Yield base data types for array fields. - if field.base_field.get_internal_type() == "ArrayField": + if field.base_field.get_internal_type() == 'ArrayField': yield from self._field_base_data_types(field.base_field) else: yield self._field_data_type(field.base_field) @@ -84,52 +80,45 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # # The same doesn't apply to array fields such as varchar[size] # and text[size], so skip them. - if "[" in db_type: + if '[' in db_type: return None - if db_type.startswith("varchar"): + if db_type.startswith('varchar'): return self._create_index_sql( model, fields=[field], - suffix="_like", - opclasses=["varchar_pattern_ops"], + suffix='_like', + opclasses=['varchar_pattern_ops'], ) - elif db_type.startswith("text"): + elif db_type.startswith('text'): return self._create_index_sql( model, fields=[field], - suffix="_like", - opclasses=["text_pattern_ops"], + suffix='_like', + opclasses=['text_pattern_ops'], ) return None def _alter_column_type_sql(self, model, old_field, new_field, new_type): - self.sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s" + self.sql_alter_column_type = 'ALTER COLUMN %(column)s TYPE %(type)s' # Cast when data type changed. - using_sql = " USING %(column)s::%(type)s" + using_sql = ' USING %(column)s::%(type)s' new_internal_type = new_field.get_internal_type() old_internal_type = old_field.get_internal_type() - if new_internal_type == "ArrayField" and new_internal_type == old_internal_type: + if new_internal_type == 'ArrayField' and new_internal_type == old_internal_type: # Compare base data types for array fields. - if list(self._field_base_data_types(old_field)) != list( - self._field_base_data_types(new_field) - ): + if list(self._field_base_data_types(old_field)) != list(self._field_base_data_types(new_field)): self.sql_alter_column_type += using_sql elif self._field_data_type(old_field) != self._field_data_type(new_field): self.sql_alter_column_type += using_sql # Make ALTER TYPE with SERIAL make sense. table = strip_quotes(model._meta.db_table) - serial_fields_map = { - "bigserial": "bigint", - "serial": "integer", - "smallserial": "smallint", - } + serial_fields_map = {'bigserial': 'bigint', 'serial': 'integer', 'smallserial': 'smallint'} if new_type.lower() in serial_fields_map: column = strip_quotes(new_field.column) sequence_name = "%s_%s_seq" % (table, column) return ( ( - self.sql_alter_column_type - % { + self.sql_alter_column_type % { "column": self.quote_name(column), "type": serial_fields_map[new_type.lower()], }, @@ -137,35 +126,29 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): ), [ ( - self.sql_delete_sequence - % { + self.sql_delete_sequence % { "sequence": self.quote_name(sequence_name), }, [], ), ( - self.sql_create_sequence - % { + self.sql_create_sequence % { "sequence": self.quote_name(sequence_name), }, [], ), ( - self.sql_alter_column - % { + self.sql_alter_column % { "table": self.quote_name(table), - "changes": self.sql_alter_column_default - % { + "changes": self.sql_alter_column_default % { "column": self.quote_name(column), - "default": "nextval('%s')" - % self.quote_name(sequence_name), - }, + "default": "nextval('%s')" % self.quote_name(sequence_name), + } }, [], ), ( - self.sql_set_sequence_max - % { + self.sql_set_sequence_max % { "table": self.quote_name(table), "column": self.quote_name(column), "sequence": self.quote_name(sequence_name), @@ -173,31 +156,24 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): [], ), ( - self.sql_set_sequence_owner - % { - "table": self.quote_name(table), - "column": self.quote_name(column), - "sequence": self.quote_name(sequence_name), + self.sql_set_sequence_owner % { + 'table': self.quote_name(table), + 'column': self.quote_name(column), + 'sequence': self.quote_name(sequence_name), }, [], ), ], ) - elif ( - old_field.db_parameters(connection=self.connection)["type"] - in serial_fields_map - ): + elif old_field.db_parameters(connection=self.connection)['type'] in serial_fields_map: # Drop the sequence if migrating away from AutoField. column = strip_quotes(new_field.column) - sequence_name = "%s_%s_seq" % (table, column) - fragment, _ = super()._alter_column_type_sql( - model, old_field, new_field, new_type - ) + sequence_name = '%s_%s_seq' % (table, column) + fragment, _ = super()._alter_column_type_sql(model, old_field, new_field, new_type) return fragment, [ ( - self.sql_delete_sequence - % { - "sequence": self.quote_name(sequence_name), + self.sql_delete_sequence % { + 'sequence': self.quote_name(sequence_name), }, [], ), @@ -205,114 +181,58 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): else: return super()._alter_column_type_sql(model, old_field, new_field, new_type) - def _alter_field( - self, - model, - old_field, - new_field, - old_type, - new_type, - old_db_params, - new_db_params, - strict=False, - ): + def _alter_field(self, model, old_field, new_field, old_type, new_type, + old_db_params, new_db_params, strict=False): # Drop indexes on varchar/text/citext columns that are changing to a # different type. if (old_field.db_index or old_field.unique) and ( - (old_type.startswith("varchar") and not new_type.startswith("varchar")) - or (old_type.startswith("text") and not new_type.startswith("text")) - or (old_type.startswith("citext") and not new_type.startswith("citext")) + (old_type.startswith('varchar') and not new_type.startswith('varchar')) or + (old_type.startswith('text') and not new_type.startswith('text')) or + (old_type.startswith('citext') and not new_type.startswith('citext')) ): - index_name = self._create_index_name( - model._meta.db_table, [old_field.column], suffix="_like" - ) + index_name = self._create_index_name(model._meta.db_table, [old_field.column], suffix='_like') self.execute(self._delete_index_sql(model, index_name)) super()._alter_field( - model, - old_field, - new_field, - old_type, - new_type, - old_db_params, - new_db_params, - strict, + model, old_field, new_field, old_type, new_type, old_db_params, + new_db_params, strict, ) # Added an index? Create any PostgreSQL-specific indexes. - if (not (old_field.db_index or old_field.unique) and new_field.db_index) or ( - not old_field.unique and new_field.unique - ): + if ((not (old_field.db_index or old_field.unique) and new_field.db_index) or + (not old_field.unique and new_field.unique)): like_index_statement = self._create_like_index_sql(model, new_field) if like_index_statement is not None: self.execute(like_index_statement) # Removed an index? Drop any PostgreSQL-specific indexes. if old_field.unique and not (new_field.db_index or new_field.unique): - index_to_remove = self._create_index_name( - model._meta.db_table, [old_field.column], suffix="_like" - ) + index_to_remove = self._create_index_name(model._meta.db_table, [old_field.column], suffix='_like') self.execute(self._delete_index_sql(model, index_to_remove)) def _index_columns(self, table, columns, col_suffixes, opclasses): if opclasses: - return IndexColumns( - table, - columns, - self.quote_name, - col_suffixes=col_suffixes, - opclasses=opclasses, - ) + return IndexColumns(table, columns, self.quote_name, col_suffixes=col_suffixes, opclasses=opclasses) return super()._index_columns(table, columns, col_suffixes, opclasses) def add_index(self, model, index, concurrently=False): - self.execute( - index.create_sql(model, self, concurrently=concurrently), params=None - ) + self.execute(index.create_sql(model, self, concurrently=concurrently), params=None) def remove_index(self, model, index, concurrently=False): self.execute(index.remove_sql(model, self, concurrently=concurrently)) def _delete_index_sql(self, model, name, sql=None, concurrently=False): - sql = ( - self.sql_delete_index_concurrently - if concurrently - else self.sql_delete_index - ) + sql = self.sql_delete_index_concurrently if concurrently else self.sql_delete_index return super()._delete_index_sql(model, name, sql) def _create_index_sql( - self, - model, - *, - fields=None, - name=None, - suffix="", - using="", - db_tablespace=None, - col_suffixes=(), - sql=None, - opclasses=(), - condition=None, - concurrently=False, - include=None, - expressions=None, + self, model, *, fields=None, name=None, suffix='', using='', + db_tablespace=None, col_suffixes=(), sql=None, opclasses=(), + condition=None, concurrently=False, include=None, expressions=None, ): - sql = ( - self.sql_create_index - if not concurrently - else self.sql_create_index_concurrently - ) + sql = self.sql_create_index if not concurrently else self.sql_create_index_concurrently return super()._create_index_sql( - model, - fields=fields, - name=name, - suffix=suffix, - using=using, - db_tablespace=db_tablespace, - col_suffixes=col_suffixes, - sql=sql, - opclasses=opclasses, - condition=condition, - include=include, + model, fields=fields, name=name, suffix=suffix, using=using, + db_tablespace=db_tablespace, col_suffixes=col_suffixes, sql=sql, + opclasses=opclasses, condition=condition, include=include, expressions=expressions, ) diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/base.py b/venv/Lib/site-packages/django/db/backends/sqlite3/base.py index 031b715..9ce3208 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/base.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/base.py @@ -14,15 +14,18 @@ import warnings from itertools import chain from sqlite3 import dbapi2 as Database +import pytz + from django.core.exceptions import ImproperlyConfigured from django.db import IntegrityError from django.db.backends import utils as backend_utils -from django.db.backends.base.base import BaseDatabaseWrapper, timezone_constructor +from django.db.backends.base.base import BaseDatabaseWrapper from django.utils import timezone from django.utils.asyncio import async_unsafe from django.utils.dateparse import parse_datetime, parse_time from django.utils.duration import duration_microseconds from django.utils.regex_helper import _lazy_re_compile +from django.utils.version import PY38 from .client import DatabaseClient from .creation import DatabaseCreation @@ -46,11 +49,9 @@ def none_guard(func): are NULL. This decorator simplifies the implementation of this for the custom functions registered below. """ - @functools.wraps(func) def wrapper(*args, **kwargs): return None if None in args else func(*args, **kwargs) - return wrapper @@ -59,19 +60,19 @@ def list_aggregate(function): Return an aggregate class that accumulates values in a list and applies the provided function to the data. """ - return type("ListAggregate", (list,), {"finalize": function, "step": list.append}) + return type('ListAggregate', (list,), {'finalize': function, 'step': list.append}) def check_sqlite_version(): if Database.sqlite_version_info < (3, 9, 0): raise ImproperlyConfigured( - "SQLite 3.9.0 or later is required (found %s)." % Database.sqlite_version + 'SQLite 3.9.0 or later is required (found %s).' % Database.sqlite_version ) check_sqlite_version() -Database.register_converter("bool", b"1".__eq__) +Database.register_converter("bool", b'1'.__eq__) Database.register_converter("time", decoder(parse_time)) Database.register_converter("datetime", decoder(parse_datetime)) Database.register_converter("timestamp", decoder(parse_datetime)) @@ -80,69 +81,70 @@ Database.register_adapter(decimal.Decimal, str) class DatabaseWrapper(BaseDatabaseWrapper): - vendor = "sqlite" - display_name = "SQLite" + vendor = 'sqlite' + display_name = 'SQLite' # SQLite doesn't actually support most of these types, but it "does the right # thing" given more verbose field definitions, so leave them as is so that # schema inspection is more useful. data_types = { - "AutoField": "integer", - "BigAutoField": "integer", - "BinaryField": "BLOB", - "BooleanField": "bool", - "CharField": "varchar(%(max_length)s)", - "DateField": "date", - "DateTimeField": "datetime", - "DecimalField": "decimal", - "DurationField": "bigint", - "FileField": "varchar(%(max_length)s)", - "FilePathField": "varchar(%(max_length)s)", - "FloatField": "real", - "IntegerField": "integer", - "BigIntegerField": "bigint", - "IPAddressField": "char(15)", - "GenericIPAddressField": "char(39)", - "JSONField": "text", - "OneToOneField": "integer", - "PositiveBigIntegerField": "bigint unsigned", - "PositiveIntegerField": "integer unsigned", - "PositiveSmallIntegerField": "smallint unsigned", - "SlugField": "varchar(%(max_length)s)", - "SmallAutoField": "integer", - "SmallIntegerField": "smallint", - "TextField": "text", - "TimeField": "time", - "UUIDField": "char(32)", + 'AutoField': 'integer', + 'BigAutoField': 'integer', + 'BinaryField': 'BLOB', + 'BooleanField': 'bool', + 'CharField': 'varchar(%(max_length)s)', + 'DateField': 'date', + 'DateTimeField': 'datetime', + 'DecimalField': 'decimal', + 'DurationField': 'bigint', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', + 'FloatField': 'real', + 'IntegerField': 'integer', + 'BigIntegerField': 'bigint', + 'IPAddressField': 'char(15)', + 'GenericIPAddressField': 'char(39)', + 'JSONField': 'text', + 'NullBooleanField': 'bool', + 'OneToOneField': 'integer', + 'PositiveBigIntegerField': 'bigint unsigned', + 'PositiveIntegerField': 'integer unsigned', + 'PositiveSmallIntegerField': 'smallint unsigned', + 'SlugField': 'varchar(%(max_length)s)', + 'SmallAutoField': 'integer', + 'SmallIntegerField': 'smallint', + 'TextField': 'text', + 'TimeField': 'time', + 'UUIDField': 'char(32)', } data_type_check_constraints = { - "PositiveBigIntegerField": '"%(column)s" >= 0', - "JSONField": '(JSON_VALID("%(column)s") OR "%(column)s" IS NULL)', - "PositiveIntegerField": '"%(column)s" >= 0', - "PositiveSmallIntegerField": '"%(column)s" >= 0', + 'PositiveBigIntegerField': '"%(column)s" >= 0', + 'JSONField': '(JSON_VALID("%(column)s") OR "%(column)s" IS NULL)', + 'PositiveIntegerField': '"%(column)s" >= 0', + 'PositiveSmallIntegerField': '"%(column)s" >= 0', } data_types_suffix = { - "AutoField": "AUTOINCREMENT", - "BigAutoField": "AUTOINCREMENT", - "SmallAutoField": "AUTOINCREMENT", + 'AutoField': 'AUTOINCREMENT', + 'BigAutoField': 'AUTOINCREMENT', + 'SmallAutoField': 'AUTOINCREMENT', } # SQLite requires LIKE statements to include an ESCAPE clause if the value # being escaped has a percent or underscore in it. # See https://www.sqlite.org/lang_expr.html for an explanation. operators = { - "exact": "= %s", - "iexact": "LIKE %s ESCAPE '\\'", - "contains": "LIKE %s ESCAPE '\\'", - "icontains": "LIKE %s ESCAPE '\\'", - "regex": "REGEXP %s", - "iregex": "REGEXP '(?i)' || %s", - "gt": "> %s", - "gte": ">= %s", - "lt": "< %s", - "lte": "<= %s", - "startswith": "LIKE %s ESCAPE '\\'", - "endswith": "LIKE %s ESCAPE '\\'", - "istartswith": "LIKE %s ESCAPE '\\'", - "iendswith": "LIKE %s ESCAPE '\\'", + 'exact': '= %s', + 'iexact': "LIKE %s ESCAPE '\\'", + 'contains': "LIKE %s ESCAPE '\\'", + 'icontains': "LIKE %s ESCAPE '\\'", + 'regex': 'REGEXP %s', + 'iregex': "REGEXP '(?i)' || %s", + 'gt': '> %s', + 'gte': '>= %s', + 'lt': '< %s', + 'lte': '<= %s', + 'startswith': "LIKE %s ESCAPE '\\'", + 'endswith': "LIKE %s ESCAPE '\\'", + 'istartswith': "LIKE %s ESCAPE '\\'", + 'iendswith': "LIKE %s ESCAPE '\\'", } # The patterns below are used to generate SQL pattern lookup clauses when @@ -155,12 +157,12 @@ class DatabaseWrapper(BaseDatabaseWrapper): # the LIKE operator. pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '\\'), '%%', '\%%'), '_', '\_')" pattern_ops = { - "contains": r"LIKE '%%' || {} || '%%' ESCAPE '\'", - "icontains": r"LIKE '%%' || UPPER({}) || '%%' ESCAPE '\'", - "startswith": r"LIKE {} || '%%' ESCAPE '\'", - "istartswith": r"LIKE UPPER({}) || '%%' ESCAPE '\'", - "endswith": r"LIKE '%%' || {} ESCAPE '\'", - "iendswith": r"LIKE '%%' || UPPER({}) ESCAPE '\'", + 'contains': r"LIKE '%%' || {} || '%%' ESCAPE '\'", + 'icontains': r"LIKE '%%' || UPPER({}) || '%%' ESCAPE '\'", + 'startswith': r"LIKE {} || '%%' ESCAPE '\'", + 'istartswith': r"LIKE UPPER({}) || '%%' ESCAPE '\'", + 'endswith': r"LIKE '%%' || {} ESCAPE '\'", + 'iendswith': r"LIKE '%%' || UPPER({}) ESCAPE '\'", } Database = Database @@ -174,15 +176,16 @@ class DatabaseWrapper(BaseDatabaseWrapper): def get_connection_params(self): settings_dict = self.settings_dict - if not settings_dict["NAME"]: + if not settings_dict['NAME']: raise ImproperlyConfigured( "settings.DATABASES is improperly configured. " - "Please supply the NAME value." - ) + "Please supply the NAME value.") kwargs = { - "database": settings_dict["NAME"], - "detect_types": Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES, - **settings_dict["OPTIONS"], + # TODO: Remove str() when dropping support for PY36. + # https://bugs.python.org/issue33496 + 'database': str(settings_dict['NAME']), + 'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES, + **settings_dict['OPTIONS'], } # Always allow the underlying SQLite connection to be shareable # between multiple threads. The safe-guarding will be handled at a @@ -190,103 +193,78 @@ class DatabaseWrapper(BaseDatabaseWrapper): # property. This is necessary as the shareability is disabled by # default in pysqlite and it cannot be changed once a connection is # opened. - if "check_same_thread" in kwargs and kwargs["check_same_thread"]: + if 'check_same_thread' in kwargs and kwargs['check_same_thread']: warnings.warn( - "The `check_same_thread` option was provided and set to " - "True. It will be overridden with False. Use the " - "`DatabaseWrapper.allow_thread_sharing` property instead " - "for controlling thread shareability.", - RuntimeWarning, + 'The `check_same_thread` option was provided and set to ' + 'True. It will be overridden with False. Use the ' + '`DatabaseWrapper.allow_thread_sharing` property instead ' + 'for controlling thread shareability.', + RuntimeWarning ) - kwargs.update({"check_same_thread": False, "uri": True}) + kwargs.update({'check_same_thread': False, 'uri': True}) return kwargs @async_unsafe def get_new_connection(self, conn_params): conn = Database.connect(**conn_params) - create_deterministic_function = functools.partial( - conn.create_function, - deterministic=True, - ) - create_deterministic_function( - "django_date_extract", 2, _sqlite_datetime_extract - ) - create_deterministic_function("django_date_trunc", 4, _sqlite_date_trunc) - create_deterministic_function( - "django_datetime_cast_date", 3, _sqlite_datetime_cast_date - ) - create_deterministic_function( - "django_datetime_cast_time", 3, _sqlite_datetime_cast_time - ) - create_deterministic_function( - "django_datetime_extract", 4, _sqlite_datetime_extract - ) - create_deterministic_function( - "django_datetime_trunc", 4, _sqlite_datetime_trunc - ) - create_deterministic_function("django_time_extract", 2, _sqlite_time_extract) - create_deterministic_function("django_time_trunc", 4, _sqlite_time_trunc) - create_deterministic_function("django_time_diff", 2, _sqlite_time_diff) - create_deterministic_function( - "django_timestamp_diff", 2, _sqlite_timestamp_diff - ) - create_deterministic_function( - "django_format_dtdelta", 3, _sqlite_format_dtdelta - ) - create_deterministic_function("regexp", 2, _sqlite_regexp) - create_deterministic_function("ACOS", 1, none_guard(math.acos)) - create_deterministic_function("ASIN", 1, none_guard(math.asin)) - create_deterministic_function("ATAN", 1, none_guard(math.atan)) - create_deterministic_function("ATAN2", 2, none_guard(math.atan2)) - create_deterministic_function("BITXOR", 2, none_guard(operator.xor)) - create_deterministic_function("CEILING", 1, none_guard(math.ceil)) - create_deterministic_function("COS", 1, none_guard(math.cos)) - create_deterministic_function("COT", 1, none_guard(lambda x: 1 / math.tan(x))) - create_deterministic_function("DEGREES", 1, none_guard(math.degrees)) - create_deterministic_function("EXP", 1, none_guard(math.exp)) - create_deterministic_function("FLOOR", 1, none_guard(math.floor)) - create_deterministic_function("LN", 1, none_guard(math.log)) - create_deterministic_function("LOG", 2, none_guard(lambda x, y: math.log(y, x))) - create_deterministic_function("LPAD", 3, _sqlite_lpad) - create_deterministic_function( - "MD5", 1, none_guard(lambda x: hashlib.md5(x.encode()).hexdigest()) - ) - create_deterministic_function("MOD", 2, none_guard(math.fmod)) - create_deterministic_function("PI", 0, lambda: math.pi) - create_deterministic_function("POWER", 2, none_guard(operator.pow)) - create_deterministic_function("RADIANS", 1, none_guard(math.radians)) - create_deterministic_function("REPEAT", 2, none_guard(operator.mul)) - create_deterministic_function("REVERSE", 1, none_guard(lambda x: x[::-1])) - create_deterministic_function("RPAD", 3, _sqlite_rpad) - create_deterministic_function( - "SHA1", 1, none_guard(lambda x: hashlib.sha1(x.encode()).hexdigest()) - ) - create_deterministic_function( - "SHA224", 1, none_guard(lambda x: hashlib.sha224(x.encode()).hexdigest()) - ) - create_deterministic_function( - "SHA256", 1, none_guard(lambda x: hashlib.sha256(x.encode()).hexdigest()) - ) - create_deterministic_function( - "SHA384", 1, none_guard(lambda x: hashlib.sha384(x.encode()).hexdigest()) - ) - create_deterministic_function( - "SHA512", 1, none_guard(lambda x: hashlib.sha512(x.encode()).hexdigest()) - ) - create_deterministic_function( - "SIGN", 1, none_guard(lambda x: (x > 0) - (x < 0)) - ) - create_deterministic_function("SIN", 1, none_guard(math.sin)) - create_deterministic_function("SQRT", 1, none_guard(math.sqrt)) - create_deterministic_function("TAN", 1, none_guard(math.tan)) + if PY38: + create_deterministic_function = functools.partial( + conn.create_function, + deterministic=True, + ) + else: + create_deterministic_function = conn.create_function + create_deterministic_function('django_date_extract', 2, _sqlite_datetime_extract) + create_deterministic_function('django_date_trunc', 4, _sqlite_date_trunc) + create_deterministic_function('django_datetime_cast_date', 3, _sqlite_datetime_cast_date) + create_deterministic_function('django_datetime_cast_time', 3, _sqlite_datetime_cast_time) + create_deterministic_function('django_datetime_extract', 4, _sqlite_datetime_extract) + create_deterministic_function('django_datetime_trunc', 4, _sqlite_datetime_trunc) + create_deterministic_function('django_time_extract', 2, _sqlite_time_extract) + create_deterministic_function('django_time_trunc', 4, _sqlite_time_trunc) + create_deterministic_function('django_time_diff', 2, _sqlite_time_diff) + create_deterministic_function('django_timestamp_diff', 2, _sqlite_timestamp_diff) + create_deterministic_function('django_format_dtdelta', 3, _sqlite_format_dtdelta) + create_deterministic_function('regexp', 2, _sqlite_regexp) + create_deterministic_function('ACOS', 1, none_guard(math.acos)) + create_deterministic_function('ASIN', 1, none_guard(math.asin)) + create_deterministic_function('ATAN', 1, none_guard(math.atan)) + create_deterministic_function('ATAN2', 2, none_guard(math.atan2)) + create_deterministic_function('BITXOR', 2, none_guard(operator.xor)) + create_deterministic_function('CEILING', 1, none_guard(math.ceil)) + create_deterministic_function('COS', 1, none_guard(math.cos)) + create_deterministic_function('COT', 1, none_guard(lambda x: 1 / math.tan(x))) + create_deterministic_function('DEGREES', 1, none_guard(math.degrees)) + create_deterministic_function('EXP', 1, none_guard(math.exp)) + create_deterministic_function('FLOOR', 1, none_guard(math.floor)) + create_deterministic_function('LN', 1, none_guard(math.log)) + create_deterministic_function('LOG', 2, none_guard(lambda x, y: math.log(y, x))) + create_deterministic_function('LPAD', 3, _sqlite_lpad) + create_deterministic_function('MD5', 1, none_guard(lambda x: hashlib.md5(x.encode()).hexdigest())) + create_deterministic_function('MOD', 2, none_guard(math.fmod)) + create_deterministic_function('PI', 0, lambda: math.pi) + create_deterministic_function('POWER', 2, none_guard(operator.pow)) + create_deterministic_function('RADIANS', 1, none_guard(math.radians)) + create_deterministic_function('REPEAT', 2, none_guard(operator.mul)) + create_deterministic_function('REVERSE', 1, none_guard(lambda x: x[::-1])) + create_deterministic_function('RPAD', 3, _sqlite_rpad) + create_deterministic_function('SHA1', 1, none_guard(lambda x: hashlib.sha1(x.encode()).hexdigest())) + create_deterministic_function('SHA224', 1, none_guard(lambda x: hashlib.sha224(x.encode()).hexdigest())) + create_deterministic_function('SHA256', 1, none_guard(lambda x: hashlib.sha256(x.encode()).hexdigest())) + create_deterministic_function('SHA384', 1, none_guard(lambda x: hashlib.sha384(x.encode()).hexdigest())) + create_deterministic_function('SHA512', 1, none_guard(lambda x: hashlib.sha512(x.encode()).hexdigest())) + create_deterministic_function('SIGN', 1, none_guard(lambda x: (x > 0) - (x < 0))) + create_deterministic_function('SIN', 1, none_guard(math.sin)) + create_deterministic_function('SQRT', 1, none_guard(math.sqrt)) + create_deterministic_function('TAN', 1, none_guard(math.tan)) # Don't use the built-in RANDOM() function because it returns a value - # in the range [-1 * 2^63, 2^63 - 1] instead of [0, 1). - conn.create_function("RAND", 0, random.random) - conn.create_aggregate("STDDEV_POP", 1, list_aggregate(statistics.pstdev)) - conn.create_aggregate("STDDEV_SAMP", 1, list_aggregate(statistics.stdev)) - conn.create_aggregate("VAR_POP", 1, list_aggregate(statistics.pvariance)) - conn.create_aggregate("VAR_SAMP", 1, list_aggregate(statistics.variance)) - conn.execute("PRAGMA foreign_keys = ON") + # in the range [2^63, 2^63 - 1] instead of [0, 1). + conn.create_function('RAND', 0, random.random) + conn.create_aggregate('STDDEV_POP', 1, list_aggregate(statistics.pstdev)) + conn.create_aggregate('STDDEV_SAMP', 1, list_aggregate(statistics.stdev)) + conn.create_aggregate('VAR_POP', 1, list_aggregate(statistics.pvariance)) + conn.create_aggregate('VAR_SAMP', 1, list_aggregate(statistics.variance)) + conn.execute('PRAGMA foreign_keys = ON') return conn def init_connection_state(self): @@ -318,7 +296,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): else: # sqlite3's internal default is ''. It's different from None. # See Modules/_sqlite/connection.c. - level = "" + level = '' # 'isolation_level' is a misleading API. # SQLite always runs at the SERIALIZABLE isolation level. with self.wrap_database_errors: @@ -326,16 +304,16 @@ class DatabaseWrapper(BaseDatabaseWrapper): def disable_constraint_checking(self): with self.cursor() as cursor: - cursor.execute("PRAGMA foreign_keys = OFF") + cursor.execute('PRAGMA foreign_keys = OFF') # Foreign key constraints cannot be turned off while in a multi- # statement transaction. Fetch the current state of the pragma # to determine if constraints are effectively disabled. - enabled = cursor.execute("PRAGMA foreign_keys").fetchone()[0] + enabled = cursor.execute('PRAGMA foreign_keys').fetchone()[0] return not bool(enabled) def enable_constraint_checking(self): with self.cursor() as cursor: - cursor.execute("PRAGMA foreign_keys = ON") + cursor.execute('PRAGMA foreign_keys = ON') def check_constraints(self, table_names=None): """ @@ -348,32 +326,24 @@ class DatabaseWrapper(BaseDatabaseWrapper): if self.features.supports_pragma_foreign_key_check: with self.cursor() as cursor: if table_names is None: - violations = cursor.execute("PRAGMA foreign_key_check").fetchall() + violations = cursor.execute('PRAGMA foreign_key_check').fetchall() else: violations = chain.from_iterable( cursor.execute( - "PRAGMA foreign_key_check(%s)" + 'PRAGMA foreign_key_check(%s)' % self.ops.quote_name(table_name) ).fetchall() for table_name in table_names ) # See https://www.sqlite.org/pragma.html#pragma_foreign_key_check - for ( - table_name, - rowid, - referenced_table_name, - foreign_key_index, - ) in violations: + for table_name, rowid, referenced_table_name, foreign_key_index in violations: foreign_key = cursor.execute( - "PRAGMA foreign_key_list(%s)" % self.ops.quote_name(table_name) + 'PRAGMA foreign_key_list(%s)' % self.ops.quote_name(table_name) ).fetchall()[foreign_key_index] column_name, referenced_column_name = foreign_key[3:5] - primary_key_column_name = self.introspection.get_primary_key_column( - cursor, table_name - ) + primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name) primary_key_value, bad_value = cursor.execute( - "SELECT %s, %s FROM %s WHERE rowid = %%s" - % ( + 'SELECT %s, %s FROM %s WHERE rowid = %%s' % ( self.ops.quote_name(primary_key_column_name), self.ops.quote_name(column_name), self.ops.quote_name(table_name), @@ -383,15 +353,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): raise IntegrityError( "The row in table '%s' with primary key '%s' has an " "invalid foreign key: %s.%s contains a value '%s' that " - "does not have a corresponding value in %s.%s." - % ( - table_name, - primary_key_value, - table_name, - column_name, - bad_value, - referenced_table_name, - referenced_column_name, + "does not have a corresponding value in %s.%s." % ( + table_name, primary_key_value, table_name, column_name, + bad_value, referenced_table_name, referenced_column_name ) ) else: @@ -399,17 +363,11 @@ class DatabaseWrapper(BaseDatabaseWrapper): if table_names is None: table_names = self.introspection.table_names(cursor) for table_name in table_names: - primary_key_column_name = self.introspection.get_primary_key_column( - cursor, table_name - ) + primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name) if not primary_key_column_name: continue key_columns = self.introspection.get_key_columns(cursor, table_name) - for ( - column_name, - referenced_table_name, - referenced_column_name, - ) in key_columns: + for column_name, referenced_table_name, referenced_column_name in key_columns: cursor.execute( """ SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING @@ -418,29 +376,18 @@ class DatabaseWrapper(BaseDatabaseWrapper): WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL """ % ( - primary_key_column_name, - column_name, - table_name, - referenced_table_name, - column_name, - referenced_column_name, - column_name, - referenced_column_name, + primary_key_column_name, column_name, table_name, + referenced_table_name, column_name, referenced_column_name, + column_name, referenced_column_name, ) ) for bad_row in cursor.fetchall(): raise IntegrityError( "The row in table '%s' with primary key '%s' has an " "invalid foreign key: %s.%s contains a value '%s' that " - "does not have a corresponding value in %s.%s." - % ( - table_name, - bad_row[0], - table_name, - column_name, - bad_row[1], - referenced_table_name, - referenced_column_name, + "does not have a corresponding value in %s.%s." % ( + table_name, bad_row[0], table_name, column_name, + bad_row[1], referenced_table_name, referenced_column_name, ) ) @@ -457,10 +404,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.cursor().execute("BEGIN") def is_in_memory_db(self): - return self.creation.is_in_memory_db(self.settings_dict["NAME"]) + return self.creation.is_in_memory_db(self.settings_dict['NAME']) -FORMAT_QMARK_REGEX = _lazy_re_compile(r"(?<!%)%s") +FORMAT_QMARK_REGEX = _lazy_re_compile(r'(?<!%)%s') class SQLiteCursorWrapper(Database.Cursor): @@ -469,7 +416,6 @@ class SQLiteCursorWrapper(Database.Cursor): This fixes it -- but note that if you want to use a literal "%s" in a query, you'll need to use "%%s". """ - def execute(self, query, params=None): if params is None: return Database.Cursor.execute(self, query) @@ -481,7 +427,7 @@ class SQLiteCursorWrapper(Database.Cursor): return Database.Cursor.executemany(self, query, param_list) def convert_query(self, query): - return FORMAT_QMARK_REGEX.sub("?", query).replace("%%", "%") + return FORMAT_QMARK_REGEX.sub('?', query).replace('%%', '%') def _sqlite_datetime_parse(dt, tzname=None, conn_tzname=None): @@ -492,14 +438,17 @@ def _sqlite_datetime_parse(dt, tzname=None, conn_tzname=None): except (TypeError, ValueError): return None if conn_tzname: - dt = dt.replace(tzinfo=timezone_constructor(conn_tzname)) + dt = dt.replace(tzinfo=pytz.timezone(conn_tzname)) if tzname is not None and tzname != conn_tzname: - tzname, sign, offset = backend_utils.split_tzname_delta(tzname) - if offset: - hours, minutes = offset.split(":") - offset_delta = datetime.timedelta(hours=int(hours), minutes=int(minutes)) - dt += offset_delta if sign == "+" else -offset_delta - dt = timezone.localtime(dt, timezone_constructor(tzname)) + sign_index = tzname.find('+') + tzname.find('-') + 1 + if sign_index > -1: + sign = tzname[sign_index] + tzname, offset = tzname.split(sign) + if offset: + hours, minutes = offset.split(':') + offset_delta = datetime.timedelta(hours=int(hours), minutes=int(minutes)) + dt += offset_delta if sign == '+' else -offset_delta + dt = timezone.localtime(dt, pytz.timezone(tzname)) return dt @@ -507,17 +456,17 @@ def _sqlite_date_trunc(lookup_type, dt, tzname, conn_tzname): dt = _sqlite_datetime_parse(dt, tzname, conn_tzname) if dt is None: return None - if lookup_type == "year": + if lookup_type == 'year': return "%i-01-01" % dt.year - elif lookup_type == "quarter": + elif lookup_type == 'quarter': month_in_quarter = dt.month - (dt.month - 1) % 3 - return "%i-%02i-01" % (dt.year, month_in_quarter) - elif lookup_type == "month": + return '%i-%02i-01' % (dt.year, month_in_quarter) + elif lookup_type == 'month': return "%i-%02i-01" % (dt.year, dt.month) - elif lookup_type == "week": + elif lookup_type == 'week': dt = dt - datetime.timedelta(days=dt.weekday()) return "%i-%02i-%02i" % (dt.year, dt.month, dt.day) - elif lookup_type == "day": + elif lookup_type == 'day': return "%i-%02i-%02i" % (dt.year, dt.month, dt.day) @@ -532,11 +481,11 @@ def _sqlite_time_trunc(lookup_type, dt, tzname, conn_tzname): return None else: dt = dt_parsed - if lookup_type == "hour": + if lookup_type == 'hour': return "%02i:00:00" % dt.hour - elif lookup_type == "minute": + elif lookup_type == 'minute': return "%02i:%02i:00" % (dt.hour, dt.minute) - elif lookup_type == "second": + elif lookup_type == 'second': return "%02i:%02i:%02i" % (dt.hour, dt.minute, dt.second) @@ -558,15 +507,15 @@ def _sqlite_datetime_extract(lookup_type, dt, tzname=None, conn_tzname=None): dt = _sqlite_datetime_parse(dt, tzname, conn_tzname) if dt is None: return None - if lookup_type == "week_day": + if lookup_type == 'week_day': return (dt.isoweekday() % 7) + 1 - elif lookup_type == "iso_week_day": + elif lookup_type == 'iso_week_day': return dt.isoweekday() - elif lookup_type == "week": + elif lookup_type == 'week': return dt.isocalendar()[1] - elif lookup_type == "quarter": + elif lookup_type == 'quarter': return math.ceil(dt.month / 3) - elif lookup_type == "iso_year": + elif lookup_type == 'iso_year': return dt.isocalendar()[0] else: return getattr(dt, lookup_type) @@ -576,37 +525,24 @@ def _sqlite_datetime_trunc(lookup_type, dt, tzname, conn_tzname): dt = _sqlite_datetime_parse(dt, tzname, conn_tzname) if dt is None: return None - if lookup_type == "year": + if lookup_type == 'year': return "%i-01-01 00:00:00" % dt.year - elif lookup_type == "quarter": + elif lookup_type == 'quarter': month_in_quarter = dt.month - (dt.month - 1) % 3 - return "%i-%02i-01 00:00:00" % (dt.year, month_in_quarter) - elif lookup_type == "month": + return '%i-%02i-01 00:00:00' % (dt.year, month_in_quarter) + elif lookup_type == 'month': return "%i-%02i-01 00:00:00" % (dt.year, dt.month) - elif lookup_type == "week": + elif lookup_type == 'week': dt = dt - datetime.timedelta(days=dt.weekday()) return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) - elif lookup_type == "day": + elif lookup_type == 'day': return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) - elif lookup_type == "hour": + elif lookup_type == 'hour': return "%i-%02i-%02i %02i:00:00" % (dt.year, dt.month, dt.day, dt.hour) - elif lookup_type == "minute": - return "%i-%02i-%02i %02i:%02i:00" % ( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - ) - elif lookup_type == "second": - return "%i-%02i-%02i %02i:%02i:%02i" % ( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - ) + elif lookup_type == 'minute': + return "%i-%02i-%02i %02i:%02i:00" % (dt.year, dt.month, dt.day, dt.hour, dt.minute) + elif lookup_type == 'second': + return "%i-%02i-%02i %02i:%02i:%02i" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) def _sqlite_time_extract(lookup_type, dt): @@ -619,40 +555,25 @@ def _sqlite_time_extract(lookup_type, dt): return getattr(dt, lookup_type) -def _sqlite_prepare_dtdelta_param(conn, param): - if conn in ["+", "-"]: - if isinstance(param, int): - return datetime.timedelta(0, 0, param) - else: - return backend_utils.typecast_timestamp(param) - return param - - @none_guard def _sqlite_format_dtdelta(conn, lhs, rhs): """ LHS and RHS can be either: - An integer number of microseconds - A string representing a datetime - - A scalar value, e.g. float """ - conn = conn.strip() try: - real_lhs = _sqlite_prepare_dtdelta_param(conn, lhs) - real_rhs = _sqlite_prepare_dtdelta_param(conn, rhs) + real_lhs = datetime.timedelta(0, 0, lhs) if isinstance(lhs, int) else backend_utils.typecast_timestamp(lhs) + real_rhs = datetime.timedelta(0, 0, rhs) if isinstance(rhs, int) else backend_utils.typecast_timestamp(rhs) + if conn.strip() == '+': + out = real_lhs + real_rhs + else: + out = real_lhs - real_rhs except (ValueError, TypeError): return None - if conn == "+": - # typecast_timestamp returns a date or a datetime without timezone. - # It will be formatted as "%Y-%m-%d" or "%Y-%m-%d %H:%M:%S[.%f]" - out = str(real_lhs + real_rhs) - elif conn == "-": - out = str(real_lhs - real_rhs) - elif conn == "*": - out = real_lhs * real_rhs - else: - out = real_lhs / real_rhs - return out + # typecast_timestamp returns a date or a datetime without timezone. + # It will be formatted as "%Y-%m-%d" or "%Y-%m-%d %H:%M:%S[.%f]" + return str(out) @none_guard @@ -660,14 +581,14 @@ def _sqlite_time_diff(lhs, rhs): left = backend_utils.typecast_time(lhs) right = backend_utils.typecast_time(rhs) return ( - (left.hour * 60 * 60 * 1000000) - + (left.minute * 60 * 1000000) - + (left.second * 1000000) - + (left.microsecond) - - (right.hour * 60 * 60 * 1000000) - - (right.minute * 60 * 1000000) - - (right.second * 1000000) - - (right.microsecond) + (left.hour * 60 * 60 * 1000000) + + (left.minute * 60 * 1000000) + + (left.second * 1000000) + + (left.microsecond) - + (right.hour * 60 * 60 * 1000000) - + (right.minute * 60 * 1000000) - + (right.second * 1000000) - + (right.microsecond) ) @@ -687,7 +608,7 @@ def _sqlite_regexp(re_pattern, re_string): def _sqlite_lpad(text, length, fill_text): if len(text) >= length: return text[:length] - return (fill_text * length)[: length - len(text)] + text + return (fill_text * length)[:length - len(text)] + text @none_guard diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/client.py b/venv/Lib/site-packages/django/db/backends/sqlite3/client.py index 7cee35d..59a2fe7 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/client.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/client.py @@ -2,9 +2,15 @@ from django.db.backends.base.client import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): - executable_name = "sqlite3" + executable_name = 'sqlite3' @classmethod def settings_to_cmd_args_env(cls, settings_dict, parameters): - args = [cls.executable_name, settings_dict["NAME"], *parameters] + args = [ + cls.executable_name, + # TODO: Remove str() when dropping support for PY37. args + # parameter accepts path-like objects on Windows since Python 3.8. + str(settings_dict['NAME']), + *parameters, + ] return args, None diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/creation.py b/venv/Lib/site-packages/django/db/backends/sqlite3/creation.py index 9d8d4a6..f3bb8dd 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/creation.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/creation.py @@ -7,16 +7,17 @@ from django.db.backends.base.creation import BaseDatabaseCreation class DatabaseCreation(BaseDatabaseCreation): + @staticmethod def is_in_memory_db(database_name): return not isinstance(database_name, Path) and ( - database_name == ":memory:" or "mode=memory" in database_name + database_name == ':memory:' or 'mode=memory' in database_name ) def _get_test_db_name(self): - test_database_name = self.connection.settings_dict["TEST"]["NAME"] or ":memory:" - if test_database_name == ":memory:": - return "file:memorydb_%s?mode=memory&cache=shared" % self.connection.alias + test_database_name = self.connection.settings_dict['TEST']['NAME'] or ':memory:' + if test_database_name == ':memory:': + return 'file:memorydb_%s?mode=memory&cache=shared' % self.connection.alias return test_database_name def _create_test_db(self, verbosity, autoclobber, keepdb=False): @@ -27,39 +28,38 @@ class DatabaseCreation(BaseDatabaseCreation): if not self.is_in_memory_db(test_database_name): # Erase the old test database if verbosity >= 1: - self.log( - "Destroying old test database for alias %s..." - % (self._get_database_display_str(verbosity, test_database_name),) - ) + self.log('Destroying old test database for alias %s...' % ( + self._get_database_display_str(verbosity, test_database_name), + )) if os.access(test_database_name, os.F_OK): if not autoclobber: confirm = input( "Type 'yes' if you would like to try deleting the test " "database '%s', or 'no' to cancel: " % test_database_name ) - if autoclobber or confirm == "yes": + if autoclobber or confirm == 'yes': try: os.remove(test_database_name) except Exception as e: - self.log("Got an error deleting the old test database: %s" % e) + self.log('Got an error deleting the old test database: %s' % e) sys.exit(2) else: - self.log("Tests cancelled.") + self.log('Tests cancelled.') sys.exit(1) return test_database_name def get_test_db_clone_settings(self, suffix): orig_settings_dict = self.connection.settings_dict - source_database_name = orig_settings_dict["NAME"] + source_database_name = orig_settings_dict['NAME'] if self.is_in_memory_db(source_database_name): return orig_settings_dict else: - root, ext = os.path.splitext(orig_settings_dict["NAME"]) - return {**orig_settings_dict, "NAME": "{}_{}{}".format(root, suffix, ext)} + root, ext = os.path.splitext(orig_settings_dict['NAME']) + return {**orig_settings_dict, 'NAME': '{}_{}.{}'.format(root, suffix, ext)} def _clone_test_db(self, suffix, verbosity, keepdb=False): - source_database_name = self.connection.settings_dict["NAME"] - target_database_name = self.get_test_db_clone_settings(suffix)["NAME"] + source_database_name = self.connection.settings_dict['NAME'] + target_database_name = self.get_test_db_clone_settings(suffix)['NAME'] # Forking automatically makes a copy of an in-memory database. if not self.is_in_memory_db(source_database_name): # Erase the old test database @@ -67,23 +67,18 @@ class DatabaseCreation(BaseDatabaseCreation): if keepdb: return if verbosity >= 1: - self.log( - "Destroying old test database for alias %s..." - % ( - self._get_database_display_str( - verbosity, target_database_name - ), - ) - ) + self.log('Destroying old test database for alias %s...' % ( + self._get_database_display_str(verbosity, target_database_name), + )) try: os.remove(target_database_name) except Exception as e: - self.log("Got an error deleting the old test database: %s" % e) + self.log('Got an error deleting the old test database: %s' % e) sys.exit(2) try: shutil.copy(source_database_name, target_database_name) except Exception as e: - self.log("Got an error cloning the test database: %s" % e) + self.log('Got an error cloning the test database: %s' % e) sys.exit(2) def _destroy_test_db(self, test_database_name, verbosity): @@ -100,7 +95,7 @@ class DatabaseCreation(BaseDatabaseCreation): TEST NAME. See https://www.sqlite.org/inmemorydb.html """ test_database_name = self._get_test_db_name() - sig = [self.connection.settings_dict["NAME"]] + sig = [self.connection.settings_dict['NAME']] if self.is_in_memory_db(test_database_name): sig.append(self.connection.alias) else: diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/features.py b/venv/Lib/site-packages/django/db/backends/sqlite3/features.py index 2b06ebe..3348256 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/features.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/features.py @@ -45,82 +45,58 @@ class DatabaseFeatures(BaseDatabaseFeatures): order_by_nulls_first = True supports_json_field_contains = False test_collations = { - "ci": "nocase", - "cs": "binary", - "non_default": "nocase", + 'ci': 'nocase', + 'cs': 'binary', + 'non_default': 'nocase', } @cached_property def django_test_skips(self): skips = { - "SQLite stores values rounded to 15 significant digits.": { - "model_fields.test_decimalfield.DecimalFieldTests." - "test_fetch_from_db_without_float_rounding", + 'SQLite stores values rounded to 15 significant digits.': { + 'model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding', }, - "SQLite naively remakes the table on field alteration.": { - "schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops", - "schema.tests.SchemaTests.test_unique_and_reverse_m2m", - "schema.tests.SchemaTests." - "test_alter_field_default_doesnt_perform_queries", - "schema.tests.SchemaTests." - "test_rename_column_renames_deferred_sql_references", + 'SQLite naively remakes the table on field alteration.': { + 'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops', + 'schema.tests.SchemaTests.test_unique_and_reverse_m2m', + 'schema.tests.SchemaTests.test_alter_field_default_doesnt_perform_queries', + 'schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references', }, "SQLite doesn't have a constraint.": { - "model_fields.test_integerfield.PositiveIntegerFieldTests." - "test_negative_values", - }, - "SQLite doesn't support negative precision for ROUND().": { - "db_functions.math.test_round.RoundTests." - "test_null_with_negative_precision", - "db_functions.math.test_round.RoundTests." - "test_decimal_with_negative_precision", - "db_functions.math.test_round.RoundTests." - "test_float_with_negative_precision", - "db_functions.math.test_round.RoundTests." - "test_integer_with_negative_precision", + 'model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values', }, } if Database.sqlite_version_info < (3, 27): - skips.update( - { - "Nondeterministic failure on SQLite < 3.27.": { - "expressions_window.tests.WindowFunctionTests." - "test_subquery_row_range_rank", - }, - } - ) + skips.update({ + 'Nondeterministic failure on SQLite < 3.27.': { + 'expressions_window.tests.WindowFunctionTests.test_subquery_row_range_rank', + }, + }) if self.connection.is_in_memory_db(): - skips.update( - { - "the sqlite backend's close() method is a no-op when using an " - "in-memory database": { - "servers.test_liveserverthread.LiveServerThreadTest." - "test_closes_connections", - "servers.tests.LiveServerTestCloseConnectionTest." - "test_closes_connections", - }, - } - ) + skips.update({ + "the sqlite backend's close() method is a no-op when using an " + "in-memory database": { + 'servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections', + }, + }) return skips @cached_property def supports_atomic_references_rename(self): # SQLite 3.28.0 bundled with MacOS 10.15 does not support renaming # references atomically. - if platform.mac_ver()[0].startswith( - "10.15." - ) and Database.sqlite_version_info == (3, 28, 0): + if platform.mac_ver()[0].startswith('10.15.') and Database.sqlite_version_info == (3, 28, 0): return False return Database.sqlite_version_info >= (3, 26, 0) @cached_property def introspected_field_types(self): - return { + return{ **super().introspected_field_types, - "BigAutoField": "AutoField", - "DurationField": "BigIntegerField", - "GenericIPAddressField": "CharField", - "SmallAutoField": "AutoField", + 'BigAutoField': 'AutoField', + 'DurationField': 'BigIntegerField', + 'GenericIPAddressField': 'CharField', + 'SmallAutoField': 'AutoField', } @cached_property @@ -133,13 +109,5 @@ class DatabaseFeatures(BaseDatabaseFeatures): return False return True - can_introspect_json_field = property(operator.attrgetter("supports_json_field")) - has_json_object_function = property(operator.attrgetter("supports_json_field")) - - @cached_property - def can_return_columns_from_insert(self): - return Database.sqlite_version_info >= (3, 35) - - can_return_rows_from_bulk_insert = property( - operator.attrgetter("can_return_columns_from_insert") - ) + can_introspect_json_field = property(operator.attrgetter('supports_json_field')) + has_json_object_function = property(operator.attrgetter('supports_json_field')) diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/introspection.py b/venv/Lib/site-packages/django/db/backends/sqlite3/introspection.py index 808bca0..f109b5d 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/introspection.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/introspection.py @@ -3,21 +3,19 @@ from collections import namedtuple import sqlparse -from django.db.backends.base.introspection import BaseDatabaseIntrospection -from django.db.backends.base.introspection import FieldInfo as BaseFieldInfo -from django.db.backends.base.introspection import TableInfo +from django.db.backends.base.introspection import ( + BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo, +) from django.db.models import Index from django.utils.regex_helper import _lazy_re_compile -FieldInfo = namedtuple( - "FieldInfo", BaseFieldInfo._fields + ("pk", "has_json_constraint") -) +FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('pk', 'has_json_constraint')) -field_size_re = _lazy_re_compile(r"^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$") +field_size_re = _lazy_re_compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$') def get_field_size(name): - """Extract the size number from a "varchar(11)" type name""" + """ Extract the size number from a "varchar(11)" type name """ m = field_size_re.search(name) return int(m[1]) if m else None @@ -30,29 +28,29 @@ class FlexibleFieldLookupDict: # entries here because SQLite allows for anything and doesn't normalize the # field type; it uses whatever was given. base_data_types_reverse = { - "bool": "BooleanField", - "boolean": "BooleanField", - "smallint": "SmallIntegerField", - "smallint unsigned": "PositiveSmallIntegerField", - "smallinteger": "SmallIntegerField", - "int": "IntegerField", - "integer": "IntegerField", - "bigint": "BigIntegerField", - "integer unsigned": "PositiveIntegerField", - "bigint unsigned": "PositiveBigIntegerField", - "decimal": "DecimalField", - "real": "FloatField", - "text": "TextField", - "char": "CharField", - "varchar": "CharField", - "blob": "BinaryField", - "date": "DateField", - "datetime": "DateTimeField", - "time": "TimeField", + 'bool': 'BooleanField', + 'boolean': 'BooleanField', + 'smallint': 'SmallIntegerField', + 'smallint unsigned': 'PositiveSmallIntegerField', + 'smallinteger': 'SmallIntegerField', + 'int': 'IntegerField', + 'integer': 'IntegerField', + 'bigint': 'BigIntegerField', + 'integer unsigned': 'PositiveIntegerField', + 'bigint unsigned': 'PositiveBigIntegerField', + 'decimal': 'DecimalField', + 'real': 'FloatField', + 'text': 'TextField', + 'char': 'CharField', + 'varchar': 'CharField', + 'blob': 'BinaryField', + 'date': 'DateField', + 'datetime': 'DateTimeField', + 'time': 'TimeField', } def __getitem__(self, key): - key = key.lower().split("(", 1)[0].strip() + key = key.lower().split('(', 1)[0].strip() return self.base_data_types_reverse[key] @@ -61,28 +59,22 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): def get_field_type(self, data_type, description): field_type = super().get_field_type(data_type, description) - if description.pk and field_type in { - "BigIntegerField", - "IntegerField", - "SmallIntegerField", - }: + if description.pk and field_type in {'BigIntegerField', 'IntegerField', 'SmallIntegerField'}: # No support for BigAutoField or SmallAutoField as SQLite treats # all integer primary keys as signed 64-bit integers. - return "AutoField" + return 'AutoField' if description.has_json_constraint: - return "JSONField" + return 'JSONField' return field_type def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" # Skip the sqlite_sequence system table used for autoincrement key # generation. - cursor.execute( - """ + cursor.execute(""" SELECT name, type FROM sqlite_master WHERE type in ('table', 'view') AND NOT name='sqlite_sequence' - ORDER BY name""" - ) + ORDER BY name""") return [TableInfo(row[0], row[1][0]) for row in cursor.fetchall()] def get_table_description(self, cursor, table_name): @@ -90,9 +82,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): Return a description of the table with the DB-API cursor.description interface. """ - cursor.execute( - "PRAGMA table_info(%s)" % self.connection.ops.quote_name(table_name) - ) + cursor.execute('PRAGMA table_info(%s)' % self.connection.ops.quote_name(table_name)) table_info = cursor.fetchall() collations = self._get_column_collations(cursor, table_name) json_columns = set() @@ -100,39 +90,27 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): for line in table_info: column = line[1] json_constraint_sql = '%%json_valid("%s")%%' % column - has_json_constraint = cursor.execute( - """ + has_json_constraint = cursor.execute(""" SELECT sql FROM sqlite_master WHERE type = 'table' AND name = %s AND sql LIKE %s - """, - [table_name, json_constraint_sql], - ).fetchone() + """, [table_name, json_constraint_sql]).fetchone() if has_json_constraint: json_columns.add(column) return [ FieldInfo( - name, - data_type, - None, - get_field_size(data_type), - None, - None, - not notnull, - default, - collations.get(name), - pk == 1, - name in json_columns, + name, data_type, None, get_field_size(data_type), None, None, + not notnull, default, collations.get(name), pk == 1, name in json_columns ) for cid, name, data_type, notnull, default, pk in table_info ] def get_sequences(self, cursor, table_name, table_fields=()): pk_col = self.get_primary_key_column(cursor, table_name) - return [{"table": table_name, "column": pk_col}] + return [{'table': table_name, 'column': pk_col}] def get_relations(self, cursor, table_name): """ @@ -146,18 +124,18 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): cursor.execute( "SELECT sql, type FROM sqlite_master " "WHERE tbl_name = %s AND type IN ('table', 'view')", - [table_name], + [table_name] ) create_sql, table_type = cursor.fetchone() - if table_type == "view": + if table_type == 'view': # It might be a view, then no results will be returned return relations - results = create_sql[create_sql.index("(") + 1 : create_sql.rindex(")")] + results = create_sql[create_sql.index('(') + 1:create_sql.rindex(')')] # Walk through and look for references to other tables. SQLite doesn't # really have enforced references, but since it echoes out the SQL used # to create the table we can look for REFERENCES statements used there. - for field_desc in results.split(","): + for field_desc in results.split(','): field_desc = field_desc.strip() if field_desc.startswith("UNIQUE"): continue @@ -169,7 +147,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): if field_desc.startswith("FOREIGN KEY"): # Find name of the target FK field - m = re.match(r"FOREIGN KEY\s*\(([^\)]*)\).*", field_desc, re.I) + m = re.match(r'FOREIGN KEY\s*\(([^\)]*)\).*', field_desc, re.I) field_name = m[1].strip('"') else: field_name = field_desc.split()[0].strip('"') @@ -177,15 +155,15 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s", [table]) result = cursor.fetchall()[0] other_table_results = result[0].strip() - li, ri = other_table_results.index("("), other_table_results.rindex(")") - other_table_results = other_table_results[li + 1 : ri] + li, ri = other_table_results.index('('), other_table_results.rindex(')') + other_table_results = other_table_results[li + 1:ri] - for other_desc in other_table_results.split(","): + for other_desc in other_table_results.split(','): other_desc = other_desc.strip() - if other_desc.startswith("UNIQUE"): + if other_desc.startswith('UNIQUE'): continue - other_name = other_desc.split(" ", 1)[0].strip('"') + other_name = other_desc.split(' ', 1)[0].strip('"') if other_name == column: relations[field_name] = (other_name, table) break @@ -200,17 +178,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): key_columns = [] # Schema for this table - cursor.execute( - "SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", - [table_name, "table"], - ) + cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"]) results = cursor.fetchone()[0].strip() - results = results[results.index("(") + 1 : results.rindex(")")] + results = results[results.index('(') + 1:results.rindex(')')] # Walk through and look for references to other tables. SQLite doesn't # really have enforced references, but since it echoes out the SQL used # to create the table we can look for REFERENCES statements used there. - for field_index, field_desc in enumerate(results.split(",")): + for field_index, field_desc in enumerate(results.split(',')): field_desc = field_desc.strip() if field_desc.startswith("UNIQUE"): continue @@ -219,9 +194,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): if not m: continue - # This will append - # (column_name, referenced_table_name, referenced_column_name) to - # key_columns. + # This will append (column_name, referenced_table_name, referenced_column_name) to key_columns key_columns.append(tuple(s.strip('"') for s in m.groups())) return key_columns @@ -232,40 +205,36 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): cursor.execute( "SELECT sql, type FROM sqlite_master " "WHERE tbl_name = %s AND type IN ('table', 'view')", - [table_name], + [table_name] ) row = cursor.fetchone() if row is None: raise ValueError("Table %s does not exist" % table_name) create_sql, table_type = row - if table_type == "view": + if table_type == 'view': # Views don't have a primary key. return None - fields_sql = create_sql[create_sql.index("(") + 1 : create_sql.rindex(")")] - for field_desc in fields_sql.split(","): + fields_sql = create_sql[create_sql.index('(') + 1:create_sql.rindex(')')] + for field_desc in fields_sql.split(','): field_desc = field_desc.strip() - m = re.match( - r'(?:(?:["`\[])(.*)(?:["`\]])|(\w+)).*PRIMARY KEY.*', field_desc - ) + m = re.match(r'(?:(?:["`\[])(.*)(?:["`\]])|(\w+)).*PRIMARY KEY.*', field_desc) if m: return m[1] if m[1] else m[2] return None def _get_foreign_key_constraints(self, cursor, table_name): constraints = {} - cursor.execute( - "PRAGMA foreign_key_list(%s)" % self.connection.ops.quote_name(table_name) - ) + cursor.execute('PRAGMA foreign_key_list(%s)' % self.connection.ops.quote_name(table_name)) for row in cursor.fetchall(): # Remaining on_update/on_delete/match values are of no interest. id_, _, table, from_, to = row[:5] - constraints["fk_%d" % id_] = { - "columns": [from_], - "primary_key": False, - "unique": False, - "foreign_key": (table, to), - "check": False, - "index": False, + constraints['fk_%d' % id_] = { + 'columns': [from_], + 'primary_key': False, + 'unique': False, + 'foreign_key': (table, to), + 'check': False, + 'index': False, } return constraints @@ -280,21 +249,19 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): check_columns = [] braces_deep = 0 for token in tokens: - if token.match(sqlparse.tokens.Punctuation, "("): + if token.match(sqlparse.tokens.Punctuation, '('): braces_deep += 1 - elif token.match(sqlparse.tokens.Punctuation, ")"): + elif token.match(sqlparse.tokens.Punctuation, ')'): braces_deep -= 1 if braces_deep < 0: # End of columns and constraints for table definition. break - elif braces_deep == 0 and token.match(sqlparse.tokens.Punctuation, ","): + elif braces_deep == 0 and token.match(sqlparse.tokens.Punctuation, ','): # End of current column or constraint definition. break # Detect column or constraint definition by first token. if is_constraint_definition is None: - is_constraint_definition = token.match( - sqlparse.tokens.Keyword, "CONSTRAINT" - ) + is_constraint_definition = token.match(sqlparse.tokens.Keyword, 'CONSTRAINT') if is_constraint_definition: continue if is_constraint_definition: @@ -305,7 +272,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): elif token.ttype == sqlparse.tokens.Literal.String.Symbol: constraint_name = token.value[1:-1] # Start constraint columns parsing after UNIQUE keyword. - if token.match(sqlparse.tokens.Keyword, "UNIQUE"): + if token.match(sqlparse.tokens.Keyword, 'UNIQUE'): unique = True unique_braces_deep = braces_deep elif unique: @@ -325,10 +292,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): field_name = token.value elif token.ttype == sqlparse.tokens.Literal.String.Symbol: field_name = token.value[1:-1] - if token.match(sqlparse.tokens.Keyword, "UNIQUE"): + if token.match(sqlparse.tokens.Keyword, 'UNIQUE'): unique_columns = [field_name] # Start constraint columns parsing after CHECK keyword. - if token.match(sqlparse.tokens.Keyword, "CHECK"): + if token.match(sqlparse.tokens.Keyword, 'CHECK'): check = True check_braces_deep = braces_deep elif check: @@ -343,30 +310,22 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): elif token.ttype == sqlparse.tokens.Literal.String.Symbol: if token.value[1:-1] in columns: check_columns.append(token.value[1:-1]) - unique_constraint = ( - { - "unique": True, - "columns": unique_columns, - "primary_key": False, - "foreign_key": None, - "check": False, - "index": False, - } - if unique_columns - else None - ) - check_constraint = ( - { - "check": True, - "columns": check_columns, - "primary_key": False, - "unique": False, - "foreign_key": None, - "index": False, - } - if check_columns - else None - ) + unique_constraint = { + 'unique': True, + 'columns': unique_columns, + 'primary_key': False, + 'foreign_key': None, + 'check': False, + 'index': False, + } if unique_columns else None + check_constraint = { + 'check': True, + 'columns': check_columns, + 'primary_key': False, + 'unique': False, + 'foreign_key': None, + 'index': False, + } if check_columns else None return constraint_name, unique_constraint, check_constraint, token def _parse_table_constraints(self, sql, columns): @@ -378,33 +337,24 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): tokens = (token for token in statement.flatten() if not token.is_whitespace) # Go to columns and constraint definition for token in tokens: - if token.match(sqlparse.tokens.Punctuation, "("): + if token.match(sqlparse.tokens.Punctuation, '('): break # Parse columns and constraint definition while True: - ( - constraint_name, - unique, - check, - end_token, - ) = self._parse_column_or_constraint_definition(tokens, columns) + constraint_name, unique, check, end_token = self._parse_column_or_constraint_definition(tokens, columns) if unique: if constraint_name: constraints[constraint_name] = unique else: unnamed_constrains_index += 1 - constraints[ - "__unnamed_constraint_%s__" % unnamed_constrains_index - ] = unique + constraints['__unnamed_constraint_%s__' % unnamed_constrains_index] = unique if check: if constraint_name: constraints[constraint_name] = check else: unnamed_constrains_index += 1 - constraints[ - "__unnamed_constraint_%s__" % unnamed_constrains_index - ] = check - if end_token.match(sqlparse.tokens.Punctuation, ")"): + constraints['__unnamed_constraint_%s__' % unnamed_constrains_index] = check + if end_token.match(sqlparse.tokens.Punctuation, ')'): break return constraints @@ -417,22 +367,19 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Find inline check constraints. try: table_schema = cursor.execute( - "SELECT sql FROM sqlite_master WHERE type='table' and name=%s" - % (self.connection.ops.quote_name(table_name),) + "SELECT sql FROM sqlite_master WHERE type='table' and name=%s" % ( + self.connection.ops.quote_name(table_name), + ) ).fetchone()[0] except TypeError: # table_name is a view. pass else: - columns = { - info.name for info in self.get_table_description(cursor, table_name) - } + columns = {info.name for info in self.get_table_description(cursor, table_name)} constraints.update(self._parse_table_constraints(table_schema, columns)) # Get the index info - cursor.execute( - "PRAGMA index_list(%s)" % self.connection.ops.quote_name(table_name) - ) + cursor.execute("PRAGMA index_list(%s)" % self.connection.ops.quote_name(table_name)) for row in cursor.fetchall(): # SQLite 3.8.9+ has 5 columns, however older versions only give 3 # columns. Discard last 2 columns if there. @@ -442,7 +389,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "WHERE type='index' AND name=%s" % self.connection.ops.quote_name(index) ) # There's at most one row. - (sql,) = cursor.fetchone() or (None,) + sql, = cursor.fetchone() or (None,) # Inline constraints are already detected in # _parse_table_constraints(). The reasons to avoid fetching inline # constraints from `PRAGMA index_list` are: @@ -453,9 +400,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # An inline constraint continue # Get the index info for that index - cursor.execute( - "PRAGMA index_info(%s)" % self.connection.ops.quote_name(index) - ) + cursor.execute('PRAGMA index_info(%s)' % self.connection.ops.quote_name(index)) for index_rank, column_rank, column in cursor.fetchall(): if index not in constraints: constraints[index] = { @@ -466,14 +411,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "check": False, "index": True, } - constraints[index]["columns"].append(column) + constraints[index]['columns'].append(column) # Add type and column orders for indexes - if constraints[index]["index"]: + if constraints[index]['index']: # SQLite doesn't support any index type other than b-tree - constraints[index]["type"] = Index.suffix + constraints[index]['type'] = Index.suffix orders = self._get_index_columns_orders(sql) if orders is not None: - constraints[index]["orders"] = orders + constraints[index]['orders'] = orders # Get the PK pk_column = self.get_primary_key_column(cursor, table_name) if pk_column: @@ -496,30 +441,27 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): tokens = sqlparse.parse(sql)[0] for token in tokens: if isinstance(token, sqlparse.sql.Parenthesis): - columns = str(token).strip("()").split(", ") - return ["DESC" if info.endswith("DESC") else "ASC" for info in columns] + columns = str(token).strip('()').split(', ') + return ['DESC' if info.endswith('DESC') else 'ASC' for info in columns] return None def _get_column_collations(self, cursor, table_name): - row = cursor.execute( - """ + row = cursor.execute(""" SELECT sql FROM sqlite_master WHERE type = 'table' AND name = %s - """, - [table_name], - ).fetchone() + """, [table_name]).fetchone() if not row: return {} sql = row[0] - columns = str(sqlparse.parse(sql)[0][-1]).strip("()").split(", ") + columns = str(sqlparse.parse(sql)[0][-1]).strip('()').split(', ') collations = {} for column in columns: tokens = column[1:].split() column_name = tokens[0].strip('"') for index, token in enumerate(tokens): - if token == "COLLATE": + if token == 'COLLATE': collation = tokens[index + 1] break else: diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/operations.py b/venv/Lib/site-packages/django/db/backends/sqlite3/operations.py index 38265ba..71ef000 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/operations.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/operations.py @@ -15,15 +15,12 @@ from django.utils.functional import cached_property class DatabaseOperations(BaseDatabaseOperations): - cast_char_field_without_max_length = "text" + cast_char_field_without_max_length = 'text' cast_data_types = { - "DateField": "TEXT", - "DateTimeField": "TEXT", + 'DateField': 'TEXT', + 'DateTimeField': 'TEXT', } - explain_prefix = "EXPLAIN QUERY PLAN" - # List of datatypes to that cannot be extracted with JSON_EXTRACT() on - # SQLite. Use JSON_TYPE() instead. - jsonfield_datatype_values = frozenset(["null", "false", "true"]) + explain_prefix = 'EXPLAIN QUERY PLAN' def bulk_batch_size(self, fields, objs): """ @@ -54,14 +51,14 @@ class DatabaseOperations(BaseDatabaseOperations): else: if isinstance(output_field, bad_fields): raise NotSupportedError( - "You cannot use Sum, Avg, StdDev, and Variance " - "aggregations on date/time fields in sqlite3 " - "since date/time is saved as text." + 'You cannot use Sum, Avg, StdDev, and Variance ' + 'aggregations on date/time fields in sqlite3 ' + 'since date/time is saved as text.' ) if ( - isinstance(expression, models.Aggregate) - and expression.distinct - and len(expression.source_expressions) > 1 + isinstance(expression, models.Aggregate) and + expression.distinct and + len(expression.source_expressions) > 1 ): raise NotSupportedError( "SQLite doesn't support DISTINCT on aggregate functions " @@ -76,13 +73,6 @@ class DatabaseOperations(BaseDatabaseOperations): """ return "django_date_extract('%s', %s)" % (lookup_type.lower(), field_name) - def fetch_returned_insert_rows(self, cursor): - """ - Given a cursor object that has just performed an INSERT...RETURNING - statement into a table, return the list of returned data. - """ - return cursor.fetchall() - def format_for_duration_arithmetic(self, sql): """Do nothing since formatting is handled in the custom function.""" return sql @@ -104,32 +94,26 @@ class DatabaseOperations(BaseDatabaseOperations): def _convert_tznames_to_sql(self, tzname): if tzname and settings.USE_TZ: return "'%s'" % tzname, "'%s'" % self.connection.timezone_name - return "NULL", "NULL" + return 'NULL', 'NULL' def datetime_cast_date_sql(self, field_name, tzname): - return "django_datetime_cast_date(%s, %s, %s)" % ( - field_name, - *self._convert_tznames_to_sql(tzname), + return 'django_datetime_cast_date(%s, %s, %s)' % ( + field_name, *self._convert_tznames_to_sql(tzname), ) def datetime_cast_time_sql(self, field_name, tzname): - return "django_datetime_cast_time(%s, %s, %s)" % ( - field_name, - *self._convert_tznames_to_sql(tzname), + return 'django_datetime_cast_time(%s, %s, %s)' % ( + field_name, *self._convert_tznames_to_sql(tzname), ) def datetime_extract_sql(self, lookup_type, field_name, tzname): return "django_datetime_extract('%s', %s, %s, %s)" % ( - lookup_type.lower(), - field_name, - *self._convert_tznames_to_sql(tzname), + lookup_type.lower(), field_name, *self._convert_tznames_to_sql(tzname), ) def datetime_trunc_sql(self, lookup_type, field_name, tzname): return "django_datetime_trunc('%s', %s, %s, %s)" % ( - lookup_type.lower(), - field_name, - *self._convert_tznames_to_sql(tzname), + lookup_type.lower(), field_name, *self._convert_tznames_to_sql(tzname), ) def time_extract_sql(self, lookup_type, field_name): @@ -151,11 +135,11 @@ class DatabaseOperations(BaseDatabaseOperations): if len(params) > BATCH_SIZE: results = () for index in range(0, len(params), BATCH_SIZE): - chunk = params[index : index + BATCH_SIZE] + chunk = params[index:index + BATCH_SIZE] results += self._quote_params_for_last_executed_query(chunk) return results - sql = "SELECT " + ", ".join(["QUOTE(?)"] * len(params)) + sql = 'SELECT ' + ', '.join(['QUOTE(?)'] * len(params)) # Bypass Django's wrappers and use the underlying sqlite3 connection # to avoid logging this query - it would trigger infinite recursion. cursor = self.connection.connection.cursor() @@ -167,9 +151,7 @@ class DatabaseOperations(BaseDatabaseOperations): def last_executed_query(self, cursor, sql, params): # Python substitutes parameters in Modules/_sqlite/cursor.c with: - # pysqlite_statement_bind_parameters( - # self->statement, parameters, allow_8bit_chars - # ); + # pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars); # Unfortunately there is no way to reach self->statement from Python, # so we quote and substitute parameters manually. if params: @@ -222,20 +204,14 @@ class DatabaseOperations(BaseDatabaseOperations): if tables and allow_cascade: # Simulate TRUNCATE CASCADE by recursively collecting the tables # referencing the tables to be flushed. - tables = set( - chain.from_iterable(self._references_graph(table) for table in tables) - ) - sql = [ - "%s %s %s;" - % ( - style.SQL_KEYWORD("DELETE"), - style.SQL_KEYWORD("FROM"), - style.SQL_FIELD(self.quote_name(table)), - ) - for table in tables - ] + tables = set(chain.from_iterable(self._references_graph(table) for table in tables)) + sql = ['%s %s %s;' % ( + style.SQL_KEYWORD('DELETE'), + style.SQL_KEYWORD('FROM'), + style.SQL_FIELD(self.quote_name(table)) + ) for table in tables] if reset_sequences: - sequences = [{"table": table} for table in tables] + sequences = [{'table': table} for table in tables] sql.extend(self.sequence_reset_by_name_sql(style, sequences)) return sql @@ -243,18 +219,17 @@ class DatabaseOperations(BaseDatabaseOperations): if not sequences: return [] return [ - "%s %s %s %s = 0 %s %s %s (%s);" - % ( - style.SQL_KEYWORD("UPDATE"), - style.SQL_TABLE(self.quote_name("sqlite_sequence")), - style.SQL_KEYWORD("SET"), - style.SQL_FIELD(self.quote_name("seq")), - style.SQL_KEYWORD("WHERE"), - style.SQL_FIELD(self.quote_name("name")), - style.SQL_KEYWORD("IN"), - ", ".join( - ["'%s'" % sequence_info["table"] for sequence_info in sequences] - ), + '%s %s %s %s = 0 %s %s %s (%s);' % ( + style.SQL_KEYWORD('UPDATE'), + style.SQL_TABLE(self.quote_name('sqlite_sequence')), + style.SQL_KEYWORD('SET'), + style.SQL_FIELD(self.quote_name('seq')), + style.SQL_KEYWORD('WHERE'), + style.SQL_FIELD(self.quote_name('name')), + style.SQL_KEYWORD('IN'), + ', '.join([ + "'%s'" % sequence_info['table'] for sequence_info in sequences + ]), ), ] @@ -263,7 +238,7 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value # SQLite doesn't support tz-aware datetimes @@ -271,10 +246,7 @@ class DatabaseOperations(BaseDatabaseOperations): if settings.USE_TZ: value = timezone.make_naive(value, self.connection.timezone) else: - raise ValueError( - "SQLite backend does not support timezone-aware datetimes when " - "USE_TZ is False." - ) + raise ValueError("SQLite backend does not support timezone-aware datetimes when USE_TZ is False.") return str(value) @@ -283,7 +255,7 @@ class DatabaseOperations(BaseDatabaseOperations): return None # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): return value # SQLite doesn't support tz-aware datetimes @@ -295,17 +267,17 @@ class DatabaseOperations(BaseDatabaseOperations): def get_db_converters(self, expression): converters = super().get_db_converters(expression) internal_type = expression.output_field.get_internal_type() - if internal_type == "DateTimeField": + if internal_type == 'DateTimeField': converters.append(self.convert_datetimefield_value) - elif internal_type == "DateField": + elif internal_type == 'DateField': converters.append(self.convert_datefield_value) - elif internal_type == "TimeField": + elif internal_type == 'TimeField': converters.append(self.convert_timefield_value) - elif internal_type == "DecimalField": + elif internal_type == 'DecimalField': converters.append(self.get_decimalfield_converter(expression)) - elif internal_type == "UUIDField": + elif internal_type == 'UUIDField': converters.append(self.convert_uuidfield_value) - elif internal_type == "BooleanField": + elif internal_type in ('NullBooleanField', 'BooleanField'): converters.append(self.convert_booleanfield_value) return converters @@ -334,22 +306,15 @@ class DatabaseOperations(BaseDatabaseOperations): # float inaccuracy must be removed. create_decimal = decimal.Context(prec=15).create_decimal_from_float if isinstance(expression, Col): - quantize_value = decimal.Decimal(1).scaleb( - -expression.output_field.decimal_places - ) + quantize_value = decimal.Decimal(1).scaleb(-expression.output_field.decimal_places) def converter(value, expression, connection): if value is not None: - return create_decimal(value).quantize( - quantize_value, context=expression.output_field.context - ) - + return create_decimal(value).quantize(quantize_value, context=expression.output_field.context) else: - def converter(value, expression, connection): if value is not None: return create_decimal(value) - return converter def convert_uuidfield_value(self, value, expression, connection): @@ -362,25 +327,26 @@ class DatabaseOperations(BaseDatabaseOperations): def bulk_insert_sql(self, fields, placeholder_rows): return " UNION ALL ".join( - "SELECT %s" % ", ".join(row) for row in placeholder_rows + "SELECT %s" % ", ".join(row) + for row in placeholder_rows ) def combine_expression(self, connector, sub_expressions): # SQLite doesn't have a ^ operator, so use the user-defined POWER # function that's registered in connect(). - if connector == "^": - return "POWER(%s)" % ",".join(sub_expressions) - elif connector == "#": - return "BITXOR(%s)" % ",".join(sub_expressions) + if connector == '^': + return 'POWER(%s)' % ','.join(sub_expressions) + elif connector == '#': + return 'BITXOR(%s)' % ','.join(sub_expressions) return super().combine_expression(connector, sub_expressions) def combine_duration_expression(self, connector, sub_expressions): - if connector not in ["+", "-", "*", "/"]: - raise DatabaseError("Invalid connector for timedelta: %s." % connector) + if connector not in ['+', '-']: + raise DatabaseError('Invalid connector for timedelta: %s.' % connector) fn_params = ["'%s'" % connector] + sub_expressions if len(fn_params) > 3: - raise ValueError("Too many params for timedelta operations.") - return "django_format_dtdelta(%s)" % ", ".join(fn_params) + raise ValueError('Too many params for timedelta operations.') + return "django_format_dtdelta(%s)" % ', '.join(fn_params) def integer_field_range(self, internal_type): # SQLite doesn't enforce any integer constraints @@ -390,27 +356,9 @@ class DatabaseOperations(BaseDatabaseOperations): lhs_sql, lhs_params = lhs rhs_sql, rhs_params = rhs params = (*lhs_params, *rhs_params) - if internal_type == "TimeField": - return "django_time_diff(%s, %s)" % (lhs_sql, rhs_sql), params - return "django_timestamp_diff(%s, %s)" % (lhs_sql, rhs_sql), params + if internal_type == 'TimeField': + return 'django_time_diff(%s, %s)' % (lhs_sql, rhs_sql), params + return 'django_timestamp_diff(%s, %s)' % (lhs_sql, rhs_sql), params def insert_statement(self, ignore_conflicts=False): - return ( - "INSERT OR IGNORE INTO" - if ignore_conflicts - else super().insert_statement(ignore_conflicts) - ) - - def return_insert_columns(self, fields): - # SQLite < 3.35 doesn't support an INSERT...RETURNING statement. - if not fields: - return "", () - columns = [ - "%s.%s" - % ( - self.quote_name(field.model._meta.db_table), - self.quote_name(field.column), - ) - for field in fields - ] - return "RETURNING %s" % ", ".join(columns), () + return 'INSERT OR IGNORE INTO' if ignore_conflicts else super().insert_statement(ignore_conflicts) diff --git a/venv/Lib/site-packages/django/db/backends/sqlite3/schema.py b/venv/Lib/site-packages/django/db/backends/sqlite3/schema.py index 072e53f..6a2c887 100644 --- a/venv/Lib/site-packages/django/db/backends/sqlite3/schema.py +++ b/venv/Lib/site-packages/django/db/backends/sqlite3/schema.py @@ -14,9 +14,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_table = "DROP TABLE %(table)s" sql_create_fk = None - sql_create_inline_fk = ( - "REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" - ) + sql_create_inline_fk = "REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" sql_create_unique = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)" sql_delete_unique = "DROP INDEX %(name)s" @@ -25,11 +23,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # disabled. Enforce it here for the duration of the schema edition. if not self.connection.disable_constraint_checking(): raise NotSupportedError( - "SQLite schema editor cannot be used while foreign key " - "constraint checks are enabled. Make sure to disable them " - "before entering a transaction.atomic() context because " - "SQLite does not support disabling them in the middle of " - "a multi-statement transaction." + 'SQLite schema editor cannot be used while foreign key ' + 'constraint checks are enabled. Make sure to disable them ' + 'before entering a transaction.atomic() context because ' + 'SQLite does not support disabling them in the middle of ' + 'a multi-statement transaction.' ) return super().__enter__() @@ -44,7 +42,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # security hardening). try: import sqlite3 - value = sqlite3.adapt(value) except ImportError: pass @@ -56,7 +53,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): elif isinstance(value, (Decimal, float, int)): return str(value) elif isinstance(value, str): - return "'%s'" % value.replace("'", "''") + return "'%s'" % value.replace("\'", "\'\'") elif value is None: return "NULL" elif isinstance(value, (bytes, bytearray, memoryview)): @@ -65,13 +62,9 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # character. return "X'%s'" % value.hex() else: - raise ValueError( - "Cannot quote parameter value %r of type %s" % (value, type(value)) - ) + raise ValueError("Cannot quote parameter value %r of type %s" % (value, type(value))) - def _is_referenced_by_fk_constraint( - self, table_name, column_name=None, ignore_self=False - ): + def _is_referenced_by_fk_constraint(self, table_name, column_name=None, ignore_self=False): """ Return whether or not the provided table name is referenced by another one. If `column_name` is specified, only references pointing to that @@ -82,36 +75,23 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): for other_table in self.connection.introspection.get_table_list(cursor): if ignore_self and other_table.name == table_name: continue - constraints = ( - self.connection.introspection._get_foreign_key_constraints( - cursor, other_table.name - ) - ) + constraints = self.connection.introspection._get_foreign_key_constraints(cursor, other_table.name) for constraint in constraints.values(): - constraint_table, constraint_column = constraint["foreign_key"] - if constraint_table == table_name and ( - column_name is None or constraint_column == column_name - ): + constraint_table, constraint_column = constraint['foreign_key'] + if (constraint_table == table_name and + (column_name is None or constraint_column == column_name)): return True return False - def alter_db_table( - self, model, old_db_table, new_db_table, disable_constraints=True - ): - if ( - not self.connection.features.supports_atomic_references_rename - and disable_constraints - and self._is_referenced_by_fk_constraint(old_db_table) - ): + def alter_db_table(self, model, old_db_table, new_db_table, disable_constraints=True): + if (not self.connection.features.supports_atomic_references_rename and + disable_constraints and self._is_referenced_by_fk_constraint(old_db_table)): if self.connection.in_atomic_block: - raise NotSupportedError( - ( - "Renaming the %r table while in a transaction is not " - "supported on SQLite < 3.26 because it would break referential " - "integrity. Try adding `atomic = False` to the Migration class." - ) - % old_db_table - ) + raise NotSupportedError(( + 'Renaming the %r table while in a transaction is not ' + 'supported on SQLite < 3.26 because it would break referential ' + 'integrity. Try adding `atomic = False` to the Migration class.' + ) % old_db_table) self.connection.enable_constraint_checking() super().alter_db_table(model, old_db_table, new_db_table) self.connection.disable_constraint_checking() @@ -124,56 +104,42 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): old_field_name = old_field.name table_name = model._meta.db_table _, old_column_name = old_field.get_attname_column() - if ( - new_field.name != old_field_name - and not self.connection.features.supports_atomic_references_rename - and self._is_referenced_by_fk_constraint( - table_name, old_column_name, ignore_self=True - ) - ): + if (new_field.name != old_field_name and + not self.connection.features.supports_atomic_references_rename and + self._is_referenced_by_fk_constraint(table_name, old_column_name, ignore_self=True)): if self.connection.in_atomic_block: - raise NotSupportedError( - ( - "Renaming the %r.%r column while in a transaction is not " - "supported on SQLite < 3.26 because it would break referential " - "integrity. Try adding `atomic = False` to the Migration class." - ) - % (model._meta.db_table, old_field_name) - ) + raise NotSupportedError(( + 'Renaming the %r.%r column while in a transaction is not ' + 'supported on SQLite < 3.26 because it would break referential ' + 'integrity. Try adding `atomic = False` to the Migration class.' + ) % (model._meta.db_table, old_field_name)) with atomic(self.connection.alias): super().alter_field(model, old_field, new_field, strict=strict) # Follow SQLite's documented procedure for performing changes # that don't affect the on-disk content. # https://sqlite.org/lang_altertable.html#otheralter with self.connection.cursor() as cursor: - schema_version = cursor.execute("PRAGMA schema_version").fetchone()[ - 0 - ] - cursor.execute("PRAGMA writable_schema = 1") + schema_version = cursor.execute('PRAGMA schema_version').fetchone()[0] + cursor.execute('PRAGMA writable_schema = 1') references_template = ' REFERENCES "%s" ("%%s") ' % table_name new_column_name = new_field.get_attname_column()[1] search = references_template % old_column_name replacement = references_template % new_column_name - cursor.execute( - "UPDATE sqlite_master SET sql = replace(sql, %s, %s)", - (search, replacement), - ) - cursor.execute("PRAGMA schema_version = %d" % (schema_version + 1)) - cursor.execute("PRAGMA writable_schema = 0") + cursor.execute('UPDATE sqlite_master SET sql = replace(sql, %s, %s)', (search, replacement)) + cursor.execute('PRAGMA schema_version = %d' % (schema_version + 1)) + cursor.execute('PRAGMA writable_schema = 0') # The integrity check will raise an exception and rollback # the transaction if the sqlite_master updates corrupt the # database. - cursor.execute("PRAGMA integrity_check") + cursor.execute('PRAGMA integrity_check') # Perform a VACUUM to refresh the database representation from # the sqlite_master table. with self.connection.cursor() as cursor: - cursor.execute("VACUUM") + cursor.execute('VACUUM') else: super().alter_field(model, old_field, new_field, strict=strict) - def _remake_table( - self, model, create_field=None, delete_field=None, alter_field=None - ): + def _remake_table(self, model, create_field=None, delete_field=None, alter_field=None): """ Shortcut to transform a model from old_model into new_model @@ -194,7 +160,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # to an altered field. def is_self_referential(f): return f.is_relation and f.remote_field.model is model - # Work out the new fields dict / mapping body = { f.name: f.clone() if is_self_referential(f) else f @@ -202,18 +167,14 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): } # Since mapping might mix column names and default values, # its values must be already quoted. - mapping = { - f.column: self.quote_name(f.column) - for f in model._meta.local_concrete_fields - } + mapping = {f.column: self.quote_name(f.column) for f in model._meta.local_concrete_fields} # This maps field names (not columns) for things like unique_together rename_mapping = {} # If any of the new or altered fields is introducing a new PK, # remove the old one restore_pk_field = None - if getattr(create_field, "primary_key", False) or ( - alter_field and getattr(alter_field[1], "primary_key", False) - ): + if getattr(create_field, 'primary_key', False) or ( + alter_field and getattr(alter_field[1], 'primary_key', False)): for name, field in list(body.items()): if field.primary_key: field.primary_key = False @@ -237,8 +198,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): body[new_field.name] = new_field if old_field.null and not new_field.null: case_sql = "coalesce(%(col)s, %(default)s)" % { - "col": self.quote_name(old_field.column), - "default": self.quote_value(self.effective_default(new_field)), + 'col': self.quote_name(old_field.column), + 'default': self.quote_value(self.effective_default(new_field)) } mapping[new_field.column] = case_sql else: @@ -249,10 +210,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): del body[delete_field.name] del mapping[delete_field.column] # Remove any implicit M2M tables - if ( - delete_field.many_to_many - and delete_field.remote_field.through._meta.auto_created - ): + if delete_field.many_to_many and delete_field.remote_field.through._meta.auto_created: return self.delete_model(delete_field.remote_field.through) # Work inside a new app registry apps = Apps() @@ -274,7 +232,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): indexes = model._meta.indexes if delete_field: indexes = [ - index for index in indexes if delete_field.name not in index.fields + index for index in indexes + if delete_field.name not in index.fields ] constraints = list(model._meta.constraints) @@ -290,57 +249,52 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # This wouldn't be required if the schema editor was operating on model # states instead of rendered models. meta_contents = { - "app_label": model._meta.app_label, - "db_table": model._meta.db_table, - "unique_together": unique_together, - "index_together": index_together, - "indexes": indexes, - "constraints": constraints, - "apps": apps, + 'app_label': model._meta.app_label, + 'db_table': model._meta.db_table, + 'unique_together': unique_together, + 'index_together': index_together, + 'indexes': indexes, + 'constraints': constraints, + 'apps': apps, } meta = type("Meta", (), meta_contents) - body_copy["Meta"] = meta - body_copy["__module__"] = model.__module__ + body_copy['Meta'] = meta + body_copy['__module__'] = model.__module__ type(model._meta.object_name, model.__bases__, body_copy) # Construct a model with a renamed table name. body_copy = copy.deepcopy(body) meta_contents = { - "app_label": model._meta.app_label, - "db_table": "new__%s" % strip_quotes(model._meta.db_table), - "unique_together": unique_together, - "index_together": index_together, - "indexes": indexes, - "constraints": constraints, - "apps": apps, + 'app_label': model._meta.app_label, + 'db_table': 'new__%s' % strip_quotes(model._meta.db_table), + 'unique_together': unique_together, + 'index_together': index_together, + 'indexes': indexes, + 'constraints': constraints, + 'apps': apps, } meta = type("Meta", (), meta_contents) - body_copy["Meta"] = meta - body_copy["__module__"] = model.__module__ - new_model = type("New%s" % model._meta.object_name, model.__bases__, body_copy) + body_copy['Meta'] = meta + body_copy['__module__'] = model.__module__ + new_model = type('New%s' % model._meta.object_name, model.__bases__, body_copy) # Create a new table with the updated schema. self.create_model(new_model) # Copy data from the old table into the new table - self.execute( - "INSERT INTO %s (%s) SELECT %s FROM %s" - % ( - self.quote_name(new_model._meta.db_table), - ", ".join(self.quote_name(x) for x in mapping), - ", ".join(mapping.values()), - self.quote_name(model._meta.db_table), - ) - ) + self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % ( + self.quote_name(new_model._meta.db_table), + ', '.join(self.quote_name(x) for x in mapping), + ', '.join(mapping.values()), + self.quote_name(model._meta.db_table), + )) # Delete the old table to make way for the new self.delete_model(model, handle_autom2m=False) # Rename the new table to take way for the old self.alter_db_table( - new_model, - new_model._meta.db_table, - model._meta.db_table, + new_model, new_model._meta.db_table, model._meta.db_table, disable_constraints=False, ) @@ -357,17 +311,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): super().delete_model(model) else: # Delete the table (and only that) - self.execute( - self.sql_delete_table - % { - "table": self.quote_name(model._meta.db_table), - } - ) + self.execute(self.sql_delete_table % { + "table": self.quote_name(model._meta.db_table), + }) # Remove all deferred statements referencing the deleted table. for sql in list(self.deferred_sql): - if isinstance(sql, Statement) and sql.references_table( - model._meta.db_table - ): + if isinstance(sql, Statement) and sql.references_table(model._meta.db_table): self.deferred_sql.remove(sql) def add_field(self, model, field): @@ -394,40 +343,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # For everything else, remake. else: # It might not actually have a column behind it - if field.db_parameters(connection=self.connection)["type"] is None: + if field.db_parameters(connection=self.connection)['type'] is None: return self._remake_table(model, delete_field=field) - def _alter_field( - self, - model, - old_field, - new_field, - old_type, - new_type, - old_db_params, - new_db_params, - strict=False, - ): + def _alter_field(self, model, old_field, new_field, old_type, new_type, + old_db_params, new_db_params, strict=False): """Perform a "physical" (non-ManyToMany) field update.""" # Use "ALTER TABLE ... RENAME COLUMN" if only the column name # changed and there aren't any constraints. - if ( - self.connection.features.can_alter_table_rename_column - and old_field.column != new_field.column - and self.column_sql(model, old_field) == self.column_sql(model, new_field) - and not ( - old_field.remote_field - and old_field.db_constraint - or new_field.remote_field - and new_field.db_constraint - ) - ): - return self.execute( - self._rename_field_sql( - model._meta.db_table, old_field, new_field, new_type - ) - ) + if (self.connection.features.can_alter_table_rename_column and + old_field.column != new_field.column and + self.column_sql(model, old_field) == self.column_sql(model, new_field) and + not (old_field.remote_field and old_field.db_constraint or + new_field.remote_field and new_field.db_constraint)): + return self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type)) # Alter by remaking table self._remake_table(model, alter_field=(old_field, new_field)) # Rebuild tables with FKs pointing to this field. @@ -455,25 +385,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def _alter_many_to_many(self, model, old_field, new_field, strict): """Alter M2Ms to repoint their to= endpoints.""" - if ( - old_field.remote_field.through._meta.db_table - == new_field.remote_field.through._meta.db_table - ): - # The field name didn't change, but some options did, so we have to - # propagate this altering. + if old_field.remote_field.through._meta.db_table == new_field.remote_field.through._meta.db_table: + # The field name didn't change, but some options did; we have to propagate this altering. self._remake_table( old_field.remote_field.through, alter_field=( - # The field that points to the target model is needed, so - # we can tell alter_field to change it - this is - # m2m_reverse_field_name() (as opposed to m2m_field_name(), - # which points to our model). - old_field.remote_field.through._meta.get_field( - old_field.m2m_reverse_field_name() - ), - new_field.remote_field.through._meta.get_field( - new_field.m2m_reverse_field_name() - ), + # We need the field that points to the target model, so we can tell alter_field to change it - + # this is m2m_reverse_field_name() (as opposed to m2m_field_name, which points to our model) + old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()), + new_field.remote_field.through._meta.get_field(new_field.m2m_reverse_field_name()), ), ) return @@ -481,51 +401,34 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # Make a new through table self.create_model(new_field.remote_field.through) # Copy the data across - self.execute( - "INSERT INTO %s (%s) SELECT %s FROM %s" - % ( - self.quote_name(new_field.remote_field.through._meta.db_table), - ", ".join( - [ - "id", - new_field.m2m_column_name(), - new_field.m2m_reverse_name(), - ] - ), - ", ".join( - [ - "id", - old_field.m2m_column_name(), - old_field.m2m_reverse_name(), - ] - ), - self.quote_name(old_field.remote_field.through._meta.db_table), - ) - ) + self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % ( + self.quote_name(new_field.remote_field.through._meta.db_table), + ', '.join([ + "id", + new_field.m2m_column_name(), + new_field.m2m_reverse_name(), + ]), + ', '.join([ + "id", + old_field.m2m_column_name(), + old_field.m2m_reverse_name(), + ]), + self.quote_name(old_field.remote_field.through._meta.db_table), + )) # Delete the old through table self.delete_model(old_field.remote_field.through) def add_constraint(self, model, constraint): - if isinstance(constraint, UniqueConstraint) and ( - constraint.condition - or constraint.contains_expressions - or constraint.include - or constraint.deferrable - ): + if isinstance(constraint, UniqueConstraint) and constraint.condition: super().add_constraint(model, constraint) else: self._remake_table(model) def remove_constraint(self, model, constraint): - if isinstance(constraint, UniqueConstraint) and ( - constraint.condition - or constraint.contains_expressions - or constraint.include - or constraint.deferrable - ): + if isinstance(constraint, UniqueConstraint) and constraint.condition: super().remove_constraint(model, constraint) else: self._remake_table(model) def _collate_sql(self, collation): - return "COLLATE " + collation + return ' COLLATE ' + collation diff --git a/venv/Lib/site-packages/django/db/backends/utils.py b/venv/Lib/site-packages/django/db/backends/utils.py index 26d07d6..45c03ba 100644 --- a/venv/Lib/site-packages/django/db/backends/utils.py +++ b/venv/Lib/site-packages/django/db/backends/utils.py @@ -7,9 +7,8 @@ import time from contextlib import contextmanager from django.db import NotSupportedError -from django.utils.dateparse import parse_time -logger = logging.getLogger("django.db.backends") +logger = logging.getLogger('django.db.backends') class CursorWrapper: @@ -17,7 +16,7 @@ class CursorWrapper: self.cursor = cursor self.db = db - WRAP_ERROR_ATTRS = frozenset(["fetchone", "fetchmany", "fetchall", "nextset"]) + WRAP_ERROR_ATTRS = frozenset(['fetchone', 'fetchmany', 'fetchall', 'nextset']) def __getattr__(self, attr): cursor_attr = getattr(self.cursor, attr) @@ -50,8 +49,8 @@ class CursorWrapper: # database driver may support them (e.g. cx_Oracle). if kparams is not None and not self.db.features.supports_callproc_kwargs: raise NotSupportedError( - "Keyword parameters for callproc are not supported on this " - "database backend." + 'Keyword parameters for callproc are not supported on this ' + 'database backend.' ) self.db.validate_no_broken_transaction() with self.db.wrap_database_errors: @@ -64,17 +63,13 @@ class CursorWrapper: return self.cursor.callproc(procname, params, kparams) def execute(self, sql, params=None): - return self._execute_with_wrappers( - sql, params, many=False, executor=self._execute - ) + return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) def executemany(self, sql, param_list): - return self._execute_with_wrappers( - sql, param_list, many=True, executor=self._executemany - ) + return self._execute_with_wrappers(sql, param_list, many=True, executor=self._executemany) def _execute_with_wrappers(self, sql, params, many, executor): - context = {"connection": self.db, "cursor": self} + context = {'connection': self.db, 'cursor': self} for wrapper in reversed(self.db.execute_wrappers): executor = functools.partial(wrapper, executor) return executor(sql, params, many, context) @@ -107,9 +102,7 @@ class CursorDebugWrapper(CursorWrapper): return super().executemany(sql, param_list) @contextmanager - def debug_sql( - self, sql=None, params=None, use_last_executed_query=False, many=False - ): + def debug_sql(self, sql=None, params=None, use_last_executed_query=False, many=False): start = time.monotonic() try: yield @@ -119,65 +112,40 @@ class CursorDebugWrapper(CursorWrapper): if use_last_executed_query: sql = self.db.ops.last_executed_query(self.cursor, sql, params) try: - times = len(params) if many else "" + times = len(params) if many else '' except TypeError: # params could be an iterator. - times = "?" - self.db.queries_log.append( - { - "sql": "%s times: %s" % (times, sql) if many else sql, - "time": "%.3f" % duration, - } - ) + times = '?' + self.db.queries_log.append({ + 'sql': '%s times: %s' % (times, sql) if many else sql, + 'time': '%.3f' % duration, + }) logger.debug( - "(%.3f) %s; args=%s; alias=%s", + '(%.3f) %s; args=%s', duration, sql, params, - self.db.alias, - extra={ - "duration": duration, - "sql": sql, - "params": params, - "alias": self.db.alias, - }, + extra={'duration': duration, 'sql': sql, 'params': params}, ) -def split_tzname_delta(tzname): - """ - Split a time zone name into a 3-tuple of (name, sign, offset). - """ - for sign in ["+", "-"]: - if sign in tzname: - name, offset = tzname.rsplit(sign, 1) - if offset and parse_time(offset): - return name, sign, offset - return tzname, None, None - - ############################################### # Converters from database (string) to Python # ############################################### - def typecast_date(s): - return ( - datetime.date(*map(int, s.split("-"))) if s else None - ) # return None if s is null + return datetime.date(*map(int, s.split('-'))) if s else None # return None if s is null def typecast_time(s): # does NOT store time zone information if not s: return None - hour, minutes, seconds = s.split(":") - if "." in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split(".") + hour, minutes, seconds = s.split(':') + if '.' in seconds: # check whether seconds have a fractional part + seconds, microseconds = seconds.split('.') else: - microseconds = "0" - return datetime.time( - int(hour), int(minutes), int(seconds), int((microseconds + "000000")[:6]) - ) + microseconds = '0' + return datetime.time(int(hour), int(minutes), int(seconds), int((microseconds + '000000')[:6])) def typecast_timestamp(s): # does NOT store time zone information @@ -185,29 +153,25 @@ def typecast_timestamp(s): # does NOT store time zone information # "2005-07-29 09:56:00-05" if not s: return None - if " " not in s: + if ' ' not in s: return typecast_date(s) d, t = s.split() # Remove timezone information. - if "-" in t: - t, _ = t.split("-", 1) - elif "+" in t: - t, _ = t.split("+", 1) - dates = d.split("-") - times = t.split(":") + if '-' in t: + t, _ = t.split('-', 1) + elif '+' in t: + t, _ = t.split('+', 1) + dates = d.split('-') + times = t.split(':') seconds = times[2] - if "." in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split(".") + if '.' in seconds: # check whether seconds have a fractional part + seconds, microseconds = seconds.split('.') else: - microseconds = "0" + microseconds = '0' return datetime.datetime( - int(dates[0]), - int(dates[1]), - int(dates[2]), - int(times[0]), - int(times[1]), - int(seconds), - int((microseconds + "000000")[:6]), + int(dates[0]), int(dates[1]), int(dates[2]), + int(times[0]), int(times[1]), int(seconds), + int((microseconds + '000000')[:6]) ) @@ -215,7 +179,6 @@ def typecast_timestamp(s): # does NOT store time zone information # Converters from Python to database (string) # ############################################### - def split_identifier(identifier): """ Split an SQL identifier into a two element tuple of (namespace, name). @@ -226,7 +189,7 @@ def split_identifier(identifier): try: namespace, name = identifier.split('"."') except ValueError: - namespace, name = "", identifier + namespace, name = '', identifier return namespace.strip('"'), name.strip('"') @@ -244,11 +207,7 @@ def truncate_name(identifier, length=None, hash_len=4): return identifier digest = names_digest(name, length=hash_len) - return "%s%s%s" % ( - '%s"."' % namespace if namespace else "", - name[: length - hash_len], - digest, - ) + return '%s%s%s' % ('%s"."' % namespace if namespace else '', name[:length - hash_len], digest) def names_digest(*args, length): @@ -273,9 +232,7 @@ def format_number(value, max_digits, decimal_places): if max_digits is not None: context.prec = max_digits if decimal_places is not None: - value = value.quantize( - decimal.Decimal(1).scaleb(-decimal_places), context=context - ) + value = value.quantize(decimal.Decimal(1).scaleb(-decimal_places), context=context) else: context.traps[decimal.Rounded] = 1 value = context.create_decimal(value) diff --git a/venv/Lib/site-packages/django/db/migrations/autodetector.py b/venv/Lib/site-packages/django/db/migrations/autodetector.py index 1ca6df9..ccdf62b 100644 --- a/venv/Lib/site-packages/django/db/migrations/autodetector.py +++ b/venv/Lib/site-packages/django/db/migrations/autodetector.py @@ -9,11 +9,7 @@ from django.db.migrations.migration import Migration from django.db.migrations.operations.models import AlterModelOptions from django.db.migrations.optimizer import MigrationOptimizer from django.db.migrations.questioner import MigrationQuestioner -from django.db.migrations.utils import ( - COMPILED_REGEX_TYPE, - RegexObject, - resolve_relation, -) +from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject from django.utils.topological_sort import stable_topological_sort @@ -59,20 +55,19 @@ class MigrationAutodetector: elif isinstance(obj, tuple): return tuple(self.deep_deconstruct(value) for value in obj) elif isinstance(obj, dict): - return {key: self.deep_deconstruct(value) for key, value in obj.items()} + return { + key: self.deep_deconstruct(value) + for key, value in obj.items() + } elif isinstance(obj, functools.partial): - return ( - obj.func, - self.deep_deconstruct(obj.args), - self.deep_deconstruct(obj.keywords), - ) + return (obj.func, self.deep_deconstruct(obj.args), self.deep_deconstruct(obj.keywords)) elif isinstance(obj, COMPILED_REGEX_TYPE): return RegexObject(obj) elif isinstance(obj, type): # If this is a type that implements 'deconstruct' as an instance method, # avoid treating this as being deconstructible itself - see #22951 return obj - elif hasattr(obj, "deconstruct"): + elif hasattr(obj, 'deconstruct'): deconstructed = obj.deconstruct() if isinstance(obj, models.Field): # we have a field which also returns a name @@ -81,7 +76,10 @@ class MigrationAutodetector: return ( path, [self.deep_deconstruct(value) for value in args], - {key: self.deep_deconstruct(value) for key, value in kwargs.items()}, + { + key: self.deep_deconstruct(value) + for key, value in kwargs.items() + }, ) else: return obj @@ -96,7 +94,7 @@ class MigrationAutodetector: for name, field in sorted(fields.items()): deconstruction = self.deep_deconstruct(field) if field.remote_field and field.remote_field.model: - del deconstruction[2]["to"] + del deconstruction[2]['to'] fields_def.append(deconstruction) return fields_def @@ -125,34 +123,36 @@ class MigrationAutodetector: # Prepare some old/new state and model lists, separating # proxy models and ignoring unmigrated apps. + self.old_apps = self.from_state.concrete_apps + self.new_apps = self.to_state.apps self.old_model_keys = set() self.old_proxy_keys = set() self.old_unmanaged_keys = set() self.new_model_keys = set() self.new_proxy_keys = set() self.new_unmanaged_keys = set() - for (app_label, model_name), model_state in self.from_state.models.items(): - if not model_state.options.get("managed", True): - self.old_unmanaged_keys.add((app_label, model_name)) - elif app_label not in self.from_state.real_apps: - if model_state.options.get("proxy"): - self.old_proxy_keys.add((app_label, model_name)) + for al, mn in self.from_state.models: + model = self.old_apps.get_model(al, mn) + if not model._meta.managed: + self.old_unmanaged_keys.add((al, mn)) + elif al not in self.from_state.real_apps: + if model._meta.proxy: + self.old_proxy_keys.add((al, mn)) else: - self.old_model_keys.add((app_label, model_name)) + self.old_model_keys.add((al, mn)) - for (app_label, model_name), model_state in self.to_state.models.items(): - if not model_state.options.get("managed", True): - self.new_unmanaged_keys.add((app_label, model_name)) - elif app_label not in self.from_state.real_apps or ( - convert_apps and app_label in convert_apps + for al, mn in self.to_state.models: + model = self.new_apps.get_model(al, mn) + if not model._meta.managed: + self.new_unmanaged_keys.add((al, mn)) + elif ( + al not in self.from_state.real_apps or + (convert_apps and al in convert_apps) ): - if model_state.options.get("proxy"): - self.new_proxy_keys.add((app_label, model_name)) + if model._meta.proxy: + self.new_proxy_keys.add((al, mn)) else: - self.new_model_keys.add((app_label, model_name)) - - self.from_state.resolve_fields_and_relations() - self.to_state.resolve_fields_and_relations() + self.new_model_keys.add((al, mn)) # Renames have to come first self.generate_renamed_models() @@ -177,12 +177,8 @@ class MigrationAutodetector: # Generate index removal operations before field is removed self.generate_removed_constraints() self.generate_removed_indexes() - # Generate field renaming operations. + # Generate field operations self.generate_renamed_fields() - # Generate removal of foo together. - self.generate_removed_altered_unique_together() - self.generate_removed_altered_index_together() - # Generate field operations. self.generate_removed_fields() self.generate_added_fields() self.generate_altered_fields() @@ -213,7 +209,8 @@ class MigrationAutodetector: (app_label, model_name, field_name) for app_label, model_name in self.kept_model_keys for field_name in self.from_state.models[ - app_label, self.renamed_models.get((app_label, model_name), model_name) + app_label, + self.renamed_models.get((app_label, model_name), model_name) ].fields } self.new_field_keys = { @@ -225,22 +222,17 @@ class MigrationAutodetector: def _generate_through_model_map(self): """Through model map generation.""" for app_label, model_name in sorted(self.old_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] - for field_name, field in old_model_state.fields.items(): - if hasattr(field, "remote_field") and getattr( - field.remote_field, "through", None - ): - through_key = resolve_relation( - field.remote_field.through, app_label, model_name - ) - self.through_users[through_key] = ( - app_label, - old_model_name, - field_name, + for field_name in old_model_state.fields: + old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name) + if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None) and + not old_field.remote_field.through._meta.auto_created): + through_key = ( + old_field.remote_field.through._meta.app_label, + old_field.remote_field.through._meta.model_name, ) + self.through_users[through_key] = (app_label, old_model_name, field_name) @staticmethod def _resolve_dependency(dependency): @@ -248,11 +240,9 @@ class MigrationAutodetector: Return the resolved dependency and a boolean denoting whether or not it was swappable. """ - if dependency[0] != "__setting__": + if dependency[0] != '__setting__': return dependency, False - resolved_app_label, resolved_object_name = getattr( - settings, dependency[1] - ).split(".") + resolved_app_label, resolved_object_name = getattr(settings, dependency[1]).split('.') return (resolved_app_label, resolved_object_name.lower()) + dependency[2:], True def _build_migration_list(self, graph=None): @@ -292,9 +282,7 @@ class MigrationAutodetector: if dep[0] != app_label: # External app dependency. See if it's not yet # satisfied. - for other_operation in self.generated_operations.get( - dep[0], [] - ): + for other_operation in self.generated_operations.get(dep[0], []): if self.check_dependency(other_operation, dep): deps_satisfied = False break @@ -302,33 +290,20 @@ class MigrationAutodetector: break else: if is_swappable_dep: - operation_dependencies.add( - (original_dep[0], original_dep[1]) - ) + operation_dependencies.add((original_dep[0], original_dep[1])) elif dep[0] in self.migrations: - operation_dependencies.add( - (dep[0], self.migrations[dep[0]][-1].name) - ) + operation_dependencies.add((dep[0], self.migrations[dep[0]][-1].name)) else: - # If we can't find the other app, we add a - # first/last dependency, but only if we've - # already been through once and checked - # everything. + # If we can't find the other app, we add a first/last dependency, + # but only if we've already been through once and checked everything if chop_mode: - # If the app already exists, we add a - # dependency on the last migration, as - # we don't know which migration - # contains the target field. If it's - # not yet migrated or has no - # migrations, we use __first__. + # If the app already exists, we add a dependency on the last migration, + # as we don't know which migration contains the target field. + # If it's not yet migrated or has no migrations, we use __first__ if graph and graph.leaf_nodes(dep[0]): - operation_dependencies.add( - graph.leaf_nodes(dep[0])[0] - ) + operation_dependencies.add(graph.leaf_nodes(dep[0])[0]) else: - operation_dependencies.add( - (dep[0], "__first__") - ) + operation_dependencies.add((dep[0], "__first__")) else: deps_satisfied = False if deps_satisfied: @@ -340,33 +315,21 @@ class MigrationAutodetector: # Make a migration! Well, only if there's stuff to put in it if dependencies or chopped: if not self.generated_operations[app_label] or chop_mode: - subclass = type( - "Migration", - (Migration,), - {"operations": [], "dependencies": []}, - ) - instance = subclass( - "auto_%i" % (len(self.migrations.get(app_label, [])) + 1), - app_label, - ) + subclass = type("Migration", (Migration,), {"operations": [], "dependencies": []}) + instance = subclass("auto_%i" % (len(self.migrations.get(app_label, [])) + 1), app_label) instance.dependencies = list(dependencies) instance.operations = chopped instance.initial = app_label not in self.existing_apps self.migrations.setdefault(app_label, []).append(instance) chop_mode = False else: - self.generated_operations[app_label] = ( - chopped + self.generated_operations[app_label] - ) + self.generated_operations[app_label] = chopped + self.generated_operations[app_label] new_num_ops = sum(len(x) for x in self.generated_operations.values()) if new_num_ops == num_ops: if not chop_mode: chop_mode = True else: - raise ValueError( - "Cannot resolve operation dependencies: %r" - % self.generated_operations - ) + raise ValueError("Cannot resolve operation dependencies: %r" % self.generated_operations) num_ops = new_num_ops def _sort_migrations(self): @@ -388,9 +351,7 @@ class MigrationAutodetector: dependency_graph[op].add(op2) # we use a stable sort for deterministic tests & general behavior - self.generated_operations[app_label] = stable_topological_sort( - ops, dependency_graph - ) + self.generated_operations[app_label] = stable_topological_sort(ops, dependency_graph) def _optimize_migrations(self): # Add in internal dependencies among the migrations @@ -406,9 +367,7 @@ class MigrationAutodetector: # Optimize migrations for app_label, migrations in self.migrations.items(): for migration in migrations: - migration.operations = MigrationOptimizer().optimize( - migration.operations, app_label - ) + migration.operations = MigrationOptimizer().optimize(migration.operations, app_label) def check_dependency(self, operation, dependency): """ @@ -418,64 +377,63 @@ class MigrationAutodetector: # Created model if dependency[2] is None and dependency[3] is True: return ( - isinstance(operation, operations.CreateModel) - and operation.name_lower == dependency[1].lower() + isinstance(operation, operations.CreateModel) and + operation.name_lower == dependency[1].lower() ) # Created field elif dependency[2] is not None and dependency[3] is True: return ( - isinstance(operation, operations.CreateModel) - and operation.name_lower == dependency[1].lower() - and any(dependency[2] == x for x, y in operation.fields) - ) or ( - isinstance(operation, operations.AddField) - and operation.model_name_lower == dependency[1].lower() - and operation.name_lower == dependency[2].lower() + ( + isinstance(operation, operations.CreateModel) and + operation.name_lower == dependency[1].lower() and + any(dependency[2] == x for x, y in operation.fields) + ) or + ( + isinstance(operation, operations.AddField) and + operation.model_name_lower == dependency[1].lower() and + operation.name_lower == dependency[2].lower() + ) ) # Removed field elif dependency[2] is not None and dependency[3] is False: return ( - isinstance(operation, operations.RemoveField) - and operation.model_name_lower == dependency[1].lower() - and operation.name_lower == dependency[2].lower() + isinstance(operation, operations.RemoveField) and + operation.model_name_lower == dependency[1].lower() and + operation.name_lower == dependency[2].lower() ) # Removed model elif dependency[2] is None and dependency[3] is False: return ( - isinstance(operation, operations.DeleteModel) - and operation.name_lower == dependency[1].lower() + isinstance(operation, operations.DeleteModel) and + operation.name_lower == dependency[1].lower() ) # Field being altered elif dependency[2] is not None and dependency[3] == "alter": return ( - isinstance(operation, operations.AlterField) - and operation.model_name_lower == dependency[1].lower() - and operation.name_lower == dependency[2].lower() + isinstance(operation, operations.AlterField) and + operation.model_name_lower == dependency[1].lower() and + operation.name_lower == dependency[2].lower() ) # order_with_respect_to being unset for a field elif dependency[2] is not None and dependency[3] == "order_wrt_unset": return ( - isinstance(operation, operations.AlterOrderWithRespectTo) - and operation.name_lower == dependency[1].lower() - and (operation.order_with_respect_to or "").lower() - != dependency[2].lower() + isinstance(operation, operations.AlterOrderWithRespectTo) and + operation.name_lower == dependency[1].lower() and + (operation.order_with_respect_to or "").lower() != dependency[2].lower() ) # Field is removed and part of an index/unique_together elif dependency[2] is not None and dependency[3] == "foo_together_change": return ( - isinstance( - operation, - (operations.AlterUniqueTogether, operations.AlterIndexTogether), - ) - and operation.name_lower == dependency[1].lower() + isinstance(operation, (operations.AlterUniqueTogether, + operations.AlterIndexTogether)) and + operation.name_lower == dependency[1].lower() ) # Unknown dependency. Raise an error. else: raise ValueError("Can't handle dependency %r" % (dependency,)) def add_operation(self, app_label, operation, dependencies=None, beginning=False): - # Dependencies are - # (app_label, model_name, field_name, create/delete as True/False) + # Dependencies are (app_label, model_name, field_name, create/delete as True/False) operation._auto_deps = dependencies or [] if beginning: self.generated_operations.setdefault(app_label, []).insert(0, operation) @@ -488,17 +446,14 @@ class MigrationAutodetector: real way to solve #22783). """ try: - model_state = self.to_state.models[item] - base_names = { - base if isinstance(base, str) else base.__name__ - for base in model_state.bases - } + model = self.new_apps.get_model(item[0], item[1]) + base_names = [base.__name__ for base in model.__bases__] string_version = "%s.%s" % (item[0], item[1]) if ( - model_state.options.get("swappable") - or "AbstractUser" in base_names - or "AbstractBaseUser" in base_names - or settings.AUTH_USER_MODEL.lower() == string_version.lower() + model._meta.swappable or + "AbstractUser" in base_names or + "AbstractBaseUser" in base_names or + settings.AUTH_USER_MODEL.lower() == string_version.lower() ): return ("___" + item[0], "___" + item[1]) except LookupError: @@ -521,34 +476,15 @@ class MigrationAutodetector: removed_models = self.old_model_keys - self.new_model_keys for rem_app_label, rem_model_name in removed_models: if rem_app_label == app_label: - rem_model_state = self.from_state.models[ - rem_app_label, rem_model_name - ] - rem_model_fields_def = self.only_relation_agnostic_fields( - rem_model_state.fields - ) + rem_model_state = self.from_state.models[rem_app_label, rem_model_name] + rem_model_fields_def = self.only_relation_agnostic_fields(rem_model_state.fields) if model_fields_def == rem_model_fields_def: - if self.questioner.ask_rename_model( - rem_model_state, model_state - ): + if self.questioner.ask_rename_model(rem_model_state, model_state): + model_opts = self.new_apps.get_model(app_label, model_name)._meta dependencies = [] - fields = list(model_state.fields.values()) + [ - field.remote_field - for relations in self.to_state.relations[ - app_label, model_name - ].values() - for field in relations.values() - ] - for field in fields: + for field in model_opts.get_fields(): if field.is_relation: - dependencies.extend( - self._get_dependencies_for_foreign_key( - app_label, - model_name, - field, - self.to_state, - ) - ) + dependencies.extend(self._get_dependencies_for_foreign_key(field)) self.add_operation( app_label, operations.RenameModel( @@ -558,13 +494,11 @@ class MigrationAutodetector: dependencies=dependencies, ) self.renamed_models[app_label, model_name] = rem_model_name - renamed_models_rel_key = "%s.%s" % ( + renamed_models_rel_key = '%s.%s' % ( rem_model_state.app_label, rem_model_state.name_lower, ) - self.renamed_models_rel[ - renamed_models_rel_key - ] = "%s.%s" % ( + self.renamed_models_rel[renamed_models_rel_key] = '%s.%s' % ( model_state.app_label, model_state.name_lower, ) @@ -587,31 +521,37 @@ class MigrationAutodetector: added_unmanaged_models = self.new_unmanaged_keys - old_keys all_added_models = chain( sorted(added_models, key=self.swappable_first_key, reverse=True), - sorted(added_unmanaged_models, key=self.swappable_first_key, reverse=True), + sorted(added_unmanaged_models, key=self.swappable_first_key, reverse=True) ) for app_label, model_name in all_added_models: model_state = self.to_state.models[app_label, model_name] + model_opts = self.new_apps.get_model(app_label, model_name)._meta # Gather related fields related_fields = {} primary_key_rel = None - for field_name, field in model_state.fields.items(): + for field in model_opts.local_fields: if field.remote_field: if field.remote_field.model: if field.primary_key: primary_key_rel = field.remote_field.model elif not field.remote_field.parent_link: - related_fields[field_name] = field - if getattr(field.remote_field, "through", None): - related_fields[field_name] = field - + related_fields[field.name] = field + # through will be none on M2Ms on swapped-out models; + # we can treat lack of through as auto_created=True, though. + if (getattr(field.remote_field, "through", None) and + not field.remote_field.through._meta.auto_created): + related_fields[field.name] = field + for field in model_opts.local_many_to_many: + if field.remote_field.model: + related_fields[field.name] = field + if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created: + related_fields[field.name] = field # Are there indexes/unique|index_together to defer? - indexes = model_state.options.pop("indexes") - constraints = model_state.options.pop("constraints") - unique_together = model_state.options.pop("unique_together", None) - index_together = model_state.options.pop("index_together", None) - order_with_respect_to = model_state.options.pop( - "order_with_respect_to", None - ) + indexes = model_state.options.pop('indexes') + constraints = model_state.options.pop('constraints') + unique_together = model_state.options.pop('unique_together', None) + index_together = model_state.options.pop('index_together', None) + order_with_respect_to = model_state.options.pop('order_with_respect_to', None) # Depend on the deletion of any possible proxy version of us dependencies = [ (app_label, model_name, None, False), @@ -623,44 +563,28 @@ class MigrationAutodetector: dependencies.append((base_app_label, base_name, None, True)) # Depend on the removal of base fields if the new model has # a field with the same name. - old_base_model_state = self.from_state.models.get( - (base_app_label, base_name) - ) - new_base_model_state = self.to_state.models.get( - (base_app_label, base_name) - ) + old_base_model_state = self.from_state.models.get((base_app_label, base_name)) + new_base_model_state = self.to_state.models.get((base_app_label, base_name)) if old_base_model_state and new_base_model_state: - removed_base_fields = ( - set(old_base_model_state.fields) - .difference( - new_base_model_state.fields, - ) - .intersection(model_state.fields) - ) + removed_base_fields = set(old_base_model_state.fields).difference( + new_base_model_state.fields, + ).intersection(model_state.fields) for removed_base_field in removed_base_fields: - dependencies.append( - (base_app_label, base_name, removed_base_field, False) - ) + dependencies.append((base_app_label, base_name, removed_base_field, False)) # Depend on the other end of the primary key if it's a relation if primary_key_rel: - dependencies.append( - resolve_relation( - primary_key_rel, - app_label, - model_name, - ) - + (None, True) - ) + dependencies.append(( + primary_key_rel._meta.app_label, + primary_key_rel._meta.object_name, + None, + True + )) # Generate creation operation self.add_operation( app_label, operations.CreateModel( name=model_state.name, - fields=[ - d - for d in model_state.fields.items() - if d[0] not in related_fields - ], + fields=[d for d in model_state.fields.items() if d[0] not in related_fields], options=model_state.options, bases=model_state.bases, managers=model_state.managers, @@ -670,17 +594,12 @@ class MigrationAutodetector: ) # Don't add operations which modify the database for unmanaged models - if not model_state.options.get("managed", True): + if not model_opts.managed: continue # Generate operations for each related field for name, field in sorted(related_fields.items()): - dependencies = self._get_dependencies_for_foreign_key( - app_label, - model_name, - field, - self.to_state, - ) + dependencies = self._get_dependencies_for_foreign_key(field) # Depend on our own model being created dependencies.append((app_label, model_name, None, True)) # Make operation @@ -704,10 +623,11 @@ class MigrationAutodetector: dependencies=[ (app_label, model_name, order_with_respect_to, True), (app_label, model_name, None, True), - ], + ] ) related_dependencies = [ - (app_label, model_name, name, True) for name in sorted(related_fields) + (app_label, model_name, name, True) + for name in sorted(related_fields) ] related_dependencies.append((app_label, model_name, None, True)) for index in indexes: @@ -735,7 +655,7 @@ class MigrationAutodetector: name=model_name, unique_together=unique_together, ), - dependencies=related_dependencies, + dependencies=related_dependencies ) if index_together: self.add_operation( @@ -744,26 +664,21 @@ class MigrationAutodetector: name=model_name, index_together=index_together, ), - dependencies=related_dependencies, + dependencies=related_dependencies ) # Fix relationships if the model changed from a proxy model to a # concrete model. - relations = self.to_state.relations if (app_label, model_name) in self.old_proxy_keys: - for related_model_key, related_fields in relations[ - app_label, model_name - ].items(): - related_model_state = self.to_state.models[related_model_key] - for related_field_name, related_field in related_fields.items(): - self.add_operation( - related_model_state.app_label, - operations.AlterField( - model_name=related_model_state.name, - name=related_field_name, - field=related_field, - ), - dependencies=[(app_label, model_name, None, True)], - ) + for related_object in model_opts.related_objects: + self.add_operation( + related_object.related_model._meta.app_label, + operations.AlterField( + model_name=related_object.related_model._meta.object_name, + name=related_object.field.name, + field=related_object.field, + ), + dependencies=[(app_label, model_name, None, True)], + ) def generate_created_proxies(self): """ @@ -811,29 +726,36 @@ class MigrationAutodetector: new_keys = self.new_model_keys | self.new_unmanaged_keys deleted_models = self.old_model_keys - new_keys deleted_unmanaged_models = self.old_unmanaged_keys - new_keys - all_deleted_models = chain( - sorted(deleted_models), sorted(deleted_unmanaged_models) - ) + all_deleted_models = chain(sorted(deleted_models), sorted(deleted_unmanaged_models)) for app_label, model_name in all_deleted_models: model_state = self.from_state.models[app_label, model_name] + model = self.old_apps.get_model(app_label, model_name) # Gather related fields related_fields = {} - for field_name, field in model_state.fields.items(): + for field in model._meta.local_fields: if field.remote_field: if field.remote_field.model: - related_fields[field_name] = field - if getattr(field.remote_field, "through", None): - related_fields[field_name] = field + related_fields[field.name] = field + # through will be none on M2Ms on swapped-out models; + # we can treat lack of through as auto_created=True, though. + if (getattr(field.remote_field, "through", None) and + not field.remote_field.through._meta.auto_created): + related_fields[field.name] = field + for field in model._meta.local_many_to_many: + if field.remote_field.model: + related_fields[field.name] = field + if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created: + related_fields[field.name] = field # Generate option removal first - unique_together = model_state.options.pop("unique_together", None) - index_together = model_state.options.pop("index_together", None) + unique_together = model_state.options.pop('unique_together', None) + index_together = model_state.options.pop('index_together', None) if unique_together: self.add_operation( app_label, operations.AlterUniqueTogether( name=model_name, unique_together=None, - ), + ) ) if index_together: self.add_operation( @@ -841,7 +763,7 @@ class MigrationAutodetector: operations.AlterIndexTogether( name=model_name, index_together=None, - ), + ) ) # Then remove each related field for name in sorted(related_fields): @@ -850,40 +772,27 @@ class MigrationAutodetector: operations.RemoveField( model_name=model_name, name=name, - ), + ) ) # Finally, remove the model. # This depends on both the removal/alteration of all incoming fields # and the removal of all its own related fields, and if it's # a through model the field that references it. dependencies = [] - relations = self.from_state.relations - for ( - related_object_app_label, - object_name, - ), relation_related_fields in relations[app_label, model_name].items(): - for field_name, field in relation_related_fields.items(): - dependencies.append( - (related_object_app_label, object_name, field_name, False), - ) - if not field.many_to_many: - dependencies.append( - ( - related_object_app_label, - object_name, - field_name, - "alter", - ), - ) + for related_object in model._meta.related_objects: + related_object_app_label = related_object.related_model._meta.app_label + object_name = related_object.related_model._meta.object_name + field_name = related_object.field.name + dependencies.append((related_object_app_label, object_name, field_name, False)) + if not related_object.many_to_many: + dependencies.append((related_object_app_label, object_name, field_name, "alter")) for name in sorted(related_fields): dependencies.append((app_label, model_name, name, False)) # We're referenced in another field's through= through_user = self.through_users.get((app_label, model_state.name_lower)) if through_user: - dependencies.append( - (through_user[0], through_user[1], through_user[2], False) - ) + dependencies.append((through_user[0], through_user[1], through_user[2], False)) # Finally, make the operation, deduping any dependencies self.add_operation( app_label, @@ -909,100 +818,66 @@ class MigrationAutodetector: def generate_renamed_fields(self): """Work out renamed fields.""" self.renamed_fields = {} - for app_label, model_name, field_name in sorted( - self.new_field_keys - self.old_field_keys - ): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + for app_label, model_name, field_name in sorted(self.new_field_keys - self.old_field_keys): + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] - new_model_state = self.to_state.models[app_label, model_name] - field = new_model_state.get_field(field_name) + field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name) # Scan to see if this is actually a rename! field_dec = self.deep_deconstruct(field) - for rem_app_label, rem_model_name, rem_field_name in sorted( - self.old_field_keys - self.new_field_keys - ): + for rem_app_label, rem_model_name, rem_field_name in sorted(self.old_field_keys - self.new_field_keys): if rem_app_label == app_label and rem_model_name == model_name: - old_field = old_model_state.get_field(rem_field_name) + old_field = old_model_state.fields[rem_field_name] old_field_dec = self.deep_deconstruct(old_field) - if ( - field.remote_field - and field.remote_field.model - and "to" in old_field_dec[2] - ): - old_rel_to = old_field_dec[2]["to"] + if field.remote_field and field.remote_field.model and 'to' in old_field_dec[2]: + old_rel_to = old_field_dec[2]['to'] if old_rel_to in self.renamed_models_rel: - old_field_dec[2]["to"] = self.renamed_models_rel[old_rel_to] + old_field_dec[2]['to'] = self.renamed_models_rel[old_rel_to] old_field.set_attributes_from_name(rem_field_name) old_db_column = old_field.get_attname_column()[1] - if old_field_dec == field_dec or ( - # Was the field renamed and db_column equal to the - # old field's column added? - old_field_dec[0:2] == field_dec[0:2] - and dict(old_field_dec[2], db_column=old_db_column) - == field_dec[2] - ): - if self.questioner.ask_rename( - model_name, rem_field_name, field_name, field - ): + if (old_field_dec == field_dec or ( + # Was the field renamed and db_column equal to the + # old field's column added? + old_field_dec[0:2] == field_dec[0:2] and + dict(old_field_dec[2], db_column=old_db_column) == field_dec[2])): + if self.questioner.ask_rename(model_name, rem_field_name, field_name, field): self.add_operation( app_label, operations.RenameField( model_name=model_name, old_name=rem_field_name, new_name=field_name, - ), - ) - self.old_field_keys.remove( - (rem_app_label, rem_model_name, rem_field_name) + ) ) + self.old_field_keys.remove((rem_app_label, rem_model_name, rem_field_name)) self.old_field_keys.add((app_label, model_name, field_name)) - self.renamed_fields[ - app_label, model_name, field_name - ] = rem_field_name + self.renamed_fields[app_label, model_name, field_name] = rem_field_name break def generate_added_fields(self): """Make AddField operations.""" - for app_label, model_name, field_name in sorted( - self.new_field_keys - self.old_field_keys - ): + for app_label, model_name, field_name in sorted(self.new_field_keys - self.old_field_keys): self._generate_added_field(app_label, model_name, field_name) def _generate_added_field(self, app_label, model_name, field_name): - field = self.to_state.models[app_label, model_name].get_field(field_name) + field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name) # Fields that are foreignkeys/m2ms depend on stuff dependencies = [] if field.remote_field and field.remote_field.model: - dependencies.extend( - self._get_dependencies_for_foreign_key( - app_label, - model_name, - field, - self.to_state, - ) - ) + dependencies.extend(self._get_dependencies_for_foreign_key(field)) # You can't just add NOT NULL fields with no default or fields # which don't allow empty strings as default. time_fields = (models.DateField, models.DateTimeField, models.TimeField) preserve_default = ( - field.null - or field.has_default() - or field.many_to_many - or (field.blank and field.empty_strings_allowed) - or (isinstance(field, time_fields) and field.auto_now) + field.null or field.has_default() or field.many_to_many or + (field.blank and field.empty_strings_allowed) or + (isinstance(field, time_fields) and field.auto_now) ) if not preserve_default: field = field.clone() if isinstance(field, time_fields) and field.auto_now_add: - field.default = self.questioner.ask_auto_now_add_addition( - field_name, model_name - ) + field.default = self.questioner.ask_auto_now_add_addition(field_name, model_name) else: - field.default = self.questioner.ask_not_null_addition( - field_name, model_name - ) + field.default = self.questioner.ask_not_null_addition(field_name, model_name) self.add_operation( app_label, operations.AddField( @@ -1016,9 +891,7 @@ class MigrationAutodetector: def generate_removed_fields(self): """Make RemoveField operations.""" - for app_label, model_name, field_name in sorted( - self.old_field_keys - self.new_field_keys - ): + for app_label, model_name, field_name in sorted(self.old_field_keys - self.new_field_keys): self._generate_removed_field(app_label, model_name, field_name) def _generate_removed_field(self, app_label, model_name, field_name): @@ -1040,37 +913,26 @@ class MigrationAutodetector: def generate_altered_fields(self): """ Make AlterField operations, or possibly RemovedField/AddField if alter - isn't possible. + isn's possible. """ - for app_label, model_name, field_name in sorted( - self.old_field_keys & self.new_field_keys - ): + for app_label, model_name, field_name in sorted(self.old_field_keys & self.new_field_keys): # Did the field change? - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) - old_field_name = self.renamed_fields.get( - (app_label, model_name, field_name), field_name - ) - old_field = self.from_state.models[app_label, old_model_name].get_field( - old_field_name - ) - new_field = self.to_state.models[app_label, model_name].get_field( - field_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) + old_field_name = self.renamed_fields.get((app_label, model_name, field_name), field_name) + old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(old_field_name) + new_field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name) dependencies = [] # Implement any model renames on relations; these are handled by RenameModel # so we need to exclude them from the comparison - if hasattr(new_field, "remote_field") and getattr( - new_field.remote_field, "model", None - ): - rename_key = resolve_relation( - new_field.remote_field.model, app_label, model_name + if hasattr(new_field, "remote_field") and getattr(new_field.remote_field, "model", None): + rename_key = ( + new_field.remote_field.model._meta.app_label, + new_field.remote_field.model._meta.model_name, ) if rename_key in self.renamed_models: new_field.remote_field.model = old_field.remote_field.model # Handle ForeignKey which can only have a single to_field. - remote_field_name = getattr(new_field.remote_field, "field_name", None) + remote_field_name = getattr(new_field.remote_field, 'field_name', None) if remote_field_name: to_field_rename_key = rename_key + (remote_field_name,) if to_field_rename_key in self.renamed_fields: @@ -1078,40 +940,24 @@ class MigrationAutodetector: # inclusion in ForeignKey.deconstruct() is based on # both. new_field.remote_field.model = old_field.remote_field.model - new_field.remote_field.field_name = ( - old_field.remote_field.field_name - ) + new_field.remote_field.field_name = old_field.remote_field.field_name # Handle ForeignObjects which can have multiple from_fields/to_fields. - from_fields = getattr(new_field, "from_fields", None) + from_fields = getattr(new_field, 'from_fields', None) if from_fields: from_rename_key = (app_label, model_name) - new_field.from_fields = tuple( - [ - self.renamed_fields.get( - from_rename_key + (from_field,), from_field - ) - for from_field in from_fields - ] - ) - new_field.to_fields = tuple( - [ - self.renamed_fields.get(rename_key + (to_field,), to_field) - for to_field in new_field.to_fields - ] - ) - dependencies.extend( - self._get_dependencies_for_foreign_key( - app_label, - model_name, - new_field, - self.to_state, - ) - ) - if hasattr(new_field, "remote_field") and getattr( - new_field.remote_field, "through", None - ): - rename_key = resolve_relation( - new_field.remote_field.through, app_label, model_name + new_field.from_fields = tuple([ + self.renamed_fields.get(from_rename_key + (from_field,), from_field) + for from_field in from_fields + ]) + new_field.to_fields = tuple([ + self.renamed_fields.get(rename_key + (to_field,), to_field) + for to_field in new_field.to_fields + ]) + dependencies.extend(self._get_dependencies_for_foreign_key(new_field)) + if hasattr(new_field, "remote_field") and getattr(new_field.remote_field, "through", None): + rename_key = ( + new_field.remote_field.through._meta.app_label, + new_field.remote_field.through._meta.model_name, ) if rename_key in self.renamed_models: new_field.remote_field.through = old_field.remote_field.through @@ -1123,16 +969,10 @@ class MigrationAutodetector: if both_m2m or neither_m2m: # Either both fields are m2m or neither is preserve_default = True - if ( - old_field.null - and not new_field.null - and not new_field.has_default() - and not new_field.many_to_many - ): + if (old_field.null and not new_field.null and not new_field.has_default() and + not new_field.many_to_many): field = new_field.clone() - new_default = self.questioner.ask_not_null_alteration( - field_name, model_name - ) + new_default = self.questioner.ask_not_null_alteration(field_name, model_name) if new_default is not models.NOT_PROVIDED: field.default = new_default preserve_default = False @@ -1156,9 +996,7 @@ class MigrationAutodetector: def create_altered_indexes(self): option_name = operations.AddIndex.option_name for app_label, model_name in sorted(self.kept_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] @@ -1167,43 +1005,38 @@ class MigrationAutodetector: add_idx = [idx for idx in new_indexes if idx not in old_indexes] rem_idx = [idx for idx in old_indexes if idx not in new_indexes] - self.altered_indexes.update( - { - (app_label, model_name): { - "added_indexes": add_idx, - "removed_indexes": rem_idx, - } + self.altered_indexes.update({ + (app_label, model_name): { + 'added_indexes': add_idx, 'removed_indexes': rem_idx, } - ) + }) def generate_added_indexes(self): for (app_label, model_name), alt_indexes in self.altered_indexes.items(): - for index in alt_indexes["added_indexes"]: + for index in alt_indexes['added_indexes']: self.add_operation( app_label, operations.AddIndex( model_name=model_name, index=index, - ), + ) ) def generate_removed_indexes(self): for (app_label, model_name), alt_indexes in self.altered_indexes.items(): - for index in alt_indexes["removed_indexes"]: + for index in alt_indexes['removed_indexes']: self.add_operation( app_label, operations.RemoveIndex( model_name=model_name, name=index.name, - ), + ) ) def create_altered_constraints(self): option_name = operations.AddConstraint.option_name for app_label, model_name in sorted(self.kept_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] @@ -1212,99 +1045,69 @@ class MigrationAutodetector: add_constraints = [c for c in new_constraints if c not in old_constraints] rem_constraints = [c for c in old_constraints if c not in new_constraints] - self.altered_constraints.update( - { - (app_label, model_name): { - "added_constraints": add_constraints, - "removed_constraints": rem_constraints, - } + self.altered_constraints.update({ + (app_label, model_name): { + 'added_constraints': add_constraints, 'removed_constraints': rem_constraints, } - ) + }) def generate_added_constraints(self): - for ( - app_label, - model_name, - ), alt_constraints in self.altered_constraints.items(): - for constraint in alt_constraints["added_constraints"]: + for (app_label, model_name), alt_constraints in self.altered_constraints.items(): + for constraint in alt_constraints['added_constraints']: self.add_operation( app_label, operations.AddConstraint( model_name=model_name, constraint=constraint, - ), + ) ) def generate_removed_constraints(self): - for ( - app_label, - model_name, - ), alt_constraints in self.altered_constraints.items(): - for constraint in alt_constraints["removed_constraints"]: + for (app_label, model_name), alt_constraints in self.altered_constraints.items(): + for constraint in alt_constraints['removed_constraints']: self.add_operation( app_label, operations.RemoveConstraint( model_name=model_name, name=constraint.name, - ), + ) ) - @staticmethod - def _get_dependencies_for_foreign_key(app_label, model_name, field, project_state): - remote_field_model = None - if hasattr(field.remote_field, "model"): - remote_field_model = field.remote_field.model - else: - relations = project_state.relations[app_label, model_name] - for (remote_app_label, remote_model_name), fields in relations.items(): - if any( - field == related_field.remote_field - for related_field in fields.values() - ): - remote_field_model = f"{remote_app_label}.{remote_model_name}" - break + def _get_dependencies_for_foreign_key(self, field): # Account for FKs to swappable models - swappable_setting = getattr(field, "swappable_setting", None) + swappable_setting = getattr(field, 'swappable_setting', None) if swappable_setting is not None: dep_app_label = "__setting__" dep_object_name = swappable_setting else: - dep_app_label, dep_object_name = resolve_relation( - remote_field_model, - app_label, - model_name, - ) + dep_app_label = field.remote_field.model._meta.app_label + dep_object_name = field.remote_field.model._meta.object_name dependencies = [(dep_app_label, dep_object_name, None, True)] - if getattr(field.remote_field, "through", None): - through_app_label, through_object_name = resolve_relation( - remote_field_model, - app_label, - model_name, - ) - dependencies.append((through_app_label, through_object_name, None, True)) + if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created: + dependencies.append(( + field.remote_field.through._meta.app_label, + field.remote_field.through._meta.object_name, + None, + True, + )) return dependencies - def _get_altered_foo_together_operations(self, option_name): + def _generate_altered_foo_together(self, operation): + option_name = operation.option_name for app_label, model_name in sorted(self.kept_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] # We run the old version through the field renames to account for those old_value = old_model_state.options.get(option_name) - old_value = ( - { - tuple( - self.renamed_fields.get((app_label, model_name, n), n) - for n in unique - ) - for unique in old_value - } - if old_value - else set() - ) + old_value = { + tuple( + self.renamed_fields.get((app_label, model_name, n), n) + for n in unique + ) + for unique in old_value + } if old_value else set() new_value = new_model_state.options.get(option_name) new_value = set(new_value) if new_value else set() @@ -1313,64 +1116,19 @@ class MigrationAutodetector: dependencies = [] for foo_togethers in new_value: for field_name in foo_togethers: - field = new_model_state.get_field(field_name) + field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name) if field.remote_field and field.remote_field.model: - dependencies.extend( - self._get_dependencies_for_foreign_key( - app_label, - model_name, - field, - self.to_state, - ) - ) - yield ( - old_value, - new_value, - app_label, - model_name, - dependencies, - ) + dependencies.extend(self._get_dependencies_for_foreign_key(field)) - def _generate_removed_altered_foo_together(self, operation): - for ( - old_value, - new_value, - app_label, - model_name, - dependencies, - ) in self._get_altered_foo_together_operations(operation.option_name): - removal_value = new_value.intersection(old_value) - if removal_value or old_value: self.add_operation( app_label, operation( - name=model_name, **{operation.option_name: removal_value} + name=model_name, + **{option_name: new_value} ), dependencies=dependencies, ) - def generate_removed_altered_unique_together(self): - self._generate_removed_altered_foo_together(operations.AlterUniqueTogether) - - def generate_removed_altered_index_together(self): - self._generate_removed_altered_foo_together(operations.AlterIndexTogether) - - def _generate_altered_foo_together(self, operation): - for ( - old_value, - new_value, - app_label, - model_name, - dependencies, - ) in self._get_altered_foo_together_operations(operation.option_name): - removal_value = new_value.intersection(old_value) - if new_value != removal_value: - self.add_operation( - app_label, - operation(name=model_name, **{operation.option_name: new_value}), - dependencies=dependencies, - ) - def generate_altered_unique_together(self): self._generate_altered_foo_together(operations.AlterUniqueTogether) @@ -1378,24 +1136,20 @@ class MigrationAutodetector: self._generate_altered_foo_together(operations.AlterIndexTogether) def generate_altered_db_table(self): - models_to_check = self.kept_model_keys.union( - self.kept_proxy_keys, self.kept_unmanaged_keys - ) + models_to_check = self.kept_model_keys.union(self.kept_proxy_keys, self.kept_unmanaged_keys) for app_label, model_name in sorted(models_to_check): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] - old_db_table_name = old_model_state.options.get("db_table") - new_db_table_name = new_model_state.options.get("db_table") + old_db_table_name = old_model_state.options.get('db_table') + new_db_table_name = new_model_state.options.get('db_table') if old_db_table_name != new_db_table_name: self.add_operation( app_label, operations.AlterModelTable( name=model_name, table=new_db_table_name, - ), + ) ) def generate_altered_options(self): @@ -1414,19 +1168,15 @@ class MigrationAutodetector: ) for app_label, model_name in sorted(models_to_check): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] old_options = { - key: value - for key, value in old_model_state.options.items() + key: value for key, value in old_model_state.options.items() if key in AlterModelOptions.ALTER_OPTION_KEYS } new_options = { - key: value - for key, value in new_model_state.options.items() + key: value for key, value in new_model_state.options.items() if key in AlterModelOptions.ALTER_OPTION_KEYS } if old_options != new_options: @@ -1435,48 +1185,39 @@ class MigrationAutodetector: operations.AlterModelOptions( name=model_name, options=new_options, - ), + ) ) def generate_altered_order_with_respect_to(self): for app_label, model_name in sorted(self.kept_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] - if old_model_state.options.get( - "order_with_respect_to" - ) != new_model_state.options.get("order_with_respect_to"): + if (old_model_state.options.get("order_with_respect_to") != + new_model_state.options.get("order_with_respect_to")): # Make sure it comes second if we're adding # (removal dependency is part of RemoveField) dependencies = [] if new_model_state.options.get("order_with_respect_to"): - dependencies.append( - ( - app_label, - model_name, - new_model_state.options["order_with_respect_to"], - True, - ) - ) + dependencies.append(( + app_label, + model_name, + new_model_state.options["order_with_respect_to"], + True, + )) # Actually generate the operation self.add_operation( app_label, operations.AlterOrderWithRespectTo( name=model_name, - order_with_respect_to=new_model_state.options.get( - "order_with_respect_to" - ), + order_with_respect_to=new_model_state.options.get('order_with_respect_to'), ), dependencies=dependencies, ) def generate_altered_managers(self): for app_label, model_name in sorted(self.kept_model_keys): - old_model_name = self.renamed_models.get( - (app_label, model_name), model_name - ) + old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] if old_model_state.managers != new_model_state.managers: @@ -1485,7 +1226,7 @@ class MigrationAutodetector: operations.AlterModelManagers( name=model_name, managers=new_model_state.managers, - ), + ) ) def arrange_for_graph(self, changes, graph, migration_name=None): @@ -1521,23 +1262,21 @@ class MigrationAutodetector: for i, migration in enumerate(migrations): if i == 0 and app_leaf: migration.dependencies.append(app_leaf) - new_name_parts = ["%04i" % next_number] + new_name_parts = ['%04i' % next_number] if migration_name: new_name_parts.append(migration_name) elif i == 0 and not app_leaf: - new_name_parts.append("initial") + new_name_parts.append('initial') else: new_name_parts.append(migration.suggest_name()[:100]) - new_name = "_".join(new_name_parts) + new_name = '_'.join(new_name_parts) name_map[(app_label, migration.name)] = (app_label, new_name) next_number += 1 migration.name = new_name # Now fix dependencies for migrations in changes.values(): for migration in migrations: - migration.dependencies = [ - name_map.get(d, d) for d in migration.dependencies - ] + migration.dependencies = [name_map.get(d, d) for d in migration.dependencies] return changes def _trim_to_apps(self, changes, app_labels): @@ -1558,9 +1297,7 @@ class MigrationAutodetector: old_required_apps = None while old_required_apps != required_apps: old_required_apps = set(required_apps) - required_apps.update( - *[app_dependencies.get(app_label, ()) for app_label in required_apps] - ) + required_apps.update(*[app_dependencies.get(app_label, ()) for app_label in required_apps]) # Remove all migrations that aren't needed for app_label in list(changes): if app_label not in required_apps: @@ -1573,7 +1310,7 @@ class MigrationAutodetector: Given a migration name, try to extract a number from the beginning of it. If no number is found, return None. """ - match = re.match(r"^\d+", name) + match = re.match(r'^\d+', name) if match: return int(match[0]) return None diff --git a/venv/Lib/site-packages/django/db/migrations/exceptions.py b/venv/Lib/site-packages/django/db/migrations/exceptions.py index dd556da..8def99d 100644 --- a/venv/Lib/site-packages/django/db/migrations/exceptions.py +++ b/venv/Lib/site-packages/django/db/migrations/exceptions.py @@ -3,37 +3,31 @@ from django.db import DatabaseError class AmbiguityError(Exception): """More than one migration matches a name prefix.""" - pass class BadMigrationError(Exception): """There's a bad migration (unreadable/bad format/etc.).""" - pass class CircularDependencyError(Exception): """There's an impossible-to-resolve circular dependency.""" - pass class InconsistentMigrationHistory(Exception): """An applied migration has some of its dependencies not applied.""" - pass class InvalidBasesError(ValueError): """A model's base classes can't be resolved.""" - pass class IrreversibleError(RuntimeError): """An irreversible migration is about to be reversed.""" - pass diff --git a/venv/Lib/site-packages/django/db/migrations/executor.py b/venv/Lib/site-packages/django/db/migrations/executor.py index 2d08f4d..57042a8 100644 --- a/venv/Lib/site-packages/django/db/migrations/executor.py +++ b/venv/Lib/site-packages/django/db/migrations/executor.py @@ -40,22 +40,13 @@ class MigrationExecutor: # If the migration is already applied, do backwards mode, # otherwise do forwards mode. elif target in applied: - # If the target is missing, it's likely a replaced migration. - # Reload the graph without replacements. - if ( - self.loader.replace_migrations - and target not in self.loader.graph.node_map - ): - self.loader.replace_migrations = False - self.loader.build_graph() - return self.migration_plan(targets, clean_start=clean_start) # Don't migrate backwards all the way to the target node (that # may roll back dependencies in other apps that don't need to # be rolled back); instead roll back through target's immediate # child(ren) in the same app, and no further. next_in_app = sorted( - n - for n in self.loader.graph.node_map[target].children + n for n in + self.loader.graph.node_map[target].children if n[0] == target[0] ) for node in next_in_app: @@ -75,15 +66,12 @@ class MigrationExecutor: Create a project state including all the applications without migrations and applied migrations if with_applied_migrations=True. """ - state = ProjectState(real_apps=self.loader.unmigrated_apps) + state = ProjectState(real_apps=list(self.loader.unmigrated_apps)) if with_applied_migrations: # Create the forwards plan Django would follow on an empty database - full_plan = self.migration_plan( - self.loader.graph.leaf_nodes(), clean_start=True - ) + full_plan = self.migration_plan(self.loader.graph.leaf_nodes(), clean_start=True) applied_migrations = { - self.loader.graph.nodes[key] - for key in self.loader.applied_migrations + self.loader.graph.nodes[key] for key in self.loader.applied_migrations if key in self.loader.graph.nodes } for migration, _ in full_plan: @@ -105,9 +93,7 @@ class MigrationExecutor: if plan is None: plan = self.migration_plan(targets) # Create the forwards plan Django would follow on an empty database - full_plan = self.migration_plan( - self.loader.graph.leaf_nodes(), clean_start=True - ) + full_plan = self.migration_plan(self.loader.graph.leaf_nodes(), clean_start=True) all_forwards = all(not backwards for mig, backwards in plan) all_backwards = all(backwards for mig, backwards in plan) @@ -122,15 +108,13 @@ class MigrationExecutor: "Migration plans with both forwards and backwards migrations " "are not supported. Please split your migration process into " "separate plans of only forwards OR backwards migrations.", - plan, + plan ) elif all_forwards: if state is None: # The resulting state should still include applied migrations. state = self._create_project_state(with_applied_migrations=True) - state = self._migrate_all_forwards( - state, plan, full_plan, fake=fake, fake_initial=fake_initial - ) + state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial) else: # No need to check for `elif all_backwards` here, as that condition # would always evaluate to true. @@ -154,15 +138,13 @@ class MigrationExecutor: # process. break if migration in migrations_to_run: - if "apps" not in state.__dict__: + if 'apps' not in state.__dict__: if self.progress_callback: self.progress_callback("render_start") state.apps # Render all -- performance critical if self.progress_callback: self.progress_callback("render_success") - state = self.apply_migration( - state, migration, fake=fake, fake_initial=fake_initial - ) + state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial) migrations_to_run.remove(migration) return state @@ -182,8 +164,7 @@ class MigrationExecutor: states = {} state = self._create_project_state() applied_migrations = { - self.loader.graph.nodes[key] - for key in self.loader.applied_migrations + self.loader.graph.nodes[key] for key in self.loader.applied_migrations if key in self.loader.graph.nodes } if self.progress_callback: @@ -196,7 +177,7 @@ class MigrationExecutor: # process. break if migration in migrations_to_run: - if "apps" not in state.__dict__: + if 'apps' not in state.__dict__: state.apps # Render all -- performance critical # The state before this migration states[migration] = state @@ -242,9 +223,7 @@ class MigrationExecutor: fake = True if not fake: # Alright, do it normally - with self.connection.schema_editor( - atomic=migration.atomic - ) as schema_editor: + with self.connection.schema_editor(atomic=migration.atomic) as schema_editor: state = migration.apply(state, schema_editor) if not schema_editor.deferred_sql: self.record_migration(migration) @@ -269,15 +248,14 @@ class MigrationExecutor: if self.progress_callback: self.progress_callback("unapply_start", migration, fake) if not fake: - with self.connection.schema_editor( - atomic=migration.atomic - ) as schema_editor: + with self.connection.schema_editor(atomic=migration.atomic) as schema_editor: state = migration.unapply(state, schema_editor) - # For replacement migrations, also record individual statuses. + # For replacement migrations, record individual statuses if migration.replaces: for app_label, name in migration.replaces: self.recorder.record_unapplied(app_label, name) - self.recorder.record_unapplied(migration.app_label, migration.name) + else: + self.recorder.record_unapplied(migration.app_label, migration.name) # Report progress if self.progress_callback: self.progress_callback("unapply_success", migration, fake) @@ -306,18 +284,15 @@ class MigrationExecutor: tables or columns it would create exist. This is intended only for use on initial migrations (as it only looks for CreateModel and AddField). """ - def should_skip_detecting_model(migration, model): """ No need to detect tables for proxy models, unmanaged models, or models that can't be migrated on the current database. """ return ( - model._meta.proxy - or not model._meta.managed - or not router.allow_migrate( - self.connection.alias, - migration.app_label, + model._meta.proxy or not model._meta.managed or not + router.allow_migrate( + self.connection.alias, migration.app_label, model_name=model._meta.model_name, ) ) @@ -331,9 +306,7 @@ class MigrationExecutor: return False, project_state if project_state is None: - after_state = self.loader.project_state( - (migration.app_label, migration.name), at_end=True - ) + after_state = self.loader.project_state((migration.app_label, migration.name), at_end=True) else: after_state = migration.mutate_state(project_state) apps = after_state.apps @@ -341,13 +314,9 @@ class MigrationExecutor: found_add_field_migration = False fold_identifier_case = self.connection.features.ignores_table_name_case with self.connection.cursor() as cursor: - existing_table_names = set( - self.connection.introspection.table_names(cursor) - ) + existing_table_names = set(self.connection.introspection.table_names(cursor)) if fold_identifier_case: - existing_table_names = { - name.casefold() for name in existing_table_names - } + existing_table_names = {name.casefold() for name in existing_table_names} # Make sure all create model and add field operations are done for operation in migration.operations: if isinstance(operation, migrations.CreateModel): @@ -387,9 +356,7 @@ class MigrationExecutor: found_add_field_migration = True continue with self.connection.cursor() as cursor: - columns = self.connection.introspection.get_table_description( - cursor, table - ) + columns = self.connection.introspection.get_table_description(cursor, table) for column in columns: field_column = field.column column_name = column.name @@ -401,6 +368,6 @@ class MigrationExecutor: break else: return False, project_state - # If we get this far and we found at least one CreateModel or AddField - # migration, the migration is considered implicitly applied. + # If we get this far and we found at least one CreateModel or AddField migration, + # the migration is considered implicitly applied. return (found_create_model_migration or found_add_field_migration), after_state diff --git a/venv/Lib/site-packages/django/db/migrations/graph.py b/venv/Lib/site-packages/django/db/migrations/graph.py index dd845c1..f70e359 100644 --- a/venv/Lib/site-packages/django/db/migrations/graph.py +++ b/venv/Lib/site-packages/django/db/migrations/graph.py @@ -11,7 +11,6 @@ class Node: A single node in the migration graph. Contains direct links to adjacent nodes in either direction. """ - def __init__(self, key): self.key = key self.children = set() @@ -33,7 +32,7 @@ class Node: return str(self.key) def __repr__(self): - return "<%s: (%r, %r)>" % (self.__class__.__name__, self.key[0], self.key[1]) + return '<%s: (%r, %r)>' % (self.__class__.__name__, self.key[0], self.key[1]) def add_child(self, child): self.children.add(child) @@ -50,7 +49,6 @@ class DummyNode(Node): After the migration graph is processed, all dummy nodes should be removed. If there are any left, a nonexistent dependency error is raised. """ - def __init__(self, key, origin, error_message): super().__init__(key) self.origin = origin @@ -102,7 +100,7 @@ class MigrationGraph: """ This may create dummy nodes if they don't yet exist. If `skip_validation=True`, validate_consistency() should be called - afterward. + afterwards. """ if child not in self.nodes: error_message = ( @@ -135,7 +133,7 @@ class MigrationGraph: raise NodeNotFoundError( "Unable to find replacement node %r. It was either never added" " to the migration graph, or has been removed." % (replacement,), - replacement, + replacement ) from err for replaced_key in replaced: self.nodes.pop(replaced_key, None) @@ -169,9 +167,8 @@ class MigrationGraph: except KeyError as err: raise NodeNotFoundError( "Unable to remove replacement node %r. It was either never added" - " to the migration graph, or has been removed already." - % (replacement,), - replacement, + " to the migration graph, or has been removed already." % (replacement,), + replacement ) from err replaced_nodes = set() replaced_nodes_parents = set() @@ -231,10 +228,7 @@ class MigrationGraph: visited.append(node.key) else: stack.append((node, True)) - stack += [ - (n, False) - for n in sorted(node.parents if forwards else node.children) - ] + stack += [(n, False) for n in sorted(node.parents if forwards else node.children)] return visited def root_nodes(self, app=None): @@ -244,9 +238,7 @@ class MigrationGraph: """ roots = set() for node in self.nodes: - if all(key[0] != node[0] for key in self.node_map[node].parents) and ( - not app or app == node[0] - ): + if all(key[0] != node[0] for key in self.node_map[node].parents) and (not app or app == node[0]): roots.add(node) return sorted(roots) @@ -260,9 +252,7 @@ class MigrationGraph: """ leaves = set() for node in self.nodes: - if all(key[0] != node[0] for key in self.node_map[node].children) and ( - not app or app == node[0] - ): + if all(key[0] != node[0] for key in self.node_map[node].children) and (not app or app == node[0]): leaves.add(node) return sorted(leaves) @@ -280,10 +270,8 @@ class MigrationGraph: # hashing. node = child.key if node in stack: - cycle = stack[stack.index(node) :] - raise CircularDependencyError( - ", ".join("%s.%s" % n for n in cycle) - ) + cycle = stack[stack.index(node):] + raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle)) if node in todo: stack.append(node) todo.remove(node) @@ -292,16 +280,14 @@ class MigrationGraph: node = stack.pop() def __str__(self): - return "Graph: %s nodes, %s edges" % self._nodes_and_edges() + return 'Graph: %s nodes, %s edges' % self._nodes_and_edges() def __repr__(self): nodes, edges = self._nodes_and_edges() - return "<%s: nodes=%s, edges=%s>" % (self.__class__.__name__, nodes, edges) + return '<%s: nodes=%s, edges=%s>' % (self.__class__.__name__, nodes, edges) def _nodes_and_edges(self): - return len(self.nodes), sum( - len(node.parents) for node in self.node_map.values() - ) + return len(self.nodes), sum(len(node.parents) for node in self.node_map.values()) def _generate_plan(self, nodes, at_end): plan = [] diff --git a/venv/Lib/site-packages/django/db/migrations/loader.py b/venv/Lib/site-packages/django/db/migrations/loader.py index 81dcd06..eb37016 100644 --- a/venv/Lib/site-packages/django/db/migrations/loader.py +++ b/venv/Lib/site-packages/django/db/migrations/loader.py @@ -8,13 +8,11 @@ from django.db.migrations.graph import MigrationGraph from django.db.migrations.recorder import MigrationRecorder from .exceptions import ( - AmbiguityError, - BadMigrationError, - InconsistentMigrationHistory, + AmbiguityError, BadMigrationError, InconsistentMigrationHistory, NodeNotFoundError, ) -MIGRATIONS_MODULE_NAME = "migrations" +MIGRATIONS_MODULE_NAME = 'migrations' class MigrationLoader: @@ -43,10 +41,7 @@ class MigrationLoader: """ def __init__( - self, - connection, - load=True, - ignore_no_migrations=False, + self, connection, load=True, ignore_no_migrations=False, replace_migrations=True, ): self.connection = connection @@ -68,7 +63,7 @@ class MigrationLoader: return settings.MIGRATION_MODULES[app_label], True else: app_package_name = apps.get_app_config(app_label).name - return "%s.%s" % (app_package_name, MIGRATIONS_MODULE_NAME), False + return '%s.%s' % (app_package_name, MIGRATIONS_MODULE_NAME), False def load_disk(self): """Load the migrations from all INSTALLED_APPS from disk.""" @@ -85,22 +80,24 @@ class MigrationLoader: try: module = import_module(module_name) except ModuleNotFoundError as e: - if (explicit and self.ignore_no_migrations) or ( - not explicit and MIGRATIONS_MODULE_NAME in e.name.split(".") + if ( + (explicit and self.ignore_no_migrations) or + (not explicit and MIGRATIONS_MODULE_NAME in e.name.split('.')) ): self.unmigrated_apps.add(app_config.label) continue raise else: # Module is not a package (e.g. migrations.py). - if not hasattr(module, "__path__"): + if not hasattr(module, '__path__'): self.unmigrated_apps.add(app_config.label) continue # Empty directories are namespaces. Namespace packages have no # __file__ and don't use a list for __path__. See # https://docs.python.org/3/reference/import.html#namespace-packages - if getattr(module, "__file__", None) is None and not isinstance( - module.__path__, list + if ( + getattr(module, '__file__', None) is None and + not isinstance(module.__path__, list) ): self.unmigrated_apps.add(app_config.label) continue @@ -109,17 +106,16 @@ class MigrationLoader: reload(module) self.migrated_apps.add(app_config.label) migration_names = { - name - for _, name, is_pkg in pkgutil.iter_modules(module.__path__) - if not is_pkg and name[0] not in "_~" + name for _, name, is_pkg in pkgutil.iter_modules(module.__path__) + if not is_pkg and name[0] not in '_~' } # Load migrations for migration_name in migration_names: - migration_path = "%s.%s" % (module_name, migration_name) + migration_path = '%s.%s' % (module_name, migration_name) try: migration_module = import_module(migration_path) except ImportError as e: - if "bad magic number" in str(e): + if 'bad magic number' in str(e): raise ImportError( "Couldn't import %r as it appears to be a stale " ".pyc file." % migration_path @@ -128,12 +124,9 @@ class MigrationLoader: raise if not hasattr(migration_module, "Migration"): raise BadMigrationError( - "Migration %s in app %s has no Migration class" - % (migration_name, app_config.label) + "Migration %s in app %s has no Migration class" % (migration_name, app_config.label) ) - self.disk_migrations[ - app_config.label, migration_name - ] = migration_module.Migration( + self.disk_migrations[app_config.label, migration_name] = migration_module.Migration( migration_name, app_config.label, ) @@ -149,20 +142,14 @@ class MigrationLoader: # Do the search results = [] for migration_app_label, migration_name in self.disk_migrations: - if migration_app_label == app_label and migration_name.startswith( - name_prefix - ): + if migration_app_label == app_label and migration_name.startswith(name_prefix): results.append((migration_app_label, migration_name)) if len(results) > 1: raise AmbiguityError( - "There is more than one migration for '%s' with the prefix '%s'" - % (app_label, name_prefix) + "There is more than one migration for '%s' with the prefix '%s'" % (app_label, name_prefix) ) elif not results: - raise KeyError( - f"There is no migration for '{app_label}' with the prefix " - f"'{name_prefix}'" - ) + raise KeyError("There no migrations for '%s' with the prefix '%s'" % (app_label, name_prefix)) else: return self.disk_migrations[results[0]] @@ -191,9 +178,7 @@ class MigrationLoader: if self.ignore_no_migrations: return None else: - raise ValueError( - "Dependency on app with no migrations: %s" % key[0] - ) + raise ValueError("Dependency on app with no migrations: %s" % key[0]) raise ValueError("Dependency on unknown app: %s" % key[0]) def add_internal_dependencies(self, key, migration): @@ -203,7 +188,7 @@ class MigrationLoader: """ for parent in migration.dependencies: # Ignore __first__ references to the same app. - if parent[0] == key[0] and parent[1] != "__first__": + if parent[0] == key[0] and parent[1] != '__first__': self.graph.add_dependency(migration, key, parent, skip_validation=True) def add_external_dependencies(self, key, migration): @@ -253,9 +238,7 @@ class MigrationLoader: for key, migration in self.replacements.items(): # Get applied status of each of this migration's replacement # targets. - applied_statuses = [ - (target in self.applied_migrations) for target in migration.replaces - ] + applied_statuses = [(target in self.applied_migrations) for target in migration.replaces] # The replacing migration is only marked as applied if all of # its replacement targets are. if all(applied_statuses): @@ -287,11 +270,9 @@ class MigrationLoader: # Try to reraise exception with more detail. if exc.node in reverse_replacements: candidates = reverse_replacements.get(exc.node, set()) - is_replaced = any( - candidate in self.graph.nodes for candidate in candidates - ) + is_replaced = any(candidate in self.graph.nodes for candidate in candidates) if not is_replaced: - tries = ", ".join("%s.%s" % c for c in candidates) + tries = ', '.join('%s.%s' % c for c in candidates) raise NodeNotFoundError( "Migration {0} depends on nonexistent node ('{1}', '{2}'). " "Django tried to replace migration {1}.{2} with any of [{3}] " @@ -299,7 +280,7 @@ class MigrationLoader: "are already applied.".format( exc.origin, exc.node[0], exc.node[1], tries ), - exc.node, + exc.node ) from exc raise self.graph.ensure_not_cyclic() @@ -320,17 +301,12 @@ class MigrationLoader: # Skip unapplied squashed migrations that have all of their # `replaces` applied. if parent in self.replacements: - if all( - m in applied for m in self.replacements[parent].replaces - ): + if all(m in applied for m in self.replacements[parent].replaces): continue raise InconsistentMigrationHistory( "Migration {}.{} is applied before its dependency " "{}.{} on database '{}'.".format( - migration[0], - migration[1], - parent[0], - parent[1], + migration[0], migration[1], parent[0], parent[1], connection.alias, ) ) @@ -347,9 +323,7 @@ class MigrationLoader: if app_label in seen_apps: conflicting_apps.add(app_label) seen_apps.setdefault(app_label, set()).add(migration_name) - return { - app_label: sorted(seen_apps[app_label]) for app_label in conflicting_apps - } + return {app_label: sorted(seen_apps[app_label]) for app_label in conflicting_apps} def project_state(self, nodes=None, at_end=True): """ @@ -358,9 +332,7 @@ class MigrationLoader: See graph.make_state() for the meaning of "nodes" and "at_end". """ - return self.graph.make_state( - nodes=nodes, at_end=at_end, real_apps=self.unmigrated_apps - ) + return self.graph.make_state(nodes=nodes, at_end=at_end, real_apps=list(self.unmigrated_apps)) def collect_sql(self, plan): """ @@ -370,13 +342,9 @@ class MigrationLoader: statements = [] state = None for migration, backwards in plan: - with self.connection.schema_editor( - collect_sql=True, atomic=migration.atomic - ) as schema_editor: + with self.connection.schema_editor(collect_sql=True, atomic=migration.atomic) as schema_editor: if state is None: - state = self.project_state( - (migration.app_label, migration.name), at_end=False - ) + state = self.project_state((migration.app_label, migration.name), at_end=False) if not backwards: state = migration.apply(state, schema_editor, collect_sql=True) else: diff --git a/venv/Lib/site-packages/django/db/migrations/migration.py b/venv/Lib/site-packages/django/db/migrations/migration.py index ccbd2f7..b9f4f8f 100644 --- a/venv/Lib/site-packages/django/db/migrations/migration.py +++ b/venv/Lib/site-packages/django/db/migrations/migration.py @@ -1,3 +1,4 @@ +from django.db.migrations import operations from django.db.migrations.utils import get_migration_name_timestamp from django.db.transaction import atomic @@ -12,8 +13,7 @@ class Migration: and subclass it as a class called Migration. It will have one or more of the following attributes: - - operations: A list of Operation instances, probably from - django.db.migrations.operations + - operations: A list of Operation instances, probably from django.db.migrations.operations - dependencies: A list of tuples of (app_path, migration_name) - run_before: A list of tuples of (app_path, migration_name) - replaces: A list of migration_names @@ -61,9 +61,9 @@ class Migration: def __eq__(self, other): return ( - isinstance(other, Migration) - and self.name == other.name - and self.app_label == other.app_label + isinstance(other, Migration) and + self.name == other.name and + self.app_label == other.app_label ) def __repr__(self): @@ -105,8 +105,7 @@ class Migration: schema_editor.collected_sql.append("--") if not operation.reduces_to_sql: schema_editor.collected_sql.append( - "-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS " - "SQL:" + "-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:" ) schema_editor.collected_sql.append("-- %s" % operation.describe()) schema_editor.collected_sql.append("--") @@ -116,21 +115,15 @@ class Migration: old_state = project_state.clone() operation.state_forwards(self.app_label, project_state) # Run the operation - atomic_operation = operation.atomic or ( - self.atomic and operation.atomic is not False - ) + atomic_operation = operation.atomic or (self.atomic and operation.atomic is not False) if not schema_editor.atomic_migration and atomic_operation: # Force a transaction on a non-transactional-DDL backend or an # atomic operation inside a non-atomic migration. with atomic(schema_editor.connection.alias): - operation.database_forwards( - self.app_label, schema_editor, old_state, project_state - ) + operation.database_forwards(self.app_label, schema_editor, old_state, project_state) else: # Normal behaviour - operation.database_forwards( - self.app_label, schema_editor, old_state, project_state - ) + operation.database_forwards(self.app_label, schema_editor, old_state, project_state) return project_state def unapply(self, project_state, schema_editor, collect_sql=False): @@ -153,9 +146,7 @@ class Migration: for operation in self.operations: # If it's irreversible, error out if not operation.reversible: - raise IrreversibleError( - "Operation %s in %s is not reversible" % (operation, self) - ) + raise IrreversibleError("Operation %s in %s is not reversible" % (operation, self)) # Preserve new state from previous run to not tamper the same state # over all operations new_state = new_state.clone() @@ -169,28 +160,21 @@ class Migration: schema_editor.collected_sql.append("--") if not operation.reduces_to_sql: schema_editor.collected_sql.append( - "-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS " - "SQL:" + "-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:" ) schema_editor.collected_sql.append("-- %s" % operation.describe()) schema_editor.collected_sql.append("--") if not operation.reduces_to_sql: continue - atomic_operation = operation.atomic or ( - self.atomic and operation.atomic is not False - ) + atomic_operation = operation.atomic or (self.atomic and operation.atomic is not False) if not schema_editor.atomic_migration and atomic_operation: # Force a transaction on a non-transactional-DDL backend or an # atomic operation inside a non-atomic migration. with atomic(schema_editor.connection.alias): - operation.database_backwards( - self.app_label, schema_editor, from_state, to_state - ) + operation.database_backwards(self.app_label, schema_editor, from_state, to_state) else: # Normal behaviour - operation.database_backwards( - self.app_label, schema_editor, from_state, to_state - ) + operation.database_backwards(self.app_label, schema_editor, from_state, to_state) return project_state def suggest_name(self): @@ -199,22 +183,16 @@ class Migration: are not guaranteed to be unique, but put some effort into the fallback name to avoid VCS conflicts if possible. """ - if self.initial: - return "initial" - - raw_fragments = [op.migration_name_fragment for op in self.operations] - fragments = [name for name in raw_fragments if name] - - if not fragments or len(fragments) != len(self.operations): - return "auto_%s" % get_migration_name_timestamp() - - name = fragments[0] - for fragment in fragments[1:]: - new_name = f"{name}_{fragment}" - if len(new_name) > 52: - name = f"{name}_and_more" - break - name = new_name + name = None + if len(self.operations) == 1: + name = self.operations[0].migration_name_fragment + elif ( + len(self.operations) > 1 and + all(isinstance(o, operations.CreateModel) for o in self.operations) + ): + name = '_'.join(sorted(o.migration_name_fragment for o in self.operations)) + if name is None: + name = 'initial' if self.initial else 'auto_%s' % get_migration_name_timestamp() return name diff --git a/venv/Lib/site-packages/django/db/migrations/operations/__init__.py b/venv/Lib/site-packages/django/db/migrations/operations/__init__.py index 793969e..119c955 100644 --- a/venv/Lib/site-packages/django/db/migrations/operations/__init__.py +++ b/venv/Lib/site-packages/django/db/migrations/operations/__init__.py @@ -1,40 +1,17 @@ from .fields import AddField, AlterField, RemoveField, RenameField from .models import ( - AddConstraint, - AddIndex, - AlterIndexTogether, - AlterModelManagers, - AlterModelOptions, - AlterModelTable, - AlterOrderWithRespectTo, - AlterUniqueTogether, - CreateModel, - DeleteModel, - RemoveConstraint, - RemoveIndex, - RenameModel, + AddConstraint, AddIndex, AlterIndexTogether, AlterModelManagers, + AlterModelOptions, AlterModelTable, AlterOrderWithRespectTo, + AlterUniqueTogether, CreateModel, DeleteModel, RemoveConstraint, + RemoveIndex, RenameModel, ) from .special import RunPython, RunSQL, SeparateDatabaseAndState __all__ = [ - "CreateModel", - "DeleteModel", - "AlterModelTable", - "AlterUniqueTogether", - "RenameModel", - "AlterIndexTogether", - "AlterModelOptions", - "AddIndex", - "RemoveIndex", - "AddField", - "RemoveField", - "AlterField", - "RenameField", - "AddConstraint", - "RemoveConstraint", - "SeparateDatabaseAndState", - "RunSQL", - "RunPython", - "AlterOrderWithRespectTo", - "AlterModelManagers", + 'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether', + 'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'AddIndex', + 'RemoveIndex', 'AddField', 'RemoveField', 'AlterField', 'RenameField', + 'AddConstraint', 'RemoveConstraint', + 'SeparateDatabaseAndState', 'RunSQL', 'RunPython', + 'AlterOrderWithRespectTo', 'AlterModelManagers', ] diff --git a/venv/Lib/site-packages/django/db/migrations/operations/base.py b/venv/Lib/site-packages/django/db/migrations/operations/base.py index 7d4dff2..1893552 100644 --- a/venv/Lib/site-packages/django/db/migrations/operations/base.py +++ b/venv/Lib/site-packages/django/db/migrations/operations/base.py @@ -56,18 +56,14 @@ class Operation: Take the state from the previous migration, and mutate it so that it matches what this migration would perform. """ - raise NotImplementedError( - "subclasses of Operation must provide a state_forwards() method" - ) + raise NotImplementedError('subclasses of Operation must provide a state_forwards() method') def database_forwards(self, app_label, schema_editor, from_state, to_state): """ Perform the mutation on the database schema in the normal (forwards) direction. """ - raise NotImplementedError( - "subclasses of Operation must provide a database_forwards() method" - ) + raise NotImplementedError('subclasses of Operation must provide a database_forwards() method') def database_backwards(self, app_label, schema_editor, from_state, to_state): """ @@ -75,9 +71,7 @@ class Operation: direction - e.g. if this were CreateModel, it would in fact drop the model's table. """ - raise NotImplementedError( - "subclasses of Operation must provide a database_backwards() method" - ) + raise NotImplementedError('subclasses of Operation must provide a database_backwards() method') def describe(self): """ diff --git a/venv/Lib/site-packages/django/db/migrations/operations/fields.py b/venv/Lib/site-packages/django/db/migrations/operations/fields.py index a1bf81e..8e494fd 100644 --- a/venv/Lib/site-packages/django/db/migrations/operations/fields.py +++ b/venv/Lib/site-packages/django/db/migrations/operations/fields.py @@ -1,8 +1,9 @@ -from django.db.migrations.utils import field_references +from django.core.exceptions import FieldDoesNotExist from django.db.models import NOT_PROVIDED from django.utils.functional import cached_property from .base import Operation +from .utils import field_is_referenced, field_references, get_references class FieldOperation(Operation): @@ -23,23 +24,16 @@ class FieldOperation(Operation): return self.model_name_lower == operation.model_name_lower def is_same_field_operation(self, operation): - return ( - self.is_same_model_operation(operation) - and self.name_lower == operation.name_lower - ) + return self.is_same_model_operation(operation) and self.name_lower == operation.name_lower def references_model(self, name, app_label): name_lower = name.lower() if name_lower == self.model_name_lower: return True if self.field: - return bool( - field_references( - (app_label, self.model_name_lower), - self.field, - (app_label, name_lower), - ) - ) + return bool(field_references( + (app_label, self.model_name_lower), self.field, (app_label, name_lower) + )) return False def references_field(self, model_name, name, app_label): @@ -48,27 +42,22 @@ class FieldOperation(Operation): if model_name_lower == self.model_name_lower: if name == self.name: return True - elif ( - self.field - and hasattr(self.field, "from_fields") - and name in self.field.from_fields - ): + elif self.field and hasattr(self.field, 'from_fields') and name in self.field.from_fields: return True # Check if this operation remotely references the field. if self.field is None: return False - return bool( - field_references( - (app_label, self.model_name_lower), - self.field, - (app_label, model_name_lower), - name, - ) - ) + return bool(field_references( + (app_label, self.model_name_lower), + self.field, + (app_label, model_name_lower), + name, + )) def reduce(self, operation, app_label): - return super().reduce(operation, app_label) or not operation.references_field( - self.model_name, self.name, app_label + return ( + super().reduce(operation, app_label) or + not operation.references_field(self.model_name, self.name, app_label) ) @@ -81,22 +70,29 @@ class AddField(FieldOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "name": self.name, - "field": self.field, + 'model_name': self.model_name, + 'name': self.name, + 'field': self.field, } if self.preserve_default is not True: - kwargs["preserve_default"] = self.preserve_default - return (self.__class__.__name__, [], kwargs) + kwargs['preserve_default'] = self.preserve_default + return ( + self.__class__.__name__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.add_field( - app_label, - self.model_name_lower, - self.name, - self.field, - self.preserve_default, - ) + # If preserve default is off, don't use the default for future state + if not self.preserve_default: + field = self.field.clone() + field.default = NOT_PROVIDED + else: + field = self.field + state.models[app_label, self.model_name_lower].fields[self.name] = field + # Delay rendering of relationships if it's not a relational field + delay = not field.is_relation + state.reload_model(app_label, self.model_name_lower, delay=delay) def database_forwards(self, app_label, schema_editor, from_state, to_state): to_model = to_state.apps.get_model(app_label, self.model_name) @@ -115,21 +111,17 @@ class AddField(FieldOperation): def database_backwards(self, app_label, schema_editor, from_state, to_state): from_model = from_state.apps.get_model(app_label, self.model_name) if self.allow_migrate_model(schema_editor.connection.alias, from_model): - schema_editor.remove_field( - from_model, from_model._meta.get_field(self.name) - ) + schema_editor.remove_field(from_model, from_model._meta.get_field(self.name)) def describe(self): return "Add field %s to %s" % (self.name, self.model_name) @property def migration_name_fragment(self): - return "%s_%s" % (self.model_name_lower, self.name_lower) + return '%s_%s' % (self.model_name_lower, self.name_lower) def reduce(self, operation, app_label): - if isinstance(operation, FieldOperation) and self.is_same_field_operation( - operation - ): + if isinstance(operation, FieldOperation) and self.is_same_field_operation(operation): if isinstance(operation, AlterField): return [ AddField( @@ -156,20 +148,26 @@ class RemoveField(FieldOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "name": self.name, + 'model_name': self.model_name, + 'name': self.name, } - return (self.__class__.__name__, [], kwargs) + return ( + self.__class__.__name__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.remove_field(app_label, self.model_name_lower, self.name) + model_state = state.models[app_label, self.model_name_lower] + old_field = model_state.fields.pop(self.name) + # Delay rendering of relationships if it's not a relational field + delay = not old_field.is_relation + state.reload_model(app_label, self.model_name_lower, delay=delay) def database_forwards(self, app_label, schema_editor, from_state, to_state): from_model = from_state.apps.get_model(app_label, self.model_name) if self.allow_migrate_model(schema_editor.connection.alias, from_model): - schema_editor.remove_field( - from_model, from_model._meta.get_field(self.name) - ) + schema_editor.remove_field(from_model, from_model._meta.get_field(self.name)) def database_backwards(self, app_label, schema_editor, from_state, to_state): to_model = to_state.apps.get_model(app_label, self.model_name) @@ -182,15 +180,11 @@ class RemoveField(FieldOperation): @property def migration_name_fragment(self): - return "remove_%s_%s" % (self.model_name_lower, self.name_lower) + return 'remove_%s_%s' % (self.model_name_lower, self.name_lower) def reduce(self, operation, app_label): from .models import DeleteModel - - if ( - isinstance(operation, DeleteModel) - and operation.name_lower == self.model_name_lower - ): + if isinstance(operation, DeleteModel) and operation.name_lower == self.model_name_lower: return [operation] return super().reduce(operation, app_label) @@ -207,22 +201,37 @@ class AlterField(FieldOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "name": self.name, - "field": self.field, + 'model_name': self.model_name, + 'name': self.name, + 'field': self.field, } if self.preserve_default is not True: - kwargs["preserve_default"] = self.preserve_default - return (self.__class__.__name__, [], kwargs) + kwargs['preserve_default'] = self.preserve_default + return ( + self.__class__.__name__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.alter_field( - app_label, - self.model_name_lower, - self.name, - self.field, - self.preserve_default, + if not self.preserve_default: + field = self.field.clone() + field.default = NOT_PROVIDED + else: + field = self.field + model_state = state.models[app_label, self.model_name_lower] + model_state.fields[self.name] = field + # TODO: investigate if old relational fields must be reloaded or if it's + # sufficient if the new field is (#27737). + # Delay rendering of relationships if it's not a relational field and + # not referenced by a foreign key. + delay = ( + not field.is_relation and + not field_is_referenced( + state, (app_label, self.model_name_lower), (self.name, field), + ) ) + state.reload_model(app_label, self.model_name_lower, delay=delay) def database_forwards(self, app_label, schema_editor, from_state, to_state): to_model = to_state.apps.get_model(app_label, self.model_name) @@ -244,16 +253,12 @@ class AlterField(FieldOperation): @property def migration_name_fragment(self): - return "alter_%s_%s" % (self.model_name_lower, self.name_lower) + return 'alter_%s_%s' % (self.model_name_lower, self.name_lower) def reduce(self, operation, app_label): - if isinstance(operation, RemoveField) and self.is_same_field_operation( - operation - ): + if isinstance(operation, RemoveField) and self.is_same_field_operation(operation): return [operation] - elif isinstance(operation, RenameField) and self.is_same_field_operation( - operation - ): + elif isinstance(operation, RenameField) and self.is_same_field_operation(operation): return [ operation, AlterField( @@ -283,16 +288,60 @@ class RenameField(FieldOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "old_name": self.old_name, - "new_name": self.new_name, + 'model_name': self.model_name, + 'old_name': self.old_name, + 'new_name': self.new_name, } - return (self.__class__.__name__, [], kwargs) + return ( + self.__class__.__name__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.rename_field( - app_label, self.model_name_lower, self.old_name, self.new_name + model_state = state.models[app_label, self.model_name_lower] + # Rename the field + fields = model_state.fields + try: + found = fields.pop(self.old_name) + except KeyError: + raise FieldDoesNotExist( + "%s.%s has no field named '%s'" % (app_label, self.model_name, self.old_name) + ) + fields[self.new_name] = found + for field in fields.values(): + # Fix from_fields to refer to the new field. + from_fields = getattr(field, 'from_fields', None) + if from_fields: + field.from_fields = tuple([ + self.new_name if from_field_name == self.old_name else from_field_name + for from_field_name in from_fields + ]) + # Fix index/unique_together to refer to the new field + options = model_state.options + for option in ('index_together', 'unique_together'): + if option in options: + options[option] = [ + [self.new_name if n == self.old_name else n for n in together] + for together in options[option] + ] + # Fix to_fields to refer to the new field. + delay = True + references = get_references( + state, (app_label, self.model_name_lower), (self.old_name, found), ) + for *_, field, reference in references: + delay = False + if reference.to: + remote_field, to_fields = reference.to + if getattr(remote_field, 'field_name', None) == self.old_name: + remote_field.field_name = self.new_name + if to_fields: + field.to_fields = tuple([ + self.new_name if to_field_name == self.old_name else to_field_name + for to_field_name in to_fields + ]) + state.reload_model(app_label, self.model_name_lower, delay=delay) def database_forwards(self, app_label, schema_editor, from_state, to_state): to_model = to_state.apps.get_model(app_label, self.model_name) @@ -315,15 +364,11 @@ class RenameField(FieldOperation): ) def describe(self): - return "Rename field %s on %s to %s" % ( - self.old_name, - self.model_name, - self.new_name, - ) + return "Rename field %s on %s to %s" % (self.old_name, self.model_name, self.new_name) @property def migration_name_fragment(self): - return "rename_%s_%s_%s" % ( + return 'rename_%s_%s_%s' % ( self.old_name_lower, self.model_name_lower, self.new_name_lower, @@ -331,15 +376,14 @@ class RenameField(FieldOperation): def references_field(self, model_name, name, app_label): return self.references_model(model_name, app_label) and ( - name.lower() == self.old_name_lower or name.lower() == self.new_name_lower + name.lower() == self.old_name_lower or + name.lower() == self.new_name_lower ) def reduce(self, operation, app_label): - if ( - isinstance(operation, RenameField) - and self.is_same_model_operation(operation) - and self.new_name_lower == operation.old_name_lower - ): + if (isinstance(operation, RenameField) and + self.is_same_model_operation(operation) and + self.new_name_lower == operation.old_name_lower): return [ RenameField( self.model_name, @@ -348,8 +392,8 @@ class RenameField(FieldOperation): ), ] # Skip `FieldOperation.reduce` as we want to run `references_field` - # against self.old_name and self.new_name. - return super(FieldOperation, self).reduce(operation, app_label) or not ( - operation.references_field(self.model_name, self.old_name, app_label) - or operation.references_field(self.model_name, self.new_name, app_label) + # against self.new_name. + return ( + super(FieldOperation, self).reduce(operation, app_label) or + not operation.references_field(self.model_name, self.new_name, app_label) ) diff --git a/venv/Lib/site-packages/django/db/migrations/operations/models.py b/venv/Lib/site-packages/django/db/migrations/operations/models.py index 5a727d4..28a7dde 100644 --- a/venv/Lib/site-packages/django/db/migrations/operations/models.py +++ b/venv/Lib/site-packages/django/db/migrations/operations/models.py @@ -1,11 +1,13 @@ from django.db import models from django.db.migrations.operations.base import Operation from django.db.migrations.state import ModelState -from django.db.migrations.utils import field_references, resolve_relation from django.db.models.options import normalize_together from django.utils.functional import cached_property -from .fields import AddField, AlterField, FieldOperation, RemoveField, RenameField +from .fields import ( + AddField, AlterField, FieldOperation, RemoveField, RenameField, +) +from .utils import field_references, get_references, resolve_relation def _check_for_duplicates(arg_name, objs): @@ -30,15 +32,16 @@ class ModelOperation(Operation): return name.lower() == self.name_lower def reduce(self, operation, app_label): - return super().reduce(operation, app_label) or not operation.references_model( - self.name, app_label + return ( + super().reduce(operation, app_label) or + not operation.references_model(self.name, app_label) ) class CreateModel(ModelOperation): """Create a model's table.""" - serialization_expand_args = ["fields", "options", "managers"] + serialization_expand_args = ['fields', 'options', 'managers'] def __init__(self, name, fields, options=None, bases=None, managers=None): self.fields = fields @@ -48,44 +51,40 @@ class CreateModel(ModelOperation): super().__init__(name) # Sanity-check that there are no duplicated field names, bases, or # manager names - _check_for_duplicates("fields", (name for name, _ in self.fields)) - _check_for_duplicates( - "bases", - ( - base._meta.label_lower - if hasattr(base, "_meta") - else base.lower() - if isinstance(base, str) - else base - for base in self.bases - ), - ) - _check_for_duplicates("managers", (name for name, _ in self.managers)) + _check_for_duplicates('fields', (name for name, _ in self.fields)) + _check_for_duplicates('bases', ( + base._meta.label_lower if hasattr(base, '_meta') else + base.lower() if isinstance(base, str) else base + for base in self.bases + )) + _check_for_duplicates('managers', (name for name, _ in self.managers)) def deconstruct(self): kwargs = { - "name": self.name, - "fields": self.fields, + 'name': self.name, + 'fields': self.fields, } if self.options: - kwargs["options"] = self.options + kwargs['options'] = self.options if self.bases and self.bases != (models.Model,): - kwargs["bases"] = self.bases - if self.managers and self.managers != [("objects", models.Manager())]: - kwargs["managers"] = self.managers - return (self.__class__.__qualname__, [], kwargs) + kwargs['bases'] = self.bases + if self.managers and self.managers != [('objects', models.Manager())]: + kwargs['managers'] = self.managers + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.add_model( - ModelState( - app_label, - self.name, - list(self.fields), - dict(self.options), - tuple(self.bases), - list(self.managers), - ) - ) + state.add_model(ModelState( + app_label, + self.name, + list(self.fields), + dict(self.options), + tuple(self.bases), + list(self.managers), + )) def database_forwards(self, app_label, schema_editor, from_state, to_state): model = to_state.apps.get_model(app_label, self.name) @@ -98,10 +97,7 @@ class CreateModel(ModelOperation): schema_editor.delete_model(model) def describe(self): - return "Create %smodel %s" % ( - "proxy " if self.options.get("proxy", False) else "", - self.name, - ) + return "Create %smodel %s" % ("proxy " if self.options.get("proxy", False) else "", self.name) @property def migration_name_fragment(self): @@ -115,32 +111,22 @@ class CreateModel(ModelOperation): # Check we didn't inherit from the model reference_model_tuple = (app_label, name_lower) for base in self.bases: - if ( - base is not models.Model - and isinstance(base, (models.base.ModelBase, str)) - and resolve_relation(base, app_label) == reference_model_tuple - ): + if (base is not models.Model and isinstance(base, (models.base.ModelBase, str)) and + resolve_relation(base, app_label) == reference_model_tuple): return True # Check we have no FKs/M2Ms with it for _name, field in self.fields: - if field_references( - (app_label, self.name_lower), field, reference_model_tuple - ): + if field_references((app_label, self.name_lower), field, reference_model_tuple): return True return False def reduce(self, operation, app_label): - if ( - isinstance(operation, DeleteModel) - and self.name_lower == operation.name_lower - and not self.options.get("proxy", False) - ): + if (isinstance(operation, DeleteModel) and + self.name_lower == operation.name_lower and + not self.options.get("proxy", False)): return [] - elif ( - isinstance(operation, RenameModel) - and self.name_lower == operation.old_name_lower - ): + elif isinstance(operation, RenameModel) and self.name_lower == operation.old_name_lower: return [ CreateModel( operation.new_name, @@ -150,10 +136,7 @@ class CreateModel(ModelOperation): managers=self.managers, ), ] - elif ( - isinstance(operation, AlterModelOptions) - and self.name_lower == operation.name_lower - ): + elif isinstance(operation, AlterModelOptions) and self.name_lower == operation.name_lower: options = {**self.options, **operation.options} for key in operation.ALTER_OPTION_KEYS: if key not in operation.options: @@ -167,42 +150,27 @@ class CreateModel(ModelOperation): managers=self.managers, ), ] - elif ( - isinstance(operation, AlterTogetherOptionOperation) - and self.name_lower == operation.name_lower - ): + elif isinstance(operation, AlterTogetherOptionOperation) and self.name_lower == operation.name_lower: return [ CreateModel( self.name, fields=self.fields, - options={ - **self.options, - **{operation.option_name: operation.option_value}, - }, + options={**self.options, **{operation.option_name: operation.option_value}}, bases=self.bases, managers=self.managers, ), ] - elif ( - isinstance(operation, AlterOrderWithRespectTo) - and self.name_lower == operation.name_lower - ): + elif isinstance(operation, AlterOrderWithRespectTo) and self.name_lower == operation.name_lower: return [ CreateModel( self.name, fields=self.fields, - options={ - **self.options, - "order_with_respect_to": operation.order_with_respect_to, - }, + options={**self.options, 'order_with_respect_to': operation.order_with_respect_to}, bases=self.bases, managers=self.managers, ), ] - elif ( - isinstance(operation, FieldOperation) - and self.name_lower == operation.model_name_lower - ): + elif isinstance(operation, FieldOperation) and self.name_lower == operation.model_name_lower: if isinstance(operation, AddField): return [ CreateModel( @@ -228,25 +196,17 @@ class CreateModel(ModelOperation): ] elif isinstance(operation, RemoveField): options = self.options.copy() - for option_name in ("unique_together", "index_together"): + for option_name in ('unique_together', 'index_together'): option = options.pop(option_name, None) if option: - option = set( - filter( - bool, - ( - tuple( - f for f in fields if f != operation.name_lower - ) - for fields in option - ), - ) - ) + option = set(filter(bool, ( + tuple(f for f in fields if f != operation.name_lower) for fields in option + ))) if option: options[option_name] = option - order_with_respect_to = options.get("order_with_respect_to") + order_with_respect_to = options.get('order_with_respect_to') if order_with_respect_to == operation.name_lower: - del options["order_with_respect_to"] + del options['order_with_respect_to'] return [ CreateModel( self.name, @@ -262,19 +222,16 @@ class CreateModel(ModelOperation): ] elif isinstance(operation, RenameField): options = self.options.copy() - for option_name in ("unique_together", "index_together"): + for option_name in ('unique_together', 'index_together'): option = options.get(option_name) if option: options[option_name] = { - tuple( - operation.new_name if f == operation.old_name else f - for f in fields - ) + tuple(operation.new_name if f == operation.old_name else f for f in fields) for fields in option } - order_with_respect_to = options.get("order_with_respect_to") + order_with_respect_to = options.get('order_with_respect_to') if order_with_respect_to == operation.old_name: - options["order_with_respect_to"] = operation.new_name + options['order_with_respect_to'] = operation.new_name return [ CreateModel( self.name, @@ -295,9 +252,13 @@ class DeleteModel(ModelOperation): def deconstruct(self): kwargs = { - "name": self.name, + 'name': self.name, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): state.remove_model(app_label, self.name_lower) @@ -322,7 +283,7 @@ class DeleteModel(ModelOperation): @property def migration_name_fragment(self): - return "delete_%s" % self.name_lower + return 'delete_%s' % self.name_lower class RenameModel(ModelOperation): @@ -343,13 +304,41 @@ class RenameModel(ModelOperation): def deconstruct(self): kwargs = { - "old_name": self.old_name, - "new_name": self.new_name, + 'old_name': self.old_name, + 'new_name': self.new_name, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.rename_model(app_label, self.old_name, self.new_name) + # Add a new model. + renamed_model = state.models[app_label, self.old_name_lower].clone() + renamed_model.name = self.new_name + state.models[app_label, self.new_name_lower] = renamed_model + # Repoint all fields pointing to the old model to the new one. + old_model_tuple = (app_label, self.old_name_lower) + new_remote_model = '%s.%s' % (app_label, self.new_name) + to_reload = set() + for model_state, name, field, reference in get_references(state, old_model_tuple): + changed_field = None + if reference.to: + changed_field = field.clone() + changed_field.remote_field.model = new_remote_model + if reference.through: + if changed_field is None: + changed_field = field.clone() + changed_field.remote_field.through = new_remote_model + if changed_field: + model_state.fields[name] = changed_field + to_reload.add((model_state.app_label, model_state.name_lower)) + # Reload models related to old model before removing the old model. + state.reload_models(to_reload, delay=True) + # Remove the old model. + state.remove_model(app_label, self.old_name_lower) + state.reload_model(app_label, self.new_name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): new_model = to_state.apps.get_model(app_label, self.new_name) @@ -372,24 +361,19 @@ class RenameModel(ModelOperation): related_object.related_model._meta.app_label, related_object.related_model._meta.model_name, ) - to_field = to_state.apps.get_model(*related_key)._meta.get_field( - related_object.field.name - ) + to_field = to_state.apps.get_model( + *related_key + )._meta.get_field(related_object.field.name) schema_editor.alter_field( model, related_object.field, to_field, ) # Rename M2M fields whose name is based on this model's name. - fields = zip( - old_model._meta.local_many_to_many, new_model._meta.local_many_to_many - ) + fields = zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many) for (old_field, new_field) in fields: # Skip self-referential fields as these are renamed above. - if ( - new_field.model == new_field.related_model - or not new_field.remote_field.through._meta.auto_created - ): + if new_field.model == new_field.related_model or not new_field.remote_field.through._meta.auto_created: continue # Rename the M2M table that's based on this model's name. old_m2m_model = old_field.remote_field.through @@ -408,23 +392,18 @@ class RenameModel(ModelOperation): ) def database_backwards(self, app_label, schema_editor, from_state, to_state): - self.new_name_lower, self.old_name_lower = ( - self.old_name_lower, - self.new_name_lower, - ) + self.new_name_lower, self.old_name_lower = self.old_name_lower, self.new_name_lower self.new_name, self.old_name = self.old_name, self.new_name self.database_forwards(app_label, schema_editor, from_state, to_state) - self.new_name_lower, self.old_name_lower = ( - self.old_name_lower, - self.new_name_lower, - ) + self.new_name_lower, self.old_name_lower = self.old_name_lower, self.new_name_lower self.new_name, self.old_name = self.old_name, self.new_name def references_model(self, name, app_label): return ( - name.lower() == self.old_name_lower or name.lower() == self.new_name_lower + name.lower() == self.old_name_lower or + name.lower() == self.new_name_lower ) def describe(self): @@ -432,13 +411,11 @@ class RenameModel(ModelOperation): @property def migration_name_fragment(self): - return "rename_%s_%s" % (self.old_name_lower, self.new_name_lower) + return 'rename_%s_%s' % (self.old_name_lower, self.new_name_lower) def reduce(self, operation, app_label): - if ( - isinstance(operation, RenameModel) - and self.new_name_lower == operation.old_name_lower - ): + if (isinstance(operation, RenameModel) and + self.new_name_lower == operation.old_name_lower): return [ RenameModel( self.old_name, @@ -447,17 +424,15 @@ class RenameModel(ModelOperation): ] # Skip `ModelOperation.reduce` as we want to run `references_model` # against self.new_name. - return super(ModelOperation, self).reduce( - operation, app_label - ) or not operation.references_model(self.new_name, app_label) + return ( + super(ModelOperation, self).reduce(operation, app_label) or + not operation.references_model(self.new_name, app_label) + ) class ModelOptionOperation(ModelOperation): def reduce(self, operation, app_label): - if ( - isinstance(operation, (self.__class__, DeleteModel)) - and self.name_lower == operation.name_lower - ): + if isinstance(operation, (self.__class__, DeleteModel)) and self.name_lower == operation.name_lower: return [operation] return super().reduce(operation, app_label) @@ -471,13 +446,18 @@ class AlterModelTable(ModelOptionOperation): def deconstruct(self): kwargs = { - "name": self.name, - "table": self.table, + 'name': self.name, + 'table': self.table, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.alter_model_options(app_label, self.name_lower, {"db_table": self.table}) + state.models[app_label, self.name_lower].options["db_table"] = self.table + state.reload_model(app_label, self.name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): new_model = to_state.apps.get_model(app_label, self.name) @@ -489,9 +469,7 @@ class AlterModelTable(ModelOptionOperation): new_model._meta.db_table, ) # Rename M2M fields whose name is based on this model's db_table - for (old_field, new_field) in zip( - old_model._meta.local_many_to_many, new_model._meta.local_many_to_many - ): + for (old_field, new_field) in zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many): if new_field.remote_field.through._meta.auto_created: schema_editor.alter_db_table( new_field.remote_field.through, @@ -505,12 +483,12 @@ class AlterModelTable(ModelOptionOperation): def describe(self): return "Rename table for %s to %s" % ( self.name, - self.table if self.table is not None else "(default)", + self.table if self.table is not None else "(default)" ) @property def migration_name_fragment(self): - return "alter_%s_table" % self.name_lower + return 'alter_%s_table' % self.name_lower class AlterTogetherOptionOperation(ModelOptionOperation): @@ -528,23 +506,25 @@ class AlterTogetherOptionOperation(ModelOptionOperation): def deconstruct(self): kwargs = { - "name": self.name, + 'name': self.name, self.option_name: self.option_value, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.alter_model_options( - app_label, - self.name_lower, - {self.option_name: self.option_value}, - ) + model_state = state.models[app_label, self.name_lower] + model_state.options[self.option_name] = self.option_value + state.reload_model(app_label, self.name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): new_model = to_state.apps.get_model(app_label, self.name) if self.allow_migrate_model(schema_editor.connection.alias, new_model): old_model = from_state.apps.get_model(app_label, self.name) - alter_together = getattr(schema_editor, "alter_%s" % self.option_name) + alter_together = getattr(schema_editor, 'alter_%s' % self.option_name) alter_together( new_model, getattr(old_model._meta, self.option_name, set()), @@ -555,21 +535,20 @@ class AlterTogetherOptionOperation(ModelOptionOperation): return self.database_forwards(app_label, schema_editor, from_state, to_state) def references_field(self, model_name, name, app_label): - return self.references_model(model_name, app_label) and ( - not self.option_value - or any((name in fields) for fields in self.option_value) + return ( + self.references_model(model_name, app_label) and + ( + not self.option_value or + any((name in fields) for fields in self.option_value) + ) ) def describe(self): - return "Alter %s for %s (%s constraint(s))" % ( - self.option_name, - self.name, - len(self.option_value or ""), - ) + return "Alter %s for %s (%s constraint(s))" % (self.option_name, self.name, len(self.option_value or '')) @property def migration_name_fragment(self): - return "alter_%s_%s" % (self.name_lower, self.option_name) + return 'alter_%s_%s' % (self.name_lower, self.option_name) class AlterUniqueTogether(AlterTogetherOptionOperation): @@ -577,8 +556,7 @@ class AlterUniqueTogether(AlterTogetherOptionOperation): Change the value of unique_together to the target one. Input value of unique_together must be a set of tuples. """ - - option_name = "unique_together" + option_name = 'unique_together' def __init__(self, name, unique_together): super().__init__(name, unique_together) @@ -589,7 +567,6 @@ class AlterIndexTogether(AlterTogetherOptionOperation): Change the value of index_together to the target one. Input value of index_together must be a set of tuples. """ - option_name = "index_together" def __init__(self, name, index_together): @@ -599,7 +576,7 @@ class AlterIndexTogether(AlterTogetherOptionOperation): class AlterOrderWithRespectTo(ModelOptionOperation): """Represent a change with the order_with_respect_to option.""" - option_name = "order_with_respect_to" + option_name = 'order_with_respect_to' def __init__(self, name, order_with_respect_to): self.order_with_respect_to = order_with_respect_to @@ -607,36 +584,30 @@ class AlterOrderWithRespectTo(ModelOptionOperation): def deconstruct(self): kwargs = { - "name": self.name, - "order_with_respect_to": self.order_with_respect_to, + 'name': self.name, + 'order_with_respect_to': self.order_with_respect_to, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.alter_model_options( - app_label, - self.name_lower, - {self.option_name: self.order_with_respect_to}, - ) + model_state = state.models[app_label, self.name_lower] + model_state.options['order_with_respect_to'] = self.order_with_respect_to + state.reload_model(app_label, self.name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): to_model = to_state.apps.get_model(app_label, self.name) if self.allow_migrate_model(schema_editor.connection.alias, to_model): from_model = from_state.apps.get_model(app_label, self.name) # Remove a field if we need to - if ( - from_model._meta.order_with_respect_to - and not to_model._meta.order_with_respect_to - ): - schema_editor.remove_field( - from_model, from_model._meta.get_field("_order") - ) + if from_model._meta.order_with_respect_to and not to_model._meta.order_with_respect_to: + schema_editor.remove_field(from_model, from_model._meta.get_field("_order")) # Add a field if we need to (altering the column is untouched as # it's likely a rename) - elif ( - to_model._meta.order_with_respect_to - and not from_model._meta.order_with_respect_to - ): + elif to_model._meta.order_with_respect_to and not from_model._meta.order_with_respect_to: field = to_model._meta.get_field("_order") if not field.has_default(): field.default = 0 @@ -649,19 +620,20 @@ class AlterOrderWithRespectTo(ModelOptionOperation): self.database_forwards(app_label, schema_editor, from_state, to_state) def references_field(self, model_name, name, app_label): - return self.references_model(model_name, app_label) and ( - self.order_with_respect_to is None or name == self.order_with_respect_to + return ( + self.references_model(model_name, app_label) and + ( + self.order_with_respect_to is None or + name == self.order_with_respect_to + ) ) def describe(self): - return "Set order_with_respect_to on %s to %s" % ( - self.name, - self.order_with_respect_to, - ) + return "Set order_with_respect_to on %s to %s" % (self.name, self.order_with_respect_to) @property def migration_name_fragment(self): - return "alter_%s_order_with_respect_to" % self.name_lower + return 'alter_%s_order_with_respect_to' % self.name_lower class AlterModelOptions(ModelOptionOperation): @@ -692,18 +664,22 @@ class AlterModelOptions(ModelOptionOperation): def deconstruct(self): kwargs = { - "name": self.name, - "options": self.options, + 'name': self.name, + 'options': self.options, } - return (self.__class__.__qualname__, [], kwargs) + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): - state.alter_model_options( - app_label, - self.name_lower, - self.options, - self.ALTER_OPTION_KEYS, - ) + model_state = state.models[app_label, self.name_lower] + model_state.options = {**model_state.options, **self.options} + for key in self.ALTER_OPTION_KEYS: + if key not in self.options: + model_state.options.pop(key, False) + state.reload_model(app_label, self.name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): pass @@ -716,23 +692,29 @@ class AlterModelOptions(ModelOptionOperation): @property def migration_name_fragment(self): - return "alter_%s_options" % self.name_lower + return 'alter_%s_options' % self.name_lower class AlterModelManagers(ModelOptionOperation): """Alter the model's managers.""" - serialization_expand_args = ["managers"] + serialization_expand_args = ['managers'] def __init__(self, name, managers): self.managers = managers super().__init__(name) def deconstruct(self): - return (self.__class__.__qualname__, [self.name, self.managers], {}) + return ( + self.__class__.__qualname__, + [self.name, self.managers], + {} + ) def state_forwards(self, app_label, state): - state.alter_model_managers(app_label, self.name_lower, self.managers) + model_state = state.models[app_label, self.name_lower] + model_state.managers = list(self.managers) + state.reload_model(app_label, self.name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): pass @@ -745,11 +727,11 @@ class AlterModelManagers(ModelOptionOperation): @property def migration_name_fragment(self): - return "alter_%s_managers" % self.name_lower + return 'alter_%s_managers' % self.name_lower class IndexOperation(Operation): - option_name = "indexes" + option_name = 'indexes' @cached_property def model_name_lower(self): @@ -769,7 +751,9 @@ class AddIndex(IndexOperation): self.index = index def state_forwards(self, app_label, state): - state.add_index(app_label, self.model_name_lower, self.index) + model_state = state.models[app_label, self.model_name_lower] + model_state.options[self.option_name] = [*model_state.options[self.option_name], self.index.clone()] + state.reload_model(app_label, self.model_name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): model = to_state.apps.get_model(app_label, self.model_name) @@ -783,8 +767,8 @@ class AddIndex(IndexOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "index": self.index, + 'model_name': self.model_name, + 'index': self.index, } return ( self.__class__.__qualname__, @@ -794,20 +778,20 @@ class AddIndex(IndexOperation): def describe(self): if self.index.expressions: - return "Create index %s on %s on model %s" % ( + return 'Create index %s on %s on model %s' % ( self.index.name, - ", ".join([str(expression) for expression in self.index.expressions]), + ', '.join([str(expression) for expression in self.index.expressions]), self.model_name, ) - return "Create index %s on field(s) %s of model %s" % ( + return 'Create index %s on field(s) %s of model %s' % ( self.index.name, - ", ".join(self.index.fields), + ', '.join(self.index.fields), self.model_name, ) @property def migration_name_fragment(self): - return "%s_%s" % (self.model_name_lower, self.index.name.lower()) + return '%s_%s' % (self.model_name_lower, self.index.name.lower()) class RemoveIndex(IndexOperation): @@ -818,7 +802,10 @@ class RemoveIndex(IndexOperation): self.name = name def state_forwards(self, app_label, state): - state.remove_index(app_label, self.model_name_lower, self.name) + model_state = state.models[app_label, self.model_name_lower] + indexes = model_state.options[self.option_name] + model_state.options[self.option_name] = [idx for idx in indexes if idx.name != self.name] + state.reload_model(app_label, self.model_name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): model = from_state.apps.get_model(app_label, self.model_name) @@ -836,8 +823,8 @@ class RemoveIndex(IndexOperation): def deconstruct(self): kwargs = { - "model_name": self.model_name, - "name": self.name, + 'model_name': self.model_name, + 'name': self.name, } return ( self.__class__.__qualname__, @@ -846,22 +833,24 @@ class RemoveIndex(IndexOperation): ) def describe(self): - return "Remove index %s from %s" % (self.name, self.model_name) + return 'Remove index %s from %s' % (self.name, self.model_name) @property def migration_name_fragment(self): - return "remove_%s_%s" % (self.model_name_lower, self.name.lower()) + return 'remove_%s_%s' % (self.model_name_lower, self.name.lower()) class AddConstraint(IndexOperation): - option_name = "constraints" + option_name = 'constraints' def __init__(self, model_name, constraint): self.model_name = model_name self.constraint = constraint def state_forwards(self, app_label, state): - state.add_constraint(app_label, self.model_name_lower, self.constraint) + model_state = state.models[app_label, self.model_name_lower] + model_state.options[self.option_name] = [*model_state.options[self.option_name], self.constraint] + state.reload_model(app_label, self.model_name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): model = to_state.apps.get_model(app_label, self.model_name) @@ -874,35 +863,31 @@ class AddConstraint(IndexOperation): schema_editor.remove_constraint(model, self.constraint) def deconstruct(self): - return ( - self.__class__.__name__, - [], - { - "model_name": self.model_name, - "constraint": self.constraint, - }, - ) + return self.__class__.__name__, [], { + 'model_name': self.model_name, + 'constraint': self.constraint, + } def describe(self): - return "Create constraint %s on model %s" % ( - self.constraint.name, - self.model_name, - ) + return 'Create constraint %s on model %s' % (self.constraint.name, self.model_name) @property def migration_name_fragment(self): - return "%s_%s" % (self.model_name_lower, self.constraint.name.lower()) + return '%s_%s' % (self.model_name_lower, self.constraint.name.lower()) class RemoveConstraint(IndexOperation): - option_name = "constraints" + option_name = 'constraints' def __init__(self, model_name, name): self.model_name = model_name self.name = name def state_forwards(self, app_label, state): - state.remove_constraint(app_label, self.model_name_lower, self.name) + model_state = state.models[app_label, self.model_name_lower] + constraints = model_state.options[self.option_name] + model_state.options[self.option_name] = [c for c in constraints if c.name != self.name] + state.reload_model(app_label, self.model_name_lower, delay=True) def database_forwards(self, app_label, schema_editor, from_state, to_state): model = to_state.apps.get_model(app_label, self.model_name) @@ -919,18 +904,14 @@ class RemoveConstraint(IndexOperation): schema_editor.add_constraint(model, constraint) def deconstruct(self): - return ( - self.__class__.__name__, - [], - { - "model_name": self.model_name, - "name": self.name, - }, - ) + return self.__class__.__name__, [], { + 'model_name': self.model_name, + 'name': self.name, + } def describe(self): - return "Remove constraint %s from model %s" % (self.name, self.model_name) + return 'Remove constraint %s from model %s' % (self.name, self.model_name) @property def migration_name_fragment(self): - return "remove_%s_%s" % (self.model_name_lower, self.name.lower()) + return 'remove_%s_%s' % (self.model_name_lower, self.name.lower()) diff --git a/venv/Lib/site-packages/django/db/migrations/operations/special.py b/venv/Lib/site-packages/django/db/migrations/operations/special.py index 94a6ec7..5a8510e 100644 --- a/venv/Lib/site-packages/django/db/migrations/operations/special.py +++ b/venv/Lib/site-packages/django/db/migrations/operations/special.py @@ -11,7 +11,7 @@ class SeparateDatabaseAndState(Operation): that affect the state or not the database, or so on. """ - serialization_expand_args = ["database_operations", "state_operations"] + serialization_expand_args = ['database_operations', 'state_operations'] def __init__(self, database_operations=None, state_operations=None): self.database_operations = database_operations or [] @@ -20,10 +20,14 @@ class SeparateDatabaseAndState(Operation): def deconstruct(self): kwargs = {} if self.database_operations: - kwargs["database_operations"] = self.database_operations + kwargs['database_operations'] = self.database_operations if self.state_operations: - kwargs["state_operations"] = self.state_operations - return (self.__class__.__qualname__, [], kwargs) + kwargs['state_operations'] = self.state_operations + return ( + self.__class__.__qualname__, + [], + kwargs + ) def state_forwards(self, app_label, state): for state_operation in self.state_operations: @@ -34,9 +38,7 @@ class SeparateDatabaseAndState(Operation): for database_operation in self.database_operations: to_state = from_state.clone() database_operation.state_forwards(app_label, to_state) - database_operation.database_forwards( - app_label, schema_editor, from_state, to_state - ) + database_operation.database_forwards(app_label, schema_editor, from_state, to_state) from_state = to_state def database_backwards(self, app_label, schema_editor, from_state, to_state): @@ -52,9 +54,7 @@ class SeparateDatabaseAndState(Operation): for database_operation in reversed(self.database_operations): from_state = to_state to_state = to_states[database_operation] - database_operation.database_backwards( - app_label, schema_editor, from_state, to_state - ) + database_operation.database_backwards(app_label, schema_editor, from_state, to_state) def describe(self): return "Custom state/database change combination" @@ -67,12 +67,9 @@ class RunSQL(Operation): Also accept a list of operations that represent the state change effected by this SQL change, in case it's custom column/table creation/deletion. """ + noop = '' - noop = "" - - def __init__( - self, sql, reverse_sql=None, state_operations=None, hints=None, elidable=False - ): + def __init__(self, sql, reverse_sql=None, state_operations=None, hints=None, elidable=False): self.sql = sql self.reverse_sql = reverse_sql self.state_operations = state_operations or [] @@ -81,15 +78,19 @@ class RunSQL(Operation): def deconstruct(self): kwargs = { - "sql": self.sql, + 'sql': self.sql, } if self.reverse_sql is not None: - kwargs["reverse_sql"] = self.reverse_sql + kwargs['reverse_sql'] = self.reverse_sql if self.state_operations: - kwargs["state_operations"] = self.state_operations + kwargs['state_operations'] = self.state_operations if self.hints: - kwargs["hints"] = self.hints - return (self.__class__.__qualname__, [], kwargs) + kwargs['hints'] = self.hints + return ( + self.__class__.__qualname__, + [], + kwargs + ) @property def reversible(self): @@ -100,17 +101,13 @@ class RunSQL(Operation): state_operation.state_forwards(app_label, state) def database_forwards(self, app_label, schema_editor, from_state, to_state): - if router.allow_migrate( - schema_editor.connection.alias, app_label, **self.hints - ): + if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints): self._run_sql(schema_editor, self.sql) def database_backwards(self, app_label, schema_editor, from_state, to_state): if self.reverse_sql is None: raise NotImplementedError("You cannot reverse this operation") - if router.allow_migrate( - schema_editor.connection.alias, app_label, **self.hints - ): + if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints): self._run_sql(schema_editor, self.reverse_sql) def describe(self): @@ -140,9 +137,7 @@ class RunPython(Operation): reduces_to_sql = False - def __init__( - self, code, reverse_code=None, atomic=None, hints=None, elidable=False - ): + def __init__(self, code, reverse_code=None, atomic=None, hints=None, elidable=False): self.atomic = atomic # Forwards code if not callable(code): @@ -160,15 +155,19 @@ class RunPython(Operation): def deconstruct(self): kwargs = { - "code": self.code, + 'code': self.code, } if self.reverse_code is not None: - kwargs["reverse_code"] = self.reverse_code + kwargs['reverse_code'] = self.reverse_code if self.atomic is not None: - kwargs["atomic"] = self.atomic + kwargs['atomic'] = self.atomic if self.hints: - kwargs["hints"] = self.hints - return (self.__class__.__qualname__, [], kwargs) + kwargs['hints'] = self.hints + return ( + self.__class__.__qualname__, + [], + kwargs + ) @property def reversible(self): @@ -183,9 +182,7 @@ class RunPython(Operation): # RunPython has access to all models. Ensure that all models are # reloaded in case any are delayed. from_state.clear_delayed_apps_cache() - if router.allow_migrate( - schema_editor.connection.alias, app_label, **self.hints - ): + if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints): # We now execute the Python code in a context that contains a 'models' # object, representing the versioned models as an app registry. # We could try to override the global cache, but then people will still @@ -195,9 +192,7 @@ class RunPython(Operation): def database_backwards(self, app_label, schema_editor, from_state, to_state): if self.reverse_code is None: raise NotImplementedError("You cannot reverse this operation") - if router.allow_migrate( - schema_editor.connection.alias, app_label, **self.hints - ): + if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints): self.reverse_code(from_state.apps, schema_editor) def describe(self): diff --git a/venv/Lib/site-packages/django/db/migrations/operations/utils.py b/venv/Lib/site-packages/django/db/migrations/operations/utils.py new file mode 100644 index 0000000..facfd9f --- /dev/null +++ b/venv/Lib/site-packages/django/db/migrations/operations/utils.py @@ -0,0 +1,102 @@ +from collections import namedtuple + +from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT + + +def resolve_relation(model, app_label=None, model_name=None): + """ + Turn a model class or model reference string and return a model tuple. + + app_label and model_name are used to resolve the scope of recursive and + unscoped model relationship. + """ + if isinstance(model, str): + if model == RECURSIVE_RELATIONSHIP_CONSTANT: + if app_label is None or model_name is None: + raise TypeError( + 'app_label and model_name must be provided to resolve ' + 'recursive relationships.' + ) + return app_label, model_name + if '.' in model: + app_label, model_name = model.split('.', 1) + return app_label, model_name.lower() + if app_label is None: + raise TypeError( + 'app_label must be provided to resolve unscoped model ' + 'relationships.' + ) + return app_label, model.lower() + return model._meta.app_label, model._meta.model_name + + +FieldReference = namedtuple('FieldReference', 'to through') + + +def field_references( + model_tuple, + field, + reference_model_tuple, + reference_field_name=None, + reference_field=None, +): + """ + Return either False or a FieldReference if `field` references provided + context. + + False positives can be returned if `reference_field_name` is provided + without `reference_field` because of the introspection limitation it + incurs. This should not be an issue when this function is used to determine + whether or not an optimization can take place. + """ + remote_field = field.remote_field + if not remote_field: + return False + references_to = None + references_through = None + if resolve_relation(remote_field.model, *model_tuple) == reference_model_tuple: + to_fields = getattr(field, 'to_fields', None) + if ( + reference_field_name is None or + # Unspecified to_field(s). + to_fields is None or + # Reference to primary key. + (None in to_fields and (reference_field is None or reference_field.primary_key)) or + # Reference to field. + reference_field_name in to_fields + ): + references_to = (remote_field, to_fields) + through = getattr(remote_field, 'through', None) + if through and resolve_relation(through, *model_tuple) == reference_model_tuple: + through_fields = remote_field.through_fields + if ( + reference_field_name is None or + # Unspecified through_fields. + through_fields is None or + # Reference to field. + reference_field_name in through_fields + ): + references_through = (remote_field, through_fields) + if not (references_to or references_through): + return False + return FieldReference(references_to, references_through) + + +def get_references(state, model_tuple, field_tuple=()): + """ + Generator of (model_state, name, field, reference) referencing + provided context. + + If field_tuple is provided only references to this particular field of + model_tuple will be generated. + """ + for state_model_tuple, model_state in state.models.items(): + for name, field in model_state.fields.items(): + reference = field_references(state_model_tuple, field, model_tuple, *field_tuple) + if reference: + yield model_state, name, field, reference + + +def field_is_referenced(state, model_tuple, field_tuple): + """Return whether `field_tuple` is referenced by any state models.""" + return next(get_references(state, model_tuple, field_tuple), None) is not None diff --git a/venv/Lib/site-packages/django/db/migrations/optimizer.py b/venv/Lib/site-packages/django/db/migrations/optimizer.py index 7e5dea2..ee20f62 100644 --- a/venv/Lib/site-packages/django/db/migrations/optimizer.py +++ b/venv/Lib/site-packages/django/db/migrations/optimizer.py @@ -28,7 +28,7 @@ class MigrationOptimizer: """ # Internal tracking variable for test assertions about # of loops if app_label is None: - raise TypeError("app_label must be a str.") + raise TypeError('app_label must be a str.') self._iterations = 0 while True: result = self.optimize_inner(operations, app_label) @@ -43,10 +43,10 @@ class MigrationOptimizer: for i, operation in enumerate(operations): right = True # Should we reduce on the right or on the left. # Compare it to each operation after it - for j, other in enumerate(operations[i + 1 :]): + for j, other in enumerate(operations[i + 1:]): result = operation.reduce(other, app_label) if isinstance(result, list): - in_between = operations[i + 1 : i + j + 1] + in_between = operations[i + 1:i + j + 1] if right: new_operations.extend(in_between) new_operations.extend(result) @@ -59,7 +59,7 @@ class MigrationOptimizer: # Otherwise keep trying. new_operations.append(operation) break - new_operations.extend(operations[i + j + 2 :]) + new_operations.extend(operations[i + j + 2:]) return new_operations elif not result: # Can't perform a right reduction. diff --git a/venv/Lib/site-packages/django/db/migrations/questioner.py b/venv/Lib/site-packages/django/db/migrations/questioner.py index 2eedfe6..9edb818 100644 --- a/venv/Lib/site-packages/django/db/migrations/questioner.py +++ b/venv/Lib/site-packages/django/db/migrations/questioner.py @@ -33,7 +33,7 @@ class MigrationQuestioner: # file check will ensure we skip South ones. try: app_config = apps.get_app_config(app_label) - except LookupError: # It's a fake app. + except LookupError: # It's a fake app. return self.defaults.get("ask_initial", False) migrations_import_path, _ = MigrationLoader.migrations_module(app_config.label) if migrations_import_path is None: @@ -44,6 +44,7 @@ class MigrationQuestioner: except ImportError: return self.defaults.get("ask_initial", False) else: + # getattr() needed on PY36 and older (replace with attribute access). if getattr(migrations_module, "__file__", None): filenames = os.listdir(os.path.dirname(migrations_module.__file__)) elif hasattr(migrations_module, "__path__"): @@ -71,7 +72,7 @@ class MigrationQuestioner: return self.defaults.get("ask_rename_model", False) def ask_merge(self, app_label): - """Should these migrations really be merged?""" + """Do you really want to merge these migrations?""" return self.defaults.get("ask_merge", False) def ask_auto_now_add_addition(self, field_name, model_name): @@ -81,6 +82,7 @@ class MigrationQuestioner: class InteractiveMigrationQuestioner(MigrationQuestioner): + def _boolean_input(self, question, default=None): result = input("%s " % question) if not result and default is not None: @@ -104,7 +106,7 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): return value result = input("Please select a valid option: ") - def _ask_default(self, default=""): + def _ask_default(self, default=''): """ Prompt for a default value. @@ -112,16 +114,13 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): string) which will be shown to the user and used as the return value if the user doesn't provide any other input. """ - print("Please enter the default value as valid Python.") + print("Please enter the default value now, as valid Python") if default: print( - f"Accept the default '{default}' by pressing 'Enter' or " - f"provide another value." + "You can accept the default '{}' by pressing 'Enter' or you " + "can provide another value.".format(default) ) - print( - "The datetime and django.utils.timezone modules are available, so " - "it is possible to provide e.g. timezone.now as a value." - ) + print("The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now") print("Type 'exit' to exit this prompt") while True: if default: @@ -132,12 +131,12 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): if not code and default: code = default if not code: - print("Please enter some code, or 'exit' (without quotes) to exit.") + print("Please enter some code, or 'exit' (with no quotes) to exit.") elif code == "exit": sys.exit(1) else: try: - return eval(code, {}, {"datetime": datetime, "timezone": timezone}) + return eval(code, {}, {'datetime': datetime, 'timezone': timezone}) except (SyntaxError, NameError) as e: print("Invalid input: %s" % e) @@ -145,18 +144,14 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): """Adding a NOT NULL field to a model.""" if not self.dry_run: choice = self._choice_input( - f"It is impossible to add a non-nullable field '{field_name}' " - f"to {model_name} without specifying a default. This is " - f"because the database needs something to populate existing " - f"rows.\n" - f"Please select a fix:", + "You are trying to add a non-nullable field '%s' to %s without a default; " + "we can't do that (the database needs something to populate existing rows).\n" + "Please select a fix:" % (field_name, model_name), [ - ( - "Provide a one-off default now (will be set on all existing " - "rows with a null value for this column)" - ), - "Quit and manually define a default value in models.py.", - ], + ("Provide a one-off default now (will be set on all existing " + "rows with a null value for this column)"), + "Quit, and let me add a default in models.py", + ] ) if choice == 2: sys.exit(3) @@ -168,21 +163,18 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): """Changing a NULL field to NOT NULL.""" if not self.dry_run: choice = self._choice_input( - f"It is impossible to change a nullable field '{field_name}' " - f"on {model_name} to non-nullable without providing a " - f"default. This is because the database needs something to " - f"populate existing rows.\n" - f"Please select a fix:", + "You are trying to change the nullable field '%s' on %s to non-nullable " + "without a default; we can't do that (the database needs something to " + "populate existing rows).\n" + "Please select a fix:" % (field_name, model_name), [ - ( - "Provide a one-off default now (will be set on all existing " - "rows with a null value for this column)" - ), - "Ignore for now. Existing rows that contain NULL values " - "will have to be handled manually, for example with a " - "RunPython or RunSQL operation.", - "Quit and manually define a default value in models.py.", - ], + ("Provide a one-off default now (will be set on all existing " + "rows with a null value for this column)"), + ("Ignore for now, and let me handle existing rows with NULL myself " + "(e.g. because you added a RunPython or RunSQL operation to handle " + "NULL values in a previous data migration)"), + "Quit, and let me add a default in models.py", + ] ) if choice == 2: return NOT_PROVIDED @@ -194,33 +186,21 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): def ask_rename(self, model_name, old_name, new_name, field_instance): """Was this field really renamed?""" - msg = "Was %s.%s renamed to %s.%s (a %s)? [y/N]" - return self._boolean_input( - msg - % ( - model_name, - old_name, - model_name, - new_name, - field_instance.__class__.__name__, - ), - False, - ) + msg = "Did you rename %s.%s to %s.%s (a %s)? [y/N]" + return self._boolean_input(msg % (model_name, old_name, model_name, new_name, + field_instance.__class__.__name__), False) def ask_rename_model(self, old_model_state, new_model_state): """Was this model really renamed?""" - msg = "Was the model %s.%s renamed to %s? [y/N]" - return self._boolean_input( - msg - % (old_model_state.app_label, old_model_state.name, new_model_state.name), - False, - ) + msg = "Did you rename the %s.%s model to %s? [y/N]" + return self._boolean_input(msg % (old_model_state.app_label, old_model_state.name, + new_model_state.name), False) def ask_merge(self, app_label): return self._boolean_input( - "\nMerging will only work if the operations printed above do not conflict\n" - + "with each other (working on different fields or models)\n" - + "Should these migration branches be merged? [y/N]", + "\nMerging will only work if the operations printed above do not conflict\n" + + "with each other (working on different fields or models)\n" + + "Do you want to merge these migration branches? [y/N]", False, ) @@ -228,24 +208,24 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): """Adding an auto_now_add field to a model.""" if not self.dry_run: choice = self._choice_input( - f"It is impossible to add the field '{field_name}' with " - f"'auto_now_add=True' to {model_name} without providing a " - f"default. This is because the database needs something to " - f"populate existing rows.\n", + "You are trying to add the field '{}' with 'auto_now_add=True' " + "to {} without a default; the database needs something to " + "populate existing rows.\n".format(field_name, model_name), [ - "Provide a one-off default now which will be set on all " - "existing rows", - "Quit and manually define a default value in models.py.", - ], + "Provide a one-off default now (will be set on all " + "existing rows)", + "Quit, and let me add a default in models.py", + ] ) if choice == 2: sys.exit(3) else: - return self._ask_default(default="timezone.now") + return self._ask_default(default='timezone.now') return None class NonInteractiveMigrationQuestioner(MigrationQuestioner): + def ask_not_null_addition(self, field_name, model_name): # We can't ask the user, so act like the user aborted. sys.exit(3) diff --git a/venv/Lib/site-packages/django/db/migrations/recorder.py b/venv/Lib/site-packages/django/db/migrations/recorder.py index 50876a9..1a37c6b 100644 --- a/venv/Lib/site-packages/django/db/migrations/recorder.py +++ b/venv/Lib/site-packages/django/db/migrations/recorder.py @@ -18,7 +18,6 @@ class MigrationRecorder: If a migration is unapplied its row is removed from the table. Having a row in the table always means a migration is applied. """ - _migration_class = None @classproperty @@ -28,7 +27,6 @@ class MigrationRecorder: MigrationRecorder. """ if cls._migration_class is None: - class Migration(models.Model): app = models.CharField(max_length=255) name = models.CharField(max_length=255) @@ -36,11 +34,11 @@ class MigrationRecorder: class Meta: apps = Apps() - app_label = "migrations" - db_table = "django_migrations" + app_label = 'migrations' + db_table = 'django_migrations' def __str__(self): - return "Migration %s for %s" % (self.name, self.app) + return 'Migration %s for %s' % (self.name, self.app) cls._migration_class = Migration return cls._migration_class @@ -69,9 +67,7 @@ class MigrationRecorder: with self.connection.schema_editor() as editor: editor.create_model(self.Migration) except DatabaseError as exc: - raise MigrationSchemaMissing( - "Unable to create the django_migrations table (%s)" % exc - ) + raise MigrationSchemaMissing("Unable to create the django_migrations table (%s)" % exc) def applied_migrations(self): """ @@ -79,10 +75,7 @@ class MigrationRecorder: for all applied migrations. """ if self.has_table(): - return { - (migration.app, migration.name): migration - for migration in self.migration_qs - } + return {(migration.app, migration.name): migration for migration in self.migration_qs} else: # If the django_migrations table doesn't exist, then no migrations # are applied. diff --git a/venv/Lib/site-packages/django/db/migrations/serializer.py b/venv/Lib/site-packages/django/db/migrations/serializer.py index fb4a196..e19c881 100644 --- a/venv/Lib/site-packages/django/db/migrations/serializer.py +++ b/venv/Lib/site-packages/django/db/migrations/serializer.py @@ -25,16 +25,12 @@ class BaseSerializer: self.value = value def serialize(self): - raise NotImplementedError( - "Subclasses of BaseSerializer must implement the serialize() method." - ) + raise NotImplementedError('Subclasses of BaseSerializer must implement the serialize() method.') class BaseSequenceSerializer(BaseSerializer): def _format(self): - raise NotImplementedError( - "Subclasses of BaseSequenceSerializer must implement the _format() method." - ) + raise NotImplementedError('Subclasses of BaseSequenceSerializer must implement the _format() method.') def serialize(self): imports = set() @@ -59,21 +55,19 @@ class ChoicesSerializer(BaseSerializer): class DateTimeSerializer(BaseSerializer): """For datetime.*, except datetime.datetime.""" - def serialize(self): - return repr(self.value), {"import datetime"} + return repr(self.value), {'import datetime'} class DatetimeDatetimeSerializer(BaseSerializer): """For datetime.datetime.""" - def serialize(self): if self.value.tzinfo is not None and self.value.tzinfo != utc: self.value = self.value.astimezone(utc) imports = ["import datetime"] if self.value.tzinfo is not None: imports.append("from django.utils.timezone import utc") - return repr(self.value).replace("datetime.timezone.utc", "utc"), set(imports) + return repr(self.value).replace('<UTC>', 'utc'), set(imports) class DecimalSerializer(BaseSerializer): @@ -129,8 +123,8 @@ class EnumSerializer(BaseSerializer): enum_class = self.value.__class__ module = enum_class.__module__ return ( - "%s.%s[%r]" % (module, enum_class.__qualname__, self.value.name), - {"import %s" % module}, + '%s.%s[%r]' % (module, enum_class.__qualname__, self.value.name), + {'import %s' % module}, ) @@ -148,29 +142,23 @@ class FrozensetSerializer(BaseSequenceSerializer): class FunctionTypeSerializer(BaseSerializer): def serialize(self): - if getattr(self.value, "__self__", None) and isinstance( - self.value.__self__, type - ): + if getattr(self.value, "__self__", None) and isinstance(self.value.__self__, type): klass = self.value.__self__ module = klass.__module__ - return "%s.%s.%s" % (module, klass.__name__, self.value.__name__), { - "import %s" % module - } + return "%s.%s.%s" % (module, klass.__name__, self.value.__name__), {"import %s" % module} # Further error checking - if self.value.__name__ == "<lambda>": + if self.value.__name__ == '<lambda>': raise ValueError("Cannot serialize function: lambda") if self.value.__module__ is None: raise ValueError("Cannot serialize function %r: No module" % self.value) module_name = self.value.__module__ - if "<" not in self.value.__qualname__: # Qualname can include <locals> - return "%s.%s" % (module_name, self.value.__qualname__), { - "import %s" % self.value.__module__ - } + if '<' not in self.value.__qualname__: # Qualname can include <locals> + return '%s.%s' % (module_name, self.value.__qualname__), {'import %s' % self.value.__module__} raise ValueError( - "Could not find function %s in %s.\n" % (self.value.__name__, module_name) + 'Could not find function %s in %s.\n' % (self.value.__name__, module_name) ) @@ -179,14 +167,11 @@ class FunctoolsPartialSerializer(BaseSerializer): # Serialize functools.partial() arguments func_string, func_imports = serializer_factory(self.value.func).serialize() args_string, args_imports = serializer_factory(self.value.args).serialize() - keywords_string, keywords_imports = serializer_factory( - self.value.keywords - ).serialize() + keywords_string, keywords_imports = serializer_factory(self.value.keywords).serialize() # Add any imports needed by arguments - imports = {"import functools", *func_imports, *args_imports, *keywords_imports} + imports = {'import functools', *func_imports, *args_imports, *keywords_imports} return ( - "functools.%s(%s, *%s, **%s)" - % ( + 'functools.%s(%s, *%s, **%s)' % ( self.value.__class__.__name__, func_string, args_string, @@ -229,10 +214,9 @@ class ModelManagerSerializer(DeconstructableSerializer): class OperationSerializer(BaseSerializer): def serialize(self): from django.db.migrations.writer import OperationWriter - string, imports = OperationWriter(self.value, indentation=0).serialize() # Nested operation, trailing comma is handled in upper OperationWriter._write() - return string.rstrip(","), imports + return string.rstrip(','), imports class PathLikeSerializer(BaseSerializer): @@ -244,24 +228,22 @@ class PathSerializer(BaseSerializer): def serialize(self): # Convert concrete paths to pure paths to avoid issues with migrations # generated on one platform being used on a different platform. - prefix = "Pure" if isinstance(self.value, pathlib.Path) else "" - return "pathlib.%s%r" % (prefix, self.value), {"import pathlib"} + prefix = 'Pure' if isinstance(self.value, pathlib.Path) else '' + return 'pathlib.%s%r' % (prefix, self.value), {'import pathlib'} class RegexSerializer(BaseSerializer): def serialize(self): - regex_pattern, pattern_imports = serializer_factory( - self.value.pattern - ).serialize() + regex_pattern, pattern_imports = serializer_factory(self.value.pattern).serialize() # Turn off default implicit flags (e.g. re.U) because regexes with the # same implicit and explicit flags aren't equal. - flags = self.value.flags ^ re.compile("").flags + flags = self.value.flags ^ re.compile('').flags regex_flags, flag_imports = serializer_factory(flags).serialize() - imports = {"import re", *pattern_imports, *flag_imports} + imports = {'import re', *pattern_imports, *flag_imports} args = [regex_pattern] if flags: args.append(regex_flags) - return "re.compile(%s)" % ", ".join(args), imports + return "re.compile(%s)" % ', '.join(args), imports class SequenceSerializer(BaseSequenceSerializer): @@ -273,14 +255,12 @@ class SetSerializer(BaseSequenceSerializer): def _format(self): # Serialize as a set literal except when value is empty because {} # is an empty dict. - return "{%s}" if self.value else "set(%s)" + return '{%s}' if self.value else 'set(%s)' class SettingsReferenceSerializer(BaseSerializer): def serialize(self): - return "settings.%s" % self.value.setting_name, { - "from django.conf import settings" - } + return "settings.%s" % self.value.setting_name, {"from django.conf import settings"} class TupleSerializer(BaseSequenceSerializer): @@ -293,8 +273,8 @@ class TupleSerializer(BaseSequenceSerializer): class TypeSerializer(BaseSerializer): def serialize(self): special_cases = [ - (models.Model, "models.Model", ["from django.db import models"]), - (type(None), "type(None)", []), + (models.Model, "models.Model", []), + (type(None), 'type(None)', []), ] for case, string, imports in special_cases: if case is self.value: @@ -304,9 +284,7 @@ class TypeSerializer(BaseSerializer): if module == builtins.__name__: return self.value.__name__, set() else: - return "%s.%s" % (module, self.value.__qualname__), { - "import %s" % module - } + return "%s.%s" % (module, self.value.__qualname__), {"import %s" % module} class UUIDSerializer(BaseSerializer): @@ -331,11 +309,7 @@ class Serializer: (bool, int, type(None), bytes, str, range): BaseSimpleSerializer, decimal.Decimal: DecimalSerializer, (functools.partial, functools.partialmethod): FunctoolsPartialSerializer, - ( - types.FunctionType, - types.BuiltinFunctionType, - types.MethodType, - ): FunctionTypeSerializer, + (types.FunctionType, types.BuiltinFunctionType, types.MethodType): FunctionTypeSerializer, collections.abc.Iterable: IterableSerializer, (COMPILED_REGEX_TYPE, RegexObject): RegexSerializer, uuid.UUID: UUIDSerializer, @@ -346,9 +320,7 @@ class Serializer: @classmethod def register(cls, type_, serializer): if not issubclass(serializer, BaseSerializer): - raise ValueError( - "'%s' must inherit from 'BaseSerializer'." % serializer.__name__ - ) + raise ValueError("'%s' must inherit from 'BaseSerializer'." % serializer.__name__) cls._registry[type_] = serializer @classmethod @@ -373,7 +345,7 @@ def serializer_factory(value): if isinstance(value, type): return TypeSerializer(value) # Anything that knows how to deconstruct itself. - if hasattr(value, "deconstruct"): + if hasattr(value, 'deconstruct'): return DeconstructableSerializer(value) for type_, serializer_cls in Serializer._registry.items(): if isinstance(value, type_): diff --git a/venv/Lib/site-packages/django/db/migrations/state.py b/venv/Lib/site-packages/django/db/migrations/state.py index 7d7a917..7966878 100644 --- a/venv/Lib/site-packages/django/db/migrations/state.py +++ b/venv/Lib/site-packages/django/db/migrations/state.py @@ -1,16 +1,10 @@ import copy -from collections import defaultdict from contextlib import contextmanager -from functools import partial from django.apps import AppConfig -from django.apps.registry import Apps -from django.apps.registry import apps as global_apps +from django.apps.registry import Apps, apps as global_apps from django.conf import settings -from django.core.exceptions import FieldDoesNotExist from django.db import models -from django.db.migrations.utils import field_is_referenced, get_references -from django.db.models import NOT_PROVIDED from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT from django.db.models.options import DEFAULT_NAMES, normalize_together from django.db.models.utils import make_model_tuple @@ -19,12 +13,11 @@ from django.utils.module_loading import import_string from django.utils.version import get_docs_version from .exceptions import InvalidBasesError -from .utils import resolve_relation -def _get_app_label_and_model_name(model, app_label=""): +def _get_app_label_and_model_name(model, app_label=''): if isinstance(model, str): - split = model.split(".", 1) + split = model.split('.', 1) return tuple(split) if len(split) == 2 else (app_label, split[0]) else: return model._meta.app_label, model._meta.model_name @@ -33,17 +26,12 @@ def _get_app_label_and_model_name(model, app_label=""): def _get_related_models(m): """Return all models that have a direct relationship to the given model.""" related_models = [ - subclass - for subclass in m.__subclasses__() + subclass for subclass in m.__subclasses__() if issubclass(subclass, models.Model) ] related_fields_models = set() for f in m._meta.get_fields(include_parents=True, include_hidden=True): - if ( - f.is_relation - and f.related_model is not None - and not isinstance(f.related_model, str) - ): + if f.is_relation and f.related_model is not None and not isinstance(f.related_model, str): related_fields_models.add(f.model) related_models.append(f.related_model) # Reverse accessors of foreign keys to proxy models are attached to their @@ -79,10 +67,7 @@ def get_related_models_recursive(model): seen = set() queue = _get_related_models(model) for rel_mod in queue: - rel_app_label, rel_model_name = ( - rel_mod._meta.app_label, - rel_mod._meta.model_name, - ) + rel_app_label, rel_model_name = rel_mod._meta.app_label, rel_mod._meta.model_name if (rel_app_label, rel_model_name) in seen: continue seen.add((rel_app_label, rel_model_name)) @@ -100,228 +85,23 @@ class ProjectState: def __init__(self, models=None, real_apps=None): self.models = models or {} # Apps to include from main registry, usually unmigrated ones - if real_apps is None: - real_apps = set() - else: - assert isinstance(real_apps, set) - self.real_apps = real_apps + self.real_apps = real_apps or [] self.is_delayed = False - # {remote_model_key: {model_key: {field_name: field}}} - self._relations = None - - @property - def relations(self): - if self._relations is None: - self.resolve_fields_and_relations() - return self._relations def add_model(self, model_state): - model_key = model_state.app_label, model_state.name_lower - self.models[model_key] = model_state - if self._relations is not None: - self.resolve_model_relations(model_key) - if "apps" in self.__dict__: # hasattr would cache the property - self.reload_model(*model_key) + app_label, model_name = model_state.app_label, model_state.name_lower + self.models[(app_label, model_name)] = model_state + if 'apps' in self.__dict__: # hasattr would cache the property + self.reload_model(app_label, model_name) def remove_model(self, app_label, model_name): - model_key = app_label, model_name - del self.models[model_key] - if self._relations is not None: - self._relations.pop(model_key, None) - # Call list() since _relations can change size during iteration. - for related_model_key, model_relations in list(self._relations.items()): - model_relations.pop(model_key, None) - if not model_relations: - del self._relations[related_model_key] - if "apps" in self.__dict__: # hasattr would cache the property - self.apps.unregister_model(*model_key) + del self.models[app_label, model_name] + if 'apps' in self.__dict__: # hasattr would cache the property + self.apps.unregister_model(app_label, model_name) # Need to do this explicitly since unregister_model() doesn't clear # the cache automatically (#24513) self.apps.clear_cache() - def rename_model(self, app_label, old_name, new_name): - # Add a new model. - old_name_lower = old_name.lower() - new_name_lower = new_name.lower() - renamed_model = self.models[app_label, old_name_lower].clone() - renamed_model.name = new_name - self.models[app_label, new_name_lower] = renamed_model - # Repoint all fields pointing to the old model to the new one. - old_model_tuple = (app_label, old_name_lower) - new_remote_model = f"{app_label}.{new_name}" - to_reload = set() - for model_state, name, field, reference in get_references( - self, old_model_tuple - ): - changed_field = None - if reference.to: - changed_field = field.clone() - changed_field.remote_field.model = new_remote_model - if reference.through: - if changed_field is None: - changed_field = field.clone() - changed_field.remote_field.through = new_remote_model - if changed_field: - model_state.fields[name] = changed_field - to_reload.add((model_state.app_label, model_state.name_lower)) - if self._relations is not None: - old_name_key = app_label, old_name_lower - new_name_key = app_label, new_name_lower - if old_name_key in self._relations: - self._relations[new_name_key] = self._relations.pop(old_name_key) - for model_relations in self._relations.values(): - if old_name_key in model_relations: - model_relations[new_name_key] = model_relations.pop(old_name_key) - # Reload models related to old model before removing the old model. - self.reload_models(to_reload, delay=True) - # Remove the old model. - self.remove_model(app_label, old_name_lower) - self.reload_model(app_label, new_name_lower, delay=True) - - def alter_model_options(self, app_label, model_name, options, option_keys=None): - model_state = self.models[app_label, model_name] - model_state.options = {**model_state.options, **options} - if option_keys: - for key in option_keys: - if key not in options: - model_state.options.pop(key, False) - self.reload_model(app_label, model_name, delay=True) - - def alter_model_managers(self, app_label, model_name, managers): - model_state = self.models[app_label, model_name] - model_state.managers = list(managers) - self.reload_model(app_label, model_name, delay=True) - - def _append_option(self, app_label, model_name, option_name, obj): - model_state = self.models[app_label, model_name] - model_state.options[option_name] = [*model_state.options[option_name], obj] - self.reload_model(app_label, model_name, delay=True) - - def _remove_option(self, app_label, model_name, option_name, obj_name): - model_state = self.models[app_label, model_name] - objs = model_state.options[option_name] - model_state.options[option_name] = [obj for obj in objs if obj.name != obj_name] - self.reload_model(app_label, model_name, delay=True) - - def add_index(self, app_label, model_name, index): - self._append_option(app_label, model_name, "indexes", index) - - def remove_index(self, app_label, model_name, index_name): - self._remove_option(app_label, model_name, "indexes", index_name) - - def add_constraint(self, app_label, model_name, constraint): - self._append_option(app_label, model_name, "constraints", constraint) - - def remove_constraint(self, app_label, model_name, constraint_name): - self._remove_option(app_label, model_name, "constraints", constraint_name) - - def add_field(self, app_label, model_name, name, field, preserve_default): - # If preserve default is off, don't use the default for future state. - if not preserve_default: - field = field.clone() - field.default = NOT_PROVIDED - else: - field = field - model_key = app_label, model_name - self.models[model_key].fields[name] = field - if self._relations is not None: - self.resolve_model_field_relations(model_key, name, field) - # Delay rendering of relationships if it's not a relational field. - delay = not field.is_relation - self.reload_model(*model_key, delay=delay) - - def remove_field(self, app_label, model_name, name): - model_key = app_label, model_name - model_state = self.models[model_key] - old_field = model_state.fields.pop(name) - if self._relations is not None: - self.resolve_model_field_relations(model_key, name, old_field) - # Delay rendering of relationships if it's not a relational field. - delay = not old_field.is_relation - self.reload_model(*model_key, delay=delay) - - def alter_field(self, app_label, model_name, name, field, preserve_default): - if not preserve_default: - field = field.clone() - field.default = NOT_PROVIDED - else: - field = field - model_key = app_label, model_name - fields = self.models[model_key].fields - if self._relations is not None: - old_field = fields.pop(name) - if old_field.is_relation: - self.resolve_model_field_relations(model_key, name, old_field) - fields[name] = field - if field.is_relation: - self.resolve_model_field_relations(model_key, name, field) - else: - fields[name] = field - # TODO: investigate if old relational fields must be reloaded or if - # it's sufficient if the new field is (#27737). - # Delay rendering of relationships if it's not a relational field and - # not referenced by a foreign key. - delay = not field.is_relation and not field_is_referenced( - self, model_key, (name, field) - ) - self.reload_model(*model_key, delay=delay) - - def rename_field(self, app_label, model_name, old_name, new_name): - model_key = app_label, model_name - model_state = self.models[model_key] - # Rename the field. - fields = model_state.fields - try: - found = fields.pop(old_name) - except KeyError: - raise FieldDoesNotExist( - f"{app_label}.{model_name} has no field named '{old_name}'" - ) - fields[new_name] = found - for field in fields.values(): - # Fix from_fields to refer to the new field. - from_fields = getattr(field, "from_fields", None) - if from_fields: - field.from_fields = tuple( - [ - new_name if from_field_name == old_name else from_field_name - for from_field_name in from_fields - ] - ) - # Fix index/unique_together to refer to the new field. - options = model_state.options - for option in ("index_together", "unique_together"): - if option in options: - options[option] = [ - [new_name if n == old_name else n for n in together] - for together in options[option] - ] - # Fix to_fields to refer to the new field. - delay = True - references = get_references(self, model_key, (old_name, found)) - for *_, field, reference in references: - delay = False - if reference.to: - remote_field, to_fields = reference.to - if getattr(remote_field, "field_name", None) == old_name: - remote_field.field_name = new_name - if to_fields: - field.to_fields = tuple( - [ - new_name if to_field_name == old_name else to_field_name - for to_field_name in to_fields - ] - ) - if self._relations is not None: - old_name_lower = old_name.lower() - new_name_lower = new_name.lower() - for to_model in self._relations.values(): - if old_name_lower in to_model[model_key]: - field = to_model[model_key].pop(old_name_lower) - field.name = new_name_lower - to_model[model_key][new_name_lower] = field - self.reload_model(*model_key, delay=delay) - def _find_reload_model(self, app_label, model_name, delay=False): if delay: self.is_delayed = True @@ -349,9 +129,7 @@ class ProjectState: if field.is_relation: if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT: continue - rel_app_label, rel_model_name = _get_app_label_and_model_name( - field.related_model, app_label - ) + rel_app_label, rel_model_name = _get_app_label_and_model_name(field.related_model, app_label) direct_related_models.add((rel_app_label, rel_model_name.lower())) # For all direct related models recursively get all related models. @@ -373,17 +151,15 @@ class ProjectState: return related_models def reload_model(self, app_label, model_name, delay=False): - if "apps" in self.__dict__: # hasattr would cache the property + if 'apps' in self.__dict__: # hasattr would cache the property related_models = self._find_reload_model(app_label, model_name, delay) self._reload(related_models) def reload_models(self, models, delay=True): - if "apps" in self.__dict__: # hasattr would cache the property + if 'apps' in self.__dict__: # hasattr would cache the property related_models = set() for app_label, model_name in models: - related_models.update( - self._find_reload_model(app_label, model_name, delay) - ) + related_models.update(self._find_reload_model(app_label, model_name, delay)) self._reload(related_models) def _reload(self, related_models): @@ -412,137 +188,30 @@ class ProjectState: # Render all models self.apps.render_multiple(states_to_be_rendered) - def update_model_field_relation( - self, - model, - model_key, - field_name, - field, - concretes, - ): - remote_model_key = resolve_relation(model, *model_key) - if remote_model_key[0] not in self.real_apps and remote_model_key in concretes: - remote_model_key = concretes[remote_model_key] - relations_to_remote_model = self._relations[remote_model_key] - if field_name in self.models[model_key].fields: - # The assert holds because it's a new relation, or an altered - # relation, in which case references have been removed by - # alter_field(). - assert field_name not in relations_to_remote_model[model_key] - relations_to_remote_model[model_key][field_name] = field - else: - del relations_to_remote_model[model_key][field_name] - if not relations_to_remote_model[model_key]: - del relations_to_remote_model[model_key] - - def resolve_model_field_relations( - self, - model_key, - field_name, - field, - concretes=None, - ): - remote_field = field.remote_field - if not remote_field: - return - if concretes is None: - concretes, _ = self._get_concrete_models_mapping_and_proxy_models() - - self.update_model_field_relation( - remote_field.model, - model_key, - field_name, - field, - concretes, - ) - - through = getattr(remote_field, "through", None) - if not through: - return - self.update_model_field_relation( - through, model_key, field_name, field, concretes - ) - - def resolve_model_relations(self, model_key, concretes=None): - if concretes is None: - concretes, _ = self._get_concrete_models_mapping_and_proxy_models() - - model_state = self.models[model_key] - for field_name, field in model_state.fields.items(): - self.resolve_model_field_relations(model_key, field_name, field, concretes) - - def resolve_fields_and_relations(self): - # Resolve fields. - for model_state in self.models.values(): - for field_name, field in model_state.fields.items(): - field.name = field_name - # Resolve relations. - # {remote_model_key: {model_key: {field_name: field}}} - self._relations = defaultdict(partial(defaultdict, dict)) - concretes, proxies = self._get_concrete_models_mapping_and_proxy_models() - - for model_key in concretes: - self.resolve_model_relations(model_key, concretes) - - for model_key in proxies: - self._relations[model_key] = self._relations[concretes[model_key]] - - def get_concrete_model_key(self, model): - ( - concrete_models_mapping, - _, - ) = self._get_concrete_models_mapping_and_proxy_models() - model_key = make_model_tuple(model) - return concrete_models_mapping[model_key] - - def _get_concrete_models_mapping_and_proxy_models(self): - concrete_models_mapping = {} - proxy_models = {} - # Split models to proxy and concrete models. - for model_key, model_state in self.models.items(): - if model_state.options.get("proxy"): - proxy_models[model_key] = model_state - # Find a concrete model for the proxy. - concrete_models_mapping[ - model_key - ] = self._find_concrete_model_from_proxy( - proxy_models, - model_state, - ) - else: - concrete_models_mapping[model_key] = model_key - return concrete_models_mapping, proxy_models - - def _find_concrete_model_from_proxy(self, proxy_models, model_state): - for base in model_state.bases: - if not (isinstance(base, str) or issubclass(base, models.Model)): - continue - base_key = make_model_tuple(base) - base_state = proxy_models.get(base_key) - if not base_state: - # Concrete model found, stop looking at bases. - return base_key - return self._find_concrete_model_from_proxy(proxy_models, base_state) - def clone(self): """Return an exact copy of this ProjectState.""" new_state = ProjectState( models={k: v.clone() for k, v in self.models.items()}, real_apps=self.real_apps, ) - if "apps" in self.__dict__: + if 'apps' in self.__dict__: new_state.apps = self.apps.clone() new_state.is_delayed = self.is_delayed return new_state def clear_delayed_apps_cache(self): - if self.is_delayed and "apps" in self.__dict__: - del self.__dict__["apps"] + if self.is_delayed and 'apps' in self.__dict__: + del self.__dict__['apps'] @cached_property def apps(self): return StateApps(self.real_apps, self.models) + @property + def concrete_apps(self): + self.apps = StateApps(self.real_apps, self.models, ignore_swappable=True) + return self.apps + @classmethod def from_apps(cls, apps): """Take an Apps and return a ProjectState matching it.""" @@ -553,12 +222,11 @@ class ProjectState: return cls(app_models) def __eq__(self, other): - return self.models == other.models and self.real_apps == other.real_apps + return self.models == other.models and set(self.real_apps) == set(other.real_apps) class AppConfigStub(AppConfig): """Stub of an AppConfig. Only provides a label and a dict of models.""" - def __init__(self, label): self.apps = None self.models = {} @@ -577,7 +245,6 @@ class StateApps(Apps): Subclass of the global Apps registry class to better handle dynamic model additions and removals. """ - def __init__(self, real_apps, models, ignore_swappable=False): # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there @@ -591,9 +258,7 @@ class StateApps(Apps): self.real_models.append(ModelState.from_model(model, exclude_rels=True)) # Populate the app registry with a stub for each application. app_labels = {model_state.app_label for model_state in models.values()} - app_configs = [ - AppConfigStub(label) for label in sorted([*real_apps, *app_labels]) - ] + app_configs = [AppConfigStub(label) for label in sorted([*real_apps, *app_labels])] super().__init__(app_configs) # These locks get in the way of copying as implemented in clone(), @@ -606,10 +271,7 @@ class StateApps(Apps): # There shouldn't be any operations pending at this point. from django.core.checks.model_checks import _check_lazy_references - - ignore = ( - {make_model_tuple(settings.AUTH_USER_MODEL)} if ignore_swappable else set() - ) + ignore = {make_model_tuple(settings.AUTH_USER_MODEL)} if ignore_swappable else set() errors = _check_lazy_references(self, ignore=ignore) if errors: raise ValueError("\n".join(error.msg for error in errors)) @@ -645,12 +307,10 @@ class StateApps(Apps): new_unrendered_models.append(model) if len(new_unrendered_models) == len(unrendered_models): raise InvalidBasesError( - "Cannot resolve bases for %r\nThis can happen if you are " - "inheriting models from an app with migrations (e.g. " - "contrib.auth)\n in an app with no migrations; see " - "https://docs.djangoproject.com/en/%s/topics/migrations/" - "#dependencies for more" - % (new_unrendered_models, get_docs_version()) + "Cannot resolve bases for %r\nThis can happen if you are inheriting models from an " + "app with migrations (e.g. contrib.auth)\n in an app with no migrations; see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#dependencies " + "for more" % (new_unrendered_models, get_docs_version()) ) unrendered_models = new_unrendered_models @@ -694,36 +354,34 @@ class ModelState: assign new ones, as these are not detached during a clone. """ - def __init__( - self, app_label, name, fields, options=None, bases=None, managers=None - ): + def __init__(self, app_label, name, fields, options=None, bases=None, managers=None): self.app_label = app_label self.name = name self.fields = dict(fields) self.options = options or {} - self.options.setdefault("indexes", []) - self.options.setdefault("constraints", []) + self.options.setdefault('indexes', []) + self.options.setdefault('constraints', []) self.bases = bases or (models.Model,) self.managers = managers or [] for name, field in self.fields.items(): # Sanity-check that fields are NOT already bound to a model. - if hasattr(field, "model"): + if hasattr(field, 'model'): raise ValueError( 'ModelState.fields cannot be bound to a model - "%s" is.' % name ) # Sanity-check that relation fields are NOT referring to a model class. - if field.is_relation and hasattr(field.related_model, "_meta"): + if field.is_relation and hasattr(field.related_model, '_meta'): raise ValueError( 'ModelState.fields cannot refer to a model class - "%s.to" does. ' - "Use a string reference instead." % name + 'Use a string reference instead.' % name ) - if field.many_to_many and hasattr(field.remote_field.through, "_meta"): + if field.many_to_many and hasattr(field.remote_field.through, '_meta'): raise ValueError( - 'ModelState.fields cannot refer to a model class - "%s.through" ' - "does. Use a string reference instead." % name + 'ModelState.fields cannot refer to a model class - "%s.through" does. ' + 'Use a string reference instead.' % name ) # Sanity-check that indexes have their name set. - for index in self.options["indexes"]: + for index in self.options['indexes']: if not index.name: raise ValueError( "Indexes passed to ModelState require a name attribute. " @@ -734,11 +392,6 @@ class ModelState: def name_lower(self): return self.name.lower() - def get_field(self, field_name): - if field_name == "_order": - field_name = self.options.get("order_with_respect_to", field_name) - return self.fields[field_name] - @classmethod def from_model(cls, model, exclude_rels=False): """Given a model, return a ModelState representing it.""" @@ -753,28 +406,22 @@ class ModelState: try: fields.append((name, field.clone())) except TypeError as e: - raise TypeError( - "Couldn't reconstruct field %s on %s: %s" - % ( - name, - model._meta.label, - e, - ) - ) + raise TypeError("Couldn't reconstruct field %s on %s: %s" % ( + name, + model._meta.label, + e, + )) if not exclude_rels: for field in model._meta.local_many_to_many: name = field.name try: fields.append((name, field.clone())) except TypeError as e: - raise TypeError( - "Couldn't reconstruct m2m field %s on %s: %s" - % ( - name, - model._meta.object_name, - e, - ) - ) + raise TypeError("Couldn't reconstruct m2m field %s on %s: %s" % ( + name, + model._meta.object_name, + e, + )) # Extract the options options = {} for name in DEFAULT_NAMES: @@ -793,11 +440,9 @@ class ModelState: for index in indexes: if not index.name: index.set_name_with_model(model) - options["indexes"] = indexes - elif name == "constraints": - options["constraints"] = [ - con.clone() for con in model._meta.constraints - ] + options['indexes'] = indexes + elif name == 'constraints': + options['constraints'] = [con.clone() for con in model._meta.constraints] else: options[name] = model._meta.original_attrs[name] # If we're ignoring relationships, remove all field-listing model @@ -807,10 +452,8 @@ class ModelState: if key in options: del options[key] # Private fields are ignored, so remove options that refer to them. - elif options.get("order_with_respect_to") in { - field.name for field in model._meta.private_fields - }: - del options["order_with_respect_to"] + elif options.get('order_with_respect_to') in {field.name for field in model._meta.private_fields}: + del options['order_with_respect_to'] def flatten_bases(model): bases = [] @@ -826,19 +469,19 @@ class ModelState: # __bases__ we may end up with duplicates and ordering issues, we # therefore discard any duplicates and reorder the bases according # to their index in the MRO. - flattened_bases = sorted( - set(flatten_bases(model)), key=lambda x: model.__mro__.index(x) - ) + flattened_bases = sorted(set(flatten_bases(model)), key=lambda x: model.__mro__.index(x)) # Make our record bases = tuple( - (base._meta.label_lower if hasattr(base, "_meta") else base) + ( + base._meta.label_lower + if hasattr(base, "_meta") else + base + ) for base in flattened_bases ) # Ensure at least one base inherits from models.Model - if not any( - (isinstance(base, str) or issubclass(base, models.Model)) for base in bases - ): + if not any((isinstance(base, str) or issubclass(base, models.Model)) for base in bases): bases = (models.Model,) managers = [] @@ -865,7 +508,7 @@ class ModelState: managers.append((manager.name, new_manager)) # Ignore a shimmed default manager called objects if it's the only one. - if managers == [("objects", default_manager_shim)]: + if managers == [('objects', default_manager_shim)]: managers = [] # Construct the new ModelState @@ -908,7 +551,7 @@ class ModelState: def render(self, apps): """Create a Model object from our current state into the given apps.""" # First, make a Meta object - meta_contents = {"app_label": self.app_label, "apps": apps, **self.options} + meta_contents = {'app_label': self.app_label, 'apps': apps, **self.options} meta = type("Meta", (), meta_contents) # Then, work out our bases try: @@ -917,13 +560,11 @@ class ModelState: for base in self.bases ) except LookupError: - raise InvalidBasesError( - "Cannot resolve one or more bases from %r" % (self.bases,) - ) + raise InvalidBasesError("Cannot resolve one or more bases from %r" % (self.bases,)) # Clone fields for the body, add other bits. body = {name: field.clone() for name, field in self.fields.items()} - body["Meta"] = meta - body["__module__"] = "__fake__" + body['Meta'] = meta + body['__module__'] = "__fake__" # Restore managers body.update(self.construct_managers()) @@ -931,33 +572,33 @@ class ModelState: return type(self.name, bases, body) def get_index_by_name(self, name): - for index in self.options["indexes"]: + for index in self.options['indexes']: if index.name == name: return index raise ValueError("No index named %s on model %s" % (name, self.name)) def get_constraint_by_name(self, name): - for constraint in self.options["constraints"]: + for constraint in self.options['constraints']: if constraint.name == name: return constraint - raise ValueError("No constraint named %s on model %s" % (name, self.name)) + raise ValueError('No constraint named %s on model %s' % (name, self.name)) def __repr__(self): return "<%s: '%s.%s'>" % (self.__class__.__name__, self.app_label, self.name) def __eq__(self, other): return ( - (self.app_label == other.app_label) - and (self.name == other.name) - and (len(self.fields) == len(other.fields)) - and all( + (self.app_label == other.app_label) and + (self.name == other.name) and + (len(self.fields) == len(other.fields)) and + all( k1 == k2 and f1.deconstruct()[1:] == f2.deconstruct()[1:] for (k1, f1), (k2, f2) in zip( sorted(self.fields.items()), sorted(other.fields.items()), ) - ) - and (self.options == other.options) - and (self.bases == other.bases) - and (self.managers == other.managers) + ) and + (self.options == other.options) and + (self.bases == other.bases) and + (self.managers == other.managers) ) diff --git a/venv/Lib/site-packages/django/db/migrations/utils.py b/venv/Lib/site-packages/django/db/migrations/utils.py index 2b45a60..8939794 100644 --- a/venv/Lib/site-packages/django/db/migrations/utils.py +++ b/venv/Lib/site-packages/django/db/migrations/utils.py @@ -1,12 +1,7 @@ import datetime import re -from collections import namedtuple -from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT - -FieldReference = namedtuple("FieldReference", "to through") - -COMPILED_REGEX_TYPE = type(re.compile("")) +COMPILED_REGEX_TYPE = type(re.compile('')) class RegexObject: @@ -20,108 +15,3 @@ class RegexObject: def get_migration_name_timestamp(): return datetime.datetime.now().strftime("%Y%m%d_%H%M") - - -def resolve_relation(model, app_label=None, model_name=None): - """ - Turn a model class or model reference string and return a model tuple. - - app_label and model_name are used to resolve the scope of recursive and - unscoped model relationship. - """ - if isinstance(model, str): - if model == RECURSIVE_RELATIONSHIP_CONSTANT: - if app_label is None or model_name is None: - raise TypeError( - "app_label and model_name must be provided to resolve " - "recursive relationships." - ) - return app_label, model_name - if "." in model: - app_label, model_name = model.split(".", 1) - return app_label, model_name.lower() - if app_label is None: - raise TypeError( - "app_label must be provided to resolve unscoped model relationships." - ) - return app_label, model.lower() - return model._meta.app_label, model._meta.model_name - - -def field_references( - model_tuple, - field, - reference_model_tuple, - reference_field_name=None, - reference_field=None, -): - """ - Return either False or a FieldReference if `field` references provided - context. - - False positives can be returned if `reference_field_name` is provided - without `reference_field` because of the introspection limitation it - incurs. This should not be an issue when this function is used to determine - whether or not an optimization can take place. - """ - remote_field = field.remote_field - if not remote_field: - return False - references_to = None - references_through = None - if resolve_relation(remote_field.model, *model_tuple) == reference_model_tuple: - to_fields = getattr(field, "to_fields", None) - if ( - reference_field_name is None - or - # Unspecified to_field(s). - to_fields is None - or - # Reference to primary key. - ( - None in to_fields - and (reference_field is None or reference_field.primary_key) - ) - or - # Reference to field. - reference_field_name in to_fields - ): - references_to = (remote_field, to_fields) - through = getattr(remote_field, "through", None) - if through and resolve_relation(through, *model_tuple) == reference_model_tuple: - through_fields = remote_field.through_fields - if ( - reference_field_name is None - or - # Unspecified through_fields. - through_fields is None - or - # Reference to field. - reference_field_name in through_fields - ): - references_through = (remote_field, through_fields) - if not (references_to or references_through): - return False - return FieldReference(references_to, references_through) - - -def get_references(state, model_tuple, field_tuple=()): - """ - Generator of (model_state, name, field, reference) referencing - provided context. - - If field_tuple is provided only references to this particular field of - model_tuple will be generated. - """ - for state_model_tuple, model_state in state.models.items(): - for name, field in model_state.fields.items(): - reference = field_references( - state_model_tuple, field, model_tuple, *field_tuple - ) - if reference: - yield model_state, name, field, reference - - -def field_is_referenced(state, model_tuple, field_tuple): - """Return whether `field_tuple` is referenced by any state models.""" - return next(get_references(state, model_tuple, field_tuple), None) is not None diff --git a/venv/Lib/site-packages/django/db/migrations/writer.py b/venv/Lib/site-packages/django/db/migrations/writer.py index a59f0c8..4918261 100644 --- a/venv/Lib/site-packages/django/db/migrations/writer.py +++ b/venv/Lib/site-packages/django/db/migrations/writer.py @@ -1,10 +1,10 @@ + import os import re from importlib import import_module from django import get_version from django.apps import apps - # SettingsReference imported for backwards compatibility in Django 2.2. from django.conf import SettingsReference # NOQA from django.db import migrations @@ -22,30 +22,30 @@ class OperationWriter: self.indentation = indentation def serialize(self): + def _write(_arg_name, _arg_value): - if _arg_name in self.operation.serialization_expand_args and isinstance( - _arg_value, (list, tuple, dict) - ): + if (_arg_name in self.operation.serialization_expand_args and + isinstance(_arg_value, (list, tuple, dict))): if isinstance(_arg_value, dict): - self.feed("%s={" % _arg_name) + self.feed('%s={' % _arg_name) self.indent() for key, value in _arg_value.items(): key_string, key_imports = MigrationWriter.serialize(key) arg_string, arg_imports = MigrationWriter.serialize(value) args = arg_string.splitlines() if len(args) > 1: - self.feed("%s: %s" % (key_string, args[0])) + self.feed('%s: %s' % (key_string, args[0])) for arg in args[1:-1]: self.feed(arg) - self.feed("%s," % args[-1]) + self.feed('%s,' % args[-1]) else: - self.feed("%s: %s," % (key_string, arg_string)) + self.feed('%s: %s,' % (key_string, arg_string)) imports.update(key_imports) imports.update(arg_imports) self.unindent() - self.feed("},") + self.feed('},') else: - self.feed("%s=[" % _arg_name) + self.feed('%s=[' % _arg_name) self.indent() for item in _arg_value: arg_string, arg_imports = MigrationWriter.serialize(item) @@ -53,22 +53,22 @@ class OperationWriter: if len(args) > 1: for arg in args[:-1]: self.feed(arg) - self.feed("%s," % args[-1]) + self.feed('%s,' % args[-1]) else: - self.feed("%s," % arg_string) + self.feed('%s,' % arg_string) imports.update(arg_imports) self.unindent() - self.feed("],") + self.feed('],') else: arg_string, arg_imports = MigrationWriter.serialize(_arg_value) args = arg_string.splitlines() if len(args) > 1: - self.feed("%s=%s" % (_arg_name, args[0])) + self.feed('%s=%s' % (_arg_name, args[0])) for arg in args[1:-1]: self.feed(arg) - self.feed("%s," % args[-1]) + self.feed('%s,' % args[-1]) else: - self.feed("%s=%s," % (_arg_name, arg_string)) + self.feed('%s=%s,' % (_arg_name, arg_string)) imports.update(arg_imports) imports = set() @@ -79,10 +79,10 @@ class OperationWriter: # We can just use the fact we already have that imported, # otherwise, we need to add an import for the operation class. if getattr(migrations, name, None) == self.operation.__class__: - self.feed("migrations.%s(" % name) + self.feed('migrations.%s(' % name) else: - imports.add("import %s" % (self.operation.__class__.__module__)) - self.feed("%s.%s(" % (self.operation.__class__.__module__, name)) + imports.add('import %s' % (self.operation.__class__.__module__)) + self.feed('%s.%s(' % (self.operation.__class__.__module__, name)) self.indent() @@ -99,7 +99,7 @@ class OperationWriter: _write(arg_name, arg_value) self.unindent() - self.feed("),") + self.feed('),') return self.render(), imports def indent(self): @@ -109,10 +109,10 @@ class OperationWriter: self.indentation -= 1 def feed(self, line): - self.buff.append(" " * (self.indentation * 4) + line) + self.buff.append(' ' * (self.indentation * 4) + line) def render(self): - return "\n".join(self.buff) + return '\n'.join(self.buff) class MigrationWriter: @@ -147,10 +147,7 @@ class MigrationWriter: dependencies = [] for dependency in self.migration.dependencies: if dependency[0] == "__setting__": - dependencies.append( - " migrations.swappable_dependency(settings.%s)," - % dependency[1] - ) + dependencies.append(" migrations.swappable_dependency(settings.%s)," % dependency[1]) imports.add("from django.conf import settings") else: dependencies.append(" %s," % self.serialize(dependency)[0]) @@ -186,28 +183,24 @@ class MigrationWriter: ) % "\n# ".join(sorted(migration_imports)) # If there's a replaces, make a string for it if self.migration.replaces: - items["replaces_str"] = ( - "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0] - ) + items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0] # Hinting that goes into comment if self.include_header: - items["migration_header"] = MIGRATION_HEADER_TEMPLATE % { - "version": get_version(), - "timestamp": now().strftime("%Y-%m-%d %H:%M"), + items['migration_header'] = MIGRATION_HEADER_TEMPLATE % { + 'version': get_version(), + 'timestamp': now().strftime("%Y-%m-%d %H:%M"), } else: - items["migration_header"] = "" + items['migration_header'] = "" if self.migration.initial: - items["initial_str"] = "\n initial = True\n" + items['initial_str'] = "\n initial = True\n" return MIGRATION_TEMPLATE % items @property def basedir(self): - migrations_package_name, _ = MigrationLoader.migrations_module( - self.migration.app_label - ) + migrations_package_name, _ = MigrationLoader.migrations_module(self.migration.app_label) if migrations_package_name is None: raise ValueError( @@ -229,11 +222,7 @@ class MigrationWriter: # Alright, see if it's a direct submodule of the app app_config = apps.get_app_config(self.migration.app_label) - ( - maybe_app_name, - _, - migrations_package_basename, - ) = migrations_package_name.rpartition(".") + maybe_app_name, _, migrations_package_basename = migrations_package_name.rpartition(".") if app_config.name == maybe_app_name: return os.path.join(app_config.path, migrations_package_basename) @@ -257,8 +246,8 @@ class MigrationWriter: raise ValueError( "Could not locate an appropriate location to create " "migrations package %s. Make sure the toplevel " - "package exists and can be imported." % migrations_package_name - ) + "package exists and can be imported." % + migrations_package_name) final_dir = os.path.join(base_dir, *missing_dirs) os.makedirs(final_dir, exist_ok=True) diff --git a/venv/Lib/site-packages/django/db/models/__init__.py b/venv/Lib/site-packages/django/db/models/__init__.py index ffca81d..a583af2 100644 --- a/venv/Lib/site-packages/django/db/models/__init__.py +++ b/venv/Lib/site-packages/django/db/models/__init__.py @@ -5,34 +5,14 @@ from django.db.models.aggregates import __all__ as aggregates_all from django.db.models.constraints import * # NOQA from django.db.models.constraints import __all__ as constraints_all from django.db.models.deletion import ( - CASCADE, - DO_NOTHING, - PROTECT, - RESTRICT, - SET, - SET_DEFAULT, - SET_NULL, - ProtectedError, - RestrictedError, + CASCADE, DO_NOTHING, PROTECT, RESTRICT, SET, SET_DEFAULT, SET_NULL, + ProtectedError, RestrictedError, ) from django.db.models.enums import * # NOQA from django.db.models.enums import __all__ as enums_all from django.db.models.expressions import ( - Case, - Exists, - Expression, - ExpressionList, - ExpressionWrapper, - F, - Func, - OrderBy, - OuterRef, - RowRange, - Subquery, - Value, - ValueRange, - When, - Window, + Case, Exists, Expression, ExpressionList, ExpressionWrapper, F, Func, + OrderBy, OuterRef, RowRange, Subquery, Value, ValueRange, When, Window, WindowFrame, ) from django.db.models.fields import * # NOQA @@ -50,66 +30,23 @@ from django.db.models.query_utils import FilteredRelation, Q # Imports that would create circular imports if sorted from django.db.models.base import DEFERRED, Model # isort:skip from django.db.models.fields.related import ( # isort:skip - ForeignKey, - ForeignObject, - OneToOneField, - ManyToManyField, - ForeignObjectRel, - ManyToOneRel, - ManyToManyRel, - OneToOneRel, + ForeignKey, ForeignObject, OneToOneField, ManyToManyField, + ForeignObjectRel, ManyToOneRel, ManyToManyRel, OneToOneRel, ) __all__ = aggregates_all + constraints_all + enums_all + fields_all + indexes_all __all__ += [ - "ObjectDoesNotExist", - "signals", - "CASCADE", - "DO_NOTHING", - "PROTECT", - "RESTRICT", - "SET", - "SET_DEFAULT", - "SET_NULL", - "ProtectedError", - "RestrictedError", - "Case", - "Exists", - "Expression", - "ExpressionList", - "ExpressionWrapper", - "F", - "Func", - "OrderBy", - "OuterRef", - "RowRange", - "Subquery", - "Value", - "ValueRange", - "When", - "Window", - "WindowFrame", - "FileField", - "ImageField", - "JSONField", - "OrderWrt", - "Lookup", - "Transform", - "Manager", - "Prefetch", - "Q", - "QuerySet", - "prefetch_related_objects", - "DEFERRED", - "Model", - "FilteredRelation", - "ForeignKey", - "ForeignObject", - "OneToOneField", - "ManyToManyField", - "ForeignObjectRel", - "ManyToOneRel", - "ManyToManyRel", - "OneToOneRel", + 'ObjectDoesNotExist', 'signals', + 'CASCADE', 'DO_NOTHING', 'PROTECT', 'RESTRICT', 'SET', 'SET_DEFAULT', + 'SET_NULL', 'ProtectedError', 'RestrictedError', + 'Case', 'Exists', 'Expression', 'ExpressionList', 'ExpressionWrapper', 'F', + 'Func', 'OrderBy', 'OuterRef', 'RowRange', 'Subquery', 'Value', + 'ValueRange', 'When', + 'Window', 'WindowFrame', + 'FileField', 'ImageField', 'JSONField', 'OrderWrt', 'Lookup', 'Transform', + 'Manager', 'Prefetch', 'Q', 'QuerySet', 'prefetch_related_objects', + 'DEFERRED', 'Model', 'FilteredRelation', + 'ForeignKey', 'ForeignObject', 'OneToOneField', 'ManyToManyField', + 'ForeignObjectRel', 'ManyToOneRel', 'ManyToManyRel', 'OneToOneRel', ] diff --git a/venv/Lib/site-packages/django/db/models/aggregates.py b/venv/Lib/site-packages/django/db/models/aggregates.py index 4517d0a..8b10829 100644 --- a/venv/Lib/site-packages/django/db/models/aggregates.py +++ b/venv/Lib/site-packages/django/db/models/aggregates.py @@ -4,43 +4,28 @@ Classes to represent the definitions of aggregate functions. from django.core.exceptions import FieldError from django.db.models.expressions import Case, Func, Star, When from django.db.models.fields import IntegerField -from django.db.models.functions.comparison import Coalesce from django.db.models.functions.mixins import ( - FixDurationInputMixin, - NumericOutputFieldMixin, + FixDurationInputMixin, NumericOutputFieldMixin, ) __all__ = [ - "Aggregate", - "Avg", - "Count", - "Max", - "Min", - "StdDev", - "Sum", - "Variance", + 'Aggregate', 'Avg', 'Count', 'Max', 'Min', 'StdDev', 'Sum', 'Variance', ] class Aggregate(Func): - template = "%(function)s(%(distinct)s%(expressions)s)" + template = '%(function)s(%(distinct)s%(expressions)s)' contains_aggregate = True name = None - filter_template = "%s FILTER (WHERE %%(filter)s)" + filter_template = '%s FILTER (WHERE %%(filter)s)' window_compatible = True allow_distinct = False - empty_result_set_value = None - def __init__( - self, *expressions, distinct=False, filter=None, default=None, **extra - ): + def __init__(self, *expressions, distinct=False, filter=None, **extra): if distinct and not self.allow_distinct: raise TypeError("%s does not allow distinct." % self.__class__.__name__) - if default is not None and self.empty_result_set_value is not None: - raise TypeError(f"{self.__class__.__name__} does not allow default.") self.distinct = distinct self.filter = filter - self.default = default super().__init__(*expressions, **extra) def get_source_fields(self): @@ -57,14 +42,10 @@ class Aggregate(Func): self.filter = self.filter and exprs.pop() return super().set_source_expressions(exprs) - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): # Aggregates are not allowed in UPDATE queries, so ignore for_save c = super().resolve_expression(query, allow_joins, reuse, summarize) - c.filter = c.filter and c.filter.resolve_expression( - query, allow_joins, reuse, summarize - ) + c.filter = c.filter and c.filter.resolve_expression(query, allow_joins, reuse, summarize) if not summarize: # Call Aggregate.get_source_expressions() to avoid # returning self.filter and including that in this loop. @@ -72,48 +53,29 @@ class Aggregate(Func): for index, expr in enumerate(expressions): if expr.contains_aggregate: before_resolved = self.get_source_expressions()[index] - name = ( - before_resolved.name - if hasattr(before_resolved, "name") - else repr(before_resolved) - ) - raise FieldError( - "Cannot compute %s('%s'): '%s' is an aggregate" - % (c.name, name, name) - ) - if (default := c.default) is None: - return c - if hasattr(default, "resolve_expression"): - default = default.resolve_expression(query, allow_joins, reuse, summarize) - c.default = None # Reset the default argument before wrapping. - coalesce = Coalesce(c, default, output_field=c._output_field_or_none) - coalesce.is_summary = c.is_summary - return coalesce + name = before_resolved.name if hasattr(before_resolved, 'name') else repr(before_resolved) + raise FieldError("Cannot compute %s('%s'): '%s' is an aggregate" % (c.name, name, name)) + return c @property def default_alias(self): expressions = self.get_source_expressions() - if len(expressions) == 1 and hasattr(expressions[0], "name"): - return "%s__%s" % (expressions[0].name, self.name.lower()) + if len(expressions) == 1 and hasattr(expressions[0], 'name'): + return '%s__%s' % (expressions[0].name, self.name.lower()) raise TypeError("Complex expressions require an alias") def get_group_by_cols(self, alias=None): return [] def as_sql(self, compiler, connection, **extra_context): - extra_context["distinct"] = "DISTINCT " if self.distinct else "" + extra_context['distinct'] = 'DISTINCT ' if self.distinct else '' if self.filter: if connection.features.supports_aggregate_filter_clause: filter_sql, filter_params = self.filter.as_sql(compiler, connection) - template = self.filter_template % extra_context.get( - "template", self.template - ) + template = self.filter_template % extra_context.get('template', self.template) sql, params = super().as_sql( - compiler, - connection, - template=template, - filter=filter_sql, - **extra_context, + compiler, connection, template=template, filter=filter_sql, + **extra_context ) return sql, params + filter_params else: @@ -122,74 +84,74 @@ class Aggregate(Func): source_expressions = copy.get_source_expressions() condition = When(self.filter, then=source_expressions[0]) copy.set_source_expressions([Case(condition)] + source_expressions[1:]) - return super(Aggregate, copy).as_sql( - compiler, connection, **extra_context - ) + return super(Aggregate, copy).as_sql(compiler, connection, **extra_context) return super().as_sql(compiler, connection, **extra_context) def _get_repr_options(self): options = super()._get_repr_options() if self.distinct: - options["distinct"] = self.distinct + options['distinct'] = self.distinct if self.filter: - options["filter"] = self.filter + options['filter'] = self.filter return options class Avg(FixDurationInputMixin, NumericOutputFieldMixin, Aggregate): - function = "AVG" - name = "Avg" + function = 'AVG' + name = 'Avg' allow_distinct = True class Count(Aggregate): - function = "COUNT" - name = "Count" + function = 'COUNT' + name = 'Count' output_field = IntegerField() allow_distinct = True - empty_result_set_value = 0 def __init__(self, expression, filter=None, **extra): - if expression == "*": + if expression == '*': expression = Star() if isinstance(expression, Star) and filter is not None: - raise ValueError("Star cannot be used with filter. Please specify a field.") + raise ValueError('Star cannot be used with filter. Please specify a field.') super().__init__(expression, filter=filter, **extra) + def convert_value(self, value, expression, connection): + return 0 if value is None else value + class Max(Aggregate): - function = "MAX" - name = "Max" + function = 'MAX' + name = 'Max' class Min(Aggregate): - function = "MIN" - name = "Min" + function = 'MIN' + name = 'Min' class StdDev(NumericOutputFieldMixin, Aggregate): - name = "StdDev" + name = 'StdDev' def __init__(self, expression, sample=False, **extra): - self.function = "STDDEV_SAMP" if sample else "STDDEV_POP" + self.function = 'STDDEV_SAMP' if sample else 'STDDEV_POP' super().__init__(expression, **extra) def _get_repr_options(self): - return {**super()._get_repr_options(), "sample": self.function == "STDDEV_SAMP"} + return {**super()._get_repr_options(), 'sample': self.function == 'STDDEV_SAMP'} class Sum(FixDurationInputMixin, Aggregate): - function = "SUM" - name = "Sum" + function = 'SUM' + name = 'Sum' allow_distinct = True class Variance(NumericOutputFieldMixin, Aggregate): - name = "Variance" + name = 'Variance' def __init__(self, expression, sample=False, **extra): - self.function = "VAR_SAMP" if sample else "VAR_POP" + self.function = 'VAR_SAMP' if sample else 'VAR_POP' super().__init__(expression, **extra) def _get_repr_options(self): - return {**super()._get_repr_options(), "sample": self.function == "VAR_SAMP"} + return {**super()._get_repr_options(), 'sample': self.function == 'VAR_SAMP'} diff --git a/venv/Lib/site-packages/django/db/models/base.py b/venv/Lib/site-packages/django/db/models/base.py index 20d16c1..f49913e 100644 --- a/venv/Lib/site-packages/django/db/models/base.py +++ b/venv/Lib/site-packages/django/db/models/base.py @@ -9,42 +9,28 @@ from django.apps import apps from django.conf import settings from django.core import checks from django.core.exceptions import ( - NON_FIELD_ERRORS, - FieldDoesNotExist, - FieldError, - MultipleObjectsReturned, - ObjectDoesNotExist, - ValidationError, + NON_FIELD_ERRORS, FieldDoesNotExist, FieldError, MultipleObjectsReturned, + ObjectDoesNotExist, ValidationError, ) from django.db import ( - DEFAULT_DB_ALIAS, - DJANGO_VERSION_PICKLE_KEY, - DatabaseError, - connection, - connections, - router, - transaction, + DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection, + connections, router, transaction, +) +from django.db.models import ( + NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value, ) -from django.db.models import NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value from django.db.models.constants import LOOKUP_SEP from django.db.models.constraints import CheckConstraint, UniqueConstraint from django.db.models.deletion import CASCADE, Collector from django.db.models.fields.related import ( - ForeignObjectRel, - OneToOneField, - lazy_related_operation, - resolve_relation, + ForeignObjectRel, OneToOneField, lazy_related_operation, resolve_relation, ) from django.db.models.functions import Coalesce from django.db.models.manager import Manager from django.db.models.options import Options from django.db.models.query import F, Q from django.db.models.signals import ( - class_prepared, - post_init, - post_save, - pre_init, - pre_save, + class_prepared, post_init, post_save, pre_init, pre_save, ) from django.db.models.utils import make_model_tuple from django.utils.encoding import force_str @@ -55,10 +41,10 @@ from django.utils.translation import gettext_lazy as _ class Deferred: def __repr__(self): - return "<Deferred field>" + return '<Deferred field>' def __str__(self): - return "<Deferred field>" + return '<Deferred field>' DEFERRED = Deferred() @@ -72,24 +58,19 @@ def subclass_exception(name, bases, module, attached_to): that the returned exception class will be added as an attribute to the 'attached_to' class. """ - return type( - name, - bases, - { - "__module__": module, - "__qualname__": "%s.%s" % (attached_to.__qualname__, name), - }, - ) + return type(name, bases, { + '__module__': module, + '__qualname__': '%s.%s' % (attached_to.__qualname__, name), + }) def _has_contribute_to_class(value): # Only call contribute_to_class() if it's bound. - return not inspect.isclass(value) and hasattr(value, "contribute_to_class") + return not inspect.isclass(value) and hasattr(value, 'contribute_to_class') class ModelBase(type): """Metaclass for all models.""" - def __new__(cls, name, bases, attrs, **kwargs): super_new = super().__new__ @@ -100,12 +81,12 @@ class ModelBase(type): return super_new(cls, name, bases, attrs) # Create the class. - module = attrs.pop("__module__") - new_attrs = {"__module__": module} - classcell = attrs.pop("__classcell__", None) + module = attrs.pop('__module__') + new_attrs = {'__module__': module} + classcell = attrs.pop('__classcell__', None) if classcell is not None: - new_attrs["__classcell__"] = classcell - attr_meta = attrs.pop("Meta", None) + new_attrs['__classcell__'] = classcell + attr_meta = attrs.pop('Meta', None) # Pass all attrs without a (Django-specific) contribute_to_class() # method to type.__new__() so that they're properly initialized # (i.e. __set_name__()). @@ -117,16 +98,16 @@ class ModelBase(type): new_attrs[obj_name] = obj new_class = super_new(cls, name, bases, new_attrs, **kwargs) - abstract = getattr(attr_meta, "abstract", False) - meta = attr_meta or getattr(new_class, "Meta", None) - base_meta = getattr(new_class, "_meta", None) + abstract = getattr(attr_meta, 'abstract', False) + meta = attr_meta or getattr(new_class, 'Meta', None) + base_meta = getattr(new_class, '_meta', None) app_label = None # Look for an application configuration to attach the model to. app_config = apps.get_containing_app_config(module) - if getattr(meta, "app_label", None) is None: + if getattr(meta, 'app_label', None) is None: if app_config is None: if not abstract: raise RuntimeError( @@ -138,43 +119,33 @@ class ModelBase(type): else: app_label = app_config.label - new_class.add_to_class("_meta", Options(meta, app_label)) + new_class.add_to_class('_meta', Options(meta, app_label)) if not abstract: new_class.add_to_class( - "DoesNotExist", + 'DoesNotExist', subclass_exception( - "DoesNotExist", + 'DoesNotExist', tuple( - x.DoesNotExist - for x in parents - if hasattr(x, "_meta") and not x._meta.abstract - ) - or (ObjectDoesNotExist,), + x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract + ) or (ObjectDoesNotExist,), module, - attached_to=new_class, - ), - ) + attached_to=new_class)) new_class.add_to_class( - "MultipleObjectsReturned", + 'MultipleObjectsReturned', subclass_exception( - "MultipleObjectsReturned", + 'MultipleObjectsReturned', tuple( - x.MultipleObjectsReturned - for x in parents - if hasattr(x, "_meta") and not x._meta.abstract - ) - or (MultipleObjectsReturned,), + x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract + ) or (MultipleObjectsReturned,), module, - attached_to=new_class, - ), - ) + attached_to=new_class)) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their # non-abstract parent (unless an ABC comes before it in the # method resolution order). - if not hasattr(meta, "ordering"): + if not hasattr(meta, 'ordering'): new_class._meta.ordering = base_meta.ordering - if not hasattr(meta, "get_latest_by"): + if not hasattr(meta, 'get_latest_by'): new_class._meta.get_latest_by = base_meta.get_latest_by is_proxy = new_class._meta.proxy @@ -182,9 +153,7 @@ class ModelBase(type): # If the model is a proxy, ensure that the base class # hasn't been swapped out. if is_proxy and base_meta and base_meta.swapped: - raise TypeError( - "%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped) - ) + raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped)) # Add remaining attributes (those with a contribute_to_class() method) # to the class. @@ -195,14 +164,14 @@ class ModelBase(type): new_fields = chain( new_class._meta.local_fields, new_class._meta.local_many_to_many, - new_class._meta.private_fields, + new_class._meta.private_fields ) field_names = {f.name for f in new_fields} # Basic setup for proxy models. if is_proxy: base = None - for parent in [kls for kls in parents if hasattr(kls, "_meta")]: + for parent in [kls for kls in parents if hasattr(kls, '_meta')]: if parent._meta.abstract: if parent._meta.fields: raise TypeError( @@ -214,14 +183,9 @@ class ModelBase(type): if base is None: base = parent elif parent._meta.concrete_model is not base._meta.concrete_model: - raise TypeError( - "Proxy model '%s' has more than one non-abstract model base " - "class." % name - ) + raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name) if base is None: - raise TypeError( - "Proxy model '%s' has no non-abstract model base class." % name - ) + raise TypeError("Proxy model '%s' has no non-abstract model base class." % name) new_class._meta.setup_proxy(base) new_class._meta.concrete_model = base._meta.concrete_model else: @@ -231,7 +195,7 @@ class ModelBase(type): parent_links = {} for base in reversed([new_class] + parents): # Conceptually equivalent to `if base is Model`. - if not hasattr(base, "_meta"): + if not hasattr(base, '_meta'): continue # Skip concrete parent classes. if base != new_class and not base._meta.abstract: @@ -246,7 +210,7 @@ class ModelBase(type): inherited_attributes = set() # Do the appropriate setup for any model parents. for base in new_class.mro(): - if base not in parents or not hasattr(base, "_meta"): + if base not in parents or not hasattr(base, '_meta'): # Things without _meta aren't functional models, so they're # uninteresting parents. inherited_attributes.update(base.__dict__) @@ -259,9 +223,8 @@ class ModelBase(type): for field in parent_fields: if field.name in field_names: raise FieldError( - "Local field %r in class %r clashes with field of " - "the same name from base class %r." - % ( + 'Local field %r in class %r clashes with field of ' + 'the same name from base class %r.' % ( field.name, name, base.__name__, @@ -276,7 +239,7 @@ class ModelBase(type): if base_key in parent_links: field = parent_links[base_key] elif not is_proxy: - attr_name = "%s_ptr" % base._meta.model_name + attr_name = '%s_ptr' % base._meta.model_name field = OneToOneField( base, on_delete=CASCADE, @@ -289,8 +252,7 @@ class ModelBase(type): raise FieldError( "Auto-generated field '%s' in class %r for " "parent_link to base class %r clashes with " - "declared field of the same name." - % ( + "declared field of the same name." % ( attr_name, name, base.__name__, @@ -309,11 +271,9 @@ class ModelBase(type): # Add fields from abstract base class if it wasn't overridden. for field in parent_fields: - if ( - field.name not in field_names - and field.name not in new_class.__dict__ - and field.name not in inherited_attributes - ): + if (field.name not in field_names and + field.name not in new_class.__dict__ and + field.name not in inherited_attributes): new_field = copy.deepcopy(field) new_class.add_to_class(field.name, new_field) # Replace parent links defined on this base by the new @@ -332,9 +292,8 @@ class ModelBase(type): if field.name in field_names: if not base._meta.abstract: raise FieldError( - "Local field %r in class %r clashes with field of " - "the same name from base class %r." - % ( + 'Local field %r in class %r clashes with field of ' + 'the same name from base class %r.' % ( field.name, name, base.__name__, @@ -348,9 +307,7 @@ class ModelBase(type): # Copy indexes so that index names are unique when models extend an # abstract model. - new_class._meta.indexes = [ - copy.deepcopy(idx) for idx in new_class._meta.indexes - ] + new_class._meta.indexes = [copy.deepcopy(idx) for idx in new_class._meta.indexes] if abstract: # Abstract base models can't be instantiated and don't appear in @@ -376,12 +333,8 @@ class ModelBase(type): opts._prepare(cls) if opts.order_with_respect_to: - cls.get_next_in_order = partialmethod( - cls._get_next_or_previous_in_order, is_next=True - ) - cls.get_previous_in_order = partialmethod( - cls._get_next_or_previous_in_order, is_next=False - ) + cls.get_next_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=True) + cls.get_previous_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=False) # Defer creating accessors on the foreign class until it has been # created and registered. If remote_field is None, we're ordering @@ -395,26 +348,21 @@ class ModelBase(type): # Give the class a docstring -- its definition. if cls.__doc__ is None: - cls.__doc__ = "%s(%s)" % ( - cls.__name__, - ", ".join(f.name for f in opts.fields), - ) + cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join(f.name for f in opts.fields)) - get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get( - opts.label_lower - ) + get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(opts.label_lower) if get_absolute_url_override: - setattr(cls, "get_absolute_url", get_absolute_url_override) + setattr(cls, 'get_absolute_url', get_absolute_url_override) if not opts.managers: - if any(f.name == "objects" for f in opts.fields): + if any(f.name == 'objects' for f in opts.fields): raise ValueError( "Model %s must specify a custom Manager, because it has a " "field named 'objects'." % cls.__name__ ) manager = Manager() manager.auto_created = True - cls.add_to_class("objects", manager) + cls.add_to_class('objects', manager) # Set the name of _meta.indexes. This can't be done in # Options.contribute_to_class() because fields haven't been added to @@ -444,7 +392,6 @@ class ModelStateFieldsCacheDescriptor: class ModelState: """Store model instance state.""" - db = None # If true, uniqueness validation checks will consider this a new, unsaved # object. Necessary for correct validation of new instances of objects with @@ -455,6 +402,7 @@ class ModelState: class Model(metaclass=ModelBase): + def __init__(self, *args, **kwargs): # Alias some things as locals to avoid repeat global lookups cls = self.__class__ @@ -462,7 +410,7 @@ class Model(metaclass=ModelBase): _setattr = setattr _DEFERRED = DEFERRED if opts.abstract: - raise TypeError("Abstract models cannot be instantiated.") + raise TypeError('Abstract models cannot be instantiated.') pre_init.send(sender=cls, args=args, kwargs=kwargs) @@ -494,11 +442,7 @@ class Model(metaclass=ModelBase): if val is _DEFERRED: continue _setattr(self, field.attname, val) - if kwargs.pop(field.name, NOT_PROVIDED) is not NOT_PROVIDED: - raise TypeError( - f"{cls.__qualname__}() got both positional and " - f"keyword arguments for field '{field.name}'." - ) + kwargs.pop(field.name, None) # Now we're left with the unprocessed fields that *must* come from # keywords, or default. @@ -556,10 +500,7 @@ class Model(metaclass=ModelBase): except (AttributeError, FieldDoesNotExist): pass for kwarg in kwargs: - raise TypeError( - "%s() got an unexpected keyword argument '%s'" - % (cls.__name__, kwarg) - ) + raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg)) super().__init__() post_init.send(sender=cls, instance=self) @@ -577,10 +518,10 @@ class Model(metaclass=ModelBase): return new def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self) + return '<%s: %s>' % (self.__class__.__name__, self) def __str__(self): - return "%s object (%s)" % (self.__class__.__name__, self.pk) + return '%s object (%s)' % (self.__class__.__name__, self.pk) def __eq__(self, other): if not isinstance(other, Model): @@ -606,18 +547,8 @@ class Model(metaclass=ModelBase): def __getstate__(self): """Hook to allow choosing the attributes to pickle.""" state = self.__dict__.copy() - state["_state"] = copy.copy(state["_state"]) - state["_state"].fields_cache = state["_state"].fields_cache.copy() - # memoryview cannot be pickled, so cast it to bytes and store - # separately. - _memoryview_attrs = [] - for attr, value in state.items(): - if isinstance(value, memoryview): - _memoryview_attrs.append((attr, bytes(value))) - if _memoryview_attrs: - state["_memoryview_attrs"] = _memoryview_attrs - for attr, value in _memoryview_attrs: - state.pop(attr) + state['_state'] = copy.copy(state['_state']) + state['_state'].fields_cache = state['_state'].fields_cache.copy() return state def __setstate__(self, state): @@ -637,9 +568,6 @@ class Model(metaclass=ModelBase): RuntimeWarning, stacklevel=2, ) - if "_memoryview_attrs" in state: - for attr, value in state.pop("_memoryview_attrs"): - state[attr] = memoryview(value) self.__dict__.update(state) def _get_pk_val(self, meta=None): @@ -659,8 +587,7 @@ class Model(metaclass=ModelBase): Return a set containing names of deferred fields for this instance. """ return { - f.attname - for f in self._meta.concrete_fields + f.attname for f in self._meta.concrete_fields if f.attname not in self.__dict__ } @@ -682,7 +609,7 @@ class Model(metaclass=ModelBase): if fields is None: self._prefetched_objects_cache = {} else: - prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ()) + prefetched_objects_cache = getattr(self, '_prefetched_objects_cache', ()) for field in fields: if field in prefetched_objects_cache: del prefetched_objects_cache[field] @@ -692,13 +619,10 @@ class Model(metaclass=ModelBase): if any(LOOKUP_SEP in f for f in fields): raise ValueError( 'Found "%s" in fields argument. Relations and transforms ' - "are not allowed in fields." % LOOKUP_SEP - ) + 'are not allowed in fields.' % LOOKUP_SEP) - hints = {"instance": self} - db_instance_qs = self.__class__._base_manager.db_manager( - using, hints=hints - ).filter(pk=self.pk) + hints = {'instance': self} + db_instance_qs = self.__class__._base_manager.db_manager(using, hints=hints).filter(pk=self.pk) # Use provided fields, if not set then reload all non-deferred fields. deferred_fields = self.get_deferred_fields() @@ -706,11 +630,8 @@ class Model(metaclass=ModelBase): fields = list(fields) db_instance_qs = db_instance_qs.only(*fields) elif deferred_fields: - fields = [ - f.attname - for f in self._meta.concrete_fields - if f.attname not in deferred_fields - ] + fields = [f.attname for f in self._meta.concrete_fields + if f.attname not in deferred_fields] db_instance_qs = db_instance_qs.only(*fields) db_instance = db_instance_qs.get() @@ -748,9 +669,8 @@ class Model(metaclass=ModelBase): return getattr(self, field_name) return getattr(self, field.attname) - def save( - self, force_insert=False, force_update=False, using=None, update_fields=None - ): + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): """ Save the current instance. Override this in a subclass if you want to control the saving process. @@ -759,7 +679,7 @@ class Model(metaclass=ModelBase): that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ - self._prepare_related_fields_for_save(operation_name="save") + self._prepare_related_fields_for_save(operation_name='save') using = using or router.db_for_write(self.__class__, instance=self) if force_insert and (force_update or update_fields): @@ -787,9 +707,9 @@ class Model(metaclass=ModelBase): if non_model_fields: raise ValueError( - "The following fields do not exist in this model, are m2m " - "fields, or are non-concrete fields: %s" - % ", ".join(non_model_fields) + 'The following fields do not exist in this model, are m2m ' + 'fields, or are non-concrete fields: %s' + % ', '.join(non_model_fields) ) # If saving to the same database, and this model is deferred, then @@ -797,29 +717,18 @@ class Model(metaclass=ModelBase): elif not force_insert and deferred_fields and using == self._state.db: field_names = set() for field in self._meta.concrete_fields: - if not field.primary_key and not hasattr(field, "through"): + if not field.primary_key and not hasattr(field, 'through'): field_names.add(field.attname) loaded_fields = field_names.difference(deferred_fields) if loaded_fields: update_fields = frozenset(loaded_fields) - self.save_base( - using=using, - force_insert=force_insert, - force_update=force_update, - update_fields=update_fields, - ) - + self.save_base(using=using, force_insert=force_insert, + force_update=force_update, update_fields=update_fields) save.alters_data = True - def save_base( - self, - raw=False, - force_insert=False, - force_update=False, - using=None, - update_fields=None, - ): + def save_base(self, raw=False, force_insert=False, + force_update=False, using=None, update_fields=None): """ Handle the parts of saving which should be done only once per save, yet need to be done in raw saves, too. This includes some sanity @@ -839,10 +748,7 @@ class Model(metaclass=ModelBase): meta = cls._meta if not meta.auto_created: pre_save.send( - sender=origin, - instance=self, - raw=raw, - using=using, + sender=origin, instance=self, raw=raw, using=using, update_fields=update_fields, ) # A transaction isn't needed if one query is issued. @@ -855,12 +761,8 @@ class Model(metaclass=ModelBase): if not raw: parent_inserted = self._save_parents(cls, using, update_fields) updated = self._save_table( - raw, - cls, - force_insert or parent_inserted, - force_update, - using, - update_fields, + raw, cls, force_insert or parent_inserted, + force_update, using, update_fields, ) # Store the database on which the object was saved self._state.db = using @@ -870,12 +772,8 @@ class Model(metaclass=ModelBase): # Signal that the save is complete if not meta.auto_created: post_save.send( - sender=origin, - instance=self, - created=(not updated), - update_fields=update_fields, - raw=raw, - using=using, + sender=origin, instance=self, created=(not updated), + update_fields=update_fields, raw=raw, using=using, ) save_base.alters_data = True @@ -886,19 +784,12 @@ class Model(metaclass=ModelBase): inserted = False for parent, field in meta.parents.items(): # Make sure the link fields are synced between parent and self. - if ( - field - and getattr(self, parent._meta.pk.attname) is None - and getattr(self, field.attname) is not None - ): + if (field and getattr(self, parent._meta.pk.attname) is None and + getattr(self, field.attname) is not None): setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) - parent_inserted = self._save_parents( - cls=parent, using=using, update_fields=update_fields - ) + parent_inserted = self._save_parents(cls=parent, using=using, update_fields=update_fields) updated = self._save_table( - cls=parent, - using=using, - update_fields=update_fields, + cls=parent, using=using, update_fields=update_fields, force_insert=parent_inserted, ) if not updated: @@ -915,15 +806,8 @@ class Model(metaclass=ModelBase): field.delete_cached_value(self) return inserted - def _save_table( - self, - raw=False, - cls=None, - force_insert=False, - force_update=False, - using=None, - update_fields=None, - ): + def _save_table(self, raw=False, cls=None, force_insert=False, + force_update=False, using=None, update_fields=None): """ Do the heavy-lifting involved in saving. Update or insert the data for a single table. @@ -932,11 +816,8 @@ class Model(metaclass=ModelBase): non_pks = [f for f in meta.local_concrete_fields if not f.primary_key] if update_fields: - non_pks = [ - f - for f in non_pks - if f.name in update_fields or f.attname in update_fields - ] + non_pks = [f for f in non_pks + if f.name in update_fields or f.attname in update_fields] pk_val = self._get_pk_val(meta) if pk_val is None: @@ -948,28 +829,21 @@ class Model(metaclass=ModelBase): updated = False # Skip an UPDATE when adding an instance and primary key has a default. if ( - not raw - and not force_insert - and self._state.adding - and meta.pk.default - and meta.pk.default is not NOT_PROVIDED + not raw and + not force_insert and + self._state.adding and + meta.pk.default and + meta.pk.default is not NOT_PROVIDED ): force_insert = True # If possible, try an UPDATE. If that doesn't update anything, do an INSERT. if pk_set and not force_insert: base_qs = cls._base_manager.using(using) - values = [ - ( - f, - None, - (getattr(self, f.attname) if raw else f.pre_save(self, False)), - ) - for f in non_pks - ] + values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) + for f in non_pks] forced_update = update_fields or force_update - updated = self._do_update( - base_qs, using, pk_val, values, update_fields, forced_update - ) + updated = self._do_update(base_qs, using, pk_val, values, update_fields, + forced_update) if force_update and not updated: raise DatabaseError("Forced update did not affect any rows.") if update_fields and not updated: @@ -980,26 +854,18 @@ class Model(metaclass=ModelBase): # autopopulate the _order field field = meta.order_with_respect_to filter_args = field.get_filter_kwargs_for_object(self) - self._order = ( - cls._base_manager.using(using) - .filter(**filter_args) - .aggregate( - _order__max=Coalesce( - ExpressionWrapper( - Max("_order") + Value(1), output_field=IntegerField() - ), - Value(0), - ), - )["_order__max"] - ) + self._order = cls._base_manager.using(using).filter(**filter_args).aggregate( + _order__max=Coalesce( + ExpressionWrapper(Max('_order') + Value(1), output_field=IntegerField()), + Value(0), + ), + )['_order__max'] fields = meta.local_concrete_fields if not pk_set: fields = [f for f in fields if f is not meta.auto_field] returning_fields = meta.db_returning_fields - results = self._do_insert( - cls._base_manager, using, fields, returning_fields, raw - ) + results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw) if results: for value, field in zip(results[0], returning_fields): setattr(self, field.attname, value) @@ -1020,8 +886,7 @@ class Model(metaclass=ModelBase): return update_fields is not None or filtered.exists() if self._meta.select_on_save and not forced_update: return ( - filtered.exists() - and + filtered.exists() and # It may happen that the object is deleted from the DB right after # this check, causing the subsequent UPDATE to return zero matching # rows. The same result can occur in some rare cases when the @@ -1039,11 +904,8 @@ class Model(metaclass=ModelBase): return the newly created data for the model. """ return manager._insert( - [self], - fields=fields, - returning_fields=returning_fields, - using=using, - raw=raw, + [self], fields=fields, returning_fields=returning_fields, + using=using, raw=raw, ) def _prepare_related_fields_for_save(self, operation_name): @@ -1077,18 +939,16 @@ class Model(metaclass=ModelBase): setattr(self, field.attname, obj.pk) # If the relationship's pk/to_field was changed, clear the # cached relationship. - if getattr(obj, field.target_field.attname) != getattr( - self, field.attname - ): + if getattr(obj, field.target_field.attname) != getattr(self, field.attname): field.delete_cached_value(self) def delete(self, using=None, keep_parents=False): - if self.pk is None: - raise ValueError( - "%s object can't be deleted because its %s attribute is set " - "to None." % (self._meta.object_name, self._meta.pk.attname) - ) using = using or router.db_for_write(self.__class__, instance=self) + assert self.pk is not None, ( + "%s object can't be deleted because its %s attribute is set to None." % + (self._meta.object_name, self._meta.pk.attname) + ) + collector = Collector(using=using) collector.collect([self], keep_parents=keep_parents) return collector.delete() @@ -1099,59 +959,42 @@ class Model(metaclass=ModelBase): value = getattr(self, field.attname) choices_dict = dict(make_hashable(field.flatchoices)) # force_str() to coerce lazy strings. - return force_str( - choices_dict.get(make_hashable(value), value), strings_only=True - ) + return force_str(choices_dict.get(make_hashable(value), value), strings_only=True) def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): if not self.pk: raise ValueError("get_next/get_previous cannot be used on unsaved objects.") - op = "gt" if is_next else "lt" - order = "" if is_next else "-" + op = 'gt' if is_next else 'lt' + order = '' if is_next else '-' param = getattr(self, field.attname) - q = Q((field.name, param), (f"pk__{op}", self.pk), _connector=Q.AND) - q = Q(q, (f"{field.name}__{op}", param), _connector=Q.OR) - qs = ( - self.__class__._default_manager.using(self._state.db) - .filter(**kwargs) - .filter(q) - .order_by("%s%s" % (order, field.name), "%spk" % order) + q = Q(**{'%s__%s' % (field.name, op): param}) + q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk}) + qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by( + '%s%s' % (order, field.name), '%spk' % order ) try: return qs[0] except IndexError: - raise self.DoesNotExist( - "%s matching query does not exist." % self.__class__._meta.object_name - ) + raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name) def _get_next_or_previous_in_order(self, is_next): cachename = "__%s_order_cache" % is_next if not hasattr(self, cachename): - op = "gt" if is_next else "lt" - order = "_order" if is_next else "-_order" + op = 'gt' if is_next else 'lt' + order = '_order' if is_next else '-_order' order_field = self._meta.order_with_respect_to filter_args = order_field.get_filter_kwargs_for_object(self) - obj = ( - self.__class__._default_manager.filter(**filter_args) - .filter( - **{ - "_order__%s" - % op: self.__class__._default_manager.values("_order").filter( - **{self._meta.pk.name: self.pk} - ) - } - ) - .order_by(order)[:1] - .get() - ) + obj = self.__class__._default_manager.filter(**filter_args).filter(**{ + '_order__%s' % op: self.__class__._default_manager.values('_order').filter(**{ + self._meta.pk.name: self.pk + }) + }).order_by(order)[:1].get() setattr(self, cachename, obj) return getattr(self, cachename) def prepare_database_save(self, field): if self.pk is None: - raise ValueError( - "Unsaved model instance %r cannot be used in an ORM query." % self - ) + raise ValueError("Unsaved model instance %r cannot be used in an ORM query." % self) return getattr(self, field.remote_field.get_related_field().attname) def clean(self): @@ -1195,9 +1038,7 @@ class Model(metaclass=ModelBase): constraints = [(self.__class__, self._meta.total_unique_constraints)] for parent_class in self._meta.get_parent_list(): if parent_class._meta.unique_together: - unique_togethers.append( - (parent_class, parent_class._meta.unique_together) - ) + unique_togethers.append((parent_class, parent_class._meta.unique_together)) if parent_class._meta.total_unique_constraints: constraints.append( (parent_class, parent_class._meta.total_unique_constraints) @@ -1232,11 +1073,11 @@ class Model(metaclass=ModelBase): if f.unique: unique_checks.append((model_class, (name,))) if f.unique_for_date and f.unique_for_date not in exclude: - date_checks.append((model_class, "date", name, f.unique_for_date)) + date_checks.append((model_class, 'date', name, f.unique_for_date)) if f.unique_for_year and f.unique_for_year not in exclude: - date_checks.append((model_class, "year", name, f.unique_for_year)) + date_checks.append((model_class, 'year', name, f.unique_for_year)) if f.unique_for_month and f.unique_for_month not in exclude: - date_checks.append((model_class, "month", name, f.unique_for_month)) + date_checks.append((model_class, 'month', name, f.unique_for_month)) return unique_checks, date_checks def _perform_unique_checks(self, unique_checks): @@ -1251,10 +1092,8 @@ class Model(metaclass=ModelBase): f = self._meta.get_field(field_name) lookup_value = getattr(self, f.attname) # TODO: Handle multiple backends with different feature flags. - if lookup_value is None or ( - lookup_value == "" - and connection.features.interprets_empty_strings_as_nulls - ): + if (lookup_value is None or + (lookup_value == '' and connection.features.interprets_empty_strings_as_nulls)): # no value, skip the lookup continue if f.primary_key and not self._state.adding: @@ -1282,9 +1121,7 @@ class Model(metaclass=ModelBase): key = unique_check[0] else: key = NON_FIELD_ERRORS - errors.setdefault(key, []).append( - self.unique_error_message(model_class, unique_check) - ) + errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check)) return errors @@ -1297,14 +1134,12 @@ class Model(metaclass=ModelBase): date = getattr(self, unique_for) if date is None: continue - if lookup_type == "date": - lookup_kwargs["%s__day" % unique_for] = date.day - lookup_kwargs["%s__month" % unique_for] = date.month - lookup_kwargs["%s__year" % unique_for] = date.year + if lookup_type == 'date': + lookup_kwargs['%s__day' % unique_for] = date.day + lookup_kwargs['%s__month' % unique_for] = date.month + lookup_kwargs['%s__year' % unique_for] = date.year else: - lookup_kwargs["%s__%s" % (unique_for, lookup_type)] = getattr( - date, lookup_type - ) + lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type) lookup_kwargs[field] = getattr(self, field) qs = model_class._default_manager.filter(**lookup_kwargs) @@ -1323,48 +1158,46 @@ class Model(metaclass=ModelBase): opts = self._meta field = opts.get_field(field_name) return ValidationError( - message=field.error_messages["unique_for_date"], - code="unique_for_date", + message=field.error_messages['unique_for_date'], + code='unique_for_date', params={ - "model": self, - "model_name": capfirst(opts.verbose_name), - "lookup_type": lookup_type, - "field": field_name, - "field_label": capfirst(field.verbose_name), - "date_field": unique_for, - "date_field_label": capfirst(opts.get_field(unique_for).verbose_name), - }, + 'model': self, + 'model_name': capfirst(opts.verbose_name), + 'lookup_type': lookup_type, + 'field': field_name, + 'field_label': capfirst(field.verbose_name), + 'date_field': unique_for, + 'date_field_label': capfirst(opts.get_field(unique_for).verbose_name), + } ) def unique_error_message(self, model_class, unique_check): opts = model_class._meta params = { - "model": self, - "model_class": model_class, - "model_name": capfirst(opts.verbose_name), - "unique_check": unique_check, + 'model': self, + 'model_class': model_class, + 'model_name': capfirst(opts.verbose_name), + 'unique_check': unique_check, } # A unique field if len(unique_check) == 1: field = opts.get_field(unique_check[0]) - params["field_label"] = capfirst(field.verbose_name) + params['field_label'] = capfirst(field.verbose_name) return ValidationError( - message=field.error_messages["unique"], - code="unique", + message=field.error_messages['unique'], + code='unique', params=params, ) # unique_together else: - field_labels = [ - capfirst(opts.get_field(f).verbose_name) for f in unique_check - ] - params["field_labels"] = get_text_list(field_labels, _("and")) + field_labels = [capfirst(opts.get_field(f).verbose_name) for f in unique_check] + params['field_labels'] = get_text_list(field_labels, _('and')) return ValidationError( message=_("%(model_name)s with this %(field_labels)s already exists."), - code="unique_together", + code='unique_together', params=params, ) @@ -1431,13 +1264,9 @@ class Model(metaclass=ModelBase): @classmethod def check(cls, **kwargs): - errors = [ - *cls._check_swappable(), - *cls._check_model(), - *cls._check_managers(**kwargs), - ] + errors = [*cls._check_swappable(), *cls._check_model(), *cls._check_managers(**kwargs)] if not cls._meta.swapped: - databases = kwargs.get("databases") or [] + databases = kwargs.get('databases') or [] errors += [ *cls._check_fields(**kwargs), *cls._check_m2m_through_same_relationship(), @@ -1469,17 +1298,16 @@ class Model(metaclass=ModelBase): @classmethod def _check_default_pk(cls): if ( - not cls._meta.abstract - and cls._meta.pk.auto_created - and + not cls._meta.abstract and + cls._meta.pk.auto_created and # Inherited PKs are checked in parents models. not ( - isinstance(cls._meta.pk, OneToOneField) - and cls._meta.pk.remote_field.parent_link - ) - and not settings.is_overridden("DEFAULT_AUTO_FIELD") - and cls._meta.app_config - and not cls._meta.app_config._is_default_auto_field_overridden + isinstance(cls._meta.pk, OneToOneField) and + cls._meta.pk.remote_field.parent_link + ) and + not settings.is_overridden('DEFAULT_AUTO_FIELD') and + cls._meta.app_config and + not cls._meta.app_config._is_default_auto_field_overridden ): return [ checks.Warning( @@ -1493,7 +1321,7 @@ class Model(metaclass=ModelBase): f"of AutoField, e.g. 'django.db.models.BigAutoField'." ), obj=cls, - id="models.W042", + id='models.W042', ), ] return [] @@ -1508,19 +1336,19 @@ class Model(metaclass=ModelBase): except ValueError: errors.append( checks.Error( - "'%s' is not of the form 'app_label.app_name'." - % cls._meta.swappable, - id="models.E001", + "'%s' is not of the form 'app_label.app_name'." % cls._meta.swappable, + id='models.E001', ) ) except LookupError: - app_label, model_name = cls._meta.swapped.split(".") + app_label, model_name = cls._meta.swapped.split('.') errors.append( checks.Error( "'%s' references '%s.%s', which has not been " - "installed, or is abstract." - % (cls._meta.swappable, app_label, model_name), - id="models.E002", + "installed, or is abstract." % ( + cls._meta.swappable, app_label, model_name + ), + id='models.E002', ) ) return errors @@ -1533,7 +1361,7 @@ class Model(metaclass=ModelBase): errors.append( checks.Error( "Proxy model '%s' contains model fields." % cls.__name__, - id="models.E017", + id='models.E017', ) ) return errors @@ -1558,7 +1386,8 @@ class Model(metaclass=ModelBase): @classmethod def _check_m2m_through_same_relationship(cls): - """Check if no relationship model is used by more than one m2m field.""" + """ Check if no relationship model is used by more than one m2m field. + """ errors = [] seen_intermediary_signatures = [] @@ -1572,20 +1401,15 @@ class Model(metaclass=ModelBase): fields = (f for f in fields if isinstance(f.remote_field.through, ModelBase)) for f in fields: - signature = ( - f.remote_field.model, - cls, - f.remote_field.through, - f.remote_field.through_fields, - ) + signature = (f.remote_field.model, cls, f.remote_field.through, f.remote_field.through_fields) if signature in seen_intermediary_signatures: errors.append( checks.Error( "The model has two identical many-to-many relations " - "through the intermediate model '%s'." - % f.remote_field.through._meta.label, + "through the intermediate model '%s'." % + f.remote_field.through._meta.label, obj=cls, - id="models.E003", + id='models.E003', ) ) else: @@ -1595,17 +1419,15 @@ class Model(metaclass=ModelBase): @classmethod def _check_id_field(cls): """Check if `id` field is a primary key.""" - fields = [ - f for f in cls._meta.local_fields if f.name == "id" and f != cls._meta.pk - ] + fields = [f for f in cls._meta.local_fields if f.name == 'id' and f != cls._meta.pk] # fields is empty or consists of the invalid "id" field - if fields and not fields[0].primary_key and cls._meta.pk.name == "id": + if fields and not fields[0].primary_key and cls._meta.pk.name == 'id': return [ checks.Error( "'id' can only be used as a field name if the field also " "sets 'primary_key=True'.", obj=cls, - id="models.E004", + id='models.E004', ) ] else: @@ -1626,10 +1448,12 @@ class Model(metaclass=ModelBase): checks.Error( "The field '%s' from parent model " "'%s' clashes with the field '%s' " - "from parent model '%s'." - % (clash.name, clash.model._meta, f.name, f.model._meta), + "from parent model '%s'." % ( + clash.name, clash.model._meta, + f.name, f.model._meta + ), obj=cls, - id="models.E005", + id='models.E005', ) ) used_fields[f.name] = f @@ -1649,16 +1473,16 @@ class Model(metaclass=ModelBase): # field "id" and automatically added unique field "id", both # defined at the same model. This special case is considered in # _check_id_field and here we ignore it. - id_conflict = ( - f.name == "id" and clash and clash.name == "id" and clash.model == cls - ) + id_conflict = f.name == "id" and clash and clash.name == "id" and clash.model == cls if clash and not id_conflict: errors.append( checks.Error( "The field '%s' clashes with the field '%s' " - "from model '%s'." % (f.name, clash.name, clash.model._meta), + "from model '%s'." % ( + f.name, clash.name, clash.model._meta + ), obj=f, - id="models.E006", + id='models.E006', ) ) used_fields[f.name] = f @@ -1683,7 +1507,7 @@ class Model(metaclass=ModelBase): "another field." % (f.name, column_name), hint="Specify a 'db_column' for the field.", obj=cls, - id="models.E007", + id='models.E007' ) ) else: @@ -1695,13 +1519,13 @@ class Model(metaclass=ModelBase): def _check_model_name_db_lookup_clashes(cls): errors = [] model_name = cls.__name__ - if model_name.startswith("_") or model_name.endswith("_"): + if model_name.startswith('_') or model_name.endswith('_'): errors.append( checks.Error( "The model name '%s' cannot start or end with an underscore " "as it collides with the query lookup syntax." % model_name, obj=cls, - id="models.E023", + id='models.E023' ) ) elif LOOKUP_SEP in model_name: @@ -1710,7 +1534,7 @@ class Model(metaclass=ModelBase): "The model name '%s' cannot contain double underscores as " "it collides with the query lookup syntax." % model_name, obj=cls, - id="models.E024", + id='models.E024' ) ) return errors @@ -1720,8 +1544,7 @@ class Model(metaclass=ModelBase): errors = [] property_names = cls._meta._property_names related_field_accessors = ( - f.get_attname() - for f in cls._meta._get_fields(reverse=False) + f.get_attname() for f in cls._meta._get_fields(reverse=False) if f.is_relation and f.related_model is not None ) for accessor in related_field_accessors: @@ -1731,7 +1554,7 @@ class Model(metaclass=ModelBase): "The property '%s' clashes with a related field " "accessor." % accessor, obj=cls, - id="models.E025", + id='models.E025', ) ) return errors @@ -1745,7 +1568,7 @@ class Model(metaclass=ModelBase): "The model cannot have more than one field with " "'primary_key=True'.", obj=cls, - id="models.E026", + id='models.E026', ) ) return errors @@ -1758,18 +1581,16 @@ class Model(metaclass=ModelBase): checks.Error( "'index_together' must be a list or tuple.", obj=cls, - id="models.E008", + id='models.E008', ) ] - elif any( - not isinstance(fields, (tuple, list)) for fields in cls._meta.index_together - ): + elif any(not isinstance(fields, (tuple, list)) for fields in cls._meta.index_together): return [ checks.Error( "All 'index_together' elements must be lists or tuples.", obj=cls, - id="models.E009", + id='models.E009', ) ] @@ -1787,19 +1608,16 @@ class Model(metaclass=ModelBase): checks.Error( "'unique_together' must be a list or tuple.", obj=cls, - id="models.E010", + id='models.E010', ) ] - elif any( - not isinstance(fields, (tuple, list)) - for fields in cls._meta.unique_together - ): + elif any(not isinstance(fields, (tuple, list)) for fields in cls._meta.unique_together): return [ checks.Error( "All 'unique_together' elements must be lists or tuples.", obj=cls, - id="models.E011", + id='models.E011', ) ] @@ -1817,13 +1635,13 @@ class Model(metaclass=ModelBase): for index in cls._meta.indexes: # Index name can't start with an underscore or a number, restricted # for cross-database compatibility with Oracle. - if index.name[0] == "_" or index.name[0].isdigit(): + if index.name[0] == '_' or index.name[0].isdigit(): errors.append( checks.Error( "The index name '%s' cannot start with an underscore " "or a number." % index.name, obj=cls, - id="models.E033", + id='models.E033', ), ) if len(index.name) > index.max_name_length: @@ -1832,7 +1650,7 @@ class Model(metaclass=ModelBase): "The index name '%s' cannot be longer than %d " "characters." % (index.name, index.max_name_length), obj=cls, - id="models.E034", + id='models.E034', ), ) if index.contains_expressions: @@ -1845,59 +1663,57 @@ class Model(metaclass=ModelBase): continue connection = connections[db] if not ( - connection.features.supports_partial_indexes - or "supports_partial_indexes" in cls._meta.required_db_features + connection.features.supports_partial_indexes or + 'supports_partial_indexes' in cls._meta.required_db_features ) and any(index.condition is not None for index in cls._meta.indexes): errors.append( checks.Warning( - "%s does not support indexes with conditions." + '%s does not support indexes with conditions.' % connection.display_name, hint=( "Conditions will be ignored. Silence this warning " "if you don't care about it." ), obj=cls, - id="models.W037", + id='models.W037', ) ) if not ( - connection.features.supports_covering_indexes - or "supports_covering_indexes" in cls._meta.required_db_features + connection.features.supports_covering_indexes or + 'supports_covering_indexes' in cls._meta.required_db_features ) and any(index.include for index in cls._meta.indexes): errors.append( checks.Warning( - "%s does not support indexes with non-key columns." + '%s does not support indexes with non-key columns.' % connection.display_name, hint=( "Non-key columns will be ignored. Silence this " "warning if you don't care about it." ), obj=cls, - id="models.W040", + id='models.W040', ) ) if not ( - connection.features.supports_expression_indexes - or "supports_expression_indexes" in cls._meta.required_db_features + connection.features.supports_expression_indexes or + 'supports_expression_indexes' in cls._meta.required_db_features ) and any(index.contains_expressions for index in cls._meta.indexes): errors.append( checks.Warning( - "%s does not support indexes on expressions." + '%s does not support indexes on expressions.' % connection.display_name, hint=( "An index won't be created. Silence this warning " "if you don't care about it." ), obj=cls, - id="models.W043", + id='models.W043', ) ) - fields = [ - field for index in cls._meta.indexes for field, _ in index.fields_orders - ] + fields = [field for index in cls._meta.indexes for field, _ in index.fields_orders] fields += [include for index in cls._meta.indexes for include in index.include] fields += references - errors.extend(cls._check_local_fields(fields, "indexes")) + errors.extend(cls._check_local_fields(fields, 'indexes')) return errors @classmethod @@ -1909,7 +1725,7 @@ class Model(metaclass=ModelBase): forward_fields_map = {} for field in cls._meta._get_fields(reverse=False): forward_fields_map[field.name] = field - if hasattr(field, "attname"): + if hasattr(field, 'attname'): forward_fields_map[field.attname] = field errors = [] @@ -1919,13 +1735,11 @@ class Model(metaclass=ModelBase): except KeyError: errors.append( checks.Error( - "'%s' refers to the nonexistent field '%s'." - % ( - option, - field_name, + "'%s' refers to the nonexistent field '%s'." % ( + option, field_name, ), obj=cls, - id="models.E012", + id='models.E012', ) ) else: @@ -1933,24 +1747,21 @@ class Model(metaclass=ModelBase): errors.append( checks.Error( "'%s' refers to a ManyToManyField '%s', but " - "ManyToManyFields are not permitted in '%s'." - % ( - option, - field_name, - option, + "ManyToManyFields are not permitted in '%s'." % ( + option, field_name, option, ), obj=cls, - id="models.E013", + id='models.E013', ) ) elif field not in cls._meta.local_fields: errors.append( checks.Error( - "'%s' refers to field '%s' which is not local to model " - "'%s'." % (option, field_name, cls._meta.object_name), + "'%s' refers to field '%s' which is not local to model '%s'." + % (option, field_name, cls._meta.object_name), hint="This issue may be caused by multi-table inheritance.", obj=cls, - id="models.E016", + id='models.E016', ) ) return errors @@ -1966,7 +1777,7 @@ class Model(metaclass=ModelBase): checks.Error( "'ordering' and 'order_with_respect_to' cannot be used together.", obj=cls, - id="models.E021", + id='models.E021', ), ] @@ -1976,10 +1787,9 @@ class Model(metaclass=ModelBase): if not isinstance(cls._meta.ordering, (list, tuple)): return [ checks.Error( - "'ordering' must be a tuple or list (even if you want to order by " - "only one field).", + "'ordering' must be a tuple or list (even if you want to order by only one field).", obj=cls, - id="models.E014", + id='models.E014', ) ] @@ -1987,10 +1797,10 @@ class Model(metaclass=ModelBase): fields = cls._meta.ordering # Skip expressions and '?' fields. - fields = (f for f in fields if isinstance(f, str) and f != "?") + fields = (f for f in fields if isinstance(f, str) and f != '?') # Convert "-field" to "field". - fields = ((f[1:] if f.startswith("-") else f) for f in fields) + fields = ((f[1:] if f.startswith('-') else f) for f in fields) # Separate related fields and non-related fields. _fields = [] @@ -2009,7 +1819,7 @@ class Model(metaclass=ModelBase): for part in field.split(LOOKUP_SEP): try: # pk is an alias that won't be found by opts.get_field. - if part == "pk": + if part == 'pk': fld = _cls._meta.pk else: fld = _cls._meta.get_field(part) @@ -2026,13 +1836,13 @@ class Model(metaclass=ModelBase): "'ordering' refers to the nonexistent field, " "related field, or lookup '%s'." % field, obj=cls, - id="models.E015", + id='models.E015', ) ) # Skip ordering on pk. This is always a valid order_by field # but is an alias and therefore won't be found by opts.get_field. - fields = {f for f in fields if f != "pk"} + fields = {f for f in fields if f != 'pk'} # Check for invalid or nonexistent fields in ordering. invalid_fields = [] @@ -2040,14 +1850,10 @@ class Model(metaclass=ModelBase): # Any field name that is not present in field_names does not exist. # Also, ordering by m2m fields is not allowed. opts = cls._meta - valid_fields = set( - chain.from_iterable( - (f.name, f.attname) - if not (f.auto_created and not f.concrete) - else (f.field.related_query_name(),) - for f in chain(opts.fields, opts.related_objects) - ) - ) + valid_fields = set(chain.from_iterable( + (f.name, f.attname) if not (f.auto_created and not f.concrete) else (f.field.related_query_name(),) + for f in chain(opts.fields, opts.related_objects) + )) invalid_fields.extend(fields - valid_fields) @@ -2057,7 +1863,7 @@ class Model(metaclass=ModelBase): "'ordering' refers to the nonexistent field, related " "field, or lookup '%s'." % invalid_field, obj=cls, - id="models.E015", + id='models.E015', ) ) return errors @@ -2099,11 +1905,7 @@ class Model(metaclass=ModelBase): # Check if auto-generated name for the field is too long # for the database. - if ( - f.db_column is None - and column_name is not None - and len(column_name) > allowed_len - ): + if f.db_column is None and column_name is not None and len(column_name) > allowed_len: errors.append( checks.Error( 'Autogenerated column name too long for field "%s". ' @@ -2111,7 +1913,7 @@ class Model(metaclass=ModelBase): % (column_name, allowed_len, db_alias), hint="Set the column name manually using 'db_column'.", obj=cls, - id="models.E018", + id='models.E018', ) ) @@ -2124,14 +1926,10 @@ class Model(metaclass=ModelBase): # for the database. for m2m in f.remote_field.through._meta.local_fields: _, rel_name = m2m.get_attname_column() - if ( - m2m.db_column is None - and rel_name is not None - and len(rel_name) > allowed_len - ): + if m2m.db_column is None and rel_name is not None and len(rel_name) > allowed_len: errors.append( checks.Error( - "Autogenerated column name too long for M2M field " + 'Autogenerated column name too long for M2M field ' '"%s". Maximum length is "%s" for database "%s".' % (rel_name, allowed_len, db_alias), hint=( @@ -2139,7 +1937,7 @@ class Model(metaclass=ModelBase): "M2M and then set column_name using 'db_column'." ), obj=cls, - id="models.E019", + id='models.E019', ) ) @@ -2157,7 +1955,7 @@ class Model(metaclass=ModelBase): yield from cls._get_expr_references(child) elif isinstance(expr, F): yield tuple(expr.name.split(LOOKUP_SEP)) - elif hasattr(expr, "get_source_expressions"): + elif hasattr(expr, 'get_source_expressions'): for src_expr in expr.get_source_expressions(): yield from cls._get_expr_references(src_expr) @@ -2169,145 +1967,107 @@ class Model(metaclass=ModelBase): continue connection = connections[db] if not ( - connection.features.supports_table_check_constraints - or "supports_table_check_constraints" in cls._meta.required_db_features + connection.features.supports_table_check_constraints or + 'supports_table_check_constraints' in cls._meta.required_db_features ) and any( isinstance(constraint, CheckConstraint) for constraint in cls._meta.constraints ): errors.append( checks.Warning( - "%s does not support check constraints." + '%s does not support check constraints.' % connection.display_name, + hint=( + "A constraint won't be created. Silence this " + "warning if you don't care about it." + ), + obj=cls, + id='models.W027', + ) + ) + if not ( + connection.features.supports_partial_indexes or + 'supports_partial_indexes' in cls._meta.required_db_features + ) and any( + isinstance(constraint, UniqueConstraint) and constraint.condition is not None + for constraint in cls._meta.constraints + ): + errors.append( + checks.Warning( + '%s does not support unique constraints with ' + 'conditions.' % connection.display_name, + hint=( + "A constraint won't be created. Silence this " + "warning if you don't care about it." + ), + obj=cls, + id='models.W036', + ) + ) + if not ( + connection.features.supports_deferrable_unique_constraints or + 'supports_deferrable_unique_constraints' in cls._meta.required_db_features + ) and any( + isinstance(constraint, UniqueConstraint) and constraint.deferrable is not None + for constraint in cls._meta.constraints + ): + errors.append( + checks.Warning( + '%s does not support deferrable unique constraints.' % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id="models.W027", + id='models.W038', ) ) if not ( - connection.features.supports_partial_indexes - or "supports_partial_indexes" in cls._meta.required_db_features - ) and any( - isinstance(constraint, UniqueConstraint) - and constraint.condition is not None - for constraint in cls._meta.constraints - ): - errors.append( - checks.Warning( - "%s does not support unique constraints with " - "conditions." % connection.display_name, - hint=( - "A constraint won't be created. Silence this " - "warning if you don't care about it." - ), - obj=cls, - id="models.W036", - ) - ) - if not ( - connection.features.supports_deferrable_unique_constraints - or "supports_deferrable_unique_constraints" - in cls._meta.required_db_features - ) and any( - isinstance(constraint, UniqueConstraint) - and constraint.deferrable is not None - for constraint in cls._meta.constraints - ): - errors.append( - checks.Warning( - "%s does not support deferrable unique constraints." - % connection.display_name, - hint=( - "A constraint won't be created. Silence this " - "warning if you don't care about it." - ), - obj=cls, - id="models.W038", - ) - ) - if not ( - connection.features.supports_covering_indexes - or "supports_covering_indexes" in cls._meta.required_db_features + connection.features.supports_covering_indexes or + 'supports_covering_indexes' in cls._meta.required_db_features ) and any( isinstance(constraint, UniqueConstraint) and constraint.include for constraint in cls._meta.constraints ): errors.append( checks.Warning( - "%s does not support unique constraints with non-key " - "columns." % connection.display_name, + '%s does not support unique constraints with non-key ' + 'columns.' % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id="models.W039", + id='models.W039', ) ) - if not ( - connection.features.supports_expression_indexes - or "supports_expression_indexes" in cls._meta.required_db_features - ) and any( - isinstance(constraint, UniqueConstraint) - and constraint.contains_expressions - for constraint in cls._meta.constraints - ): - errors.append( - checks.Warning( - "%s does not support unique constraints on " - "expressions." % connection.display_name, - hint=( - "A constraint won't be created. Silence this " - "warning if you don't care about it." - ), - obj=cls, - id="models.W044", - ) - ) - fields = set( - chain.from_iterable( - (*constraint.fields, *constraint.include) - for constraint in cls._meta.constraints - if isinstance(constraint, UniqueConstraint) - ) - ) + fields = set(chain.from_iterable( + (*constraint.fields, *constraint.include) + for constraint in cls._meta.constraints if isinstance(constraint, UniqueConstraint) + )) references = set() for constraint in cls._meta.constraints: if isinstance(constraint, UniqueConstraint): if ( - connection.features.supports_partial_indexes - or "supports_partial_indexes" - not in cls._meta.required_db_features + connection.features.supports_partial_indexes or + 'supports_partial_indexes' not in cls._meta.required_db_features ) and isinstance(constraint.condition, Q): - references.update( - cls._get_expr_references(constraint.condition) - ) - if ( - connection.features.supports_expression_indexes - or "supports_expression_indexes" - not in cls._meta.required_db_features - ) and constraint.contains_expressions: - for expression in constraint.expressions: - references.update(cls._get_expr_references(expression)) + references.update(cls._get_expr_references(constraint.condition)) elif isinstance(constraint, CheckConstraint): if ( - connection.features.supports_table_check_constraints - or "supports_table_check_constraints" - not in cls._meta.required_db_features + connection.features.supports_table_check_constraints or + 'supports_table_check_constraints' not in cls._meta.required_db_features ) and isinstance(constraint.check, Q): references.update(cls._get_expr_references(constraint.check)) for field_name, *lookups in references: # pk is an alias that won't be found by opts.get_field. - if field_name != "pk": + if field_name != 'pk': fields.add(field_name) if not lookups: # If it has no lookups it cannot result in a JOIN. continue try: - if field_name == "pk": + if field_name == 'pk': field = cls._meta.pk else: field = cls._meta.get_field(field_name) @@ -2318,20 +2078,20 @@ class Model(metaclass=ModelBase): # JOIN must happen at the first lookup. first_lookup = lookups[0] if ( - hasattr(field, "get_transform") - and hasattr(field, "get_lookup") - and field.get_transform(first_lookup) is None - and field.get_lookup(first_lookup) is None + hasattr(field, 'get_transform') and + hasattr(field, 'get_lookup') and + field.get_transform(first_lookup) is None and + field.get_lookup(first_lookup) is None ): errors.append( checks.Error( "'constraints' refers to the joined field '%s'." % LOOKUP_SEP.join([field_name] + lookups), obj=cls, - id="models.E041", + id='models.E041', ) ) - errors.extend(cls._check_local_fields(fields, "constraints")) + errors.extend(cls._check_local_fields(fields, 'constraints')) return errors @@ -2341,16 +2101,14 @@ class Model(metaclass=ModelBase): # ORDERING METHODS ######################### - def method_set_order(self, ordered_obj, id_list, using=None): if using is None: using = DEFAULT_DB_ALIAS order_wrt = ordered_obj._meta.order_with_respect_to filter_args = order_wrt.get_forward_related_filter(self) - ordered_obj.objects.db_manager(using).filter(**filter_args).bulk_update( - [ordered_obj(pk=pk, _order=order) for order, pk in enumerate(id_list)], - ["_order"], - ) + ordered_obj.objects.db_manager(using).filter(**filter_args).bulk_update([ + ordered_obj(pk=pk, _order=order) for order, pk in enumerate(id_list) + ], ['_order']) def method_get_order(self, ordered_obj): @@ -2363,16 +2121,15 @@ def method_get_order(self, ordered_obj): def make_foreign_order_accessors(model, related_model): setattr( related_model, - "get_%s_order" % model.__name__.lower(), - partialmethod(method_get_order, model), + 'get_%s_order' % model.__name__.lower(), + partialmethod(method_get_order, model) ) setattr( related_model, - "set_%s_order" % model.__name__.lower(), - partialmethod(method_set_order, model), + 'set_%s_order' % model.__name__.lower(), + partialmethod(method_set_order, model) ) - ######## # MISC # ######## diff --git a/venv/Lib/site-packages/django/db/models/constants.py b/venv/Lib/site-packages/django/db/models/constants.py index a92fbf0..a7e6c25 100644 --- a/venv/Lib/site-packages/django/db/models/constants.py +++ b/venv/Lib/site-packages/django/db/models/constants.py @@ -3,4 +3,4 @@ Constants used across the ORM in general. """ # Separator used to split filter strings apart. -LOOKUP_SEP = "__" +LOOKUP_SEP = '__' diff --git a/venv/Lib/site-packages/django/db/models/constraints.py b/venv/Lib/site-packages/django/db/models/constraints.py index 721e43a..fd01ec5 100644 --- a/venv/Lib/site-packages/django/db/models/constraints.py +++ b/venv/Lib/site-packages/django/db/models/constraints.py @@ -1,34 +1,28 @@ from enum import Enum -from django.db.models.expressions import ExpressionList, F -from django.db.models.indexes import IndexExpression from django.db.models.query_utils import Q from django.db.models.sql.query import Query -__all__ = ["CheckConstraint", "Deferrable", "UniqueConstraint"] +__all__ = ['CheckConstraint', 'Deferrable', 'UniqueConstraint'] class BaseConstraint: def __init__(self, name): self.name = name - @property - def contains_expressions(self): - return False - def constraint_sql(self, model, schema_editor): - raise NotImplementedError("This method must be implemented by a subclass.") + raise NotImplementedError('This method must be implemented by a subclass.') def create_sql(self, model, schema_editor): - raise NotImplementedError("This method must be implemented by a subclass.") + raise NotImplementedError('This method must be implemented by a subclass.') def remove_sql(self, model, schema_editor): - raise NotImplementedError("This method must be implemented by a subclass.") + raise NotImplementedError('This method must be implemented by a subclass.') def deconstruct(self): - path = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) - path = path.replace("django.db.models.constraints", "django.db.models") - return (path, (), {"name": self.name}) + path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) + path = path.replace('django.db.models.constraints', 'django.db.models') + return (path, (), {'name': self.name}) def clone(self): _, args, kwargs = self.deconstruct() @@ -38,9 +32,10 @@ class BaseConstraint: class CheckConstraint(BaseConstraint): def __init__(self, *, check, name): self.check = check - if not getattr(check, "conditional", False): + if not getattr(check, 'conditional', False): raise TypeError( - "CheckConstraint.check must be a Q instance or boolean expression." + 'CheckConstraint.check must be a Q instance or boolean ' + 'expression.' ) super().__init__(name) @@ -63,11 +58,7 @@ class CheckConstraint(BaseConstraint): return schema_editor._delete_check_sql(model, self.name) def __repr__(self): - return "<%s: check=%s name=%s>" % ( - self.__class__.__qualname__, - self.check, - repr(self.name), - ) + return "<%s: check='%s' name=%r>" % (self.__class__.__name__, self.check, self.name) def __eq__(self, other): if isinstance(other, CheckConstraint): @@ -76,84 +67,62 @@ class CheckConstraint(BaseConstraint): def deconstruct(self): path, args, kwargs = super().deconstruct() - kwargs["check"] = self.check + kwargs['check'] = self.check return path, args, kwargs class Deferrable(Enum): - DEFERRED = "deferred" - IMMEDIATE = "immediate" - - # A similar format was proposed for Python 3.10. - def __repr__(self): - return f"{self.__class__.__qualname__}.{self._name_}" + DEFERRED = 'deferred' + IMMEDIATE = 'immediate' class UniqueConstraint(BaseConstraint): def __init__( self, - *expressions, - fields=(), - name=None, + *, + fields, + name, condition=None, deferrable=None, include=None, opclasses=(), ): - if not name: - raise ValueError("A unique constraint must be named.") - if not expressions and not fields: - raise ValueError( - "At least one field or expression is required to define a " - "unique constraint." - ) - if expressions and fields: - raise ValueError( - "UniqueConstraint.fields and expressions are mutually exclusive." - ) + if not fields: + raise ValueError('At least one field is required to define a unique constraint.') if not isinstance(condition, (type(None), Q)): - raise ValueError("UniqueConstraint.condition must be a Q instance.") + raise ValueError('UniqueConstraint.condition must be a Q instance.') if condition and deferrable: - raise ValueError("UniqueConstraint with conditions cannot be deferred.") - if include and deferrable: - raise ValueError("UniqueConstraint with include fields cannot be deferred.") - if opclasses and deferrable: - raise ValueError("UniqueConstraint with opclasses cannot be deferred.") - if expressions and deferrable: - raise ValueError("UniqueConstraint with expressions cannot be deferred.") - if expressions and opclasses: raise ValueError( - "UniqueConstraint.opclasses cannot be used with expressions. " - "Use django.contrib.postgres.indexes.OpClass() instead." + 'UniqueConstraint with conditions cannot be deferred.' + ) + if include and deferrable: + raise ValueError( + 'UniqueConstraint with include fields cannot be deferred.' + ) + if opclasses and deferrable: + raise ValueError( + 'UniqueConstraint with opclasses cannot be deferred.' ) if not isinstance(deferrable, (type(None), Deferrable)): raise ValueError( - "UniqueConstraint.deferrable must be a Deferrable instance." + 'UniqueConstraint.deferrable must be a Deferrable instance.' ) if not isinstance(include, (type(None), list, tuple)): - raise ValueError("UniqueConstraint.include must be a list or tuple.") + raise ValueError('UniqueConstraint.include must be a list or tuple.') if not isinstance(opclasses, (list, tuple)): - raise ValueError("UniqueConstraint.opclasses must be a list or tuple.") + raise ValueError('UniqueConstraint.opclasses must be a list or tuple.') if opclasses and len(fields) != len(opclasses): raise ValueError( - "UniqueConstraint.fields and UniqueConstraint.opclasses must " - "have the same number of elements." + 'UniqueConstraint.fields and UniqueConstraint.opclasses must ' + 'have the same number of elements.' ) self.fields = tuple(fields) self.condition = condition self.deferrable = deferrable self.include = tuple(include) if include else () self.opclasses = opclasses - self.expressions = tuple( - F(expression) if isinstance(expression, str) else expression - for expression in expressions - ) super().__init__(name) - @property - def contains_expressions(self): - return bool(self.expressions) - def _get_condition_sql(self, model, schema_editor): if self.condition is None: return None @@ -163,105 +132,64 @@ class UniqueConstraint(BaseConstraint): sql, params = where.as_sql(compiler, schema_editor.connection) return sql % tuple(schema_editor.quote_value(p) for p in params) - def _get_index_expressions(self, model, schema_editor): - if not self.expressions: - return None - index_expressions = [] - for expression in self.expressions: - index_expression = IndexExpression(expression) - index_expression.set_wrapper_classes(schema_editor.connection) - index_expressions.append(index_expression) - return ExpressionList(*index_expressions).resolve_expression( - Query(model, alias_cols=False), - ) - def constraint_sql(self, model, schema_editor): - fields = [model._meta.get_field(field_name) for field_name in self.fields] - include = [ - model._meta.get_field(field_name).column for field_name in self.include - ] + fields = [model._meta.get_field(field_name).column for field_name in self.fields] + include = [model._meta.get_field(field_name).column for field_name in self.include] condition = self._get_condition_sql(model, schema_editor) - expressions = self._get_index_expressions(model, schema_editor) return schema_editor._unique_sql( - model, - fields, - self.name, - condition=condition, - deferrable=self.deferrable, - include=include, + model, fields, self.name, condition=condition, + deferrable=self.deferrable, include=include, opclasses=self.opclasses, - expressions=expressions, ) def create_sql(self, model, schema_editor): - fields = [model._meta.get_field(field_name) for field_name in self.fields] - include = [ - model._meta.get_field(field_name).column for field_name in self.include - ] + fields = [model._meta.get_field(field_name).column for field_name in self.fields] + include = [model._meta.get_field(field_name).column for field_name in self.include] condition = self._get_condition_sql(model, schema_editor) - expressions = self._get_index_expressions(model, schema_editor) return schema_editor._create_unique_sql( - model, - fields, - self.name, - condition=condition, - deferrable=self.deferrable, - include=include, + model, fields, self.name, condition=condition, + deferrable=self.deferrable, include=include, opclasses=self.opclasses, - expressions=expressions, ) def remove_sql(self, model, schema_editor): condition = self._get_condition_sql(model, schema_editor) - include = [ - model._meta.get_field(field_name).column for field_name in self.include - ] - expressions = self._get_index_expressions(model, schema_editor) + include = [model._meta.get_field(field_name).column for field_name in self.include] return schema_editor._delete_unique_sql( - model, - self.name, - condition=condition, - deferrable=self.deferrable, - include=include, - opclasses=self.opclasses, - expressions=expressions, + model, self.name, condition=condition, deferrable=self.deferrable, + include=include, opclasses=self.opclasses, ) def __repr__(self): - return "<%s:%s%s%s%s%s%s%s>" % ( - self.__class__.__qualname__, - "" if not self.fields else " fields=%s" % repr(self.fields), - "" if not self.expressions else " expressions=%s" % repr(self.expressions), - " name=%s" % repr(self.name), - "" if self.condition is None else " condition=%s" % self.condition, - "" if self.deferrable is None else " deferrable=%r" % self.deferrable, - "" if not self.include else " include=%s" % repr(self.include), - "" if not self.opclasses else " opclasses=%s" % repr(self.opclasses), + return '<%s: fields=%r name=%r%s%s%s%s>' % ( + self.__class__.__name__, self.fields, self.name, + '' if self.condition is None else ' condition=%s' % self.condition, + '' if self.deferrable is None else ' deferrable=%s' % self.deferrable, + '' if not self.include else ' include=%s' % repr(self.include), + '' if not self.opclasses else ' opclasses=%s' % repr(self.opclasses), ) def __eq__(self, other): if isinstance(other, UniqueConstraint): return ( - self.name == other.name - and self.fields == other.fields - and self.condition == other.condition - and self.deferrable == other.deferrable - and self.include == other.include - and self.opclasses == other.opclasses - and self.expressions == other.expressions + self.name == other.name and + self.fields == other.fields and + self.condition == other.condition and + self.deferrable == other.deferrable and + self.include == other.include and + self.opclasses == other.opclasses ) return super().__eq__(other) def deconstruct(self): path, args, kwargs = super().deconstruct() - if self.fields: - kwargs["fields"] = self.fields + kwargs['fields'] = self.fields if self.condition: - kwargs["condition"] = self.condition + kwargs['condition'] = self.condition if self.deferrable: - kwargs["deferrable"] = self.deferrable + kwargs['deferrable'] = self.deferrable if self.include: - kwargs["include"] = self.include + kwargs['include'] = self.include if self.opclasses: - kwargs["opclasses"] = self.opclasses - return path, self.expressions, kwargs + kwargs['opclasses'] = self.opclasses + return path, args, kwargs diff --git a/venv/Lib/site-packages/django/db/models/deletion.py b/venv/Lib/site-packages/django/db/models/deletion.py index 2d08da5..0493b0e 100644 --- a/venv/Lib/site-packages/django/db/models/deletion.py +++ b/venv/Lib/site-packages/django/db/models/deletion.py @@ -1,5 +1,6 @@ +import operator from collections import Counter, defaultdict -from functools import partial +from functools import partial, reduce from itertools import chain from operator import attrgetter @@ -21,11 +22,8 @@ class RestrictedError(IntegrityError): def CASCADE(collector, field, sub_objs, using): collector.collect( - sub_objs, - source=field.remote_field.model, - source_attr=field.name, - nullable=field.null, - fail_on_restricted=False, + sub_objs, source=field.remote_field.model, source_attr=field.name, + nullable=field.null, fail_on_restricted=False, ) if field.null and not connections[using].features.can_defer_constraint_checks: collector.add_field_update(field, None, sub_objs) @@ -34,13 +32,10 @@ def CASCADE(collector, field, sub_objs, using): def PROTECT(collector, field, sub_objs, using): raise ProtectedError( "Cannot delete some instances of model '%s' because they are " - "referenced through a protected foreign key: '%s.%s'" - % ( - field.remote_field.model.__name__, - sub_objs[0].__class__.__name__, - field.name, + "referenced through a protected foreign key: '%s.%s'" % ( + field.remote_field.model.__name__, sub_objs[0].__class__.__name__, field.name ), - sub_objs, + sub_objs ) @@ -51,16 +46,12 @@ def RESTRICT(collector, field, sub_objs, using): def SET(value): if callable(value): - def set_on_delete(collector, field, sub_objs, using): collector.add_field_update(field, value(), sub_objs) - else: - def set_on_delete(collector, field, sub_objs, using): collector.add_field_update(field, value, sub_objs) - - set_on_delete.deconstruct = lambda: ("django.db.models.SET", (value,), {}) + set_on_delete.deconstruct = lambda: ('django.db.models.SET', (value,), {}) return set_on_delete @@ -80,8 +71,7 @@ def get_candidate_relations_to_delete(opts): # The candidate relations are the ones that come from N-1 and 1-1 relations. # N-N (i.e., many-to-many) relations aren't candidates for deletion. return ( - f - for f in opts.get_fields(include_hidden=True) + f for f in opts.get_fields(include_hidden=True) if f.auto_created and not f.concrete and (f.one_to_one or f.one_to_many) ) @@ -133,9 +123,7 @@ class Collector: def add_dependency(self, model, dependency, reverse_dependency=False): if reverse_dependency: model, dependency = dependency, model - self.dependencies[model._meta.concrete_model].add( - dependency._meta.concrete_model - ) + self.dependencies[model._meta.concrete_model].add(dependency._meta.concrete_model) self.data.setdefault(dependency, self.data.default_factory()) def add_field_update(self, field, value, objs): @@ -162,21 +150,17 @@ class Collector: def clear_restricted_objects_from_queryset(self, model, qs): if model in self.restricted_objects: - objs = set( - qs.filter( - pk__in=[ - obj.pk - for objs in self.restricted_objects[model].values() - for obj in objs - ] - ) - ) + objs = set(qs.filter(pk__in=[ + obj.pk + for objs in self.restricted_objects[model].values() for obj in objs + ])) self.clear_restricted_objects_from_set(model, objs) def _has_signal_listeners(self, model): - return signals.pre_delete.has_listeners( - model - ) or signals.post_delete.has_listeners(model) + return ( + signals.pre_delete.has_listeners(model) or + signals.post_delete.has_listeners(model) + ) def can_fast_delete(self, objs, from_field=None): """ @@ -191,9 +175,9 @@ class Collector: """ if from_field and from_field.remote_field.on_delete is not CASCADE: return False - if hasattr(objs, "_meta"): + if hasattr(objs, '_meta'): model = objs._meta.model - elif hasattr(objs, "model") and hasattr(objs, "_raw_delete"): + elif hasattr(objs, 'model') and hasattr(objs, '_raw_delete'): model = objs.model else: return False @@ -203,22 +187,14 @@ class Collector: # parent when parent delete is cascading to child. opts = model._meta return ( - all( - link == from_field - for link in opts.concrete_model._meta.parents.values() - ) - and + all(link == from_field for link in opts.concrete_model._meta.parents.values()) and # Foreign keys pointing to this model. all( related.field.remote_field.on_delete is DO_NOTHING for related in get_candidate_relations_to_delete(opts) - ) - and ( + ) and ( # Something like generic foreign key. - not any( - hasattr(field, "bulk_related_objects") - for field in opts.private_fields - ) + not any(hasattr(field, 'bulk_related_objects') for field in opts.private_fields) ) ) @@ -228,27 +204,16 @@ class Collector: """ field_names = [field.name for field in fields] conn_batch_size = max( - connections[self.using].ops.bulk_batch_size(field_names, objs), 1 - ) + connections[self.using].ops.bulk_batch_size(field_names, objs), 1) if len(objs) > conn_batch_size: - return [ - objs[i : i + conn_batch_size] - for i in range(0, len(objs), conn_batch_size) - ] + return [objs[i:i + conn_batch_size] + for i in range(0, len(objs), conn_batch_size)] else: return [objs] - def collect( - self, - objs, - source=None, - nullable=False, - collect_related=True, - source_attr=None, - reverse_dependency=False, - keep_parents=False, - fail_on_restricted=True, - ): + def collect(self, objs, source=None, nullable=False, collect_related=True, + source_attr=None, reverse_dependency=False, keep_parents=False, + fail_on_restricted=True): """ Add 'objs' to the collection of objects to be deleted as well as all parent instances. 'objs' must be a homogeneous iterable collection of @@ -275,9 +240,8 @@ class Collector: if self.can_fast_delete(objs): self.fast_deletes.append(objs) return - new_objs = self.add( - objs, source, nullable, reverse_dependency=reverse_dependency - ) + new_objs = self.add(objs, source, nullable, + reverse_dependency=reverse_dependency) if not new_objs: return @@ -290,14 +254,11 @@ class Collector: for ptr in concrete_model._meta.parents.values(): if ptr: parent_objs = [getattr(obj, ptr.name) for obj in new_objs] - self.collect( - parent_objs, - source=model, - source_attr=ptr.remote_field.related_name, - collect_related=False, - reverse_dependency=True, - fail_on_restricted=False, - ) + self.collect(parent_objs, source=model, + source_attr=ptr.remote_field.related_name, + collect_related=False, + reverse_dependency=True, + fail_on_restricted=False) if not collect_related: return @@ -325,18 +286,11 @@ class Collector: # relationships are select_related as interactions between both # features are hard to get right. This should only happen in # the rare cases where .related_objects is overridden anyway. - if not ( - sub_objs.query.select_related - or self._has_signal_listeners(related_model) - ): - referenced_fields = set( - chain.from_iterable( - (rf.attname for rf in rel.field.foreign_related_fields) - for rel in get_candidate_relations_to_delete( - related_model._meta - ) - ) - ) + if not (sub_objs.query.select_related or self._has_signal_listeners(related_model)): + referenced_fields = set(chain.from_iterable( + (rf.attname for rf in rel.field.foreign_related_fields) + for rel in get_candidate_relations_to_delete(related_model._meta) + )) sub_objs = sub_objs.only(*tuple(referenced_fields)) if sub_objs: try: @@ -346,11 +300,10 @@ class Collector: protected_objects[key] += error.protected_objects if protected_objects: raise ProtectedError( - "Cannot delete some instances of model %r because they are " - "referenced through protected foreign keys: %s." - % ( + 'Cannot delete some instances of model %r because they are ' + 'referenced through protected foreign keys: %s.' % ( model.__name__, - ", ".join(protected_objects), + ', '.join(protected_objects), ), set(chain.from_iterable(protected_objects.values())), ) @@ -360,12 +313,10 @@ class Collector: sub_objs = self.related_objects(related_model, related_fields, batch) self.fast_deletes.append(sub_objs) for field in model._meta.private_fields: - if hasattr(field, "bulk_related_objects"): + if hasattr(field, 'bulk_related_objects'): # It's something like generic foreign key. sub_objs = field.bulk_related_objects(new_objs, self.using) - self.collect( - sub_objs, source=model, nullable=True, fail_on_restricted=False - ) + self.collect(sub_objs, source=model, nullable=True, fail_on_restricted=False) if fail_on_restricted: # Raise an error if collected restricted objects (RESTRICT) aren't @@ -383,12 +334,11 @@ class Collector: restricted_objects[key] += objs if restricted_objects: raise RestrictedError( - "Cannot delete some instances of model %r because " - "they are referenced through restricted foreign keys: " - "%s." - % ( + 'Cannot delete some instances of model %r because ' + 'they are referenced through restricted foreign keys: ' + '%s.' % ( model.__name__, - ", ".join(restricted_objects), + ', '.join(restricted_objects), ), set(chain.from_iterable(restricted_objects.values())), ) @@ -397,10 +347,10 @@ class Collector: """ Get a QuerySet of the related model to objs via related fields. """ - predicate = query_utils.Q( - *((f"{related_field.name}__in", objs) for related_field in related_fields), - _connector=query_utils.Q.OR, - ) + predicate = reduce(operator.or_, ( + query_utils.Q(**{'%s__in' % related_field.name: objs}) + for related_field in related_fields + )) return related_model._base_manager.using(self.using).filter(predicate) def instances_with_model(self): @@ -443,9 +393,7 @@ class Collector: instance = list(instances)[0] if self.can_fast_delete(instance): with transaction.mark_for_rollback_on_error(self.using): - count = sql.DeleteQuery(model).delete_batch( - [instance.pk], self.using - ) + count = sql.DeleteQuery(model).delete_batch([instance.pk], self.using) setattr(instance, model._meta.pk.attname, None) return count, {model._meta.label: count} @@ -467,9 +415,8 @@ class Collector: for model, instances_for_fieldvalues in self.field_updates.items(): for (field, value), instances in instances_for_fieldvalues.items(): query = sql.UpdateQuery(model) - query.update_batch( - [obj.pk for obj in instances], {field.name: value}, self.using - ) + query.update_batch([obj.pk for obj in instances], + {field.name: value}, self.using) # reverse instance collections for instances in self.data.values(): diff --git a/venv/Lib/site-packages/django/db/models/enums.py b/venv/Lib/site-packages/django/db/models/enums.py index 9a7a2bb..f04698b 100644 --- a/venv/Lib/site-packages/django/db/models/enums.py +++ b/venv/Lib/site-packages/django/db/models/enums.py @@ -1,9 +1,8 @@ import enum -from types import DynamicClassAttribute from django.utils.functional import Promise -__all__ = ["Choices", "IntegerChoices", "TextChoices"] +__all__ = ['Choices', 'IntegerChoices', 'TextChoices'] class ChoicesMeta(enum.EnumMeta): @@ -14,21 +13,25 @@ class ChoicesMeta(enum.EnumMeta): for key in classdict._member_names: value = classdict[key] if ( - isinstance(value, (list, tuple)) - and len(value) > 1 - and isinstance(value[-1], (Promise, str)) + isinstance(value, (list, tuple)) and + len(value) > 1 and + isinstance(value[-1], (Promise, str)) ): *value, label = value value = tuple(value) else: - label = key.replace("_", " ").title() + label = key.replace('_', ' ').title() labels.append(label) # Use dict.__setitem__() to suppress defenses against double # assignment in enum's classdict. dict.__setitem__(classdict, key, value) cls = super().__new__(metacls, classname, bases, classdict, **kwds) - for member, label in zip(cls.__members__.values(), labels): - member._label_ = label + cls._value2label_map_ = dict(zip(cls._value2member_map_, labels)) + # Add a label property to instances of enum which uses the enum member + # that is passed in as "self" as the value to use when looking up the + # label in the choices. + cls.label = property(lambda self: cls._value2label_map_.get(self.value)) + cls.do_not_call_in_templates = True return enum.unique(cls) def __contains__(cls, member): @@ -39,12 +42,12 @@ class ChoicesMeta(enum.EnumMeta): @property def names(cls): - empty = ["__empty__"] if hasattr(cls, "__empty__") else [] + empty = ['__empty__'] if hasattr(cls, '__empty__') else [] return empty + [member.name for member in cls] @property def choices(cls): - empty = [(None, cls.__empty__)] if hasattr(cls, "__empty__") else [] + empty = [(None, cls.__empty__)] if hasattr(cls, '__empty__') else [] return empty + [(member.value, member.label) for member in cls] @property @@ -59,14 +62,6 @@ class ChoicesMeta(enum.EnumMeta): class Choices(enum.Enum, metaclass=ChoicesMeta): """Class for creating enumerated choices.""" - @DynamicClassAttribute - def label(self): - return self._label_ - - @property - def do_not_call_in_templates(self): - return True - def __str__(self): """ Use value when cast to str, so that Choices set as model instance @@ -74,14 +69,9 @@ class Choices(enum.Enum, metaclass=ChoicesMeta): """ return str(self.value) - # A similar format was proposed for Python 3.10. - def __repr__(self): - return f"{self.__class__.__qualname__}.{self._name_}" - class IntegerChoices(int, Choices): """Class for creating enumerated integer choices.""" - pass diff --git a/venv/Lib/site-packages/django/db/models/expressions.py b/venv/Lib/site-packages/django/db/models/expressions.py index 4c5f132..08ee5fe 100644 --- a/venv/Lib/site-packages/django/db/models/expressions.py +++ b/venv/Lib/site-packages/django/db/models/expressions.py @@ -6,7 +6,7 @@ from decimal import Decimal from uuid import UUID from django.core.exceptions import EmptyResultSet, FieldError -from django.db import DatabaseError, NotSupportedError, connection +from django.db import NotSupportedError, connection from django.db.models import fields from django.db.models.constants import LOOKUP_SEP from django.db.models.query_utils import Q @@ -20,12 +20,11 @@ class SQLiteNumericMixin: Some expressions with output_field=DecimalField() must be cast to numeric to be properly filtered. """ - def as_sqlite(self, compiler, connection, **extra_context): sql, params = self.as_sql(compiler, connection, **extra_context) try: - if self.output_field.get_internal_type() == "DecimalField": - sql = "CAST(%s AS NUMERIC)" % sql + if self.output_field.get_internal_type() == 'DecimalField': + sql = 'CAST(%s AS NUMERIC)' % sql except FieldError: pass return sql, params @@ -38,26 +37,26 @@ class Combinable: """ # Arithmetic connectors - ADD = "+" - SUB = "-" - MUL = "*" - DIV = "/" - POW = "^" + ADD = '+' + SUB = '-' + MUL = '*' + DIV = '/' + POW = '^' # The following is a quoted % operator - it is quoted because it can be # used in strings that also have parameter substitution. - MOD = "%%" + MOD = '%%' # Bitwise operators - note that these are generated by .bitand() # and .bitor(), the '&' and '|' are reserved for boolean operator # usage. - BITAND = "&" - BITOR = "|" - BITLEFTSHIFT = "<<" - BITRIGHTSHIFT = ">>" - BITXOR = "#" + BITAND = '&' + BITOR = '|' + BITLEFTSHIFT = '<<' + BITRIGHTSHIFT = '>>' + BITXOR = '#' def _combine(self, other, connector, reversed): - if not hasattr(other, "resolve_expression"): + if not hasattr(other, 'resolve_expression'): # everything must be resolvable to an expression other = Value(other) @@ -91,7 +90,7 @@ class Combinable: return self._combine(other, self.POW, False) def __and__(self, other): - if getattr(self, "conditional", False) and getattr(other, "conditional", False): + if getattr(self, 'conditional', False) and getattr(other, 'conditional', False): return Q(self) & Q(other) raise NotImplementedError( "Use .bitand() and .bitor() for bitwise logical operations." @@ -110,7 +109,7 @@ class Combinable: return self._combine(other, self.BITXOR, False) def __or__(self, other): - if getattr(self, "conditional", False) and getattr(other, "conditional", False): + if getattr(self, 'conditional', False) and getattr(other, 'conditional', False): return Q(self) | Q(other) raise NotImplementedError( "Use .bitand() and .bitor() for bitwise logical operations." @@ -151,7 +150,6 @@ class Combinable: class BaseExpression: """Base class for all query expressions.""" - empty_result_set_value = NotImplemented # aggregate specific fields is_summary = False _output_field_resolved_to_none = False @@ -166,14 +164,14 @@ class BaseExpression: def __getstate__(self): state = self.__dict__.copy() - state.pop("convert_value", None) + state.pop('convert_value', None) return state def get_db_converters(self, connection): return ( [] - if self.convert_value is self._convert_value_noop - else [self.convert_value] + if self.convert_value is self._convert_value_noop else + [self.convert_value] ) + self.output_field.get_db_converters(connection) def get_source_expressions(self): @@ -184,10 +182,9 @@ class BaseExpression: def _parse_expressions(self, *expressions): return [ - arg - if hasattr(arg, "resolve_expression") - else (F(arg) if isinstance(arg, str) else Value(arg)) - for arg in expressions + arg if hasattr(arg, 'resolve_expression') else ( + F(arg) if isinstance(arg, str) else Value(arg) + ) for arg in expressions ] def as_sql(self, compiler, connection): @@ -220,26 +217,17 @@ class BaseExpression: @cached_property def contains_aggregate(self): - return any( - expr and expr.contains_aggregate for expr in self.get_source_expressions() - ) + return any(expr and expr.contains_aggregate for expr in self.get_source_expressions()) @cached_property def contains_over_clause(self): - return any( - expr and expr.contains_over_clause for expr in self.get_source_expressions() - ) + return any(expr and expr.contains_over_clause for expr in self.get_source_expressions()) @cached_property def contains_column_references(self): - return any( - expr and expr.contains_column_references - for expr in self.get_source_expressions() - ) + return any(expr and expr.contains_column_references for expr in self.get_source_expressions()) - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): """ Provide the chance to do any preprocessing or validation before being added to the query. @@ -256,14 +244,11 @@ class BaseExpression: """ c = self.copy() c.is_summary = summarize - c.set_source_expressions( - [ - expr.resolve_expression(query, allow_joins, reuse, summarize) - if expr - else None - for expr in c.get_source_expressions() - ] - ) + c.set_source_expressions([ + expr.resolve_expression(query, allow_joins, reuse, summarize) + if expr else None + for expr in c.get_source_expressions() + ]) return c @property @@ -280,7 +265,7 @@ class BaseExpression: output_field = self._resolve_output_field() if output_field is None: self._output_field_resolved_to_none = True - raise FieldError("Cannot resolve expression type, unknown output_field") + raise FieldError('Cannot resolve expression type, unknown output_field') return output_field @cached_property @@ -309,16 +294,13 @@ class BaseExpression: If all sources are None, then an error is raised higher up the stack in the output_field property. """ - sources_iter = ( - source for source in self.get_source_fields() if source is not None - ) + sources_iter = (source for source in self.get_source_fields() if source is not None) for output_field in sources_iter: for source in sources_iter: if not isinstance(output_field, source.__class__): raise FieldError( - "Expression contains mixed types: %s, %s. You must " - "set output_field." - % ( + 'Expression contains mixed types: %s, %s. You must ' + 'set output_field.' % ( output_field.__class__.__name__, source.__class__.__name__, ) @@ -338,24 +320,12 @@ class BaseExpression: """ field = self.output_field internal_type = field.get_internal_type() - if internal_type == "FloatField": - return ( - lambda value, expression, connection: None - if value is None - else float(value) - ) - elif internal_type.endswith("IntegerField"): - return ( - lambda value, expression, connection: None - if value is None - else int(value) - ) - elif internal_type == "DecimalField": - return ( - lambda value, expression, connection: None - if value is None - else Decimal(value) - ) + if internal_type == 'FloatField': + return lambda value, expression, connection: None if value is None else float(value) + elif internal_type.endswith('IntegerField'): + return lambda value, expression, connection: None if value is None else int(value) + elif internal_type == 'DecimalField': + return lambda value, expression, connection: None if value is None else Decimal(value) return self._convert_value_noop def get_lookup(self, lookup): @@ -366,12 +336,10 @@ class BaseExpression: def relabeled_clone(self, change_map): clone = self.copy() - clone.set_source_expressions( - [ - e.relabeled_clone(change_map) if e is not None else None - for e in self.get_source_expressions() - ] - ) + clone.set_source_expressions([ + e.relabeled_clone(change_map) if e is not None else None + for e in self.get_source_expressions() + ]) return clone def copy(self): @@ -406,7 +374,7 @@ class BaseExpression: yield self for expr in self.get_source_expressions(): if expr: - if hasattr(expr, "flatten"): + if hasattr(expr, 'flatten'): yield from expr.flatten() else: yield expr @@ -416,7 +384,7 @@ class BaseExpression: Custom format for select clauses. For example, EXISTS expressions need to be wrapped in CASE WHEN on Oracle. """ - if hasattr(self.output_field, "select_format"): + if hasattr(self.output_field, 'select_format'): return self.output_field.select_format(compiler, sql, params) return sql, params @@ -469,13 +437,12 @@ _connector_combinators = { def _resolve_combined_type(connector, lhs_type, rhs_type): combinators = _connector_combinators.get(connector, ()) for combinator_lhs_type, combinator_rhs_type, combined_type in combinators: - if issubclass(lhs_type, combinator_lhs_type) and issubclass( - rhs_type, combinator_rhs_type - ): + if issubclass(lhs_type, combinator_lhs_type) and issubclass(rhs_type, combinator_rhs_type): return combined_type class CombinedExpression(SQLiteNumericMixin, Expression): + def __init__(self, lhs, connector, rhs, output_field=None): super().__init__(output_field=output_field) self.connector = connector @@ -517,19 +484,13 @@ class CombinedExpression(SQLiteNumericMixin, Expression): expressions.append(sql) expression_params.extend(params) # order of precedence - expression_wrapper = "(%s)" + expression_wrapper = '(%s)' sql = connection.ops.combine_expression(self.connector, expressions) return expression_wrapper % sql, expression_params - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): - lhs = self.lhs.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) - rhs = self.rhs.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): + lhs = self.lhs.resolve_expression(query, allow_joins, reuse, summarize, for_save) + rhs = self.rhs.resolve_expression(query, allow_joins, reuse, summarize, for_save) if not isinstance(self, (DurationExpression, TemporalSubtraction)): try: lhs_type = lhs.output_field.get_internal_type() @@ -539,28 +500,14 @@ class CombinedExpression(SQLiteNumericMixin, Expression): rhs_type = rhs.output_field.get_internal_type() except (AttributeError, FieldError): rhs_type = None - if "DurationField" in {lhs_type, rhs_type} and lhs_type != rhs_type: - return DurationExpression( - self.lhs, self.connector, self.rhs - ).resolve_expression( - query, - allow_joins, - reuse, - summarize, - for_save, + if 'DurationField' in {lhs_type, rhs_type} and lhs_type != rhs_type: + return DurationExpression(self.lhs, self.connector, self.rhs).resolve_expression( + query, allow_joins, reuse, summarize, for_save, ) - datetime_fields = {"DateField", "DateTimeField", "TimeField"} - if ( - self.connector == self.SUB - and lhs_type in datetime_fields - and lhs_type == rhs_type - ): + datetime_fields = {'DateField', 'DateTimeField', 'TimeField'} + if self.connector == self.SUB and lhs_type in datetime_fields and lhs_type == rhs_type: return TemporalSubtraction(self.lhs, self.rhs).resolve_expression( - query, - allow_joins, - reuse, - summarize, - for_save, + query, allow_joins, reuse, summarize, for_save, ) c = self.copy() c.is_summary = summarize @@ -576,7 +523,7 @@ class DurationExpression(CombinedExpression): except FieldError: pass else: - if output.get_internal_type() == "DurationField": + if output.get_internal_type() == 'DurationField': sql, params = compiler.compile(side) return connection.ops.format_for_duration_arithmetic(sql), params return compiler.compile(side) @@ -594,31 +541,10 @@ class DurationExpression(CombinedExpression): expressions.append(sql) expression_params.extend(params) # order of precedence - expression_wrapper = "(%s)" + expression_wrapper = '(%s)' sql = connection.ops.combine_duration_expression(self.connector, expressions) return expression_wrapper % sql, expression_params - def as_sqlite(self, compiler, connection, **extra_context): - sql, params = self.as_sql(compiler, connection, **extra_context) - if self.connector in {Combinable.MUL, Combinable.DIV}: - try: - lhs_type = self.lhs.output_field.get_internal_type() - rhs_type = self.rhs.output_field.get_internal_type() - except (AttributeError, FieldError): - pass - else: - allowed_fields = { - "DecimalField", - "DurationField", - "FloatField", - "IntegerField", - } - if lhs_type not in allowed_fields or rhs_type not in allowed_fields: - raise DatabaseError( - f"Invalid arguments for operator {self.connector}." - ) - return sql, params - class TemporalSubtraction(CombinedExpression): output_field = fields.DurationField() @@ -630,9 +556,7 @@ class TemporalSubtraction(CombinedExpression): connection.ops.check_expression_support(self) lhs = compiler.compile(self.lhs) rhs = compiler.compile(self.rhs) - return connection.ops.subtract_temporals( - self.lhs.output_field.get_internal_type(), lhs, rhs - ) + return connection.ops.subtract_temporals(self.lhs.output_field.get_internal_type(), lhs, rhs) @deconstructible @@ -649,9 +573,8 @@ class F(Combinable): def __repr__(self): return "{}({})".format(self.__class__.__name__, self.name) - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, + summarize=False, for_save=False): return query.resolve_ref(self.name, allow_joins, reuse, summarize) def asc(self, **kwargs): @@ -674,13 +597,12 @@ class ResolvedOuterRef(F): In this case, the reference to the outer query has been resolved because the inner query has been used as a subquery. """ - contains_aggregate = False def as_sql(self, *args, **kwargs): raise ValueError( - "This queryset contains a reference to an outer query and may " - "only be used in a subquery." + 'This queryset contains a reference to an outer query and may ' + 'only be used in a subquery.' ) def resolve_expression(self, *args, **kwargs): @@ -712,17 +634,15 @@ class OuterRef(F): class Func(SQLiteNumericMixin, Expression): """An SQL function call.""" - function = None - template = "%(function)s(%(expressions)s)" - arg_joiner = ", " + template = '%(function)s(%(expressions)s)' + arg_joiner = ', ' arity = None # The number of arguments the function accepts. def __init__(self, *expressions, output_field=None, **extra): if self.arity is not None and len(expressions) != self.arity: raise TypeError( - "'%s' takes exactly %s %s (%s given)" - % ( + "'%s' takes exactly %s %s (%s given)" % ( self.__class__.__name__, self.arity, "argument" if self.arity == 1 else "arguments", @@ -737,9 +657,7 @@ class Func(SQLiteNumericMixin, Expression): args = self.arg_joiner.join(str(arg) for arg in self.source_expressions) extra = {**self.extra, **self._get_repr_options()} if extra: - extra = ", ".join( - str(key) + "=" + str(val) for key, val in sorted(extra.items()) - ) + extra = ', '.join(str(key) + '=' + str(val) for key, val in sorted(extra.items())) return "{}({}, {})".format(self.__class__.__name__, args, extra) return "{}({})".format(self.__class__.__name__, args) @@ -753,39 +671,19 @@ class Func(SQLiteNumericMixin, Expression): def set_source_expressions(self, exprs): self.source_expressions = exprs - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): c = self.copy() c.is_summary = summarize for pos, arg in enumerate(c.source_expressions): - c.source_expressions[pos] = arg.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + c.source_expressions[pos] = arg.resolve_expression(query, allow_joins, reuse, summarize, for_save) return c - def as_sql( - self, - compiler, - connection, - function=None, - template=None, - arg_joiner=None, - **extra_context, - ): + def as_sql(self, compiler, connection, function=None, template=None, arg_joiner=None, **extra_context): connection.ops.check_expression_support(self) sql_parts = [] params = [] for arg in self.source_expressions: - try: - arg_sql, arg_params = compiler.compile(arg) - except EmptyResultSet: - empty_result_set_value = getattr( - arg, "empty_result_set_value", NotImplemented - ) - if empty_result_set_value is NotImplemented: - raise - arg_sql, arg_params = compiler.compile(Value(empty_result_set_value)) + arg_sql, arg_params = compiler.compile(arg) sql_parts.append(arg_sql) params.extend(arg_params) data = {**self.extra, **extra_context} @@ -793,12 +691,12 @@ class Func(SQLiteNumericMixin, Expression): # method, a value supplied in __init__()'s **extra (the value in # `data`), or the value defined on the class. if function is not None: - data["function"] = function + data['function'] = function else: - data.setdefault("function", self.function) - template = template or data.get("template", self.template) - arg_joiner = arg_joiner or data.get("arg_joiner", self.arg_joiner) - data["expressions"] = data["field"] = arg_joiner.join(sql_parts) + data.setdefault('function', self.function) + template = template or data.get('template', self.template) + arg_joiner = arg_joiner or data.get('arg_joiner', self.arg_joiner) + data['expressions'] = data['field'] = arg_joiner.join(sql_parts) return template % data, params def copy(self): @@ -808,9 +706,8 @@ class Func(SQLiteNumericMixin, Expression): return copy -class Value(SQLiteNumericMixin, Expression): +class Value(Expression): """Represent a wrapped value as a node within an expression.""" - # Provide a default value for `for_save` in order to allow unresolved # instances to be compiled until a decision is taken in #25425. for_save = False @@ -828,7 +725,7 @@ class Value(SQLiteNumericMixin, Expression): self.value = value def __repr__(self): - return f"{self.__class__.__name__}({self.value!r})" + return "{}({})".format(self.__class__.__name__, self.value) def as_sql(self, compiler, connection): connection.ops.check_expression_support(self) @@ -839,18 +736,16 @@ class Value(SQLiteNumericMixin, Expression): val = output_field.get_db_prep_save(val, connection=connection) else: val = output_field.get_db_prep_value(val, connection=connection) - if hasattr(output_field, "get_placeholder"): + if hasattr(output_field, 'get_placeholder'): return output_field.get_placeholder(val, compiler, connection), [val] if val is None: # cx_Oracle does not always convert None to the appropriate # NULL type (like in case expressions using numbers), so we # use a literal SQL NULL - return "NULL", [] - return "%s", [val] + return 'NULL', [] + return '%s', [val] - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) c.for_save = for_save return c @@ -882,10 +777,6 @@ class Value(SQLiteNumericMixin, Expression): if isinstance(self.value, UUID): return fields.UUIDField() - @property - def empty_result_set_value(self): - return self.value - class RawSQL(Expression): def __init__(self, sql, params, output_field=None): @@ -898,14 +789,12 @@ class RawSQL(Expression): return "{}({}, {})".format(self.__class__.__name__, self.sql, self.params) def as_sql(self, compiler, connection): - return "(%s)" % self.sql, self.params + return '(%s)' % self.sql, self.params def get_group_by_cols(self, alias=None): return [self] - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): # Resolve parents fields used in raw SQL. for parent in query.model._meta.get_parent_list(): for parent_field in parent._meta.local_fields: @@ -913,9 +802,7 @@ class RawSQL(Expression): if column_name.lower() in self.sql.lower(): query.resolve_ref(parent_field.name, allow_joins, reuse, summarize) break - return super().resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + return super().resolve_expression(query, allow_joins, reuse, summarize, for_save) class Star(Expression): @@ -923,7 +810,7 @@ class Star(Expression): return "'*'" def as_sql(self, compiler, connection): - return "*", [] + return '*', [] class Col(Expression): @@ -940,20 +827,18 @@ class Col(Expression): def __repr__(self): alias, target = self.alias, self.target identifiers = (alias, str(target)) if alias else (str(target),) - return "{}({})".format(self.__class__.__name__, ", ".join(identifiers)) + return '{}({})'.format(self.__class__.__name__, ', '.join(identifiers)) def as_sql(self, compiler, connection): alias, column = self.alias, self.target.column identifiers = (alias, column) if alias else (column,) - sql = ".".join(map(compiler.quote_name_unless_alias, identifiers)) + sql = '.'.join(map(compiler.quote_name_unless_alias, identifiers)) return sql, [] def relabeled_clone(self, relabels): if self.alias is None: return self - return self.__class__( - relabels.get(self.alias, self.alias), self.target, self.output_field - ) + return self.__class__(relabels.get(self.alias, self.alias), self.target, self.output_field) def get_group_by_cols(self, alias=None): return [self] @@ -961,9 +846,8 @@ class Col(Expression): def get_db_converters(self, connection): if self.target == self.output_field: return self.output_field.get_db_converters(connection) - return self.output_field.get_db_converters( - connection - ) + self.target.get_db_converters(connection) + return (self.output_field.get_db_converters(connection) + + self.target.get_db_converters(connection)) class Ref(Expression): @@ -971,7 +855,6 @@ class Ref(Expression): Reference to column alias of the query. For example, Ref('sum_cost') in qs.annotate(sum_cost=Sum('cost')) query. """ - def __init__(self, refs, source): super().__init__() self.refs, self.source = refs, source @@ -983,11 +866,9 @@ class Ref(Expression): return [self.source] def set_source_expressions(self, exprs): - (self.source,) = exprs + self.source, = exprs - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): # The sub-expression `source` has already been resolved, as this is # just a reference to the name of `source`. return self @@ -1008,14 +889,11 @@ class ExpressionList(Func): list of expressions as an argument to another expression, like an ordering clause. """ - - template = "%(expressions)s" + template = '%(expressions)s' def __init__(self, *expressions, **extra): if not expressions: - raise ValueError( - "%s requires at least one expression." % self.__class__.__name__ - ) + raise ValueError('%s requires at least one expression.' % self.__class__.__name__) super().__init__(*expressions, **extra) def __str__(self): @@ -1059,7 +937,7 @@ class ExpressionWrapper(Expression): class When(Expression): - template = "WHEN %(condition)s THEN %(result)s" + template = 'WHEN %(condition)s THEN %(result)s' # This isn't a complete conditional expression, must be used in Case(). conditional = False @@ -1067,12 +945,12 @@ class When(Expression): if lookups: if condition is None: condition, lookups = Q(**lookups), None - elif getattr(condition, "conditional", False): + elif getattr(condition, 'conditional', False): condition, lookups = Q(condition, **lookups), None - if condition is None or not getattr(condition, "conditional", False) or lookups: + if condition is None or not getattr(condition, 'conditional', False) or lookups: raise TypeError( - "When() supports a Q object, a boolean expression, or lookups " - "as a condition." + 'When() supports a Q object, a boolean expression, or lookups ' + 'as a condition.' ) if isinstance(condition, Q) and not condition: raise ValueError("An empty Q() can't be used as a When() condition.") @@ -1096,18 +974,12 @@ class When(Expression): # We're only interested in the fields of the result expressions. return [self.result._output_field_or_none] - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): c = self.copy() c.is_summary = summarize - if hasattr(c.condition, "resolve_expression"): - c.condition = c.condition.resolve_expression( - query, allow_joins, reuse, summarize, False - ) - c.result = c.result.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + if hasattr(c.condition, 'resolve_expression'): + c.condition = c.condition.resolve_expression(query, allow_joins, reuse, summarize, False) + c.result = c.result.resolve_expression(query, allow_joins, reuse, summarize, for_save) return c def as_sql(self, compiler, connection, template=None, **extra_context): @@ -1115,10 +987,10 @@ class When(Expression): template_params = extra_context sql_params = [] condition_sql, condition_params = compiler.compile(self.condition) - template_params["condition"] = condition_sql + template_params['condition'] = condition_sql sql_params.extend(condition_params) result_sql, result_params = compiler.compile(self.result) - template_params["result"] = result_sql + template_params['result'] = result_sql sql_params.extend(result_params) template = template or self.template return template % template_params, sql_params @@ -1143,9 +1015,8 @@ class Case(Expression): ELSE 'zero' END """ - - template = "CASE %(cases)s ELSE %(default)s END" - case_joiner = " " + template = 'CASE %(cases)s ELSE %(default)s END' + case_joiner = ' ' def __init__(self, *cases, default=None, output_field=None, **extra): if not all(isinstance(case, When) for case in cases): @@ -1156,10 +1027,7 @@ class Case(Expression): self.extra = extra def __str__(self): - return "CASE %s, ELSE %r" % ( - ", ".join(str(c) for c in self.cases), - self.default, - ) + return "CASE %s, ELSE %r" % (', '.join(str(c) for c in self.cases), self.default) def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self) @@ -1170,18 +1038,12 @@ class Case(Expression): def set_source_expressions(self, exprs): *self.cases, self.default = exprs - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): c = self.copy() c.is_summary = summarize for pos, case in enumerate(c.cases): - c.cases[pos] = case.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) - c.default = c.default.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + c.cases[pos] = case.resolve_expression(query, allow_joins, reuse, summarize, for_save) + c.default = c.default.resolve_expression(query, allow_joins, reuse, summarize, for_save) return c def copy(self): @@ -1189,9 +1051,7 @@ class Case(Expression): c.cases = c.cases[:] return c - def as_sql( - self, compiler, connection, template=None, case_joiner=None, **extra_context - ): + def as_sql(self, compiler, connection, template=None, case_joiner=None, **extra_context): connection.ops.check_expression_support(self) if not self.cases: return compiler.compile(self.default) @@ -1209,10 +1069,10 @@ class Case(Expression): if not case_parts: return default_sql, default_params case_joiner = case_joiner or self.case_joiner - template_params["cases"] = case_joiner.join(case_parts) - template_params["default"] = default_sql + template_params['cases'] = case_joiner.join(case_parts) + template_params['default'] = default_sql sql_params.extend(default_params) - template = template or template_params.get("template", self.template) + template = template or template_params.get('template', self.template) sql = template % template_params if self._output_field_or_none is not None: sql = connection.ops.unification_cast_sql(self.output_field) % sql @@ -1229,14 +1089,12 @@ class Subquery(BaseExpression, Combinable): An explicit subquery. It may contain OuterRef() references to the outer query which will be resolved when it is applied to that query. """ - - template = "(%(subquery)s)" + template = '(%(subquery)s)' contains_aggregate = False - empty_result_set_value = None def __init__(self, queryset, output_field=None, **extra): # Allow the usage of both QuerySet and sql.Query objects. - self.query = getattr(queryset, "query", queryset) + self.query = getattr(queryset, 'query', queryset) self.extra = extra super().__init__(output_field) @@ -1266,9 +1124,9 @@ class Subquery(BaseExpression, Combinable): template_params = {**self.extra, **extra_context} query = query or self.query subquery_sql, sql_params = query.as_sql(compiler, connection) - template_params["subquery"] = subquery_sql[1:-1] + template_params['subquery'] = subquery_sql[1:-1] - template = template or template_params.get("template", self.template) + template = template or template_params.get('template', self.template) sql = template % template_params return sql, sql_params @@ -1282,7 +1140,7 @@ class Subquery(BaseExpression, Combinable): class Exists(Subquery): - template = "EXISTS(%(subquery)s)" + template = 'EXISTS(%(subquery)s)' output_field = fields.BooleanField() def __init__(self, queryset, negated=False, **kwargs): @@ -1304,7 +1162,7 @@ class Exists(Subquery): **extra_context, ) if self.negated: - sql = "NOT {}".format(sql) + sql = 'NOT {}'.format(sql) return sql, params def select_format(self, compiler, sql, params): @@ -1312,30 +1170,27 @@ class Exists(Subquery): # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP # BY list. if not compiler.connection.features.supports_boolean_expr_in_select_clause: - sql = "CASE WHEN {} THEN 1 ELSE 0 END".format(sql) + sql = 'CASE WHEN {} THEN 1 ELSE 0 END'.format(sql) return sql, params class OrderBy(Expression): - template = "%(expression)s %(ordering)s" + template = '%(expression)s %(ordering)s' conditional = False - def __init__( - self, expression, descending=False, nulls_first=False, nulls_last=False - ): + def __init__(self, expression, descending=False, nulls_first=False, nulls_last=False): if nulls_first and nulls_last: - raise ValueError("nulls_first and nulls_last are mutually exclusive") + raise ValueError('nulls_first and nulls_last are mutually exclusive') self.nulls_first = nulls_first self.nulls_last = nulls_last self.descending = descending - if not hasattr(expression, "resolve_expression"): - raise ValueError("expression must be an expression type") + if not hasattr(expression, 'resolve_expression'): + raise ValueError('expression must be an expression type') self.expression = expression def __repr__(self): return "{}({}, descending={})".format( - self.__class__.__name__, self.expression, self.descending - ) + self.__class__.__name__, self.expression, self.descending) def set_source_expressions(self, exprs): self.expression = exprs[0] @@ -1347,34 +1202,33 @@ class OrderBy(Expression): template = template or self.template if connection.features.supports_order_by_nulls_modifier: if self.nulls_last: - template = "%s NULLS LAST" % template + template = '%s NULLS LAST' % template elif self.nulls_first: - template = "%s NULLS FIRST" % template + template = '%s NULLS FIRST' % template else: if self.nulls_last and not ( self.descending and connection.features.order_by_nulls_first ): - template = "%%(expression)s IS NULL, %s" % template + template = '%%(expression)s IS NULL, %s' % template elif self.nulls_first and not ( not self.descending and connection.features.order_by_nulls_first ): - template = "%%(expression)s IS NOT NULL, %s" % template + template = '%%(expression)s IS NOT NULL, %s' % template connection.ops.check_expression_support(self) expression_sql, params = compiler.compile(self.expression) placeholders = { - "expression": expression_sql, - "ordering": "DESC" if self.descending else "ASC", + 'expression': expression_sql, + 'ordering': 'DESC' if self.descending else 'ASC', **extra_context, } - params *= template.count("%(expression)s") + template = template or self.template + params *= template.count('%(expression)s') return (template % placeholders).rstrip(), params def as_oracle(self, compiler, connection): - # Oracle doesn't allow ORDER BY EXISTS() or filters unless it's wrapped - # in a CASE WHEN. - if connection.ops.conditional_expression_supported_in_where_clause( - self.expression - ): + # Oracle doesn't allow ORDER BY EXISTS() unless it's wrapped in + # a CASE WHEN. + if isinstance(self.expression, Exists): copy = self.copy() copy.expression = Case( When(self.expression, then=True), @@ -1404,7 +1258,7 @@ class OrderBy(Expression): class Window(SQLiteNumericMixin, Expression): - template = "%(expression)s OVER (%(window)s)" + template = '%(expression)s OVER (%(window)s)' # Although the main expression may either be an aggregate or an # expression with an aggregate function, the GROUP BY that will # be introduced in the query as a result is not desired. @@ -1412,22 +1266,15 @@ class Window(SQLiteNumericMixin, Expression): contains_over_clause = True filterable = False - def __init__( - self, - expression, - partition_by=None, - order_by=None, - frame=None, - output_field=None, - ): + def __init__(self, expression, partition_by=None, order_by=None, frame=None, output_field=None): self.partition_by = partition_by self.order_by = order_by self.frame = frame - if not getattr(expression, "window_compatible", False): + if not getattr(expression, 'window_compatible', False): raise ValueError( - "Expression '%s' isn't compatible with OVER clauses." - % expression.__class__.__name__ + "Expression '%s' isn't compatible with OVER clauses." % + expression.__class__.__name__ ) if self.partition_by is not None: @@ -1440,8 +1287,8 @@ class Window(SQLiteNumericMixin, Expression): self.order_by = ExpressionList(*self.order_by) elif not isinstance(self.order_by, BaseExpression): raise ValueError( - "order_by must be either an Expression or a sequence of " - "expressions." + 'order_by must be either an Expression or a sequence of ' + 'expressions.' ) super().__init__(output_field=output_field) self.source_expression = self._parse_expressions(expression)[0] @@ -1458,37 +1305,36 @@ class Window(SQLiteNumericMixin, Expression): def as_sql(self, compiler, connection, template=None): connection.ops.check_expression_support(self) if not connection.features.supports_over_clause: - raise NotSupportedError("This backend does not support window expressions.") + raise NotSupportedError('This backend does not support window expressions.') expr_sql, params = compiler.compile(self.source_expression) window_sql, window_params = [], [] if self.partition_by is not None: sql_expr, sql_params = self.partition_by.as_sql( - compiler=compiler, - connection=connection, - template="PARTITION BY %(expressions)s", + compiler=compiler, connection=connection, + template='PARTITION BY %(expressions)s', ) window_sql.extend(sql_expr) window_params.extend(sql_params) if self.order_by is not None: - window_sql.append(" ORDER BY ") + window_sql.append(' ORDER BY ') order_sql, order_params = compiler.compile(self.order_by) window_sql.extend(order_sql) window_params.extend(order_params) if self.frame: frame_sql, frame_params = compiler.compile(self.frame) - window_sql.append(" " + frame_sql) + window_sql.append(' ' + frame_sql) window_params.extend(frame_params) params.extend(window_params) template = template or self.template - return ( - template % {"expression": expr_sql, "window": "".join(window_sql).strip()}, - params, - ) + return template % { + 'expression': expr_sql, + 'window': ''.join(window_sql).strip() + }, params def as_sqlite(self, compiler, connection): if isinstance(self.output_field, fields.DecimalField): @@ -1501,15 +1347,15 @@ class Window(SQLiteNumericMixin, Expression): return self.as_sql(compiler, connection) def __str__(self): - return "{} OVER ({}{}{})".format( + return '{} OVER ({}{}{})'.format( str(self.source_expression), - "PARTITION BY " + str(self.partition_by) if self.partition_by else "", - "ORDER BY " + str(self.order_by) if self.order_by else "", - str(self.frame or ""), + 'PARTITION BY ' + str(self.partition_by) if self.partition_by else '', + 'ORDER BY ' + str(self.order_by) if self.order_by else '', + str(self.frame or ''), ) def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self) + return '<%s: %s>' % (self.__class__.__name__, self) def get_group_by_cols(self, alias=None): return [] @@ -1523,8 +1369,7 @@ class WindowFrame(Expression): frame is optional (the default is UNBOUNDED FOLLOWING, which is the last row in the frame). """ - - template = "%(frame_type)s BETWEEN %(start)s AND %(end)s" + template = '%(frame_type)s BETWEEN %(start)s AND %(end)s' def __init__(self, start=None, end=None): self.start = Value(start) @@ -1538,58 +1383,52 @@ class WindowFrame(Expression): def as_sql(self, compiler, connection): connection.ops.check_expression_support(self) - start, end = self.window_frame_start_end( - connection, self.start.value, self.end.value - ) - return ( - self.template - % { - "frame_type": self.frame_type, - "start": start, - "end": end, - }, - [], - ) + start, end = self.window_frame_start_end(connection, self.start.value, self.end.value) + return self.template % { + 'frame_type': self.frame_type, + 'start': start, + 'end': end, + }, [] def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self) + return '<%s: %s>' % (self.__class__.__name__, self) def get_group_by_cols(self, alias=None): return [] def __str__(self): if self.start.value is not None and self.start.value < 0: - start = "%d %s" % (abs(self.start.value), connection.ops.PRECEDING) + start = '%d %s' % (abs(self.start.value), connection.ops.PRECEDING) elif self.start.value is not None and self.start.value == 0: start = connection.ops.CURRENT_ROW else: start = connection.ops.UNBOUNDED_PRECEDING if self.end.value is not None and self.end.value > 0: - end = "%d %s" % (self.end.value, connection.ops.FOLLOWING) + end = '%d %s' % (self.end.value, connection.ops.FOLLOWING) elif self.end.value is not None and self.end.value == 0: end = connection.ops.CURRENT_ROW else: end = connection.ops.UNBOUNDED_FOLLOWING return self.template % { - "frame_type": self.frame_type, - "start": start, - "end": end, + 'frame_type': self.frame_type, + 'start': start, + 'end': end, } def window_frame_start_end(self, connection, start, end): - raise NotImplementedError("Subclasses must implement window_frame_start_end().") + raise NotImplementedError('Subclasses must implement window_frame_start_end().') class RowRange(WindowFrame): - frame_type = "ROWS" + frame_type = 'ROWS' def window_frame_start_end(self, connection, start, end): return connection.ops.window_frame_rows_start_end(start, end) class ValueRange(WindowFrame): - frame_type = "RANGE" + frame_type = 'RANGE' def window_frame_start_end(self, connection, start, end): return connection.ops.window_frame_range_start_end(start, end) diff --git a/venv/Lib/site-packages/django/db/models/fields/__init__.py b/venv/Lib/site-packages/django/db/models/fields/__init__.py index b2a16f0..167c3d2 100644 --- a/venv/Lib/site-packages/django/db/models/fields/__init__.py +++ b/venv/Lib/site-packages/django/db/models/fields/__init__.py @@ -2,7 +2,6 @@ import collections.abc import copy import datetime import decimal -import math import operator import uuid import warnings @@ -19,10 +18,7 @@ from django.db.models.query_utils import DeferredAttribute, RegisterLookupMixin from django.utils import timezone from django.utils.datastructures import DictWrapper from django.utils.dateparse import ( - parse_date, - parse_datetime, - parse_duration, - parse_time, + parse_date, parse_datetime, parse_duration, parse_time, ) from django.utils.duration import duration_microseconds, duration_string from django.utils.functional import Promise, cached_property @@ -32,38 +28,14 @@ from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ __all__ = [ - "AutoField", - "BLANK_CHOICE_DASH", - "BigAutoField", - "BigIntegerField", - "BinaryField", - "BooleanField", - "CharField", - "CommaSeparatedIntegerField", - "DateField", - "DateTimeField", - "DecimalField", - "DurationField", - "EmailField", - "Empty", - "Field", - "FilePathField", - "FloatField", - "GenericIPAddressField", - "IPAddressField", - "IntegerField", - "NOT_PROVIDED", - "NullBooleanField", - "PositiveBigIntegerField", - "PositiveIntegerField", - "PositiveSmallIntegerField", - "SlugField", - "SmallAutoField", - "SmallIntegerField", - "TextField", - "TimeField", - "URLField", - "UUIDField", + 'AutoField', 'BLANK_CHOICE_DASH', 'BigAutoField', 'BigIntegerField', + 'BinaryField', 'BooleanField', 'CharField', 'CommaSeparatedIntegerField', + 'DateField', 'DateTimeField', 'DecimalField', 'DurationField', + 'EmailField', 'Empty', 'Field', 'FilePathField', 'FloatField', + 'GenericIPAddressField', 'IPAddressField', 'IntegerField', 'NOT_PROVIDED', + 'NullBooleanField', 'PositiveBigIntegerField', 'PositiveIntegerField', + 'PositiveSmallIntegerField', 'SlugField', 'SmallAutoField', + 'SmallIntegerField', 'TextField', 'TimeField', 'URLField', 'UUIDField', ] @@ -99,7 +71,6 @@ def _load_field(app_label, model_name, field_name): # # getattr(obj, opts.pk.attname) - def _empty(of_cls): new = Empty() new.__class__ = of_cls @@ -126,16 +97,15 @@ class Field(RegisterLookupMixin): auto_creation_counter = -1 default_validators = [] # Default set of validators default_error_messages = { - "invalid_choice": _("Value %(value)r is not a valid choice."), - "null": _("This field cannot be null."), - "blank": _("This field cannot be blank."), - "unique": _("%(model_name)s with this %(field_label)s already exists."), + 'invalid_choice': _('Value %(value)r is not a valid choice.'), + 'null': _('This field cannot be null.'), + 'blank': _('This field cannot be blank.'), + 'unique': _('%(model_name)s with this %(field_label)s ' + 'already exists.'), # Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. # Eg: "Title must be unique for pub_date year" - "unique_for_date": _( - "%(field_label)s must be unique for " - "%(date_field_label)s %(lookup_type)s." - ), + 'unique_for_date': _("%(field_label)s must be unique for " + "%(date_field_label)s %(lookup_type)s."), } system_check_deprecated_details = None system_check_removed_details = None @@ -153,37 +123,18 @@ class Field(RegisterLookupMixin): # Generic field type description, usually overridden by subclasses def _description(self): - return _("Field of type: %(field_type)s") % { - "field_type": self.__class__.__name__ + return _('Field of type: %(field_type)s') % { + 'field_type': self.__class__.__name__ } - description = property(_description) - def __init__( - self, - verbose_name=None, - name=None, - primary_key=False, - max_length=None, - unique=False, - blank=False, - null=False, - db_index=False, - rel=None, - default=NOT_PROVIDED, - editable=True, - serialize=True, - unique_for_date=None, - unique_for_month=None, - unique_for_year=None, - choices=None, - help_text="", - db_column=None, - db_tablespace=None, - auto_created=False, - validators=(), - error_messages=None, - ): + def __init__(self, verbose_name=None, name=None, primary_key=False, + max_length=None, unique=False, blank=False, null=False, + db_index=False, rel=None, default=NOT_PROVIDED, editable=True, + serialize=True, unique_for_date=None, unique_for_month=None, + unique_for_year=None, choices=None, help_text='', db_column=None, + db_tablespace=None, auto_created=False, validators=(), + error_messages=None): self.name = name self.verbose_name = verbose_name # May be set by set_attributes_from_name self._verbose_name = verbose_name # Store original for deconstruction @@ -219,7 +170,7 @@ class Field(RegisterLookupMixin): messages = {} for c in reversed(self.__class__.__mro__): - messages.update(getattr(c, "default_error_messages", {})) + messages.update(getattr(c, 'default_error_messages', {})) messages.update(error_messages or {}) self._error_messages = error_messages # Store for deconstruction later self.error_messages = messages @@ -229,18 +180,18 @@ class Field(RegisterLookupMixin): Return "app_label.model_label.field_name" for fields attached to models. """ - if not hasattr(self, "model"): + if not hasattr(self, 'model'): return super().__str__() model = self.model - return "%s.%s" % (model._meta.label, self.name) + return '%s.%s' % (model._meta.label, self.name) def __repr__(self): """Display the module, class, and name of the field.""" - path = "%s.%s" % (self.__class__.__module__, self.__class__.__qualname__) - name = getattr(self, "name", None) + path = '%s.%s' % (self.__class__.__module__, self.__class__.__qualname__) + name = getattr(self, 'name', None) if name is not None: - return "<%s: %s>" % (path, name) - return "<%s>" % path + return '<%s: %s>' % (path, name) + return '<%s>' % path def check(self, **kwargs): return [ @@ -258,12 +209,12 @@ class Field(RegisterLookupMixin): Check if field name is valid, i.e. 1) does not end with an underscore, 2) does not contain "__" and 3) is not "pk". """ - if self.name.endswith("_"): + if self.name.endswith('_'): return [ checks.Error( - "Field names must not end with an underscore.", + 'Field names must not end with an underscore.', obj=self, - id="fields.E001", + id='fields.E001', ) ] elif LOOKUP_SEP in self.name: @@ -271,15 +222,15 @@ class Field(RegisterLookupMixin): checks.Error( 'Field names must not contain "%s".' % LOOKUP_SEP, obj=self, - id="fields.E002", + id='fields.E002', ) ] - elif self.name == "pk": + elif self.name == 'pk': return [ checks.Error( "'pk' is a reserved word that cannot be used as a field name.", obj=self, - id="fields.E003", + id='fields.E003', ) ] else: @@ -298,7 +249,7 @@ class Field(RegisterLookupMixin): checks.Error( "'choices' must be an iterable (e.g., a list or tuple).", obj=self, - id="fields.E004", + id='fields.E004', ) ] @@ -317,22 +268,14 @@ class Field(RegisterLookupMixin): ): break if self.max_length is not None and group_choices: - choice_max_length = max( - [ - choice_max_length, - *( - len(value) - for value, _ in group_choices - if isinstance(value, str) - ), - ] - ) + choice_max_length = max([ + choice_max_length, + *(len(value) for value, _ in group_choices if isinstance(value, str)), + ]) except (TypeError, ValueError): # No groups, choices in the form [value, display] value, human_name = group_name, group_choices - if not self._choices_is_value(value) or not self._choices_is_value( - human_name - ): + if not self._choices_is_value(value) or not self._choices_is_value(human_name): break if self.max_length is not None and isinstance(value, str): choice_max_length = max(choice_max_length, len(value)) @@ -347,7 +290,7 @@ class Field(RegisterLookupMixin): "'max_length' is too small to fit the longest value " "in 'choices' (%d characters)." % choice_max_length, obj=self, - id="fields.E009", + id='fields.E009', ), ] return [] @@ -357,7 +300,7 @@ class Field(RegisterLookupMixin): "'choices' must be an iterable containing " "(actual value, human readable name) tuples.", obj=self, - id="fields.E005", + id='fields.E005', ) ] @@ -367,30 +310,25 @@ class Field(RegisterLookupMixin): checks.Error( "'db_index' must be None, True or False.", obj=self, - id="fields.E006", + id='fields.E006', ) ] else: return [] def _check_null_allowed_for_primary_keys(self): - if ( - self.primary_key - and self.null - and not connection.features.interprets_empty_strings_as_nulls - ): + if (self.primary_key and self.null and + not connection.features.interprets_empty_strings_as_nulls): # We cannot reliably check this for backends like Oracle which # consider NULL and '' to be equal (and thus set up # character-based fields a little differently). return [ checks.Error( - "Primary keys must not have null=True.", - hint=( - "Set null=False on the field, or " - "remove primary_key=True argument." - ), + 'Primary keys must not have null=True.', + hint=('Set null=False on the field, or ' + 'remove primary_key=True argument.'), obj=self, - id="fields.E007", + id='fields.E007', ) ] else: @@ -402,9 +340,7 @@ class Field(RegisterLookupMixin): app_label = self.model._meta.app_label errors = [] for alias in databases: - if router.allow_migrate( - alias, app_label, model_name=self.model._meta.model_name - ): + if router.allow_migrate(alias, app_label, model_name=self.model._meta.model_name): errors.extend(connections[alias].validation.check_field(self, **kwargs)) return errors @@ -418,12 +354,11 @@ class Field(RegisterLookupMixin): hint=( "validators[{i}] ({repr}) isn't a function or " "instance of a validator class.".format( - i=i, - repr=repr(validator), + i=i, repr=repr(validator), ) ), obj=self, - id="fields.E008", + id='fields.E008', ) ) return errors @@ -433,41 +368,41 @@ class Field(RegisterLookupMixin): return [ checks.Error( self.system_check_removed_details.get( - "msg", - "%s has been removed except for support in historical " - "migrations." % self.__class__.__name__, + 'msg', + '%s has been removed except for support in historical ' + 'migrations.' % self.__class__.__name__ ), - hint=self.system_check_removed_details.get("hint"), + hint=self.system_check_removed_details.get('hint'), obj=self, - id=self.system_check_removed_details.get("id", "fields.EXXX"), + id=self.system_check_removed_details.get('id', 'fields.EXXX'), ) ] elif self.system_check_deprecated_details is not None: return [ checks.Warning( self.system_check_deprecated_details.get( - "msg", "%s has been deprecated." % self.__class__.__name__ + 'msg', + '%s has been deprecated.' % self.__class__.__name__ ), - hint=self.system_check_deprecated_details.get("hint"), + hint=self.system_check_deprecated_details.get('hint'), obj=self, - id=self.system_check_deprecated_details.get("id", "fields.WXXX"), + id=self.system_check_deprecated_details.get('id', 'fields.WXXX'), ) ] return [] def get_col(self, alias, output_field=None): - if alias == self.model._meta.db_table and ( - output_field is None or output_field == self - ): + if output_field is None: + output_field = self + if alias != self.model._meta.db_table or output_field != self: + from django.db.models.expressions import Col + return Col(alias, self, output_field) + else: return self.cached_col - from django.db.models.expressions import Col - - return Col(alias, self, output_field) @cached_property def cached_col(self): from django.db.models.expressions import Col - return Col(self.model._meta.db_table, self) def select_format(self, compiler, sql, params): @@ -484,8 +419,8 @@ class Field(RegisterLookupMixin): * The name of the field on the model, if contribute_to_class() has been run. - * The import path of the field, including the class, e.g. - django.db.models.IntegerField. This should be the most portable + * The import path of the field, including the class:e.g. + django.db.models.IntegerField This should be the most portable version, so less specific may be better. * A list of positional arguments. * A dict of keyword arguments. @@ -527,7 +462,7 @@ class Field(RegisterLookupMixin): "unique_for_month": None, "unique_for_year": None, "choices": None, - "help_text": "", + "help_text": '', "db_column": None, "db_tablespace": None, "auto_created": False, @@ -560,8 +495,8 @@ class Field(RegisterLookupMixin): path = path.replace("django.db.models.fields.related", "django.db.models") elif path.startswith("django.db.models.fields.files"): path = path.replace("django.db.models.fields.files", "django.db.models") - elif path.startswith("django.db.models.fields.json"): - path = path.replace("django.db.models.fields.json", "django.db.models") + elif path.startswith('django.db.models.fields.json'): + path = path.replace('django.db.models.fields.json', 'django.db.models') elif path.startswith("django.db.models.fields.proxy"): path = path.replace("django.db.models.fields.proxy", "django.db.models") elif path.startswith("django.db.models.fields"): @@ -580,9 +515,10 @@ class Field(RegisterLookupMixin): def __eq__(self, other): # Needed for @total_ordering if isinstance(other, Field): - return self.creation_counter == other.creation_counter and getattr( - self, "model", None - ) == getattr(other, "model", None) + return ( + self.creation_counter == other.creation_counter and + getattr(self, 'model', None) == getattr(other, 'model', None) + ) return NotImplemented def __lt__(self, other): @@ -590,29 +526,26 @@ class Field(RegisterLookupMixin): # Order by creation_counter first for backward compatibility. if isinstance(other, Field): if ( - self.creation_counter != other.creation_counter - or not hasattr(self, "model") - and not hasattr(other, "model") + self.creation_counter != other.creation_counter or + not hasattr(self, 'model') and not hasattr(other, 'model') ): return self.creation_counter < other.creation_counter - elif hasattr(self, "model") != hasattr(other, "model"): - return not hasattr(self, "model") # Order no-model fields first + elif hasattr(self, 'model') != hasattr(other, 'model'): + return not hasattr(self, 'model') # Order no-model fields first else: # creation_counter's are equal, compare only models. - return (self.model._meta.app_label, self.model._meta.model_name) < ( - other.model._meta.app_label, - other.model._meta.model_name, + return ( + (self.model._meta.app_label, self.model._meta.model_name) < + (other.model._meta.app_label, other.model._meta.model_name) ) return NotImplemented def __hash__(self): - return hash( - ( - self.creation_counter, - self.model._meta.app_label if hasattr(self, "model") else None, - self.model._meta.model_name if hasattr(self, "model") else None, - ) - ) + return hash(( + self.creation_counter, + self.model._meta.app_label if hasattr(self, 'model') else None, + self.model._meta.model_name if hasattr(self, 'model') else None, + )) def __deepcopy__(self, memodict): # We don't have to deepcopy very much here, since most things are not @@ -620,7 +553,7 @@ class Field(RegisterLookupMixin): obj = copy.copy(self) if self.remote_field: obj.remote_field = copy.copy(self.remote_field) - if hasattr(self.remote_field, "field") and self.remote_field.field is self: + if hasattr(self.remote_field, 'field') and self.remote_field.field is self: obj.remote_field.field = obj memodict[id(self)] = obj return obj @@ -639,7 +572,7 @@ class Field(RegisterLookupMixin): not a new copy of that field. So, use the app registry to load the model and then the field back. """ - if not hasattr(self, "model"): + if not hasattr(self, 'model'): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of @@ -648,13 +581,10 @@ class Field(RegisterLookupMixin): state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. - state.pop("_get_default", None) + state.pop('_get_default', None) return _empty, (self.__class__,), state - return _load_field, ( - self.model._meta.app_label, - self.model._meta.object_name, - self.name, - ) + return _load_field, (self.model._meta.app_label, self.model._meta.object_name, + self.name) def get_pk_value_on_save(self, instance): """ @@ -692,7 +622,7 @@ class Field(RegisterLookupMixin): try: v(value) except exceptions.ValidationError as e: - if hasattr(e, "code") and e.code in self.error_messages: + if hasattr(e, 'code') and e.code in self.error_messages: e.message = self.error_messages[e.code] errors.extend(e.error_list) @@ -719,16 +649,16 @@ class Field(RegisterLookupMixin): elif value == option_key: return raise exceptions.ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": value}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': value}, ) if value is None and not self.null: - raise exceptions.ValidationError(self.error_messages["null"], code="null") + raise exceptions.ValidationError(self.error_messages['null'], code='null') if not self.blank and value in self.empty_values: - raise exceptions.ValidationError(self.error_messages["blank"], code="blank") + raise exceptions.ValidationError(self.error_messages['blank'], code='blank') def clean(self, value, model_instance): """ @@ -742,7 +672,7 @@ class Field(RegisterLookupMixin): return value def db_type_parameters(self, connection): - return DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") + return DictWrapper(self.__dict__, connection.ops.quote_name, 'qn_') def db_check(self, connection): """ @@ -752,9 +682,7 @@ class Field(RegisterLookupMixin): """ data = self.db_type_parameters(connection) try: - return ( - connection.data_type_check_constraints[self.get_internal_type()] % data - ) + return connection.data_type_check_constraints[self.get_internal_type()] % data except KeyError: return None @@ -816,7 +744,7 @@ class Field(RegisterLookupMixin): return connection.data_types_suffix.get(self.get_internal_type()) def get_db_converters(self, connection): - if hasattr(self, "from_db_value"): + if hasattr(self, 'from_db_value'): return [self.from_db_value] return [] @@ -841,7 +769,7 @@ class Field(RegisterLookupMixin): self.attname, self.column = self.get_attname_column() self.concrete = self.column is not None if self.verbose_name is None and self.name: - self.verbose_name = self.name.replace("_", " ") + self.verbose_name = self.name.replace('_', ' ') def contribute_to_class(self, cls, name, private_only=False): """ @@ -854,16 +782,20 @@ class Field(RegisterLookupMixin): self.model = cls cls._meta.add_field(self, private=private_only) if self.column: - setattr(cls, self.attname, self.descriptor_class(self)) + # Don't override classmethods with the descriptor. This means that + # if you have a classmethod and a field with the same name, then + # such fields can't be deferred (we don't have a check for this). + if not getattr(cls, self.attname, None): + setattr(cls, self.attname, self.descriptor_class(self)) if self.choices is not None: # Don't override a get_FOO_display() method defined explicitly on # this class, but don't check methods derived from inheritance, to # allow overriding inherited choices. For more complex inheritance # structures users should override contribute_to_class(). - if "get_%s_display" % self.name not in cls.__dict__: + if 'get_%s_display' % self.name not in cls.__dict__: setattr( cls, - "get_%s_display" % self.name, + 'get_%s_display' % self.name, partialmethod(cls._get_FIELD_display, field=self), ) @@ -924,21 +856,11 @@ class Field(RegisterLookupMixin): return self.default return lambda: self.default - if ( - not self.empty_strings_allowed - or self.null - and not connection.features.interprets_empty_strings_as_nulls - ): + if not self.empty_strings_allowed or self.null and not connection.features.interprets_empty_strings_as_nulls: return return_None return str # return empty string - def get_choices( - self, - include_blank=True, - blank_choice=BLANK_CHOICE_DASH, - limit_choices_to=None, - ordering=(), - ): + def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_choices_to=None, ordering=()): """ Return choices with a default blank choices included, for use as <select> choices for this field. @@ -946,9 +868,7 @@ class Field(RegisterLookupMixin): if self.choices is not None: choices = list(self.choices) if include_blank: - blank_defined = any( - choice in ("", None) for choice, _ in self.flatchoices - ) + blank_defined = any(choice in ('', None) for choice, _ in self.flatchoices) if not blank_defined: choices = blank_choice + choices return choices @@ -956,8 +876,8 @@ class Field(RegisterLookupMixin): limit_choices_to = limit_choices_to or self.get_limit_choices_to() choice_func = operator.attrgetter( self.remote_field.get_related_field().attname - if hasattr(self.remote_field, "get_related_field") - else "pk" + if hasattr(self.remote_field, 'get_related_field') + else 'pk' ) qs = rel_model._default_manager.complex_filter(limit_choices_to) if ordering: @@ -984,7 +904,6 @@ class Field(RegisterLookupMixin): else: flat.append((choice, value)) return flat - flatchoices = property(_get_flatchoices) def save_form_data(self, instance, data): @@ -993,25 +912,24 @@ class Field(RegisterLookupMixin): def formfield(self, form_class=None, choices_form_class=None, **kwargs): """Return a django.forms.Field instance for this field.""" defaults = { - "required": not self.blank, - "label": capfirst(self.verbose_name), - "help_text": self.help_text, + 'required': not self.blank, + 'label': capfirst(self.verbose_name), + 'help_text': self.help_text, } if self.has_default(): if callable(self.default): - defaults["initial"] = self.default - defaults["show_hidden_initial"] = True + defaults['initial'] = self.default + defaults['show_hidden_initial'] = True else: - defaults["initial"] = self.get_default() + defaults['initial'] = self.get_default() if self.choices is not None: # Fields with choices get special treatment. - include_blank = self.blank or not ( - self.has_default() or "initial" in kwargs - ) - defaults["choices"] = self.get_choices(include_blank=include_blank) - defaults["coerce"] = self.to_python + include_blank = (self.blank or + not (self.has_default() or 'initial' in kwargs)) + defaults['choices'] = self.get_choices(include_blank=include_blank) + defaults['coerce'] = self.to_python if self.null: - defaults["empty_value"] = None + defaults['empty_value'] = None if choices_form_class is not None: form_class = choices_form_class else: @@ -1020,19 +938,9 @@ class Field(RegisterLookupMixin): # max_value) don't apply for choice fields, so be sure to only pass # the values that TypedChoiceField will understand. for k in list(kwargs): - if k not in ( - "coerce", - "empty_value", - "choices", - "required", - "widget", - "label", - "initial", - "help_text", - "error_messages", - "show_hidden_initial", - "disabled", - ): + if k not in ('coerce', 'empty_value', 'choices', 'required', + 'widget', 'label', 'initial', 'help_text', + 'error_messages', 'show_hidden_initial', 'disabled'): del kwargs[k] defaults.update(kwargs) if form_class is None: @@ -1047,8 +955,8 @@ class Field(RegisterLookupMixin): class BooleanField(Field): empty_strings_allowed = False default_error_messages = { - "invalid": _("“%(value)s” value must be either True or False."), - "invalid_nullable": _("“%(value)s” value must be either True, False, or None."), + 'invalid': _('“%(value)s” value must be either True or False.'), + 'invalid_nullable': _('“%(value)s” value must be either True, False, or None.'), } description = _("Boolean (Either True or False)") @@ -1061,14 +969,14 @@ class BooleanField(Field): if value in (True, False): # 1/0 are equal to True/False. bool() converts former to latter. return bool(value) - if value in ("t", "True", "1"): + if value in ('t', 'True', '1'): return True - if value in ("f", "False", "0"): + if value in ('f', 'False', '0'): return False raise exceptions.ValidationError( - self.error_messages["invalid_nullable" if self.null else "invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid_nullable' if self.null else 'invalid'], + code='invalid', + params={'value': value}, ) def get_prep_value(self, value): @@ -1079,14 +987,14 @@ class BooleanField(Field): def formfield(self, **kwargs): if self.choices is not None: - include_blank = not (self.has_default() or "initial" in kwargs) - defaults = {"choices": self.get_choices(include_blank=include_blank)} + include_blank = not (self.has_default() or 'initial' in kwargs) + defaults = {'choices': self.get_choices(include_blank=include_blank)} else: form_class = forms.NullBooleanField if self.null else forms.BooleanField # In HTML checkboxes, 'required' means "must be checked" which is # different from the choices case ("must select some value"). # required=False allows unchecked checkboxes. - defaults = {"form_class": form_class, "required": False} + defaults = {'form_class': form_class, 'required': False} return super().formfield(**{**defaults, **kwargs}) @@ -1099,7 +1007,7 @@ class CharField(Field): self.validators.append(validators.MaxLengthValidator(self.max_length)) def check(self, **kwargs): - databases = kwargs.get("databases") or [] + databases = kwargs.get('databases') or [] return [ *super().check(**kwargs), *self._check_db_collation(databases), @@ -1112,19 +1020,16 @@ class CharField(Field): checks.Error( "CharFields must define a 'max_length' attribute.", obj=self, - id="fields.E120", + id='fields.E120', ) ] - elif ( - not isinstance(self.max_length, int) - or isinstance(self.max_length, bool) - or self.max_length <= 0 - ): + elif (not isinstance(self.max_length, int) or isinstance(self.max_length, bool) or + self.max_length <= 0): return [ checks.Error( "'max_length' must be a positive integer.", obj=self, - id="fields.E121", + id='fields.E121', ) ] else: @@ -1137,17 +1042,16 @@ class CharField(Field): continue connection = connections[db] if not ( - self.db_collation is None - or "supports_collation_on_charfield" - in self.model._meta.required_db_features - or connection.features.supports_collation_on_charfield + self.db_collation is None or + 'supports_collation_on_charfield' in self.model._meta.required_db_features or + connection.features.supports_collation_on_charfield ): errors.append( checks.Error( - "%s does not support a database collation on " - "CharFields." % connection.display_name, + '%s does not support a database collation on ' + 'CharFields.' % connection.display_name, obj=self, - id="fields.E190", + id='fields.E190', ), ) return errors @@ -1173,17 +1077,17 @@ class CharField(Field): # Passing max_length to forms.CharField means that the value's length # will be validated twice. This is considered acceptable since we want # the value in the form field (to pass into widget for example). - defaults = {"max_length": self.max_length} + defaults = {'max_length': self.max_length} # TODO: Handle multiple backends with different feature flags. if self.null and not connection.features.interprets_empty_strings_as_nulls: - defaults["empty_value"] = None + defaults['empty_value'] = None defaults.update(kwargs) return super().formfield(**defaults) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.db_collation: - kwargs["db_collation"] = self.db_collation + kwargs['db_collation'] = self.db_collation return name, path, args, kwargs @@ -1191,29 +1095,20 @@ class CommaSeparatedIntegerField(CharField): default_validators = [validators.validate_comma_separated_integer_list] description = _("Comma-separated integers") system_check_removed_details = { - "msg": ( - "CommaSeparatedIntegerField is removed except for support in " - "historical migrations." + 'msg': ( + 'CommaSeparatedIntegerField is removed except for support in ' + 'historical migrations.' ), - "hint": ( - "Use CharField(validators=[validate_comma_separated_integer_list]) " - "instead." + 'hint': ( + 'Use CharField(validators=[validate_comma_separated_integer_list]) ' + 'instead.' ), - "id": "fields.E901", + 'id': 'fields.E901', } -def _to_naive(value): - if timezone.is_aware(value): - value = timezone.make_naive(value, timezone.utc) - return value - - -def _get_naive_now(): - return _to_naive(timezone.now()) - - class DateTimeCheckMixin: + def check(self, **kwargs): return [ *super().check(**kwargs), @@ -1225,14 +1120,8 @@ class DateTimeCheckMixin: # auto_now, auto_now_add, and default are mutually exclusive # options. The use of more than one of these options together # will trigger an Error - mutually_exclusive_options = [ - self.auto_now_add, - self.auto_now, - self.has_default(), - ] - enabled_options = [ - option not in (None, False) for option in mutually_exclusive_options - ].count(True) + mutually_exclusive_options = [self.auto_now_add, self.auto_now, self.has_default()] + enabled_options = [option not in (None, False) for option in mutually_exclusive_options].count(True) if enabled_options > 1: return [ checks.Error( @@ -1240,7 +1129,7 @@ class DateTimeCheckMixin: "are mutually exclusive. Only one of these options " "may be present.", obj=self, - id="fields.E160", + id='fields.E160', ) ] else: @@ -1249,64 +1138,23 @@ class DateTimeCheckMixin: def _check_fix_default_value(self): return [] - # Concrete subclasses use this in their implementations of - # _check_fix_default_value(). - def _check_if_value_fixed(self, value, now=None): - """ - Check if the given value appears to have been provided as a "fixed" - time value, and include a warning in the returned list if it does. The - value argument must be a date object or aware/naive datetime object. If - now is provided, it must be a naive datetime object. - """ - if now is None: - now = _get_naive_now() - offset = datetime.timedelta(seconds=10) - lower = now - offset - upper = now + offset - if isinstance(value, datetime.datetime): - value = _to_naive(value) - else: - assert isinstance(value, datetime.date) - lower = lower.date() - upper = upper.date() - if lower <= value <= upper: - return [ - checks.Warning( - "Fixed default value provided.", - hint=( - "It seems you set a fixed date / time / datetime " - "value as default for this field. This may not be " - "what you want. If you want to have the current date " - "as default, use `django.utils.timezone.now`" - ), - obj=self, - id="fields.W161", - ) - ] - return [] - class DateField(DateTimeCheckMixin, Field): empty_strings_allowed = False default_error_messages = { - "invalid": _( - "“%(value)s” value has an invalid date format. It must be " - "in YYYY-MM-DD format." - ), - "invalid_date": _( - "“%(value)s” value has the correct format (YYYY-MM-DD) " - "but it is an invalid date." - ), + 'invalid': _('“%(value)s” value has an invalid date format. It must be ' + 'in YYYY-MM-DD format.'), + 'invalid_date': _('“%(value)s” value has the correct format (YYYY-MM-DD) ' + 'but it is an invalid date.'), } description = _("Date (without time)") - def __init__( - self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs - ): + def __init__(self, verbose_name=None, name=None, auto_now=False, + auto_now_add=False, **kwargs): self.auto_now, self.auto_now_add = auto_now, auto_now_add if auto_now or auto_now_add: - kwargs["editable"] = False - kwargs["blank"] = True + kwargs['editable'] = False + kwargs['blank'] = True super().__init__(verbose_name, name, **kwargs) def _check_fix_default_value(self): @@ -1317,26 +1165,47 @@ class DateField(DateTimeCheckMixin, Field): if not self.has_default(): return [] + now = timezone.now() + if not timezone.is_naive(now): + now = timezone.make_naive(now, timezone.utc) value = self.default if isinstance(value, datetime.datetime): - value = _to_naive(value).date() + if not timezone.is_naive(value): + value = timezone.make_naive(value, timezone.utc) + value = value.date() elif isinstance(value, datetime.date): + # Nothing to do, as dates don't have tz information pass else: # No explicit date / datetime value -- no checks necessary return [] - # At this point, value is a date object. - return self._check_if_value_fixed(value) + offset = datetime.timedelta(days=1) + lower = (now - offset).date() + upper = (now + offset).date() + if lower <= value <= upper: + return [ + checks.Warning( + 'Fixed default value provided.', + hint='It seems you set a fixed date / time / datetime ' + 'value as default for this field. This may not be ' + 'what you want. If you want to have the current date ' + 'as default, use `django.utils.timezone.now`', + obj=self, + id='fields.W161', + ) + ] + + return [] def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.auto_now: - kwargs["auto_now"] = True + kwargs['auto_now'] = True if self.auto_now_add: - kwargs["auto_now_add"] = True + kwargs['auto_now_add'] = True if self.auto_now or self.auto_now_add: - del kwargs["editable"] - del kwargs["blank"] + del kwargs['editable'] + del kwargs['blank'] return name, path, args, kwargs def get_internal_type(self): @@ -1361,15 +1230,15 @@ class DateField(DateTimeCheckMixin, Field): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages["invalid_date"], - code="invalid_date", - params={"value": value}, + self.error_messages['invalid_date'], + code='invalid_date', + params={'value': value}, ) raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def pre_save(self, model_instance, add): @@ -1384,18 +1253,12 @@ class DateField(DateTimeCheckMixin, Field): super().contribute_to_class(cls, name, **kwargs) if not self.null: setattr( - cls, - "get_next_by_%s" % self.name, - partialmethod( - cls._get_next_or_previous_by_FIELD, field=self, is_next=True - ), + cls, 'get_next_by_%s' % self.name, + partialmethod(cls._get_next_or_previous_by_FIELD, field=self, is_next=True) ) setattr( - cls, - "get_previous_by_%s" % self.name, - partialmethod( - cls._get_next_or_previous_by_FIELD, field=self, is_next=False - ), + cls, 'get_previous_by_%s' % self.name, + partialmethod(cls._get_next_or_previous_by_FIELD, field=self, is_next=False) ) def get_prep_value(self, value): @@ -1410,33 +1273,25 @@ class DateField(DateTimeCheckMixin, Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return "" if val is None else val.isoformat() + return '' if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.DateField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.DateField, + **kwargs, + }) class DateTimeField(DateField): empty_strings_allowed = False default_error_messages = { - "invalid": _( - "“%(value)s” value has an invalid format. It must be in " - "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format." - ), - "invalid_date": _( - "“%(value)s” value has the correct format " - "(YYYY-MM-DD) but it is an invalid date." - ), - "invalid_datetime": _( - "“%(value)s” value has the correct format " - "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " - "but it is an invalid date/time." - ), + 'invalid': _('“%(value)s” value has an invalid format. It must be in ' + 'YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.'), + 'invalid_date': _("“%(value)s” value has the correct format " + "(YYYY-MM-DD) but it is an invalid date."), + 'invalid_datetime': _('“%(value)s” value has the correct format ' + '(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) ' + 'but it is an invalid date/time.'), } description = _("Date (with time)") @@ -1450,10 +1305,39 @@ class DateTimeField(DateField): if not self.has_default(): return [] + now = timezone.now() + if not timezone.is_naive(now): + now = timezone.make_naive(now, timezone.utc) value = self.default - if isinstance(value, (datetime.datetime, datetime.date)): - return self._check_if_value_fixed(value) - # No explicit date / datetime value -- no checks necessary. + if isinstance(value, datetime.datetime): + second_offset = datetime.timedelta(seconds=10) + lower = now - second_offset + upper = now + second_offset + if timezone.is_aware(value): + value = timezone.make_naive(value, timezone.utc) + elif isinstance(value, datetime.date): + second_offset = datetime.timedelta(seconds=10) + lower = now - second_offset + lower = datetime.datetime(lower.year, lower.month, lower.day) + upper = now + second_offset + upper = datetime.datetime(upper.year, upper.month, upper.day) + value = datetime.datetime(value.year, value.month, value.day) + else: + # No explicit date / datetime value -- no checks necessary + return [] + if lower <= value <= upper: + return [ + checks.Warning( + 'Fixed default value provided.', + hint='It seems you set a fixed date / time / datetime ' + 'value as default for this field. This may not be ' + 'what you want. If you want to have the current date ' + 'as default, use `django.utils.timezone.now`', + obj=self, + id='fields.W161', + ) + ] + return [] def get_internal_type(self): @@ -1471,12 +1355,10 @@ class DateTimeField(DateField): # local time. This won't work during DST change, but we can't # do much about it, so we let the exceptions percolate up the # call stack. - warnings.warn( - "DateTimeField %s.%s received a naive datetime " - "(%s) while time zone support is active." - % (self.model.__name__, self.name, value), - RuntimeWarning, - ) + warnings.warn("DateTimeField %s.%s received a naive datetime " + "(%s) while time zone support is active." % + (self.model.__name__, self.name, value), + RuntimeWarning) default_timezone = timezone.get_default_timezone() value = timezone.make_aware(value, default_timezone) return value @@ -1487,9 +1369,9 @@ class DateTimeField(DateField): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages["invalid_datetime"], - code="invalid_datetime", - params={"value": value}, + self.error_messages['invalid_datetime'], + code='invalid_datetime', + params={'value': value}, ) try: @@ -1498,15 +1380,15 @@ class DateTimeField(DateField): return datetime.datetime(parsed.year, parsed.month, parsed.day) except ValueError: raise exceptions.ValidationError( - self.error_messages["invalid_date"], - code="invalid_date", - params={"value": value}, + self.error_messages['invalid_date'], + code='invalid_date', + params={'value': value}, ) raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def pre_save(self, model_instance, add): @@ -1528,14 +1410,13 @@ class DateTimeField(DateField): # time. This won't work during DST change, but we can't do much # about it, so we let the exceptions percolate up the call stack. try: - name = "%s.%s" % (self.model.__name__, self.name) + name = '%s.%s' % (self.model.__name__, self.name) except AttributeError: - name = "(unbound)" - warnings.warn( - "DateTimeField %s received a naive datetime (%s)" - " while time zone support is active." % (name, value), - RuntimeWarning, - ) + name = '(unbound)' + warnings.warn("DateTimeField %s received a naive datetime (%s)" + " while time zone support is active." % + (name, value), + RuntimeWarning) default_timezone = timezone.get_default_timezone() value = timezone.make_aware(value, default_timezone) return value @@ -1548,32 +1429,24 @@ class DateTimeField(DateField): def value_to_string(self, obj): val = self.value_from_object(obj) - return "" if val is None else val.isoformat() + return '' if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.DateTimeField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.DateTimeField, + **kwargs, + }) class DecimalField(Field): empty_strings_allowed = False default_error_messages = { - "invalid": _("“%(value)s” value must be a decimal number."), + 'invalid': _('“%(value)s” value must be a decimal number.'), } description = _("Decimal number") - def __init__( - self, - verbose_name=None, - name=None, - max_digits=None, - decimal_places=None, - **kwargs, - ): + def __init__(self, verbose_name=None, name=None, max_digits=None, + decimal_places=None, **kwargs): self.max_digits, self.decimal_places = max_digits, decimal_places super().__init__(verbose_name, name, **kwargs) @@ -1600,7 +1473,7 @@ class DecimalField(Field): checks.Error( "DecimalFields must define a 'decimal_places' attribute.", obj=self, - id="fields.E130", + id='fields.E130', ) ] except ValueError: @@ -1608,7 +1481,7 @@ class DecimalField(Field): checks.Error( "'decimal_places' must be a non-negative integer.", obj=self, - id="fields.E131", + id='fields.E131', ) ] else: @@ -1624,7 +1497,7 @@ class DecimalField(Field): checks.Error( "DecimalFields must define a 'max_digits' attribute.", obj=self, - id="fields.E132", + id='fields.E132', ) ] except ValueError: @@ -1632,7 +1505,7 @@ class DecimalField(Field): checks.Error( "'max_digits' must be a positive integer.", obj=self, - id="fields.E133", + id='fields.E133', ) ] else: @@ -1644,7 +1517,7 @@ class DecimalField(Field): checks.Error( "'max_digits' must be greater or equal to 'decimal_places'.", obj=self, - id="fields.E134", + id='fields.E134', ) ] return [] @@ -1662,9 +1535,9 @@ class DecimalField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.max_digits is not None: - kwargs["max_digits"] = self.max_digits + kwargs['max_digits'] = self.max_digits if self.decimal_places is not None: - kwargs["decimal_places"] = self.decimal_places + kwargs['decimal_places'] = self.decimal_places return name, path, args, kwargs def get_internal_type(self): @@ -1674,40 +1547,30 @@ class DecimalField(Field): if value is None: return value if isinstance(value, float): - if math.isnan(value): - raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, - ) return self.context.create_decimal_from_float(value) try: return decimal.Decimal(value) except (decimal.InvalidOperation, TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def get_db_prep_save(self, value, connection): - return connection.ops.adapt_decimalfield_value( - self.to_python(value), self.max_digits, self.decimal_places - ) + return connection.ops.adapt_decimalfield_value(self.to_python(value), self.max_digits, self.decimal_places) def get_prep_value(self, value): value = super().get_prep_value(value) return self.to_python(value) def formfield(self, **kwargs): - return super().formfield( - **{ - "max_digits": self.max_digits, - "decimal_places": self.decimal_places, - "form_class": forms.DecimalField, - **kwargs, - } - ) + return super().formfield(**{ + 'max_digits': self.max_digits, + 'decimal_places': self.decimal_places, + 'form_class': forms.DecimalField, + **kwargs, + }) class DurationField(Field): @@ -1717,13 +1580,10 @@ class DurationField(Field): Use interval on PostgreSQL, INTERVAL DAY TO SECOND on Oracle, and bigint of microseconds on other databases. """ - empty_strings_allowed = False default_error_messages = { - "invalid": _( - "“%(value)s” value has an invalid format. It must be in " - "[DD] [[HH:]MM:]ss[.uuuuuu] format." - ) + 'invalid': _('“%(value)s” value has an invalid format. It must be in ' + '[DD] [[HH:]MM:]ss[.uuuuuu] format.') } description = _("Duration") @@ -1744,9 +1604,9 @@ class DurationField(Field): return parsed raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def get_db_prep_value(self, value, connection, prepared=False): @@ -1764,15 +1624,13 @@ class DurationField(Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return "" if val is None else duration_string(val) + return '' if val is None else duration_string(val) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.DurationField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.DurationField, + **kwargs, + }) class EmailField(CharField): @@ -1781,7 +1639,7 @@ class EmailField(CharField): def __init__(self, *args, **kwargs): # max_length=254 to be compliant with RFCs 3696 and 5321 - kwargs.setdefault("max_length", 254) + kwargs.setdefault('max_length', 254) super().__init__(*args, **kwargs) def deconstruct(self): @@ -1793,31 +1651,20 @@ class EmailField(CharField): def formfield(self, **kwargs): # As with CharField, this will cause email validation to be performed # twice. - return super().formfield( - **{ - "form_class": forms.EmailField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.EmailField, + **kwargs, + }) class FilePathField(Field): description = _("File path") - def __init__( - self, - verbose_name=None, - name=None, - path="", - match=None, - recursive=False, - allow_files=True, - allow_folders=False, - **kwargs, - ): + def __init__(self, verbose_name=None, name=None, path='', match=None, + recursive=False, allow_files=True, allow_folders=False, **kwargs): self.path, self.match, self.recursive = path, match, recursive self.allow_files, self.allow_folders = allow_files, allow_folders - kwargs.setdefault("max_length", 100) + kwargs.setdefault('max_length', 100) super().__init__(verbose_name, name, **kwargs) def check(self, **kwargs): @@ -1830,26 +1677,25 @@ class FilePathField(Field): if not self.allow_files and not self.allow_folders: return [ checks.Error( - "FilePathFields must have either 'allow_files' or 'allow_folders' " - "set to True.", + "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.", obj=self, - id="fields.E140", + id='fields.E140', ) ] return [] def deconstruct(self): name, path, args, kwargs = super().deconstruct() - if self.path != "": - kwargs["path"] = self.path + if self.path != '': + kwargs['path'] = self.path if self.match is not None: - kwargs["match"] = self.match + kwargs['match'] = self.match if self.recursive is not False: - kwargs["recursive"] = self.recursive + kwargs['recursive'] = self.recursive if self.allow_files is not True: - kwargs["allow_files"] = self.allow_files + kwargs['allow_files'] = self.allow_files if self.allow_folders is not False: - kwargs["allow_folders"] = self.allow_folders + kwargs['allow_folders'] = self.allow_folders if kwargs.get("max_length") == 100: del kwargs["max_length"] return name, path, args, kwargs @@ -1861,17 +1707,15 @@ class FilePathField(Field): return str(value) def formfield(self, **kwargs): - return super().formfield( - **{ - "path": self.path() if callable(self.path) else self.path, - "match": self.match, - "recursive": self.recursive, - "form_class": forms.FilePathField, - "allow_files": self.allow_files, - "allow_folders": self.allow_folders, - **kwargs, - } - ) + return super().formfield(**{ + 'path': self.path() if callable(self.path) else self.path, + 'match': self.match, + 'recursive': self.recursive, + 'form_class': forms.FilePathField, + 'allow_files': self.allow_files, + 'allow_folders': self.allow_folders, + **kwargs, + }) def get_internal_type(self): return "FilePathField" @@ -1880,7 +1724,7 @@ class FilePathField(Field): class FloatField(Field): empty_strings_allowed = False default_error_messages = { - "invalid": _("“%(value)s” value must be a float."), + 'invalid': _('“%(value)s” value must be a float.'), } description = _("Floating point number") @@ -1905,24 +1749,22 @@ class FloatField(Field): return float(value) except (TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.FloatField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.FloatField, + **kwargs, + }) class IntegerField(Field): empty_strings_allowed = False default_error_messages = { - "invalid": _("“%(value)s” value must be an integer."), + 'invalid': _('“%(value)s” value must be an integer.'), } description = _("Integer") @@ -1936,11 +1778,10 @@ class IntegerField(Field): if self.max_length is not None: return [ checks.Warning( - "'max_length' is ignored when used with %s." - % self.__class__.__name__, + "'max_length' is ignored when used with %s." % self.__class__.__name__, hint="Remove 'max_length' from field", obj=self, - id="fields.W122", + id='fields.W122', ) ] return [] @@ -1954,28 +1795,22 @@ class IntegerField(Field): min_value, max_value = connection.ops.integer_field_range(internal_type) if min_value is not None and not any( ( - isinstance(validator, validators.MinValueValidator) - and ( + isinstance(validator, validators.MinValueValidator) and ( validator.limit_value() if callable(validator.limit_value) else validator.limit_value - ) - >= min_value - ) - for validator in validators_ + ) >= min_value + ) for validator in validators_ ): validators_.append(validators.MinValueValidator(min_value)) if max_value is not None and not any( ( - isinstance(validator, validators.MaxValueValidator) - and ( + isinstance(validator, validators.MaxValueValidator) and ( validator.limit_value() if callable(validator.limit_value) else validator.limit_value - ) - <= max_value - ) - for validator in validators_ + ) <= max_value + ) for validator in validators_ ): validators_.append(validators.MaxValueValidator(max_value)) return validators_ @@ -2001,18 +1836,16 @@ class IntegerField(Field): return int(value) except (TypeError, ValueError): raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.IntegerField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.IntegerField, + **kwargs, + }) class BigIntegerField(IntegerField): @@ -2023,41 +1856,39 @@ class BigIntegerField(IntegerField): return "BigIntegerField" def formfield(self, **kwargs): - return super().formfield( - **{ - "min_value": -BigIntegerField.MAX_BIGINT - 1, - "max_value": BigIntegerField.MAX_BIGINT, - **kwargs, - } - ) + return super().formfield(**{ + 'min_value': -BigIntegerField.MAX_BIGINT - 1, + 'max_value': BigIntegerField.MAX_BIGINT, + **kwargs, + }) class SmallIntegerField(IntegerField): - description = _("Small integer") + description = _('Small integer') def get_internal_type(self): - return "SmallIntegerField" + return 'SmallIntegerField' class IPAddressField(Field): empty_strings_allowed = False description = _("IPv4 address") system_check_removed_details = { - "msg": ( - "IPAddressField has been removed except for support in " - "historical migrations." + 'msg': ( + 'IPAddressField has been removed except for support in ' + 'historical migrations.' ), - "hint": "Use GenericIPAddressField instead.", - "id": "fields.E900", + 'hint': 'Use GenericIPAddressField instead.', + 'id': 'fields.E900', } def __init__(self, *args, **kwargs): - kwargs["max_length"] = 15 + kwargs['max_length'] = 15 super().__init__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs["max_length"] + del kwargs['max_length'] return name, path, args, kwargs def get_prep_value(self, value): @@ -2075,23 +1906,14 @@ class GenericIPAddressField(Field): description = _("IP address") default_error_messages = {} - def __init__( - self, - verbose_name=None, - name=None, - protocol="both", - unpack_ipv4=False, - *args, - **kwargs, - ): + def __init__(self, verbose_name=None, name=None, protocol='both', + unpack_ipv4=False, *args, **kwargs): self.unpack_ipv4 = unpack_ipv4 self.protocol = protocol - ( - self.default_validators, - invalid_error_message, - ) = validators.ip_address_validators(protocol, unpack_ipv4) - self.default_error_messages["invalid"] = invalid_error_message - kwargs["max_length"] = 39 + self.default_validators, invalid_error_message = \ + validators.ip_address_validators(protocol, unpack_ipv4) + self.default_error_messages['invalid'] = invalid_error_message + kwargs['max_length'] = 39 super().__init__(verbose_name, name, *args, **kwargs) def check(self, **kwargs): @@ -2101,13 +1923,13 @@ class GenericIPAddressField(Field): ] def _check_blank_and_null_values(self, **kwargs): - if not getattr(self, "null", False) and getattr(self, "blank", False): + if not getattr(self, 'null', False) and getattr(self, 'blank', False): return [ checks.Error( - "GenericIPAddressFields cannot have blank=True if null=False, " - "as blank values are stored as nulls.", + 'GenericIPAddressFields cannot have blank=True if null=False, ' + 'as blank values are stored as nulls.', obj=self, - id="fields.E150", + id='fields.E150', ) ] return [] @@ -2115,11 +1937,11 @@ class GenericIPAddressField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.unpack_ipv4 is not False: - kwargs["unpack_ipv4"] = self.unpack_ipv4 + kwargs['unpack_ipv4'] = self.unpack_ipv4 if self.protocol != "both": - kwargs["protocol"] = self.protocol + kwargs['protocol'] = self.protocol if kwargs.get("max_length") == 39: - del kwargs["max_length"] + del kwargs['max_length'] return name, path, args, kwargs def get_internal_type(self): @@ -2131,10 +1953,8 @@ class GenericIPAddressField(Field): if not isinstance(value, str): value = str(value) value = value.strip() - if ":" in value: - return clean_ipv6_address( - value, self.unpack_ipv4, self.error_messages["invalid"] - ) + if ':' in value: + return clean_ipv6_address(value, self.unpack_ipv4, self.error_messages['invalid']) return value def get_db_prep_value(self, value, connection, prepared=False): @@ -2146,7 +1966,7 @@ class GenericIPAddressField(Field): value = super().get_prep_value(value) if value is None: return None - if value and ":" in value: + if value and ':' in value: try: return clean_ipv6_address(value, self.unpack_ipv4) except exceptions.ValidationError: @@ -2154,46 +1974,47 @@ class GenericIPAddressField(Field): return str(value) def formfield(self, **kwargs): - return super().formfield( - **{ - "protocol": self.protocol, - "form_class": forms.GenericIPAddressField, - **kwargs, - } - ) + return super().formfield(**{ + 'protocol': self.protocol, + 'form_class': forms.GenericIPAddressField, + **kwargs, + }) class NullBooleanField(BooleanField): default_error_messages = { - "invalid": _("“%(value)s” value must be either None, True or False."), - "invalid_nullable": _("“%(value)s” value must be either None, True or False."), + 'invalid': _('“%(value)s” value must be either None, True or False.'), + 'invalid_nullable': _('“%(value)s” value must be either None, True or False.'), } description = _("Boolean (Either True, False or None)") - system_check_removed_details = { - "msg": ( - "NullBooleanField is removed except for support in historical " - "migrations." + system_check_deprecated_details = { + 'msg': ( + 'NullBooleanField is deprecated. Support for it (except in ' + 'historical migrations) will be removed in Django 4.0.' ), - "hint": "Use BooleanField(null=True) instead.", - "id": "fields.E903", + 'hint': 'Use BooleanField(null=True) instead.', + 'id': 'fields.W903', } def __init__(self, *args, **kwargs): - kwargs["null"] = True - kwargs["blank"] = True + kwargs['null'] = True + kwargs['blank'] = True super().__init__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs["null"] - del kwargs["blank"] + del kwargs['null'] + del kwargs['blank'] return name, path, args, kwargs + def get_internal_type(self): + return "NullBooleanField" + class PositiveIntegerRelDbTypeMixin: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) - if not hasattr(cls, "integer_field_class"): + if not hasattr(cls, 'integer_field_class'): cls.integer_field_class = next( ( parent @@ -2219,18 +2040,16 @@ class PositiveIntegerRelDbTypeMixin: class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField): - description = _("Positive big integer") + description = _('Positive big integer') def get_internal_type(self): - return "PositiveBigIntegerField" + return 'PositiveBigIntegerField' def formfield(self, **kwargs): - return super().formfield( - **{ - "min_value": 0, - **kwargs, - } - ) + return super().formfield(**{ + 'min_value': 0, + **kwargs, + }) class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): @@ -2240,12 +2059,10 @@ class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): return "PositiveIntegerField" def formfield(self, **kwargs): - return super().formfield( - **{ - "min_value": 0, - **kwargs, - } - ) + return super().formfield(**{ + 'min_value': 0, + **kwargs, + }) class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField): @@ -2255,21 +2072,17 @@ class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField return "PositiveSmallIntegerField" def formfield(self, **kwargs): - return super().formfield( - **{ - "min_value": 0, - **kwargs, - } - ) + return super().formfield(**{ + 'min_value': 0, + **kwargs, + }) class SlugField(CharField): default_validators = [validators.validate_slug] description = _("Slug (up to %(max_length)s)") - def __init__( - self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs - ): + def __init__(self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs): self.allow_unicode = allow_unicode if self.allow_unicode: self.default_validators = [validators.validate_unicode_slug] @@ -2278,26 +2091,24 @@ class SlugField(CharField): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 50: - del kwargs["max_length"] + del kwargs['max_length'] if self.db_index is False: - kwargs["db_index"] = False + kwargs['db_index'] = False else: - del kwargs["db_index"] + del kwargs['db_index'] if self.allow_unicode is not False: - kwargs["allow_unicode"] = self.allow_unicode + kwargs['allow_unicode'] = self.allow_unicode return name, path, args, kwargs def get_internal_type(self): return "SlugField" def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.SlugField, - "allow_unicode": self.allow_unicode, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.SlugField, + 'allow_unicode': self.allow_unicode, + **kwargs, + }) class TextField(Field): @@ -2308,7 +2119,7 @@ class TextField(Field): self.db_collation = db_collation def check(self, **kwargs): - databases = kwargs.get("databases") or [] + databases = kwargs.get('databases') or [] return [ *super().check(**kwargs), *self._check_db_collation(databases), @@ -2321,17 +2132,16 @@ class TextField(Field): continue connection = connections[db] if not ( - self.db_collation is None - or "supports_collation_on_textfield" - in self.model._meta.required_db_features - or connection.features.supports_collation_on_textfield + self.db_collation is None or + 'supports_collation_on_textfield' in self.model._meta.required_db_features or + connection.features.supports_collation_on_textfield ): errors.append( checks.Error( - "%s does not support a database collation on " - "TextFields." % connection.display_name, + '%s does not support a database collation on ' + 'TextFields.' % connection.display_name, obj=self, - id="fields.E190", + id='fields.E190', ), ) return errors @@ -2352,42 +2162,35 @@ class TextField(Field): # Passing max_length to forms.CharField means that the value's length # will be validated twice. This is considered acceptable since we want # the value in the form field (to pass into widget for example). - return super().formfield( - **{ - "max_length": self.max_length, - **({} if self.choices is not None else {"widget": forms.Textarea}), - **kwargs, - } - ) + return super().formfield(**{ + 'max_length': self.max_length, + **({} if self.choices is not None else {'widget': forms.Textarea}), + **kwargs, + }) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.db_collation: - kwargs["db_collation"] = self.db_collation + kwargs['db_collation'] = self.db_collation return name, path, args, kwargs class TimeField(DateTimeCheckMixin, Field): empty_strings_allowed = False default_error_messages = { - "invalid": _( - "“%(value)s” value has an invalid format. It must be in " - "HH:MM[:ss[.uuuuuu]] format." - ), - "invalid_time": _( - "“%(value)s” value has the correct format " - "(HH:MM[:ss[.uuuuuu]]) but it is an invalid time." - ), + 'invalid': _('“%(value)s” value has an invalid format. It must be in ' + 'HH:MM[:ss[.uuuuuu]] format.'), + 'invalid_time': _('“%(value)s” value has the correct format ' + '(HH:MM[:ss[.uuuuuu]]) but it is an invalid time.'), } description = _("Time") - def __init__( - self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs - ): + def __init__(self, verbose_name=None, name=None, auto_now=False, + auto_now_add=False, **kwargs): self.auto_now, self.auto_now_add = auto_now, auto_now_add if auto_now or auto_now_add: - kwargs["editable"] = False - kwargs["blank"] = True + kwargs['editable'] = False + kwargs['blank'] = True super().__init__(verbose_name, name, **kwargs) def _check_fix_default_value(self): @@ -2398,19 +2201,40 @@ class TimeField(DateTimeCheckMixin, Field): if not self.has_default(): return [] + now = timezone.now() + if not timezone.is_naive(now): + now = timezone.make_naive(now, timezone.utc) value = self.default if isinstance(value, datetime.datetime): - now = None + second_offset = datetime.timedelta(seconds=10) + lower = now - second_offset + upper = now + second_offset + if timezone.is_aware(value): + value = timezone.make_naive(value, timezone.utc) elif isinstance(value, datetime.time): - now = _get_naive_now() - # This will not use the right date in the race condition where now - # is just before the date change and value is just past 0:00. + second_offset = datetime.timedelta(seconds=10) + lower = now - second_offset + upper = now + second_offset value = datetime.datetime.combine(now.date(), value) + if timezone.is_aware(value): + value = timezone.make_naive(value, timezone.utc).time() else: # No explicit time / datetime value -- no checks necessary return [] - # At this point, value is a datetime object. - return self._check_if_value_fixed(value, now=now) + if lower <= value <= upper: + return [ + checks.Warning( + 'Fixed default value provided.', + hint='It seems you set a fixed date / time / datetime ' + 'value as default for this field. This may not be ' + 'what you want. If you want to have the current date ' + 'as default, use `django.utils.timezone.now`', + obj=self, + id='fields.W161', + ) + ] + + return [] def deconstruct(self): name, path, args, kwargs = super().deconstruct() @@ -2419,8 +2243,8 @@ class TimeField(DateTimeCheckMixin, Field): if self.auto_now_add is not False: kwargs["auto_now_add"] = self.auto_now_add if self.auto_now or self.auto_now_add: - del kwargs["blank"] - del kwargs["editable"] + del kwargs['blank'] + del kwargs['editable'] return name, path, args, kwargs def get_internal_type(self): @@ -2443,15 +2267,15 @@ class TimeField(DateTimeCheckMixin, Field): return parsed except ValueError: raise exceptions.ValidationError( - self.error_messages["invalid_time"], - code="invalid_time", - params={"value": value}, + self.error_messages['invalid_time'], + code='invalid_time', + params={'value': value}, ) raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def pre_save(self, model_instance, add): @@ -2474,15 +2298,13 @@ class TimeField(DateTimeCheckMixin, Field): def value_to_string(self, obj): val = self.value_from_object(obj) - return "" if val is None else val.isoformat() + return '' if val is None else val.isoformat() def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.TimeField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.TimeField, + **kwargs, + }) class URLField(CharField): @@ -2490,32 +2312,30 @@ class URLField(CharField): description = _("URL") def __init__(self, verbose_name=None, name=None, **kwargs): - kwargs.setdefault("max_length", 200) + kwargs.setdefault('max_length', 200) super().__init__(verbose_name, name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 200: - del kwargs["max_length"] + del kwargs['max_length'] return name, path, args, kwargs def formfield(self, **kwargs): # As with CharField, this will cause URL validation to be performed # twice. - return super().formfield( - **{ - "form_class": forms.URLField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.URLField, + **kwargs, + }) class BinaryField(Field): description = _("Raw binary data") - empty_values = [None, b""] + empty_values = [None, b''] def __init__(self, *args, **kwargs): - kwargs.setdefault("editable", False) + kwargs.setdefault('editable', False) super().__init__(*args, **kwargs) if self.max_length is not None: self.validators.append(validators.MaxLengthValidator(self.max_length)) @@ -2530,7 +2350,7 @@ class BinaryField(Field): "BinaryField's default cannot be a string. Use bytes " "content instead.", obj=self, - id="fields.E170", + id='fields.E170', ) ] return [] @@ -2538,9 +2358,9 @@ class BinaryField(Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.editable: - kwargs["editable"] = True + kwargs['editable'] = True else: - del kwargs["editable"] + del kwargs['editable'] return name, path, args, kwargs def get_internal_type(self): @@ -2553,8 +2373,8 @@ class BinaryField(Field): if self.has_default() and not callable(self.default): return self.default default = super().get_default() - if default == "": - return b"" + if default == '': + return b'' return default def get_db_prep_value(self, value, connection, prepared=False): @@ -2565,29 +2385,29 @@ class BinaryField(Field): def value_to_string(self, obj): """Binary data is serialized as base64""" - return b64encode(self.value_from_object(obj)).decode("ascii") + return b64encode(self.value_from_object(obj)).decode('ascii') def to_python(self, value): # If it's a string, it should be base64-encoded data if isinstance(value, str): - return memoryview(b64decode(value.encode("ascii"))) + return memoryview(b64decode(value.encode('ascii'))) return value class UUIDField(Field): default_error_messages = { - "invalid": _("“%(value)s” is not a valid UUID."), + 'invalid': _('“%(value)s” is not a valid UUID.'), } - description = _("Universally unique identifier") + description = _('Universally unique identifier') empty_strings_allowed = False def __init__(self, verbose_name=None, **kwargs): - kwargs["max_length"] = 32 + kwargs['max_length'] = 32 super().__init__(verbose_name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs["max_length"] + del kwargs['max_length'] return name, path, args, kwargs def get_internal_type(self): @@ -2609,31 +2429,29 @@ class UUIDField(Field): def to_python(self, value): if value is not None and not isinstance(value, uuid.UUID): - input_form = "int" if isinstance(value, int) else "hex" + input_form = 'int' if isinstance(value, int) else 'hex' try: return uuid.UUID(**{input_form: value}) except (AttributeError, ValueError): raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) return value def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.UUIDField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.UUIDField, + **kwargs, + }) class AutoFieldMixin: db_returning = True def __init__(self, *args, **kwargs): - kwargs["blank"] = True + kwargs['blank'] = True super().__init__(*args, **kwargs) def check(self, **kwargs): @@ -2646,9 +2464,9 @@ class AutoFieldMixin: if not self.primary_key: return [ checks.Error( - "AutoFields must set primary_key=True.", + 'AutoFields must set primary_key=True.', obj=self, - id="fields.E100", + id='fields.E100', ), ] else: @@ -2656,8 +2474,8 @@ class AutoFieldMixin: def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs["blank"] - kwargs["primary_key"] = True + del kwargs['blank'] + kwargs['primary_key'] = True return name, path, args, kwargs def validate(self, value, model_instance): @@ -2670,11 +2488,10 @@ class AutoFieldMixin: return value def contribute_to_class(self, cls, name, **kwargs): - if cls._meta.auto_field: - raise ValueError( - "Model %s can't have more than one auto-generated field." - % cls._meta.label - ) + assert not cls._meta.auto_field, ( + "Model %s can't have more than one auto-generated field." + % cls._meta.label + ) super().contribute_to_class(cls, name, **kwargs) cls._meta.auto_field = self @@ -2704,35 +2521,34 @@ class AutoFieldMeta(type): return (BigAutoField, SmallAutoField) def __instancecheck__(self, instance): - return isinstance(instance, self._subclasses) or super().__instancecheck__( - instance - ) + return isinstance(instance, self._subclasses) or super().__instancecheck__(instance) def __subclasscheck__(self, subclass): - return issubclass(subclass, self._subclasses) or super().__subclasscheck__( - subclass - ) + return issubclass(subclass, self._subclasses) or super().__subclasscheck__(subclass) class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta): + def get_internal_type(self): - return "AutoField" + return 'AutoField' def rel_db_type(self, connection): return IntegerField().db_type(connection=connection) class BigAutoField(AutoFieldMixin, BigIntegerField): + def get_internal_type(self): - return "BigAutoField" + return 'BigAutoField' def rel_db_type(self, connection): return BigIntegerField().db_type(connection=connection) class SmallAutoField(AutoFieldMixin, SmallIntegerField): + def get_internal_type(self): - return "SmallAutoField" + return 'SmallAutoField' def rel_db_type(self, connection): return SmallIntegerField().db_type(connection=connection) diff --git a/venv/Lib/site-packages/django/db/models/fields/files.py b/venv/Lib/site-packages/django/db/models/fields/files.py index 1a3a0ce..18900f7 100644 --- a/venv/Lib/site-packages/django/db/models/fields/files.py +++ b/venv/Lib/site-packages/django/db/models/fields/files.py @@ -24,7 +24,7 @@ class FieldFile(File): def __eq__(self, other): # Older code may be expecting FileField values to be simple strings. # By overriding the == operator, it can remain backwards compatibility. - if hasattr(other, "name"): + if hasattr(other, 'name'): return self.name == other.name return self.name == other @@ -37,14 +37,12 @@ class FieldFile(File): def _require_file(self): if not self: - raise ValueError( - "The '%s' attribute has no file associated with it." % self.field.name - ) + raise ValueError("The '%s' attribute has no file associated with it." % self.field.name) def _get_file(self): self._require_file() - if getattr(self, "_file", None) is None: - self._file = self.storage.open(self.name, "rb") + if getattr(self, '_file', None) is None: + self._file = self.storage.open(self.name, 'rb') return self._file def _set_file(self, file): @@ -72,14 +70,13 @@ class FieldFile(File): return self.file.size return self.storage.size(self.name) - def open(self, mode="rb"): + def open(self, mode='rb'): self._require_file() - if getattr(self, "_file", None) is None: + if getattr(self, '_file', None) is None: self.file = self.storage.open(self.name, mode) else: self.file.open(mode) return self - # open() doesn't alter the file's contents, but it does reset the pointer open.alters_data = True @@ -96,7 +93,6 @@ class FieldFile(File): # Save the object because it has changed, unless save is False if save: self.instance.save() - save.alters_data = True def delete(self, save=True): @@ -104,7 +100,7 @@ class FieldFile(File): return # Only close the file if it's already open, which we know by the # presence of self._file - if hasattr(self, "_file"): + if hasattr(self, '_file'): self.close() del self.file @@ -116,16 +112,15 @@ class FieldFile(File): if save: self.instance.save() - delete.alters_data = True @property def closed(self): - file = getattr(self, "_file", None) + file = getattr(self, '_file', None) return file is None or file.closed def close(self): - file = getattr(self, "_file", None) + file = getattr(self, '_file', None) if file is not None: file.close() @@ -134,12 +129,12 @@ class FieldFile(File): # the file's name. Everything else will be restored later, by # FileDescriptor below. return { - "name": self.name, - "closed": False, - "_committed": True, - "_file": None, - "instance": self.instance, - "field": self.field, + 'name': self.name, + 'closed': False, + '_committed': True, + '_file': None, + 'instance': self.instance, + 'field': self.field, } def __setstate__(self, state): @@ -161,7 +156,6 @@ class FileDescriptor(DeferredAttribute): >>> with open('/path/to/hello.world') as f: ... instance.file = File(f) """ - def __get__(self, instance, cls=None): if instance is None: return self @@ -204,7 +198,7 @@ class FileDescriptor(DeferredAttribute): # Finally, because of the (some would say boneheaded) way pickle works, # the underlying FieldFile might not actually itself have an associated # file. So we need to reset the details of the FieldFile in those cases. - elif isinstance(file, FieldFile) and not hasattr(file, "field"): + elif isinstance(file, FieldFile) and not hasattr(file, 'field'): file.instance = instance file.field = self.field file.storage = self.field.storage @@ -231,10 +225,8 @@ class FileField(Field): description = _("File") - def __init__( - self, verbose_name=None, name=None, upload_to="", storage=None, **kwargs - ): - self._primary_key_set_explicitly = "primary_key" in kwargs + def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): + self._primary_key_set_explicitly = 'primary_key' in kwargs self.storage = storage or default_storage if callable(self.storage): @@ -244,15 +236,11 @@ class FileField(Field): if not isinstance(self.storage, Storage): raise TypeError( "%s.storage must be a subclass/instance of %s.%s" - % ( - self.__class__.__qualname__, - Storage.__module__, - Storage.__qualname__, - ) + % (self.__class__.__qualname__, Storage.__module__, Storage.__qualname__) ) self.upload_to = upload_to - kwargs.setdefault("max_length", 100) + kwargs.setdefault('max_length', 100) super().__init__(verbose_name, name, **kwargs) def check(self, **kwargs): @@ -266,24 +254,23 @@ class FileField(Field): if self._primary_key_set_explicitly: return [ checks.Error( - "'primary_key' is not a valid argument for a %s." - % self.__class__.__name__, + "'primary_key' is not a valid argument for a %s." % self.__class__.__name__, obj=self, - id="fields.E201", + id='fields.E201', ) ] else: return [] def _check_upload_to(self): - if isinstance(self.upload_to, str) and self.upload_to.startswith("/"): + if isinstance(self.upload_to, str) and self.upload_to.startswith('/'): return [ checks.Error( "%s's 'upload_to' argument must be a relative path, not an " "absolute path." % self.__class__.__name__, obj=self, - id="fields.E202", - hint="Remove the leading slash.", + id='fields.E202', + hint='Remove the leading slash.', ) ] else: @@ -293,9 +280,9 @@ class FileField(Field): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 100: del kwargs["max_length"] - kwargs["upload_to"] = self.upload_to + kwargs['upload_to'] = self.upload_to if self.storage is not default_storage: - kwargs["storage"] = getattr(self, "_storage_callable", self.storage) + kwargs['storage'] = getattr(self, '_storage_callable', self.storage) return name, path, args, kwargs def get_internal_type(self): @@ -303,8 +290,7 @@ class FileField(Field): def get_prep_value(self, value): value = super().get_prep_value(value) - # Need to convert File objects provided via a form to string for - # database insertion. + # Need to convert File objects provided via a form to string for database insertion if value is None: return None return str(value) @@ -343,16 +329,14 @@ class FileField(Field): if data is not None: # This value will be converted to str and stored in the # database, so leaving False as-is is not acceptable. - setattr(instance, self.name, data or "") + setattr(instance, self.name, data or '') def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.FileField, - "max_length": self.max_length, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.FileField, + 'max_length': self.max_length, + **kwargs, + }) class ImageFileDescriptor(FileDescriptor): @@ -360,7 +344,6 @@ class ImageFileDescriptor(FileDescriptor): Just like the FileDescriptor, but for ImageFields. The only difference is assigning the width/height to the width_field/height_field, if appropriate. """ - def __set__(self, instance, value): previous_file = instance.__dict__.get(self.field.attname) super().__set__(instance, value) @@ -381,7 +364,7 @@ class ImageFileDescriptor(FileDescriptor): class ImageFieldFile(ImageFile, FieldFile): def delete(self, save=True): # Clear the image dimensions cache - if hasattr(self, "_dimensions_cache"): + if hasattr(self, '_dimensions_cache'): del self._dimensions_cache super().delete(save) @@ -391,14 +374,7 @@ class ImageField(FileField): descriptor_class = ImageFileDescriptor description = _("Image") - def __init__( - self, - verbose_name=None, - name=None, - width_field=None, - height_field=None, - **kwargs, - ): + def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): self.width_field, self.height_field = width_field, height_field super().__init__(verbose_name, name, **kwargs) @@ -414,13 +390,11 @@ class ImageField(FileField): except ImportError: return [ checks.Error( - "Cannot use ImageField because Pillow is not installed.", - hint=( - "Get Pillow at https://pypi.org/project/Pillow/ " - 'or run command "python -m pip install Pillow".' - ), + 'Cannot use ImageField because Pillow is not installed.', + hint=('Get Pillow at https://pypi.org/project/Pillow/ ' + 'or run command "python -m pip install Pillow".'), obj=self, - id="fields.E210", + id='fields.E210', ) ] else: @@ -429,9 +403,9 @@ class ImageField(FileField): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.width_field: - kwargs["width_field"] = self.width_field + kwargs['width_field'] = self.width_field if self.height_field: - kwargs["height_field"] = self.height_field + kwargs['height_field'] = self.height_field return name, path, args, kwargs def contribute_to_class(self, cls, name, **kwargs): @@ -471,9 +445,9 @@ class ImageField(FileField): if not file and not force: return - dimension_fields_filled = not ( - (self.width_field and not getattr(instance, self.width_field)) - or (self.height_field and not getattr(instance, self.height_field)) + dimension_fields_filled = not( + (self.width_field and not getattr(instance, self.width_field)) or + (self.height_field and not getattr(instance, self.height_field)) ) # When both dimension fields have values, we are most likely loading # data from the database or updating an image field that already had @@ -501,9 +475,7 @@ class ImageField(FileField): setattr(instance, self.height_field, height) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.ImageField, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.ImageField, + **kwargs, + }) diff --git a/venv/Lib/site-packages/django/db/models/fields/json.py b/venv/Lib/site-packages/django/db/models/fields/json.py index fdca700..1b06010 100644 --- a/venv/Lib/site-packages/django/db/models/fields/json.py +++ b/venv/Lib/site-packages/django/db/models/fields/json.py @@ -10,36 +10,32 @@ from django.utils.translation import gettext_lazy as _ from . import Field from .mixins import CheckFieldDefaultMixin -__all__ = ["JSONField"] +__all__ = ['JSONField'] class JSONField(CheckFieldDefaultMixin, Field): empty_strings_allowed = False - description = _("A JSON object") + description = _('A JSON object') default_error_messages = { - "invalid": _("Value must be valid JSON."), + 'invalid': _('Value must be valid JSON.'), } - _default_hint = ("dict", "{}") + _default_hint = ('dict', '{}') def __init__( - self, - verbose_name=None, - name=None, - encoder=None, - decoder=None, + self, verbose_name=None, name=None, encoder=None, decoder=None, **kwargs, ): if encoder and not callable(encoder): - raise ValueError("The encoder parameter must be a callable object.") + raise ValueError('The encoder parameter must be a callable object.') if decoder and not callable(decoder): - raise ValueError("The decoder parameter must be a callable object.") + raise ValueError('The decoder parameter must be a callable object.') self.encoder = encoder self.decoder = decoder super().__init__(verbose_name, name, **kwargs) def check(self, **kwargs): errors = super().check(**kwargs) - databases = kwargs.get("databases") or [] + databases = kwargs.get('databases') or [] errors.extend(self._check_supported(databases)) return errors @@ -50,19 +46,20 @@ class JSONField(CheckFieldDefaultMixin, Field): continue connection = connections[db] if ( - self.model._meta.required_db_vendor - and self.model._meta.required_db_vendor != connection.vendor + self.model._meta.required_db_vendor and + self.model._meta.required_db_vendor != connection.vendor ): continue if not ( - "supports_json_field" in self.model._meta.required_db_features - or connection.features.supports_json_field + 'supports_json_field' in self.model._meta.required_db_features or + connection.features.supports_json_field ): errors.append( checks.Error( - "%s does not support JSONFields." % connection.display_name, + '%s does not support JSONFields.' + % connection.display_name, obj=self.model, - id="fields.E180", + id='fields.E180', ) ) return errors @@ -70,9 +67,9 @@ class JSONField(CheckFieldDefaultMixin, Field): def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.encoder is not None: - kwargs["encoder"] = self.encoder + kwargs['encoder'] = self.encoder if self.decoder is not None: - kwargs["decoder"] = self.decoder + kwargs['decoder'] = self.decoder return name, path, args, kwargs def from_db_value(self, value, expression, connection): @@ -88,7 +85,7 @@ class JSONField(CheckFieldDefaultMixin, Field): return value def get_internal_type(self): - return "JSONField" + return 'JSONField' def get_prep_value(self, value): if value is None: @@ -107,66 +104,64 @@ class JSONField(CheckFieldDefaultMixin, Field): json.dumps(value, cls=self.encoder) except TypeError: raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) def value_to_string(self, obj): return self.value_from_object(obj) def formfield(self, **kwargs): - return super().formfield( - **{ - "form_class": forms.JSONField, - "encoder": self.encoder, - "decoder": self.decoder, - **kwargs, - } - ) + return super().formfield(**{ + 'form_class': forms.JSONField, + 'encoder': self.encoder, + 'decoder': self.decoder, + **kwargs, + }) def compile_json_path(key_transforms, include_root=True): - path = ["$"] if include_root else [] + path = ['$'] if include_root else [] for key_transform in key_transforms: try: num = int(key_transform) except ValueError: # non-integer - path.append(".") + path.append('.') path.append(json.dumps(key_transform)) else: - path.append("[%s]" % num) - return "".join(path) + path.append('[%s]' % num) + return ''.join(path) class DataContains(PostgresOperatorLookup): - lookup_name = "contains" - postgres_operator = "@>" + lookup_name = 'contains' + postgres_operator = '@>' def as_sql(self, compiler, connection): if not connection.features.supports_json_field_contains: raise NotSupportedError( - "contains lookup is not supported on this database backend." + 'contains lookup is not supported on this database backend.' ) lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = tuple(lhs_params) + tuple(rhs_params) - return "JSON_CONTAINS(%s, %s)" % (lhs, rhs), params + return 'JSON_CONTAINS(%s, %s)' % (lhs, rhs), params class ContainedBy(PostgresOperatorLookup): - lookup_name = "contained_by" - postgres_operator = "<@" + lookup_name = 'contained_by' + postgres_operator = '<@' def as_sql(self, compiler, connection): if not connection.features.supports_json_field_contains: raise NotSupportedError( - "contained_by lookup is not supported on this database backend." + 'contained_by lookup is not supported on this database backend.' ) lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = tuple(rhs_params) + tuple(lhs_params) - return "JSON_CONTAINS(%s, %s)" % (rhs, lhs), params + return 'JSON_CONTAINS(%s, %s)' % (rhs, lhs), params class HasKeyLookup(PostgresOperatorLookup): @@ -175,13 +170,11 @@ class HasKeyLookup(PostgresOperatorLookup): def as_sql(self, compiler, connection, template=None): # Process JSON path from the left-hand side. if isinstance(self.lhs, KeyTransform): - lhs, lhs_params, lhs_key_transforms = self.lhs.preprocess_lhs( - compiler, connection - ) + lhs, lhs_params, lhs_key_transforms = self.lhs.preprocess_lhs(compiler, connection) lhs_json_path = compile_json_path(lhs_key_transforms) else: lhs, lhs_params = self.process_lhs(compiler, connection) - lhs_json_path = "$" + lhs_json_path = '$' sql = template % lhs # Process JSON path from the right-hand side. rhs = self.rhs @@ -193,27 +186,20 @@ class HasKeyLookup(PostgresOperatorLookup): *_, rhs_key_transforms = key.preprocess_lhs(compiler, connection) else: rhs_key_transforms = [key] - rhs_params.append( - "%s%s" - % ( - lhs_json_path, - compile_json_path(rhs_key_transforms, include_root=False), - ) - ) + rhs_params.append('%s%s' % ( + lhs_json_path, + compile_json_path(rhs_key_transforms, include_root=False), + )) # Add condition for each key. if self.logical_operator: - sql = "(%s)" % self.logical_operator.join([sql] * len(rhs_params)) + sql = '(%s)' % self.logical_operator.join([sql] * len(rhs_params)) return sql, tuple(lhs_params) + tuple(rhs_params) def as_mysql(self, compiler, connection): - return self.as_sql( - compiler, connection, template="JSON_CONTAINS_PATH(%s, 'one', %%s)" - ) + return self.as_sql(compiler, connection, template="JSON_CONTAINS_PATH(%s, 'one', %%s)") def as_oracle(self, compiler, connection): - sql, params = self.as_sql( - compiler, connection, template="JSON_EXISTS(%s, '%%s')" - ) + sql, params = self.as_sql(compiler, connection, template="JSON_EXISTS(%s, '%%s')") # Add paths directly into SQL because path expressions cannot be passed # as bind variables on Oracle. return sql % tuple(params), [] @@ -227,83 +213,64 @@ class HasKeyLookup(PostgresOperatorLookup): return super().as_postgresql(compiler, connection) def as_sqlite(self, compiler, connection): - return self.as_sql( - compiler, connection, template="JSON_TYPE(%s, %%s) IS NOT NULL" - ) + return self.as_sql(compiler, connection, template='JSON_TYPE(%s, %%s) IS NOT NULL') class HasKey(HasKeyLookup): - lookup_name = "has_key" - postgres_operator = "?" + lookup_name = 'has_key' + postgres_operator = '?' prepare_rhs = False class HasKeys(HasKeyLookup): - lookup_name = "has_keys" - postgres_operator = "?&" - logical_operator = " AND " + lookup_name = 'has_keys' + postgres_operator = '?&' + logical_operator = ' AND ' def get_prep_lookup(self): return [str(item) for item in self.rhs] class HasAnyKeys(HasKeys): - lookup_name = "has_any_keys" - postgres_operator = "?|" - logical_operator = " OR " - - -class CaseInsensitiveMixin: - """ - Mixin to allow case-insensitive comparison of JSON values on MySQL. - MySQL handles strings used in JSON context using the utf8mb4_bin collation. - Because utf8mb4_bin is a binary collation, comparison of JSON values is - case-sensitive. - """ - - def process_lhs(self, compiler, connection): - lhs, lhs_params = super().process_lhs(compiler, connection) - if connection.vendor == "mysql": - return "LOWER(%s)" % lhs, lhs_params - return lhs, lhs_params - - def process_rhs(self, compiler, connection): - rhs, rhs_params = super().process_rhs(compiler, connection) - if connection.vendor == "mysql": - return "LOWER(%s)" % rhs, rhs_params - return rhs, rhs_params + lookup_name = 'has_any_keys' + postgres_operator = '?|' + logical_operator = ' OR ' class JSONExact(lookups.Exact): can_use_none_as_rhs = True + def process_lhs(self, compiler, connection): + lhs, lhs_params = super().process_lhs(compiler, connection) + if connection.vendor == 'sqlite': + rhs, rhs_params = super().process_rhs(compiler, connection) + if rhs == '%s' and rhs_params == [None]: + # Use JSON_TYPE instead of JSON_EXTRACT for NULLs. + lhs = "JSON_TYPE(%s, '$')" % lhs + return lhs, lhs_params + def process_rhs(self, compiler, connection): rhs, rhs_params = super().process_rhs(compiler, connection) # Treat None lookup values as null. - if rhs == "%s" and rhs_params == [None]: - rhs_params = ["null"] - if connection.vendor == "mysql": + if rhs == '%s' and rhs_params == [None]: + rhs_params = ['null'] + if connection.vendor == 'mysql': func = ["JSON_EXTRACT(%s, '$')"] * len(rhs_params) rhs = rhs % tuple(func) return rhs, rhs_params -class JSONIContains(CaseInsensitiveMixin, lookups.IContains): - pass - - JSONField.register_lookup(DataContains) JSONField.register_lookup(ContainedBy) JSONField.register_lookup(HasKey) JSONField.register_lookup(HasKeys) JSONField.register_lookup(HasAnyKeys) JSONField.register_lookup(JSONExact) -JSONField.register_lookup(JSONIContains) class KeyTransform(Transform): - postgres_operator = "->" - postgres_nested_operator = "#>" + postgres_operator = '->' + postgres_nested_operator = '#>' def __init__(self, key_name, *args, **kwargs): super().__init__(*args, **kwargs) @@ -316,50 +283,44 @@ class KeyTransform(Transform): key_transforms.insert(0, previous.key_name) previous = previous.lhs lhs, params = compiler.compile(previous) - if connection.vendor == "oracle": + if connection.vendor == 'oracle': # Escape string-formatting. - key_transforms = [key.replace("%", "%%") for key in key_transforms] + key_transforms = [key.replace('%', '%%') for key in key_transforms] return lhs, params, key_transforms def as_mysql(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) json_path = compile_json_path(key_transforms) - return "JSON_EXTRACT(%s, %%s)" % lhs, tuple(params) + (json_path,) + return 'JSON_EXTRACT(%s, %%s)' % lhs, tuple(params) + (json_path,) def as_oracle(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) json_path = compile_json_path(key_transforms) return ( - "COALESCE(JSON_QUERY(%s, '%s'), JSON_VALUE(%s, '%s'))" - % ((lhs, json_path) * 2) + "COALESCE(JSON_QUERY(%s, '%s'), JSON_VALUE(%s, '%s'))" % + ((lhs, json_path) * 2) ), tuple(params) * 2 def as_postgresql(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) if len(key_transforms) > 1: - sql = "(%s %s %%s)" % (lhs, self.postgres_nested_operator) + sql = '(%s %s %%s)' % (lhs, self.postgres_nested_operator) return sql, tuple(params) + (key_transforms,) try: lookup = int(self.key_name) except ValueError: lookup = self.key_name - return "(%s %s %%s)" % (lhs, self.postgres_operator), tuple(params) + (lookup,) + return '(%s %s %%s)' % (lhs, self.postgres_operator), tuple(params) + (lookup,) def as_sqlite(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) json_path = compile_json_path(key_transforms) - datatype_values = ",".join( - [repr(datatype) for datatype in connection.ops.jsonfield_datatype_values] - ) - return ( - "(CASE WHEN JSON_TYPE(%s, %%s) IN (%s) " - "THEN JSON_TYPE(%s, %%s) ELSE JSON_EXTRACT(%s, %%s) END)" - ) % (lhs, datatype_values, lhs, lhs), (tuple(params) + (json_path,)) * 3 + return 'JSON_EXTRACT(%s, %%s)' % lhs, tuple(params) + (json_path,) class KeyTextTransform(KeyTransform): - postgres_operator = "->>" - postgres_nested_operator = "#>>" + postgres_operator = '->>' + postgres_nested_operator = '#>>' class KeyTransformTextLookupMixin: @@ -369,21 +330,39 @@ class KeyTransformTextLookupMixin: key values to text and performing the lookup on the resulting representation. """ - def __init__(self, key_transform, *args, **kwargs): if not isinstance(key_transform, KeyTransform): raise TypeError( - "Transform should be an instance of KeyTransform in order to " - "use this lookup." + 'Transform should be an instance of KeyTransform in order to ' + 'use this lookup.' ) key_text_transform = KeyTextTransform( - key_transform.key_name, - *key_transform.source_expressions, + key_transform.key_name, *key_transform.source_expressions, **key_transform.extra, ) super().__init__(key_text_transform, *args, **kwargs) +class CaseInsensitiveMixin: + """ + Mixin to allow case-insensitive comparison of JSON values on MySQL. + MySQL handles strings used in JSON context using the utf8mb4_bin collation. + Because utf8mb4_bin is a binary collation, comparison of JSON values is + case-sensitive. + """ + def process_lhs(self, compiler, connection): + lhs, lhs_params = super().process_lhs(compiler, connection) + if connection.vendor == 'mysql': + return 'LOWER(%s)' % lhs, lhs_params + return lhs, lhs_params + + def process_rhs(self, compiler, connection): + rhs, rhs_params = super().process_rhs(compiler, connection) + if connection.vendor == 'mysql': + return 'LOWER(%s)' % rhs, rhs_params + return rhs, rhs_params + + class KeyTransformIsNull(lookups.IsNull): # key__isnull=False is the same as has_key='key' def as_oracle(self, compiler, connection): @@ -395,12 +374,12 @@ class KeyTransformIsNull(lookups.IsNull): return sql, params # Column doesn't have a key or IS NULL. lhs, lhs_params, _ = self.lhs.preprocess_lhs(compiler, connection) - return "(NOT %s OR %s IS NULL)" % (sql, lhs), tuple(params) + tuple(lhs_params) + return '(NOT %s OR %s IS NULL)' % (sql, lhs), tuple(params) + tuple(lhs_params) def as_sqlite(self, compiler, connection): - template = "JSON_TYPE(%s, %%s) IS NULL" + template = 'JSON_TYPE(%s, %%s) IS NULL' if not self.rhs: - template = "JSON_TYPE(%s, %%s) IS NOT NULL" + template = 'JSON_TYPE(%s, %%s) IS NOT NULL' return HasKey(self.lhs.lhs, self.lhs.key_name).as_sql( compiler, connection, @@ -411,81 +390,75 @@ class KeyTransformIsNull(lookups.IsNull): class KeyTransformIn(lookups.In): def resolve_expression_parameter(self, compiler, connection, sql, param): sql, params = super().resolve_expression_parameter( - compiler, - connection, - sql, - param, + compiler, connection, sql, param, ) if ( - not hasattr(param, "as_sql") - and not connection.features.has_native_json_field + not hasattr(param, 'as_sql') and + not connection.features.has_native_json_field ): - if connection.vendor == "oracle": + if connection.vendor == 'oracle': value = json.loads(param) sql = "%s(JSON_OBJECT('value' VALUE %%s FORMAT JSON), '$.value')" if isinstance(value, (list, dict)): - sql = sql % "JSON_QUERY" + sql = sql % 'JSON_QUERY' else: - sql = sql % "JSON_VALUE" - elif connection.vendor == "mysql" or ( - connection.vendor == "sqlite" - and params[0] not in connection.ops.jsonfield_datatype_values - ): + sql = sql % 'JSON_VALUE' + elif connection.vendor in {'sqlite', 'mysql'}: sql = "JSON_EXTRACT(%s, '$')" - if connection.vendor == "mysql" and connection.mysql_is_mariadb: - sql = "JSON_UNQUOTE(%s)" % sql + if connection.vendor == 'mysql' and connection.mysql_is_mariadb: + sql = 'JSON_UNQUOTE(%s)' % sql return sql, params class KeyTransformExact(JSONExact): + def process_lhs(self, compiler, connection): + lhs, lhs_params = super().process_lhs(compiler, connection) + if connection.vendor == 'sqlite': + rhs, rhs_params = super().process_rhs(compiler, connection) + if rhs == '%s' and rhs_params == ['null']: + lhs, *_ = self.lhs.preprocess_lhs(compiler, connection) + lhs = 'JSON_TYPE(%s, %%s)' % lhs + return lhs, lhs_params + def process_rhs(self, compiler, connection): if isinstance(self.rhs, KeyTransform): return super(lookups.Exact, self).process_rhs(compiler, connection) rhs, rhs_params = super().process_rhs(compiler, connection) - if connection.vendor == "oracle": + if connection.vendor == 'oracle': func = [] sql = "%s(JSON_OBJECT('value' VALUE %%s FORMAT JSON), '$.value')" for value in rhs_params: value = json.loads(value) if isinstance(value, (list, dict)): - func.append(sql % "JSON_QUERY") + func.append(sql % 'JSON_QUERY') else: - func.append(sql % "JSON_VALUE") + func.append(sql % 'JSON_VALUE') rhs = rhs % tuple(func) - elif connection.vendor == "sqlite": - func = [] - for value in rhs_params: - if value in connection.ops.jsonfield_datatype_values: - func.append("%s") - else: - func.append("JSON_EXTRACT(%s, '$')") + elif connection.vendor == 'sqlite': + func = ["JSON_EXTRACT(%s, '$')" if value != 'null' else '%s' for value in rhs_params] rhs = rhs % tuple(func) return rhs, rhs_params def as_oracle(self, compiler, connection): rhs, rhs_params = super().process_rhs(compiler, connection) - if rhs_params == ["null"]: + if rhs_params == ['null']: # Field has key and it's NULL. has_key_expr = HasKey(self.lhs.lhs, self.lhs.key_name) has_key_sql, has_key_params = has_key_expr.as_oracle(compiler, connection) - is_null_expr = self.lhs.get_lookup("isnull")(self.lhs, True) + is_null_expr = self.lhs.get_lookup('isnull')(self.lhs, True) is_null_sql, is_null_params = is_null_expr.as_sql(compiler, connection) return ( - "%s AND %s" % (has_key_sql, is_null_sql), + '%s AND %s' % (has_key_sql, is_null_sql), tuple(has_key_params) + tuple(is_null_params), ) return super().as_sql(compiler, connection) -class KeyTransformIExact( - CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IExact -): +class KeyTransformIExact(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IExact): pass -class KeyTransformIContains( - CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IContains -): +class KeyTransformIContains(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IContains): pass @@ -493,9 +466,7 @@ class KeyTransformStartsWith(KeyTransformTextLookupMixin, lookups.StartsWith): pass -class KeyTransformIStartsWith( - CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IStartsWith -): +class KeyTransformIStartsWith(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IStartsWith): pass @@ -503,9 +474,7 @@ class KeyTransformEndsWith(KeyTransformTextLookupMixin, lookups.EndsWith): pass -class KeyTransformIEndsWith( - CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IEndsWith -): +class KeyTransformIEndsWith(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IEndsWith): pass @@ -513,9 +482,7 @@ class KeyTransformRegex(KeyTransformTextLookupMixin, lookups.Regex): pass -class KeyTransformIRegex( - CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IRegex -): +class KeyTransformIRegex(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IRegex): pass @@ -562,6 +529,7 @@ KeyTransform.register_lookup(KeyTransformGte) class KeyTransformFactory: + def __init__(self, key_name): self.key_name = key_name diff --git a/venv/Lib/site-packages/django/db/models/fields/mixins.py b/venv/Lib/site-packages/django/db/models/fields/mixins.py index e7f2822..3afa8d9 100644 --- a/venv/Lib/site-packages/django/db/models/fields/mixins.py +++ b/venv/Lib/site-packages/django/db/models/fields/mixins.py @@ -29,25 +29,22 @@ class FieldCacheMixin: class CheckFieldDefaultMixin: - _default_hint = ("<valid default>", "<invalid default>") + _default_hint = ('<valid default>', '<invalid default>') def _check_default(self): - if ( - self.has_default() - and self.default is not None - and not callable(self.default) - ): + if self.has_default() and self.default is not None and not callable(self.default): return [ checks.Warning( "%s default should be a callable instead of an instance " - "so that it's not shared between all field instances." - % (self.__class__.__name__,), + "so that it's not shared between all field instances." % ( + self.__class__.__name__, + ), hint=( - "Use a callable instead, e.g., use `%s` instead of " - "`%s`." % self._default_hint + 'Use a callable instead, e.g., use `%s` instead of ' + '`%s`.' % self._default_hint ), obj=self, - id="fields.E010", + id='fields.E010', ) ] else: diff --git a/venv/Lib/site-packages/django/db/models/fields/proxy.py b/venv/Lib/site-packages/django/db/models/fields/proxy.py index ac02e47..0ecf04a 100644 --- a/venv/Lib/site-packages/django/db/models/fields/proxy.py +++ b/venv/Lib/site-packages/django/db/models/fields/proxy.py @@ -13,6 +13,6 @@ class OrderWrt(fields.IntegerField): """ def __init__(self, *args, **kwargs): - kwargs["name"] = "_order" - kwargs["editable"] = False + kwargs['name'] = '_order' + kwargs['editable'] = False super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/django/db/models/fields/related.py b/venv/Lib/site-packages/django/db/models/fields/related.py index 6f08c27..899ae8e 100644 --- a/venv/Lib/site-packages/django/db/models/fields/related.py +++ b/venv/Lib/site-packages/django/db/models/fields/related.py @@ -19,25 +19,19 @@ from django.utils.translation import gettext_lazy as _ from . import Field from .mixins import FieldCacheMixin from .related_descriptors import ( - ForeignKeyDeferredAttribute, - ForwardManyToOneDescriptor, - ForwardOneToOneDescriptor, - ManyToManyDescriptor, - ReverseManyToOneDescriptor, - ReverseOneToOneDescriptor, + ForeignKeyDeferredAttribute, ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, ManyToManyDescriptor, + ReverseManyToOneDescriptor, ReverseOneToOneDescriptor, ) from .related_lookups import ( - RelatedExact, - RelatedGreaterThan, - RelatedGreaterThanOrEqual, - RelatedIn, - RelatedIsNull, - RelatedLessThan, - RelatedLessThanOrEqual, + RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn, + RelatedIsNull, RelatedLessThan, RelatedLessThanOrEqual, +) +from .reverse_related import ( + ForeignObjectRel, ManyToManyRel, ManyToOneRel, OneToOneRel, ) -from .reverse_related import ForeignObjectRel, ManyToManyRel, ManyToOneRel, OneToOneRel -RECURSIVE_RELATIONSHIP_CONSTANT = "self" +RECURSIVE_RELATIONSHIP_CONSTANT = 'self' def resolve_relation(scope_model, relation): @@ -95,18 +89,6 @@ class RelatedField(FieldCacheMixin, Field): many_to_many = False many_to_one = False - def __init__( - self, - related_name=None, - related_query_name=None, - limit_choices_to=None, - **kwargs, - ): - self._related_name = related_name - self._related_query_name = related_query_name - self._limit_choices_to = limit_choices_to - super().__init__(**kwargs) - @cached_property def related_model(self): # Can't cache this property until all the models are loaded. @@ -125,28 +107,19 @@ class RelatedField(FieldCacheMixin, Field): def _check_related_name_is_valid(self): import keyword - related_name = self.remote_field.related_name if related_name is None: return [] - is_valid_id = ( - not keyword.iskeyword(related_name) and related_name.isidentifier() - ) - if not (is_valid_id or related_name.endswith("+")): + is_valid_id = not keyword.iskeyword(related_name) and related_name.isidentifier() + if not (is_valid_id or related_name.endswith('+')): return [ checks.Error( - "The name '%s' is invalid related_name for field %s.%s" - % ( - self.remote_field.related_name, - self.model._meta.object_name, - self.name, - ), - hint=( - "Related name must be a valid Python identifier or end with a " - "'+'" - ), + "The name '%s' is invalid related_name for field %s.%s" % + (self.remote_field.related_name, self.model._meta.object_name, + self.name), + hint="Related name must be a valid Python identifier or end with a '+'", obj=self, - id="fields.E306", + id='fields.E306', ) ] return [] @@ -156,17 +129,15 @@ class RelatedField(FieldCacheMixin, Field): return [] rel_query_name = self.related_query_name() errors = [] - if rel_query_name.endswith("_"): + if rel_query_name.endswith('_'): errors.append( checks.Error( "Reverse query name '%s' must not end with an underscore." % rel_query_name, - hint=( - "Add or change a related_name or related_query_name " - "argument for this field." - ), + hint=("Add or change a related_name or related_query_name " + "argument for this field."), obj=self, - id="fields.E308", + id='fields.E308', ) ) if LOOKUP_SEP in rel_query_name: @@ -174,12 +145,10 @@ class RelatedField(FieldCacheMixin, Field): checks.Error( "Reverse query name '%s' must not contain '%s'." % (rel_query_name, LOOKUP_SEP), - hint=( - "Add or change a related_name or related_query_name " - "argument for this field." - ), + hint=("Add or change a related_name or related_query_name " + "argument for this field."), obj=self, - id="fields.E309", + id='fields.E309', ) ) return errors @@ -187,38 +156,29 @@ class RelatedField(FieldCacheMixin, Field): def _check_relation_model_exists(self): rel_is_missing = self.remote_field.model not in self.opts.apps.get_models() rel_is_string = isinstance(self.remote_field.model, str) - model_name = ( - self.remote_field.model - if rel_is_string - else self.remote_field.model._meta.object_name - ) - if rel_is_missing and ( - rel_is_string or not self.remote_field.model._meta.swapped - ): + model_name = self.remote_field.model if rel_is_string else self.remote_field.model._meta.object_name + if rel_is_missing and (rel_is_string or not self.remote_field.model._meta.swapped): return [ checks.Error( "Field defines a relation with model '%s', which is either " "not installed, or is abstract." % model_name, obj=self, - id="fields.E300", + id='fields.E300', ) ] return [] def _check_referencing_to_swapped_model(self): - if ( - self.remote_field.model not in self.opts.apps.get_models() - and not isinstance(self.remote_field.model, str) - and self.remote_field.model._meta.swapped - ): + if (self.remote_field.model not in self.opts.apps.get_models() and + not isinstance(self.remote_field.model, str) and + self.remote_field.model._meta.swapped): return [ checks.Error( "Field defines a relation with the model '%s', which has " "been swapped out." % self.remote_field.model._meta.label, - hint="Update the relation to point at 'settings.%s'." - % self.remote_field.model._meta.swappable, + hint="Update the relation to point at 'settings.%s'." % self.remote_field.model._meta.swappable, obj=self, - id="fields.E301", + id='fields.E301', ) ] return [] @@ -230,8 +190,8 @@ class RelatedField(FieldCacheMixin, Field): errors = [] opts = self.model._meta - # f.remote_field.model may be a string instead of a model. Skip if - # model name is not resolved. + # `f.remote_field.model` may be a string instead of a model. Skip if model name is + # not resolved. if not isinstance(self.remote_field.model, ModelBase): return [] @@ -255,7 +215,7 @@ class RelatedField(FieldCacheMixin, Field): rel_name = self.remote_field.get_accessor_name() # i. e. "model_set" rel_query_name = self.related_query_name() # i. e. "model" # i.e. "app_label.Model.field". - field_name = "%s.%s" % (opts.label, self.name) + field_name = '%s.%s' % (opts.label, self.name) # Check clashes between accessor or reverse query name of `field` # and any other field name -- i.e. accessor for Model.foreign is @@ -263,34 +223,26 @@ class RelatedField(FieldCacheMixin, Field): potential_clashes = rel_opts.fields + rel_opts.many_to_many for clash_field in potential_clashes: # i.e. "app_label.Target.model_set". - clash_name = "%s.%s" % (rel_opts.label, clash_field.name) + clash_name = '%s.%s' % (rel_opts.label, clash_field.name) if not rel_is_hidden and clash_field.name == rel_name: errors.append( checks.Error( - "Reverse accessor for '%s' clashes with field name '%s'." - % (field_name, clash_name), - hint=( - "Rename field '%s', or add/change a related_name " - "argument to the definition for field '%s'." - ) - % (clash_name, field_name), + "Reverse accessor for '%s' clashes with field name '%s'." % (field_name, clash_name), + hint=("Rename field '%s', or add/change a related_name " + "argument to the definition for field '%s'.") % (clash_name, field_name), obj=self, - id="fields.E302", + id='fields.E302', ) ) if clash_field.name == rel_query_name: errors.append( checks.Error( - "Reverse query name for '%s' clashes with field name '%s'." - % (field_name, clash_name), - hint=( - "Rename field '%s', or add/change a related_name " - "argument to the definition for field '%s'." - ) - % (clash_name, field_name), + "Reverse query name for '%s' clashes with field name '%s'." % (field_name, clash_name), + hint=("Rename field '%s', or add/change a related_name " + "argument to the definition for field '%s'.") % (clash_name, field_name), obj=self, - id="fields.E303", + id='fields.E303', ) ) @@ -300,37 +252,30 @@ class RelatedField(FieldCacheMixin, Field): potential_clashes = (r for r in rel_opts.related_objects if r.field is not self) for clash_field in potential_clashes: # i.e. "app_label.Model.m2m". - clash_name = "%s.%s" % ( + clash_name = '%s.%s' % ( clash_field.related_model._meta.label, clash_field.field.name, ) if not rel_is_hidden and clash_field.get_accessor_name() == rel_name: errors.append( checks.Error( - "Reverse accessor for '%s' clashes with reverse accessor for " - "'%s'." % (field_name, clash_name), - hint=( - "Add or change a related_name argument " - "to the definition for '%s' or '%s'." - ) - % (field_name, clash_name), + "Reverse accessor for '%s' clashes with reverse accessor for '%s'." % (field_name, clash_name), + hint=("Add or change a related_name argument " + "to the definition for '%s' or '%s'.") % (field_name, clash_name), obj=self, - id="fields.E304", + id='fields.E304', ) ) if clash_field.get_accessor_name() == rel_query_name: errors.append( checks.Error( - "Reverse query name for '%s' clashes with reverse query name " - "for '%s'." % (field_name, clash_name), - hint=( - "Add or change a related_name argument " - "to the definition for '%s' or '%s'." - ) + "Reverse query name for '%s' clashes with reverse query name for '%s'." % (field_name, clash_name), + hint=("Add or change a related_name argument " + "to the definition for '%s' or '%s'.") % (field_name, clash_name), obj=self, - id="fields.E305", + id='fields.E305', ) ) @@ -354,35 +299,32 @@ class RelatedField(FieldCacheMixin, Field): related_name = self.opts.default_related_name if related_name: related_name = related_name % { - "class": cls.__name__.lower(), - "model_name": cls._meta.model_name.lower(), - "app_label": cls._meta.app_label.lower(), + 'class': cls.__name__.lower(), + 'model_name': cls._meta.model_name.lower(), + 'app_label': cls._meta.app_label.lower() } self.remote_field.related_name = related_name if self.remote_field.related_query_name: related_query_name = self.remote_field.related_query_name % { - "class": cls.__name__.lower(), - "app_label": cls._meta.app_label.lower(), + 'class': cls.__name__.lower(), + 'app_label': cls._meta.app_label.lower(), } self.remote_field.related_query_name = related_query_name def resolve_related_class(model, related, field): field.remote_field.model = related field.do_related_class(related, model) - - lazy_related_operation( - resolve_related_class, cls, self.remote_field.model, field=self - ) + lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self) def deconstruct(self): name, path, args, kwargs = super().deconstruct() - if self._limit_choices_to: - kwargs["limit_choices_to"] = self._limit_choices_to - if self._related_name is not None: - kwargs["related_name"] = self._related_name - if self._related_query_name is not None: - kwargs["related_query_name"] = self._related_query_name + if self.remote_field.limit_choices_to: + kwargs['limit_choices_to'] = self.remote_field.limit_choices_to + if self.remote_field.related_name is not None: + kwargs['related_name'] = self.remote_field.related_name + if self.remote_field.related_query_name is not None: + kwargs['related_query_name'] = self.remote_field.related_query_name return name, path, args, kwargs def get_forward_related_filter(self, obj): @@ -394,7 +336,7 @@ class RelatedField(FieldCacheMixin, Field): self.related_field.model. """ return { - "%s__%s" % (self.name, rh_field.name): getattr(obj, rh_field.attname) + '%s__%s' % (self.name, rh_field.name): getattr(obj, rh_field.attname) for _, rh_field in self.related_fields } @@ -405,12 +347,12 @@ class RelatedField(FieldCacheMixin, Field): select all instances of self.related_field.model related through this field to obj. obj is an instance of self.model. """ - base_filter = ( - (rh_field.attname, getattr(obj, lh_field.attname)) + base_filter = { + rh_field.attname: getattr(obj, lh_field.attname) for lh_field, rh_field in self.related_fields - ) + } descriptor_filter = self.get_extra_descriptor_filter(obj) - base_q = Q(*base_filter) + base_q = Q(**base_filter) if isinstance(descriptor_filter, dict): return base_q & Q(**descriptor_filter) elif descriptor_filter: @@ -433,10 +375,9 @@ class RelatedField(FieldCacheMixin, Field): return None def set_attributes_from_rel(self): - self.name = self.name or ( - self.remote_field.model._meta.model_name - + "_" - + self.remote_field.model._meta.pk.name + self.name = ( + self.name or + (self.remote_field.model._meta.model_name + '_' + self.remote_field.model._meta.pk.name) ) if self.verbose_name is None: self.verbose_name = self.remote_field.model._meta.verbose_name @@ -466,16 +407,14 @@ class RelatedField(FieldCacheMixin, Field): being constructed. """ defaults = {} - if hasattr(self.remote_field, "get_related_field"): + if hasattr(self.remote_field, 'get_related_field'): # If this is a callable, do not invoke it here. Just pass # it in the defaults for when the form class will later be # instantiated. limit_choices_to = self.remote_field.limit_choices_to - defaults.update( - { - "limit_choices_to": limit_choices_to, - } - ) + defaults.update({ + 'limit_choices_to': limit_choices_to, + }) defaults.update(kwargs) return super().formfield(**defaults) @@ -484,11 +423,7 @@ class RelatedField(FieldCacheMixin, Field): Define the name that can be used to identify this related object in a table-spanning query. """ - return ( - self.remote_field.related_query_name - or self.remote_field.related_name - or self.opts.model_name - ) + return self.remote_field.related_query_name or self.remote_field.related_name or self.opts.model_name @property def target_field(self): @@ -499,9 +434,7 @@ class RelatedField(FieldCacheMixin, Field): target_fields = self.get_path_info()[-1].target_fields if len(target_fields) > 1: raise exceptions.FieldError( - "The relation has multiple target fields, but only single target field " - "was asked for" - ) + "The relation has multiple target fields, but only single target field was asked for") return target_fields[0] def get_cache_name(self): @@ -524,25 +457,13 @@ class ForeignObject(RelatedField): forward_related_accessor_class = ForwardManyToOneDescriptor rel_class = ForeignObjectRel - def __init__( - self, - to, - on_delete, - from_fields, - to_fields, - rel=None, - related_name=None, - related_query_name=None, - limit_choices_to=None, - parent_link=False, - swappable=True, - **kwargs, - ): + def __init__(self, to, on_delete, from_fields, to_fields, rel=None, related_name=None, + related_query_name=None, limit_choices_to=None, parent_link=False, + swappable=True, **kwargs): if rel is None: rel = self.rel_class( - self, - to, + self, to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, @@ -550,13 +471,7 @@ class ForeignObject(RelatedField): on_delete=on_delete, ) - super().__init__( - rel=rel, - related_name=related_name, - related_query_name=related_query_name, - limit_choices_to=limit_choices_to, - **kwargs, - ) + super().__init__(rel=rel, **kwargs) self.from_fields = from_fields self.to_fields = to_fields @@ -586,7 +501,7 @@ class ForeignObject(RelatedField): "model '%s'." % (to_field, self.remote_field.model._meta.label), obj=self, - id="fields.E312", + id='fields.E312', ) ) return errors @@ -607,22 +522,21 @@ class ForeignObject(RelatedField): unique_foreign_fields = { frozenset([f.name]) for f in self.remote_field.model._meta.get_fields() - if getattr(f, "unique", False) + if getattr(f, 'unique', False) } - unique_foreign_fields.update( - {frozenset(ut) for ut in self.remote_field.model._meta.unique_together} - ) - unique_foreign_fields.update( - { - frozenset(uc.fields) - for uc in self.remote_field.model._meta.total_unique_constraints - } - ) + unique_foreign_fields.update({ + frozenset(ut) + for ut in self.remote_field.model._meta.unique_together + }) + unique_foreign_fields.update({ + frozenset(uc.fields) + for uc in self.remote_field.model._meta.total_unique_constraints + }) foreign_fields = {f.name for f in self.foreign_related_fields} has_unique_constraint = any(u <= foreign_fields for u in unique_foreign_fields) if not has_unique_constraint and len(self.foreign_related_fields) > 1: - field_combination = ", ".join( + field_combination = ', '.join( "'%s'" % rel_field.name for rel_field in self.foreign_related_fields ) model_name = self.remote_field.model.__name__ @@ -631,13 +545,13 @@ class ForeignObject(RelatedField): "No subset of the fields %s on model '%s' is unique." % (field_combination, model_name), hint=( - "Mark a single field as unique=True or add a set of " - "fields to a unique constraint (via unique_together " - "or a UniqueConstraint (without condition) in the " - "model Meta.constraints)." + 'Mark a single field as unique=True or add a set of ' + 'fields to a unique constraint (via unique_together ' + 'or a UniqueConstraint (without condition) in the ' + 'model Meta.constraints).' ), obj=self, - id="fields.E310", + id='fields.E310', ) ] elif not has_unique_constraint: @@ -648,12 +562,12 @@ class ForeignObject(RelatedField): "'%s.%s' must be unique because it is referenced by " "a foreign key." % (model_name, field_name), hint=( - "Add unique=True to this field or add a " - "UniqueConstraint (without condition) in the model " - "Meta.constraints." + 'Add unique=True to this field or add a ' + 'UniqueConstraint (without condition) in the model ' + 'Meta.constraints.' ), obj=self, - id="fields.E311", + id='fields.E311', ) ] else: @@ -661,48 +575,44 @@ class ForeignObject(RelatedField): def deconstruct(self): name, path, args, kwargs = super().deconstruct() - kwargs["on_delete"] = self.remote_field.on_delete - kwargs["from_fields"] = self.from_fields - kwargs["to_fields"] = self.to_fields + kwargs['on_delete'] = self.remote_field.on_delete + kwargs['from_fields'] = self.from_fields + kwargs['to_fields'] = self.to_fields if self.remote_field.parent_link: - kwargs["parent_link"] = self.remote_field.parent_link + kwargs['parent_link'] = self.remote_field.parent_link if isinstance(self.remote_field.model, str): - if "." in self.remote_field.model: - app_label, model_name = self.remote_field.model.split(".") - kwargs["to"] = "%s.%s" % (app_label, model_name.lower()) + if '.' in self.remote_field.model: + app_label, model_name = self.remote_field.model.split('.') + kwargs['to'] = '%s.%s' % (app_label, model_name.lower()) else: - kwargs["to"] = self.remote_field.model.lower() + kwargs['to'] = self.remote_field.model.lower() else: - kwargs["to"] = self.remote_field.model._meta.label_lower + kwargs['to'] = self.remote_field.model._meta.label_lower # If swappable is True, then see if we're actually pointing to the target # of a swap. swappable_setting = self.swappable_setting if swappable_setting is not None: # If it's already a settings reference, error - if hasattr(kwargs["to"], "setting_name"): - if kwargs["to"].setting_name != swappable_setting: + if hasattr(kwargs['to'], "setting_name"): + if kwargs['to'].setting_name != swappable_setting: raise ValueError( "Cannot deconstruct a ForeignKey pointing to a model " "that is swapped in place of more than one model (%s and %s)" - % (kwargs["to"].setting_name, swappable_setting) + % (kwargs['to'].setting_name, swappable_setting) ) # Set it - kwargs["to"] = SettingsReference( - kwargs["to"], + kwargs['to'] = SettingsReference( + kwargs['to'], swappable_setting, ) return name, path, args, kwargs def resolve_related_fields(self): if not self.from_fields or len(self.from_fields) != len(self.to_fields): - raise ValueError( - "Foreign Object from and to fields must be the same non-zero length" - ) + raise ValueError('Foreign Object from and to fields must be the same non-zero length') if isinstance(self.remote_field.model, str): - raise ValueError( - "Related model %r cannot be resolved" % self.remote_field.model - ) + raise ValueError('Related model %r cannot be resolved' % self.remote_field.model) related_fields = [] for index in range(len(self.from_fields)): from_field_name = self.from_fields[index] @@ -712,11 +622,8 @@ class ForeignObject(RelatedField): if from_field_name == RECURSIVE_RELATIONSHIP_CONSTANT else self.opts.get_field(from_field_name) ) - to_field = ( - self.remote_field.model._meta.pk - if to_field_name is None - else self.remote_field.model._meta.get_field(to_field_name) - ) + to_field = (self.remote_field.model._meta.pk if to_field_name is None + else self.remote_field.model._meta.get_field(to_field_name)) related_fields.append((from_field, to_field)) return related_fields @@ -734,9 +641,7 @@ class ForeignObject(RelatedField): @cached_property def foreign_related_fields(self): - return tuple( - rhs_field for lhs_field, rhs_field in self.related_fields if rhs_field - ) + return tuple(rhs_field for lhs_field, rhs_field in self.related_fields if rhs_field) def get_local_related_value(self, instance): return self.get_instance_value_for_fields(instance, self.local_related_fields) @@ -754,11 +659,9 @@ class ForeignObject(RelatedField): # instance.pk (that is, parent_ptr_id) when asked for instance.id. if field.primary_key: possible_parent_link = opts.get_ancestor_link(field.model) - if ( - not possible_parent_link - or possible_parent_link.primary_key - or possible_parent_link.model._meta.abstract - ): + if (not possible_parent_link or + possible_parent_link.primary_key or + possible_parent_link.model._meta.abstract): ret.append(instance.pk) continue ret.append(getattr(instance, field.attname)) @@ -770,9 +673,7 @@ class ForeignObject(RelatedField): def get_joining_columns(self, reverse_join=False): source = self.reverse_related_fields if reverse_join else self.related_fields - return tuple( - (lhs_field.column, rhs_field.column) for lhs_field, rhs_field in source - ) + return tuple((lhs_field.column, rhs_field.column) for lhs_field, rhs_field in source) def get_reverse_joining_columns(self): return self.get_joining_columns(reverse_join=True) @@ -792,7 +693,7 @@ class ForeignObject(RelatedField): """ return {} - def get_extra_restriction(self, alias, related_alias): + def get_extra_restriction(self, where_class, alias, related_alias): """ Return a pair condition used for joining and subquery pushdown. The condition is something that responds to as_sql(compiler, connection) @@ -810,40 +711,36 @@ class ForeignObject(RelatedField): """Get path from this field to the related model.""" opts = self.remote_field.model._meta from_opts = self.model._meta - return [ - PathInfo( - from_opts=from_opts, - to_opts=opts, - target_fields=self.foreign_related_fields, - join_field=self, - m2m=False, - direct=True, - filtered_relation=filtered_relation, - ) - ] + return [PathInfo( + from_opts=from_opts, + to_opts=opts, + target_fields=self.foreign_related_fields, + join_field=self, + m2m=False, + direct=True, + filtered_relation=filtered_relation, + )] def get_reverse_path_info(self, filtered_relation=None): """Get path from the related model to this field's model.""" opts = self.model._meta from_opts = self.remote_field.model._meta - return [ - PathInfo( - from_opts=from_opts, - to_opts=opts, - target_fields=(opts.pk,), - join_field=self.remote_field, - m2m=not self.unique, - direct=False, - filtered_relation=filtered_relation, - ) - ] + return [PathInfo( + from_opts=from_opts, + to_opts=opts, + target_fields=(opts.pk,), + join_field=self.remote_field, + m2m=not self.unique, + direct=False, + filtered_relation=filtered_relation, + )] @classmethod @functools.lru_cache(maxsize=None) def get_lookups(cls): bases = inspect.getmro(cls) - bases = bases[: bases.index(ForeignObject) + 1] - class_lookups = [parent.__dict__.get("class_lookups", {}) for parent in bases] + bases = bases[:bases.index(ForeignObject) + 1] + class_lookups = [parent.__dict__.get('class_lookups', {}) for parent in bases] return cls.merge_dicts(class_lookups) def contribute_to_class(self, cls, name, private_only=False, **kwargs): @@ -853,22 +750,13 @@ class ForeignObject(RelatedField): def contribute_to_related_class(self, cls, related): # Internal FK's - i.e., those with a related name ending with '+' - # and swapped models don't get a related descriptor. - if ( - not self.remote_field.is_hidden() - and not related.related_model._meta.swapped - ): - setattr( - cls._meta.concrete_model, - related.get_accessor_name(), - self.related_accessor_class(related), - ) + if not self.remote_field.is_hidden() and not related.related_model._meta.swapped: + setattr(cls._meta.concrete_model, related.get_accessor_name(), self.related_accessor_class(related)) # While 'limit_choices_to' might be a callable, simply pass # it along for later - this is too early because it's still # model load time. if self.remote_field.limit_choices_to: - cls._meta.related_fkey_lookups.append( - self.remote_field.limit_choices_to - ) + cls._meta.related_fkey_lookups.append(self.remote_field.limit_choices_to) ForeignObject.register_lookup(RelatedIn) @@ -888,7 +776,6 @@ class ForeignKey(ForeignObject): By default ForeignKey will target the pk of the remote model but this behavior can be changed by using the ``to_field`` argument. """ - descriptor_class = ForeignKeyDeferredAttribute # Field flags many_to_many = False @@ -900,61 +787,44 @@ class ForeignKey(ForeignObject): empty_strings_allowed = False default_error_messages = { - "invalid": _("%(model)s instance with %(field)s %(value)r does not exist.") + 'invalid': _('%(model)s instance with %(field)s %(value)r does not exist.') } description = _("Foreign Key (type determined by related field)") - def __init__( - self, - to, - on_delete, - related_name=None, - related_query_name=None, - limit_choices_to=None, - parent_link=False, - to_field=None, - db_constraint=True, - **kwargs, - ): + def __init__(self, to, on_delete, related_name=None, related_query_name=None, + limit_choices_to=None, parent_link=False, to_field=None, + db_constraint=True, **kwargs): try: to._meta.model_name except AttributeError: - if not isinstance(to, str): - raise TypeError( - "%s(%r) is invalid. First parameter to ForeignKey must be " - "either a model, a model name, or the string %r" - % ( - self.__class__.__name__, - to, - RECURSIVE_RELATIONSHIP_CONSTANT, - ) + assert isinstance(to, str), ( + "%s(%r) is invalid. First parameter to ForeignKey must be " + "either a model, a model name, or the string %r" % ( + self.__class__.__name__, to, + RECURSIVE_RELATIONSHIP_CONSTANT, ) + ) else: # For backwards compatibility purposes, we need to *try* and set # the to_field during FK construction. It won't be guaranteed to # be correct until contribute_to_class is called. Refs #12190. to_field = to_field or (to._meta.pk and to._meta.pk.name) if not callable(on_delete): - raise TypeError("on_delete must be callable.") + raise TypeError('on_delete must be callable.') - kwargs["rel"] = self.rel_class( - self, - to, - to_field, + kwargs['rel'] = self.rel_class( + self, to, to_field, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete, ) - kwargs.setdefault("db_index", True) + kwargs.setdefault('db_index', True) super().__init__( to, on_delete, - related_name=related_name, - related_query_name=related_query_name, - limit_choices_to=limit_choices_to, from_fields=[RECURSIVE_RELATIONSHIP_CONSTANT], to_fields=[to_field], **kwargs, @@ -969,67 +839,54 @@ class ForeignKey(ForeignObject): ] def _check_on_delete(self): - on_delete = getattr(self.remote_field, "on_delete", None) + on_delete = getattr(self.remote_field, 'on_delete', None) if on_delete == SET_NULL and not self.null: return [ checks.Error( - "Field specifies on_delete=SET_NULL, but cannot be null.", - hint=( - "Set null=True argument on the field, or change the on_delete " - "rule." - ), + 'Field specifies on_delete=SET_NULL, but cannot be null.', + hint='Set null=True argument on the field, or change the on_delete rule.', obj=self, - id="fields.E320", + id='fields.E320', ) ] elif on_delete == SET_DEFAULT and not self.has_default(): return [ checks.Error( - "Field specifies on_delete=SET_DEFAULT, but has no default value.", - hint="Set a default value, or change the on_delete rule.", + 'Field specifies on_delete=SET_DEFAULT, but has no default value.', + hint='Set a default value, or change the on_delete rule.', obj=self, - id="fields.E321", + id='fields.E321', ) ] else: return [] def _check_unique(self, **kwargs): - return ( - [ - checks.Warning( - "Setting unique=True on a ForeignKey has the same effect as using " - "a OneToOneField.", - hint=( - "ForeignKey(unique=True) is usually better served by a " - "OneToOneField." - ), - obj=self, - id="fields.W342", - ) - ] - if self.unique - else [] - ) + return [ + checks.Warning( + 'Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.', + hint='ForeignKey(unique=True) is usually better served by a OneToOneField.', + obj=self, + id='fields.W342', + ) + ] if self.unique else [] def deconstruct(self): name, path, args, kwargs = super().deconstruct() - del kwargs["to_fields"] - del kwargs["from_fields"] + del kwargs['to_fields'] + del kwargs['from_fields'] # Handle the simpler arguments if self.db_index: - del kwargs["db_index"] + del kwargs['db_index'] else: - kwargs["db_index"] = False + kwargs['db_index'] = False if self.db_constraint is not True: - kwargs["db_constraint"] = self.db_constraint + kwargs['db_constraint'] = self.db_constraint # Rel needs more work. to_meta = getattr(self.remote_field.model, "_meta", None) if self.remote_field.field_name and ( - not to_meta - or (to_meta.pk and self.remote_field.field_name != to_meta.pk.name) - ): - kwargs["to_field"] = self.remote_field.field_name + not to_meta or (to_meta.pk and self.remote_field.field_name != to_meta.pk.name)): + kwargs['to_field'] = self.remote_field.field_name return name, path, args, kwargs def to_python(self, value): @@ -1043,17 +900,15 @@ class ForeignKey(ForeignObject): """Get path from the related model to this field's model.""" opts = self.model._meta from_opts = self.remote_field.model._meta - return [ - PathInfo( - from_opts=from_opts, - to_opts=opts, - target_fields=(opts.pk,), - join_field=self.remote_field, - m2m=not self.unique, - direct=False, - filtered_relation=filtered_relation, - ) - ] + return [PathInfo( + from_opts=from_opts, + to_opts=opts, + target_fields=(opts.pk,), + join_field=self.remote_field, + m2m=not self.unique, + direct=False, + filtered_relation=filtered_relation, + )] def validate(self, value, model_instance): if self.remote_field.parent_link: @@ -1069,27 +924,21 @@ class ForeignKey(ForeignObject): qs = qs.complex_filter(self.get_limit_choices_to()) if not qs.exists(): raise exceptions.ValidationError( - self.error_messages["invalid"], - code="invalid", + self.error_messages['invalid'], + code='invalid', params={ - "model": self.remote_field.model._meta.verbose_name, - "pk": value, - "field": self.remote_field.field_name, - "value": value, + 'model': self.remote_field.model._meta.verbose_name, 'pk': value, + 'field': self.remote_field.field_name, 'value': value, }, # 'pk' is included for backwards compatibility ) def resolve_related_fields(self): related_fields = super().resolve_related_fields() for from_field, to_field in related_fields: - if ( - to_field - and to_field.model != self.remote_field.model._meta.concrete_model - ): + if to_field and to_field.model != self.remote_field.model._meta.concrete_model: raise exceptions.FieldError( "'%s.%s' refers to field '%s' which is not local to model " - "'%s'." - % ( + "'%s'." % ( self.model._meta.label, self.name, to_field.name, @@ -1099,7 +948,7 @@ class ForeignKey(ForeignObject): return related_fields def get_attname(self): - return "%s_id" % self.name + return '%s_id' % self.name def get_attname_column(self): attname = self.get_attname() @@ -1114,13 +963,9 @@ class ForeignKey(ForeignObject): return field_default def get_db_prep_save(self, value, connection): - if value is None or ( - value == "" - and ( - not self.target_field.empty_strings_allowed - or connection.features.interprets_empty_strings_as_nulls - ) - ): + if value is None or (value == '' and + (not self.target_field.empty_strings_allowed or + connection.features.interprets_empty_strings_as_nulls)): return None else: return self.target_field.get_db_prep_save(value, connection=connection) @@ -1138,20 +983,16 @@ class ForeignKey(ForeignObject): def formfield(self, *, using=None, **kwargs): if isinstance(self.remote_field.model, str): - raise ValueError( - "Cannot create form field for %r yet, because " - "its related model %r has not been loaded yet" - % (self.name, self.remote_field.model) - ) - return super().formfield( - **{ - "form_class": forms.ModelChoiceField, - "queryset": self.remote_field.model._default_manager.using(using), - "to_field_name": self.remote_field.field_name, - **kwargs, - "blank": self.blank, - } - ) + raise ValueError("Cannot create form field for %r yet, because " + "its related model %r has not been loaded yet" % + (self.name, self.remote_field.model)) + return super().formfield(**{ + 'form_class': forms.ModelChoiceField, + 'queryset': self.remote_field.model._default_manager.using(using), + 'to_field_name': self.remote_field.field_name, + **kwargs, + 'blank': self.blank, + }) def db_check(self, connection): return [] @@ -1179,7 +1020,7 @@ class ForeignKey(ForeignObject): while isinstance(output_field, ForeignKey): output_field = output_field.target_field if output_field is self: - raise ValueError("Cannot resolve output_field.") + raise ValueError('Cannot resolve output_field.') return super().get_col(alias, output_field) @@ -1204,13 +1045,13 @@ class OneToOneField(ForeignKey): description = _("One-to-one relationship") def __init__(self, to, on_delete, to_field=None, **kwargs): - kwargs["unique"] = True + kwargs['unique'] = True super().__init__(to, on_delete, to_field=to_field, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() if "unique" in kwargs: - del kwargs["unique"] + del kwargs['unique'] return name, path, args, kwargs def formfield(self, **kwargs): @@ -1240,54 +1081,44 @@ def create_many_to_many_intermediary_model(field, klass): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) - name = "%s_%s" % (klass._meta.object_name, field.name) + name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: - to = "to_%s" % to - from_ = "from_%s" % from_ + to = 'to_%s' % to + from_ = 'from_%s' % from_ - meta = type( - "Meta", - (), - { - "db_table": field._get_m2m_db_table(klass._meta), - "auto_created": klass, - "app_label": klass._meta.app_label, - "db_tablespace": klass._meta.db_tablespace, - "unique_together": (from_, to), - "verbose_name": _("%(from)s-%(to)s relationship") - % {"from": from_, "to": to}, - "verbose_name_plural": _("%(from)s-%(to)s relationships") - % {"from": from_, "to": to}, - "apps": field.model._meta.apps, - }, - ) + meta = type('Meta', (), { + 'db_table': field._get_m2m_db_table(klass._meta), + 'auto_created': klass, + 'app_label': klass._meta.app_label, + 'db_tablespace': klass._meta.db_tablespace, + 'unique_together': (from_, to), + 'verbose_name': _('%(from)s-%(to)s relationship') % {'from': from_, 'to': to}, + 'verbose_name_plural': _('%(from)s-%(to)s relationships') % {'from': from_, 'to': to}, + 'apps': field.model._meta.apps, + }) # Construct and return the new class. - return type( - name, - (models.Model,), - { - "Meta": meta, - "__module__": klass.__module__, - from_: models.ForeignKey( - klass, - related_name="%s+" % name, - db_tablespace=field.db_tablespace, - db_constraint=field.remote_field.db_constraint, - on_delete=CASCADE, - ), - to: models.ForeignKey( - to_model, - related_name="%s+" % name, - db_tablespace=field.db_tablespace, - db_constraint=field.remote_field.db_constraint, - on_delete=CASCADE, - ), - }, - ) + return type(name, (models.Model,), { + 'Meta': meta, + '__module__': klass.__module__, + from_: models.ForeignKey( + klass, + related_name='%s+' % name, + db_tablespace=field.db_tablespace, + db_constraint=field.remote_field.db_constraint, + on_delete=CASCADE, + ), + to: models.ForeignKey( + to_model, + related_name='%s+' % name, + db_tablespace=field.db_tablespace, + db_constraint=field.remote_field.db_constraint, + on_delete=CASCADE, + ) + }) class ManyToManyField(RelatedField): @@ -1310,45 +1141,29 @@ class ManyToManyField(RelatedField): description = _("Many-to-many relationship") - def __init__( - self, - to, - related_name=None, - related_query_name=None, - limit_choices_to=None, - symmetrical=None, - through=None, - through_fields=None, - db_constraint=True, - db_table=None, - swappable=True, - **kwargs, - ): + def __init__(self, to, related_name=None, related_query_name=None, + limit_choices_to=None, symmetrical=None, through=None, + through_fields=None, db_constraint=True, db_table=None, + swappable=True, **kwargs): try: to._meta except AttributeError: - if not isinstance(to, str): - raise TypeError( - "%s(%r) is invalid. First parameter to ManyToManyField " - "must be either a model, a model name, or the string %r" - % ( - self.__class__.__name__, - to, - RECURSIVE_RELATIONSHIP_CONSTANT, - ) - ) + assert isinstance(to, str), ( + "%s(%r) is invalid. First parameter to ManyToManyField must be " + "either a model, a model name, or the string %r" % + (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT) + ) if symmetrical is None: - symmetrical = to == RECURSIVE_RELATIONSHIP_CONSTANT + symmetrical = (to == RECURSIVE_RELATIONSHIP_CONSTANT) - if through is not None and db_table is not None: - raise ValueError( + if through is not None: + assert db_table is None, ( "Cannot specify a db_table if an intermediary model is used." ) - kwargs["rel"] = self.rel_class( - self, - to, + kwargs['rel'] = self.rel_class( + self, to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, @@ -1357,14 +1172,9 @@ class ManyToManyField(RelatedField): through_fields=through_fields, db_constraint=db_constraint, ) - self.has_null_arg = "null" in kwargs + self.has_null_arg = 'null' in kwargs - super().__init__( - related_name=related_name, - related_query_name=related_query_name, - limit_choices_to=limit_choices_to, - **kwargs, - ) + super().__init__(**kwargs) self.db_table = db_table self.swappable = swappable @@ -1382,9 +1192,9 @@ class ManyToManyField(RelatedField): if self.unique: return [ checks.Error( - "ManyToManyFields cannot be unique.", + 'ManyToManyFields cannot be unique.', obj=self, - id="fields.E330", + id='fields.E330', ) ] return [] @@ -1395,53 +1205,50 @@ class ManyToManyField(RelatedField): if self.has_null_arg: warnings.append( checks.Warning( - "null has no effect on ManyToManyField.", + 'null has no effect on ManyToManyField.', obj=self, - id="fields.W340", + id='fields.W340', ) ) if self._validators: warnings.append( checks.Warning( - "ManyToManyField does not support validators.", + 'ManyToManyField does not support validators.', obj=self, - id="fields.W341", + id='fields.W341', ) ) - if self.remote_field.symmetrical and self._related_name: + if (self.remote_field.limit_choices_to and self.remote_field.through and + not self.remote_field.through._meta.auto_created): warnings.append( checks.Warning( - "related_name has no effect on ManyToManyField " - 'with a symmetrical relationship, e.g. to "self".', + 'limit_choices_to has no effect on ManyToManyField ' + 'with a through model.', obj=self, - id="fields.W345", + id='fields.W343', ) ) return warnings def _check_relationship_model(self, from_model=None, **kwargs): - if hasattr(self.remote_field.through, "_meta"): + if hasattr(self.remote_field.through, '_meta'): qualified_model_name = "%s.%s" % ( - self.remote_field.through._meta.app_label, - self.remote_field.through.__name__, - ) + self.remote_field.through._meta.app_label, self.remote_field.through.__name__) else: qualified_model_name = self.remote_field.through errors = [] - if self.remote_field.through not in self.opts.apps.get_models( - include_auto_created=True - ): + if self.remote_field.through not in self.opts.apps.get_models(include_auto_created=True): # The relationship model is not installed. errors.append( checks.Error( "Field specifies a many-to-many relation through model " "'%s', which has not been installed." % qualified_model_name, obj=self, - id="fields.E331", + id='fields.E331', ) ) @@ -1463,7 +1270,7 @@ class ManyToManyField(RelatedField): # Count foreign keys in intermediate model if self_referential: seen_self = sum( - from_model == getattr(field.remote_field, "model", None) + from_model == getattr(field.remote_field, 'model', None) for field in self.remote_field.through._meta.fields ) @@ -1474,49 +1281,41 @@ class ManyToManyField(RelatedField): "'%s', but it has more than two foreign keys " "to '%s', which is ambiguous. You must specify " "which two foreign keys Django should use via the " - "through_fields keyword argument." - % (self, from_model_name), - hint=( - "Use through_fields to specify which two foreign keys " - "Django should use." - ), + "through_fields keyword argument." % (self, from_model_name), + hint="Use through_fields to specify which two foreign keys Django should use.", obj=self.remote_field.through, - id="fields.E333", + id='fields.E333', ) ) else: # Count foreign keys in relationship model seen_from = sum( - from_model == getattr(field.remote_field, "model", None) + from_model == getattr(field.remote_field, 'model', None) for field in self.remote_field.through._meta.fields ) seen_to = sum( - to_model == getattr(field.remote_field, "model", None) + to_model == getattr(field.remote_field, 'model', None) for field in self.remote_field.through._meta.fields ) if seen_from > 1 and not self.remote_field.through_fields: errors.append( checks.Error( - ( - "The model is used as an intermediate model by " - "'%s', but it has more than one foreign key " - "from '%s', which is ambiguous. You must specify " - "which foreign key Django should use via the " - "through_fields keyword argument." - ) - % (self, from_model_name), + ("The model is used as an intermediate model by " + "'%s', but it has more than one foreign key " + "from '%s', which is ambiguous. You must specify " + "which foreign key Django should use via the " + "through_fields keyword argument.") % (self, from_model_name), hint=( - "If you want to create a recursive relationship, " + 'If you want to create a recursive relationship, ' 'use ManyToManyField("%s", through="%s").' - ) - % ( + ) % ( RECURSIVE_RELATIONSHIP_CONSTANT, relationship_model_name, ), obj=self, - id="fields.E334", + id='fields.E334', ) ) @@ -1529,15 +1328,14 @@ class ManyToManyField(RelatedField): "which foreign key Django should use via the " "through_fields keyword argument." % (self, to_model_name), hint=( - "If you want to create a recursive relationship, " + 'If you want to create a recursive relationship, ' 'use ManyToManyField("%s", through="%s").' - ) - % ( + ) % ( RECURSIVE_RELATIONSHIP_CONSTANT, relationship_model_name, ), obj=self, - id="fields.E335", + id='fields.E335', ) ) @@ -1545,10 +1343,11 @@ class ManyToManyField(RelatedField): errors.append( checks.Error( "The model is used as an intermediate model by " - "'%s', but it does not have a foreign key to '%s' or '%s'." - % (self, from_model_name, to_model_name), + "'%s', but it does not have a foreign key to '%s' or '%s'." % ( + self, from_model_name, to_model_name + ), obj=self.remote_field.through, - id="fields.E336", + id='fields.E336', ) ) @@ -1556,22 +1355,16 @@ class ManyToManyField(RelatedField): if self.remote_field.through_fields is not None: # Validate that we're given an iterable of at least two items # and that none of them is "falsy". - if not ( - len(self.remote_field.through_fields) >= 2 - and self.remote_field.through_fields[0] - and self.remote_field.through_fields[1] - ): + if not (len(self.remote_field.through_fields) >= 2 and + self.remote_field.through_fields[0] and self.remote_field.through_fields[1]): errors.append( checks.Error( "Field specifies 'through_fields' but does not provide " "the names of the two link fields that should be used " "for the relation through model '%s'." % qualified_model_name, - hint=( - "Make sure you specify 'through_fields' as " - "through_fields=('field1', 'field2')" - ), + hint="Make sure you specify 'through_fields' as through_fields=('field1', 'field2')", obj=self, - id="fields.E337", + id='fields.E337', ) ) @@ -1585,35 +1378,20 @@ class ManyToManyField(RelatedField): "where the field is attached to." ) - source, through, target = ( - from_model, - self.remote_field.through, - self.remote_field.model, - ) - source_field_name, target_field_name = self.remote_field.through_fields[ - :2 - ] + source, through, target = from_model, self.remote_field.through, self.remote_field.model + source_field_name, target_field_name = self.remote_field.through_fields[:2] - for field_name, related_model in ( - (source_field_name, source), - (target_field_name, target), - ): + for field_name, related_model in ((source_field_name, source), + (target_field_name, target)): possible_field_names = [] for f in through._meta.fields: - if ( - hasattr(f, "remote_field") - and getattr(f.remote_field, "model", None) == related_model - ): + if hasattr(f, 'remote_field') and getattr(f.remote_field, 'model', None) == related_model: possible_field_names.append(f.name) if possible_field_names: - hint = ( - "Did you mean one of the following foreign keys to '%s': " - "%s?" - % ( - related_model._meta.object_name, - ", ".join(possible_field_names), - ) + hint = "Did you mean one of the following foreign keys to '%s': %s?" % ( + related_model._meta.object_name, + ', '.join(possible_field_names), ) else: hint = None @@ -1627,36 +1405,28 @@ class ManyToManyField(RelatedField): % (qualified_model_name, field_name), hint=hint, obj=self, - id="fields.E338", + id='fields.E338', ) ) else: - if not ( - hasattr(field, "remote_field") - and getattr(field.remote_field, "model", None) - == related_model - ): + if not (hasattr(field, 'remote_field') and + getattr(field.remote_field, 'model', None) == related_model): errors.append( checks.Error( - "'%s.%s' is not a foreign key to '%s'." - % ( - through._meta.object_name, - field_name, + "'%s.%s' is not a foreign key to '%s'." % ( + through._meta.object_name, field_name, related_model._meta.object_name, ), hint=hint, obj=self, - id="fields.E339", + id='fields.E339', ) ) return errors def _check_table_uniqueness(self, **kwargs): - if ( - isinstance(self.remote_field.through, str) - or not self.remote_field.through._meta.managed - ): + if isinstance(self.remote_field.through, str) or not self.remote_field.through._meta.managed: return [] registered_tables = { model._meta.db_table: model @@ -1667,31 +1437,25 @@ class ManyToManyField(RelatedField): model = registered_tables.get(m2m_db_table) # The second condition allows multiple m2m relations on a model if # some point to a through model that proxies another through model. - if ( - model - and model._meta.concrete_model - != self.remote_field.through._meta.concrete_model - ): + if model and model._meta.concrete_model != self.remote_field.through._meta.concrete_model: if model._meta.auto_created: - def _get_field_name(model): for field in model._meta.auto_created._meta.many_to_many: if field.remote_field.through is model: return field.name - opts = model._meta.auto_created._meta - clashing_obj = "%s.%s" % (opts.label, _get_field_name(model)) + clashing_obj = '%s.%s' % (opts.label, _get_field_name(model)) else: clashing_obj = model._meta.label if settings.DATABASE_ROUTERS: - error_class, error_id = checks.Warning, "fields.W344" + error_class, error_id = checks.Warning, 'fields.W344' error_hint = ( - "You have configured settings.DATABASE_ROUTERS. Verify " - "that the table of %r is correctly routed to a separate " - "database." % clashing_obj + 'You have configured settings.DATABASE_ROUTERS. Verify ' + 'that the table of %r is correctly routed to a separate ' + 'database.' % clashing_obj ) else: - error_class, error_id = checks.Error, "fields.E340" + error_class, error_id = checks.Error, 'fields.E340' error_hint = None return [ error_class( @@ -1708,38 +1472,34 @@ class ManyToManyField(RelatedField): name, path, args, kwargs = super().deconstruct() # Handle the simpler arguments. if self.db_table is not None: - kwargs["db_table"] = self.db_table + kwargs['db_table'] = self.db_table if self.remote_field.db_constraint is not True: - kwargs["db_constraint"] = self.remote_field.db_constraint - # Lowercase model names as they should be treated as case-insensitive. + kwargs['db_constraint'] = self.remote_field.db_constraint + # Rel needs more work. if isinstance(self.remote_field.model, str): - if "." in self.remote_field.model: - app_label, model_name = self.remote_field.model.split(".") - kwargs["to"] = "%s.%s" % (app_label, model_name.lower()) - else: - kwargs["to"] = self.remote_field.model.lower() + kwargs['to'] = self.remote_field.model else: - kwargs["to"] = self.remote_field.model._meta.label_lower - if getattr(self.remote_field, "through", None) is not None: + kwargs['to'] = self.remote_field.model._meta.label + if getattr(self.remote_field, 'through', None) is not None: if isinstance(self.remote_field.through, str): - kwargs["through"] = self.remote_field.through + kwargs['through'] = self.remote_field.through elif not self.remote_field.through._meta.auto_created: - kwargs["through"] = self.remote_field.through._meta.label + kwargs['through'] = self.remote_field.through._meta.label # If swappable is True, then see if we're actually pointing to the target # of a swap. swappable_setting = self.swappable_setting if swappable_setting is not None: # If it's already a settings reference, error. - if hasattr(kwargs["to"], "setting_name"): - if kwargs["to"].setting_name != swappable_setting: + if hasattr(kwargs['to'], "setting_name"): + if kwargs['to'].setting_name != swappable_setting: raise ValueError( "Cannot deconstruct a ManyToManyField pointing to a " "model that is swapped in place of more than one model " - "(%s and %s)" % (kwargs["to"].setting_name, swappable_setting) + "(%s and %s)" % (kwargs['to'].setting_name, swappable_setting) ) - kwargs["to"] = SettingsReference( - kwargs["to"], + kwargs['to'] = SettingsReference( + kwargs['to'], swappable_setting, ) return name, path, args, kwargs @@ -1786,7 +1546,7 @@ class ManyToManyField(RelatedField): elif self.db_table: return self.db_table else: - m2m_table_name = "%s_%s" % (utils.strip_quotes(opts.db_table), self.name) + m2m_table_name = '%s_%s' % (utils.strip_quotes(opts.db_table), self.name) return utils.truncate_name(m2m_table_name, connection.ops.max_name_length()) def _get_m2m_attr(self, related, attr): @@ -1794,7 +1554,7 @@ class ManyToManyField(RelatedField): Function that can be curried to provide the source accessor or DB column name for the m2m table. """ - cache_attr = "_m2m_%s_cache" % attr + cache_attr = '_m2m_%s_cache' % attr if hasattr(self, cache_attr): return getattr(self, cache_attr) if self.remote_field.through_fields is not None: @@ -1802,11 +1562,8 @@ class ManyToManyField(RelatedField): else: link_field_name = None for f in self.remote_field.through._meta.fields: - if ( - f.is_relation - and f.remote_field.model == related.related_model - and (link_field_name is None or link_field_name == f.name) - ): + if (f.is_relation and f.remote_field.model == related.related_model and + (link_field_name is None or link_field_name == f.name)): setattr(self, cache_attr, getattr(f, attr)) return getattr(self, cache_attr) @@ -1815,7 +1572,7 @@ class ManyToManyField(RelatedField): Function that can be curried to provide the related accessor or DB column name for the m2m table. """ - cache_attr = "_m2m_reverse_%s_cache" % attr + cache_attr = '_m2m_reverse_%s_cache' % attr if hasattr(self, cache_attr): return getattr(self, cache_attr) found = False @@ -1848,8 +1605,8 @@ class ManyToManyField(RelatedField): # automatically. The funky name reduces the chance of an accidental # clash. if self.remote_field.symmetrical and ( - self.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT - or self.remote_field.model == cls._meta.object_name + self.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT or + self.remote_field.model == cls._meta.object_name ): self.remote_field.related_name = "%s_rel_+" % name elif self.remote_field.is_hidden(): @@ -1857,7 +1614,7 @@ class ManyToManyField(RelatedField): # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. - self.remote_field.related_name = "_%s_%s_%s_+" % ( + self.remote_field.related_name = '_%s_%s_%s_+' % ( cls._meta.app_label, cls.__name__.lower(), name, @@ -1871,17 +1628,11 @@ class ManyToManyField(RelatedField): # 3) The class owning the m2m field has been swapped out. if not cls._meta.abstract: if self.remote_field.through: - def resolve_through_model(_, model, field): field.remote_field.through = model - - lazy_related_operation( - resolve_through_model, cls, self.remote_field.through, field=self - ) + lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self) elif not cls._meta.swapped: - self.remote_field.through = create_many_to_many_intermediary_model( - self, cls - ) + self.remote_field.through = create_many_to_many_intermediary_model(self, cls) # Add the descriptor for the m2m relation. setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False)) @@ -1892,30 +1643,19 @@ class ManyToManyField(RelatedField): def contribute_to_related_class(self, cls, related): # Internal M2Ms (i.e., those with a related name ending with '+') # and swapped models don't get a related descriptor. - if ( - not self.remote_field.is_hidden() - and not related.related_model._meta.swapped - ): - setattr( - cls, - related.get_accessor_name(), - ManyToManyDescriptor(self.remote_field, reverse=True), - ) + if not self.remote_field.is_hidden() and not related.related_model._meta.swapped: + setattr(cls, related.get_accessor_name(), ManyToManyDescriptor(self.remote_field, reverse=True)) # Set up the accessors for the column names on the m2m table. - self.m2m_column_name = partial(self._get_m2m_attr, related, "column") - self.m2m_reverse_name = partial(self._get_m2m_reverse_attr, related, "column") + self.m2m_column_name = partial(self._get_m2m_attr, related, 'column') + self.m2m_reverse_name = partial(self._get_m2m_reverse_attr, related, 'column') - self.m2m_field_name = partial(self._get_m2m_attr, related, "name") - self.m2m_reverse_field_name = partial( - self._get_m2m_reverse_attr, related, "name" - ) + self.m2m_field_name = partial(self._get_m2m_attr, related, 'name') + self.m2m_reverse_field_name = partial(self._get_m2m_reverse_attr, related, 'name') - get_m2m_rel = partial(self._get_m2m_attr, related, "remote_field") + get_m2m_rel = partial(self._get_m2m_attr, related, 'remote_field') self.m2m_target_field_name = lambda: get_m2m_rel().field_name - get_m2m_reverse_rel = partial( - self._get_m2m_reverse_attr, related, "remote_field" - ) + get_m2m_reverse_rel = partial(self._get_m2m_reverse_attr, related, 'remote_field') self.m2m_reverse_target_field_name = lambda: get_m2m_reverse_rel().field_name def set_attributes_from_rel(self): @@ -1929,17 +1669,17 @@ class ManyToManyField(RelatedField): def formfield(self, *, using=None, **kwargs): defaults = { - "form_class": forms.ModelMultipleChoiceField, - "queryset": self.remote_field.model._default_manager.using(using), + 'form_class': forms.ModelMultipleChoiceField, + 'queryset': self.remote_field.model._default_manager.using(using), **kwargs, } # If initial is passed in, it's a list of related objects, but the # MultipleChoiceField takes a list of IDs. - if defaults.get("initial") is not None: - initial = defaults["initial"] + if defaults.get('initial') is not None: + initial = defaults['initial'] if callable(initial): initial = initial() - defaults["initial"] = [i.pk for i in initial] + defaults['initial'] = [i.pk for i in initial] return super().formfield(**defaults) def db_check(self, connection): diff --git a/venv/Lib/site-packages/django/db/models/fields/related_descriptors.py b/venv/Lib/site-packages/django/db/models/fields/related_descriptors.py index c53eb49..872a4c9 100644 --- a/venv/Lib/site-packages/django/db/models/fields/related_descriptors.py +++ b/venv/Lib/site-packages/django/db/models/fields/related_descriptors.py @@ -74,9 +74,7 @@ from django.utils.functional import cached_property class ForeignKeyDeferredAttribute(DeferredAttribute): def __set__(self, instance, value): - if instance.__dict__.get(self.field.attname) != value and self.field.is_cached( - instance - ): + if instance.__dict__.get(self.field.attname) != value and self.field.is_cached(instance): self.field.delete_cached_value(instance) instance.__dict__[self.field.attname] = value @@ -103,16 +101,14 @@ class ForwardManyToOneDescriptor: # related model might not be resolved yet; `self.field.model` might # still be a string model reference. return type( - "RelatedObjectDoesNotExist", - (self.field.remote_field.model.DoesNotExist, AttributeError), - { - "__module__": self.field.model.__module__, - "__qualname__": "%s.%s.RelatedObjectDoesNotExist" - % ( + 'RelatedObjectDoesNotExist', + (self.field.remote_field.model.DoesNotExist, AttributeError), { + '__module__': self.field.model.__module__, + '__qualname__': '%s.%s.RelatedObjectDoesNotExist' % ( self.field.model.__qualname__, self.field.name, ), - }, + } ) def is_cached(self, instance): @@ -139,12 +135,9 @@ class ForwardManyToOneDescriptor: # The check for len(...) == 1 is a special case that allows the query # to be join-less and smaller. Refs #21760. if remote_field.is_hidden() or len(self.field.foreign_related_fields) == 1: - query = { - "%s__in" - % related_field.name: {instance_attr(inst)[0] for inst in instances} - } + query = {'%s__in' % related_field.name: {instance_attr(inst)[0] for inst in instances}} else: - query = {"%s__in" % self.field.related_query_name(): instances} + query = {'%s__in' % self.field.related_query_name(): instances} queryset = queryset.filter(**query) # Since we're going to assign directly in the cache, @@ -153,14 +146,7 @@ class ForwardManyToOneDescriptor: for rel_obj in queryset: instance = instances_dict[rel_obj_attr(rel_obj)] remote_field.set_cached_value(rel_obj, instance) - return ( - queryset, - rel_obj_attr, - instance_attr, - True, - self.field.get_cache_name(), - False, - ) + return queryset, rel_obj_attr, instance_attr, True, self.field.get_cache_name(), False def get_object(self, instance): qs = self.get_queryset(instance=instance) @@ -187,11 +173,7 @@ class ForwardManyToOneDescriptor: rel_obj = self.field.get_cached_value(instance) except KeyError: has_value = None not in self.field.get_local_related_value(instance) - ancestor_link = ( - instance._meta.get_ancestor_link(self.field.model) - if has_value - else None - ) + ancestor_link = instance._meta.get_ancestor_link(self.field.model) if has_value else None if ancestor_link and ancestor_link.is_cached(instance): # An ancestor link will exist if this field is defined on a # multi-table inheritance parent of the instance's class. @@ -229,12 +211,9 @@ class ForwardManyToOneDescriptor: - ``value`` is the ``parent`` instance on the right of the equal sign """ # An object must be an instance of the related class. - if value is not None and not isinstance( - value, self.field.remote_field.model._meta.concrete_model - ): + if value is not None and not isinstance(value, self.field.remote_field.model._meta.concrete_model): raise ValueError( - 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' - % ( + 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % ( value, instance._meta.object_name, self.field.name, @@ -243,18 +222,11 @@ class ForwardManyToOneDescriptor: ) elif value is not None: if instance._state.db is None: - instance._state.db = router.db_for_write( - instance.__class__, instance=value - ) + instance._state.db = router.db_for_write(instance.__class__, instance=value) if value._state.db is None: - value._state.db = router.db_for_write( - value.__class__, instance=instance - ) + value._state.db = router.db_for_write(value.__class__, instance=instance) if not router.allow_relation(value, instance): - raise ValueError( - 'Cannot assign "%r": the current database router prevents this ' - "relation." % value - ) + raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value) remote_field = self.field.remote_field # If we're setting the value of a OneToOneField to None, we need to clear @@ -342,15 +314,12 @@ class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor): opts = instance._meta # Inherited primary key fields from this object's base classes. inherited_pk_fields = [ - field - for field in opts.concrete_fields + field for field in opts.concrete_fields if field.primary_key and field.remote_field ] for field in inherited_pk_fields: rel_model_pk_name = field.remote_field.model._meta.pk.attname - raw_value = ( - getattr(value, rel_model_pk_name) if value is not None else None - ) + raw_value = getattr(value, rel_model_pk_name) if value is not None else None setattr(instance, rel_model_pk_name, raw_value) @@ -377,15 +346,13 @@ class ReverseOneToOneDescriptor: # The exception isn't created at initialization time for the sake of # consistency with `ForwardManyToOneDescriptor`. return type( - "RelatedObjectDoesNotExist", - (self.related.related_model.DoesNotExist, AttributeError), - { - "__module__": self.related.model.__module__, - "__qualname__": "%s.%s.RelatedObjectDoesNotExist" - % ( + 'RelatedObjectDoesNotExist', + (self.related.related_model.DoesNotExist, AttributeError), { + '__module__': self.related.model.__module__, + '__qualname__': '%s.%s.RelatedObjectDoesNotExist' % ( self.related.model.__qualname__, self.related.name, - ), + ) }, ) @@ -403,7 +370,7 @@ class ReverseOneToOneDescriptor: rel_obj_attr = self.related.field.get_local_related_value instance_attr = self.related.field.get_foreign_related_value instances_dict = {instance_attr(inst): inst for inst in instances} - query = {"%s__in" % self.related.field.name: instances} + query = {'%s__in' % self.related.field.name: instances} queryset = queryset.filter(**query) # Since we're going to assign directly in the cache, @@ -411,14 +378,7 @@ class ReverseOneToOneDescriptor: for rel_obj in queryset: instance = instances_dict[rel_obj_attr(rel_obj)] self.related.field.set_cached_value(rel_obj, instance) - return ( - queryset, - rel_obj_attr, - instance_attr, - True, - self.related.get_cache_name(), - False, - ) + return queryset, rel_obj_attr, instance_attr, True, self.related.get_cache_name(), False def __get__(self, instance, cls=None): """ @@ -459,8 +419,10 @@ class ReverseOneToOneDescriptor: if rel_obj is None: raise self.RelatedObjectDoesNotExist( - "%s has no %s." - % (instance.__class__.__name__, self.related.get_accessor_name()) + "%s has no %s." % ( + instance.__class__.__name__, + self.related.get_accessor_name() + ) ) else: return rel_obj @@ -496,8 +458,7 @@ class ReverseOneToOneDescriptor: elif not isinstance(value, self.related.related_model): # An object must be an instance of the related class. raise ValueError( - 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' - % ( + 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % ( value, instance._meta.object_name, self.related.get_accessor_name(), @@ -506,25 +467,14 @@ class ReverseOneToOneDescriptor: ) else: if instance._state.db is None: - instance._state.db = router.db_for_write( - instance.__class__, instance=value - ) + instance._state.db = router.db_for_write(instance.__class__, instance=value) if value._state.db is None: - value._state.db = router.db_for_write( - value.__class__, instance=instance - ) + value._state.db = router.db_for_write(value.__class__, instance=instance) if not router.allow_relation(value, instance): - raise ValueError( - 'Cannot assign "%r": the current database router prevents this ' - "relation." % value - ) + raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value) - related_pk = tuple( - getattr(instance, field.attname) - for field in self.related.field.foreign_related_fields - ) - # Set the value of the related field to the value of the related - # object's related field. + related_pk = tuple(getattr(instance, field.attname) for field in self.related.field.foreign_related_fields) + # Set the value of the related field to the value of the related object's related field for index, field in enumerate(self.related.field.local_related_fields): setattr(value, field.attname, related_pk[index]) @@ -587,13 +537,13 @@ class ReverseManyToOneDescriptor: def _get_set_deprecation_msg_params(self): return ( - "reverse side of a related set", + 'reverse side of a related set', self.rel.get_accessor_name(), ) def __set__(self, instance, value): raise TypeError( - "Direct assignment to the %s is prohibited. Use %s.set() instead." + 'Direct assignment to the %s is prohibited. Use %s.set() instead.' % self._get_set_deprecation_msg_params(), ) @@ -620,7 +570,6 @@ def create_reverse_many_to_one_manager(superclass, rel): manager = getattr(self.model, manager) manager_class = create_reverse_many_to_one_manager(manager.__class__, rel) return manager_class(self.instance) - do_not_call_in_templates = True def _apply_rel_filters(self, queryset): @@ -628,9 +577,7 @@ def create_reverse_many_to_one_manager(superclass, rel): Filter the queryset for the instance this manager is bound to. """ db = self._db or router.db_for_read(self.model, instance=self.instance) - empty_strings_as_null = connections[ - db - ].features.interprets_empty_strings_as_nulls + empty_strings_as_null = connections[db].features.interprets_empty_strings_as_nulls queryset._add_hints(instance=self.instance) if self._db: queryset = queryset.using(self._db) @@ -638,7 +585,7 @@ def create_reverse_many_to_one_manager(superclass, rel): queryset = queryset.filter(**self.core_filters) for field in self.field.foreign_related_fields: val = getattr(self.instance, field.attname) - if val is None or (val == "" and empty_strings_as_null): + if val is None or (val == '' and empty_strings_as_null): return queryset.none() if self.field.many_to_one: # Guard against field-like objects such as GenericRelation @@ -650,34 +597,24 @@ def create_reverse_many_to_one_manager(superclass, rel): except FieldError: # The relationship has multiple target fields. Use a tuple # for related object id. - rel_obj_id = tuple( - [ - getattr(self.instance, target_field.attname) - for target_field in self.field.get_path_info()[ - -1 - ].target_fields - ] - ) + rel_obj_id = tuple([ + getattr(self.instance, target_field.attname) + for target_field in self.field.get_path_info()[-1].target_fields + ]) else: rel_obj_id = getattr(self.instance, target_field.attname) - queryset._known_related_objects = { - self.field: {rel_obj_id: self.instance} - } + queryset._known_related_objects = {self.field: {rel_obj_id: self.instance}} return queryset def _remove_prefetched_objects(self): try: - self.instance._prefetched_objects_cache.pop( - self.field.remote_field.get_cache_name() - ) + self.instance._prefetched_objects_cache.pop(self.field.remote_field.get_cache_name()) except (AttributeError, KeyError): pass # nothing to clear from cache def get_queryset(self): try: - return self.instance._prefetched_objects_cache[ - self.field.remote_field.get_cache_name() - ] + return self.instance._prefetched_objects_cache[self.field.remote_field.get_cache_name()] except (AttributeError, KeyError): queryset = super().get_queryset() return self._apply_rel_filters(queryset) @@ -692,7 +629,7 @@ def create_reverse_many_to_one_manager(superclass, rel): rel_obj_attr = self.field.get_local_related_value instance_attr = self.field.get_foreign_related_value instances_dict = {instance_attr(inst): inst for inst in instances} - query = {"%s__in" % self.field.name: instances} + query = {'%s__in' % self.field.name: instances} queryset = queryset.filter(**query) # Since we just bypassed this class' get_queryset(), we must manage @@ -709,13 +646,9 @@ def create_reverse_many_to_one_manager(superclass, rel): def check_and_update_obj(obj): if not isinstance(obj, self.model): - raise TypeError( - "'%s' instance expected, got %r" - % ( - self.model._meta.object_name, - obj, - ) - ) + raise TypeError("'%s' instance expected, got %r" % ( + self.model._meta.object_name, obj, + )) setattr(obj, self.field.name, self.instance) if bulk: @@ -728,44 +661,36 @@ def create_reverse_many_to_one_manager(superclass, rel): "the object first." % obj ) pks.append(obj.pk) - self.model._base_manager.using(db).filter(pk__in=pks).update( - **{ - self.field.name: self.instance, - } - ) + self.model._base_manager.using(db).filter(pk__in=pks).update(**{ + self.field.name: self.instance, + }) else: with transaction.atomic(using=db, savepoint=False): for obj in objs: check_and_update_obj(obj) obj.save() - add.alters_data = True def create(self, **kwargs): kwargs[self.field.name] = self.instance db = router.db_for_write(self.model, instance=self.instance) return super(RelatedManager, self.db_manager(db)).create(**kwargs) - create.alters_data = True def get_or_create(self, **kwargs): kwargs[self.field.name] = self.instance db = router.db_for_write(self.model, instance=self.instance) return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs) - get_or_create.alters_data = True def update_or_create(self, **kwargs): kwargs[self.field.name] = self.instance db = router.db_for_write(self.model, instance=self.instance) return super(RelatedManager, self.db_manager(db)).update_or_create(**kwargs) - update_or_create.alters_data = True - # remove() and clear() are only provided if the ForeignKey can have a - # value of null. + # remove() and clear() are only provided if the ForeignKey can have a value of null. if rel.field.null: - def remove(self, *objs, bulk=True): if not objs: return @@ -773,13 +698,9 @@ def create_reverse_many_to_one_manager(superclass, rel): old_ids = set() for obj in objs: if not isinstance(obj, self.model): - raise TypeError( - "'%s' instance expected, got %r" - % ( - self.model._meta.object_name, - obj, - ) - ) + raise TypeError("'%s' instance expected, got %r" % ( + self.model._meta.object_name, obj, + )) # Is obj actually part of this descriptor set? if self.field.get_local_related_value(obj) == val: old_ids.add(obj.pk) @@ -788,12 +709,10 @@ def create_reverse_many_to_one_manager(superclass, rel): "%r is not related to %r." % (obj, self.instance) ) self._clear(self.filter(pk__in=old_ids), bulk) - remove.alters_data = True def clear(self, *, bulk=True): self._clear(self, bulk) - clear.alters_data = True def _clear(self, queryset, bulk): @@ -808,7 +727,6 @@ def create_reverse_many_to_one_manager(superclass, rel): for obj in queryset: setattr(obj, self.field.name, None) obj.save(update_fields=[self.field.name]) - _clear.alters_data = True def set(self, objs, *, bulk=True, clear=False): @@ -835,7 +753,6 @@ def create_reverse_many_to_one_manager(superclass, rel): self.add(*new_objs, bulk=bulk) else: self.add(*objs, bulk=bulk) - set.alters_data = True return RelatedManager @@ -882,8 +799,7 @@ class ManyToManyDescriptor(ReverseManyToOneDescriptor): def _get_set_deprecation_msg_params(self): return ( - "%s side of a many-to-many set" - % ("reverse" if self.reverse else "forward"), + '%s side of a many-to-many set' % ('reverse' if self.reverse else 'forward'), self.rel.get_accessor_name() if self.reverse else self.field.name, ) @@ -926,51 +842,42 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): self.core_filters = {} self.pk_field_names = {} for lh_field, rh_field in self.source_field.related_fields: - core_filter_key = "%s__%s" % (self.query_field_name, rh_field.name) + core_filter_key = '%s__%s' % (self.query_field_name, rh_field.name) self.core_filters[core_filter_key] = getattr(instance, rh_field.attname) self.pk_field_names[lh_field.name] = rh_field.name self.related_val = self.source_field.get_foreign_related_value(instance) if None in self.related_val: - raise ValueError( - '"%r" needs to have a value for field "%s" before ' - "this many-to-many relationship can be used." - % (instance, self.pk_field_names[self.source_field_name]) - ) + raise ValueError('"%r" needs to have a value for field "%s" before ' + 'this many-to-many relationship can be used.' % + (instance, self.pk_field_names[self.source_field_name])) # Even if this relation is not to pk, we require still pk value. # The wish is that the instance has been already saved to DB, # although having a pk value isn't a guarantee of that. if instance.pk is None: - raise ValueError( - "%r instance needs to have a primary key value before " - "a many-to-many relationship can be used." - % instance.__class__.__name__ - ) + raise ValueError("%r instance needs to have a primary key value before " + "a many-to-many relationship can be used." % + instance.__class__.__name__) def __call__(self, *, manager): manager = getattr(self.model, manager) - manager_class = create_forward_many_to_many_manager( - manager.__class__, rel, reverse - ) + manager_class = create_forward_many_to_many_manager(manager.__class__, rel, reverse) return manager_class(instance=self.instance) - do_not_call_in_templates = True def _build_remove_filters(self, removed_vals): - filters = Q((self.source_field_name, self.related_val)) + filters = Q(**{self.source_field_name: self.related_val}) # No need to add a subquery condition if removed_vals is a QuerySet without # filters. - removed_vals_filters = ( - not isinstance(removed_vals, QuerySet) or removed_vals._has_filters() - ) + removed_vals_filters = (not isinstance(removed_vals, QuerySet) or + removed_vals._has_filters()) if removed_vals_filters: - filters &= Q((f"{self.target_field_name}__in", removed_vals)) + filters &= Q(**{'%s__in' % self.target_field_name: removed_vals}) if self.symmetrical: - symmetrical_filters = Q((self.target_field_name, self.related_val)) + symmetrical_filters = Q(**{self.target_field_name: self.related_val}) if removed_vals_filters: symmetrical_filters &= Q( - (f"{self.source_field_name}__in", removed_vals) - ) + **{'%s__in' % self.source_field_name: removed_vals}) filters |= symmetrical_filters return filters @@ -1004,7 +911,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): queryset._add_hints(instance=instances[0]) queryset = queryset.using(queryset._db or self._db) - query = {"%s__in" % self.query_field_name: instances} + query = {'%s__in' % self.query_field_name: instances} queryset = queryset._next_is_sticky().filter(**query) # M2M: need to annotate the query in order to get the primary model @@ -1018,18 +925,13 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): join_table = fk.model._meta.db_table connection = connections[queryset.db] qn = connection.ops.quote_name - queryset = queryset.extra( - select={ - "_prefetch_related_val_%s" - % f.attname: "%s.%s" - % (qn(join_table), qn(f.column)) - for f in fk.local_related_fields - } - ) + queryset = queryset.extra(select={ + '_prefetch_related_val_%s' % f.attname: + '%s.%s' % (qn(join_table), qn(f.column)) for f in fk.local_related_fields}) return ( queryset, lambda result: tuple( - getattr(result, "_prefetch_related_val_%s" % f.attname) + getattr(result, '_prefetch_related_val_%s' % f.attname) for f in fk.local_related_fields ), lambda inst: tuple( @@ -1046,9 +948,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): db = router.db_for_write(self.through, instance=self.instance) with transaction.atomic(using=db, savepoint=False): self._add_items( - self.source_field_name, - self.target_field_name, - *objs, + self.source_field_name, self.target_field_name, *objs, through_defaults=through_defaults, ) # If this is a symmetrical m2m relation to self, add the mirror @@ -1060,41 +960,30 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): *objs, through_defaults=through_defaults, ) - add.alters_data = True def remove(self, *objs): self._remove_prefetched_objects() self._remove_items(self.source_field_name, self.target_field_name, *objs) - remove.alters_data = True def clear(self): db = router.db_for_write(self.through, instance=self.instance) with transaction.atomic(using=db, savepoint=False): signals.m2m_changed.send( - sender=self.through, - action="pre_clear", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=None, - using=db, + sender=self.through, action="pre_clear", + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=None, using=db, ) self._remove_prefetched_objects() filters = self._build_remove_filters(super().get_queryset().using(db)) self.through._default_manager.using(db).filter(filters).delete() signals.m2m_changed.send( - sender=self.through, - action="post_clear", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=None, - using=db, + sender=self.through, action="post_clear", + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=None, using=db, ) - clear.alters_data = True def set(self, objs, *, clear=False, through_defaults=None): @@ -1108,11 +997,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): self.clear() self.add(*objs, through_defaults=through_defaults) else: - old_ids = set( - self.using(db).values_list( - self.target_field.target_field.attname, flat=True - ) - ) + old_ids = set(self.using(db).values_list(self.target_field.target_field.attname, flat=True)) new_objs = [] for obj in objs: @@ -1128,7 +1013,6 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): self.remove(*old_ids) self.add(*new_objs, through_defaults=through_defaults) - set.alters_data = True def create(self, *, through_defaults=None, **kwargs): @@ -1136,33 +1020,26 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): new_obj = super(ManyRelatedManager, self.db_manager(db)).create(**kwargs) self.add(new_obj, through_defaults=through_defaults) return new_obj - create.alters_data = True def get_or_create(self, *, through_defaults=None, **kwargs): db = router.db_for_write(self.instance.__class__, instance=self.instance) - obj, created = super(ManyRelatedManager, self.db_manager(db)).get_or_create( - **kwargs - ) + obj, created = super(ManyRelatedManager, self.db_manager(db)).get_or_create(**kwargs) # We only need to add() if created because if we got an object back # from get() then the relationship already exists. if created: self.add(obj, through_defaults=through_defaults) return obj, created - get_or_create.alters_data = True def update_or_create(self, *, through_defaults=None, **kwargs): db = router.db_for_write(self.instance.__class__, instance=self.instance) - obj, created = super( - ManyRelatedManager, self.db_manager(db) - ).update_or_create(**kwargs) + obj, created = super(ManyRelatedManager, self.db_manager(db)).update_or_create(**kwargs) # We only need to add() if created because if we got an object back # from get() then the relationship already exists. if created: self.add(obj, through_defaults=through_defaults) return obj, created - update_or_create.alters_data = True def _get_target_ids(self, target_field_name, objs): @@ -1170,7 +1047,6 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): Return the set of ids of `objs` that the target field references. """ from django.db.models import Model - target_ids = set() target_field = self.through._meta.get_field(target_field_name) for obj in objs: @@ -1178,42 +1054,36 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): if not router.allow_relation(obj, self.instance): raise ValueError( 'Cannot add "%r": instance is on database "%s", ' - 'value is on database "%s"' - % (obj, self.instance._state.db, obj._state.db) + 'value is on database "%s"' % + (obj, self.instance._state.db, obj._state.db) ) target_id = target_field.get_foreign_related_value(obj)[0] if target_id is None: raise ValueError( - 'Cannot add "%r": the value for field "%s" is None' - % (obj, target_field_name) + 'Cannot add "%r": the value for field "%s" is None' % + (obj, target_field_name) ) target_ids.add(target_id) elif isinstance(obj, Model): raise TypeError( - "'%s' instance expected, got %r" - % (self.model._meta.object_name, obj) + "'%s' instance expected, got %r" % + (self.model._meta.object_name, obj) ) else: target_ids.add(target_field.get_prep_value(obj)) return target_ids - def _get_missing_target_ids( - self, source_field_name, target_field_name, db, target_ids - ): + def _get_missing_target_ids(self, source_field_name, target_field_name, db, target_ids): """ Return the subset of ids of `objs` that aren't already assigned to this relationship. """ - vals = ( - self.through._default_manager.using(db) - .values_list(target_field_name, flat=True) - .filter( - **{ - source_field_name: self.related_val[0], - "%s__in" % target_field_name: target_ids, - } - ) - ) + vals = self.through._default_manager.using(db).values_list( + target_field_name, flat=True + ).filter(**{ + source_field_name: self.related_val[0], + '%s__in' % target_field_name: target_ids, + }) return target_ids.difference(vals) def _get_add_plan(self, db, source_field_name): @@ -1231,53 +1101,39 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): # user-defined intermediary models as they could have other fields # causing conflicts which must be surfaced. can_ignore_conflicts = ( - connections[db].features.supports_ignore_conflicts - and self.through._meta.auto_created is not False + connections[db].features.supports_ignore_conflicts and + self.through._meta.auto_created is not False ) # Don't send the signal when inserting duplicate data row # for symmetrical reverse entries. - must_send_signals = ( - self.reverse or source_field_name == self.source_field_name - ) and (signals.m2m_changed.has_listeners(self.through)) + must_send_signals = (self.reverse or source_field_name == self.source_field_name) and ( + signals.m2m_changed.has_listeners(self.through) + ) # Fast addition through bulk insertion can only be performed # if no m2m_changed listeners are connected for self.through # as they require the added set of ids to be provided via # pk_set. - return ( - can_ignore_conflicts, - must_send_signals, - (can_ignore_conflicts and not must_send_signals), - ) + return can_ignore_conflicts, must_send_signals, (can_ignore_conflicts and not must_send_signals) - def _add_items( - self, source_field_name, target_field_name, *objs, through_defaults=None - ): + def _add_items(self, source_field_name, target_field_name, *objs, through_defaults=None): # source_field_name: the PK fieldname in join table for the source object # target_field_name: the PK fieldname in join table for the target object - # *objs - objects to add. Either object instances, or primary keys - # of object instances. + # *objs - objects to add. Either object instances, or primary keys of object instances. if not objs: return through_defaults = dict(resolve_callables(through_defaults or {})) target_ids = self._get_target_ids(target_field_name, objs) db = router.db_for_write(self.through, instance=self.instance) - can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan( - db, source_field_name - ) + can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan(db, source_field_name) if can_fast_add: - self.through._default_manager.using(db).bulk_create( - [ - self.through( - **{ - "%s_id" % source_field_name: self.related_val[0], - "%s_id" % target_field_name: target_id, - } - ) - for target_id in target_ids - ], - ignore_conflicts=True, - ) + self.through._default_manager.using(db).bulk_create([ + self.through(**{ + '%s_id' % source_field_name: self.related_val[0], + '%s_id' % target_field_name: target_id, + }) + for target_id in target_ids + ], ignore_conflicts=True) return missing_target_ids = self._get_missing_target_ids( @@ -1286,38 +1142,24 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): with transaction.atomic(using=db, savepoint=False): if must_send_signals: signals.m2m_changed.send( - sender=self.through, - action="pre_add", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=missing_target_ids, - using=db, + sender=self.through, action='pre_add', + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=missing_target_ids, using=db, ) # Add the ones that aren't there already. - self.through._default_manager.using(db).bulk_create( - [ - self.through( - **through_defaults, - **{ - "%s_id" % source_field_name: self.related_val[0], - "%s_id" % target_field_name: target_id, - }, - ) - for target_id in missing_target_ids - ], - ignore_conflicts=can_ignore_conflicts, - ) + self.through._default_manager.using(db).bulk_create([ + self.through(**through_defaults, **{ + '%s_id' % source_field_name: self.related_val[0], + '%s_id' % target_field_name: target_id, + }) + for target_id in missing_target_ids + ], ignore_conflicts=can_ignore_conflicts) if must_send_signals: signals.m2m_changed.send( - sender=self.through, - action="post_add", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=missing_target_ids, - using=db, + sender=self.through, action='post_add', + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=missing_target_ids, using=db, ) def _remove_items(self, source_field_name, target_field_name, *objs): @@ -1341,32 +1183,23 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): with transaction.atomic(using=db, savepoint=False): # Send a signal to the other end if need be. signals.m2m_changed.send( - sender=self.through, - action="pre_remove", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=old_ids, - using=db, + sender=self.through, action="pre_remove", + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=old_ids, using=db, ) target_model_qs = super().get_queryset() if target_model_qs._has_filters(): - old_vals = target_model_qs.using(db).filter( - **{"%s__in" % self.target_field.target_field.attname: old_ids} - ) + old_vals = target_model_qs.using(db).filter(**{ + '%s__in' % self.target_field.target_field.attname: old_ids}) else: old_vals = old_ids filters = self._build_remove_filters(old_vals) self.through._default_manager.using(db).filter(filters).delete() signals.m2m_changed.send( - sender=self.through, - action="post_remove", - instance=self.instance, - reverse=self.reverse, - model=self.model, - pk_set=old_ids, - using=db, + sender=self.through, action="post_remove", + instance=self.instance, reverse=self.reverse, + model=self.model, pk_set=old_ids, using=db, ) return ManyRelatedManager diff --git a/venv/Lib/site-packages/django/db/models/fields/related_lookups.py b/venv/Lib/site-packages/django/db/models/fields/related_lookups.py index 910f621..d745ecd 100644 --- a/venv/Lib/site-packages/django/db/models/fields/related_lookups.py +++ b/venv/Lib/site-packages/django/db/models/fields/related_lookups.py @@ -1,10 +1,5 @@ from django.db.models.lookups import ( - Exact, - GreaterThan, - GreaterThanOrEqual, - In, - IsNull, - LessThan, + Exact, GreaterThan, GreaterThanOrEqual, In, IsNull, LessThan, LessThanOrEqual, ) @@ -13,40 +8,29 @@ class MultiColSource: contains_aggregate = False def __init__(self, alias, targets, sources, field): - self.targets, self.sources, self.field, self.alias = ( - targets, - sources, - field, - alias, - ) + self.targets, self.sources, self.field, self.alias = targets, sources, field, alias self.output_field = self.field def __repr__(self): - return "{}({}, {})".format(self.__class__.__name__, self.alias, self.field) + return "{}({}, {})".format( + self.__class__.__name__, self.alias, self.field) def relabeled_clone(self, relabels): - return self.__class__( - relabels.get(self.alias, self.alias), self.targets, self.sources, self.field - ) + return self.__class__(relabels.get(self.alias, self.alias), + self.targets, self.sources, self.field) def get_lookup(self, lookup): return self.output_field.get_lookup(lookup) - def resolve_expression(self, *args, **kwargs): - return self - def get_normalized_value(value, lhs): from django.db.models import Model - if isinstance(value, Model): value_list = [] sources = lhs.output_field.get_path_info()[-1].target_fields for source in sources: while not isinstance(value, source.model) and source.remote_field: - source = source.remote_field.model._meta.get_field( - source.remote_field.field_name - ) + source = source.remote_field.model._meta.get_field(source.remote_field.field_name) try: value_list.append(getattr(value, source.attname)) except AttributeError: @@ -68,26 +52,20 @@ class RelatedIn(In): # ForeignKey to IntegerField given value 'abc'. The ForeignKey itself # doesn't have validation for non-integers, so we must run validation # using the target field. - if hasattr(self.lhs.output_field, "get_path_info"): + if hasattr(self.lhs.output_field, 'get_path_info'): # Run the target field's get_prep_value. We can safely assume there is # only one as we don't get to the direct value branch otherwise. - target_field = self.lhs.output_field.get_path_info()[-1].target_fields[ - -1 - ] + target_field = self.lhs.output_field.get_path_info()[-1].target_fields[-1] self.rhs = [target_field.get_prep_value(v) for v in self.rhs] return super().get_prep_lookup() def as_sql(self, compiler, connection): if isinstance(self.lhs, MultiColSource): # For multicolumn lookups we need to build a multicolumn where clause. - # This clause is either a SubqueryConstraint (for values that need - # to be compiled to SQL) or an OR-combined list of - # (col1 = val1 AND col2 = val2 AND ...) clauses. + # This clause is either a SubqueryConstraint (for values that need to be compiled to + # SQL) or an OR-combined list of (col1 = val1 AND col2 = val2 AND ...) clauses. from django.db.models.sql.where import ( - AND, - OR, - SubqueryConstraint, - WhereNode, + AND, OR, SubqueryConstraint, WhereNode, ) root_constraint = WhereNode(connector=OR) @@ -95,35 +73,24 @@ class RelatedIn(In): values = [get_normalized_value(value, self.lhs) for value in self.rhs] for value in values: value_constraint = WhereNode() - for source, target, val in zip( - self.lhs.sources, self.lhs.targets, value - ): - lookup_class = target.get_lookup("exact") - lookup = lookup_class( - target.get_col(self.lhs.alias, source), val - ) + for source, target, val in zip(self.lhs.sources, self.lhs.targets, value): + lookup_class = target.get_lookup('exact') + lookup = lookup_class(target.get_col(self.lhs.alias, source), val) value_constraint.add(lookup, AND) root_constraint.add(value_constraint, OR) else: root_constraint.add( SubqueryConstraint( - self.lhs.alias, - [target.column for target in self.lhs.targets], - [source.name for source in self.lhs.sources], - self.rhs, - ), - AND, - ) + self.lhs.alias, [target.column for target in self.lhs.targets], + [source.name for source in self.lhs.sources], self.rhs), + AND) return root_constraint.as_sql(compiler, connection) else: - if not getattr(self.rhs, "has_select_fields", True) and not getattr( - self.lhs.field.target_field, "primary_key", False - ): + if (not getattr(self.rhs, 'has_select_fields', True) and + not getattr(self.lhs.field.target_field, 'primary_key', False)): self.rhs.clear_select_clause() - if ( - getattr(self.lhs.output_field, "primary_key", False) - and self.lhs.output_field.model == self.rhs.model - ): + if (getattr(self.lhs.output_field, 'primary_key', False) and + self.lhs.output_field.model == self.rhs.model): # A case like Restaurant.objects.filter(place__in=restaurant_qs), # where place is a OneToOneField and the primary key of # Restaurant. @@ -136,21 +103,17 @@ class RelatedIn(In): class RelatedLookupMixin: def get_prep_lookup(self): - if not isinstance(self.lhs, MultiColSource) and not hasattr( - self.rhs, "resolve_expression" - ): + if not isinstance(self.lhs, MultiColSource) and not hasattr(self.rhs, 'resolve_expression'): # If we get here, we are dealing with single-column relations. self.rhs = get_normalized_value(self.rhs, self.lhs)[0] # We need to run the related field's get_prep_value(). Consider case # ForeignKey to IntegerField given value 'abc'. The ForeignKey itself # doesn't have validation for non-integers, so we must run validation # using the target field. - if self.prepare_rhs and hasattr(self.lhs.output_field, "get_path_info"): + if self.prepare_rhs and hasattr(self.lhs.output_field, 'get_path_info'): # Get the target field. We can safely assume there is only one # as we don't get to the direct value branch otherwise. - target_field = self.lhs.output_field.get_path_info()[-1].target_fields[ - -1 - ] + target_field = self.lhs.output_field.get_path_info()[-1].target_fields[-1] self.rhs = target_field.get_prep_value(self.rhs) return super().get_prep_lookup() @@ -160,15 +123,11 @@ class RelatedLookupMixin: assert self.rhs_is_direct_value() self.rhs = get_normalized_value(self.rhs, self.lhs) from django.db.models.sql.where import AND, WhereNode - root_constraint = WhereNode() - for target, source, val in zip( - self.lhs.targets, self.lhs.sources, self.rhs - ): + for target, source, val in zip(self.lhs.targets, self.lhs.sources, self.rhs): lookup_class = target.get_lookup(self.lookup_name) root_constraint.add( - lookup_class(target.get_col(self.lhs.alias, source), val), AND - ) + lookup_class(target.get_col(self.lhs.alias, source), val), AND) return root_constraint.as_sql(compiler, connection) return super().as_sql(compiler, connection) diff --git a/venv/Lib/site-packages/django/db/models/fields/reverse_related.py b/venv/Lib/site-packages/django/db/models/fields/reverse_related.py index d003beb..12160ad 100644 --- a/venv/Lib/site-packages/django/db/models/fields/reverse_related.py +++ b/venv/Lib/site-packages/django/db/models/fields/reverse_related.py @@ -36,16 +36,8 @@ class ForeignObjectRel(FieldCacheMixin): null = True empty_strings_allowed = False - def __init__( - self, - field, - to, - related_name=None, - related_query_name=None, - limit_choices_to=None, - parent_link=False, - on_delete=None, - ): + def __init__(self, field, to, related_name=None, related_query_name=None, + limit_choices_to=None, parent_link=False, on_delete=None): self.field = field self.model = to self.related_name = related_name @@ -81,18 +73,14 @@ class ForeignObjectRel(FieldCacheMixin): """ target_fields = self.get_path_info()[-1].target_fields if len(target_fields) > 1: - raise exceptions.FieldError( - "Can't use target_field for multicolumn relations." - ) + raise exceptions.FieldError("Can't use target_field for multicolumn relations.") return target_fields[0] @cached_property def related_model(self): if not self.field.model: raise AttributeError( - "This property can't be accessed before self.field.contribute_to_class " - "has been called." - ) + "This property can't be accessed before self.field.contribute_to_class has been called.") return self.field.model @cached_property @@ -122,7 +110,7 @@ class ForeignObjectRel(FieldCacheMixin): return self.field.db_type def __repr__(self): - return "<%s: %s.%s>" % ( + return '<%s: %s.%s>' % ( type(self).__name__, self.related_model._meta.app_label, self.related_model._meta.model_name, @@ -151,11 +139,8 @@ class ForeignObjectRel(FieldCacheMixin): return hash(self.identity) def get_choices( - self, - include_blank=True, - blank_choice=BLANK_CHOICE_DASH, - limit_choices_to=None, - ordering=(), + self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, + limit_choices_to=None, ordering=(), ): """ Return choices with a default blank choices included, for use @@ -168,17 +153,19 @@ class ForeignObjectRel(FieldCacheMixin): qs = self.related_model._default_manager.complex_filter(limit_choices_to) if ordering: qs = qs.order_by(*ordering) - return (blank_choice if include_blank else []) + [(x.pk, str(x)) for x in qs] + return (blank_choice if include_blank else []) + [ + (x.pk, str(x)) for x in qs + ] def is_hidden(self): """Should the related object be hidden?""" - return bool(self.related_name) and self.related_name[-1] == "+" + return bool(self.related_name) and self.related_name[-1] == '+' def get_joining_columns(self): return self.field.get_reverse_joining_columns() - def get_extra_restriction(self, alias, related_alias): - return self.field.get_extra_restriction(related_alias, alias) + def get_extra_restriction(self, where_class, alias, related_alias): + return self.field.get_extra_restriction(where_class, related_alias, alias) def set_field_name(self): """ @@ -200,13 +187,12 @@ class ForeignObjectRel(FieldCacheMixin): opts = model._meta if model else self.related_model._meta model = model or self.related_model if self.multiple: - # If this is a symmetrical m2m relation on self, there is no - # reverse accessor. + # If this is a symmetrical m2m relation on self, there is no reverse accessor. if self.symmetrical and model == self.model: return None if self.related_name: return self.related_name - return opts.model_name + ("_set" if self.multiple else "") + return opts.model_name + ('_set' if self.multiple else '') def get_path_info(self, filtered_relation=None): return self.field.get_reverse_path_info(filtered_relation) @@ -234,20 +220,10 @@ class ManyToOneRel(ForeignObjectRel): reverse relations into actual fields. """ - def __init__( - self, - field, - to, - field_name, - related_name=None, - related_query_name=None, - limit_choices_to=None, - parent_link=False, - on_delete=None, - ): + def __init__(self, field, to, field_name, related_name=None, related_query_name=None, + limit_choices_to=None, parent_link=False, on_delete=None): super().__init__( - field, - to, + field, to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, @@ -259,7 +235,7 @@ class ManyToOneRel(ForeignObjectRel): def __getstate__(self): state = self.__dict__.copy() - state.pop("related_model", None) + state.pop('related_model', None) return state @property @@ -272,9 +248,7 @@ class ManyToOneRel(ForeignObjectRel): """ field = self.model._meta.get_field(self.field_name) if not field.concrete: - raise exceptions.FieldDoesNotExist( - "No related field named '%s'" % self.field_name - ) + raise exceptions.FieldDoesNotExist("No related field named '%s'" % self.field_name) return field def set_field_name(self): @@ -289,21 +263,10 @@ class OneToOneRel(ManyToOneRel): flags for the reverse relation. """ - def __init__( - self, - field, - to, - field_name, - related_name=None, - related_query_name=None, - limit_choices_to=None, - parent_link=False, - on_delete=None, - ): + def __init__(self, field, to, field_name, related_name=None, related_query_name=None, + limit_choices_to=None, parent_link=False, on_delete=None): super().__init__( - field, - to, - field_name, + field, to, field_name, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, @@ -322,21 +285,11 @@ class ManyToManyRel(ForeignObjectRel): flags for the reverse relation. """ - def __init__( - self, - field, - to, - related_name=None, - related_query_name=None, - limit_choices_to=None, - symmetrical=True, - through=None, - through_fields=None, - db_constraint=True, - ): + def __init__(self, field, to, related_name=None, related_query_name=None, + limit_choices_to=None, symmetrical=True, through=None, + through_fields=None, db_constraint=True): super().__init__( - field, - to, + field, to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, @@ -357,7 +310,7 @@ class ManyToManyRel(ForeignObjectRel): def identity(self): return super().identity + ( self.through, - make_hashable(self.through_fields), + self.through_fields, self.db_constraint, ) @@ -371,7 +324,7 @@ class ManyToManyRel(ForeignObjectRel): field = opts.get_field(self.through_fields[0]) else: for field in opts.fields: - rel = getattr(field, "remote_field", None) + rel = getattr(field, 'remote_field', None) if rel and rel.model == self.model: break return field.foreign_related_fields[0] diff --git a/venv/Lib/site-packages/django/db/models/functions/__init__.py b/venv/Lib/site-packages/django/db/models/functions/__init__.py index cd7c801..d687af1 100644 --- a/venv/Lib/site-packages/django/db/models/functions/__init__.py +++ b/venv/Lib/site-packages/django/db/models/functions/__init__.py @@ -1,190 +1,46 @@ -from .comparison import Cast, Coalesce, Collate, Greatest, JSONObject, Least, NullIf +from .comparison import ( + Cast, Coalesce, Collate, Greatest, JSONObject, Least, NullIf, +) from .datetime import ( - Extract, - ExtractDay, - ExtractHour, - ExtractIsoWeekDay, - ExtractIsoYear, - ExtractMinute, - ExtractMonth, - ExtractQuarter, - ExtractSecond, - ExtractWeek, - ExtractWeekDay, - ExtractYear, - Now, - Trunc, - TruncDate, - TruncDay, - TruncHour, - TruncMinute, - TruncMonth, - TruncQuarter, - TruncSecond, - TruncTime, - TruncWeek, + Extract, ExtractDay, ExtractHour, ExtractIsoWeekDay, ExtractIsoYear, + ExtractMinute, ExtractMonth, ExtractQuarter, ExtractSecond, ExtractWeek, + ExtractWeekDay, ExtractYear, Now, Trunc, TruncDate, TruncDay, TruncHour, + TruncMinute, TruncMonth, TruncQuarter, TruncSecond, TruncTime, TruncWeek, TruncYear, ) from .math import ( - Abs, - ACos, - ASin, - ATan, - ATan2, - Ceil, - Cos, - Cot, - Degrees, - Exp, - Floor, - Ln, - Log, - Mod, - Pi, - Power, - Radians, - Random, - Round, - Sign, - Sin, - Sqrt, - Tan, + Abs, ACos, ASin, ATan, ATan2, Ceil, Cos, Cot, Degrees, Exp, Floor, Ln, Log, + Mod, Pi, Power, Radians, Random, Round, Sign, Sin, Sqrt, Tan, ) from .text import ( - MD5, - SHA1, - SHA224, - SHA256, - SHA384, - SHA512, - Chr, - Concat, - ConcatPair, - Left, - Length, - Lower, - LPad, - LTrim, - Ord, - Repeat, - Replace, - Reverse, - Right, - RPad, - RTrim, - StrIndex, - Substr, - Trim, - Upper, + MD5, SHA1, SHA224, SHA256, SHA384, SHA512, Chr, Concat, ConcatPair, Left, + Length, Lower, LPad, LTrim, Ord, Repeat, Replace, Reverse, Right, RPad, + RTrim, StrIndex, Substr, Trim, Upper, ) from .window import ( - CumeDist, - DenseRank, - FirstValue, - Lag, - LastValue, - Lead, - NthValue, - Ntile, - PercentRank, - Rank, - RowNumber, + CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile, + PercentRank, Rank, RowNumber, ) __all__ = [ # comparison and conversion - "Cast", - "Coalesce", - "Collate", - "Greatest", - "JSONObject", - "Least", - "NullIf", + 'Cast', 'Coalesce', 'Collate', 'Greatest', 'JSONObject', 'Least', 'NullIf', # datetime - "Extract", - "ExtractDay", - "ExtractHour", - "ExtractMinute", - "ExtractMonth", - "ExtractQuarter", - "ExtractSecond", - "ExtractWeek", - "ExtractIsoWeekDay", - "ExtractWeekDay", - "ExtractIsoYear", - "ExtractYear", - "Now", - "Trunc", - "TruncDate", - "TruncDay", - "TruncHour", - "TruncMinute", - "TruncMonth", - "TruncQuarter", - "TruncSecond", - "TruncTime", - "TruncWeek", - "TruncYear", + 'Extract', 'ExtractDay', 'ExtractHour', 'ExtractMinute', 'ExtractMonth', + 'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractIsoWeekDay', + 'ExtractWeekDay', 'ExtractIsoYear', 'ExtractYear', 'Now', 'Trunc', + 'TruncDate', 'TruncDay', 'TruncHour', 'TruncMinute', 'TruncMonth', + 'TruncQuarter', 'TruncSecond', 'TruncTime', 'TruncWeek', 'TruncYear', # math - "Abs", - "ACos", - "ASin", - "ATan", - "ATan2", - "Ceil", - "Cos", - "Cot", - "Degrees", - "Exp", - "Floor", - "Ln", - "Log", - "Mod", - "Pi", - "Power", - "Radians", - "Random", - "Round", - "Sign", - "Sin", - "Sqrt", - "Tan", + 'Abs', 'ACos', 'ASin', 'ATan', 'ATan2', 'Ceil', 'Cos', 'Cot', 'Degrees', + 'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Random', + 'Round', 'Sign', 'Sin', 'Sqrt', 'Tan', # text - "MD5", - "SHA1", - "SHA224", - "SHA256", - "SHA384", - "SHA512", - "Chr", - "Concat", - "ConcatPair", - "Left", - "Length", - "Lower", - "LPad", - "LTrim", - "Ord", - "Repeat", - "Replace", - "Reverse", - "Right", - "RPad", - "RTrim", - "StrIndex", - "Substr", - "Trim", - "Upper", + 'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'Chr', 'Concat', + 'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', 'LTrim', 'Ord', 'Repeat', + 'Replace', 'Reverse', 'Right', 'RPad', 'RTrim', 'StrIndex', 'Substr', + 'Trim', 'Upper', # window - "CumeDist", - "DenseRank", - "FirstValue", - "Lag", - "LastValue", - "Lead", - "NthValue", - "Ntile", - "PercentRank", - "Rank", - "RowNumber", + 'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead', + 'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber', ] diff --git a/venv/Lib/site-packages/django/db/models/functions/comparison.py b/venv/Lib/site-packages/django/db/models/functions/comparison.py index eb1f20a..2976985 100644 --- a/venv/Lib/site-packages/django/db/models/functions/comparison.py +++ b/venv/Lib/site-packages/django/db/models/functions/comparison.py @@ -7,115 +7,84 @@ from django.utils.regex_helper import _lazy_re_compile class Cast(Func): """Coerce an expression to a new field type.""" - - function = "CAST" - template = "%(function)s(%(expressions)s AS %(db_type)s)" + function = 'CAST' + template = '%(function)s(%(expressions)s AS %(db_type)s)' def __init__(self, expression, output_field): super().__init__(expression, output_field=output_field) def as_sql(self, compiler, connection, **extra_context): - extra_context["db_type"] = self.output_field.cast_db_type(connection) + extra_context['db_type'] = self.output_field.cast_db_type(connection) return super().as_sql(compiler, connection, **extra_context) def as_sqlite(self, compiler, connection, **extra_context): db_type = self.output_field.db_type(connection) - if db_type in {"datetime", "time"}: + if db_type in {'datetime', 'time'}: # Use strftime as datetime/time don't keep fractional seconds. - template = "strftime(%%s, %(expressions)s)" - sql, params = super().as_sql( - compiler, connection, template=template, **extra_context - ) - format_string = "%H:%M:%f" if db_type == "time" else "%Y-%m-%d %H:%M:%f" + template = 'strftime(%%s, %(expressions)s)' + sql, params = super().as_sql(compiler, connection, template=template, **extra_context) + format_string = '%H:%M:%f' if db_type == 'time' else '%Y-%m-%d %H:%M:%f' params.insert(0, format_string) return sql, params - elif db_type == "date": - template = "date(%(expressions)s)" - return super().as_sql( - compiler, connection, template=template, **extra_context - ) + elif db_type == 'date': + template = 'date(%(expressions)s)' + return super().as_sql(compiler, connection, template=template, **extra_context) return self.as_sql(compiler, connection, **extra_context) def as_mysql(self, compiler, connection, **extra_context): template = None output_type = self.output_field.get_internal_type() # MySQL doesn't support explicit cast to float. - if output_type == "FloatField": - template = "(%(expressions)s + 0.0)" + if output_type == 'FloatField': + template = '(%(expressions)s + 0.0)' # MariaDB doesn't support explicit cast to JSON. - elif output_type == "JSONField" and connection.mysql_is_mariadb: + elif output_type == 'JSONField' and connection.mysql_is_mariadb: template = "JSON_EXTRACT(%(expressions)s, '$')" return self.as_sql(compiler, connection, template=template, **extra_context) - def as_postgresql(self, compiler, connection, **extra_context): - # CAST would be valid too, but the :: shortcut syntax is more readable. - # 'expressions' is wrapped in parentheses in case it's a complex - # expression. - return self.as_sql( - compiler, - connection, - template="(%(expressions)s)::%(db_type)s", - **extra_context, - ) - def as_oracle(self, compiler, connection, **extra_context): - if self.output_field.get_internal_type() == "JSONField": + if self.output_field.get_internal_type() == 'JSONField': # Oracle doesn't support explicit cast to JSON. template = "JSON_QUERY(%(expressions)s, '$')" - return super().as_sql( - compiler, connection, template=template, **extra_context - ) + return super().as_sql(compiler, connection, template=template, **extra_context) return self.as_sql(compiler, connection, **extra_context) class Coalesce(Func): """Return, from left to right, the first non-null expression.""" - - function = "COALESCE" + function = 'COALESCE' def __init__(self, *expressions, **extra): if len(expressions) < 2: - raise ValueError("Coalesce must take at least two expressions") + raise ValueError('Coalesce must take at least two expressions') super().__init__(*expressions, **extra) - @property - def empty_result_set_value(self): - for expression in self.get_source_expressions(): - result = expression.empty_result_set_value - if result is NotImplemented or result is not None: - return result - return None - def as_oracle(self, compiler, connection, **extra_context): # Oracle prohibits mixing TextField (NCLOB) and CharField (NVARCHAR2), # so convert all fields to NCLOB when that type is expected. - if self.output_field.get_internal_type() == "TextField": + if self.output_field.get_internal_type() == 'TextField': clone = self.copy() - clone.set_source_expressions( - [ - Func(expression, function="TO_NCLOB") - for expression in self.get_source_expressions() - ] - ) + clone.set_source_expressions([ + Func(expression, function='TO_NCLOB') for expression in self.get_source_expressions() + ]) return super(Coalesce, clone).as_sql(compiler, connection, **extra_context) return self.as_sql(compiler, connection, **extra_context) class Collate(Func): - function = "COLLATE" - template = "%(expressions)s %(function)s %(collation)s" - # Inspired from - # https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS - collation_re = _lazy_re_compile(r"^[\w\-]+$") + function = 'COLLATE' + template = '%(expressions)s %(function)s %(collation)s' + # Inspired from https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + collation_re = _lazy_re_compile(r'^[\w\-]+$') def __init__(self, expression, collation): if not (collation and self.collation_re.match(collation)): - raise ValueError("Invalid collation name: %r." % collation) + raise ValueError('Invalid collation name: %r.' % collation) self.collation = collation super().__init__(expression) def as_sql(self, compiler, connection, **extra_context): - extra_context.setdefault("collation", connection.ops.quote_name(self.collation)) + extra_context.setdefault('collation', connection.ops.quote_name(self.collation)) return super().as_sql(compiler, connection, **extra_context) @@ -127,21 +96,20 @@ class Greatest(Func): On PostgreSQL, the maximum not-null expression is returned. On MySQL, Oracle, and SQLite, if any expression is null, null is returned. """ - - function = "GREATEST" + function = 'GREATEST' def __init__(self, *expressions, **extra): if len(expressions) < 2: - raise ValueError("Greatest must take at least two expressions") + raise ValueError('Greatest must take at least two expressions') super().__init__(*expressions, **extra) def as_sqlite(self, compiler, connection, **extra_context): """Use the MAX function on SQLite.""" - return super().as_sqlite(compiler, connection, function="MAX", **extra_context) + return super().as_sqlite(compiler, connection, function='MAX', **extra_context) class JSONObject(Func): - function = "JSON_OBJECT" + function = 'JSON_OBJECT' output_field = JSONField() def __init__(self, **fields): @@ -153,7 +121,7 @@ class JSONObject(Func): def as_sql(self, compiler, connection, **extra_context): if not connection.features.has_json_object_function: raise NotSupportedError( - "JSONObject() is not supported on this database backend." + 'JSONObject() is not supported on this database backend.' ) return super().as_sql(compiler, connection, **extra_context) @@ -161,21 +129,21 @@ class JSONObject(Func): return self.as_sql( compiler, connection, - function="JSONB_BUILD_OBJECT", + function='JSONB_BUILD_OBJECT', **extra_context, ) def as_oracle(self, compiler, connection, **extra_context): class ArgJoiner: def join(self, args): - args = [" VALUE ".join(arg) for arg in zip(args[::2], args[1::2])] - return ", ".join(args) + args = [' VALUE '.join(arg) for arg in zip(args[::2], args[1::2])] + return ', '.join(args) return self.as_sql( compiler, connection, arg_joiner=ArgJoiner(), - template="%(function)s(%(expressions)s RETURNING CLOB)", + template='%(function)s(%(expressions)s RETURNING CLOB)', **extra_context, ) @@ -188,25 +156,24 @@ class Least(Func): On PostgreSQL, return the minimum not-null expression. On MySQL, Oracle, and SQLite, if any expression is null, return null. """ - - function = "LEAST" + function = 'LEAST' def __init__(self, *expressions, **extra): if len(expressions) < 2: - raise ValueError("Least must take at least two expressions") + raise ValueError('Least must take at least two expressions') super().__init__(*expressions, **extra) def as_sqlite(self, compiler, connection, **extra_context): """Use the MIN function on SQLite.""" - return super().as_sqlite(compiler, connection, function="MIN", **extra_context) + return super().as_sqlite(compiler, connection, function='MIN', **extra_context) class NullIf(Func): - function = "NULLIF" + function = 'NULLIF' arity = 2 def as_oracle(self, compiler, connection, **extra_context): expression1 = self.get_source_expressions()[0] if isinstance(expression1, Value) and expression1.value is None: - raise ValueError("Oracle does not allow Value(None) for expression1.") + raise ValueError('Oracle does not allow Value(None) for expression1.') return super().as_sql(compiler, connection, **extra_context) diff --git a/venv/Lib/site-packages/django/db/models/functions/datetime.py b/venv/Lib/site-packages/django/db/models/functions/datetime.py index 2d6ec70..90e6f41 100644 --- a/venv/Lib/site-packages/django/db/models/functions/datetime.py +++ b/venv/Lib/site-packages/django/db/models/functions/datetime.py @@ -3,20 +3,10 @@ from datetime import datetime from django.conf import settings from django.db.models.expressions import Func from django.db.models.fields import ( - DateField, - DateTimeField, - DurationField, - Field, - IntegerField, - TimeField, + DateField, DateTimeField, DurationField, Field, IntegerField, TimeField, ) from django.db.models.lookups import ( - Transform, - YearExact, - YearGt, - YearGte, - YearLt, - YearLte, + Transform, YearExact, YearGt, YearGte, YearLt, YearLte, ) from django.utils import timezone @@ -46,7 +36,7 @@ class Extract(TimezoneMixin, Transform): if self.lookup_name is None: self.lookup_name = lookup_name if self.lookup_name is None: - raise ValueError("lookup_name must be provided") + raise ValueError('lookup_name must be provided') self.tzinfo = tzinfo super().__init__(expression, **extra) @@ -57,16 +47,14 @@ class Extract(TimezoneMixin, Transform): tzname = self.get_tzname() sql = connection.ops.datetime_extract_sql(self.lookup_name, sql, tzname) elif self.tzinfo is not None: - raise ValueError("tzinfo can only be used with DateTimeField.") + raise ValueError('tzinfo can only be used with DateTimeField.') elif isinstance(lhs_output_field, DateField): sql = connection.ops.date_extract_sql(self.lookup_name, sql) elif isinstance(lhs_output_field, TimeField): sql = connection.ops.time_extract_sql(self.lookup_name, sql) elif isinstance(lhs_output_field, DurationField): if not connection.features.has_native_duration_field: - raise ValueError( - "Extract requires native DurationField database support." - ) + raise ValueError('Extract requires native DurationField database support.') sql = connection.ops.time_extract_sql(self.lookup_name, sql) else: # resolve_expression has already validated the output_field so this @@ -74,38 +62,22 @@ class Extract(TimezoneMixin, Transform): assert False, "Tried to Extract from an invalid type." return sql, params - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): - copy = super().resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) - field = getattr(copy.lhs, "output_field", None) - if field is None: - return copy + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): + copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) + field = copy.lhs.output_field if not isinstance(field, (DateField, DateTimeField, TimeField, DurationField)): raise ValueError( - "Extract input expression must be DateField, DateTimeField, " - "TimeField, or DurationField." + 'Extract input expression must be DateField, DateTimeField, ' + 'TimeField, or DurationField.' ) # Passing dates to functions expecting datetimes is most likely a mistake. - if type(field) == DateField and copy.lookup_name in ( - "hour", - "minute", - "second", - ): + if type(field) == DateField and copy.lookup_name in ('hour', 'minute', 'second'): raise ValueError( - "Cannot extract time component '%s' from DateField '%s'." - % (copy.lookup_name, field.name) + "Cannot extract time component '%s' from DateField '%s'. " % (copy.lookup_name, field.name) ) - if isinstance(field, DurationField) and copy.lookup_name in ( - "year", - "iso_year", - "month", - "week", - "week_day", - "iso_week_day", - "quarter", + if ( + isinstance(field, DurationField) and + copy.lookup_name in ('year', 'iso_year', 'month', 'week', 'week_day', 'iso_week_day', 'quarter') ): raise ValueError( "Cannot extract component '%s' from DurationField '%s'." @@ -115,21 +87,20 @@ class Extract(TimezoneMixin, Transform): class ExtractYear(Extract): - lookup_name = "year" + lookup_name = 'year' class ExtractIsoYear(Extract): """Return the ISO-8601 week-numbering year.""" - - lookup_name = "iso_year" + lookup_name = 'iso_year' class ExtractMonth(Extract): - lookup_name = "month" + lookup_name = 'month' class ExtractDay(Extract): - lookup_name = "day" + lookup_name = 'day' class ExtractWeek(Extract): @@ -137,8 +108,7 @@ class ExtractWeek(Extract): Return 1-52 or 53, based on ISO-8601, i.e., Monday is the first of the week. """ - - lookup_name = "week" + lookup_name = 'week' class ExtractWeekDay(Extract): @@ -147,30 +117,28 @@ class ExtractWeekDay(Extract): To replicate this in Python: (mydatetime.isoweekday() % 7) + 1 """ - - lookup_name = "week_day" + lookup_name = 'week_day' class ExtractIsoWeekDay(Extract): """Return Monday=1 through Sunday=7, based on ISO-8601.""" - - lookup_name = "iso_week_day" + lookup_name = 'iso_week_day' class ExtractQuarter(Extract): - lookup_name = "quarter" + lookup_name = 'quarter' class ExtractHour(Extract): - lookup_name = "hour" + lookup_name = 'hour' class ExtractMinute(Extract): - lookup_name = "minute" + lookup_name = 'minute' class ExtractSecond(Extract): - lookup_name = "second" + lookup_name = 'second' DateField.register_lookup(ExtractYear) @@ -204,32 +172,21 @@ ExtractIsoYear.register_lookup(YearLte) class Now(Func): - template = "CURRENT_TIMESTAMP" + template = 'CURRENT_TIMESTAMP' output_field = DateTimeField() def as_postgresql(self, compiler, connection, **extra_context): # PostgreSQL's CURRENT_TIMESTAMP means "the time at the start of the # transaction". Use STATEMENT_TIMESTAMP to be cross-compatible with # other databases. - return self.as_sql( - compiler, connection, template="STATEMENT_TIMESTAMP()", **extra_context - ) + return self.as_sql(compiler, connection, template='STATEMENT_TIMESTAMP()', **extra_context) class TruncBase(TimezoneMixin, Transform): kind = None tzinfo = None - # RemovedInDjango50Warning: when the deprecation ends, remove is_dst - # argument. - def __init__( - self, - expression, - output_field=None, - tzinfo=None, - is_dst=timezone.NOT_PASSED, - **extra, - ): + def __init__(self, expression, output_field=None, tzinfo=None, is_dst=None, **extra): self.tzinfo = tzinfo self.is_dst = is_dst super().__init__(expression, output_field=output_field, **extra) @@ -240,7 +197,7 @@ class TruncBase(TimezoneMixin, Transform): if isinstance(self.lhs.output_field, DateTimeField): tzname = self.get_tzname() elif self.tzinfo is not None: - raise ValueError("tzinfo can only be used with DateTimeField.") + raise ValueError('tzinfo can only be used with DateTimeField.') if isinstance(self.output_field, DateTimeField): sql = connection.ops.datetime_trunc_sql(self.kind, inner_sql, tzname) elif isinstance(self.output_field, DateField): @@ -248,66 +205,36 @@ class TruncBase(TimezoneMixin, Transform): elif isinstance(self.output_field, TimeField): sql = connection.ops.time_trunc_sql(self.kind, inner_sql, tzname) else: - raise ValueError( - "Trunc only valid on DateField, TimeField, or DateTimeField." - ) + raise ValueError('Trunc only valid on DateField, TimeField, or DateTimeField.') return sql, inner_params - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): - copy = super().resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): + copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) field = copy.lhs.output_field # DateTimeField is a subclass of DateField so this works for both. - if not isinstance(field, (DateField, TimeField)): - raise TypeError( - "%r isn't a DateField, TimeField, or DateTimeField." % field.name - ) + assert isinstance(field, (DateField, TimeField)), ( + "%r isn't a DateField, TimeField, or DateTimeField." % field.name + ) # If self.output_field was None, then accessing the field will trigger # the resolver to assign it to self.lhs.output_field. if not isinstance(copy.output_field, (DateField, DateTimeField, TimeField)): - raise ValueError( - "output_field must be either DateField, TimeField, or DateTimeField" - ) + raise ValueError('output_field must be either DateField, TimeField, or DateTimeField') # Passing dates or times to functions expecting datetimes is most # likely a mistake. - class_output_field = ( - self.__class__.output_field - if isinstance(self.__class__.output_field, Field) - else None - ) + class_output_field = self.__class__.output_field if isinstance(self.__class__.output_field, Field) else None output_field = class_output_field or copy.output_field - has_explicit_output_field = ( - class_output_field or field.__class__ is not copy.output_field.__class__ - ) + has_explicit_output_field = class_output_field or field.__class__ is not copy.output_field.__class__ if type(field) == DateField and ( - isinstance(output_field, DateTimeField) - or copy.kind in ("hour", "minute", "second", "time") - ): - raise ValueError( - "Cannot truncate DateField '%s' to %s." - % ( - field.name, - output_field.__class__.__name__ - if has_explicit_output_field - else "DateTimeField", - ) - ) + isinstance(output_field, DateTimeField) or copy.kind in ('hour', 'minute', 'second', 'time')): + raise ValueError("Cannot truncate DateField '%s' to %s. " % ( + field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' + )) elif isinstance(field, TimeField) and ( - isinstance(output_field, DateTimeField) - or copy.kind in ("year", "quarter", "month", "week", "day", "date") - ): - raise ValueError( - "Cannot truncate TimeField '%s' to %s." - % ( - field.name, - output_field.__class__.__name__ - if has_explicit_output_field - else "DateTimeField", - ) - ) + isinstance(output_field, DateTimeField) or + copy.kind in ('year', 'quarter', 'month', 'week', 'day', 'date')): + raise ValueError("Cannot truncate TimeField '%s' to %s. " % ( + field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' + )) return copy def convert_value(self, value, expression, connection): @@ -319,8 +246,8 @@ class TruncBase(TimezoneMixin, Transform): value = timezone.make_aware(value, self.tzinfo, is_dst=self.is_dst) elif not connection.features.has_zoneinfo_database: raise ValueError( - "Database returned an invalid datetime value. Are time " - "zone definitions for your database installed?" + 'Database returned an invalid datetime value. Are time ' + 'zone definitions for your database installed?' ) elif isinstance(value, datetime): if value is None: @@ -334,48 +261,38 @@ class TruncBase(TimezoneMixin, Transform): class Trunc(TruncBase): - # RemovedInDjango50Warning: when the deprecation ends, remove is_dst - # argument. - def __init__( - self, - expression, - kind, - output_field=None, - tzinfo=None, - is_dst=timezone.NOT_PASSED, - **extra, - ): + def __init__(self, expression, kind, output_field=None, tzinfo=None, is_dst=None, **extra): self.kind = kind super().__init__( - expression, output_field=output_field, tzinfo=tzinfo, is_dst=is_dst, **extra + expression, output_field=output_field, tzinfo=tzinfo, + is_dst=is_dst, **extra ) class TruncYear(TruncBase): - kind = "year" + kind = 'year' class TruncQuarter(TruncBase): - kind = "quarter" + kind = 'quarter' class TruncMonth(TruncBase): - kind = "month" + kind = 'month' class TruncWeek(TruncBase): """Truncate to midnight on the Monday of the week.""" - - kind = "week" + kind = 'week' class TruncDay(TruncBase): - kind = "day" + kind = 'day' class TruncDate(TruncBase): - kind = "date" - lookup_name = "date" + kind = 'date' + lookup_name = 'date' output_field = DateField() def as_sql(self, compiler, connection): @@ -387,8 +304,8 @@ class TruncDate(TruncBase): class TruncTime(TruncBase): - kind = "time" - lookup_name = "time" + kind = 'time' + lookup_name = 'time' output_field = TimeField() def as_sql(self, compiler, connection): @@ -400,15 +317,15 @@ class TruncTime(TruncBase): class TruncHour(TruncBase): - kind = "hour" + kind = 'hour' class TruncMinute(TruncBase): - kind = "minute" + kind = 'minute' class TruncSecond(TruncBase): - kind = "second" + kind = 'second' DateTimeField.register_lookup(TruncDate) diff --git a/venv/Lib/site-packages/django/db/models/functions/math.py b/venv/Lib/site-packages/django/db/models/functions/math.py index 8b5fd79..15915f4 100644 --- a/venv/Lib/site-packages/django/db/models/functions/math.py +++ b/venv/Lib/site-packages/django/db/models/functions/math.py @@ -1,43 +1,40 @@ import math -from django.db.models.expressions import Func, Value +from django.db.models.expressions import Func from django.db.models.fields import FloatField, IntegerField from django.db.models.functions import Cast from django.db.models.functions.mixins import ( - FixDecimalInputMixin, - NumericOutputFieldMixin, + FixDecimalInputMixin, NumericOutputFieldMixin, ) from django.db.models.lookups import Transform class Abs(Transform): - function = "ABS" - lookup_name = "abs" + function = 'ABS' + lookup_name = 'abs' class ACos(NumericOutputFieldMixin, Transform): - function = "ACOS" - lookup_name = "acos" + function = 'ACOS' + lookup_name = 'acos' class ASin(NumericOutputFieldMixin, Transform): - function = "ASIN" - lookup_name = "asin" + function = 'ASIN' + lookup_name = 'asin' class ATan(NumericOutputFieldMixin, Transform): - function = "ATAN" - lookup_name = "atan" + function = 'ATAN' + lookup_name = 'atan' class ATan2(NumericOutputFieldMixin, Func): - function = "ATAN2" + function = 'ATAN2' arity = 2 def as_sqlite(self, compiler, connection, **extra_context): - if not getattr( - connection.ops, "spatialite", False - ) or connection.ops.spatial_version >= (5, 0, 0): + if not getattr(connection.ops, 'spatialite', False) or connection.ops.spatial_version >= (5, 0, 0): return self.as_sql(compiler, connection) # This function is usually ATan2(y, x), returning the inverse tangent # of y / x, but it's ATan2(x, y) on SpatiaLite < 5.0.0. @@ -45,74 +42,67 @@ class ATan2(NumericOutputFieldMixin, Func): # arguments are mixed between integer and float or decimal. # https://www.gaia-gis.it/fossil/libspatialite/tktview?name=0f72cca3a2 clone = self.copy() - clone.set_source_expressions( - [ - Cast(expression, FloatField()) - if isinstance(expression.output_field, IntegerField) - else expression - for expression in self.get_source_expressions()[::-1] - ] - ) + clone.set_source_expressions([ + Cast(expression, FloatField()) if isinstance(expression.output_field, IntegerField) + else expression for expression in self.get_source_expressions()[::-1] + ]) return clone.as_sql(compiler, connection, **extra_context) class Ceil(Transform): - function = "CEILING" - lookup_name = "ceil" + function = 'CEILING' + lookup_name = 'ceil' def as_oracle(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="CEIL", **extra_context) + return super().as_sql(compiler, connection, function='CEIL', **extra_context) class Cos(NumericOutputFieldMixin, Transform): - function = "COS" - lookup_name = "cos" + function = 'COS' + lookup_name = 'cos' class Cot(NumericOutputFieldMixin, Transform): - function = "COT" - lookup_name = "cot" + function = 'COT' + lookup_name = 'cot' def as_oracle(self, compiler, connection, **extra_context): - return super().as_sql( - compiler, connection, template="(1 / TAN(%(expressions)s))", **extra_context - ) + return super().as_sql(compiler, connection, template='(1 / TAN(%(expressions)s))', **extra_context) class Degrees(NumericOutputFieldMixin, Transform): - function = "DEGREES" - lookup_name = "degrees" + function = 'DEGREES' + lookup_name = 'degrees' def as_oracle(self, compiler, connection, **extra_context): return super().as_sql( - compiler, - connection, - template="((%%(expressions)s) * 180 / %s)" % math.pi, - **extra_context, + compiler, connection, + template='((%%(expressions)s) * 180 / %s)' % math.pi, + **extra_context ) class Exp(NumericOutputFieldMixin, Transform): - function = "EXP" - lookup_name = "exp" + function = 'EXP' + lookup_name = 'exp' class Floor(Transform): - function = "FLOOR" - lookup_name = "floor" + function = 'FLOOR' + lookup_name = 'floor' class Ln(NumericOutputFieldMixin, Transform): - function = "LN" - lookup_name = "ln" + function = 'LN' + lookup_name = 'ln' class Log(FixDecimalInputMixin, NumericOutputFieldMixin, Func): - function = "LOG" + function = 'LOG' arity = 2 def as_sqlite(self, compiler, connection, **extra_context): - if not getattr(connection.ops, "spatialite", False): + if not getattr(connection.ops, 'spatialite', False): return self.as_sql(compiler, connection) # This function is usually Log(b, x) returning the logarithm of x to # the base b, but on SpatiaLite it's Log(x, b). @@ -122,91 +112,72 @@ class Log(FixDecimalInputMixin, NumericOutputFieldMixin, Func): class Mod(FixDecimalInputMixin, NumericOutputFieldMixin, Func): - function = "MOD" + function = 'MOD' arity = 2 class Pi(NumericOutputFieldMixin, Func): - function = "PI" + function = 'PI' arity = 0 def as_oracle(self, compiler, connection, **extra_context): - return super().as_sql( - compiler, connection, template=str(math.pi), **extra_context - ) + return super().as_sql(compiler, connection, template=str(math.pi), **extra_context) class Power(NumericOutputFieldMixin, Func): - function = "POWER" + function = 'POWER' arity = 2 class Radians(NumericOutputFieldMixin, Transform): - function = "RADIANS" - lookup_name = "radians" + function = 'RADIANS' + lookup_name = 'radians' def as_oracle(self, compiler, connection, **extra_context): return super().as_sql( - compiler, - connection, - template="((%%(expressions)s) * %s / 180)" % math.pi, - **extra_context, + compiler, connection, + template='((%%(expressions)s) * %s / 180)' % math.pi, + **extra_context ) class Random(NumericOutputFieldMixin, Func): - function = "RANDOM" + function = 'RANDOM' arity = 0 def as_mysql(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="RAND", **extra_context) + return super().as_sql(compiler, connection, function='RAND', **extra_context) def as_oracle(self, compiler, connection, **extra_context): - return super().as_sql( - compiler, connection, function="DBMS_RANDOM.VALUE", **extra_context - ) + return super().as_sql(compiler, connection, function='DBMS_RANDOM.VALUE', **extra_context) def as_sqlite(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="RAND", **extra_context) + return super().as_sql(compiler, connection, function='RAND', **extra_context) def get_group_by_cols(self, alias=None): return [] -class Round(FixDecimalInputMixin, Transform): - function = "ROUND" - lookup_name = "round" - arity = None # Override Transform's arity=1 to enable passing precision. - - def __init__(self, expression, precision=0, **extra): - super().__init__(expression, precision, **extra) - - def as_sqlite(self, compiler, connection, **extra_context): - precision = self.get_source_expressions()[1] - if isinstance(precision, Value) and precision.value < 0: - raise ValueError("SQLite does not support negative precision.") - return super().as_sqlite(compiler, connection, **extra_context) - - def _resolve_output_field(self): - source = self.get_source_expressions()[0] - return source.output_field +class Round(Transform): + function = 'ROUND' + lookup_name = 'round' class Sign(Transform): - function = "SIGN" - lookup_name = "sign" + function = 'SIGN' + lookup_name = 'sign' class Sin(NumericOutputFieldMixin, Transform): - function = "SIN" - lookup_name = "sin" + function = 'SIN' + lookup_name = 'sin' class Sqrt(NumericOutputFieldMixin, Transform): - function = "SQRT" - lookup_name = "sqrt" + function = 'SQRT' + lookup_name = 'sqrt' class Tan(NumericOutputFieldMixin, Transform): - function = "TAN" - lookup_name = "tan" + function = 'TAN' + lookup_name = 'tan' diff --git a/venv/Lib/site-packages/django/db/models/functions/mixins.py b/venv/Lib/site-packages/django/db/models/functions/mixins.py index caf20e1..00cfd1b 100644 --- a/venv/Lib/site-packages/django/db/models/functions/mixins.py +++ b/venv/Lib/site-packages/django/db/models/functions/mixins.py @@ -5,6 +5,7 @@ from django.db.models.functions import Cast class FixDecimalInputMixin: + def as_postgresql(self, compiler, connection, **extra_context): # Cast FloatField to DecimalField as PostgreSQL doesn't support the # following function signatures: @@ -12,42 +13,36 @@ class FixDecimalInputMixin: # - MOD(double, double) output_field = DecimalField(decimal_places=sys.float_info.dig, max_digits=1000) clone = self.copy() - clone.set_source_expressions( - [ - Cast(expression, output_field) - if isinstance(expression.output_field, FloatField) - else expression - for expression in self.get_source_expressions() - ] - ) + clone.set_source_expressions([ + Cast(expression, output_field) if isinstance(expression.output_field, FloatField) + else expression for expression in self.get_source_expressions() + ]) return clone.as_sql(compiler, connection, **extra_context) class FixDurationInputMixin: + def as_mysql(self, compiler, connection, **extra_context): sql, params = super().as_sql(compiler, connection, **extra_context) - if self.output_field.get_internal_type() == "DurationField": - sql = "CAST(%s AS SIGNED)" % sql + if self.output_field.get_internal_type() == 'DurationField': + sql = 'CAST(%s AS SIGNED)' % sql return sql, params def as_oracle(self, compiler, connection, **extra_context): - if self.output_field.get_internal_type() == "DurationField": + if self.output_field.get_internal_type() == 'DurationField': expression = self.get_source_expressions()[0] options = self._get_repr_options() from django.db.backends.oracle.functions import ( - IntervalToSeconds, - SecondsToInterval, + IntervalToSeconds, SecondsToInterval, ) - return compiler.compile( - SecondsToInterval( - self.__class__(IntervalToSeconds(expression), **options) - ) + SecondsToInterval(self.__class__(IntervalToSeconds(expression), **options)) ) return super().as_sql(compiler, connection, **extra_context) class NumericOutputFieldMixin: + def _resolve_output_field(self): source_fields = self.get_source_fields() if any(isinstance(s, DecimalField) for s in source_fields): diff --git a/venv/Lib/site-packages/django/db/models/functions/text.py b/venv/Lib/site-packages/django/db/models/functions/text.py index a54ce8f..4c52222 100644 --- a/venv/Lib/site-packages/django/db/models/functions/text.py +++ b/venv/Lib/site-packages/django/db/models/functions/text.py @@ -10,7 +10,7 @@ class MySQLSHA2Mixin: return super().as_sql( compiler, connection, - template="SHA2(%%(expressions)s, %s)" % self.function[3:], + template='SHA2(%%(expressions)s, %s)' % self.function[3:], **extra_content, ) @@ -40,28 +40,25 @@ class PostgreSQLSHAMixin: class Chr(Transform): - function = "CHR" - lookup_name = "chr" + function = 'CHR' + lookup_name = 'chr' def as_mysql(self, compiler, connection, **extra_context): return super().as_sql( - compiler, - connection, - function="CHAR", - template="%(function)s(%(expressions)s USING utf16)", - **extra_context, + compiler, connection, function='CHAR', + template='%(function)s(%(expressions)s USING utf16)', + **extra_context ) def as_oracle(self, compiler, connection, **extra_context): return super().as_sql( - compiler, - connection, - template="%(function)s(%(expressions)s USING NCHAR_CS)", - **extra_context, + compiler, connection, + template='%(function)s(%(expressions)s USING NCHAR_CS)', + **extra_context ) def as_sqlite(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="CHAR", **extra_context) + return super().as_sql(compiler, connection, function='CHAR', **extra_context) class ConcatPair(Func): @@ -69,38 +66,29 @@ class ConcatPair(Func): Concatenate two arguments together. This is used by `Concat` because not all backend databases support more than two arguments. """ - - function = "CONCAT" + function = 'CONCAT' def as_sqlite(self, compiler, connection, **extra_context): coalesced = self.coalesce() return super(ConcatPair, coalesced).as_sql( - compiler, - connection, - template="%(expressions)s", - arg_joiner=" || ", - **extra_context, + compiler, connection, template='%(expressions)s', arg_joiner=' || ', + **extra_context ) def as_mysql(self, compiler, connection, **extra_context): # Use CONCAT_WS with an empty separator so that NULLs are ignored. return super().as_sql( - compiler, - connection, - function="CONCAT_WS", + compiler, connection, function='CONCAT_WS', template="%(function)s('', %(expressions)s)", - **extra_context, + **extra_context ) def coalesce(self): # null on either side results in null for expression, wrap with coalesce c = self.copy() - c.set_source_expressions( - [ - Coalesce(expression, Value("")) - for expression in c.get_source_expressions() - ] - ) + c.set_source_expressions([ + Coalesce(expression, Value('')) for expression in c.get_source_expressions() + ]) return c @@ -110,13 +98,12 @@ class Concat(Func): null expression when any arguments are null will wrap each argument in coalesce functions to ensure a non-null result. """ - function = None template = "%(expressions)s" def __init__(self, *expressions, **extra): if len(expressions) < 2: - raise ValueError("Concat must take at least two expressions") + raise ValueError('Concat must take at least two expressions') paired = self._paired(expressions) super().__init__(paired, **extra) @@ -130,7 +117,7 @@ class Concat(Func): class Left(Func): - function = "LEFT" + function = 'LEFT' arity = 2 output_field = CharField() @@ -139,7 +126,7 @@ class Left(Func): expression: the name of a field, or an expression returning a string length: the number of characters to return from the start of the string """ - if not hasattr(length, "resolve_expression"): + if not hasattr(length, 'resolve_expression'): if length < 1: raise ValueError("'length' must be greater than 0.") super().__init__(expression, length, **extra) @@ -156,68 +143,57 @@ class Left(Func): class Length(Transform): """Return the number of characters in the expression.""" - - function = "LENGTH" - lookup_name = "length" + function = 'LENGTH' + lookup_name = 'length' output_field = IntegerField() def as_mysql(self, compiler, connection, **extra_context): - return super().as_sql( - compiler, connection, function="CHAR_LENGTH", **extra_context - ) + return super().as_sql(compiler, connection, function='CHAR_LENGTH', **extra_context) class Lower(Transform): - function = "LOWER" - lookup_name = "lower" + function = 'LOWER' + lookup_name = 'lower' class LPad(Func): - function = "LPAD" + function = 'LPAD' output_field = CharField() - def __init__(self, expression, length, fill_text=Value(" "), **extra): - if ( - not hasattr(length, "resolve_expression") - and length is not None - and length < 0 - ): + def __init__(self, expression, length, fill_text=Value(' '), **extra): + if not hasattr(length, 'resolve_expression') and length is not None and length < 0: raise ValueError("'length' must be greater or equal to 0.") super().__init__(expression, length, fill_text, **extra) class LTrim(Transform): - function = "LTRIM" - lookup_name = "ltrim" + function = 'LTRIM' + lookup_name = 'ltrim' class MD5(OracleHashMixin, Transform): - function = "MD5" - lookup_name = "md5" + function = 'MD5' + lookup_name = 'md5' class Ord(Transform): - function = "ASCII" - lookup_name = "ord" + function = 'ASCII' + lookup_name = 'ord' output_field = IntegerField() def as_mysql(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="ORD", **extra_context) + return super().as_sql(compiler, connection, function='ORD', **extra_context) def as_sqlite(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="UNICODE", **extra_context) + return super().as_sql(compiler, connection, function='UNICODE', **extra_context) class Repeat(Func): - function = "REPEAT" + function = 'REPEAT' output_field = CharField() def __init__(self, expression, number, **extra): - if ( - not hasattr(number, "resolve_expression") - and number is not None - and number < 0 - ): + if not hasattr(number, 'resolve_expression') and number is not None and number < 0: raise ValueError("'number' must be greater or equal to 0.") super().__init__(expression, number, **extra) @@ -229,76 +205,73 @@ class Repeat(Func): class Replace(Func): - function = "REPLACE" + function = 'REPLACE' - def __init__(self, expression, text, replacement=Value(""), **extra): + def __init__(self, expression, text, replacement=Value(''), **extra): super().__init__(expression, text, replacement, **extra) class Reverse(Transform): - function = "REVERSE" - lookup_name = "reverse" + function = 'REVERSE' + lookup_name = 'reverse' def as_oracle(self, compiler, connection, **extra_context): # REVERSE in Oracle is undocumented and doesn't support multi-byte # strings. Use a special subquery instead. return super().as_sql( - compiler, - connection, + compiler, connection, template=( - "(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM " - "(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s " - "FROM DUAL CONNECT BY LEVEL <= LENGTH(%(expressions)s)) " - "GROUP BY %(expressions)s)" + '(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM ' + '(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s ' + 'FROM DUAL CONNECT BY LEVEL <= LENGTH(%(expressions)s)) ' + 'GROUP BY %(expressions)s)' ), - **extra_context, + **extra_context ) class Right(Left): - function = "RIGHT" + function = 'RIGHT' def get_substr(self): - return Substr( - self.source_expressions[0], self.source_expressions[1] * Value(-1) - ) + return Substr(self.source_expressions[0], self.source_expressions[1] * Value(-1)) class RPad(LPad): - function = "RPAD" + function = 'RPAD' class RTrim(Transform): - function = "RTRIM" - lookup_name = "rtrim" + function = 'RTRIM' + lookup_name = 'rtrim' class SHA1(OracleHashMixin, PostgreSQLSHAMixin, Transform): - function = "SHA1" - lookup_name = "sha1" + function = 'SHA1' + lookup_name = 'sha1' class SHA224(MySQLSHA2Mixin, PostgreSQLSHAMixin, Transform): - function = "SHA224" - lookup_name = "sha224" + function = 'SHA224' + lookup_name = 'sha224' def as_oracle(self, compiler, connection, **extra_context): - raise NotSupportedError("SHA224 is not supported on Oracle.") + raise NotSupportedError('SHA224 is not supported on Oracle.') class SHA256(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): - function = "SHA256" - lookup_name = "sha256" + function = 'SHA256' + lookup_name = 'sha256' class SHA384(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): - function = "SHA384" - lookup_name = "sha384" + function = 'SHA384' + lookup_name = 'sha384' class SHA512(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): - function = "SHA512" - lookup_name = "sha512" + function = 'SHA512' + lookup_name = 'sha512' class StrIndex(Func): @@ -307,17 +280,16 @@ class StrIndex(Func): first occurrence of a substring inside another string, or 0 if the substring is not found. """ - - function = "INSTR" + function = 'INSTR' arity = 2 output_field = IntegerField() def as_postgresql(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="STRPOS", **extra_context) + return super().as_sql(compiler, connection, function='STRPOS', **extra_context) class Substr(Func): - function = "SUBSTRING" + function = 'SUBSTRING' output_field = CharField() def __init__(self, expression, pos, length=None, **extra): @@ -326,7 +298,7 @@ class Substr(Func): pos: an integer > 0, or an expression returning an integer length: an optional number of characters to return """ - if not hasattr(pos, "resolve_expression"): + if not hasattr(pos, 'resolve_expression'): if pos < 1: raise ValueError("'pos' must be greater than 0") expressions = [expression, pos] @@ -335,17 +307,17 @@ class Substr(Func): super().__init__(*expressions, **extra) def as_sqlite(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="SUBSTR", **extra_context) + return super().as_sql(compiler, connection, function='SUBSTR', **extra_context) def as_oracle(self, compiler, connection, **extra_context): - return super().as_sql(compiler, connection, function="SUBSTR", **extra_context) + return super().as_sql(compiler, connection, function='SUBSTR', **extra_context) class Trim(Transform): - function = "TRIM" - lookup_name = "trim" + function = 'TRIM' + lookup_name = 'trim' class Upper(Transform): - function = "UPPER" - lookup_name = "upper" + function = 'UPPER' + lookup_name = 'upper' diff --git a/venv/Lib/site-packages/django/db/models/functions/window.py b/venv/Lib/site-packages/django/db/models/functions/window.py index 671017a..84b2b24 100644 --- a/venv/Lib/site-packages/django/db/models/functions/window.py +++ b/venv/Lib/site-packages/django/db/models/functions/window.py @@ -2,35 +2,26 @@ from django.db.models.expressions import Func from django.db.models.fields import FloatField, IntegerField __all__ = [ - "CumeDist", - "DenseRank", - "FirstValue", - "Lag", - "LastValue", - "Lead", - "NthValue", - "Ntile", - "PercentRank", - "Rank", - "RowNumber", + 'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead', + 'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber', ] class CumeDist(Func): - function = "CUME_DIST" + function = 'CUME_DIST' output_field = FloatField() window_compatible = True class DenseRank(Func): - function = "DENSE_RANK" + function = 'DENSE_RANK' output_field = IntegerField() window_compatible = True class FirstValue(Func): arity = 1 - function = "FIRST_VALUE" + function = 'FIRST_VALUE' window_compatible = True @@ -40,12 +31,13 @@ class LagLeadFunction(Func): def __init__(self, expression, offset=1, default=None, **extra): if expression is None: raise ValueError( - "%s requires a non-null source expression." % self.__class__.__name__ + '%s requires a non-null source expression.' % + self.__class__.__name__ ) if offset is None or offset <= 0: raise ValueError( - "%s requires a positive integer for the offset." - % self.__class__.__name__ + '%s requires a positive integer for the offset.' % + self.__class__.__name__ ) args = (expression, offset) if default is not None: @@ -58,32 +50,28 @@ class LagLeadFunction(Func): class Lag(LagLeadFunction): - function = "LAG" + function = 'LAG' class LastValue(Func): arity = 1 - function = "LAST_VALUE" + function = 'LAST_VALUE' window_compatible = True class Lead(LagLeadFunction): - function = "LEAD" + function = 'LEAD' class NthValue(Func): - function = "NTH_VALUE" + function = 'NTH_VALUE' window_compatible = True def __init__(self, expression, nth=1, **extra): if expression is None: - raise ValueError( - "%s requires a non-null source expression." % self.__class__.__name__ - ) + raise ValueError('%s requires a non-null source expression.' % self.__class__.__name__) if nth is None or nth <= 0: - raise ValueError( - "%s requires a positive integer as for nth." % self.__class__.__name__ - ) + raise ValueError('%s requires a positive integer as for nth.' % self.__class__.__name__) super().__init__(expression, nth, **extra) def _resolve_output_field(self): @@ -92,29 +80,29 @@ class NthValue(Func): class Ntile(Func): - function = "NTILE" + function = 'NTILE' output_field = IntegerField() window_compatible = True def __init__(self, num_buckets=1, **extra): if num_buckets <= 0: - raise ValueError("num_buckets must be greater than 0.") + raise ValueError('num_buckets must be greater than 0.') super().__init__(num_buckets, **extra) class PercentRank(Func): - function = "PERCENT_RANK" + function = 'PERCENT_RANK' output_field = FloatField() window_compatible = True class Rank(Func): - function = "RANK" + function = 'RANK' output_field = IntegerField() window_compatible = True class RowNumber(Func): - function = "ROW_NUMBER" + function = 'ROW_NUMBER' output_field = IntegerField() window_compatible = True diff --git a/venv/Lib/site-packages/django/db/models/indexes.py b/venv/Lib/site-packages/django/db/models/indexes.py index 3c2b699..5530d0b 100644 --- a/venv/Lib/site-packages/django/db/models/indexes.py +++ b/venv/Lib/site-packages/django/db/models/indexes.py @@ -5,11 +5,11 @@ from django.db.models.query_utils import Q from django.db.models.sql import Query from django.utils.functional import partition -__all__ = ["Index"] +__all__ = ['Index'] class Index: - suffix = "idx" + suffix = 'idx' # The max length of the name of the index (restricted to 30 for # cross-database compatibility with Oracle) max_name_length = 30 @@ -25,48 +25,46 @@ class Index: include=None, ): if opclasses and not name: - raise ValueError("An index must be named to use opclasses.") + raise ValueError('An index must be named to use opclasses.') if not isinstance(condition, (type(None), Q)): - raise ValueError("Index.condition must be a Q instance.") + raise ValueError('Index.condition must be a Q instance.') if condition and not name: - raise ValueError("An index must be named to use condition.") + raise ValueError('An index must be named to use condition.') if not isinstance(fields, (list, tuple)): - raise ValueError("Index.fields must be a list or tuple.") + raise ValueError('Index.fields must be a list or tuple.') if not isinstance(opclasses, (list, tuple)): - raise ValueError("Index.opclasses must be a list or tuple.") + raise ValueError('Index.opclasses must be a list or tuple.') if not expressions and not fields: raise ValueError( - "At least one field or expression is required to define an index." + 'At least one field or expression is required to define an ' + 'index.' ) if expressions and fields: raise ValueError( - "Index.fields and expressions are mutually exclusive.", + 'Index.fields and expressions are mutually exclusive.', ) if expressions and not name: - raise ValueError("An index must be named to use expressions.") + raise ValueError('An index must be named to use expressions.') if expressions and opclasses: raise ValueError( - "Index.opclasses cannot be used with expressions. Use " - "django.contrib.postgres.indexes.OpClass() instead." + 'Index.opclasses cannot be used with expressions. Use ' + 'django.contrib.postgres.indexes.OpClass() instead.' ) if opclasses and len(fields) != len(opclasses): - raise ValueError( - "Index.fields and Index.opclasses must have the same number of " - "elements." - ) + raise ValueError('Index.fields and Index.opclasses must have the same number of elements.') if fields and not all(isinstance(field, str) for field in fields): - raise ValueError("Index.fields must contain only strings with field names.") + raise ValueError('Index.fields must contain only strings with field names.') if include and not name: - raise ValueError("A covering index must be named.") + raise ValueError('A covering index must be named.') if not isinstance(include, (type(None), list, tuple)): - raise ValueError("Index.include must be a list or tuple.") + raise ValueError('Index.include must be a list or tuple.') self.fields = list(fields) # A list of 2-tuple with the field name and ordering ('' or 'DESC'). self.fields_orders = [ - (field_name[1:], "DESC") if field_name.startswith("-") else (field_name, "") + (field_name[1:], 'DESC') if field_name.startswith('-') else (field_name, '') for field_name in self.fields ] - self.name = name or "" + self.name = name or '' self.db_tablespace = db_tablespace self.opclasses = opclasses self.condition = condition @@ -89,10 +87,8 @@ class Index: sql, params = where.as_sql(compiler, schema_editor.connection) return sql % tuple(schema_editor.quote_value(p) for p in params) - def create_sql(self, model, schema_editor, using="", **kwargs): - include = [ - model._meta.get_field(field_name).column for field_name in self.include - ] + def create_sql(self, model, schema_editor, using='', **kwargs): + include = [model._meta.get_field(field_name).column for field_name in self.include] condition = self._get_condition_sql(model, schema_editor) if self.expressions: index_expressions = [] @@ -113,36 +109,29 @@ class Index: col_suffixes = [order[1] for order in self.fields_orders] expressions = None return schema_editor._create_index_sql( - model, - fields=fields, - name=self.name, - using=using, - db_tablespace=self.db_tablespace, - col_suffixes=col_suffixes, - opclasses=self.opclasses, - condition=condition, - include=include, - expressions=expressions, - **kwargs, + model, fields=fields, name=self.name, using=using, + db_tablespace=self.db_tablespace, col_suffixes=col_suffixes, + opclasses=self.opclasses, condition=condition, include=include, + expressions=expressions, **kwargs, ) def remove_sql(self, model, schema_editor, **kwargs): return schema_editor._delete_index_sql(model, self.name, **kwargs) def deconstruct(self): - path = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) - path = path.replace("django.db.models.indexes", "django.db.models") - kwargs = {"name": self.name} + path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) + path = path.replace('django.db.models.indexes', 'django.db.models') + kwargs = {'name': self.name} if self.fields: - kwargs["fields"] = self.fields + kwargs['fields'] = self.fields if self.db_tablespace is not None: - kwargs["db_tablespace"] = self.db_tablespace + kwargs['db_tablespace'] = self.db_tablespace if self.opclasses: - kwargs["opclasses"] = self.opclasses + kwargs['opclasses'] = self.opclasses if self.condition: - kwargs["condition"] = self.condition + kwargs['condition'] = self.condition if self.include: - kwargs["include"] = self.include + kwargs['include'] = self.include return (path, self.expressions, kwargs) def clone(self): @@ -159,44 +148,36 @@ class Index: fit its size by truncating the excess length. """ _, table_name = split_identifier(model._meta.db_table) - column_names = [ - model._meta.get_field(field_name).column - for field_name, order in self.fields_orders - ] + column_names = [model._meta.get_field(field_name).column for field_name, order in self.fields_orders] column_names_with_order = [ - (("-%s" if order else "%s") % column_name) - for column_name, (field_name, order) in zip( - column_names, self.fields_orders - ) + (('-%s' if order else '%s') % column_name) + for column_name, (field_name, order) in zip(column_names, self.fields_orders) ] # The length of the parts of the name is based on the default max # length of 30 characters. hash_data = [table_name] + column_names_with_order + [self.suffix] - self.name = "%s_%s_%s" % ( + self.name = '%s_%s_%s' % ( table_name[:11], column_names[0][:7], - "%s_%s" % (names_digest(*hash_data, length=6), self.suffix), + '%s_%s' % (names_digest(*hash_data, length=6), self.suffix), ) - if len(self.name) > self.max_name_length: - raise ValueError( - "Index too long for multiple database support. Is self.suffix " - "longer than 3 characters?" - ) - if self.name[0] == "_" or self.name[0].isdigit(): - self.name = "D%s" % self.name[1:] + assert len(self.name) <= self.max_name_length, ( + 'Index too long for multiple database support. Is self.suffix ' + 'longer than 3 characters?' + ) + if self.name[0] == '_' or self.name[0].isdigit(): + self.name = 'D%s' % self.name[1:] def __repr__(self): - return "<%s:%s%s%s%s%s%s%s>" % ( - self.__class__.__qualname__, - "" if not self.fields else " fields=%s" % repr(self.fields), - "" if not self.expressions else " expressions=%s" % repr(self.expressions), - "" if not self.name else " name=%s" % repr(self.name), - "" - if self.db_tablespace is None - else " db_tablespace=%s" % repr(self.db_tablespace), - "" if self.condition is None else " condition=%s" % self.condition, - "" if not self.include else " include=%s" % repr(self.include), - "" if not self.opclasses else " opclasses=%s" % repr(self.opclasses), + return '<%s:%s%s%s%s%s>' % ( + self.__class__.__name__, + '' if not self.fields else " fields='%s'" % ', '.join(self.fields), + '' if not self.expressions else " expressions='%s'" % ', '.join([ + str(expression) for expression in self.expressions + ]), + '' if self.condition is None else ' condition=%s' % self.condition, + '' if not self.include else " include='%s'" % ', '.join(self.include), + '' if not self.opclasses else " opclasses='%s'" % ', '.join(self.opclasses), ) def __eq__(self, other): @@ -207,20 +188,17 @@ class Index: class IndexExpression(Func): """Order and wrap expressions for CREATE INDEX statements.""" - - template = "%(expressions)s" + template = '%(expressions)s' wrapper_classes = (OrderBy, Collate) def set_wrapper_classes(self, connection=None): # Some databases (e.g. MySQL) treats COLLATE as an indexed expression. if connection and connection.features.collate_as_index_expression: - self.wrapper_classes = tuple( - [ - wrapper_cls - for wrapper_cls in self.wrapper_classes - if wrapper_cls is not Collate - ] - ) + self.wrapper_classes = tuple([ + wrapper_cls + for wrapper_cls in self.wrapper_classes + if wrapper_cls is not Collate + ]) @classmethod def register_wrappers(cls, *wrapper_classes): @@ -244,17 +222,16 @@ class IndexExpression(Func): if len(wrapper_types) != len(set(wrapper_types)): raise ValueError( "Multiple references to %s can't be used in an indexed " - "expression." - % ", ".join( - [wrapper_cls.__qualname__ for wrapper_cls in self.wrapper_classes] - ) + "expression." % ', '.join([ + wrapper_cls.__qualname__ for wrapper_cls in self.wrapper_classes + ]) ) - if expressions[1 : len(wrappers) + 1] != wrappers: + if expressions[1:len(wrappers) + 1] != wrappers: raise ValueError( - "%s must be topmost expressions in an indexed expression." - % ", ".join( - [wrapper_cls.__qualname__ for wrapper_cls in self.wrapper_classes] - ) + '%s must be topmost expressions in an indexed expression.' + % ', '.join([ + wrapper_cls.__qualname__ for wrapper_cls in self.wrapper_classes + ]) ) # Wrap expressions in parentheses if they are not column references. root_expression = index_expressions[1] @@ -266,7 +243,7 @@ class IndexExpression(Func): for_save, ) if not isinstance(resolve_root_expression, Col): - root_expression = Func(root_expression, template="(%(expressions)s)") + root_expression = Func(root_expression, template='(%(expressions)s)') if wrappers: # Order wrappers and set their expressions. @@ -283,9 +260,7 @@ class IndexExpression(Func): else: # Use the root expression, if there are no wrappers. self.set_source_expressions([root_expression]) - return super().resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) + return super().resolve_expression(query, allow_joins, reuse, summarize, for_save) def as_sqlite(self, compiler, connection, **extra_context): # Casting to numeric is unnecessary. diff --git a/venv/Lib/site-packages/django/db/models/lookups.py b/venv/Lib/site-packages/django/db/models/lookups.py index d1b00bd..43f40c2 100644 --- a/venv/Lib/site-packages/django/db/models/lookups.py +++ b/venv/Lib/site-packages/django/db/models/lookups.py @@ -1,23 +1,21 @@ import itertools import math +import warnings +from copy import copy from django.core.exceptions import EmptyResultSet -from django.db.models.expressions import Case, Expression, Func, Value, When +from django.db.models.expressions import Case, Exists, Func, Value, When from django.db.models.fields import ( - BooleanField, - CharField, - DateTimeField, - Field, - IntegerField, - UUIDField, + CharField, DateTimeField, Field, IntegerField, UUIDField, ) from django.db.models.query_utils import RegisterLookupMixin from django.utils.datastructures import OrderedSet +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import cached_property from django.utils.hashable import make_hashable -class Lookup(Expression): +class Lookup: lookup_name = None prepare_rhs = True can_use_none_as_rhs = False @@ -25,20 +23,18 @@ class Lookup(Expression): def __init__(self, lhs, rhs): self.lhs, self.rhs = lhs, rhs self.rhs = self.get_prep_lookup() - self.lhs = self.get_prep_lhs() - if hasattr(self.lhs, "get_bilateral_transforms"): + if hasattr(self.lhs, 'get_bilateral_transforms'): bilateral_transforms = self.lhs.get_bilateral_transforms() else: bilateral_transforms = [] if bilateral_transforms: # Warn the user as soon as possible if they are trying to apply # a bilateral transformation on a nested QuerySet: that won't work. - from django.db.models.sql.query import Query # avoid circular import - + from django.db.models.sql.query import ( # avoid circular import + Query, + ) if isinstance(rhs, Query): - raise NotImplementedError( - "Bilateral transformations on nested querysets are not implemented." - ) + raise NotImplementedError("Bilateral transformations on nested querysets are not implemented.") self.bilateral_transforms = bilateral_transforms def apply_bilateral_transforms(self, value): @@ -46,9 +42,6 @@ class Lookup(Expression): value = transform(value) return value - def __repr__(self): - return f"{self.__class__.__name__}({self.lhs!r}, {self.rhs!r})" - def batch_process_rhs(self, compiler, connection, rhs=None): if rhs is None: rhs = self.rhs @@ -63,7 +56,7 @@ class Lookup(Expression): sqls_params.extend(sql_params) else: _, params = self.get_db_prep_lookup(rhs, connection) - sqls, sqls_params = ["%s"] * len(params), params + sqls, sqls_params = ['%s'] * len(params), params return sqls, sqls_params def get_source_expressions(self): @@ -78,32 +71,20 @@ class Lookup(Expression): self.lhs, self.rhs = new_exprs def get_prep_lookup(self): - if not self.prepare_rhs or hasattr(self.rhs, "resolve_expression"): + if hasattr(self.rhs, 'resolve_expression'): return self.rhs - if hasattr(self.lhs, "output_field"): - if hasattr(self.lhs.output_field, "get_prep_value"): - return self.lhs.output_field.get_prep_value(self.rhs) - elif self.rhs_is_direct_value(): - return Value(self.rhs) + if self.prepare_rhs and hasattr(self.lhs.output_field, 'get_prep_value'): + return self.lhs.output_field.get_prep_value(self.rhs) return self.rhs - def get_prep_lhs(self): - if hasattr(self.lhs, "resolve_expression"): - return self.lhs - return Value(self.lhs) - def get_db_prep_lookup(self, value, connection): - return ("%s", [value]) + return ('%s', [value]) def process_lhs(self, compiler, connection, lhs=None): lhs = lhs or self.lhs - if hasattr(lhs, "resolve_expression"): + if hasattr(lhs, 'resolve_expression'): lhs = lhs.resolve_expression(compiler.query) - sql, params = compiler.compile(lhs) - if isinstance(lhs, Lookup): - # Wrapped in parentheses to respect operator precedence. - sql = f"({sql})" - return sql, params + return compiler.compile(lhs) def process_rhs(self, compiler, connection): value = self.rhs @@ -114,33 +95,37 @@ class Lookup(Expression): value = Value(value, output_field=self.lhs.output_field) value = self.apply_bilateral_transforms(value) value = value.resolve_expression(compiler.query) - if hasattr(value, "as_sql"): - sql, params = compiler.compile(value) - # Ensure expression is wrapped in parentheses to respect operator - # precedence but avoid double wrapping as it can be misinterpreted - # on some backends (e.g. subqueries on SQLite). - if sql and sql[0] != "(": - sql = "(%s)" % sql - return sql, params + if hasattr(value, 'as_sql'): + return compiler.compile(value) else: return self.get_db_prep_lookup(value, connection) def rhs_is_direct_value(self): - return not hasattr(self.rhs, "as_sql") + return not hasattr(self.rhs, 'as_sql') + + def relabeled_clone(self, relabels): + new = copy(self) + new.lhs = new.lhs.relabeled_clone(relabels) + if hasattr(new.rhs, 'relabeled_clone'): + new.rhs = new.rhs.relabeled_clone(relabels) + return new def get_group_by_cols(self, alias=None): - cols = [] - for source in self.get_source_expressions(): - cols.extend(source.get_group_by_cols()) + cols = self.lhs.get_group_by_cols() + if hasattr(self.rhs, 'get_group_by_cols'): + cols.extend(self.rhs.get_group_by_cols()) return cols + def as_sql(self, compiler, connection): + raise NotImplementedError + def as_oracle(self, compiler, connection): - # Oracle doesn't allow EXISTS() and filters to be compared to another - # expression unless they're wrapped in a CASE WHEN. + # Oracle doesn't allow EXISTS() to be compared to another expression + # unless it's wrapped in a CASE WHEN. wrapped = False exprs = [] for expr in (self.lhs, self.rhs): - if connection.ops.conditional_expression_supported_in_where_clause(expr): + if isinstance(expr, Exists): expr = Case(When(expr, then=True), default=False) wrapped = True exprs.append(expr) @@ -148,8 +133,16 @@ class Lookup(Expression): return lookup.as_sql(compiler, connection) @cached_property - def output_field(self): - return BooleanField() + def contains_aggregate(self): + return self.lhs.contains_aggregate or getattr(self.rhs, 'contains_aggregate', False) + + @cached_property + def contains_over_clause(self): + return self.lhs.contains_over_clause or getattr(self.rhs, 'contains_over_clause', False) + + @property + def is_summary(self): + return self.lhs.is_summary or getattr(self.rhs, 'is_summary', False) @property def identity(self): @@ -163,34 +156,12 @@ class Lookup(Expression): def __hash__(self): return hash(make_hashable(self.identity)) - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): - c = self.copy() - c.is_summary = summarize - c.lhs = self.lhs.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) - c.rhs = self.rhs.resolve_expression( - query, allow_joins, reuse, summarize, for_save - ) - return c - - def select_format(self, compiler, sql, params): - # Wrap filters with a CASE WHEN expression if a database backend - # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP - # BY list. - if not compiler.connection.features.supports_boolean_expr_in_select_clause: - sql = f"CASE WHEN {sql} THEN 1 ELSE 0 END" - return sql, params - class Transform(RegisterLookupMixin, Func): """ RegisterLookupMixin() is first so that get_lookup() and get_transform() first examine self and then check output_field. """ - bilateral = False arity = 1 @@ -199,7 +170,7 @@ class Transform(RegisterLookupMixin, Func): return self.get_source_expressions()[0] def get_bilateral_transforms(self): - if hasattr(self.lhs, "get_bilateral_transforms"): + if hasattr(self.lhs, 'get_bilateral_transforms'): bilateral_transforms = self.lhs.get_bilateral_transforms() else: bilateral_transforms = [] @@ -213,10 +184,9 @@ class BuiltinLookup(Lookup): lhs_sql, params = super().process_lhs(compiler, connection, lhs) field_internal_type = self.lhs.output_field.get_internal_type() db_type = self.lhs.output_field.db_type(connection=connection) - lhs_sql = connection.ops.field_cast_sql(db_type, field_internal_type) % lhs_sql - lhs_sql = ( - connection.ops.lookup_cast(self.lookup_name, field_internal_type) % lhs_sql - ) + lhs_sql = connection.ops.field_cast_sql( + db_type, field_internal_type) % lhs_sql + lhs_sql = connection.ops.lookup_cast(self.lookup_name, field_internal_type) % lhs_sql return lhs_sql, list(params) def as_sql(self, compiler, connection): @@ -224,7 +194,7 @@ class BuiltinLookup(Lookup): rhs_sql, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) rhs_sql = self.get_rhs_op(connection, rhs_sql) - return "%s %s" % (lhs_sql, rhs_sql), params + return '%s %s' % (lhs_sql, rhs_sql), params def get_rhs_op(self, connection, rhs): return connection.operators[self.lookup_name] % rhs @@ -235,22 +205,18 @@ class FieldGetDbPrepValueMixin: Some lookups require Field.get_db_prep_value() to be called on their inputs. """ - get_db_prep_lookup_value_is_iterable = False def get_db_prep_lookup(self, value, connection): # For relational fields, use the 'target_field' attribute of the # output_field. - field = getattr(self.lhs.output_field, "target_field", None) - get_db_prep_value = ( - getattr(field, "get_db_prep_value", None) - or self.lhs.output_field.get_db_prep_value - ) + field = getattr(self.lhs.output_field, 'target_field', None) + get_db_prep_value = getattr(field, 'get_db_prep_value', None) or self.lhs.output_field.get_db_prep_value return ( - "%s", + '%s', [get_db_prep_value(v, connection, prepared=True) for v in value] - if self.get_db_prep_lookup_value_is_iterable - else [get_db_prep_value(value, connection, prepared=True)], + if self.get_db_prep_lookup_value_is_iterable else + [get_db_prep_value(value, connection, prepared=True)] ) @@ -259,19 +225,18 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin): Some lookups require Field.get_db_prep_value() to be called on each value in an iterable. """ - get_db_prep_lookup_value_is_iterable = True def get_prep_lookup(self): - if hasattr(self.rhs, "resolve_expression"): + if hasattr(self.rhs, 'resolve_expression'): return self.rhs prepared_values = [] for rhs_value in self.rhs: - if hasattr(rhs_value, "resolve_expression"): + if hasattr(rhs_value, 'resolve_expression'): # An expression will be handled by the database but can coexist # alongside real values. pass - elif self.prepare_rhs and hasattr(self.lhs.output_field, "get_prep_value"): + elif self.prepare_rhs and hasattr(self.lhs.output_field, 'get_prep_value'): rhs_value = self.lhs.output_field.get_prep_value(rhs_value) prepared_values.append(rhs_value) return prepared_values @@ -286,9 +251,9 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin): def resolve_expression_parameter(self, compiler, connection, sql, param): params = [param] - if hasattr(param, "resolve_expression"): + if hasattr(param, 'resolve_expression'): param = param.resolve_expression(compiler.query) - if hasattr(param, "as_sql"): + if hasattr(param, 'as_sql'): sql, params = compiler.compile(param) return sql, params @@ -298,44 +263,40 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin): # sql/param pair. Zip them to get sql and param pairs that refer to the # same argument and attempt to replace them with the result of # compiling the param step. - sql, params = zip( - *( - self.resolve_expression_parameter(compiler, connection, sql, param) - for sql, param in zip(*pre_processed) - ) - ) + sql, params = zip(*( + self.resolve_expression_parameter(compiler, connection, sql, param) + for sql, param in zip(*pre_processed) + )) params = itertools.chain.from_iterable(params) return sql, tuple(params) class PostgresOperatorLookup(FieldGetDbPrepValueMixin, Lookup): """Lookup defined by operators on PostgreSQL.""" - postgres_operator = None def as_postgresql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params = tuple(lhs_params) + tuple(rhs_params) - return "%s %s %s" % (lhs, self.postgres_operator, rhs), params + return '%s %s %s' % (lhs, self.postgres_operator, rhs), params @Field.register_lookup class Exact(FieldGetDbPrepValueMixin, BuiltinLookup): - lookup_name = "exact" + lookup_name = 'exact' def process_rhs(self, compiler, connection): from django.db.models.sql.query import Query - if isinstance(self.rhs, Query): if self.rhs.has_limit_one(): if not self.rhs.has_select_fields: self.rhs.clear_select_clause() - self.rhs.add_fields(["pk"]) + self.rhs.add_fields(['pk']) else: raise ValueError( - "The QuerySet value for an exact lookup must be limited to " - "one result using slicing." + 'The QuerySet value for an exact lookup must be limited to ' + 'one result using slicing.' ) return super().process_rhs(compiler, connection) @@ -344,21 +305,19 @@ class Exact(FieldGetDbPrepValueMixin, BuiltinLookup): # turns "boolfield__exact=True" into "WHERE boolean_field" instead of # "WHERE boolean_field = True" when allowed. if ( - isinstance(self.rhs, bool) - and getattr(self.lhs, "conditional", False) - and connection.ops.conditional_expression_supported_in_where_clause( - self.lhs - ) + isinstance(self.rhs, bool) and + getattr(self.lhs, 'conditional', False) and + connection.ops.conditional_expression_supported_in_where_clause(self.lhs) ): lhs_sql, params = self.process_lhs(compiler, connection) - template = "%s" if self.rhs else "NOT %s" + template = '%s' if self.rhs else 'NOT %s' return template % lhs_sql, params return super().as_sql(compiler, connection) @Field.register_lookup class IExact(BuiltinLookup): - lookup_name = "iexact" + lookup_name = 'iexact' prepare_rhs = False def process_rhs(self, qn, connection): @@ -370,22 +329,22 @@ class IExact(BuiltinLookup): @Field.register_lookup class GreaterThan(FieldGetDbPrepValueMixin, BuiltinLookup): - lookup_name = "gt" + lookup_name = 'gt' @Field.register_lookup class GreaterThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup): - lookup_name = "gte" + lookup_name = 'gte' @Field.register_lookup class LessThan(FieldGetDbPrepValueMixin, BuiltinLookup): - lookup_name = "lt" + lookup_name = 'lt' @Field.register_lookup class LessThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup): - lookup_name = "lte" + lookup_name = 'lte' class IntegerFieldFloatRounding: @@ -393,7 +352,6 @@ class IntegerFieldFloatRounding: Allow floats to work as query values for IntegerField. Without this, the decimal portion of the float would always be discarded. """ - def get_prep_lookup(self): if isinstance(self.rhs, float): self.rhs = math.ceil(self.rhs) @@ -412,10 +370,10 @@ class IntegerLessThan(IntegerFieldFloatRounding, LessThan): @Field.register_lookup class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup): - lookup_name = "in" + lookup_name = 'in' def process_rhs(self, compiler, connection): - db_rhs = getattr(self.rhs, "_db", None) + db_rhs = getattr(self.rhs, '_db', None) if db_rhs is not None and db_rhs != connection.alias: raise ValueError( "Subqueries aren't allowed across different databases. Force " @@ -436,39 +394,20 @@ class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup): # rhs should be an iterable; use batch_process_rhs() to # prepare/transform those values. sqls, sqls_params = self.batch_process_rhs(compiler, connection, rhs) - placeholder = "(" + ", ".join(sqls) + ")" + placeholder = '(' + ', '.join(sqls) + ')' return (placeholder, sqls_params) else: - from django.db.models.sql.query import Query # avoid circular import - - if isinstance(self.rhs, Query): - query = self.rhs - query.clear_ordering(clear_default=True) - if not query.has_select_fields: - query.clear_select_clause() - query.add_fields(["pk"]) - + if not getattr(self.rhs, 'has_select_fields', True): + self.rhs.clear_select_clause() + self.rhs.add_fields(['pk']) return super().process_rhs(compiler, connection) - def get_group_by_cols(self, alias=None): - cols = self.lhs.get_group_by_cols() - if hasattr(self.rhs, "get_group_by_cols"): - if not getattr(self.rhs, "has_select_fields", True): - self.rhs.clear_select_clause() - self.rhs.add_fields(["pk"]) - cols.extend(self.rhs.get_group_by_cols()) - return cols - def get_rhs_op(self, connection, rhs): - return "IN %s" % rhs + return 'IN %s' % rhs def as_sql(self, compiler, connection): max_in_list_size = connection.ops.max_in_list_size() - if ( - self.rhs_is_direct_value() - and max_in_list_size - and len(self.rhs) > max_in_list_size - ): + if self.rhs_is_direct_value() and max_in_list_size and len(self.rhs) > max_in_list_size: return self.split_parameter_list_as_sql(compiler, connection) return super().as_sql(compiler, connection) @@ -478,25 +417,25 @@ class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup): max_in_list_size = connection.ops.max_in_list_size() lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.batch_process_rhs(compiler, connection) - in_clause_elements = ["("] + in_clause_elements = ['('] params = [] for offset in range(0, len(rhs_params), max_in_list_size): if offset > 0: - in_clause_elements.append(" OR ") - in_clause_elements.append("%s IN (" % lhs) + in_clause_elements.append(' OR ') + in_clause_elements.append('%s IN (' % lhs) params.extend(lhs_params) - sqls = rhs[offset : offset + max_in_list_size] - sqls_params = rhs_params[offset : offset + max_in_list_size] - param_group = ", ".join(sqls) + sqls = rhs[offset: offset + max_in_list_size] + sqls_params = rhs_params[offset: offset + max_in_list_size] + param_group = ', '.join(sqls) in_clause_elements.append(param_group) - in_clause_elements.append(")") + in_clause_elements.append(')') params.extend(sqls_params) - in_clause_elements.append(")") - return "".join(in_clause_elements), params + in_clause_elements.append(')') + return ''.join(in_clause_elements), params class PatternLookup(BuiltinLookup): - param_pattern = "%%%s%%" + param_pattern = '%%%s%%' prepare_rhs = False def get_rhs_op(self, connection, rhs): @@ -509,10 +448,8 @@ class PatternLookup(BuiltinLookup): # So, for Python values we don't need any special pattern, but for # SQL reference values or SQL transformations we need the correct # pattern added. - if hasattr(self.rhs, "as_sql") or self.bilateral_transforms: - pattern = connection.pattern_ops[self.lookup_name].format( - connection.pattern_esc - ) + if hasattr(self.rhs, 'as_sql') or self.bilateral_transforms: + pattern = connection.pattern_ops[self.lookup_name].format(connection.pattern_esc) return pattern.format(rhs) else: return super().get_rhs_op(connection, rhs) @@ -520,47 +457,45 @@ class PatternLookup(BuiltinLookup): def process_rhs(self, qn, connection): rhs, params = super().process_rhs(qn, connection) if self.rhs_is_direct_value() and params and not self.bilateral_transforms: - params[0] = self.param_pattern % connection.ops.prep_for_like_query( - params[0] - ) + params[0] = self.param_pattern % connection.ops.prep_for_like_query(params[0]) return rhs, params @Field.register_lookup class Contains(PatternLookup): - lookup_name = "contains" + lookup_name = 'contains' @Field.register_lookup class IContains(Contains): - lookup_name = "icontains" + lookup_name = 'icontains' @Field.register_lookup class StartsWith(PatternLookup): - lookup_name = "startswith" - param_pattern = "%s%%" + lookup_name = 'startswith' + param_pattern = '%s%%' @Field.register_lookup class IStartsWith(StartsWith): - lookup_name = "istartswith" + lookup_name = 'istartswith' @Field.register_lookup class EndsWith(PatternLookup): - lookup_name = "endswith" - param_pattern = "%%%s" + lookup_name = 'endswith' + param_pattern = '%%%s' @Field.register_lookup class IEndsWith(EndsWith): - lookup_name = "iendswith" + lookup_name = 'iendswith' @Field.register_lookup class Range(FieldGetDbPrepValueIterableMixin, BuiltinLookup): - lookup_name = "range" + lookup_name = 'range' def get_rhs_op(self, connection, rhs): return "BETWEEN %s AND %s" % (rhs[0], rhs[1]) @@ -568,13 +503,20 @@ class Range(FieldGetDbPrepValueIterableMixin, BuiltinLookup): @Field.register_lookup class IsNull(BuiltinLookup): - lookup_name = "isnull" + lookup_name = 'isnull' prepare_rhs = False def as_sql(self, compiler, connection): if not isinstance(self.rhs, bool): - raise ValueError( - "The QuerySet value for an isnull lookup must be True or False." + # When the deprecation ends, replace with: + # raise ValueError( + # 'The QuerySet value for an isnull lookup must be True or ' + # 'False.' + # ) + warnings.warn( + 'Using a non-boolean value for an isnull lookup is ' + 'deprecated, use True or False instead.', + RemovedInDjango40Warning, ) sql, params = compiler.compile(self.lhs) if self.rhs: @@ -585,7 +527,7 @@ class IsNull(BuiltinLookup): @Field.register_lookup class Regex(BuiltinLookup): - lookup_name = "regex" + lookup_name = 'regex' prepare_rhs = False def as_sql(self, compiler, connection): @@ -600,25 +542,16 @@ class Regex(BuiltinLookup): @Field.register_lookup class IRegex(Regex): - lookup_name = "iregex" + lookup_name = 'iregex' class YearLookup(Lookup): def year_lookup_bounds(self, connection, year): - from django.db.models.functions import ExtractIsoYear - - iso_year = isinstance(self.lhs, ExtractIsoYear) output_field = self.lhs.lhs.output_field if isinstance(output_field, DateTimeField): - bounds = connection.ops.year_lookup_bounds_for_datetime_field( - year, - iso_year=iso_year, - ) + bounds = connection.ops.year_lookup_bounds_for_datetime_field(year) else: - bounds = connection.ops.year_lookup_bounds_for_date_field( - year, - iso_year=iso_year, - ) + bounds = connection.ops.year_lookup_bounds_for_date_field(year) return bounds def as_sql(self, compiler, connection): @@ -632,7 +565,7 @@ class YearLookup(Lookup): rhs_sql = self.get_direct_rhs_sql(connection, rhs_sql) start, finish = self.year_lookup_bounds(connection, self.rhs) params.extend(self.get_bound_params(start, finish)) - return "%s %s" % (lhs_sql, rhs_sql), params + return '%s %s' % (lhs_sql, rhs_sql), params return super().as_sql(compiler, connection) def get_direct_rhs_sql(self, connection, rhs): @@ -640,13 +573,13 @@ class YearLookup(Lookup): def get_bound_params(self, start, finish): raise NotImplementedError( - "subclasses of YearLookup must provide a get_bound_params() method" + 'subclasses of YearLookup must provide a get_bound_params() method' ) class YearExact(YearLookup, Exact): def get_direct_rhs_sql(self, connection, rhs): - return "BETWEEN %s AND %s" + return 'BETWEEN %s AND %s' def get_bound_params(self, start, finish): return (start, finish) @@ -677,16 +610,12 @@ class UUIDTextMixin: Strip hyphens from a value when filtering a UUIDField on backends without a native datatype for UUID. """ - def process_rhs(self, qn, connection): if not connection.features.has_native_uuid_field: from django.db.models.functions import Replace - if self.rhs_is_direct_value(): self.rhs = Value(self.rhs) - self.rhs = Replace( - self.rhs, Value("-"), Value(""), output_field=CharField() - ) + self.rhs = Replace(self.rhs, Value('-'), Value(''), output_field=CharField()) rhs, params = super().process_rhs(qn, connection) return rhs, params diff --git a/venv/Lib/site-packages/django/db/models/manager.py b/venv/Lib/site-packages/django/db/models/manager.py index e37e56c..655dfcf 100644 --- a/venv/Lib/site-packages/django/db/models/manager.py +++ b/venv/Lib/site-packages/django/db/models/manager.py @@ -33,7 +33,7 @@ class BaseManager: def __str__(self): """Return "app_label.model_label.manager_name".""" - return "%s.%s" % (self.model._meta.label, self.name) + return '%s.%s' % (self.model._meta.label, self.name) def __class_getitem__(cls, *args, **kwargs): return cls @@ -46,12 +46,12 @@ class BaseManager: Raise a ValueError if the manager is dynamically generated. """ qs_class = self._queryset_class - if getattr(self, "_built_with_as_manager", False): + if getattr(self, '_built_with_as_manager', False): # using MyQuerySet.as_manager() return ( True, # as_manager None, # manager_class - "%s.%s" % (qs_class.__module__, qs_class.__name__), # qs_class + '%s.%s' % (qs_class.__module__, qs_class.__name__), # qs_class None, # args None, # kwargs ) @@ -69,7 +69,7 @@ class BaseManager: ) return ( False, # as_manager - "%s.%s" % (module_name, name), # manager_class + '%s.%s' % (module_name, name), # manager_class None, # qs_class self._constructor_args[0], # args self._constructor_args[1], # kwargs @@ -83,22 +83,18 @@ class BaseManager: def create_method(name, method): def manager_method(self, *args, **kwargs): return getattr(self.get_queryset(), name)(*args, **kwargs) - manager_method.__name__ = method.__name__ manager_method.__doc__ = method.__doc__ return manager_method new_methods = {} - for name, method in inspect.getmembers( - queryset_class, predicate=inspect.isfunction - ): + for name, method in inspect.getmembers(queryset_class, predicate=inspect.isfunction): # Only copy missing methods. if hasattr(cls, name): continue - # Only copy public methods or methods with the attribute - # queryset_only=False. - queryset_only = getattr(method, "queryset_only", None) - if queryset_only or (queryset_only is None and name.startswith("_")): + # Only copy public methods or methods with the attribute `queryset_only=False`. + queryset_only = getattr(method, 'queryset_only', None) + if queryset_only or (queryset_only is None and name.startswith('_')): continue # Copy the method onto the manager. new_methods[name] = create_method(name, method) @@ -107,15 +103,11 @@ class BaseManager: @classmethod def from_queryset(cls, queryset_class, class_name=None): if class_name is None: - class_name = "%sFrom%s" % (cls.__name__, queryset_class.__name__) - return type( - class_name, - (cls,), - { - "_queryset_class": queryset_class, - **cls._get_queryset_methods(queryset_class), - }, - ) + class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__) + return type(class_name, (cls,), { + '_queryset_class': queryset_class, + **cls._get_queryset_methods(queryset_class), + }) def contribute_to_class(self, cls, name): self.name = self.name or name @@ -165,8 +157,8 @@ class BaseManager: def __eq__(self, other): return ( - isinstance(other, self.__class__) - and self._constructor_args == other._constructor_args + isinstance(other, self.__class__) and + self._constructor_args == other._constructor_args ) def __hash__(self): @@ -178,24 +170,22 @@ class Manager(BaseManager.from_queryset(QuerySet)): class ManagerDescriptor: + def __init__(self, manager): self.manager = manager def __get__(self, instance, cls=None): if instance is not None: - raise AttributeError( - "Manager isn't accessible via %s instances" % cls.__name__ - ) + raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__) if cls._meta.abstract: - raise AttributeError( - "Manager isn't available; %s is abstract" % (cls._meta.object_name,) - ) + raise AttributeError("Manager isn't available; %s is abstract" % ( + cls._meta.object_name, + )) if cls._meta.swapped: raise AttributeError( - "Manager isn't available; '%s' has been swapped for '%s'" - % ( + "Manager isn't available; '%s' has been swapped for '%s'" % ( cls._meta.label, cls._meta.swapped, ) diff --git a/venv/Lib/site-packages/django/db/models/options.py b/venv/Lib/site-packages/django/db/models/options.py index 6b0749b..4028e05 100644 --- a/venv/Lib/site-packages/django/db/models/options.py +++ b/venv/Lib/site-packages/django/db/models/options.py @@ -20,37 +20,18 @@ PROXY_PARENTS = object() EMPTY_RELATION_TREE = () IMMUTABLE_WARNING = ( - "The return type of '%s' should never be mutated. If you want to manipulate this " - "list for your own use, make a copy first." + "The return type of '%s' should never be mutated. If you want to manipulate this list " + "for your own use, make a copy first." ) DEFAULT_NAMES = ( - "verbose_name", - "verbose_name_plural", - "db_table", - "ordering", - "unique_together", - "permissions", - "get_latest_by", - "order_with_respect_to", - "app_label", - "db_tablespace", - "abstract", - "managed", - "proxy", - "swappable", - "auto_created", - "index_together", - "apps", - "default_permissions", - "select_on_save", - "default_related_name", - "required_db_features", - "required_db_vendor", - "base_manager_name", - "default_manager_name", - "indexes", - "constraints", + 'verbose_name', 'verbose_name_plural', 'db_table', 'ordering', + 'unique_together', 'permissions', 'get_latest_by', 'order_with_respect_to', + 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'swappable', + 'auto_created', 'index_together', 'apps', 'default_permissions', + 'select_on_save', 'default_related_name', 'required_db_features', + 'required_db_vendor', 'base_manager_name', 'default_manager_name', + 'indexes', 'constraints', ) @@ -82,17 +63,11 @@ def make_immutable_fields_list(name, data): class Options: FORWARD_PROPERTIES = { - "fields", - "many_to_many", - "concrete_fields", - "local_concrete_fields", - "_forward_fields_map", - "managers", - "managers_map", - "base_manager", - "default_manager", + 'fields', 'many_to_many', 'concrete_fields', 'local_concrete_fields', + '_forward_fields_map', 'managers', 'managers_map', 'base_manager', + 'default_manager', } - REVERSE_PROPERTIES = {"related_objects", "fields_map", "_relation_tree"} + REVERSE_PROPERTIES = {'related_objects', 'fields_map', '_relation_tree'} default_apps = apps @@ -107,7 +82,7 @@ class Options: self.model_name = None self.verbose_name = None self.verbose_name_plural = None - self.db_table = "" + self.db_table = '' self.ordering = [] self._ordering_clash = False self.indexes = [] @@ -115,7 +90,7 @@ class Options: self.unique_together = [] self.index_together = [] self.select_on_save = False - self.default_permissions = ("add", "change", "delete", "view") + self.default_permissions = ('add', 'change', 'delete', 'view') self.permissions = [] self.object_name = None self.app_label = app_label @@ -155,11 +130,11 @@ class Options: @property def label(self): - return "%s.%s" % (self.app_label, self.object_name) + return '%s.%s' % (self.app_label, self.object_name) @property def label_lower(self): - return "%s.%s" % (self.app_label, self.model_name) + return '%s.%s' % (self.app_label, self.model_name) @property def app_config(self): @@ -192,7 +167,7 @@ class Options: # Ignore any private attributes that Django doesn't care about. # NOTE: We can't modify a dictionary's contents while looping # over it, so we loop over the *original* dictionary instead. - if name.startswith("_"): + if name.startswith('_'): del meta_attrs[name] for attr_name in DEFAULT_NAMES: if attr_name in meta_attrs: @@ -206,34 +181,30 @@ class Options: self.index_together = normalize_together(self.index_together) # App label/class name interpolation for names of constraints and # indexes. - if not getattr(cls._meta, "abstract", False): - for attr_name in {"constraints", "indexes"}: + if not getattr(cls._meta, 'abstract', False): + for attr_name in {'constraints', 'indexes'}: objs = getattr(self, attr_name, []) setattr(self, attr_name, self._format_names_with_class(cls, objs)) # verbose_name_plural is a special case because it uses a 's' # by default. if self.verbose_name_plural is None: - self.verbose_name_plural = format_lazy("{}s", self.verbose_name) + self.verbose_name_plural = format_lazy('{}s', self.verbose_name) # order_with_respect_and ordering are mutually exclusive. self._ordering_clash = bool(self.ordering and self.order_with_respect_to) # Any leftover attributes must be invalid. if meta_attrs != {}: - raise TypeError( - "'class Meta' got invalid attribute(s): %s" % ",".join(meta_attrs) - ) + raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs)) else: - self.verbose_name_plural = format_lazy("{}s", self.verbose_name) + self.verbose_name_plural = format_lazy('{}s', self.verbose_name) del self.meta # If the db_table wasn't provided, use the app_label + model_name. if not self.db_table: self.db_table = "%s_%s" % (self.app_label, self.model_name) - self.db_table = truncate_name( - self.db_table, connection.ops.max_name_length() - ) + self.db_table = truncate_name(self.db_table, connection.ops.max_name_length()) def _format_names_with_class(self, cls, objs): """App label/class name interpolation for object names.""" @@ -241,8 +212,8 @@ class Options: for obj in objs: obj = obj.clone() obj.name = obj.name % { - "app_label": cls._meta.app_label.lower(), - "class": cls.__name__.lower(), + 'app_label': cls._meta.app_label.lower(), + 'class': cls.__name__.lower(), } new_objs.append(obj) return new_objs @@ -250,19 +221,19 @@ class Options: def _get_default_pk_class(self): pk_class_path = getattr( self.app_config, - "default_auto_field", + 'default_auto_field', settings.DEFAULT_AUTO_FIELD, ) if self.app_config and self.app_config._is_default_auto_field_overridden: app_config_class = type(self.app_config) source = ( - f"{app_config_class.__module__}." - f"{app_config_class.__qualname__}.default_auto_field" + f'{app_config_class.__module__}.' + f'{app_config_class.__qualname__}.default_auto_field' ) else: - source = "DEFAULT_AUTO_FIELD" + source = 'DEFAULT_AUTO_FIELD' if not pk_class_path: - raise ImproperlyConfigured(f"{source} must not be empty.") + raise ImproperlyConfigured(f'{source} must not be empty.') try: pk_class = import_string(pk_class_path) except ImportError as e: @@ -285,20 +256,15 @@ class Options: query = self.order_with_respect_to try: self.order_with_respect_to = next( - f - for f in self._get_fields(reverse=False) + f for f in self._get_fields(reverse=False) if f.name == query or f.attname == query ) except StopIteration: - raise FieldDoesNotExist( - "%s has no field named '%s'" % (self.object_name, query) - ) + raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, query)) - self.ordering = ("_order",) - if not any( - isinstance(field, OrderWrt) for field in model._meta.local_fields - ): - model.add_to_class("_order", OrderWrt()) + self.ordering = ('_order',) + if not any(isinstance(field, OrderWrt) for field in model._meta.local_fields): + model.add_to_class('_order', OrderWrt()) else: self.order_with_respect_to = None @@ -310,17 +276,15 @@ class Options: # Look for a local field with the same name as the # first parent link. If a local field has already been # created, use it instead of promoting the parent - already_created = [ - fld for fld in self.local_fields if fld.name == field.name - ] + already_created = [fld for fld in self.local_fields if fld.name == field.name] if already_created: field = already_created[0] field.primary_key = True self.setup_pk(field) else: pk_class = self._get_default_pk_class() - auto = pk_class(verbose_name="ID", primary_key=True, auto_created=True) - model.add_to_class("id", auto) + auto = pk_class(verbose_name='ID', primary_key=True, auto_created=True) + model.add_to_class('id', auto) def add_manager(self, manager): self.local_managers.append(manager) @@ -347,11 +311,7 @@ class Options: # ideally, we'd just ask for field.related_model. However, related_model # is a cached property, and all the models haven't been loaded yet, so # we need to make sure we don't cache a string reference. - if ( - field.is_relation - and hasattr(field.remote_field, "model") - and field.remote_field.model - ): + if field.is_relation and hasattr(field.remote_field, 'model') and field.remote_field.model: try: field.remote_field.model._meta._expire_cache(forward=False) except AttributeError: @@ -375,7 +335,7 @@ class Options: self.db_table = target._meta.db_table def __repr__(self): - return "<Options for %s>" % self.object_name + return '<Options for %s>' % self.object_name def __str__(self): return self.label_lower @@ -392,10 +352,8 @@ class Options: if self.required_db_vendor: return self.required_db_vendor == connection.vendor if self.required_db_features: - return all( - getattr(connection.features, feat, False) - for feat in self.required_db_features - ) + return all(getattr(connection.features, feat, False) + for feat in self.required_db_features) return True @property @@ -417,7 +375,7 @@ class Options: swapped_for = getattr(settings, self.swappable, None) if swapped_for: try: - swapped_label, swapped_object = swapped_for.split(".") + swapped_label, swapped_object = swapped_for.split('.') except ValueError: # setting not in the format app_label.model_name # raising ImproperlyConfigured here causes problems with @@ -425,10 +383,7 @@ class Options: # or as part of validation. return swapped_for - if ( - "%s.%s" % (swapped_label, swapped_object.lower()) - != self.label_lower - ): + if '%s.%s' % (swapped_label, swapped_object.lower()) != self.label_lower: return swapped_for return None @@ -436,7 +391,7 @@ class Options: def managers(self): managers = [] seen_managers = set() - bases = (b for b in self.model.mro() if hasattr(b, "_meta")) + bases = (b for b in self.model.mro() if hasattr(b, '_meta')) for depth, base in enumerate(bases): for manager in base._meta.local_managers: if manager.name in seen_managers: @@ -462,8 +417,8 @@ class Options: if not base_manager_name: # Get the first parent's base_manager_name if there's one. for parent in self.model.mro()[1:]: - if hasattr(parent, "_meta"): - if parent._base_manager.name != "_base_manager": + if hasattr(parent, '_meta'): + if parent._base_manager.name != '_base_manager': base_manager_name = parent._base_manager.name break @@ -472,15 +427,14 @@ class Options: return self.managers_map[base_manager_name] except KeyError: raise ValueError( - "%s has no manager named %r" - % ( + "%s has no manager named %r" % ( self.object_name, base_manager_name, ) ) manager = Manager() - manager.name = "_base_manager" + manager.name = '_base_manager' manager.model = self.model manager.auto_created = True return manager @@ -491,7 +445,7 @@ class Options: if not default_manager_name and not self.local_managers: # Get the first parent's default_manager_name if there's one. for parent in self.model.mro()[1:]: - if hasattr(parent, "_meta"): + if hasattr(parent, '_meta'): default_manager_name = parent._meta.default_manager_name break @@ -500,8 +454,7 @@ class Options: return self.managers_map[default_manager_name] except KeyError: raise ValueError( - "%s has no manager named %r" - % ( + "%s has no manager named %r" % ( self.object_name, default_manager_name, ) @@ -535,20 +488,13 @@ class Options: def is_not_a_generic_foreign_key(f): return not ( - f.is_relation - and f.many_to_one - and not (hasattr(f.remote_field, "model") and f.remote_field.model) + f.is_relation and f.many_to_one and not (hasattr(f.remote_field, 'model') and f.remote_field.model) ) return make_immutable_fields_list( "fields", - ( - f - for f in self._get_fields(reverse=False) - if is_not_an_m2m_field(f) - and is_not_a_generic_relation(f) - and is_not_a_generic_foreign_key(f) - ), + (f for f in self._get_fields(reverse=False) + if is_not_an_m2m_field(f) and is_not_a_generic_relation(f) and is_not_a_generic_foreign_key(f)) ) @cached_property @@ -588,11 +534,7 @@ class Options: """ return make_immutable_fields_list( "many_to_many", - ( - f - for f in self._get_fields(reverse=False) - if f.is_relation and f.many_to_many - ), + (f for f in self._get_fields(reverse=False) if f.is_relation and f.many_to_many) ) @cached_property @@ -606,16 +548,10 @@ class Options: combined with filtering of field properties is the public API for obtaining this field list. """ - all_related_fields = self._get_fields( - forward=False, reverse=True, include_hidden=True - ) + all_related_fields = self._get_fields(forward=False, reverse=True, include_hidden=True) return make_immutable_fields_list( "related_objects", - ( - obj - for obj in all_related_fields - if not obj.hidden or obj.field.many_to_many - ), + (obj for obj in all_related_fields if not obj.hidden or obj.field.many_to_many) ) @cached_property @@ -671,9 +607,7 @@ class Options: # field map. return self.fields_map[field_name] except KeyError: - raise FieldDoesNotExist( - "%s has no field named '%s'" % (self.object_name, field_name) - ) + raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name)) def get_base_chain(self, model): """ @@ -742,17 +676,15 @@ class Options: final_field = opts.parents[int_model] targets = (final_field.remote_field.get_related_field(),) opts = int_model._meta - path.append( - PathInfo( - from_opts=final_field.model._meta, - to_opts=opts, - target_fields=targets, - join_field=final_field, - m2m=False, - direct=True, - filtered_relation=None, - ) - ) + path.append(PathInfo( + from_opts=final_field.model._meta, + to_opts=opts, + target_fields=targets, + join_field=final_field, + m2m=False, + direct=True, + filtered_relation=None, + )) return path def get_path_from_parent(self, parent): @@ -794,8 +726,7 @@ class Options: if opts.abstract: continue fields_with_relations = ( - f - for f in opts._get_fields(reverse=False, include_parents=False) + f for f in opts._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: @@ -809,13 +740,11 @@ class Options: # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. - related_objects = related_objects_graph[ - model._meta.concrete_model._meta.label - ] - model._meta.__dict__["_relation_tree"] = related_objects + related_objects = related_objects_graph[model._meta.concrete_model._meta.label] + model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). - return self.__dict__.get("_relation_tree", EMPTY_RELATION_TREE) + return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE) @cached_property def _relation_tree(self): @@ -846,18 +775,10 @@ class Options: """ if include_parents is False: include_parents = PROXY_PARENTS - return self._get_fields( - include_parents=include_parents, include_hidden=include_hidden - ) + return self._get_fields(include_parents=include_parents, include_hidden=include_hidden) - def _get_fields( - self, - forward=True, - reverse=True, - include_parents=True, - include_hidden=False, - seen_models=None, - ): + def _get_fields(self, forward=True, reverse=True, include_parents=True, include_hidden=False, + seen_models=None): """ Internal helper function to return fields of the model. * If forward=True, then fields defined on this model are returned. @@ -870,9 +791,7 @@ class Options: parent chain to the model's concrete model. """ if include_parents not in (True, False, PROXY_PARENTS): - raise TypeError( - "Invalid argument for include_parents: %s" % (include_parents,) - ) + raise TypeError("Invalid argument for include_parents: %s" % (include_parents,)) # This helper function is used to allow recursion in ``get_fields()`` # implementation and to provide a fast way for Django's internals to # access specific subsets of fields. @@ -904,22 +823,13 @@ class Options: # fields from the same parent again. if parent in seen_models: continue - if ( - parent._meta.concrete_model != self.concrete_model - and include_parents == PROXY_PARENTS - ): + if (parent._meta.concrete_model != self.concrete_model and + include_parents == PROXY_PARENTS): continue for obj in parent._meta._get_fields( - forward=forward, - reverse=reverse, - include_parents=include_parents, - include_hidden=include_hidden, - seen_models=seen_models, - ): - if ( - not getattr(obj, "parent_link", False) - or obj.model == self.concrete_model - ): + forward=forward, reverse=reverse, include_parents=include_parents, + include_hidden=include_hidden, seen_models=seen_models): + if not getattr(obj, 'parent_link', False) or obj.model == self.concrete_model: fields.append(obj) if reverse and not self.proxy: # Tree is computed once and cached until the app cache is expired. @@ -960,11 +870,7 @@ class Options: return [ constraint for constraint in self.constraints - if ( - isinstance(constraint, UniqueConstraint) - and constraint.condition is None - and not constraint.contains_expressions - ) + if isinstance(constraint, UniqueConstraint) and constraint.condition is None ] @cached_property @@ -984,9 +890,6 @@ class Options: Fields to be returned after a database insert. """ return [ - field - for field in self._get_fields( - forward=True, reverse=False, include_parents=PROXY_PARENTS - ) - if getattr(field, "db_returning", False) + field for field in self._get_fields(forward=True, reverse=False, include_parents=PROXY_PARENTS) + if getattr(field, 'db_returning', False) ] diff --git a/venv/Lib/site-packages/django/db/models/query.py b/venv/Lib/site-packages/django/db/models/query.py index 57adf60..02c1b31 100644 --- a/venv/Lib/site-packages/django/db/models/query.py +++ b/venv/Lib/site-packages/django/db/models/query.py @@ -11,12 +11,8 @@ import django from django.conf import settings from django.core import exceptions from django.db import ( - DJANGO_VERSION_PICKLE_KEY, - IntegrityError, - NotSupportedError, - connections, - router, - transaction, + DJANGO_VERSION_PICKLE_KEY, IntegrityError, NotSupportedError, connections, + router, transaction, ) from django.db.models import AutoField, DateField, DateTimeField, sql from django.db.models.constants import LOOKUP_SEP @@ -37,9 +33,7 @@ REPR_OUTPUT_SIZE = 20 class BaseIterable: - def __init__( - self, queryset, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE - ): + def __init__(self, queryset, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE): self.queryset = queryset self.chunked_fetch = chunked_fetch self.chunk_size = chunk_size @@ -54,40 +48,25 @@ class ModelIterable(BaseIterable): compiler = queryset.query.get_compiler(using=db) # Execute the query. This will also fill compiler.select, klass_info, # and annotations. - results = compiler.execute_sql( - chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size - ) - select, klass_info, annotation_col_map = ( - compiler.select, - compiler.klass_info, - compiler.annotation_col_map, - ) - model_cls = klass_info["model"] - select_fields = klass_info["select_fields"] + results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size) + select, klass_info, annotation_col_map = (compiler.select, compiler.klass_info, + compiler.annotation_col_map) + model_cls = klass_info['model'] + select_fields = klass_info['select_fields'] model_fields_start, model_fields_end = select_fields[0], select_fields[-1] + 1 - init_list = [ - f[0].target.attname for f in select[model_fields_start:model_fields_end] - ] + init_list = [f[0].target.attname + for f in select[model_fields_start:model_fields_end]] related_populators = get_related_populators(klass_info, select, db) known_related_objects = [ - ( - field, - related_objs, - operator.attrgetter( - *[ - field.attname - if from_field == "self" - else queryset.model._meta.get_field(from_field).attname - for from_field in field.from_fields - ] - ), - ) - for field, related_objs in queryset._known_related_objects.items() + (field, related_objs, operator.attrgetter(*[ + field.attname + if from_field == 'self' else + queryset.model._meta.get_field(from_field).attname + for from_field in field.from_fields + ])) for field, related_objs in queryset._known_related_objects.items() ] for row in compiler.results_iter(results): - obj = model_cls.from_db( - db, init_list, row[model_fields_start:model_fields_end] - ) + obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end]) for rel_populator in related_populators: rel_populator.populate(row, obj) if annotation_col_map: @@ -127,9 +106,7 @@ class ValuesIterable(BaseIterable): *query.annotation_select, ] indexes = range(len(names)) - for row in compiler.results_iter( - chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size - ): + for row in compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size): yield {names[i]: row[i] for i in indexes} @@ -151,25 +128,16 @@ class ValuesListIterable(BaseIterable): *query.values_select, *query.annotation_select, ] - fields = [ - *queryset._fields, - *(f for f in query.annotation_select if f not in queryset._fields), - ] + fields = [*queryset._fields, *(f for f in query.annotation_select if f not in queryset._fields)] if fields != names: # Reorder according to fields. index_map = {name: idx for idx, name in enumerate(names)} rowfactory = operator.itemgetter(*[index_map[f] for f in fields]) return map( rowfactory, - compiler.results_iter( - chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size - ), + compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size) ) - return compiler.results_iter( - tuple_expected=True, - chunked_fetch=self.chunked_fetch, - chunk_size=self.chunk_size, - ) + return compiler.results_iter(tuple_expected=True, chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size) class NamedValuesListIterable(ValuesListIterable): @@ -184,11 +152,7 @@ class NamedValuesListIterable(ValuesListIterable): names = queryset._fields else: query = queryset.query - names = [ - *query.extra_select, - *query.values_select, - *query.annotation_select, - ] + names = [*query.extra_select, *query.values_select, *query.annotation_select] tuple_class = create_namedtuple_class(*names) new = tuple.__new__ for row in super().__iter__(): @@ -204,9 +168,7 @@ class FlatValuesListIterable(BaseIterable): def __iter__(self): queryset = self.queryset compiler = queryset.query.get_compiler(queryset.db) - for row in compiler.results_iter( - chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size - ): + for row in compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size): yield row[0] @@ -246,11 +208,9 @@ class QuerySet: def as_manager(cls): # Address the circular dependency between `Queryset` and `Manager`. from django.db.models.manager import Manager - manager = Manager.from_queryset(cls)() manager._built_with_as_manager = True return manager - as_manager.queryset_only = True as_manager = classmethod(as_manager) @@ -262,7 +222,7 @@ class QuerySet: """Don't populate the QuerySet's cache.""" obj = self.__class__() for k, v in self.__dict__.items(): - if k == "_result_cache": + if k == '_result_cache': obj.__dict__[k] = None else: obj.__dict__[k] = copy.deepcopy(v, memo) @@ -293,10 +253,10 @@ class QuerySet: self.__dict__.update(state) def __repr__(self): - data = list(self[: REPR_OUTPUT_SIZE + 1]) + data = list(self[:REPR_OUTPUT_SIZE + 1]) if len(data) > REPR_OUTPUT_SIZE: data[-1] = "...(remaining elements truncated)..." - return "<%s %r>" % (self.__class__.__name__, data) + return '<%s %r>' % (self.__class__.__name__, data) def __len__(self): self._fetch_all() @@ -328,17 +288,13 @@ class QuerySet: """Retrieve an item or slice from the set of results.""" if not isinstance(k, (int, slice)): raise TypeError( - "QuerySet indices must be integers or slices, not %s." + 'QuerySet indices must be integers or slices, not %s.' % type(k).__name__ ) - if (isinstance(k, int) and k < 0) or ( - isinstance(k, slice) - and ( - (k.start is not None and k.start < 0) - or (k.stop is not None and k.stop < 0) - ) - ): - raise ValueError("Negative indexing is not supported.") + assert ((not isinstance(k, slice) and (k >= 0)) or + (isinstance(k, slice) and (k.start is None or k.start >= 0) and + (k.stop is None or k.stop >= 0))), \ + "Negative indexing is not supported." if self._result_cache is not None: return self._result_cache[k] @@ -354,7 +310,7 @@ class QuerySet: else: stop = None qs.query.set_limits(start, stop) - return list(qs)[:: k.step] if k.step else qs + return list(qs)[::k.step] if k.step else qs qs = self._chain() qs.query.set_limits(k, k + 1) @@ -381,15 +337,11 @@ class QuerySet: return other if isinstance(other, EmptyQuerySet): return self - query = ( - self - if self.query.can_filter() - else self.model._base_manager.filter(pk__in=self.values("pk")) - ) + query = self if self.query.can_filter() else self.model._base_manager.filter(pk__in=self.values('pk')) combined = query._chain() combined._merge_known_related_objects(other) if not other.query.can_filter(): - other = other.model._base_manager.filter(pk__in=other.values("pk")) + other = other.model._base_manager.filter(pk__in=other.values('pk')) combined.query.combine(other.query, sql.OR) return combined @@ -398,9 +350,7 @@ class QuerySet: #################################### def _iterator(self, use_chunked_fetch, chunk_size): - yield from self._iterable_class( - self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size - ) + yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size) def iterator(self, chunk_size=2000): """ @@ -408,10 +358,8 @@ class QuerySet: database. """ if chunk_size <= 0: - raise ValueError("Chunk size must be strictly positive.") - use_chunked_fetch = not connections[self.db].settings_dict.get( - "DISABLE_SERVER_SIDE_CURSORS" - ) + raise ValueError('Chunk size must be strictly positive.') + use_chunked_fetch = not connections[self.db].settings_dict.get('DISABLE_SERVER_SIDE_CURSORS') return self._iterator(use_chunked_fetch, chunk_size) def aggregate(self, *args, **kwargs): @@ -424,9 +372,7 @@ class QuerySet: """ if self.query.distinct_fields: raise NotImplementedError("aggregate() + distinct(fields) not implemented.") - self._validate_values_are_expressions( - (*args, *kwargs.values()), method_name="aggregate" - ) + self._validate_values_are_expressions((*args, *kwargs.values()), method_name='aggregate') for arg in args: # The default_alias property raises TypeError if default_alias # can't be set automatically or AttributeError if it isn't an @@ -444,11 +390,7 @@ class QuerySet: if not annotation.contains_aggregate: raise TypeError("%s is not an aggregate expression" % alias) for expr in annotation.get_source_expressions(): - if ( - expr.contains_aggregate - and isinstance(expr, Ref) - and expr.refs in kwargs - ): + if expr.contains_aggregate and isinstance(expr, Ref) and expr.refs in kwargs: name = expr.refs raise exceptions.FieldError( "Cannot compute %s('%s'): '%s' is an aggregate" @@ -476,17 +418,14 @@ class QuerySet: """ if self.query.combinator and (args or kwargs): raise NotSupportedError( - "Calling QuerySet.get(...) with filters after %s() is not " - "supported." % self.query.combinator + 'Calling QuerySet.get(...) with filters after %s() is not ' + 'supported.' % self.query.combinator ) clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) if self.query.can_filter() and not self.query.distinct_fields: clone = clone.order_by() limit = None - if ( - not clone.query.select_for_update - or connections[clone.db].features.supports_select_for_update_with_limit - ): + if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit: limit = MAX_GET_RESULTS clone.query.set_limits(high=limit) num = len(clone) @@ -494,13 +433,13 @@ class QuerySet: return clone._result_cache[0] if not num: raise self.model.DoesNotExist( - "%s matching query does not exist." % self.model._meta.object_name + "%s matching query does not exist." % + self.model._meta.object_name ) raise self.model.MultipleObjectsReturned( - "get() returned more than one %s -- it returned %s!" - % ( + 'get() returned more than one %s -- it returned %s!' % ( self.model._meta.object_name, - num if not limit or num < limit else "more than %s" % (limit - 1), + num if not limit or num < limit else 'more than %s' % (limit - 1), ) ) @@ -519,7 +458,7 @@ class QuerySet: if obj.pk is None: # Populate new PK values. obj.pk = obj._meta.pk.get_pk_value_on_save(obj) - obj._prepare_related_fields_for_save(operation_name="bulk_create") + obj._prepare_related_fields_for_save(operation_name='bulk_create') def bulk_create(self, objs, batch_size=None, ignore_conflicts=False): """ @@ -541,8 +480,7 @@ class QuerySet: # PostgreSQL via the RETURNING ID clause. It should be possible for # Oracle as well, but the semantics for extracting the primary keys is # trickier so it's not done yet. - if batch_size is not None and batch_size <= 0: - raise ValueError("Batch size must be a positive integer.") + assert batch_size is None or batch_size > 0 # Check that the parents share the same concrete model with the our # model to detect the inheritance pattern ConcreteGrandParent -> # MultiTableParent -> ProxyChild. Simply checking self.model._meta.proxy @@ -562,10 +500,7 @@ class QuerySet: objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs) if objs_with_pk: returned_columns = self._batched_insert( - objs_with_pk, - fields, - batch_size, - ignore_conflicts=ignore_conflicts, + objs_with_pk, fields, batch_size, ignore_conflicts=ignore_conflicts, ) for obj_with_pk, results in zip(objs_with_pk, returned_columns): for result, field in zip(results, opts.db_returning_fields): @@ -577,15 +512,9 @@ class QuerySet: if objs_without_pk: fields = [f for f in fields if not isinstance(f, AutoField)] returned_columns = self._batched_insert( - objs_without_pk, - fields, - batch_size, - ignore_conflicts=ignore_conflicts, + objs_without_pk, fields, batch_size, ignore_conflicts=ignore_conflicts, ) - if ( - connection.features.can_return_rows_from_bulk_insert - and not ignore_conflicts - ): + if connection.features.can_return_rows_from_bulk_insert and not ignore_conflicts: assert len(returned_columns) == len(objs_without_pk) for obj_without_pk, results in zip(objs_without_pk, returned_columns): for result, field in zip(results, opts.db_returning_fields): @@ -600,27 +529,25 @@ class QuerySet: Update the given fields in each of the given objects in the database. """ if batch_size is not None and batch_size < 0: - raise ValueError("Batch size must be a positive integer.") + raise ValueError('Batch size must be a positive integer.') if not fields: - raise ValueError("Field names must be given to bulk_update().") + raise ValueError('Field names must be given to bulk_update().') objs = tuple(objs) if any(obj.pk is None for obj in objs): - raise ValueError("All bulk_update() objects must have a primary key set.") + raise ValueError('All bulk_update() objects must have a primary key set.') fields = [self.model._meta.get_field(name) for name in fields] if any(not f.concrete or f.many_to_many for f in fields): - raise ValueError("bulk_update() can only be used with concrete fields.") + raise ValueError('bulk_update() can only be used with concrete fields.') if any(f.primary_key for f in fields): - raise ValueError("bulk_update() cannot be used with primary key fields.") + raise ValueError('bulk_update() cannot be used with primary key fields.') if not objs: - return 0 + return # PK is used twice in the resulting update query, once in the filter # and once in the WHEN. Each field will also have one CAST. - max_batch_size = connections[self.db].ops.bulk_batch_size( - ["pk", "pk"] + fields, objs - ) + max_batch_size = connections[self.db].ops.bulk_batch_size(['pk', 'pk'] + fields, objs) batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size requires_casting = connections[self.db].features.requires_casted_case_in_updates - batches = (objs[i : i + batch_size] for i in range(0, len(objs), batch_size)) + batches = (objs[i:i + batch_size] for i in range(0, len(objs), batch_size)) updates = [] for batch_objs in batches: update_kwargs = {} @@ -636,12 +563,9 @@ class QuerySet: case_statement = Cast(case_statement, output_field=field) update_kwargs[field.attname] = case_statement updates.append(([obj.pk for obj in batch_objs], update_kwargs)) - rows_updated = 0 with transaction.atomic(using=self.db, savepoint=False): for pks, update_kwargs in updates: - rows_updated += self.filter(pk__in=pks).update(**update_kwargs) - return rows_updated - + self.filter(pk__in=pks).update(**update_kwargs) bulk_update.alters_data = True def get_or_create(self, defaults=None, **kwargs): @@ -708,12 +632,10 @@ class QuerySet: invalid_params.append(param) if invalid_params: raise exceptions.FieldError( - "Invalid field name(s) for model %s: '%s'." - % ( + "Invalid field name(s) for model %s: '%s'." % ( self.model._meta.object_name, "', '".join(sorted(invalid_params)), - ) - ) + )) return params def _earliest(self, *fields): @@ -724,7 +646,7 @@ class QuerySet: if fields: order_by = fields else: - order_by = getattr(self.model._meta, "get_latest_by") + order_by = getattr(self.model._meta, 'get_latest_by') if order_by and not isinstance(order_by, (tuple, list)): order_by = (order_by,) if order_by is None: @@ -732,39 +654,38 @@ class QuerySet: "earliest() and latest() require either fields as positional " "arguments or 'get_latest_by' in the model's Meta." ) + + assert not self.query.is_sliced, \ + "Cannot change a query once a slice has been taken." obj = self._chain() obj.query.set_limits(high=1) - obj.query.clear_ordering(force=True) + obj.query.clear_ordering(force_empty=True) obj.query.add_ordering(*order_by) return obj.get() def earliest(self, *fields): - if self.query.is_sliced: - raise TypeError("Cannot change a query once a slice has been taken.") return self._earliest(*fields) def latest(self, *fields): - if self.query.is_sliced: - raise TypeError("Cannot change a query once a slice has been taken.") return self.reverse()._earliest(*fields) def first(self): """Return the first object of a query or None if no match is found.""" - for obj in (self if self.ordered else self.order_by("pk"))[:1]: + for obj in (self if self.ordered else self.order_by('pk'))[:1]: return obj def last(self): """Return the last object of a query or None if no match is found.""" - for obj in (self.reverse() if self.ordered else self.order_by("-pk"))[:1]: + for obj in (self.reverse() if self.ordered else self.order_by('-pk'))[:1]: return obj - def in_bulk(self, id_list=None, *, field_name="pk"): + def in_bulk(self, id_list=None, *, field_name='pk'): """ Return a dictionary mapping each of the given IDs to the object with that ID. If `id_list` isn't provided, evaluate the entire QuerySet. """ - if self.query.is_sliced: - raise TypeError("Cannot use 'limit' or 'offset' with in_bulk().") + assert not self.query.is_sliced, \ + "Cannot use 'limit' or 'offset' with in_bulk" opts = self.model._meta unique_fields = [ constraint.fields[0] @@ -772,19 +693,16 @@ class QuerySet: if len(constraint.fields) == 1 ] if ( - field_name != "pk" - and not opts.get_field(field_name).unique - and field_name not in unique_fields - and self.query.distinct_fields != (field_name,) + field_name != 'pk' and + not opts.get_field(field_name).unique and + field_name not in unique_fields and + self.query.distinct_fields != (field_name,) ): - raise ValueError( - "in_bulk()'s field_name must be a unique field but %r isn't." - % field_name - ) + raise ValueError("in_bulk()'s field_name must be a unique field but %r isn't." % field_name) if id_list is not None: if not id_list: return {} - filter_key = "{}__in".format(field_name) + filter_key = '{}__in'.format(field_name) batch_size = connections[self.db].features.max_query_params id_list = tuple(id_list) # If the database has a limit on the number of query parameters @@ -792,7 +710,7 @@ class QuerySet: if batch_size and batch_size < len(id_list): qs = () for offset in range(0, len(id_list), batch_size): - batch = id_list[offset : offset + batch_size] + batch = id_list[offset:offset + batch_size] qs += tuple(self.filter(**{filter_key: batch}).order_by()) else: qs = self.filter(**{filter_key: id_list}).order_by() @@ -802,11 +720,12 @@ class QuerySet: def delete(self): """Delete the records in the current QuerySet.""" - self._not_support_combined_queries("delete") - if self.query.is_sliced: - raise TypeError("Cannot use 'limit' or 'offset' with delete().") + self._not_support_combined_queries('delete') + assert not self.query.is_sliced, \ + "Cannot use 'limit' or 'offset' with delete." + if self.query.distinct or self.query.distinct_fields: - raise TypeError("Cannot call delete() after .distinct().") + raise TypeError('Cannot call delete() after .distinct().') if self._fields is not None: raise TypeError("Cannot call delete() after .values() or .values_list()") @@ -820,7 +739,7 @@ class QuerySet: # Disable non-supported fields. del_query.query.select_for_update = False del_query.query.select_related = False - del_query.query.clear_ordering(force=True) + del_query.query.clear_ordering(force_empty=True) collector = Collector(using=del_query.db) collector.collect(del_query) @@ -845,7 +764,6 @@ class QuerySet: with cursor: return cursor.rowcount return 0 - _raw_delete.alters_data = True def update(self, **kwargs): @@ -853,9 +771,9 @@ class QuerySet: Update all elements in the current QuerySet, setting all the given fields to the appropriate values. """ - self._not_support_combined_queries("update") - if self.query.is_sliced: - raise TypeError("Cannot update a query once a slice has been taken.") + self._not_support_combined_queries('update') + assert not self.query.is_sliced, \ + "Cannot update a query once a slice has been taken." self._for_write = True query = self.query.chain(sql.UpdateQuery) query.add_update_values(kwargs) @@ -865,7 +783,6 @@ class QuerySet: rows = query.get_compiler(self.db).execute_sql(CURSOR) self._result_cache = None return rows - update.alters_data = True def _update(self, values): @@ -875,15 +792,14 @@ class QuerySet: code (it requires too much poking around at model internals to be useful at that level). """ - if self.query.is_sliced: - raise TypeError("Cannot update a query once a slice has been taken.") + assert not self.query.is_sliced, \ + "Cannot update a query once a slice has been taken." query = self.query.chain(sql.UpdateQuery) query.add_update_fields(values) # Clear any annotations so that they won't be present in subqueries. query.annotations = {} self._result_cache = None return query.get_compiler(self.db).execute_sql(CURSOR) - _update.alters_data = True _update.queryset_only = False @@ -892,24 +808,6 @@ class QuerySet: return self.query.has_results(using=self.db) return bool(self._result_cache) - def contains(self, obj): - """Return True if the queryset contains an object.""" - self._not_support_combined_queries("contains") - if self._fields is not None: - raise TypeError( - "Cannot call QuerySet.contains() after .values() or .values_list()." - ) - try: - if obj._meta.concrete_model != self.model._meta.concrete_model: - return False - except AttributeError: - raise TypeError("'obj' must be a model instance.") - if obj.pk is None: - raise ValueError("QuerySet.contains() cannot be used on unsaved objects.") - if self._result_cache is not None: - return obj in self._result_cache - return self.filter(pk=obj.pk).exists() - def _prefetch_related_objects(self): # This method can only be called once the result cache has been filled. prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups) @@ -925,13 +823,7 @@ class QuerySet: def raw(self, raw_query, params=(), translations=None, using=None): if using is None: using = self.db - qs = RawQuerySet( - raw_query, - model=self.model, - params=params, - translations=translations, - using=using, - ) + qs = RawQuerySet(raw_query, model=self.model, params=params, translations=translations, using=using) qs._prefetch_related_lookups = self._prefetch_related_lookups[:] return qs @@ -953,20 +845,15 @@ class QuerySet: if flat and named: raise TypeError("'flat' and 'named' can't be used together.") if flat and len(fields) > 1: - raise TypeError( - "'flat' is not valid when values_list is called with more than one " - "field." - ) + raise TypeError("'flat' is not valid when values_list is called with more than one field.") - field_names = {f for f in fields if not hasattr(f, "resolve_expression")} + field_names = {f for f in fields if not hasattr(f, 'resolve_expression')} _fields = [] expressions = {} counter = 1 for field in fields: - if hasattr(field, "resolve_expression"): - field_id_prefix = getattr( - field, "default_alias", field.__class__.__name__.lower() - ) + if hasattr(field, 'resolve_expression'): + field_id_prefix = getattr(field, 'default_alias', field.__class__.__name__.lower()) while True: field_id = field_id_prefix + str(counter) counter += 1 @@ -979,71 +866,54 @@ class QuerySet: clone = self._values(*_fields, **expressions) clone._iterable_class = ( - NamedValuesListIterable - if named - else FlatValuesListIterable - if flat + NamedValuesListIterable if named + else FlatValuesListIterable if flat else ValuesListIterable ) return clone - def dates(self, field_name, kind, order="ASC"): + def dates(self, field_name, kind, order='ASC'): """ Return a list of date objects representing all available dates for the given field_name, scoped to 'kind'. """ - if kind not in ("year", "month", "week", "day"): - raise ValueError("'kind' must be one of 'year', 'month', 'week', or 'day'.") - if order not in ("ASC", "DESC"): - raise ValueError("'order' must be either 'ASC' or 'DESC'.") - return ( - self.annotate( - datefield=Trunc(field_name, kind, output_field=DateField()), - plain_field=F(field_name), - ) - .values_list("datefield", flat=True) - .distinct() - .filter(plain_field__isnull=False) - .order_by(("-" if order == "DESC" else "") + "datefield") - ) + assert kind in ('year', 'month', 'week', 'day'), \ + "'kind' must be one of 'year', 'month', 'week', or 'day'." + assert order in ('ASC', 'DESC'), \ + "'order' must be either 'ASC' or 'DESC'." + return self.annotate( + datefield=Trunc(field_name, kind, output_field=DateField()), + plain_field=F(field_name) + ).values_list( + 'datefield', flat=True + ).distinct().filter(plain_field__isnull=False).order_by(('-' if order == 'DESC' else '') + 'datefield') - # RemovedInDjango50Warning: when the deprecation ends, remove is_dst - # argument. - def datetimes( - self, field_name, kind, order="ASC", tzinfo=None, is_dst=timezone.NOT_PASSED - ): + def datetimes(self, field_name, kind, order='ASC', tzinfo=None, is_dst=None): """ Return a list of datetime objects representing all available datetimes for the given field_name, scoped to 'kind'. """ - if kind not in ("year", "month", "week", "day", "hour", "minute", "second"): - raise ValueError( - "'kind' must be one of 'year', 'month', 'week', 'day', " - "'hour', 'minute', or 'second'." - ) - if order not in ("ASC", "DESC"): - raise ValueError("'order' must be either 'ASC' or 'DESC'.") + assert kind in ('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), \ + "'kind' must be one of 'year', 'month', 'week', 'day', 'hour', 'minute', or 'second'." + assert order in ('ASC', 'DESC'), \ + "'order' must be either 'ASC' or 'DESC'." if settings.USE_TZ: if tzinfo is None: tzinfo = timezone.get_current_timezone() else: tzinfo = None - return ( - self.annotate( - datetimefield=Trunc( - field_name, - kind, - output_field=DateTimeField(), - tzinfo=tzinfo, - is_dst=is_dst, - ), - plain_field=F(field_name), - ) - .values_list("datetimefield", flat=True) - .distinct() - .filter(plain_field__isnull=False) - .order_by(("-" if order == "DESC" else "") + "datetimefield") - ) + return self.annotate( + datetimefield=Trunc( + field_name, + kind, + output_field=DateTimeField(), + tzinfo=tzinfo, + is_dst=is_dst, + ), + plain_field=F(field_name) + ).values_list( + 'datetimefield', flat=True + ).distinct().filter(plain_field__isnull=False).order_by(('-' if order == 'DESC' else '') + 'datetimefield') def none(self): """Return an empty QuerySet.""" @@ -1067,7 +937,7 @@ class QuerySet: Return a new QuerySet instance with the args ANDed to the existing set. """ - self._not_support_combined_queries("filter") + self._not_support_combined_queries('filter') return self._filter_or_exclude(False, args, kwargs) def exclude(self, *args, **kwargs): @@ -1075,12 +945,14 @@ class QuerySet: Return a new QuerySet instance with NOT (args) ANDed to the existing set. """ - self._not_support_combined_queries("exclude") + self._not_support_combined_queries('exclude') return self._filter_or_exclude(True, args, kwargs) def _filter_or_exclude(self, negate, args, kwargs): - if (args or kwargs) and self.query.is_sliced: - raise TypeError("Cannot filter a query once a slice has been taken.") + if args or kwargs: + assert not self.query.is_sliced, \ + "Cannot filter a query once a slice has been taken." + clone = self._chain() if self._defer_next_filter: self._defer_next_filter = False @@ -1116,11 +988,9 @@ class QuerySet: # Clone the query to inherit the select list and everything clone = self._chain() # Clear limits and ordering so they can be reapplied - clone.query.clear_ordering(force=True) + clone.query.clear_ordering(True) clone.query.clear_limits() - clone.query.combined_queries = (self.query,) + tuple( - qs.query for qs in other_qs - ) + clone.query.combined_queries = (self.query,) + tuple(qs.query for qs in other_qs) clone.query.combinator = combinator clone.query.combinator_all = all return clone @@ -1133,8 +1003,8 @@ class QuerySet: return self if len(qs) == 1: return qs[0] - return qs[0]._combinator_query("union", *qs[1:], all=all) - return self._combinator_query("union", *other_qs, all=all) + return qs[0]._combinator_query('union', *qs[1:], all=all) + return self._combinator_query('union', *other_qs, all=all) def intersection(self, *other_qs): # If any query is an EmptyQuerySet, return it. @@ -1143,13 +1013,13 @@ class QuerySet: for other in other_qs: if isinstance(other, EmptyQuerySet): return other - return self._combinator_query("intersection", *other_qs) + return self._combinator_query('intersection', *other_qs) def difference(self, *other_qs): # If the query is an EmptyQuerySet, return it. if isinstance(self, EmptyQuerySet): return self - return self._combinator_query("difference", *other_qs) + return self._combinator_query('difference', *other_qs) def select_for_update(self, nowait=False, skip_locked=False, of=(), no_key=False): """ @@ -1157,7 +1027,7 @@ class QuerySet: FOR UPDATE lock. """ if nowait and skip_locked: - raise ValueError("The nowait option cannot be used with skip_locked.") + raise ValueError('The nowait option cannot be used with skip_locked.') obj = self._chain() obj._for_write = True obj.query.select_for_update = True @@ -1176,11 +1046,9 @@ class QuerySet: If select_related(None) is called, clear the list. """ - self._not_support_combined_queries("select_related") + self._not_support_combined_queries('select_related') if self._fields is not None: - raise TypeError( - "Cannot call select_related() after .values() or .values_list()" - ) + raise TypeError("Cannot call select_related() after .values() or .values_list()") obj = self._chain() if fields == (None,): @@ -1200,7 +1068,7 @@ class QuerySet: When prefetch_related() is called more than once, append to the list of prefetch lookups. If prefetch_related(None) is called, clear the list. """ - self._not_support_combined_queries("prefetch_related") + self._not_support_combined_queries('prefetch_related') clone = self._chain() if lookups == (None,): clone._prefetch_related_lookups = () @@ -1210,9 +1078,7 @@ class QuerySet: lookup = lookup.prefetch_to lookup = lookup.split(LOOKUP_SEP, 1)[0] if lookup in self.query._filtered_relations: - raise ValueError( - "prefetch_related() is not supported with FilteredRelation." - ) + raise ValueError('prefetch_related() is not supported with FilteredRelation.') clone._prefetch_related_lookups = clone._prefetch_related_lookups + lookups return clone @@ -1221,29 +1087,26 @@ class QuerySet: Return a query set in which the returned objects have been annotated with extra data or aggregations. """ - self._not_support_combined_queries("annotate") + self._not_support_combined_queries('annotate') return self._annotate(args, kwargs, select=True) def alias(self, *args, **kwargs): """ Return a query set with added aliases for extra data or aggregations. """ - self._not_support_combined_queries("alias") + self._not_support_combined_queries('alias') return self._annotate(args, kwargs, select=False) def _annotate(self, args, kwargs, select=True): - self._validate_values_are_expressions( - args + tuple(kwargs.values()), method_name="annotate" - ) + self._validate_values_are_expressions(args + tuple(kwargs.values()), method_name='annotate') annotations = {} for arg in args: # The default_alias property may raise a TypeError. try: if arg.default_alias in kwargs: - raise ValueError( - "The named annotation '%s' conflicts with the " - "default name for another annotation." % arg.default_alias - ) + raise ValueError("The named annotation '%s' conflicts with the " + "default name for another annotation." + % arg.default_alias) except TypeError: raise TypeError("Complex annotations require an alias") annotations[arg.default_alias] = arg @@ -1252,29 +1115,20 @@ class QuerySet: clone = self._chain() names = self._fields if names is None: - names = set( - chain.from_iterable( - (field.name, field.attname) - if hasattr(field, "attname") - else (field.name,) - for field in self.model._meta.get_fields() - ) - ) + names = set(chain.from_iterable( + (field.name, field.attname) if hasattr(field, 'attname') else (field.name,) + for field in self.model._meta.get_fields() + )) for alias, annotation in annotations.items(): if alias in names: - raise ValueError( - "The annotation '%s' conflicts with a field on " - "the model." % alias - ) + raise ValueError("The annotation '%s' conflicts with a field on " + "the model." % alias) if isinstance(annotation, FilteredRelation): clone.query.add_filtered_relation(annotation, alias) else: clone.query.add_annotation( - annotation, - alias, - is_summary=False, - select=select, + annotation, alias, is_summary=False, select=select, ) for alias, annotation in clone.query.annotations.items(): if alias in annotations and annotation.contains_aggregate: @@ -1288,10 +1142,10 @@ class QuerySet: def order_by(self, *field_names): """Return a new QuerySet instance with the ordering changed.""" - if self.query.is_sliced: - raise TypeError("Cannot reorder a query once a slice has been taken.") + assert not self.query.is_sliced, \ + "Cannot reorder a query once a slice has been taken." obj = self._chain() - obj.query.clear_ordering(force=True, clear_default=False) + obj.query.clear_ordering(force_empty=False) obj.query.add_ordering(*field_names) return obj @@ -1299,28 +1153,19 @@ class QuerySet: """ Return a new QuerySet instance that will select only distinct results. """ - self._not_support_combined_queries("distinct") - if self.query.is_sliced: - raise TypeError( - "Cannot create distinct fields once a slice has been taken." - ) + self._not_support_combined_queries('distinct') + assert not self.query.is_sliced, \ + "Cannot create distinct fields once a slice has been taken." obj = self._chain() obj.query.add_distinct_fields(*field_names) return obj - def extra( - self, - select=None, - where=None, - params=None, - tables=None, - order_by=None, - select_params=None, - ): + def extra(self, select=None, where=None, params=None, tables=None, + order_by=None, select_params=None): """Add extra SQL fragments to the query.""" - self._not_support_combined_queries("extra") - if self.query.is_sliced: - raise TypeError("Cannot change a query once a slice has been taken.") + self._not_support_combined_queries('extra') + assert not self.query.is_sliced, \ + "Cannot change a query once a slice has been taken" clone = self._chain() clone.query.add_extra(select, select_params, where, params, tables, order_by) return clone @@ -1328,7 +1173,7 @@ class QuerySet: def reverse(self): """Reverse the ordering of the QuerySet.""" if self.query.is_sliced: - raise TypeError("Cannot reverse a query once a slice has been taken.") + raise TypeError('Cannot reverse a query once a slice has been taken.') clone = self._chain() clone.query.standard_ordering = not clone.query.standard_ordering return clone @@ -1340,7 +1185,7 @@ class QuerySet: The only exception to this is if None is passed in as the only parameter, in which case removal all deferrals. """ - self._not_support_combined_queries("defer") + self._not_support_combined_queries('defer') if self._fields is not None: raise TypeError("Cannot call defer() after .values() or .values_list()") clone = self._chain() @@ -1356,7 +1201,7 @@ class QuerySet: method and that are not already specified as deferred are loaded immediately when the queryset is evaluated. """ - self._not_support_combined_queries("only") + self._not_support_combined_queries('only') if self._fields is not None: raise TypeError("Cannot call only() after .values() or .values_list()") if fields == (None,): @@ -1366,7 +1211,7 @@ class QuerySet: for field in fields: field = field.split(LOOKUP_SEP, 1)[0] if field in self.query._filtered_relations: - raise ValueError("only() is not supported with FilteredRelation.") + raise ValueError('only() is not supported with FilteredRelation.') clone = self._chain() clone.query.add_immediate_loading(fields) return clone @@ -1392,9 +1237,8 @@ class QuerySet: if self.query.extra_order_by or self.query.order_by: return True elif ( - self.query.default_ordering - and self.query.get_meta().ordering - and + self.query.default_ordering and + self.query.get_meta().ordering and # A default ordering doesn't affect GROUP BY queries. not self.query.group_by ): @@ -1413,15 +1257,7 @@ class QuerySet: # PRIVATE METHODS # ################### - def _insert( - self, - objs, - fields, - returning_fields=None, - raw=False, - using=None, - ignore_conflicts=False, - ): + def _insert(self, objs, fields, returning_fields=None, raw=False, using=None, ignore_conflicts=False): """ Insert a new record for the given model. This provides an interface to the InsertQuery class and is how Model.save() is implemented. @@ -1432,7 +1268,6 @@ class QuerySet: query = sql.InsertQuery(self.model, ignore_conflicts=ignore_conflicts) query.insert_values(fields, objs, raw=raw) return query.get_compiler(using=using).execute_sql(returning_fields) - _insert.alters_data = True _insert.queryset_only = False @@ -1440,39 +1275,25 @@ class QuerySet: """ Helper method for bulk_create() to insert objs one batch at a time. """ - if ( - ignore_conflicts - and not connections[self.db].features.supports_ignore_conflicts - ): - raise NotSupportedError( - "This database backend does not support ignoring conflicts." - ) + if ignore_conflicts and not connections[self.db].features.supports_ignore_conflicts: + raise NotSupportedError('This database backend does not support ignoring conflicts.') ops = connections[self.db].ops max_batch_size = max(ops.bulk_batch_size(fields, objs), 1) batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size inserted_rows = [] bulk_return = connections[self.db].features.can_return_rows_from_bulk_insert - for item in [objs[i : i + batch_size] for i in range(0, len(objs), batch_size)]: + for item in [objs[i:i + batch_size] for i in range(0, len(objs), batch_size)]: if bulk_return and not ignore_conflicts: - inserted_rows.extend( - self._insert( - item, - fields=fields, - using=self.db, - returning_fields=self.model._meta.db_returning_fields, - ignore_conflicts=ignore_conflicts, - ) - ) - else: - self._insert( - item, - fields=fields, - using=self.db, + inserted_rows.extend(self._insert( + item, fields=fields, using=self.db, + returning_fields=self.model._meta.db_returning_fields, ignore_conflicts=ignore_conflicts, - ) + )) + else: + self._insert(item, fields=fields, using=self.db, ignore_conflicts=ignore_conflicts) return inserted_rows - def _chain(self): + def _chain(self, **kwargs): """ Return a copy of the current QuerySet that's ready for another operation. @@ -1481,6 +1302,7 @@ class QuerySet: if obj._sticky_filter: obj.query.filter_is_sticky = True obj._sticky_filter = False + obj.__dict__.update(kwargs) return obj def _clone(self): @@ -1488,12 +1310,7 @@ class QuerySet: Return a copy of the current QuerySet. A lightweight alternative to deepcopy(). """ - c = self.__class__( - model=self.model, - query=self.query.chain(), - using=self._db, - hints=self._hints, - ) + c = self.__class__(model=self.model, query=self.query.chain(), using=self._db, hints=self._hints) c._sticky_filter = self._sticky_filter c._for_write = self._for_write c._prefetch_related_lookups = self._prefetch_related_lookups[:] @@ -1525,10 +1342,9 @@ class QuerySet: def _merge_sanity_check(self, other): """Check that two QuerySet classes may be merged.""" if self._fields is not None and ( - set(self.query.values_select) != set(other.query.values_select) - or set(self.query.extra_select) != set(other.query.extra_select) - or set(self.query.annotation_select) != set(other.query.annotation_select) - ): + set(self.query.values_select) != set(other.query.values_select) or + set(self.query.extra_select) != set(other.query.extra_select) or + set(self.query.annotation_select) != set(other.query.annotation_select)): raise TypeError( "Merging '%s' classes must involve the same values in each case." % self.__class__.__name__ @@ -1545,11 +1361,10 @@ class QuerySet: if self._fields and len(self._fields) > 1: # values() queryset can only be used as nested queries # if they are set up to select only a single field. - raise TypeError("Cannot use multi-field values as a filter value.") + raise TypeError('Cannot use multi-field values as a filter value.') query = self.query.resolve_expression(*args, **kwargs) query._db = self._db return query - resolve_expression.queryset_only = True def _add_hints(self, **hints): @@ -1569,22 +1384,19 @@ class QuerySet: @staticmethod def _validate_values_are_expressions(values, method_name): - invalid_args = sorted( - str(arg) for arg in values if not hasattr(arg, "resolve_expression") - ) + invalid_args = sorted(str(arg) for arg in values if not hasattr(arg, 'resolve_expression')) if invalid_args: raise TypeError( - "QuerySet.%s() received non-expression(s): %s." - % ( + 'QuerySet.%s() received non-expression(s): %s.' % ( method_name, - ", ".join(invalid_args), + ', '.join(invalid_args), ) ) def _not_support_combined_queries(self, operation_name): if self.query.combinator: raise NotSupportedError( - "Calling QuerySet.%s() after %s() is not supported." + 'Calling QuerySet.%s() after %s() is not supported.' % (operation_name, self.query.combinator) ) @@ -1609,17 +1421,8 @@ class RawQuerySet: Provide an iterator which converts the results of raw SQL queries into annotated model instances. """ - - def __init__( - self, - raw_query, - model=None, - query=None, - params=(), - translations=None, - using=None, - hints=None, - ): + def __init__(self, raw_query, model=None, query=None, params=(), + translations=None, using=None, hints=None): self.raw_query = raw_query self.model = model self._db = using @@ -1634,17 +1437,10 @@ class RawQuerySet: def resolve_model_init_order(self): """Resolve the init field names and value positions.""" converter = connections[self.db].introspection.identifier_converter - model_init_fields = [ - f for f in self.model._meta.fields if converter(f.column) in self.columns - ] - annotation_fields = [ - (column, pos) - for pos, column in enumerate(self.columns) - if column not in self.model_fields - ] - model_init_order = [ - self.columns.index(converter(f.column)) for f in model_init_fields - ] + model_init_fields = [f for f in self.model._meta.fields if converter(f.column) in self.columns] + annotation_fields = [(column, pos) for pos, column in enumerate(self.columns) + if column not in self.model_fields] + model_init_order = [self.columns.index(converter(f.column)) for f in model_init_fields] model_init_names = [f.attname for f in model_init_fields] return model_init_names, model_init_order, annotation_fields @@ -1664,13 +1460,8 @@ class RawQuerySet: def _clone(self): """Same as QuerySet._clone()""" c = self.__class__( - self.raw_query, - model=self.model, - query=self.query, - params=self.params, - translations=self.translations, - using=self._db, - hints=self._hints, + self.raw_query, model=self.model, query=self.query, params=self.params, + translations=self.translations, using=self._db, hints=self._hints ) c._prefetch_related_lookups = self._prefetch_related_lookups[:] return c @@ -1696,27 +1487,23 @@ class RawQuerySet: def iterator(self): # Cache some things for performance reasons outside the loop. db = self.db - compiler = connections[db].ops.compiler("SQLCompiler")( + compiler = connections[db].ops.compiler('SQLCompiler')( self.query, connections[db], db ) query = iter(self.query) try: - ( - model_init_names, - model_init_pos, - annotation_fields, - ) = self.resolve_model_init_order() + model_init_names, model_init_pos, annotation_fields = self.resolve_model_init_order() if self.model._meta.pk.attname not in model_init_names: raise exceptions.FieldDoesNotExist( - "Raw query must include the primary key" + 'Raw query must include the primary key' ) model_cls = self.model fields = [self.model_fields.get(c) for c in self.columns] - converters = compiler.get_converters( - [f.get_col(f.model._meta.db_table) if f else None for f in fields] - ) + converters = compiler.get_converters([ + f.get_col(f.model._meta.db_table) if f else None for f in fields + ]) if converters: query = compiler.apply_converters(query, converters) for values in query: @@ -1729,7 +1516,7 @@ class RawQuerySet: yield instance finally: # Done iterating the Query. If it has its own cursor, close it. - if hasattr(self.query, "cursor") and self.query.cursor: + if hasattr(self.query, 'cursor') and self.query.cursor: self.query.cursor.close() def __repr__(self): @@ -1746,11 +1533,9 @@ class RawQuerySet: def using(self, alias): """Select the database this RawQuerySet should execute against.""" return RawQuerySet( - self.raw_query, - model=self.model, + self.raw_query, model=self.model, query=self.query.chain(using=alias), - params=self.params, - translations=self.translations, + params=self.params, translations=self.translations, using=alias, ) @@ -1790,19 +1575,17 @@ class Prefetch: # `prefetch_to` is the path to the attribute that stores the result. self.prefetch_to = lookup if queryset is not None and ( - isinstance(queryset, RawQuerySet) - or ( - hasattr(queryset, "_iterable_class") - and not issubclass(queryset._iterable_class, ModelIterable) + isinstance(queryset, RawQuerySet) or ( + hasattr(queryset, '_iterable_class') and + not issubclass(queryset._iterable_class, ModelIterable) ) ): raise ValueError( - "Prefetch querysets cannot use raw(), values(), and values_list()." + 'Prefetch querysets cannot use raw(), values(), and ' + 'values_list().' ) if to_attr: - self.prefetch_to = LOOKUP_SEP.join( - lookup.split(LOOKUP_SEP)[:-1] + [to_attr] - ) + self.prefetch_to = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[:-1] + [to_attr]) self.queryset = queryset self.to_attr = to_attr @@ -1810,11 +1593,11 @@ class Prefetch: def __getstate__(self): obj_dict = self.__dict__.copy() if self.queryset is not None: - queryset = self.queryset._chain() # Prevent the QuerySet from being evaluated - queryset._result_cache = [] - queryset._prefetch_done = True - obj_dict["queryset"] = queryset + obj_dict['queryset'] = self.queryset._chain( + _result_cache=[], + _prefetch_done=True, + ) return obj_dict def add_prefix(self, prefix): @@ -1822,7 +1605,7 @@ class Prefetch: self.prefetch_to = prefix + LOOKUP_SEP + self.prefetch_to def get_current_prefetch_to(self, level): - return LOOKUP_SEP.join(self.prefetch_to.split(LOOKUP_SEP)[: level + 1]) + return LOOKUP_SEP.join(self.prefetch_to.split(LOOKUP_SEP)[:level + 1]) def get_current_to_attr(self, level): parts = self.prefetch_to.split(LOOKUP_SEP) @@ -1867,7 +1650,7 @@ def prefetch_related_objects(model_instances, *related_lookups): # We need to be able to dynamically add to the list of prefetch_related # lookups that we look up (see below). So we need some book keeping to # ensure we don't do duplicate work. - done_queries = {} # dictionary of things like 'foo__bar': [results] + done_queries = {} # dictionary of things like 'foo__bar': [results] auto_lookups = set() # we add to this as we go through. followed_descriptors = set() # recursion protection @@ -1877,11 +1660,8 @@ def prefetch_related_objects(model_instances, *related_lookups): lookup = all_lookups.pop() if lookup.prefetch_to in done_queries: if lookup.queryset is not None: - raise ValueError( - "'%s' lookup was already seen with a different queryset. " - "You may need to adjust the ordering of your lookups." - % lookup.prefetch_to - ) + raise ValueError("'%s' lookup was already seen with a different queryset. " + "You may need to adjust the ordering of your lookups." % lookup.prefetch_to) continue @@ -1907,7 +1687,7 @@ def prefetch_related_objects(model_instances, *related_lookups): # Since prefetching can re-use instances, it is possible to have # the same instance multiple times in obj_list, so obj might # already be prepared. - if not hasattr(obj, "_prefetched_objects_cache"): + if not hasattr(obj, '_prefetched_objects_cache'): try: obj._prefetched_objects_cache = {} except (AttributeError, TypeError): @@ -1927,30 +1707,20 @@ def prefetch_related_objects(model_instances, *related_lookups): # of prefetch_related), so what applies to first object applies to all. first_obj = obj_list[0] to_attr = lookup.get_current_to_attr(level)[0] - prefetcher, descriptor, attr_found, is_fetched = get_prefetcher( - first_obj, through_attr, to_attr - ) + prefetcher, descriptor, attr_found, is_fetched = get_prefetcher(first_obj, through_attr, to_attr) if not attr_found: - raise AttributeError( - "Cannot find '%s' on %s object, '%s' is an invalid " - "parameter to prefetch_related()" - % ( - through_attr, - first_obj.__class__.__name__, - lookup.prefetch_through, - ) - ) + raise AttributeError("Cannot find '%s' on %s object, '%s' is an invalid " + "parameter to prefetch_related()" % + (through_attr, first_obj.__class__.__name__, lookup.prefetch_through)) if level == len(through_attrs) - 1 and prefetcher is None: # Last one, this *must* resolve to something that supports # prefetching, otherwise there is no point adding it and the # developer asking for it has made a mistake. - raise ValueError( - "'%s' does not resolve to an item that supports " - "prefetching - this is an invalid parameter to " - "prefetch_related()." % lookup.prefetch_through - ) + raise ValueError("'%s' does not resolve to an item that supports " + "prefetching - this is an invalid parameter to " + "prefetch_related()." % lookup.prefetch_through) obj_to_fetch = None if prefetcher is not None: @@ -1967,15 +1737,9 @@ def prefetch_related_objects(model_instances, *related_lookups): # same relationships to stop infinite recursion. So, if we # are already on an automatically added lookup, don't add # the new lookups from relationships we've seen already. - if not ( - prefetch_to in done_queries - and lookup in auto_lookups - and descriptor in followed_descriptors - ): + if not (prefetch_to in done_queries and lookup in auto_lookups and descriptor in followed_descriptors): done_queries[prefetch_to] = obj_list - new_lookups = normalize_prefetch_lookups( - reversed(additional_lookups), prefetch_to - ) + new_lookups = normalize_prefetch_lookups(reversed(additional_lookups), prefetch_to) auto_lookups.update(new_lookups) all_lookups.extend(new_lookups) followed_descriptors.add(descriptor) @@ -1989,7 +1753,7 @@ def prefetch_related_objects(model_instances, *related_lookups): # that we can continue with nullable or reverse relations. new_obj_list = [] for obj in obj_list: - if through_attr in getattr(obj, "_prefetched_objects_cache", ()): + if through_attr in getattr(obj, '_prefetched_objects_cache', ()): # If related objects have been prefetched, use the # cache rather than the object's through_attr. new_obj = list(obj._prefetched_objects_cache.get(through_attr)) @@ -2021,7 +1785,6 @@ def get_prefetcher(instance, through_attr, to_attr): a function that takes an instance and returns a boolean that is True if the attribute has already been fetched for that instance) """ - def has_to_attr_attribute(instance): return hasattr(instance, to_attr) @@ -2039,7 +1802,7 @@ def get_prefetcher(instance, through_attr, to_attr): if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_queryset() method. - if hasattr(rel_obj_descriptor, "get_prefetch_queryset"): + if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'): prefetcher = rel_obj_descriptor is_fetched = rel_obj_descriptor.is_cached else: @@ -2047,21 +1810,17 @@ def get_prefetcher(instance, through_attr, to_attr): # the attribute on the instance rather than the class to # support many related managers rel_obj = getattr(instance, through_attr) - if hasattr(rel_obj, "get_prefetch_queryset"): + if hasattr(rel_obj, 'get_prefetch_queryset'): prefetcher = rel_obj if through_attr != to_attr: # Special case cached_property instances because hasattr # triggers attribute computation and assignment. - if isinstance( - getattr(instance.__class__, to_attr, None), cached_property - ): - + if isinstance(getattr(instance.__class__, to_attr, None), cached_property): def has_cached_property(instance): return to_attr in instance.__dict__ is_fetched = has_cached_property else: - def in_prefetched_cache(instance): return through_attr in instance._prefetched_objects_cache @@ -2092,14 +1851,8 @@ def prefetch_one_level(instances, prefetcher, lookup, level): # The 'values to be matched' must be hashable as they will be used # in a dictionary. - ( - rel_qs, - rel_obj_attr, - instance_attr, - single, - cache_name, - is_descriptor, - ) = prefetcher.get_prefetch_queryset(instances, lookup.get_current_queryset(level)) + rel_qs, rel_obj_attr, instance_attr, single, cache_name, is_descriptor = ( + prefetcher.get_prefetch_queryset(instances, lookup.get_current_queryset(level))) # We have to handle the possibility that the QuerySet we just got back # contains some prefetch_related lookups. We don't want to trigger the # prefetch_related functionality by evaluating the query. Rather, we need @@ -2107,8 +1860,8 @@ def prefetch_one_level(instances, prefetcher, lookup, level): # Copy the lookups in case it is a Prefetch object which could be reused # later (happens in nested prefetch_related). additional_lookups = [ - copy.copy(additional_lookup) - for additional_lookup in getattr(rel_qs, "_prefetch_related_lookups", ()) + copy.copy(additional_lookup) for additional_lookup + in getattr(rel_qs, '_prefetch_related_lookups', ()) ] if additional_lookups: # Don't need to clone because the manager should have given us a fresh @@ -2134,7 +1887,7 @@ def prefetch_one_level(instances, prefetcher, lookup, level): except exceptions.FieldDoesNotExist: pass else: - msg = "to_attr={} conflicts with a field on the {} model." + msg = 'to_attr={} conflicts with a field on the {} model.' raise ValueError(msg.format(to_attr, model.__name__)) # Whether or not we're prefetching the last part of the lookup. @@ -2190,7 +1943,6 @@ class RelatedPopulator: method gets row and from_obj as input and populates the select_related() model instance. """ - def __init__(self, klass_info, select, db): self.db = db # Pre-compute needed attributes. The attributes are: @@ -2216,40 +1968,32 @@ class RelatedPopulator: # - local_setter, remote_setter: Methods to set cached values on # the object being populated and on the remote object. Usually # these are Field.set_cached_value() methods. - select_fields = klass_info["select_fields"] - from_parent = klass_info["from_parent"] + select_fields = klass_info['select_fields'] + from_parent = klass_info['from_parent'] if not from_parent: self.cols_start = select_fields[0] self.cols_end = select_fields[-1] + 1 self.init_list = [ - f[0].target.attname for f in select[self.cols_start : self.cols_end] + f[0].target.attname for f in select[self.cols_start:self.cols_end] ] self.reorder_for_init = None else: - attname_indexes = { - select[idx][0].target.attname: idx for idx in select_fields - } - model_init_attnames = ( - f.attname for f in klass_info["model"]._meta.concrete_fields - ) - self.init_list = [ - attname for attname in model_init_attnames if attname in attname_indexes - ] - self.reorder_for_init = operator.itemgetter( - *[attname_indexes[attname] for attname in self.init_list] - ) + attname_indexes = {select[idx][0].target.attname: idx for idx in select_fields} + model_init_attnames = (f.attname for f in klass_info['model']._meta.concrete_fields) + self.init_list = [attname for attname in model_init_attnames if attname in attname_indexes] + self.reorder_for_init = operator.itemgetter(*[attname_indexes[attname] for attname in self.init_list]) - self.model_cls = klass_info["model"] + self.model_cls = klass_info['model'] self.pk_idx = self.init_list.index(self.model_cls._meta.pk.attname) self.related_populators = get_related_populators(klass_info, select, self.db) - self.local_setter = klass_info["local_setter"] - self.remote_setter = klass_info["remote_setter"] + self.local_setter = klass_info['local_setter'] + self.remote_setter = klass_info['remote_setter'] def populate(self, row, from_obj): if self.reorder_for_init: obj_data = self.reorder_for_init(row) else: - obj_data = row[self.cols_start : self.cols_end] + obj_data = row[self.cols_start:self.cols_end] if obj_data[self.pk_idx] is None: obj = None else: @@ -2263,7 +2007,7 @@ class RelatedPopulator: def get_related_populators(klass_info, select, db): iterators = [] - related_klass_infos = klass_info.get("related_klass_infos", []) + related_klass_infos = klass_info.get('related_klass_infos', []) for rel_klass_info in related_klass_infos: rel_cls = RelatedPopulator(rel_klass_info, select, db) iterators.append(rel_cls) diff --git a/venv/Lib/site-packages/django/db/models/query_utils.py b/venv/Lib/site-packages/django/db/models/query_utils.py index 6ea82b6..e8ad7cd 100644 --- a/venv/Lib/site-packages/django/db/models/query_utils.py +++ b/venv/Lib/site-packages/django/db/models/query_utils.py @@ -8,19 +8,44 @@ circular import difficulties. import copy import functools import inspect +import warnings from collections import namedtuple -from django.core.exceptions import FieldError +from django.core.exceptions import FieldDoesNotExist, FieldError from django.db.models.constants import LOOKUP_SEP from django.utils import tree +from django.utils.deprecation import RemovedInDjango40Warning # PathInfo is used when converting lookups (fk__somecol). The contents # describe the relation in Model terms (model Options and Fields for both # sides of the relation. The join_field is the field backing the relation. -PathInfo = namedtuple( - "PathInfo", - "from_opts to_opts target_fields join_field m2m direct filtered_relation", -) +PathInfo = namedtuple('PathInfo', 'from_opts to_opts target_fields join_field m2m direct filtered_relation') + + +class InvalidQueryType(type): + @property + def _subclasses(self): + return (FieldDoesNotExist, FieldError) + + def __warn(self): + warnings.warn( + 'The InvalidQuery exception class is deprecated. Use ' + 'FieldDoesNotExist or FieldError instead.', + category=RemovedInDjango40Warning, + stacklevel=4, + ) + + def __instancecheck__(self, instance): + self.__warn() + return isinstance(instance, self._subclasses) or super().__instancecheck__(instance) + + def __subclasscheck__(self, subclass): + self.__warn() + return issubclass(subclass, self._subclasses) or super().__subclasscheck__(subclass) + + +class InvalidQuery(Exception, metaclass=InvalidQueryType): + pass def subclasses(cls): @@ -34,26 +59,21 @@ class Q(tree.Node): Encapsulate filters as objects that can then be combined logically (using `&` and `|`). """ - # Connection types - AND = "AND" - OR = "OR" + AND = 'AND' + OR = 'OR' default = AND conditional = True def __init__(self, *args, _connector=None, _negated=False, **kwargs): - super().__init__( - children=[*args, *sorted(kwargs.items())], - connector=_connector, - negated=_negated, - ) + super().__init__(children=[*args, *sorted(kwargs.items())], connector=_connector, negated=_negated) def _combine(self, other, conn): - if not (isinstance(other, Q) or getattr(other, "conditional", False) is True): + if not(isinstance(other, Q) or getattr(other, 'conditional', False) is True): raise TypeError(other) if not self: - return other.copy() if hasattr(other, "copy") else copy.copy(other) + return other.copy() if hasattr(other, 'copy') else copy.copy(other) elif isinstance(other, Q) and not other: _, args, kwargs = self.deconstruct() return type(self)(*args, **kwargs) @@ -76,31 +96,26 @@ class Q(tree.Node): obj.negate() return obj - def resolve_expression( - self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False - ): + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): # We must promote any new joins to left outer joins so that when Q is # used as an expression, rows aren't filtered due to joins. clause, joins = query._add_q( - self, - reuse, - allow_joins=allow_joins, - split_subq=False, + self, reuse, allow_joins=allow_joins, split_subq=False, check_filterable=False, ) query.promote_joins(joins) return clause def deconstruct(self): - path = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) - if path.startswith("django.db.models.query_utils"): - path = path.replace("django.db.models.query_utils", "django.db.models") + path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) + if path.startswith('django.db.models.query_utils'): + path = path.replace('django.db.models.query_utils', 'django.db.models') args = tuple(self.children) kwargs = {} if self.connector != self.default: - kwargs["_connector"] = self.connector + kwargs['_connector'] = self.connector if self.negated: - kwargs["_negated"] = True + kwargs['_negated'] = True return path, args, kwargs @@ -109,7 +124,6 @@ class DeferredAttribute: A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed. """ - def __init__(self, field): self.field = field @@ -146,6 +160,7 @@ class DeferredAttribute: class RegisterLookupMixin: + @classmethod def _get_lookup(cls, lookup_name): return cls.get_lookups().get(lookup_name, None) @@ -153,16 +168,13 @@ class RegisterLookupMixin: @classmethod @functools.lru_cache(maxsize=None) def get_lookups(cls): - class_lookups = [ - parent.__dict__.get("class_lookups", {}) for parent in inspect.getmro(cls) - ] + class_lookups = [parent.__dict__.get('class_lookups', {}) for parent in inspect.getmro(cls)] return cls.merge_dicts(class_lookups) def get_lookup(self, lookup_name): from django.db.models.lookups import Lookup - found = self._get_lookup(lookup_name) - if found is None and hasattr(self, "output_field"): + if found is None and hasattr(self, 'output_field'): return self.output_field.get_lookup(lookup_name) if found is not None and not issubclass(found, Lookup): return None @@ -170,9 +182,8 @@ class RegisterLookupMixin: def get_transform(self, lookup_name): from django.db.models.lookups import Transform - found = self._get_lookup(lookup_name) - if found is None and hasattr(self, "output_field"): + if found is None and hasattr(self, 'output_field'): return self.output_field.get_transform(lookup_name) if found is not None and not issubclass(found, Transform): return None @@ -198,7 +209,7 @@ class RegisterLookupMixin: def register_lookup(cls, lookup, lookup_name=None): if lookup_name is None: lookup_name = lookup.lookup_name - if "class_lookups" not in cls.__dict__: + if 'class_lookups' not in cls.__dict__: cls.class_lookups = {} cls.class_lookups[lookup_name] = lookup cls._clear_cached_lookups() @@ -245,8 +256,8 @@ def select_related_descend(field, restricted, requested, load_fields, reverse=Fa if field.attname not in load_fields: if restricted and field.name in requested: msg = ( - "Field %s.%s cannot be both deferred and traversed using " - "select_related at the same time." + 'Field %s.%s cannot be both deferred and traversed using ' + 'select_related at the same time.' ) % (field.model._meta.object_name, field.name) raise FieldError(msg) return True @@ -272,14 +283,12 @@ def check_rel_lookup_compatibility(model, target_opts, field): 1) model and opts match (where proxy inheritance is removed) 2) model is parent of opts' model or the other way around """ - def check(opts): return ( - model._meta.concrete_model == opts.concrete_model - or opts.concrete_model in model._meta.get_parent_list() - or model in opts.get_parent_list() + model._meta.concrete_model == opts.concrete_model or + opts.concrete_model in model._meta.get_parent_list() or + model in opts.get_parent_list() ) - # If the field is a primary key, then doing a query against the field's # model is ok, too. Consider the case: # class Restaurant(models.Model): @@ -289,8 +298,9 @@ def check_rel_lookup_compatibility(model, target_opts, field): # give Place's opts as the target opts, but Restaurant isn't compatible # with that. This logic applies only to primary keys, as when doing __in=qs, # we are going to turn this into __in=qs.values('pk') later on. - return check(target_opts) or ( - getattr(field, "primary_key", False) and check(field.model._meta) + return ( + check(target_opts) or + (getattr(field, 'primary_key', False) and check(field.model._meta)) ) @@ -299,11 +309,11 @@ class FilteredRelation: def __init__(self, relation_name, *, condition=Q()): if not relation_name: - raise ValueError("relation_name cannot be empty.") + raise ValueError('relation_name cannot be empty.') self.relation_name = relation_name self.alias = None if not isinstance(condition, Q): - raise ValueError("condition argument must be a Q() instance.") + raise ValueError('condition argument must be a Q() instance.') self.condition = condition self.path = [] @@ -311,9 +321,9 @@ class FilteredRelation: if not isinstance(other, self.__class__): return NotImplemented return ( - self.relation_name == other.relation_name - and self.alias == other.alias - and self.condition == other.condition + self.relation_name == other.relation_name and + self.alias == other.alias and + self.condition == other.condition ) def clone(self): @@ -327,7 +337,7 @@ class FilteredRelation: QuerySet.annotate() only accepts expression-like arguments (with a resolve_expression() method). """ - raise NotImplementedError("FilteredRelation.resolve_expression() is unused.") + raise NotImplementedError('FilteredRelation.resolve_expression() is unused.') def as_sql(self, compiler, connection): # Resolve the condition in Join.filtered_relation. diff --git a/venv/Lib/site-packages/django/db/models/signals.py b/venv/Lib/site-packages/django/db/models/signals.py index a072093..d14eaaf 100644 --- a/venv/Lib/site-packages/django/db/models/signals.py +++ b/venv/Lib/site-packages/django/db/models/signals.py @@ -11,7 +11,6 @@ class ModelSignal(Signal): Signal subclass that allows the sender to be lazily specified as a string of the `app_label.ModelName` form. """ - def _lazy_method(self, method, apps, receiver, sender, **kwargs): from django.db.models.options import Options @@ -25,12 +24,8 @@ class ModelSignal(Signal): def connect(self, receiver, sender=None, weak=True, dispatch_uid=None, apps=None): self._lazy_method( - super().connect, - apps, - receiver, - sender, - weak=weak, - dispatch_uid=dispatch_uid, + super().connect, apps, receiver, sender, + weak=weak, dispatch_uid=dispatch_uid, ) def disconnect(self, receiver=None, sender=None, dispatch_uid=None, apps=None): diff --git a/venv/Lib/site-packages/django/db/models/sql/__init__.py b/venv/Lib/site-packages/django/db/models/sql/__init__.py index 2956e04..5fa52f6 100644 --- a/venv/Lib/site-packages/django/db/models/sql/__init__.py +++ b/venv/Lib/site-packages/django/db/models/sql/__init__.py @@ -3,4 +3,4 @@ from django.db.models.sql.query import Query from django.db.models.sql.subqueries import * # NOQA from django.db.models.sql.where import AND, OR -__all__ = ["Query", "AND", "OR"] +__all__ = ['Query', 'AND', 'OR'] diff --git a/venv/Lib/site-packages/django/db/models/sql/compiler.py b/venv/Lib/site-packages/django/db/models/sql/compiler.py index 05a2e43..6254946 100644 --- a/venv/Lib/site-packages/django/db/models/sql/compiler.py +++ b/venv/Lib/site-packages/django/db/models/sql/compiler.py @@ -1,5 +1,4 @@ import collections -import json import re from functools import partial from itertools import chain @@ -9,14 +8,9 @@ from django.db import DatabaseError, NotSupportedError from django.db.models.constants import LOOKUP_SEP from django.db.models.expressions import F, OrderBy, RawSQL, Ref, Value from django.db.models.functions import Cast, Random -from django.db.models.query_utils import select_related_descend +from django.db.models.query_utils import Q, select_related_descend from django.db.models.sql.constants import ( - CURSOR, - GET_ITERATOR_CHUNK_SIZE, - MULTI, - NO_RESULTS, - ORDER_DIR, - SINGLE, + CURSOR, GET_ITERATOR_CHUNK_SIZE, MULTI, NO_RESULTS, ORDER_DIR, SINGLE, ) from django.db.models.sql.query import Query, get_order_dir from django.db.transaction import TransactionManagementError @@ -28,18 +22,15 @@ from django.utils.regex_helper import _lazy_re_compile class SQLCompiler: # Multiline ordering SQL clause may appear from RawSQL. ordering_parts = _lazy_re_compile( - r"^(.*)\s(?:ASC|DESC).*", + r'^(.*)\s(?:ASC|DESC).*', re.MULTILINE | re.DOTALL, ) - def __init__(self, query, connection, using, elide_empty=True): + def __init__(self, query, connection, using): self.query = query self.connection = connection self.using = using - # Some queries, e.g. coalesced aggregation, need to be executed even if - # they would return an empty result set. - self.elide_empty = elide_empty - self.quote_cache = {"*": "*"} + self.quote_cache = {'*': '*'} # The select, klass_info, and annotations are needed by QuerySet.iterator() # these are set as a side-effect of executing the query. Note that we calculate # separately a list of extra select columns needed for grammatical correctness @@ -87,8 +78,7 @@ class SQLCompiler: # SomeModel.objects.annotate(Count('somecol')).values('name') # GROUP BY: all cols of the model # - # SomeModel.objects.values('name', 'pk') - # .annotate(Count('somecol')).values('pk') + # SomeModel.objects.values('name', 'pk').annotate(Count('somecol')).values('pk') # GROUP BY: name, pk # # SomeModel.objects.values('name').annotate(Count('somecol')).values('pk') @@ -117,14 +107,16 @@ class SQLCompiler: # when we have public API way of forcing the GROUP BY clause. # Converts string references to expressions. for expr in self.query.group_by: - if not hasattr(expr, "as_sql"): + if not hasattr(expr, 'as_sql'): expressions.append(self.query.resolve_ref(expr)) else: expressions.append(expr) # Note that even if the group_by is set, it is only the minimal # set to group by. So, we need to add cols in select, order_by, and # having into the select in any case. - ref_sources = {expr.source for expr in expressions if isinstance(expr, Ref)} + ref_sources = { + expr.source for expr in expressions if isinstance(expr, Ref) + } for expr, _, _ in select: # Skip members of the select clause that are already included # by reference. @@ -133,12 +125,11 @@ class SQLCompiler: cols = expr.get_group_by_cols() for col in cols: expressions.append(col) - if not self._meta_ordering: - for expr, (sql, params, is_ref) in order_by: - # Skip references to the SELECT clause, as all expressions in - # the SELECT clause are already part of the GROUP BY. - if not is_ref: - expressions.extend(expr.get_group_by_cols()) + for expr, (sql, params, is_ref) in order_by: + # Skip References to the select clause, as all expressions in the + # select clause are already part of the group by. + if not is_ref: + expressions.extend(expr.get_group_by_cols()) having_group_by = self.having.get_group_by_cols() if self.having else () for expr in having_group_by: expressions.append(expr) @@ -166,10 +157,8 @@ class SQLCompiler: for expr in expressions: # Is this a reference to query's base table primary key? If the # expression isn't a Col-like, then skip the expression. - if ( - getattr(expr, "target", None) == self.query.model._meta.pk - and getattr(expr, "alias", None) == self.query.base_table - ): + if (getattr(expr, 'target', None) == self.query.model._meta.pk and + getattr(expr, 'alias', None) == self.query.base_table): pk = expr break # If the main model's primary key is in the query, group by that @@ -177,17 +166,13 @@ class SQLCompiler: # that don't have a primary key included in the grouped columns. if pk: pk_aliases = { - expr.alias - for expr in expressions - if hasattr(expr, "target") and expr.target.primary_key + expr.alias for expr in expressions + if hasattr(expr, 'target') and expr.target.primary_key } expressions = [pk] + [ - expr - for expr in expressions - if expr in having - or ( - getattr(expr, "alias", None) is not None - and expr.alias not in pk_aliases + expr for expr in expressions + if expr in having or ( + getattr(expr, 'alias', None) is not None and expr.alias not in pk_aliases ) ] elif self.connection.features.allows_group_by_selected_pks: @@ -198,21 +183,16 @@ class SQLCompiler: # Unmanaged models are excluded because they could be representing # database views on which the optimization might not be allowed. pks = { - expr - for expr in expressions + expr for expr in expressions if ( - hasattr(expr, "target") - and expr.target.primary_key - and self.connection.features.allows_group_by_selected_pks_on_model( - expr.target.model - ) + hasattr(expr, 'target') and + expr.target.primary_key and + self.connection.features.allows_group_by_selected_pks_on_model(expr.target.model) ) } aliases = {expr.alias for expr in pks} expressions = [ - expr - for expr in expressions - if expr in pks or getattr(expr, "alias", None) not in aliases + expr for expr in expressions if expr in pks or getattr(expr, 'alias', None) not in aliases ] return expressions @@ -256,8 +236,8 @@ class SQLCompiler: select.append((col, None)) select_idx += 1 klass_info = { - "model": self.query.model, - "select_fields": select_list, + 'model': self.query.model, + 'select_fields': select_list, } for alias, annotation in self.query.annotation_select.items(): annotations[alias] = select_idx @@ -266,16 +246,14 @@ class SQLCompiler: if self.query.select_related: related_klass_infos = self.get_related_selections(select) - klass_info["related_klass_infos"] = related_klass_infos + klass_info['related_klass_infos'] = related_klass_infos def get_select_from_parent(klass_info): - for ki in klass_info["related_klass_infos"]: - if ki["from_parent"]: - ki["select_fields"] = ( - klass_info["select_fields"] + ki["select_fields"] - ) + for ki in klass_info['related_klass_infos']: + if ki['from_parent']: + ki['select_fields'] = (klass_info['select_fields'] + + ki['select_fields']) get_select_from_parent(ki) - get_select_from_parent(klass_info) ret = [] @@ -283,20 +261,22 @@ class SQLCompiler: try: sql, params = self.compile(col) except EmptyResultSet: - empty_result_set_value = getattr( - col, "empty_result_set_value", NotImplemented - ) - if empty_result_set_value is NotImplemented: - # Select a predicate that's always False. - sql, params = "0", () - else: - sql, params = self.compile(Value(empty_result_set_value)) + # Select a predicate that's always False. + sql, params = '0', () else: sql, params = col.select_format(self, sql, params) ret.append((col, (sql, params), alias)) return ret, klass_info, annotations - def _order_by_pairs(self): + def get_order_by(self): + """ + Return a list of 2-tuples of form (expr, (sql, params, is_ref)) for the + ORDER BY clause. + + The order_by clause can alter the select clause (for example it + can add aliases to clauses that do not yet have one, or it can + add totally new select clauses). + """ if self.query.extra_order_by: ordering = self.query.extra_order_by elif not self.query.default_ordering: @@ -309,12 +289,13 @@ class SQLCompiler: else: ordering = [] if self.query.standard_ordering: - default_order, _ = ORDER_DIR["ASC"] + asc, desc = ORDER_DIR['ASC'] else: - default_order, _ = ORDER_DIR["DESC"] + asc, desc = ORDER_DIR['DESC'] + order_by = [] for field in ordering: - if hasattr(field, "resolve_expression"): + if hasattr(field, 'resolve_expression'): if isinstance(field, Value): # output_field must be resolved for constants. field = Cast(field, field.output_field) @@ -323,24 +304,20 @@ class SQLCompiler: if not self.query.standard_ordering: field = field.copy() field.reverse_ordering() - yield field, False + order_by.append((field, False)) continue - if field == "?": # random - yield OrderBy(Random()), False + if field == '?': # random + order_by.append((OrderBy(Random()), False)) continue - col, order = get_order_dir(field, default_order) - descending = order == "DESC" + col, order = get_order_dir(field, asc) + descending = order == 'DESC' if col in self.query.annotation_select: # Reference to expression in SELECT clause - yield ( - OrderBy( - Ref(col, self.query.annotation_select[col]), - descending=descending, - ), - True, - ) + order_by.append(( + OrderBy(Ref(col, self.query.annotation_select[col]), descending=descending), + True)) continue if col in self.query.annotations: # References to an expression which is masked out of the SELECT @@ -354,65 +331,44 @@ class SQLCompiler: if isinstance(expr, Value): # output_field must be resolved for constants. expr = Cast(expr, expr.output_field) - yield OrderBy(expr, descending=descending), False + order_by.append((OrderBy(expr, descending=descending), False)) continue - if "." in field: + if '.' in field: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. - table, col = col.split(".", 1) - yield ( + table, col = col.split('.', 1) + order_by.append(( OrderBy( - RawSQL( - "%s.%s" % (self.quote_name_unless_alias(table), col), [] - ), - descending=descending, - ), - False, - ) + RawSQL('%s.%s' % (self.quote_name_unless_alias(table), col), []), + descending=descending + ), False)) continue - if self.query.extra and col in self.query.extra: - if col in self.query.extra_select: - yield ( - OrderBy( - Ref(col, RawSQL(*self.query.extra[col])), - descending=descending, - ), - True, - ) - else: - yield ( - OrderBy(RawSQL(*self.query.extra[col]), descending=descending), - False, - ) - else: + if not self.query.extra or col not in self.query.extra: if self.query.combinator and self.select: # Don't use the first model's field because other # combinated queries might define it differently. - yield OrderBy(F(col), descending=descending), False + order_by.append((OrderBy(F(col), descending=descending), False)) else: # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. - yield from self.find_ordering_name( - field, - self.query.get_meta(), - default_order=default_order, - ) - - def get_order_by(self): - """ - Return a list of 2-tuples of the form (expr, (sql, params, is_ref)) for - the ORDER BY clause. - - The order_by clause can alter the select clause (for example it can add - aliases to clauses that do not yet have one, or it can add totally new - select clauses). - """ + order_by.extend(self.find_ordering_name( + field, self.query.get_meta(), default_order=asc, + )) + else: + if col not in self.query.extra_select: + order_by.append(( + OrderBy(RawSQL(*self.query.extra[col]), descending=descending), + False)) + else: + order_by.append(( + OrderBy(Ref(col, RawSQL(*self.query.extra[col])), descending=descending), + True)) result = [] seen = set() - for expr, is_ref in self._order_by_pairs(): + for expr, is_ref in order_by: resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) if self.query.combinator and self.select: src = resolved.get_source_expressions()[0] @@ -428,21 +384,19 @@ class SQLCompiler: ): continue if src == sel_expr: - resolved.set_source_expressions([RawSQL("%d" % (idx + 1), ())]) + resolved.set_source_expressions([RawSQL('%d' % (idx + 1), ())]) break else: if col_alias: - raise DatabaseError( - "ORDER BY term does not match any column in the result set." - ) + raise DatabaseError('ORDER BY term does not match any column in the result set.') # Add column used in ORDER BY clause to the selected # columns and to each combined query. order_by_idx = len(self.query.select) + 1 - col_name = f"__orderbycol{order_by_idx}" + col_name = f'__orderbycol{order_by_idx}' for q in self.query.combined_queries: q.add_annotation(expr_src, col_name) self.query.add_select_col(resolved, col_name) - resolved.set_source_expressions([RawSQL(f"{order_by_idx}", ())]) + resolved.set_source_expressions([RawSQL(f'{order_by_idx}', ())]) sql, params = self.compile(resolved) # Don't add the same column twice, but the order direction is # not taken into account so we strip it. When this entire method @@ -474,14 +428,9 @@ class SQLCompiler: """ if name in self.quote_cache: return self.quote_cache[name] - if ( - (name in self.query.alias_map and name not in self.query.table_map) - or name in self.query.extra_select - or ( - self.query.external_aliases.get(name) - and name not in self.query.table_map - ) - ): + if ((name in self.query.alias_map and name not in self.query.table_map) or + name in self.query.extra_select or ( + self.query.external_aliases.get(name) and name not in self.query.table_map)): self.quote_cache[name] = name return name r = self.connection.ops.quote_name(name) @@ -489,7 +438,7 @@ class SQLCompiler: return r def compile(self, node): - vendor_impl = getattr(node, "as_" + self.connection.vendor, None) + vendor_impl = getattr(node, 'as_' + self.connection.vendor, None) if vendor_impl: sql, params = vendor_impl(self, self.connection) else: @@ -499,20 +448,15 @@ class SQLCompiler: def get_combinator_sql(self, combinator, all): features = self.connection.features compilers = [ - query.get_compiler(self.using, self.connection, self.elide_empty) - for query in self.query.combined_queries - if not query.is_empty() + query.get_compiler(self.using, self.connection) + for query in self.query.combined_queries if not query.is_empty() ] if not features.supports_slicing_ordering_in_compound: for query, compiler in zip(self.query.combined_queries, compilers): if query.low_mark or query.high_mark: - raise DatabaseError( - "LIMIT/OFFSET not allowed in subqueries of compound statements." - ) + raise DatabaseError('LIMIT/OFFSET not allowed in subqueries of compound statements.') if compiler.get_order_by(): - raise DatabaseError( - "ORDER BY not allowed in subqueries of compound statements." - ) + raise DatabaseError('ORDER BY not allowed in subqueries of compound statements.') parts = () for compiler in compilers: try: @@ -521,40 +465,36 @@ class SQLCompiler: # the query on all combined queries, if not already set. if not compiler.query.values_select and self.query.values_select: compiler.query = compiler.query.clone() - compiler.query.set_values( - ( - *self.query.extra_select, - *self.query.values_select, - *self.query.annotation_select, - ) - ) + compiler.query.set_values(( + *self.query.extra_select, + *self.query.values_select, + *self.query.annotation_select, + )) part_sql, part_args = compiler.as_sql() if compiler.query.combinator: # Wrap in a subquery if wrapping in parentheses isn't # supported. if not features.supports_parentheses_in_compound: - part_sql = "SELECT * FROM ({})".format(part_sql) + part_sql = 'SELECT * FROM ({})'.format(part_sql) # Add parentheses when combining with compound query if not # already added for all compound queries. elif not features.supports_slicing_ordering_in_compound: - part_sql = "({})".format(part_sql) + part_sql = '({})'.format(part_sql) parts += ((part_sql, part_args),) except EmptyResultSet: # Omit the empty queryset with UNION and with DIFFERENCE if the # first queryset is nonempty. - if combinator == "union" or (combinator == "difference" and parts): + if combinator == 'union' or (combinator == 'difference' and parts): continue raise if not parts: raise EmptyResultSet combinator_sql = self.connection.ops.set_operators[combinator] - if all and combinator == "union": - combinator_sql += " ALL" - braces = "({})" if features.supports_slicing_ordering_in_compound else "{}" - sql_parts, args_parts = zip( - *((braces.format(sql), args) for sql, args in parts) - ) - result = [" {} ".format(combinator_sql).join(sql_parts)] + if all and combinator == 'union': + combinator_sql += ' ALL' + braces = '({})' if features.supports_slicing_ordering_in_compound else '{}' + sql_parts, args_parts = zip(*((braces.format(sql), args) for sql, args in parts)) + result = [' {} '.format(combinator_sql).join(sql_parts)] params = [] for part in args_parts: params.extend(part) @@ -573,39 +513,21 @@ class SQLCompiler: extra_select, order_by, group_by = self.pre_sql_setup() for_update_part = None # Is a LIMIT/OFFSET clause needed? - with_limit_offset = with_limits and ( - self.query.high_mark is not None or self.query.low_mark - ) + with_limit_offset = with_limits and (self.query.high_mark is not None or self.query.low_mark) combinator = self.query.combinator features = self.connection.features if combinator: - if not getattr(features, "supports_select_{}".format(combinator)): - raise NotSupportedError( - "{} is not supported on this database backend.".format( - combinator - ) - ) - result, params = self.get_combinator_sql( - combinator, self.query.combinator_all - ) + if not getattr(features, 'supports_select_{}'.format(combinator)): + raise NotSupportedError('{} is not supported on this database backend.'.format(combinator)) + result, params = self.get_combinator_sql(combinator, self.query.combinator_all) else: distinct_fields, distinct_params = self.get_distinct() # This must come after 'select', 'ordering', and 'distinct' # (see docstring of get_from_clause() for details). from_, f_params = self.get_from_clause() - try: - where, w_params = ( - self.compile(self.where) if self.where is not None else ("", []) - ) - except EmptyResultSet: - if self.elide_empty: - raise - # Use a predicate that's always False. - where, w_params = "0 = 1", [] - having, h_params = ( - self.compile(self.having) if self.having is not None else ("", []) - ) - result = ["SELECT"] + where, w_params = self.compile(self.where) if self.where is not None else ("", []) + having, h_params = self.compile(self.having) if self.having is not None else ("", []) + result = ['SELECT'] params = [] if self.query.distinct: @@ -620,35 +542,24 @@ class SQLCompiler: col_idx = 1 for _, (s_sql, s_params), alias in self.select + extra_select: if alias: - s_sql = "%s AS %s" % ( - s_sql, - self.connection.ops.quote_name(alias), - ) + s_sql = '%s AS %s' % (s_sql, self.connection.ops.quote_name(alias)) elif with_col_aliases: - s_sql = "%s AS %s" % ( - s_sql, - self.connection.ops.quote_name("col%d" % col_idx), - ) + s_sql = '%s AS %s' % (s_sql, 'Col%d' % col_idx) col_idx += 1 params.extend(s_params) out_cols.append(s_sql) - result += [", ".join(out_cols), "FROM", *from_] + result += [', '.join(out_cols), 'FROM', *from_] params.extend(f_params) - if self.query.select_for_update and features.has_select_for_update: + if self.query.select_for_update and self.connection.features.has_select_for_update: if self.connection.get_autocommit(): - raise TransactionManagementError( - "select_for_update cannot be used outside of a transaction." - ) + raise TransactionManagementError('select_for_update cannot be used outside of a transaction.') - if ( - with_limit_offset - and not features.supports_select_for_update_with_limit - ): + if with_limit_offset and not self.connection.features.supports_select_for_update_with_limit: raise NotSupportedError( - "LIMIT/OFFSET is not supported with " - "select_for_update on this database backend." + 'LIMIT/OFFSET is not supported with ' + 'select_for_update on this database backend.' ) nowait = self.query.select_for_update_nowait skip_locked = self.query.select_for_update_skip_locked @@ -657,22 +568,16 @@ class SQLCompiler: # If it's a NOWAIT/SKIP LOCKED/OF/NO KEY query but the # backend doesn't support it, raise NotSupportedError to # prevent a possible deadlock. - if nowait and not features.has_select_for_update_nowait: + if nowait and not self.connection.features.has_select_for_update_nowait: + raise NotSupportedError('NOWAIT is not supported on this database backend.') + elif skip_locked and not self.connection.features.has_select_for_update_skip_locked: + raise NotSupportedError('SKIP LOCKED is not supported on this database backend.') + elif of and not self.connection.features.has_select_for_update_of: + raise NotSupportedError('FOR UPDATE OF is not supported on this database backend.') + elif no_key and not self.connection.features.has_select_for_no_key_update: raise NotSupportedError( - "NOWAIT is not supported on this database backend." - ) - elif skip_locked and not features.has_select_for_update_skip_locked: - raise NotSupportedError( - "SKIP LOCKED is not supported on this database backend." - ) - elif of and not features.has_select_for_update_of: - raise NotSupportedError( - "FOR UPDATE OF is not supported on this database backend." - ) - elif no_key and not features.has_select_for_no_key_update: - raise NotSupportedError( - "FOR NO KEY UPDATE is not supported on this " - "database backend." + 'FOR NO KEY UPDATE is not supported on this ' + 'database backend.' ) for_update_part = self.connection.ops.for_update_sql( nowait=nowait, @@ -681,11 +586,11 @@ class SQLCompiler: no_key=no_key, ) - if for_update_part and features.for_update_after_from: + if for_update_part and self.connection.features.for_update_after_from: result.append(for_update_part) if where: - result.append("WHERE %s" % where) + result.append('WHERE %s' % where) params.extend(w_params) grouping = [] @@ -694,41 +599,32 @@ class SQLCompiler: params.extend(g_params) if grouping: if distinct_fields: - raise NotImplementedError( - "annotate() + distinct(fields) is not implemented." - ) + raise NotImplementedError('annotate() + distinct(fields) is not implemented.') order_by = order_by or self.connection.ops.force_no_ordering() - result.append("GROUP BY %s" % ", ".join(grouping)) + result.append('GROUP BY %s' % ', '.join(grouping)) if self._meta_ordering: order_by = None if having: - result.append("HAVING %s" % having) + result.append('HAVING %s' % having) params.extend(h_params) - if self.query.explain_info: - result.insert( - 0, - self.connection.ops.explain_query_prefix( - self.query.explain_info.format, - **self.query.explain_info.options, - ), - ) + if self.query.explain_query: + result.insert(0, self.connection.ops.explain_query_prefix( + self.query.explain_format, + **self.query.explain_options + )) if order_by: ordering = [] for _, (o_sql, o_params, _) in order_by: ordering.append(o_sql) params.extend(o_params) - result.append("ORDER BY %s" % ", ".join(ordering)) + result.append('ORDER BY %s' % ', '.join(ordering)) if with_limit_offset: - result.append( - self.connection.ops.limit_offset_sql( - self.query.low_mark, self.query.high_mark - ) - ) + result.append(self.connection.ops.limit_offset_sql(self.query.low_mark, self.query.high_mark)) - if for_update_part and not features.for_update_after_from: + if for_update_part and not self.connection.features.for_update_after_from: result.append(for_update_part) if self.query.subquery and extra_select: @@ -742,30 +638,23 @@ class SQLCompiler: sub_params = [] for index, (select, _, alias) in enumerate(self.select, start=1): if not alias and with_col_aliases: - alias = "col%d" % index + alias = 'col%d' % index if alias: - sub_selects.append( - "%s.%s" - % ( - self.connection.ops.quote_name("subquery"), - self.connection.ops.quote_name(alias), - ) - ) + sub_selects.append("%s.%s" % ( + self.connection.ops.quote_name('subquery'), + self.connection.ops.quote_name(alias), + )) else: - select_clone = select.relabeled_clone( - {select.alias: "subquery"} - ) - subselect, subparams = select_clone.as_sql( - self, self.connection - ) + select_clone = select.relabeled_clone({select.alias: 'subquery'}) + subselect, subparams = select_clone.as_sql(self, self.connection) sub_selects.append(subselect) sub_params.extend(subparams) - return "SELECT %s FROM (%s) subquery" % ( - ", ".join(sub_selects), - " ".join(result), + return 'SELECT %s FROM (%s) subquery' % ( + ', '.join(sub_selects), + ' '.join(result), ), tuple(sub_params + params) - return " ".join(result), tuple(params) + return ' '.join(result), tuple(params) finally: # Finally do cleanup - get rid of the joins we created above. self.query.reset_refcounts(refcounts_before) @@ -798,13 +687,8 @@ class SQLCompiler: # will assign None if the field belongs to this model. if model == opts.model: model = None - if ( - from_parent - and model is not None - and issubclass( - from_parent._meta.concrete_model, model._meta.concrete_model - ) - ): + if from_parent and model is not None and issubclass( + from_parent._meta.concrete_model, model._meta.concrete_model): # Avoid loading data for already loaded parents. # We end up here in the case select_related() resolution # proceeds from parent model to child model. In that case the @@ -813,7 +697,8 @@ class SQLCompiler: continue if field.model in only_load and field.attname not in only_load[field.model]: continue - alias = self.query.join_parent_model(opts, model, start_alias, seen_models) + alias = self.query.join_parent_model(opts, model, start_alias, + seen_models) column = field.get_col(alias) result.append(column) return result @@ -831,9 +716,7 @@ class SQLCompiler: for name in self.query.distinct_fields: parts = name.split(LOOKUP_SEP) - _, targets, alias, joins, path, _, transform_function = self._setup_joins( - parts, opts, None - ) + _, targets, alias, joins, path, _, transform_function = self._setup_joins(parts, opts, None) targets, alias, _ = self.query.trim_joins(targets, joins, path) for target in targets: if name in self.query.annotation_select: @@ -844,63 +727,46 @@ class SQLCompiler: params.append(p) return result, params - def find_ordering_name( - self, name, opts, alias=None, default_order="ASC", already_seen=None - ): + def find_ordering_name(self, name, opts, alias=None, default_order='ASC', + already_seen=None): """ Return the table alias (the name might be ambiguous, the alias will not be) and column name for ordering by the given 'name' parameter. The 'name' is of the form 'field1__field2__...__fieldN'. """ name, order = get_order_dir(name, default_order) - descending = order == "DESC" + descending = order == 'DESC' pieces = name.split(LOOKUP_SEP) - ( - field, - targets, - alias, - joins, - path, - opts, - transform_function, - ) = self._setup_joins(pieces, opts, alias) + field, targets, alias, joins, path, opts, transform_function = self._setup_joins(pieces, opts, alias) # If we get to this point and the field is a relation to another model, # append the default ordering for that model unless it is the pk # shortcut or the attribute name of the field that is specified. if ( - field.is_relation - and opts.ordering - and getattr(field, "attname", None) != pieces[-1] - and name != "pk" + field.is_relation and + opts.ordering and + getattr(field, 'attname', None) != pieces[-1] and + name != 'pk' ): # Firstly, avoid infinite loops. already_seen = already_seen or set() - join_tuple = tuple( - getattr(self.query.alias_map[j], "join_cols", None) for j in joins - ) + join_tuple = tuple(getattr(self.query.alias_map[j], 'join_cols', None) for j in joins) if join_tuple in already_seen: - raise FieldError("Infinite loop caused by ordering.") + raise FieldError('Infinite loop caused by ordering.') already_seen.add(join_tuple) results = [] for item in opts.ordering: - if hasattr(item, "resolve_expression") and not isinstance( - item, OrderBy - ): + if hasattr(item, 'resolve_expression') and not isinstance(item, OrderBy): item = item.desc() if descending else item.asc() if isinstance(item, OrderBy): results.append((item, False)) continue - results.extend( - self.find_ordering_name(item, opts, alias, order, already_seen) - ) + results.extend(self.find_ordering_name(item, opts, alias, + order, already_seen)) return results targets, alias, _ = self.query.trim_joins(targets, joins, path) - return [ - (OrderBy(transform_function(t, alias), descending=descending), False) - for t in targets - ] + return [(OrderBy(transform_function(t, alias), descending=descending), False) for t in targets] def _setup_joins(self, pieces, opts, alias): """ @@ -911,9 +777,7 @@ class SQLCompiler: match. Executing SQL where this is not true is an error. """ alias = alias or self.query.get_initial_alias() - field, targets, opts, joins, path, transform_function = self.query.setup_joins( - pieces, opts, alias - ) + field, targets, opts, joins, path, transform_function = self.query.setup_joins(pieces, opts, alias) alias = joins[-1] return field, targets, alias, joins, path, opts, transform_function @@ -947,39 +811,25 @@ class SQLCompiler: # Only add the alias if it's not already present (the table_alias() # call increments the refcount, so an alias refcount of one means # this is the only reference). - if ( - alias not in self.query.alias_map - or self.query.alias_refcount[alias] == 1 - ): - result.append(", %s" % self.quote_name_unless_alias(alias)) + if alias not in self.query.alias_map or self.query.alias_refcount[alias] == 1: + result.append(', %s' % self.quote_name_unless_alias(alias)) return result, params - def get_related_selections( - self, - select, - opts=None, - root_alias=None, - cur_depth=1, - requested=None, - restricted=None, - ): + def get_related_selections(self, select, opts=None, root_alias=None, cur_depth=1, + requested=None, restricted=None): """ Fill in the information needed for a select_related query. The current depth is measured as the number of connections away from the root model (for example, cur_depth=1 means we are looking at models with direct connections to the root model). """ - def _get_field_choices(): direct_choices = (f.name for f in opts.fields if f.is_relation) reverse_choices = ( f.field.related_query_name() - for f in opts.related_objects - if f.field.unique - ) - return chain( - direct_choices, reverse_choices, self.query._filtered_relations + for f in opts.related_objects if f.field.unique ) + return chain(direct_choices, reverse_choices, self.query._filtered_relations) related_klass_infos = [] if not restricted and cur_depth > self.query.max_depth: @@ -1000,7 +850,7 @@ class SQLCompiler: requested = self.query.select_related def get_related_klass_infos(klass_info, related_klass_infos): - klass_info["related_klass_infos"] = related_klass_infos + klass_info['related_klass_infos'] = related_klass_infos for f in opts.fields: field_model = f.model._meta.concrete_model @@ -1014,48 +864,37 @@ class SQLCompiler: if next or f.name in requested: raise FieldError( "Non-relational field given in select_related: '%s'. " - "Choices are: %s" - % ( + "Choices are: %s" % ( f.name, - ", ".join(_get_field_choices()) or "(none)", + ", ".join(_get_field_choices()) or '(none)', ) ) else: next = False - if not select_related_descend( - f, restricted, requested, only_load.get(field_model) - ): + if not select_related_descend(f, restricted, requested, + only_load.get(field_model)): continue klass_info = { - "model": f.remote_field.model, - "field": f, - "reverse": False, - "local_setter": f.set_cached_value, - "remote_setter": f.remote_field.set_cached_value - if f.unique - else lambda x, y: None, - "from_parent": False, + 'model': f.remote_field.model, + 'field': f, + 'reverse': False, + 'local_setter': f.set_cached_value, + 'remote_setter': f.remote_field.set_cached_value if f.unique else lambda x, y: None, + 'from_parent': False, } related_klass_infos.append(klass_info) select_fields = [] - _, _, _, joins, _, _ = self.query.setup_joins([f.name], opts, root_alias) + _, _, _, joins, _, _ = self.query.setup_joins( + [f.name], opts, root_alias) alias = joins[-1] - columns = self.get_default_columns( - start_alias=alias, opts=f.remote_field.model._meta - ) + columns = self.get_default_columns(start_alias=alias, opts=f.remote_field.model._meta) for col in columns: select_fields.append(len(select)) select.append((col, None)) - klass_info["select_fields"] = select_fields + klass_info['select_fields'] = select_fields next_klass_infos = self.get_related_selections( - select, - f.remote_field.model._meta, - alias, - cur_depth + 1, - next, - restricted, - ) + select, f.remote_field.model._meta, alias, cur_depth + 1, next, restricted) get_related_klass_infos(klass_info, next_klass_infos) if restricted: @@ -1065,40 +904,36 @@ class SQLCompiler: if o.field.unique and not o.many_to_many ] for f, model in related_fields: - if not select_related_descend( - f, restricted, requested, only_load.get(model), reverse=True - ): + if not select_related_descend(f, restricted, requested, + only_load.get(model), reverse=True): continue related_field_name = f.related_query_name() fields_found.add(related_field_name) - join_info = self.query.setup_joins( - [related_field_name], opts, root_alias - ) + join_info = self.query.setup_joins([related_field_name], opts, root_alias) alias = join_info.joins[-1] from_parent = issubclass(model, opts.model) and model is not opts.model klass_info = { - "model": model, - "field": f, - "reverse": True, - "local_setter": f.remote_field.set_cached_value, - "remote_setter": f.set_cached_value, - "from_parent": from_parent, + 'model': model, + 'field': f, + 'reverse': True, + 'local_setter': f.remote_field.set_cached_value, + 'remote_setter': f.set_cached_value, + 'from_parent': from_parent, } related_klass_infos.append(klass_info) select_fields = [] columns = self.get_default_columns( - start_alias=alias, opts=model._meta, from_parent=opts.model - ) + start_alias=alias, opts=model._meta, from_parent=opts.model) for col in columns: select_fields.append(len(select)) select.append((col, None)) - klass_info["select_fields"] = select_fields + klass_info['select_fields'] = select_fields next = requested.get(f.related_query_name(), {}) next_klass_infos = self.get_related_selections( - select, model._meta, alias, cur_depth + 1, next, restricted - ) + select, model._meta, alias, cur_depth + 1, + next, restricted) get_related_klass_infos(klass_info, next_klass_infos) def local_setter(obj, from_obj): @@ -1115,40 +950,32 @@ class SQLCompiler: break if name in self.query._filtered_relations: fields_found.add(name) - f, _, join_opts, joins, _, _ = self.query.setup_joins( - [name], opts, root_alias - ) + f, _, join_opts, joins, _, _ = self.query.setup_joins([name], opts, root_alias) model = join_opts.model alias = joins[-1] - from_parent = ( - issubclass(model, opts.model) and model is not opts.model - ) + from_parent = issubclass(model, opts.model) and model is not opts.model klass_info = { - "model": model, - "field": f, - "reverse": True, - "local_setter": local_setter, - "remote_setter": partial(remote_setter, name), - "from_parent": from_parent, + 'model': model, + 'field': f, + 'reverse': True, + 'local_setter': local_setter, + 'remote_setter': partial(remote_setter, name), + 'from_parent': from_parent, } related_klass_infos.append(klass_info) select_fields = [] columns = self.get_default_columns( - start_alias=alias, - opts=model._meta, + start_alias=alias, opts=model._meta, from_parent=opts.model, ) for col in columns: select_fields.append(len(select)) select.append((col, None)) - klass_info["select_fields"] = select_fields + klass_info['select_fields'] = select_fields next_requested = requested.get(name, {}) next_klass_infos = self.get_related_selections( - select, - opts=model._meta, - root_alias=alias, - cur_depth=cur_depth + 1, - requested=next_requested, + select, opts=model._meta, root_alias=alias, + cur_depth=cur_depth + 1, requested=next_requested, restricted=restricted, ) get_related_klass_infos(klass_info, next_klass_infos) @@ -1156,11 +983,10 @@ class SQLCompiler: if fields_not_found: invalid_fields = ("'%s'" % s for s in fields_not_found) raise FieldError( - "Invalid field name(s) given in select_related: %s. " - "Choices are: %s" - % ( - ", ".join(invalid_fields), - ", ".join(_get_field_choices()) or "(none)", + 'Invalid field name(s) given in select_related: %s. ' + 'Choices are: %s' % ( + ', '.join(invalid_fields), + ', '.join(_get_field_choices()) or '(none)', ) ) return related_klass_infos @@ -1170,22 +996,21 @@ class SQLCompiler: Return a quoted list of arguments for the SELECT FOR UPDATE OF part of the query. """ - def _get_parent_klass_info(klass_info): - concrete_model = klass_info["model"]._meta.concrete_model + concrete_model = klass_info['model']._meta.concrete_model for parent_model, parent_link in concrete_model._meta.parents.items(): parent_list = parent_model._meta.get_parent_list() yield { - "model": parent_model, - "field": parent_link, - "reverse": False, - "select_fields": [ + 'model': parent_model, + 'field': parent_link, + 'reverse': False, + 'select_fields': [ select_index - for select_index in klass_info["select_fields"] + for select_index in klass_info['select_fields'] # Selected columns from a model or its parents. if ( - self.select[select_index][0].target.model == parent_model - or self.select[select_index][0].target.model in parent_list + self.select[select_index][0].target.model == parent_model or + self.select[select_index][0].target.model in parent_list ) ], } @@ -1198,8 +1023,8 @@ class SQLCompiler: select_fields is filled recursively, so it also contains fields from the parent models. """ - concrete_model = klass_info["model"]._meta.concrete_model - for select_index in klass_info["select_fields"]: + concrete_model = klass_info['model']._meta.concrete_model + for select_index in klass_info['select_fields']: if self.select[select_index][0].target.model == concrete_model: return self.select[select_index][0] @@ -1210,10 +1035,10 @@ class SQLCompiler: parent_path, klass_info = queue.popleft() if parent_path is None: path = [] - yield "self" + yield 'self' else: - field = klass_info["field"] - if klass_info["reverse"]: + field = klass_info['field'] + if klass_info['reverse']: field = field.remote_field path = parent_path + [field.name] yield LOOKUP_SEP.join(path) @@ -1223,24 +1048,23 @@ class SQLCompiler: ) queue.extend( (path, klass_info) - for klass_info in klass_info.get("related_klass_infos", []) + for klass_info in klass_info.get('related_klass_infos', []) ) - result = [] invalid_names = [] for name in self.query.select_for_update_of: klass_info = self.klass_info - if name == "self": + if name == 'self': col = _get_first_selected_col_from_model(klass_info) else: for part in name.split(LOOKUP_SEP): klass_infos = ( - *klass_info.get("related_klass_infos", []), + *klass_info.get('related_klass_infos', []), *_get_parent_klass_info(klass_info), ) for related_klass_info in klass_infos: - field = related_klass_info["field"] - if related_klass_info["reverse"]: + field = related_klass_info['field'] + if related_klass_info['reverse']: field = field.remote_field if field.name == part: klass_info = related_klass_info @@ -1259,12 +1083,11 @@ class SQLCompiler: result.append(self.quote_name_unless_alias(col.alias)) if invalid_names: raise FieldError( - "Invalid field name(s) given in select_for_update(of=(...)): %s. " - "Only relational fields followed in the query are allowed. " - "Choices are: %s." - % ( - ", ".join(invalid_names), - ", ".join(_get_field_choices()), + 'Invalid field name(s) given in select_for_update(of=(...)): %s. ' + 'Only relational fields followed in the query are allowed. ' + 'Choices are: %s.' % ( + ', '.join(invalid_names), + ', '.join(_get_field_choices()), ) ) return result @@ -1300,19 +1123,12 @@ class SQLCompiler: row[pos] = value yield row - def results_iter( - self, - results=None, - tuple_expected=False, - chunked_fetch=False, - chunk_size=GET_ITERATOR_CHUNK_SIZE, - ): + def results_iter(self, results=None, tuple_expected=False, chunked_fetch=False, + chunk_size=GET_ITERATOR_CHUNK_SIZE): """Return an iterator over the results from executing this query.""" if results is None: - results = self.execute_sql( - MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size - ) - fields = [s[0] for s in self.select[0 : self.col_count]] + results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size) + fields = [s[0] for s in self.select[0:self.col_count]] converters = self.get_converters(fields) rows = chain.from_iterable(results) if converters: @@ -1328,9 +1144,7 @@ class SQLCompiler: """ return bool(self.execute_sql(SINGLE)) - def execute_sql( - self, result_type=MULTI, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE - ): + def execute_sql(self, result_type=MULTI, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE): """ Run the query against the database and return the result(s). The return value is a single data item if result_type is SINGLE, or an @@ -1371,7 +1185,7 @@ class SQLCompiler: try: val = cursor.fetchone() if val: - return val[0 : self.col_count] + return val[0:self.col_count] return val finally: # done with the cursor @@ -1381,17 +1195,20 @@ class SQLCompiler: return result = cursor_iter( - cursor, - self.connection.features.empty_fetchmany_value, + cursor, self.connection.features.empty_fetchmany_value, self.col_count if self.has_extra_select else None, chunk_size, ) if not chunked_fetch or not self.connection.features.can_use_chunked_reads: - # If we are using non-chunked reads, we return the same data - # structure as normally, but ensure it is all read into memory - # before going any further. Use chunked_fetch if requested, - # unless the database doesn't support it. - return list(result) + try: + # If we are using non-chunked reads, we return the same data + # structure as normally, but ensure it is all read into memory + # before going any further. Use chunked_fetch if requested, + # unless the database doesn't support it. + return list(result) + finally: + # done with the cursor + cursor.close() return result def as_subquery_condition(self, alias, columns, compiler): @@ -1400,22 +1217,20 @@ class SQLCompiler: for index, select_col in enumerate(self.query.select): lhs_sql, lhs_params = self.compile(select_col) - rhs = "%s.%s" % (qn(alias), qn2(columns[index])) - self.query.where.add(RawSQL("%s = %s" % (lhs_sql, rhs), lhs_params), "AND") + rhs = '%s.%s' % (qn(alias), qn2(columns[index])) + self.query.where.add( + RawSQL('%s = %s' % (lhs_sql, rhs), lhs_params), 'AND') sql, params = self.as_sql() - return "EXISTS (%s)" % sql, params + return 'EXISTS (%s)' % sql, params def explain_query(self): result = list(self.execute_sql()) # Some backends return 1 item tuples with strings, and others return # tuples with integers and strings. Flatten them out into strings. - output_formatter = ( - json.dumps if self.query.explain_info.format == "json" else str - ) for row in result[0]: if not isinstance(row, str): - yield " ".join(output_formatter(c) for c in row) + yield ' '.join(str(c) for c in row) else: yield row @@ -1436,16 +1251,16 @@ class SQLInsertCompiler(SQLCompiler): if field is None: # A field value of None means the value is raw. sql, params = val, [] - elif hasattr(val, "as_sql"): + elif hasattr(val, 'as_sql'): # This is an expression, let's compile it. sql, params = self.compile(val) - elif hasattr(field, "get_placeholder"): + elif hasattr(field, 'get_placeholder'): # Some fields (e.g. geo fields) need special munging before # they can be inserted. sql, params = field.get_placeholder(val, self, self.connection), [val] else: # Return the common case for the placeholder - sql, params = "%s", [val] + sql, params = '%s', [val] # The following hook is only used by Oracle Spatial, which sometimes # needs to yield 'NULL' and [] as its placeholder and params instead @@ -1461,26 +1276,24 @@ class SQLInsertCompiler(SQLCompiler): Prepare a value to be used in a query by resolving it if it is an expression and otherwise calling the field's get_db_prep_save(). """ - if hasattr(value, "resolve_expression"): - value = value.resolve_expression( - self.query, allow_joins=False, for_save=True - ) + if hasattr(value, 'resolve_expression'): + value = value.resolve_expression(self.query, allow_joins=False, for_save=True) # Don't allow values containing Col expressions. They refer to # existing columns on a row, but in the case of insert the row # doesn't exist yet. if value.contains_column_references: raise ValueError( 'Failed to insert expression "%s" on %s. F() expressions ' - "can only be used to update, not to insert." % (value, field) + 'can only be used to update, not to insert.' % (value, field) ) if value.contains_aggregate: raise FieldError( - "Aggregate functions are not allowed in this query " - "(%s=%r)." % (field.name, value) + 'Aggregate functions are not allowed in this query ' + '(%s=%r).' % (field.name, value) ) if value.contains_over_clause: raise FieldError( - "Window expressions are not allowed in this query (%s=%r)." + 'Window expressions are not allowed in this query (%s=%r).' % (field.name, value) ) else: @@ -1536,49 +1349,35 @@ class SQLInsertCompiler(SQLCompiler): # going to be column names (so we can avoid the extra overhead). qn = self.connection.ops.quote_name opts = self.query.get_meta() - insert_statement = self.connection.ops.insert_statement( - ignore_conflicts=self.query.ignore_conflicts - ) - result = ["%s %s" % (insert_statement, qn(opts.db_table))] + insert_statement = self.connection.ops.insert_statement(ignore_conflicts=self.query.ignore_conflicts) + result = ['%s %s' % (insert_statement, qn(opts.db_table))] fields = self.query.fields or [opts.pk] - result.append("(%s)" % ", ".join(qn(f.column) for f in fields)) + result.append('(%s)' % ', '.join(qn(f.column) for f in fields)) if self.query.fields: value_rows = [ - [ - self.prepare_value(field, self.pre_save_val(field, obj)) - for field in fields - ] + [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields] for obj in self.query.objs ] else: # An empty object. - value_rows = [ - [self.connection.ops.pk_default_value()] for _ in self.query.objs - ] + value_rows = [[self.connection.ops.pk_default_value()] for _ in self.query.objs] fields = [None] # Currently the backends just accept values when generating bulk # queries and generate their own placeholders. Doing that isn't # necessary and it should be possible to use placeholders and # expressions in bulk inserts too. - can_bulk = ( - not self.returning_fields and self.connection.features.has_bulk_insert - ) + can_bulk = (not self.returning_fields and self.connection.features.has_bulk_insert) placeholder_rows, param_rows = self.assemble_as_sql(fields, value_rows) ignore_conflicts_suffix_sql = self.connection.ops.ignore_conflicts_suffix_sql( ignore_conflicts=self.query.ignore_conflicts ) - if ( - self.returning_fields - and self.connection.features.can_return_columns_from_insert - ): + if self.returning_fields and self.connection.features.can_return_columns_from_insert: if self.connection.features.can_return_rows_from_bulk_insert: - result.append( - self.connection.ops.bulk_insert_sql(fields, placeholder_rows) - ) + result.append(self.connection.ops.bulk_insert_sql(fields, placeholder_rows)) params = param_rows else: result.append("VALUES (%s)" % ", ".join(placeholder_rows[0])) @@ -1587,9 +1386,7 @@ class SQLInsertCompiler(SQLCompiler): result.append(ignore_conflicts_suffix_sql) # Skip empty r_sql to allow subclasses to customize behavior for # 3rd party backends. Refs #19096. - r_sql, self.returning_params = self.connection.ops.return_insert_columns( - self.returning_fields - ) + r_sql, self.returning_params = self.connection.ops.return_insert_columns(self.returning_fields) if r_sql: result.append(r_sql) params += [self.returning_params] @@ -1610,45 +1407,23 @@ class SQLInsertCompiler(SQLCompiler): def execute_sql(self, returning_fields=None): assert not ( - returning_fields - and len(self.query.objs) != 1 - and not self.connection.features.can_return_rows_from_bulk_insert + returning_fields and len(self.query.objs) != 1 and + not self.connection.features.can_return_rows_from_bulk_insert ) - opts = self.query.get_meta() self.returning_fields = returning_fields with self.connection.cursor() as cursor: for sql, params in self.as_sql(): cursor.execute(sql, params) if not self.returning_fields: return [] - if ( - self.connection.features.can_return_rows_from_bulk_insert - and len(self.query.objs) > 1 - ): - rows = self.connection.ops.fetch_returned_insert_rows(cursor) - elif self.connection.features.can_return_columns_from_insert: + if self.connection.features.can_return_rows_from_bulk_insert and len(self.query.objs) > 1: + return self.connection.ops.fetch_returned_insert_rows(cursor) + if self.connection.features.can_return_columns_from_insert: assert len(self.query.objs) == 1 - rows = [ - self.connection.ops.fetch_returned_insert_columns( - cursor, - self.returning_params, - ) - ] - else: - rows = [ - ( - self.connection.ops.last_insert_id( - cursor, - opts.db_table, - opts.pk.column, - ), - ) - ] - cols = [field.get_col(opts.db_table) for field in self.returning_fields] - converters = self.get_converters(cols) - if converters: - rows = list(self.apply_converters(rows, converters)) - return rows + return [self.connection.ops.fetch_returned_insert_columns(cursor, self.returning_params)] + return [(self.connection.ops.last_insert_id( + cursor, self.query.get_meta().db_table, self.query.get_meta().pk.column + ),)] class SQLDeleteCompiler(SQLCompiler): @@ -1662,7 +1437,7 @@ class SQLDeleteCompiler(SQLCompiler): def _expr_refs_base_model(cls, expr, base_model): if isinstance(expr, Query): return expr.model == base_model - if not hasattr(expr, "get_source_expressions"): + if not hasattr(expr, 'get_source_expressions'): return False return any( cls._expr_refs_base_model(source_expr, base_model) @@ -1673,17 +1448,17 @@ class SQLDeleteCompiler(SQLCompiler): def contains_self_reference_subquery(self): return any( self._expr_refs_base_model(expr, self.query.model) - for expr in chain( - self.query.annotations.values(), self.query.where.children - ) + for expr in chain(self.query.annotations.values(), self.query.where.children) ) def _as_sql(self, query): - result = ["DELETE FROM %s" % self.quote_name_unless_alias(query.base_table)] + result = [ + 'DELETE FROM %s' % self.quote_name_unless_alias(query.base_table) + ] where, params = self.compile(query.where) if where: - result.append("WHERE %s" % where) - return " ".join(result), tuple(params) + result.append('WHERE %s' % where) + return ' '.join(result), tuple(params) def as_sql(self): """ @@ -1696,14 +1471,17 @@ class SQLDeleteCompiler(SQLCompiler): innerq.__class__ = Query innerq.clear_select_clause() pk = self.query.model._meta.pk - innerq.select = [pk.get_col(self.query.get_initial_alias())] + innerq.select = [ + pk.get_col(self.query.get_initial_alias()) + ] outerq = Query(self.query.model) + outerq.where = self.query.where_class() if not self.connection.features.update_can_self_select: # Force the materialization of the inner query to allow reference # to the target table on MySQL. sql, params = innerq.get_compiler(connection=self.connection).as_sql() - innerq = RawSQL("SELECT * FROM (%s) subquery" % sql, params) - outerq.add_filter("pk__in", innerq) + innerq = RawSQL('SELECT * FROM (%s) subquery' % sql, params) + outerq.add_q(Q(pk__in=innerq)) return self._as_sql(outerq) @@ -1715,25 +1493,23 @@ class SQLUpdateCompiler(SQLCompiler): """ self.pre_sql_setup() if not self.query.values: - return "", () + return '', () qn = self.quote_name_unless_alias values, update_params = [], [] for field, model, val in self.query.values: - if hasattr(val, "resolve_expression"): - val = val.resolve_expression( - self.query, allow_joins=False, for_save=True - ) + if hasattr(val, 'resolve_expression'): + val = val.resolve_expression(self.query, allow_joins=False, for_save=True) if val.contains_aggregate: raise FieldError( - "Aggregate functions are not allowed in this query " - "(%s=%r)." % (field.name, val) + 'Aggregate functions are not allowed in this query ' + '(%s=%r).' % (field.name, val) ) if val.contains_over_clause: raise FieldError( - "Window expressions are not allowed in this query " - "(%s=%r)." % (field.name, val) + 'Window expressions are not allowed in this query ' + '(%s=%r).' % (field.name, val) ) - elif hasattr(val, "prepare_database_save"): + elif hasattr(val, 'prepare_database_save'): if field.remote_field: val = field.get_db_prep_save( val.prepare_database_save(field), @@ -1749,29 +1525,29 @@ class SQLUpdateCompiler(SQLCompiler): val = field.get_db_prep_save(val, connection=self.connection) # Getting the placeholder for the field. - if hasattr(field, "get_placeholder"): + if hasattr(field, 'get_placeholder'): placeholder = field.get_placeholder(val, self, self.connection) else: - placeholder = "%s" + placeholder = '%s' name = field.column - if hasattr(val, "as_sql"): + if hasattr(val, 'as_sql'): sql, params = self.compile(val) - values.append("%s = %s" % (qn(name), placeholder % sql)) + values.append('%s = %s' % (qn(name), placeholder % sql)) update_params.extend(params) elif val is not None: - values.append("%s = %s" % (qn(name), placeholder)) + values.append('%s = %s' % (qn(name), placeholder)) update_params.append(val) else: - values.append("%s = NULL" % qn(name)) + values.append('%s = NULL' % qn(name)) table = self.query.base_table result = [ - "UPDATE %s SET" % qn(table), - ", ".join(values), + 'UPDATE %s SET' % qn(table), + ', '.join(values), ] where, params = self.compile(self.query.where) if where: - result.append("WHERE %s" % where) - return " ".join(result), tuple(update_params + params) + result.append('WHERE %s' % where) + return ' '.join(result), tuple(update_params + params) def execute_sql(self, result_type): """ @@ -1811,19 +1587,17 @@ class SQLUpdateCompiler(SQLCompiler): return query = self.query.chain(klass=Query) query.select_related = False - query.clear_ordering(force=True) + query.clear_ordering(True) query.extra = {} query.select = [] query.add_fields([query.get_meta().pk.name]) super().pre_sql_setup() - must_pre_select = ( - count > 1 and not self.connection.features.update_can_self_select - ) + must_pre_select = count > 1 and not self.connection.features.update_can_self_select # Now we adjust the current query: reset the where clause and get rid # of all the tables we don't need (since they're in the sub-select). - self.query.clear_where() + self.query.where = self.query.where_class() if self.query.related_updates or must_pre_select: # Either we're using the idents in multiple update queries (so # don't want them to change), or the db backend doesn't support @@ -1831,11 +1605,11 @@ class SQLUpdateCompiler(SQLCompiler): idents = [] for rows in query.get_compiler(self.using).execute_sql(MULTI): idents.extend(r[0] for r in rows) - self.query.add_filter("pk__in", idents) + self.query.add_filter(('pk__in', idents)) self.query.related_ids = idents else: # The fast path. Filters and updates in one query. - self.query.add_filter("pk__in", query) + self.query.add_filter(('pk__in', query)) self.query.reset_refcounts(refcounts_before) @@ -1852,14 +1626,13 @@ class SQLAggregateCompiler(SQLCompiler): sql.append(ann_sql) params.extend(ann_params) self.col_count = len(self.query.annotation_select) - sql = ", ".join(sql) + sql = ', '.join(sql) params = tuple(params) inner_query_sql, inner_query_params = self.query.inner_query.get_compiler( - self.using, - elide_empty=self.elide_empty, + self.using ).as_sql(with_col_aliases=True) - sql = "SELECT %s FROM (%s) subquery" % (sql, inner_query_sql) + sql = 'SELECT %s FROM (%s) subquery' % (sql, inner_query_sql) params = params + inner_query_params return sql, params diff --git a/venv/Lib/site-packages/django/db/models/sql/constants.py b/venv/Lib/site-packages/django/db/models/sql/constants.py index fdfb2ea..97edf75 100644 --- a/venv/Lib/site-packages/django/db/models/sql/constants.py +++ b/venv/Lib/site-packages/django/db/models/sql/constants.py @@ -1,6 +1,7 @@ """ Constants specific to the SQL storage portion of the ORM. """ +from django.utils.regex_helper import _lazy_re_compile # Size of each "chunk" for get_iterator calls. # Larger values are slightly faster at the expense of more storage space. @@ -9,16 +10,17 @@ GET_ITERATOR_CHUNK_SIZE = 100 # Namedtuples for sql.* internal use. # How many results to expect from a cursor.execute call -MULTI = "multi" -SINGLE = "single" -CURSOR = "cursor" -NO_RESULTS = "no results" +MULTI = 'multi' +SINGLE = 'single' +CURSOR = 'cursor' +NO_RESULTS = 'no results' ORDER_DIR = { - "ASC": ("ASC", "DESC"), - "DESC": ("DESC", "ASC"), + 'ASC': ('ASC', 'DESC'), + 'DESC': ('DESC', 'ASC'), } +ORDER_PATTERN = _lazy_re_compile(r'[-+]?[.\w]+$') # SQL join types. -INNER = "INNER JOIN" -LOUTER = "LEFT OUTER JOIN" +INNER = 'INNER JOIN' +LOUTER = 'LEFT OUTER JOIN' diff --git a/venv/Lib/site-packages/django/db/models/sql/datastructures.py b/venv/Lib/site-packages/django/db/models/sql/datastructures.py index 1edf040..c9598b3 100644 --- a/venv/Lib/site-packages/django/db/models/sql/datastructures.py +++ b/venv/Lib/site-packages/django/db/models/sql/datastructures.py @@ -11,7 +11,6 @@ class MultiJoin(Exception): multi-valued join was attempted (if the caller wants to treat that exceptionally). """ - def __init__(self, names_pos, path_with_names): self.level = names_pos # The path travelled, this includes the path to the multijoin. @@ -26,8 +25,7 @@ class Join: """ Used by sql.Query and sql.SQLCompiler to generate JOIN clauses into the FROM entry. For example, the SQL generated could be - LEFT OUTER JOIN "sometable" T1 - ON ("othertable"."sometable_id" = "sometable"."id") + LEFT OUTER JOIN "sometable" T1 ON ("othertable"."sometable_id" = "sometable"."id") This class is primarily used in Query.alias_map. All entries in alias_map must be Join compatible by providing the following attributes and methods: @@ -40,17 +38,8 @@ class Join: - as_sql() - relabeled_clone() """ - - def __init__( - self, - table_name, - parent_alias, - table_alias, - join_type, - join_field, - nullable, - filtered_relation=None, - ): + def __init__(self, table_name, parent_alias, table_alias, join_type, + join_field, nullable, filtered_relation=None): # Join table self.table_name = table_name self.parent_alias = parent_alias @@ -80,47 +69,36 @@ class Join: # Add a join condition for each pair of joining columns. for lhs_col, rhs_col in self.join_cols: - join_conditions.append( - "%s.%s = %s.%s" - % ( - qn(self.parent_alias), - qn2(lhs_col), - qn(self.table_alias), - qn2(rhs_col), - ) - ) + join_conditions.append('%s.%s = %s.%s' % ( + qn(self.parent_alias), + qn2(lhs_col), + qn(self.table_alias), + qn2(rhs_col), + )) # Add a single condition inside parentheses for whatever # get_extra_restriction() returns. extra_cond = self.join_field.get_extra_restriction( - self.table_alias, self.parent_alias - ) + compiler.query.where_class, self.table_alias, self.parent_alias) if extra_cond: extra_sql, extra_params = compiler.compile(extra_cond) - join_conditions.append("(%s)" % extra_sql) + join_conditions.append('(%s)' % extra_sql) params.extend(extra_params) if self.filtered_relation: extra_sql, extra_params = compiler.compile(self.filtered_relation) if extra_sql: - join_conditions.append("(%s)" % extra_sql) + join_conditions.append('(%s)' % extra_sql) params.extend(extra_params) if not join_conditions: # This might be a rel on the other end of an actual declared field. - declared_field = getattr(self.join_field, "field", self.join_field) + declared_field = getattr(self.join_field, 'field', self.join_field) raise ValueError( "Join generated an empty ON clause. %s did not yield either " "joining columns or extra restrictions." % declared_field.__class__ ) - on_clause_sql = " AND ".join(join_conditions) - alias_str = ( - "" if self.table_alias == self.table_name else (" %s" % self.table_alias) - ) - sql = "%s %s%s ON (%s)" % ( - self.join_type, - qn(self.table_name), - alias_str, - on_clause_sql, - ) + on_clause_sql = ' AND '.join(join_conditions) + alias_str = '' if self.table_alias == self.table_name else (' %s' % self.table_alias) + sql = '%s %s%s ON (%s)' % (self.join_type, qn(self.table_name), alias_str, on_clause_sql) return sql, params def relabeled_clone(self, change_map): @@ -128,19 +106,12 @@ class Join: new_table_alias = change_map.get(self.table_alias, self.table_alias) if self.filtered_relation is not None: filtered_relation = self.filtered_relation.clone() - filtered_relation.path = [ - change_map.get(p, p) for p in self.filtered_relation.path - ] + filtered_relation.path = [change_map.get(p, p) for p in self.filtered_relation.path] else: filtered_relation = None return self.__class__( - self.table_name, - new_parent_alias, - new_table_alias, - self.join_type, - self.join_field, - self.nullable, - filtered_relation=filtered_relation, + self.table_name, new_parent_alias, new_table_alias, self.join_type, + self.join_field, self.nullable, filtered_relation=filtered_relation, ) @property @@ -161,8 +132,9 @@ class Join: def __hash__(self): return hash(self.identity) - def equals(self, other): - # Ignore filtered_relation in equality check. + def equals(self, other, with_filtered_relation): + if with_filtered_relation: + return self == other return self.identity[:-1] == other.identity[:-1] def demote(self): @@ -183,7 +155,6 @@ class BaseTable: SELECT * FROM "foo" WHERE somecond could be generated by this class. """ - join_type = None parent_alias = None filtered_relation = None @@ -193,16 +164,12 @@ class BaseTable: self.table_alias = alias def as_sql(self, compiler, connection): - alias_str = ( - "" if self.table_alias == self.table_name else (" %s" % self.table_alias) - ) + alias_str = '' if self.table_alias == self.table_name else (' %s' % self.table_alias) base_sql = compiler.quote_name_unless_alias(self.table_name) return base_sql + alias_str, [] def relabeled_clone(self, change_map): - return self.__class__( - self.table_name, change_map.get(self.table_alias, self.table_alias) - ) + return self.__class__(self.table_name, change_map.get(self.table_alias, self.table_alias)) @property def identity(self): @@ -216,5 +183,5 @@ class BaseTable: def __hash__(self): return hash(self.identity) - def equals(self, other): + def equals(self, other, with_filtered_relation): return self.identity == other.identity diff --git a/venv/Lib/site-packages/django/db/models/sql/query.py b/venv/Lib/site-packages/django/db/models/sql/query.py index fd4bbda..f5f85a4 100644 --- a/venv/Lib/site-packages/django/db/models/sql/query.py +++ b/venv/Lib/site-packages/django/db/models/sql/query.py @@ -9,7 +9,9 @@ all about the internals of models in order to get the information it needs. import copy import difflib import functools +import inspect import sys +import warnings from collections import Counter, namedtuple from collections.abc import Iterator, Mapping from itertools import chain, count, product @@ -20,46 +22,35 @@ from django.db import DEFAULT_DB_ALIAS, NotSupportedError, connections from django.db.models.aggregates import Count from django.db.models.constants import LOOKUP_SEP from django.db.models.expressions import ( - BaseExpression, - Col, - Exists, - F, - OuterRef, - Ref, - ResolvedOuterRef, + BaseExpression, Col, Exists, F, OuterRef, Ref, ResolvedOuterRef, ) from django.db.models.fields import Field from django.db.models.fields.related_lookups import MultiColSource from django.db.models.lookups import Lookup from django.db.models.query_utils import ( - Q, - check_rel_lookup_compatibility, - refs_expression, + Q, check_rel_lookup_compatibility, refs_expression, ) -from django.db.models.sql.constants import INNER, LOUTER, ORDER_DIR, SINGLE -from django.db.models.sql.datastructures import BaseTable, Empty, Join, MultiJoin -from django.db.models.sql.where import AND, OR, ExtraWhere, NothingNode, WhereNode +from django.db.models.sql.constants import ( + INNER, LOUTER, ORDER_DIR, ORDER_PATTERN, SINGLE, +) +from django.db.models.sql.datastructures import ( + BaseTable, Empty, Join, MultiJoin, +) +from django.db.models.sql.where import ( + AND, OR, ExtraWhere, NothingNode, WhereNode, +) +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import cached_property -from django.utils.regex_helper import _lazy_re_compile from django.utils.tree import Node -__all__ = ["Query", "RawQuery"] - -# Quotation marks ('"`[]), whitespace characters, semicolons, or inline -# SQL comments are forbidden in column aliases. -FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|--|/\*|\*/") - -# Inspired from -# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS -EXPLAIN_OPTIONS_PATTERN = _lazy_re_compile(r"[\w\-]+") +__all__ = ['Query', 'RawQuery'] def get_field_names_from_opts(opts): - return set( - chain.from_iterable( - (f.name, f.attname) if f.concrete else (f.name,) for f in opts.get_fields() - ) - ) + return set(chain.from_iterable( + (f.name, f.attname) if f.concrete else (f.name,) + for f in opts.get_fields() + )) def get_children_from_q(q): @@ -71,8 +62,8 @@ def get_children_from_q(q): JoinInfo = namedtuple( - "JoinInfo", - ("final_field", "targets", "opts", "joins", "path", "transform_function"), + 'JoinInfo', + ('final_field', 'targets', 'opts', 'joins', 'path', 'transform_function') ) @@ -101,7 +92,8 @@ class RawQuery: if self.cursor is None: self._execute_query() converter = connections[self.using].introspection.identifier_converter - return [converter(column_meta[0]) for column_meta in self.cursor.description] + return [converter(column_meta[0]) + for column_meta in self.cursor.description] def __iter__(self): # Always execute a new query for a new iterator. @@ -149,19 +141,15 @@ class RawQuery: self.cursor.execute(self.sql, params) -ExplainInfo = namedtuple("ExplainInfo", ("format", "options")) - - class Query(BaseExpression): """A single SQL query.""" - alias_prefix = "T" - empty_result_set_value = None + alias_prefix = 'T' subq_aliases = frozenset([alias_prefix]) - compiler = "SQLCompiler" + compiler = 'SQLCompiler' - def __init__(self, model, alias_cols=True): + def __init__(self, model, where=WhereNode, alias_cols=True): self.model = model self.alias_refcount = {} # alias_map is the most important data structure regarding joins. @@ -177,7 +165,7 @@ class Query(BaseExpression): # aliases too. # Map external tables to whether they are aliased. self.external_aliases = {} - self.table_map = {} # Maps table names to list of aliases. + self.table_map = {} # Maps table names to list of aliases. self.default_cols = True self.default_ordering = True self.standard_ordering = True @@ -192,7 +180,8 @@ class Query(BaseExpression): # clause to contain other than default fields (values(), subqueries...) # Note that annotations go to annotations dictionary. self.select = () - self.where = WhereNode() + self.where = where() + self.where_class = where # The group_by attribute can have one of the following forms: # - None: no group by at all in the query # - A tuple of expressions: group by (at least) those expressions. @@ -244,21 +233,21 @@ class Query(BaseExpression): self._filtered_relations = {} - self.explain_info = None + self.explain_query = False + self.explain_format = None + self.explain_options = {} @property def output_field(self): if len(self.select) == 1: select = self.select[0] - return getattr(select, "target", None) or select.field + return getattr(select, 'target', None) or select.field elif len(self.annotation_select) == 1: return next(iter(self.annotation_select.values())).output_field @property def has_select_fields(self): - return bool( - self.select or self.annotation_select_mask or self.extra_select_mask - ) + return bool(self.select or self.annotation_select_mask or self.extra_select_mask) @cached_property def base_table(self): @@ -289,14 +278,12 @@ class Query(BaseExpression): memo[id(self)] = result return result - def get_compiler(self, using=None, connection=None, elide_empty=True): + def get_compiler(self, using=None, connection=None): if using is None and connection is None: raise ValueError("Need either using or connection") if using: connection = connections[using] - return connection.ops.compiler(self.compiler)( - self, connection, using, elide_empty - ) + return connection.ops.compiler(self.compiler)(self, connection, using) def get_meta(self): """ @@ -322,12 +309,11 @@ class Query(BaseExpression): obj.table_map = self.table_map.copy() obj.where = self.where.clone() obj.annotations = self.annotations.copy() - if self.annotation_select_mask is not None: + if self.annotation_select_mask is None: + obj.annotation_select_mask = None + else: obj.annotation_select_mask = self.annotation_select_mask.copy() - if self.combined_queries: - obj.combined_queries = tuple( - [query.clone() for query in self.combined_queries] - ) + obj.combined_queries = tuple(query.clone() for query in self.combined_queries) # _annotation_select_cache cannot be copied, as doing so breaks the # (necessary) state in which both annotations and # _annotation_select_cache point to the same underlying objects. @@ -335,15 +321,19 @@ class Query(BaseExpression): # used. obj._annotation_select_cache = None obj.extra = self.extra.copy() - if self.extra_select_mask is not None: + if self.extra_select_mask is None: + obj.extra_select_mask = None + else: obj.extra_select_mask = self.extra_select_mask.copy() - if self._extra_select_cache is not None: + if self._extra_select_cache is None: + obj._extra_select_cache = None + else: obj._extra_select_cache = self._extra_select_cache.copy() if self.select_related is not False: # Use deepcopy because select_related stores fields in nested # dicts. obj.select_related = copy.deepcopy(obj.select_related) - if "subq_aliases" in self.__dict__: + if 'subq_aliases' in self.__dict__: obj.subq_aliases = self.subq_aliases.copy() obj.used_aliases = self.used_aliases.copy() obj._filtered_relations = self._filtered_relations.copy() @@ -365,7 +355,7 @@ class Query(BaseExpression): if not obj.filter_is_sticky: obj.used_aliases = set() obj.filter_is_sticky = False - if hasattr(obj, "_setup_query"): + if hasattr(obj, '_setup_query'): obj._setup_query() return obj @@ -415,13 +405,11 @@ class Query(BaseExpression): break else: # An expression that is not selected the subquery. - if isinstance(expr, Col) or ( - expr.contains_aggregate and not expr.is_summary - ): + if isinstance(expr, Col) or (expr.contains_aggregate and not expr.is_summary): # Reference column or another aggregate. Select it # under a non-conflicting alias. col_cnt += 1 - col_alias = "__col%d" % col_cnt + col_alias = '__col%d' % col_cnt self.annotations[col_alias] = expr self.append_annotation_mask([col_alias]) new_expr = Ref(col_alias, expr) @@ -440,8 +428,8 @@ class Query(BaseExpression): if not self.annotation_select: return {} existing_annotations = [ - annotation - for alias, annotation in self.annotations.items() + annotation for alias, annotation + in self.annotations.items() if alias not in added_aggregate_names ] # Decide if we need to use a subquery. @@ -455,25 +443,20 @@ class Query(BaseExpression): # those operations must be done in a subquery so that the query # aggregates on the limit and/or distinct results instead of applying # the distinct and limit after the aggregation. - if ( - isinstance(self.group_by, tuple) - or self.is_sliced - or existing_annotations - or self.distinct - or self.combinator - ): + if (isinstance(self.group_by, tuple) or self.is_sliced or existing_annotations or + self.distinct or self.combinator): from django.db.models.sql.subqueries import AggregateQuery - inner_query = self.clone() inner_query.subquery = True outer_query = AggregateQuery(self.model, inner_query) inner_query.select_for_update = False inner_query.select_related = False inner_query.set_annotation_mask(self.annotation_select) - # Queries with distinct_fields need ordering and when a limit is - # applied we must take the slice from the ordered query. Otherwise - # no need for ordering. - inner_query.clear_ordering(force=False) + if not self.is_sliced and not self.distinct_fields: + # Queries with distinct_fields need ordering and when a limit + # is applied we must take the slice from the ordered query. + # Otherwise no need for ordering. + inner_query.clear_ordering(True) if not inner_query.distinct: # If the inner query uses default select and it has some # aggregate annotations, then we must make sure the inner @@ -481,18 +464,15 @@ class Query(BaseExpression): # clearing the select clause can alter results if distinct is # used. has_existing_aggregate_annotations = any( - annotation - for annotation in existing_annotations - if getattr(annotation, "contains_aggregate", True) + annotation for annotation in existing_annotations + if getattr(annotation, 'contains_aggregate', True) ) if inner_query.default_cols and has_existing_aggregate_annotations: - inner_query.group_by = ( - self.model._meta.pk.get_col(inner_query.get_initial_alias()), - ) + inner_query.group_by = (self.model._meta.pk.get_col(inner_query.get_initial_alias()),) inner_query.default_cols = False - relabels = {t: "subquery" for t in inner_query.alias_map} - relabels[None] = "subquery" + relabels = {t: 'subquery' for t in inner_query.alias_map} + relabels[None] = 'subquery' # Remove any aggregates marked for reduction from the subquery # and move them to the outer AggregateQuery. col_cnt = 0 @@ -500,43 +480,30 @@ class Query(BaseExpression): annotation_select_mask = inner_query.annotation_select_mask if expression.is_summary: expression, col_cnt = inner_query.rewrite_cols(expression, col_cnt) - outer_query.annotations[alias] = expression.relabeled_clone( - relabels - ) + outer_query.annotations[alias] = expression.relabeled_clone(relabels) del inner_query.annotations[alias] annotation_select_mask.remove(alias) # Make sure the annotation_select wont use cached results. inner_query.set_annotation_mask(inner_query.annotation_select_mask) - if ( - inner_query.select == () - and not inner_query.default_cols - and not inner_query.annotation_select_mask - ): + if inner_query.select == () and not inner_query.default_cols and not inner_query.annotation_select_mask: # In case of Model.objects[0:3].count(), there would be no # field selected in the inner query, yet we must use a subquery. # So, make sure at least one field is selected. - inner_query.select = ( - self.model._meta.pk.get_col(inner_query.get_initial_alias()), - ) + inner_query.select = (self.model._meta.pk.get_col(inner_query.get_initial_alias()),) else: outer_query = self self.select = () self.default_cols = False self.extra = {} - empty_set_result = [ - expression.empty_result_set_value - for expression in outer_query.annotation_select.values() - ] - elide_empty = not any(result is NotImplemented for result in empty_set_result) - outer_query.clear_ordering(force=True) + outer_query.clear_ordering(True) outer_query.clear_limits() outer_query.select_for_update = False outer_query.select_related = False - compiler = outer_query.get_compiler(using, elide_empty=elide_empty) + compiler = outer_query.get_compiler(using) result = compiler.execute_sql(SINGLE) if result is None: - result = empty_set_result + result = [None] * len(outer_query.annotation_select) converters = compiler.get_converters(outer_query.annotation_select.values()) result = next(compiler.apply_converters((result,), converters)) @@ -548,8 +515,8 @@ class Query(BaseExpression): Perform a COUNT() query using the current filter constraints. """ obj = self.clone() - obj.add_annotation(Count("*"), alias="__count", is_summary=True) - number = obj.get_aggregation(using, ["__count"])["__count"] + obj.add_annotation(Count('*'), alias='__count', is_summary=True) + number = obj.get_aggregation(using, ['__count'])['__count'] if number is None: number = 0 return number @@ -561,26 +528,22 @@ class Query(BaseExpression): q = self.clone() if not q.distinct: if q.group_by is True: - q.add_fields( - (f.attname for f in self.model._meta.concrete_fields), False - ) + q.add_fields((f.attname for f in self.model._meta.concrete_fields), False) # Disable GROUP BY aliases to avoid orphaning references to the # SELECT clause which is about to be cleared. q.set_group_by(allow_aliases=False) q.clear_select_clause() - if q.combined_queries and q.combinator == "union": - limit_combined = connections[ - using - ].features.supports_slicing_ordering_in_compound + if q.combined_queries and q.combinator == 'union': + limit_combined = connections[using].features.supports_slicing_ordering_in_compound q.combined_queries = tuple( combined_query.exists(using, limit=limit_combined) for combined_query in q.combined_queries ) - q.clear_ordering(force=True) + q.clear_ordering(True) if limit: q.set_limits(high=1) - q.add_extra({"a": 1}, None, None, None, None, None) - q.set_extra_mask(["a"]) + q.add_extra({'a': 1}, None, None, None, None, None) + q.set_extra_mask(['a']) return q def has_results(self, using): @@ -590,15 +553,11 @@ class Query(BaseExpression): def explain(self, using, format=None, **options): q = self.clone() - for option_name in options: - if ( - not EXPLAIN_OPTIONS_PATTERN.fullmatch(option_name) - or "--" in option_name - ): - raise ValueError(f"Invalid option name: {option_name!r}.") - q.explain_info = ExplainInfo(format, options) + q.explain_query = True + q.explain_format = format + q.explain_options = options compiler = q.get_compiler(using=using) - return "\n".join(compiler.explain_query()) + return '\n'.join(compiler.explain_query()) def combine(self, rhs, connector): """ @@ -609,18 +568,18 @@ class Query(BaseExpression): The 'connector' parameter describes how to connect filters from the 'rhs' query. """ - if self.model != rhs.model: - raise TypeError("Cannot combine queries on two different base models.") - if self.is_sliced: - raise TypeError("Cannot combine queries once a slice has been taken.") - if self.distinct != rhs.distinct: - raise TypeError("Cannot combine a unique query with a non-unique query.") - if self.distinct_fields != rhs.distinct_fields: - raise TypeError("Cannot combine queries with different distinct fields.") + assert self.model == rhs.model, \ + "Cannot combine queries on two different base models." + assert not self.is_sliced, \ + "Cannot combine queries once a slice has been taken." + assert self.distinct == rhs.distinct, \ + "Cannot combine a unique query with a non-unique query." + assert self.distinct_fields == rhs.distinct_fields, \ + "Cannot combine queries with different distinct fields." # Work out how to relabel the rhs aliases, if necessary. change_map = {} - conjunction = connector == AND + conjunction = (connector == AND) # Determine which existing joins can be reused. When combining the # query with AND we must recreate all joins for m2m filters. When @@ -640,8 +599,7 @@ class Query(BaseExpression): self.get_initial_alias() joinpromoter = JoinPromoter(connector, 2, False) joinpromoter.add_votes( - j for j in self.alias_map if self.alias_map[j].join_type == INNER - ) + j for j in self.alias_map if self.alias_map[j].join_type == INNER) rhs_votes = set() # Now, add the joins from rhs query into the new query (skipping base # table). @@ -690,10 +648,7 @@ class Query(BaseExpression): # really make sense (or return consistent value sets). Not worth # the extra complexity when you can write a real query instead. if self.extra and rhs.extra: - raise ValueError( - "When merging querysets using 'or', you cannot have " - "extra(select=...) on both sides." - ) + raise ValueError("When merging querysets using 'or', you cannot have extra(select=...) on both sides.") self.extra.update(rhs.extra) extra_select_mask = set() if self.extra_select_mask is not None: @@ -811,13 +766,11 @@ class Query(BaseExpression): # Create a new alias for this table. if alias_list: - alias = "%s%d" % (self.alias_prefix, len(self.alias_map) + 1) + alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1) alias_list.append(alias) else: # The first occurrence of a table uses the table name directly. - alias = ( - filtered_relation.alias if filtered_relation is not None else table_name - ) + alias = filtered_relation.alias if filtered_relation is not None else table_name self.table_map[table_name] = [alias] self.alias_refcount[alias] = 1 return alias, True @@ -852,19 +805,16 @@ class Query(BaseExpression): # Only the first alias (skipped above) should have None join_type assert self.alias_map[alias].join_type is not None parent_alias = self.alias_map[alias].parent_alias - parent_louter = ( - parent_alias and self.alias_map[parent_alias].join_type == LOUTER - ) + parent_louter = parent_alias and self.alias_map[parent_alias].join_type == LOUTER already_louter = self.alias_map[alias].join_type == LOUTER - if (self.alias_map[alias].nullable or parent_louter) and not already_louter: + if ((self.alias_map[alias].nullable or parent_louter) and + not already_louter): self.alias_map[alias] = self.alias_map[alias].promote() # Join type of 'alias' changed, so re-examine all aliases that # refer to this one. aliases.extend( - join - for join in self.alias_map - if self.alias_map[join].parent_alias == alias - and join not in aliases + join for join in self.alias_map + if self.alias_map[join].parent_alias == alias and join not in aliases ) def demote_joins(self, aliases): @@ -907,13 +857,10 @@ class Query(BaseExpression): # "group by" and "where". self.where.relabel_aliases(change_map) if isinstance(self.group_by, tuple): - self.group_by = tuple( - [col.relabeled_clone(change_map) for col in self.group_by] - ) + self.group_by = tuple([col.relabeled_clone(change_map) for col in self.group_by]) self.select = tuple([col.relabeled_clone(change_map) for col in self.select]) self.annotations = self.annotations and { - key: col.relabeled_clone(change_map) - for key, col in self.annotations.items() + key: col.relabeled_clone(change_map) for key, col in self.annotations.items() } # 2. Rename the alias in the internal table/alias datastructures. @@ -944,7 +891,6 @@ class Query(BaseExpression): conflict. Even tables that previously had no alias will get an alias after this call. """ - def prefix_gen(): """ Generate a sequence of characters in alphabetical order: @@ -958,9 +904,9 @@ class Query(BaseExpression): prefix = chr(ord(self.alias_prefix) + 1) yield prefix for n in count(1): - seq = alphabet[alphabet.index(prefix) :] if prefix else alphabet + seq = alphabet[alphabet.index(prefix):] if prefix else alphabet for s in product(seq, repeat=n): - yield "".join(s) + yield ''.join(s) prefix = None if self.alias_prefix != outer_query.alias_prefix: @@ -978,16 +924,14 @@ class Query(BaseExpression): break if pos > local_recursion_limit: raise RecursionError( - "Maximum recursion depth exceeded: too many subqueries." + 'Maximum recursion depth exceeded: too many subqueries.' ) self.subq_aliases = self.subq_aliases.union([self.alias_prefix]) outer_query.subq_aliases = outer_query.subq_aliases.union(self.subq_aliases) - self.change_aliases( - { - alias: "%s%d" % (self.alias_prefix, pos) - for pos, alias in enumerate(self.alias_map) - } - ) + self.change_aliases({ + alias: '%s%d' % (self.alias_prefix, pos) + for pos, alias in enumerate(self.alias_map) + }) def get_initial_alias(self): """ @@ -1027,12 +971,12 @@ class Query(BaseExpression): """ if reuse_with_filtered_relation and reuse: reuse_aliases = [ - a for a, j in self.alias_map.items() if a in reuse and j.equals(join) + a for a, j in self.alias_map.items() + if a in reuse and j.equals(join, with_filtered_relation=False) ] else: reuse_aliases = [ - a - for a, j in self.alias_map.items() + a for a, j in self.alias_map.items() if (reuse is None or a in reuse) and j == join ] if reuse_aliases: @@ -1046,9 +990,7 @@ class Query(BaseExpression): return reuse_alias # No reuse is possible, so we need a new alias. - alias, _ = self.table_alias( - join.table_name, create=True, filtered_relation=join.filtered_relation - ) + alias, _ = self.table_alias(join.table_name, create=True, filtered_relation=join.filtered_relation) if join.join_type: if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable: join_type = LOUTER @@ -1092,19 +1034,10 @@ class Query(BaseExpression): alias = seen[int_model] = join_info.joins[-1] return alias or seen[None] - def check_alias(self, alias): - if FORBIDDEN_ALIAS_PATTERN.search(alias): - raise ValueError( - "Column aliases cannot contain whitespace characters, quotation marks, " - "semicolons, or SQL comments." - ) - def add_annotation(self, annotation, alias, is_summary=False, select=True): """Add a single annotation expression to the Query.""" - self.check_alias(alias) - annotation = annotation.resolve_expression( - self, allow_joins=True, reuse=None, summarize=is_summary - ) + annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None, + summarize=is_summary) if select: self.append_annotation_mask([alias]) else: @@ -1116,49 +1049,43 @@ class Query(BaseExpression): # Subqueries need to use a different set of aliases than the outer query. clone.bump_prefix(query) clone.subquery = True + # It's safe to drop ordering if the queryset isn't using slicing, + # distinct(*fields) or select_for_update(). + if (self.low_mark == 0 and self.high_mark is None and + not self.distinct_fields and + not self.select_for_update): + clone.clear_ordering(True) clone.where.resolve_expression(query, *args, **kwargs) for key, value in clone.annotations.items(): resolved = value.resolve_expression(query, *args, **kwargs) - if hasattr(resolved, "external_aliases"): + if hasattr(resolved, 'external_aliases'): resolved.external_aliases.update(clone.external_aliases) clone.annotations[key] = resolved # Outer query's aliases are considered external. for alias, table in query.alias_map.items(): clone.external_aliases[alias] = ( - isinstance(table, Join) - and table.join_field.related_model._meta.db_table != alias - ) or ( - isinstance(table, BaseTable) and table.table_name != table.table_alias + (isinstance(table, Join) and table.join_field.related_model._meta.db_table != alias) or + (isinstance(table, BaseTable) and table.table_name != table.table_alias) ) return clone def get_external_cols(self): exprs = chain(self.annotations.values(), self.where.children) return [ - col - for col in self._gen_cols(exprs, include_external=True) + col for col in self._gen_cols(exprs, include_external=True) if col.alias in self.external_aliases ] def as_sql(self, compiler, connection): - # Some backends (e.g. Oracle) raise an error when a subquery contains - # unnecessary ORDER BY clause. - if ( - self.subquery - and not connection.features.ignores_unnecessary_order_by_in_subqueries - ): - self.clear_ordering(force=False) sql, params = self.get_compiler(connection=connection).as_sql() if self.subquery: - sql = "(%s)" % sql + sql = '(%s)' % sql return sql, params def resolve_lookup_value(self, value, can_reuse, allow_joins): - if hasattr(value, "resolve_expression"): + if hasattr(value, 'resolve_expression'): value = value.resolve_expression( - self, - reuse=can_reuse, - allow_joins=allow_joins, + self, reuse=can_reuse, allow_joins=allow_joins, ) elif isinstance(value, (list, tuple)): # The items of the iterable may be expressions and therefore need @@ -1168,7 +1095,7 @@ class Query(BaseExpression): for sub_value in value ) type_ = type(value) - if hasattr(type_, "_make"): # namedtuple + if hasattr(type_, '_make'): # namedtuple return type_(*values) return type_(values) return value @@ -1179,17 +1106,15 @@ class Query(BaseExpression): """ lookup_splitted = lookup.split(LOOKUP_SEP) if self.annotations: - expression, expression_lookups = refs_expression( - lookup_splitted, self.annotations - ) + expression, expression_lookups = refs_expression(lookup_splitted, self.annotations) if expression: return expression_lookups, (), expression _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta()) - field_parts = lookup_splitted[0 : len(lookup_splitted) - len(lookup_parts)] + field_parts = lookup_splitted[0:len(lookup_splitted) - len(lookup_parts)] if len(lookup_parts) > 1 and not field_parts: raise FieldError( - 'Invalid lookup "%s" for model %s".' - % (lookup, self.get_meta().model.__name__) + 'Invalid lookup "%s" for model %s".' % + (lookup, self.get_meta().model.__name__) ) return lookup_parts, field_parts, False @@ -1198,12 +1123,11 @@ class Query(BaseExpression): Check whether the object passed while querying is of the correct type. If not, raise a ValueError specifying the wrong object. """ - if hasattr(value, "_meta"): + if hasattr(value, '_meta'): if not check_rel_lookup_compatibility(value._meta.model, opts, field): raise ValueError( - 'Cannot query "%s": Must be "%s" instance.' - % (value, opts.object_name) - ) + 'Cannot query "%s": Must be "%s" instance.' % + (value, opts.object_name)) def check_related_objects(self, field, value, opts): """Check the type of object passed to query relations.""" @@ -1213,31 +1137,29 @@ class Query(BaseExpression): # opts would be Author's (from the author field) and value.model # would be Author.objects.all() queryset's .model (Author also). # The field is the related field on the lhs side. - if ( - isinstance(value, Query) - and not value.has_select_fields - and not check_rel_lookup_compatibility(value.model, opts, field) - ): + if (isinstance(value, Query) and not value.has_select_fields and + not check_rel_lookup_compatibility(value.model, opts, field)): raise ValueError( - 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' - % (value.model._meta.object_name, opts.object_name) + 'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' % + (value.model._meta.object_name, opts.object_name) ) - elif hasattr(value, "_meta"): + elif hasattr(value, '_meta'): self.check_query_object_type(value, opts, field) - elif hasattr(value, "__iter__"): + elif hasattr(value, '__iter__'): for v in value: self.check_query_object_type(v, opts, field) def check_filterable(self, expression): """Raise an error if expression cannot be used in a WHERE clause.""" - if hasattr(expression, "resolve_expression") and not getattr( - expression, "filterable", True + if ( + hasattr(expression, 'resolve_expression') and + not getattr(expression, 'filterable', True) ): raise NotSupportedError( - expression.__class__.__name__ + " is disallowed in the filter " - "clause." + expression.__class__.__name__ + ' is disallowed in the filter ' + 'clause.' ) - if hasattr(expression, "get_source_expressions"): + if hasattr(expression, 'get_source_expressions'): for expr in expression.get_source_expressions(): self.check_filterable(expr) @@ -1251,7 +1173,7 @@ class Query(BaseExpression): and get_transform(). """ # __exact is the default lookup if one isn't given. - *transforms, lookup_name = lookups or ["exact"] + *transforms, lookup_name = lookups or ['exact'] for name in transforms: lhs = self.try_transform(lhs, name) # First try get_lookup() so that the lookup takes precedence if the lhs @@ -1259,13 +1181,11 @@ class Query(BaseExpression): lookup_class = lhs.get_lookup(lookup_name) if not lookup_class: if lhs.field.is_relation: - raise FieldError( - "Related Field got invalid lookup: {}".format(lookup_name) - ) + raise FieldError('Related Field got invalid lookup: {}'.format(lookup_name)) # A lookup wasn't found. Try to interpret the name as a transform # and do an Exact lookup against it. lhs = self.try_transform(lhs, lookup_name) - lookup_name = "exact" + lookup_name = 'exact' lookup_class = lhs.get_lookup(lookup_name) if not lookup_class: return @@ -1274,20 +1194,17 @@ class Query(BaseExpression): # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all # uses of None as a query value unless the lookup supports it. if lookup.rhs is None and not lookup.can_use_none_as_rhs: - if lookup_name not in ("exact", "iexact"): + if lookup_name not in ('exact', 'iexact'): raise ValueError("Cannot use None as a query value") - return lhs.get_lookup("isnull")(lhs, True) + return lhs.get_lookup('isnull')(lhs, True) # For Oracle '' is equivalent to null. The check must be done at this # stage because join promotion can't be done in the compiler. Using # DEFAULT_DB_ALIAS isn't nice but it's the best that can be done here. # A similar thing is done in is_nullable(), too. - if ( - lookup_name == "exact" - and lookup.rhs == "" - and connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls - ): - return lhs.get_lookup("isnull")(lhs, True) + if (connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and + lookup_name == 'exact' and lookup.rhs == ''): + return lhs.get_lookup('isnull')(lhs, True) return lookup @@ -1301,29 +1218,19 @@ class Query(BaseExpression): return transform_class(lhs) else: output_field = lhs.output_field.__class__ - suggested_lookups = difflib.get_close_matches( - name, output_field.get_lookups() - ) + suggested_lookups = difflib.get_close_matches(name, output_field.get_lookups()) if suggested_lookups: - suggestion = ", perhaps you meant %s?" % " or ".join(suggested_lookups) + suggestion = ', perhaps you meant %s?' % ' or '.join(suggested_lookups) else: - suggestion = "." + suggestion = '.' raise FieldError( "Unsupported lookup '%s' for %s or join on the field not " "permitted%s" % (name, output_field.__name__, suggestion) ) - def build_filter( - self, - filter_expr, - branch_negated=False, - current_negated=False, - can_reuse=None, - allow_joins=True, - split_subq=True, - reuse_with_filtered_relation=False, - check_filterable=True, - ): + def build_filter(self, filter_expr, branch_negated=False, current_negated=False, + can_reuse=None, allow_joins=True, split_subq=True, + reuse_with_filtered_relation=False, check_filterable=True): """ Build a WhereNode for a single filter clause but don't add it to this Query. Query.add_q() will then add this filter to the where @@ -1364,13 +1271,15 @@ class Query(BaseExpression): split_subq=split_subq, check_filterable=check_filterable, ) - if hasattr(filter_expr, "resolve_expression"): - if not getattr(filter_expr, "conditional", False): - raise TypeError("Cannot filter against a non-conditional expression.") - condition = filter_expr.resolve_expression(self, allow_joins=allow_joins) - if not isinstance(condition, Lookup): - condition = self.build_lookup(["exact"], condition, True) - return WhereNode([condition], connector=AND), [] + if hasattr(filter_expr, 'resolve_expression'): + if not getattr(filter_expr, 'conditional', False): + raise TypeError('Cannot filter against a non-conditional expression.') + condition = self.build_lookup( + ['exact'], filter_expr.resolve_expression(self, allow_joins=allow_joins), True + ) + clause = self.where_class() + clause.add(condition, AND) + return clause, [] arg, value = filter_expr if not arg: raise FieldError("Cannot parse keyword query %r" % arg) @@ -1384,16 +1293,16 @@ class Query(BaseExpression): pre_joins = self.alias_refcount.copy() value = self.resolve_lookup_value(value, can_reuse, allow_joins) - used_joins = { - k for k, v in self.alias_refcount.items() if v > pre_joins.get(k, 0) - } + used_joins = {k for k, v in self.alias_refcount.items() if v > pre_joins.get(k, 0)} if check_filterable: self.check_filterable(value) + clause = self.where_class() if reffed_expression: condition = self.build_lookup(lookups, reffed_expression, value) - return WhereNode([condition], connector=AND), [] + clause.add(condition, AND) + return clause, [] opts = self.get_meta() alias = self.get_initial_alias() @@ -1401,11 +1310,7 @@ class Query(BaseExpression): try: join_info = self.setup_joins( - parts, - opts, - alias, - can_reuse=can_reuse, - allow_many=allow_many, + parts, opts, alias, can_reuse=can_reuse, allow_many=allow_many, reuse_with_filtered_relation=reuse_with_filtered_relation, ) @@ -1423,9 +1328,7 @@ class Query(BaseExpression): # Update used_joins before trimming since they are reused to determine # which joins could be later promoted to INNER. used_joins.update(join_info.joins) - targets, alias, join_list = self.trim_joins( - join_info.targets, join_info.joins, join_info.path - ) + targets, alias, join_list = self.trim_joins(join_info.targets, join_info.joins, join_info.path) if can_reuse is not None: can_reuse.update(join_list) @@ -1433,32 +1336,22 @@ class Query(BaseExpression): # No support for transforms for relational fields num_lookups = len(lookups) if num_lookups > 1: - raise FieldError( - "Related Field got invalid lookup: {}".format(lookups[0]) - ) + raise FieldError('Related Field got invalid lookup: {}'.format(lookups[0])) if len(targets) == 1: col = self._get_col(targets[0], join_info.final_field, alias) else: - col = MultiColSource( - alias, targets, join_info.targets, join_info.final_field - ) + col = MultiColSource(alias, targets, join_info.targets, join_info.final_field) else: col = self._get_col(targets[0], join_info.final_field, alias) condition = self.build_lookup(lookups, col, value) lookup_type = condition.lookup_name - clause = WhereNode([condition], connector=AND) + clause.add(condition, AND) - require_outer = ( - lookup_type == "isnull" and condition.rhs is True and not current_negated - ) - if ( - current_negated - and (lookup_type != "isnull" or condition.rhs is False) - and condition.rhs is not None - ): + require_outer = lookup_type == 'isnull' and condition.rhs is True and not current_negated + if current_negated and (lookup_type != 'isnull' or condition.rhs is False) and condition.rhs is not None: require_outer = True - if lookup_type != "isnull": + if lookup_type != 'isnull': # The condition added here will be SQL like this: # NOT (col IS NOT NULL), where the first NOT is added in # upper layers of code. The reason for addition is that if col @@ -1469,21 +1362,21 @@ class Query(BaseExpression): # <=> # NOT (col IS NOT NULL AND col = someval). if ( - self.is_nullable(targets[0]) - or self.alias_map[join_list[-1]].join_type == LOUTER + self.is_nullable(targets[0]) or + self.alias_map[join_list[-1]].join_type == LOUTER ): - lookup_class = targets[0].get_lookup("isnull") + lookup_class = targets[0].get_lookup('isnull') col = self._get_col(targets[0], join_info.targets[0], alias) clause.add(lookup_class(col, False), AND) # If someval is a nullable column, someval IS NOT NULL is # added. if isinstance(value, Col) and self.is_nullable(value.target): - lookup_class = value.target.get_lookup("isnull") + lookup_class = value.target.get_lookup('isnull') clause.add(lookup_class(value, False), AND) return clause, used_joins if not require_outer else () - def add_filter(self, filter_lhs, filter_rhs): - self.add_q(Q((filter_lhs, filter_rhs))) + def add_filter(self, filter_clause): + self.add_q(Q(**{filter_clause[0]: filter_clause[1]})) def add_q(self, q_object): """ @@ -1496,9 +1389,7 @@ class Query(BaseExpression): # (Consider case where rel_a is LOUTER and rel_a__col=1 is added - if # rel_a doesn't produce any rows, then the whole condition must fail. # So, demotion is OK. - existing_inner = { - a for a in self.alias_map if self.alias_map[a].join_type == INNER - } + existing_inner = {a for a in self.alias_map if self.alias_map[a].join_type == INNER} clause, _ = self._add_q(q_object, self.used_aliases) if clause: self.where.add(clause, AND) @@ -1507,36 +1398,21 @@ class Query(BaseExpression): def build_where(self, filter_expr): return self.build_filter(filter_expr, allow_joins=False)[0] - def clear_where(self): - self.where = WhereNode() - - def _add_q( - self, - q_object, - used_aliases, - branch_negated=False, - current_negated=False, - allow_joins=True, - split_subq=True, - check_filterable=True, - ): + def _add_q(self, q_object, used_aliases, branch_negated=False, + current_negated=False, allow_joins=True, split_subq=True, + check_filterable=True): """Add a Q-object to the current filter.""" connector = q_object.connector current_negated = current_negated ^ q_object.negated branch_negated = branch_negated or q_object.negated - target_clause = WhereNode(connector=connector, negated=q_object.negated) - joinpromoter = JoinPromoter( - q_object.connector, len(q_object.children), current_negated - ) + target_clause = self.where_class(connector=connector, + negated=q_object.negated) + joinpromoter = JoinPromoter(q_object.connector, len(q_object.children), current_negated) for child in q_object.children: child_clause, needed_inner = self.build_filter( - child, - can_reuse=used_aliases, - branch_negated=branch_negated, - current_negated=current_negated, - allow_joins=allow_joins, - split_subq=split_subq, - check_filterable=check_filterable, + child, can_reuse=used_aliases, branch_negated=branch_negated, + current_negated=current_negated, allow_joins=allow_joins, + split_subq=split_subq, check_filterable=check_filterable, ) joinpromoter.add_votes(needed_inner) if child_clause: @@ -1544,30 +1420,23 @@ class Query(BaseExpression): needed_inner = joinpromoter.update_join_types(self) return target_clause, needed_inner - def build_filtered_relation_q( - self, q_object, reuse, branch_negated=False, current_negated=False - ): + def build_filtered_relation_q(self, q_object, reuse, branch_negated=False, current_negated=False): """Add a FilteredRelation object to the current filter.""" connector = q_object.connector current_negated ^= q_object.negated branch_negated = branch_negated or q_object.negated - target_clause = WhereNode(connector=connector, negated=q_object.negated) + target_clause = self.where_class(connector=connector, negated=q_object.negated) for child in q_object.children: if isinstance(child, Node): child_clause = self.build_filtered_relation_q( - child, - reuse=reuse, - branch_negated=branch_negated, + child, reuse=reuse, branch_negated=branch_negated, current_negated=current_negated, ) else: child_clause, _ = self.build_filter( - child, - can_reuse=reuse, - branch_negated=branch_negated, + child, can_reuse=reuse, branch_negated=branch_negated, current_negated=current_negated, - allow_joins=True, - split_subq=False, + allow_joins=True, split_subq=False, reuse_with_filtered_relation=True, ) target_clause.add(child_clause, connector) @@ -1576,9 +1445,7 @@ class Query(BaseExpression): def add_filtered_relation(self, filtered_relation, alias): filtered_relation.alias = alias lookups = dict(get_children_from_q(filtered_relation.condition)) - relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type( - filtered_relation.relation_name - ) + relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type(filtered_relation.relation_name) if relation_lookup_parts: raise ValueError( "FilteredRelation's relation_name cannot contain lookups " @@ -1622,7 +1489,7 @@ class Query(BaseExpression): path, names_with_path = [], [] for pos, name in enumerate(names): cur_names_with_path = (name, []) - if name == "pk": + if name == 'pk': name = opts.pk.name field = None @@ -1637,10 +1504,7 @@ class Query(BaseExpression): if LOOKUP_SEP in filtered_relation.relation_name: parts = filtered_relation.relation_name.split(LOOKUP_SEP) filtered_relation_path, field, _, _ = self.names_to_path( - parts, - opts, - allow_many, - fail_on_missing, + parts, opts, allow_many, fail_on_missing, ) path.extend(filtered_relation_path[:-1]) else: @@ -1667,17 +1531,13 @@ class Query(BaseExpression): # one step. pos -= 1 if pos == -1 or fail_on_missing: - available = sorted( - [ - *get_field_names_from_opts(opts), - *self.annotation_select, - *self._filtered_relations, - ] - ) - raise FieldError( - "Cannot resolve keyword '%s' into field. " - "Choices are: %s" % (name, ", ".join(available)) - ) + available = sorted([ + *get_field_names_from_opts(opts), + *self.annotation_select, + *self._filtered_relations, + ]) + raise FieldError("Cannot resolve keyword '%s' into field. " + "Choices are: %s" % (name, ", ".join(available))) break # Check if we need any joins for concrete inheritance cases (the # field lives in parent, but we are currently in one of its @@ -1688,12 +1548,12 @@ class Query(BaseExpression): path.extend(path_to_parent) cur_names_with_path[1].extend(path_to_parent) opts = path_to_parent[-1].to_opts - if hasattr(field, "get_path_info"): + if hasattr(field, 'get_path_info'): pathinfos = field.get_path_info(filtered_relation) if not allow_many: for inner_pos, p in enumerate(pathinfos): if p.m2m: - cur_names_with_path[1].extend(pathinfos[0 : inner_pos + 1]) + cur_names_with_path[1].extend(pathinfos[0:inner_pos + 1]) names_with_path.append(cur_names_with_path) raise MultiJoin(pos + 1, names_with_path) last = pathinfos[-1] @@ -1710,20 +1570,12 @@ class Query(BaseExpression): if fail_on_missing and pos + 1 != len(names): raise FieldError( "Cannot resolve keyword %r into field. Join on '%s'" - " not permitted." % (names[pos + 1], name) - ) + " not permitted." % (names[pos + 1], name)) break - return path, final_field, targets, names[pos + 1 :] + return path, final_field, targets, names[pos + 1:] - def setup_joins( - self, - names, - opts, - alias, - can_reuse=None, - allow_many=True, - reuse_with_filtered_relation=False, - ): + def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True, + reuse_with_filtered_relation=False): """ Compute the necessary table joins for the passage through the fields given in 'names'. 'opts' is the Options class for the current model @@ -1771,10 +1623,7 @@ class Query(BaseExpression): for pivot in range(len(names), 0, -1): try: path, final_field, targets, rest = self.names_to_path( - names[:pivot], - opts, - allow_many, - fail_on_missing=True, + names[:pivot], opts, allow_many, fail_on_missing=True, ) except FieldError as exc: if pivot == 1: @@ -1789,7 +1638,6 @@ class Query(BaseExpression): transforms = names[pivot:] break for name in transforms: - def transform(field, alias, *, name, previous): try: wrapped = previous(field, alias) @@ -1800,10 +1648,7 @@ class Query(BaseExpression): raise last_field_exception else: raise - - final_transformer = functools.partial( - transform, name=name, previous=final_transformer - ) + final_transformer = functools.partial(transform, name=name, previous=final_transformer) # Then, add the path to the query's joins. Note that we can't trim # joins at this stage - we will need the information about join type # of the trimmed joins. @@ -1820,18 +1665,12 @@ class Query(BaseExpression): else: nullable = True connection = Join( - opts.db_table, - alias, - table_alias, - INNER, - join.join_field, - nullable, - filtered_relation=filtered_relation, + opts.db_table, alias, table_alias, INNER, join.join_field, + nullable, filtered_relation=filtered_relation, ) reuse = can_reuse if join.m2m or reuse_with_filtered_relation else None alias = self.join( - connection, - reuse=reuse, + connection, reuse=reuse, reuse_with_filtered_relation=reuse_with_filtered_relation, ) joins.append(alias) @@ -1862,11 +1701,7 @@ class Query(BaseExpression): cur_targets = {t.column for t in targets} if not cur_targets.issubset(join_targets): break - targets_dict = { - r[1].column: r[0] - for r in info.join_field.related_fields - if r[1].column in cur_targets - } + targets_dict = {r[1].column: r[0] for r in info.join_field.related_fields if r[1].column in cur_targets} targets = tuple(targets_dict[t.column] for t in targets) self.unref_alias(joins.pop()) return targets, joins[-1], joins @@ -1876,11 +1711,9 @@ class Query(BaseExpression): for expr in exprs: if isinstance(expr, Col): yield expr - elif include_external and callable( - getattr(expr, "get_external_cols", None) - ): + elif include_external and callable(getattr(expr, 'get_external_cols', None)): yield from expr.get_external_cols() - elif hasattr(expr, "get_source_expressions"): + else: yield from cls._gen_cols( expr.get_source_expressions(), include_external=include_external, @@ -1897,7 +1730,8 @@ class Query(BaseExpression): for alias in self._gen_col_aliases([annotation]): if isinstance(self.alias_map[alias], Join): raise FieldError( - "Joined field references are not permitted in this query" + 'Joined field references are not permitted in ' + 'this query' ) if summarize: # Summarize currently means we are doing an aggregate() query @@ -1919,20 +1753,13 @@ class Query(BaseExpression): for transform in field_list[1:]: annotation = self.try_transform(annotation, transform) return annotation - join_info = self.setup_joins( - field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse - ) - targets, final_alias, join_list = self.trim_joins( - join_info.targets, join_info.joins, join_info.path - ) + join_info = self.setup_joins(field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse) + targets, final_alias, join_list = self.trim_joins(join_info.targets, join_info.joins, join_info.path) if not allow_joins and len(join_list) > 1: - raise FieldError( - "Joined field references are not permitted in this query" - ) + raise FieldError('Joined field references are not permitted in this query') if len(targets) > 1: - raise FieldError( - "Referencing multicolumn fields with F() objects isn't supported" - ) + raise FieldError("Referencing multicolumn fields with F() objects " + "isn't supported") # Verify that the last lookup in name is a field or a transform: # transform_function() raises FieldError if not. transform = join_info.transform_function(targets[0], final_alias) @@ -1959,16 +1786,16 @@ class Query(BaseExpression): LIMIT 1 ) """ + filter_lhs, filter_rhs = filter_expr + if isinstance(filter_rhs, OuterRef): + filter_expr = (filter_lhs, OuterRef(filter_rhs)) + elif isinstance(filter_rhs, F): + filter_expr = (filter_lhs, OuterRef(filter_rhs.name)) # Generate the inner query. query = Query(self.model) query._filtered_relations = self._filtered_relations - filter_lhs, filter_rhs = filter_expr - if isinstance(filter_rhs, OuterRef): - filter_rhs = OuterRef(filter_rhs) - elif isinstance(filter_rhs, F): - filter_rhs = OuterRef(filter_rhs.name) - query.add_filter(filter_lhs, filter_rhs) - query.clear_ordering(force=True) + query.add_filter(filter_expr) + query.clear_ordering(True) # Try to have as simple as possible subquery -> trim leading joins from # the subquery. trimmed_prefix, contains_louter = query.trim_start(names_with_path) @@ -1981,25 +1808,23 @@ class Query(BaseExpression): # Need to add a restriction so that outer query's filters are in effect for # the subquery, too. query.bump_prefix(self) - lookup_class = select_field.get_lookup("exact") + lookup_class = select_field.get_lookup('exact') # Note that the query.select[0].alias is different from alias # due to bump_prefix above. - lookup = lookup_class(pk.get_col(query.select[0].alias), pk.get_col(alias)) + lookup = lookup_class(pk.get_col(query.select[0].alias), + pk.get_col(alias)) query.where.add(lookup, AND) query.external_aliases[alias] = True - lookup_class = select_field.get_lookup("exact") + lookup_class = select_field.get_lookup('exact') lookup = lookup_class(col, ResolvedOuterRef(trimmed_prefix)) query.where.add(lookup, AND) condition, needed_inner = self.build_filter(Exists(query)) if contains_louter: or_null_condition, _ = self.build_filter( - ("%s__isnull" % trimmed_prefix, True), - current_negated=True, - branch_negated=True, - can_reuse=can_reuse, - ) + ('%s__isnull' % trimmed_prefix, True), + current_negated=True, branch_negated=True, can_reuse=can_reuse) condition.add(or_null_condition, OR) # Note that the end result will be: # (outercol NOT IN innerq AND outercol IS NOT NULL) OR outercol IS NULL. @@ -2077,8 +1902,8 @@ class Query(BaseExpression): self.values_select = () def add_select_col(self, col, name): - self.select += (col,) - self.values_select += (name,) + self.select += col, + self.values_select += name, def set_select(self, cols): self.default_cols = False @@ -2104,9 +1929,7 @@ class Query(BaseExpression): for name in field_names: # Join promotion note - we must not remove any rows here, so # if there is no existing joins, use outer join. - join_info = self.setup_joins( - name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m - ) + join_info = self.setup_joins(name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m) targets, final_alias, joins = self.trim_joins( join_info.targets, join_info.joins, @@ -2129,18 +1952,12 @@ class Query(BaseExpression): "it." % name ) else: - names = sorted( - [ - *get_field_names_from_opts(opts), - *self.extra, - *self.annotation_select, - *self._filtered_relations, - ] - ) - raise FieldError( - "Cannot resolve keyword %r into field. " - "Choices are: %s" % (name, ", ".join(names)) - ) + names = sorted([ + *get_field_names_from_opts(opts), *self.extra, + *self.annotation_select, *self._filtered_relations + ]) + raise FieldError("Cannot resolve keyword %r into field. " + "Choices are: %s" % (name, ", ".join(names))) def add_ordering(self, *ordering): """ @@ -2154,9 +1971,18 @@ class Query(BaseExpression): errors = [] for item in ordering: if isinstance(item, str): - if item == "?": + if '.' in item and ORDER_PATTERN.match(item): + warnings.warn( + 'Passing column raw column aliases to order_by() is ' + 'deprecated. Wrap %r in a RawSQL expression before ' + 'passing it to order_by().' % item, + category=RemovedInDjango40Warning, + stacklevel=3, + ) continue - if item.startswith("-"): + if item == '?': + continue + if item.startswith('-'): item = item[1:] if item in self.annotations: continue @@ -2165,34 +1991,28 @@ class Query(BaseExpression): # names_to_path() validates the lookup. A descriptive # FieldError will be raise if it's not. self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) - elif not hasattr(item, "resolve_expression"): + elif not hasattr(item, 'resolve_expression'): errors.append(item) - if getattr(item, "contains_aggregate", False): + if getattr(item, 'contains_aggregate', False): raise FieldError( - "Using an aggregate in order_by() without also including " - "it in annotate() is not allowed: %s" % item + 'Using an aggregate in order_by() without also including ' + 'it in annotate() is not allowed: %s' % item ) if errors: - raise FieldError("Invalid order_by arguments: %s" % errors) + raise FieldError('Invalid order_by arguments: %s' % errors) if ordering: self.order_by += ordering else: self.default_ordering = False - def clear_ordering(self, force=False, clear_default=True): + def clear_ordering(self, force_empty): """ - Remove any ordering settings if the current query allows it without - side effects, set 'force' to True to clear the ordering regardless. - If 'clear_default' is True, there will be no ordering in the resulting - query (not even the model's default). + Remove any ordering settings. If 'force_empty' is True, there will be + no ordering in the resulting query (not even the model's default). """ - if not force and ( - self.is_sliced or self.distinct_fields or self.select_for_update - ): - return self.order_by = () self.extra_order_by = () - if clear_default: + if force_empty: self.default_ordering = False def set_group_by(self, allow_aliases=True): @@ -2211,17 +2031,28 @@ class Query(BaseExpression): for join in list(self.alias_map.values())[1:]: # Skip base table. model = join.join_field.related_model if model not in seen_models: - column_names.update( - {field.column for field in model._meta.local_concrete_fields} - ) + column_names.update({ + field.column + for field in model._meta.local_concrete_fields + }) seen_models.add(model) group_by = list(self.select) if self.annotation_select: for alias, annotation in self.annotation_select.items(): - if not allow_aliases or alias in column_names: - alias = None - group_by_cols = annotation.get_group_by_cols(alias=alias) + signature = inspect.signature(annotation.get_group_by_cols) + if 'alias' not in signature.parameters: + annotation_class = annotation.__class__ + msg = ( + '`alias=None` must be added to the signature of ' + '%s.%s.get_group_by_cols().' + ) % (annotation_class.__module__, annotation_class.__qualname__) + warnings.warn(msg, category=RemovedInDjango40Warning) + group_by_cols = annotation.get_group_by_cols() + else: + if not allow_aliases or alias in column_names: + alias = None + group_by_cols = annotation.get_group_by_cols(alias=alias) group_by.extend(group_by_cols) self.group_by = tuple(group_by) @@ -2257,12 +2088,11 @@ class Query(BaseExpression): else: param_iter = iter([]) for name, entry in select.items(): - self.check_alias(name) entry = str(entry) entry_params = [] pos = entry.find("%s") while pos != -1: - if pos == 0 or entry[pos - 1] != "%": + if pos == 0 or entry[pos - 1] != '%': entry_params.append(next(param_iter)) pos = entry.find("%s", pos + 2) select_pairs[name] = (entry, entry_params) @@ -2296,12 +2126,7 @@ class Query(BaseExpression): self.deferred_loading = existing.union(field_names), True else: # Remove names from the set of any existing "immediate load" names. - if new_existing := existing.difference(field_names): - self.deferred_loading = new_existing, False - else: - self.clear_deferred_loading() - if new_only := set(field_names).difference(existing): - self.deferred_loading = new_only, True + self.deferred_loading = existing.difference(field_names), False def add_immediate_loading(self, field_names): """ @@ -2315,8 +2140,8 @@ class Query(BaseExpression): """ existing, defer = self.deferred_loading field_names = set(field_names) - if "pk" in field_names: - field_names.remove("pk") + if 'pk' in field_names: + field_names.remove('pk') field_names.add(self.get_meta().pk.name) if defer: @@ -2404,9 +2229,7 @@ class Query(BaseExpression): # Selected annotations must be known before setting the GROUP BY # clause. if self.group_by is True: - self.add_fields( - (f.attname for f in self.model._meta.concrete_fields), False - ) + self.add_fields((f.attname for f in self.model._meta.concrete_fields), False) # Disable GROUP BY aliases to avoid orphaning references to the # SELECT clause which is about to be cleared. self.set_group_by(allow_aliases=False) @@ -2436,8 +2259,7 @@ class Query(BaseExpression): return {} elif self.annotation_select_mask is not None: self._annotation_select_cache = { - k: v - for k, v in self.annotations.items() + k: v for k, v in self.annotations.items() if k in self.annotation_select_mask } return self._annotation_select_cache @@ -2452,7 +2274,8 @@ class Query(BaseExpression): return {} elif self.extra_select_mask is not None: self._extra_select_cache = { - k: v for k, v in self.extra.items() if k in self.extra_select_mask + k: v for k, v in self.extra.items() + if k in self.extra_select_mask } return self._extra_select_cache else: @@ -2479,7 +2302,8 @@ class Query(BaseExpression): # the lookup part of the query. That is, avoid trimming # joins generated for F() expressions. lookup_tables = [ - t for t in self.alias_map if t in self._lookup_joins or t == self.base_table + t for t in self.alias_map + if t in self._lookup_joins or t == self.base_table ] for trimmed_paths, path in enumerate(all_paths): if path.m2m: @@ -2498,7 +2322,8 @@ class Query(BaseExpression): break trimmed_prefix.append(name) paths_in_prefix -= len(path) - trimmed_prefix.append(join_field.foreign_related_fields[0].name) + trimmed_prefix.append( + join_field.foreign_related_fields[0].name) trimmed_prefix = LOOKUP_SEP.join(trimmed_prefix) # Lets still see if we can trim the first join from the inner query # (that is, self). We can't do this for: @@ -2512,8 +2337,7 @@ class Query(BaseExpression): select_alias = lookup_tables[trimmed_paths + 1] self.unref_alias(lookup_tables[trimmed_paths]) extra_restriction = join_field.get_extra_restriction( - None, lookup_tables[trimmed_paths + 1] - ) + self.where_class, None, lookup_tables[trimmed_paths + 1]) if extra_restriction: self.where.add(extra_restriction, AND) else: @@ -2526,9 +2350,7 @@ class Query(BaseExpression): # But the first entry in the query's FROM clause must not be a JOIN. for table in self.alias_map: if self.alias_refcount[table] > 0: - self.alias_map[table] = BaseTable( - self.alias_map[table].table_name, table - ) + self.alias_map[table] = BaseTable(self.alias_map[table].table_name, table) break self.set_select([f.get_col(select_alias) for f in select_fields]) return trimmed_prefix, contains_louter @@ -2547,12 +2369,12 @@ class Query(BaseExpression): # is_nullable() is needed to the compiler stage, but that is not easy # to do currently. return ( - connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls - and field.empty_strings_allowed + connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and + field.empty_strings_allowed ) or field.null -def get_order_dir(field, default="ASC"): +def get_order_dir(field, default='ASC'): """ Return the field name and direction for an order specification. For example, '-foo' is returned as ('foo', 'DESC'). @@ -2561,7 +2383,7 @@ def get_order_dir(field, default="ASC"): prefix) should sort. The '-' prefix always sorts the opposite way. """ dirn = ORDER_DIR[default] - if field[0] == "-": + if field[0] == '-': return field[1:], dirn[1] return field, dirn[0] @@ -2635,7 +2457,7 @@ class JoinPromoter: # to rel_a would remove a valid match from the query. So, we need # to promote any existing INNER to LOUTER (it is possible this # promotion in turn will be demoted later on). - if self.effective_connector == "OR" and votes < self.num_children: + if self.effective_connector == 'OR' and votes < self.num_children: to_promote.add(table) # If connector is AND and there is a filter that can match only # when there is a joinable row, then use INNER. For example, in @@ -2647,9 +2469,8 @@ class JoinPromoter: # (rel_a__col__icontains=Alex | rel_a__col__icontains=Russell) # then if rel_a doesn't produce any rows, the whole condition # can't match. Hence we can safely use INNER join. - if self.effective_connector == "AND" or ( - self.effective_connector == "OR" and votes == self.num_children - ): + if self.effective_connector == 'AND' or ( + self.effective_connector == 'OR' and votes == self.num_children): to_demote.add(table) # Finally, what happens in cases where we have: # (rel_a__col=1|rel_b__col=2) & rel_a__col__gte=0 diff --git a/venv/Lib/site-packages/django/db/models/sql/subqueries.py b/venv/Lib/site-packages/django/db/models/sql/subqueries.py index 1ed6e56..e83112b 100644 --- a/venv/Lib/site-packages/django/db/models/sql/subqueries.py +++ b/venv/Lib/site-packages/django/db/models/sql/subqueries.py @@ -3,16 +3,19 @@ Query subclasses which provide extra functionality beyond simple data retrieval. """ from django.core.exceptions import FieldError -from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS +from django.db.models.query_utils import Q +from django.db.models.sql.constants import ( + CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS, +) from django.db.models.sql.query import Query -__all__ = ["DeleteQuery", "UpdateQuery", "InsertQuery", "AggregateQuery"] +__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'AggregateQuery'] class DeleteQuery(Query): """A DELETE SQL query.""" - compiler = "SQLDeleteCompiler" + compiler = 'SQLDeleteCompiler' def do_query(self, table, where, using): self.alias_map = {table: self.alias_map[table]} @@ -34,21 +37,17 @@ class DeleteQuery(Query): num_deleted = 0 field = self.get_meta().pk for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): - self.clear_where() - self.add_filter( - f"{field.attname}__in", - pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE], - ) - num_deleted += self.do_query( - self.get_meta().db_table, self.where, using=using - ) + self.where = self.where_class() + self.add_q(Q( + **{field.attname + '__in': pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]})) + num_deleted += self.do_query(self.get_meta().db_table, self.where, using=using) return num_deleted class UpdateQuery(Query): """An UPDATE SQL query.""" - compiler = "SQLUpdateCompiler" + compiler = 'SQLUpdateCompiler' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -71,10 +70,8 @@ class UpdateQuery(Query): def update_batch(self, pk_list, values, using): self.add_update_values(values) for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): - self.clear_where() - self.add_filter( - "pk__in", pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE] - ) + self.where = self.where_class() + self.add_q(Q(pk__in=pk_list[offset: offset + GET_ITERATOR_CHUNK_SIZE])) self.get_compiler(using).execute_sql(NO_RESULTS) def add_update_values(self, values): @@ -86,14 +83,12 @@ class UpdateQuery(Query): values_seq = [] for name, val in values.items(): field = self.get_meta().get_field(name) - direct = ( - not (field.auto_created and not field.concrete) or not field.concrete - ) + direct = not (field.auto_created and not field.concrete) or not field.concrete model = field.model._meta.concrete_model if not direct or (field.is_relation and field.many_to_many): raise FieldError( - "Cannot update model field %r (only non-relations and " - "foreign keys permitted)." % field + 'Cannot update model field %r (only non-relations and ' + 'foreign keys permitted).' % field ) if model is not self.get_meta().concrete_model: self.add_related_update(model, field, val) @@ -108,7 +103,7 @@ class UpdateQuery(Query): called add_update_targets() to hint at the extra information here. """ for field, model, val in values_seq: - if hasattr(val, "resolve_expression"): + if hasattr(val, 'resolve_expression'): # Resolve expressions here so that annotations are no longer needed val = val.resolve_expression(self, allow_joins=False, for_save=True) self.values.append((field, model, val)) @@ -134,13 +129,13 @@ class UpdateQuery(Query): query = UpdateQuery(model) query.values = values if self.related_ids is not None: - query.add_filter("pk__in", self.related_ids) + query.add_filter(('pk__in', self.related_ids)) result.append(query) return result class InsertQuery(Query): - compiler = "SQLInsertCompiler" + compiler = 'SQLInsertCompiler' def __init__(self, *args, ignore_conflicts=False, **kwargs): super().__init__(*args, **kwargs) @@ -160,7 +155,7 @@ class AggregateQuery(Query): elements in the provided list. """ - compiler = "SQLAggregateCompiler" + compiler = 'SQLAggregateCompiler' def __init__(self, model, inner_query): self.inner_query = inner_query diff --git a/venv/Lib/site-packages/django/db/models/sql/where.py b/venv/Lib/site-packages/django/db/models/sql/where.py index 532780f..795eff8 100644 --- a/venv/Lib/site-packages/django/db/models/sql/where.py +++ b/venv/Lib/site-packages/django/db/models/sql/where.py @@ -7,8 +7,8 @@ from django.utils import tree from django.utils.functional import cached_property # Connection types -AND = "AND" -OR = "OR" +AND = 'AND' +OR = 'OR' class WhereNode(tree.Node): @@ -25,7 +25,6 @@ class WhereNode(tree.Node): relabeled_clone() method or relabel_aliases() and clone() methods and contains_aggregate attribute. """ - default = AND resolved = False conditional = True @@ -41,15 +40,15 @@ class WhereNode(tree.Node): in_negated = negated ^ self.negated # If the effective connector is OR and this node contains an aggregate, # then we need to push the whole branch to HAVING clause. - may_need_split = (in_negated and self.connector == AND) or ( - not in_negated and self.connector == OR - ) + may_need_split = ( + (in_negated and self.connector == AND) or + (not in_negated and self.connector == OR)) if may_need_split and self.contains_aggregate: return None, self where_parts = [] having_parts = [] for c in self.children: - if hasattr(c, "split_having"): + if hasattr(c, 'split_having'): where_part, having_part = c.split_having(in_negated) if where_part is not None: where_parts.append(where_part) @@ -59,16 +58,8 @@ class WhereNode(tree.Node): having_parts.append(c) else: where_parts.append(c) - having_node = ( - self.__class__(having_parts, self.connector, self.negated) - if having_parts - else None - ) - where_node = ( - self.__class__(where_parts, self.connector, self.negated) - if where_parts - else None - ) + having_node = self.__class__(having_parts, self.connector, self.negated) if having_parts else None + where_node = self.__class__(where_parts, self.connector, self.negated) if where_parts else None return where_node, having_node def as_sql(self, compiler, connection): @@ -103,24 +94,24 @@ class WhereNode(tree.Node): # counts. if empty_needed == 0: if self.negated: - return "", [] + return '', [] else: raise EmptyResultSet if full_needed == 0: if self.negated: raise EmptyResultSet else: - return "", [] - conn = " %s " % self.connector + return '', [] + conn = ' %s ' % self.connector sql_string = conn.join(result) if sql_string: if self.negated: # Some backends (Oracle at least) need parentheses # around the inner SQL in the negated case, even if the # inner SQL contains just a single expression. - sql_string = "NOT (%s)" % sql_string + sql_string = 'NOT (%s)' % sql_string elif len(result) > 1 or self.resolved: - sql_string = "(%s)" % sql_string + sql_string = '(%s)' % sql_string return sql_string, result_params def get_group_by_cols(self, alias=None): @@ -142,10 +133,10 @@ class WhereNode(tree.Node): mapping old (current) alias values to the new values. """ for pos, child in enumerate(self.children): - if hasattr(child, "relabel_aliases"): + if hasattr(child, 'relabel_aliases'): # For example another WhereNode child.relabel_aliases(change_map) - elif hasattr(child, "relabeled_clone"): + elif hasattr(child, 'relabeled_clone'): self.children[pos] = child.relabeled_clone(change_map) def clone(self): @@ -155,12 +146,9 @@ class WhereNode(tree.Node): value) tuples, or objects supporting .clone(). """ clone = self.__class__._new_instance( - children=None, - connector=self.connector, - negated=self.negated, - ) + children=[], connector=self.connector, negated=self.negated) for child in self.children: - if hasattr(child, "clone"): + if hasattr(child, 'clone'): clone.children.append(child.clone()) else: clone.children.append(child) @@ -194,20 +182,24 @@ class WhereNode(tree.Node): def contains_over_clause(self): return self._contains_over_clause(self) + @property + def is_summary(self): + return any(child.is_summary for child in self.children) + @staticmethod def _resolve_leaf(expr, query, *args, **kwargs): - if hasattr(expr, "resolve_expression"): + if hasattr(expr, 'resolve_expression'): expr = expr.resolve_expression(query, *args, **kwargs) return expr @classmethod def _resolve_node(cls, node, query, *args, **kwargs): - if hasattr(node, "children"): + if hasattr(node, 'children'): for child in node.children: cls._resolve_node(child, query, *args, **kwargs) - if hasattr(node, "lhs"): + if hasattr(node, 'lhs'): node.lhs = cls._resolve_leaf(node.lhs, query, *args, **kwargs) - if hasattr(node, "rhs"): + if hasattr(node, 'rhs'): node.rhs = cls._resolve_leaf(node.rhs, query, *args, **kwargs) def resolve_expression(self, *args, **kwargs): @@ -216,30 +208,9 @@ class WhereNode(tree.Node): clone.resolved = True return clone - @cached_property - def output_field(self): - from django.db.models import BooleanField - - return BooleanField() - - def select_format(self, compiler, sql, params): - # Wrap filters with a CASE WHEN expression if a database backend - # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP - # BY list. - if not compiler.connection.features.supports_boolean_expr_in_select_clause: - sql = f"CASE WHEN {sql} THEN 1 ELSE 0 END" - return sql, params - - def get_db_converters(self, connection): - return self.output_field.get_db_converters(connection) - - def get_lookup(self, lookup): - return self.output_field.get_lookup(lookup) - class NothingNode: """A node that matches nothing.""" - contains_aggregate = False def as_sql(self, compiler=None, connection=None): @@ -268,7 +239,6 @@ class SubqueryConstraint: self.alias = alias self.columns = columns self.targets = targets - query_object.clear_ordering(clear_default=True) self.query_object = query_object def as_sql(self, compiler, connection): diff --git a/venv/Lib/site-packages/django/db/models/utils.py b/venv/Lib/site-packages/django/db/models/utils.py index 7a167b1..a8bd20f 100644 --- a/venv/Lib/site-packages/django/db/models/utils.py +++ b/venv/Lib/site-packages/django/db/models/utils.py @@ -46,7 +46,7 @@ def create_namedtuple_class(*names): return unpickle_named_row, (names, tuple(self)) return type( - "Row", - (namedtuple("Row", names),), - {"__reduce__": __reduce__, "__slots__": ()}, + 'Row', + (namedtuple('Row', names),), + {'__reduce__': __reduce__, '__slots__': ()}, ) diff --git a/venv/Lib/site-packages/django/db/transaction.py b/venv/Lib/site-packages/django/db/transaction.py index 5344f5d..6d39e4a 100644 --- a/venv/Lib/site-packages/django/db/transaction.py +++ b/venv/Lib/site-packages/django/db/transaction.py @@ -1,17 +1,12 @@ from contextlib import ContextDecorator, contextmanager from django.db import ( - DEFAULT_DB_ALIAS, - DatabaseError, - Error, - ProgrammingError, - connections, + DEFAULT_DB_ALIAS, DatabaseError, Error, ProgrammingError, connections, ) class TransactionManagementError(ProgrammingError): """Transaction management is used improperly.""" - pass @@ -137,7 +132,6 @@ def on_commit(func, using=None): # Decorators / context managers # ################################# - class Atomic(ContextDecorator): """ Guarantee the atomic execution of a given block. @@ -171,7 +165,6 @@ class Atomic(ContextDecorator): This is a private API. """ - # This private flag is provided only to disable the durability checks in # TestCase. _ensure_durability = True @@ -186,8 +179,8 @@ class Atomic(ContextDecorator): if self.durable and self._ensure_durability and connection.in_atomic_block: raise RuntimeError( - "A durable atomic block cannot be nested within another " - "atomic block." + 'A durable atomic block cannot be nested within another ' + 'atomic block.' ) if not connection.in_atomic_block: # Reset state when entering an outermost atomic block. @@ -211,9 +204,7 @@ class Atomic(ContextDecorator): else: connection.savepoint_ids.append(None) else: - connection.set_autocommit( - False, force_begin_transaction_with_broken_autocommit=True - ) + connection.set_autocommit(False, force_begin_transaction_with_broken_autocommit=True) connection.in_atomic_block = True def __exit__(self, exc_type, exc_value, traceback): diff --git a/venv/Lib/site-packages/django/db/utils.py b/venv/Lib/site-packages/django/db/utils.py index 282aa40..f8d56bc 100644 --- a/venv/Lib/site-packages/django/db/utils.py +++ b/venv/Lib/site-packages/django/db/utils.py @@ -3,15 +3,14 @@ from importlib import import_module from django.conf import settings from django.core.exceptions import ImproperlyConfigured - # For backwards compatibility with Django < 3.2 from django.utils.connection import ConnectionDoesNotExist # NOQA: F401 from django.utils.connection import BaseConnectionHandler from django.utils.functional import cached_property from django.utils.module_loading import import_string -DEFAULT_DB_ALIAS = "default" -DJANGO_VERSION_PICKLE_KEY = "_django_version" +DEFAULT_DB_ALIAS = 'default' +DJANGO_VERSION_PICKLE_KEY = '_django_version' class Error(Exception): @@ -71,15 +70,15 @@ class DatabaseErrorWrapper: if exc_type is None: return for dj_exc_type in ( - DataError, - OperationalError, - IntegrityError, - InternalError, - ProgrammingError, - NotSupportedError, - DatabaseError, - InterfaceError, - Error, + DataError, + OperationalError, + IntegrityError, + InternalError, + ProgrammingError, + NotSupportedError, + DatabaseError, + InterfaceError, + Error, ): db_exc_type = getattr(self.wrapper.Database, dj_exc_type.__name__) if issubclass(exc_type, db_exc_type): @@ -96,7 +95,6 @@ class DatabaseErrorWrapper: def inner(*args, **kwargs): with self: return func(*args, **kwargs) - return inner @@ -106,22 +104,20 @@ def load_backend(backend_name): backend name, or raise an error if it doesn't exist. """ # This backend was renamed in Django 1.9. - if backend_name == "django.db.backends.postgresql_psycopg2": - backend_name = "django.db.backends.postgresql" + if backend_name == 'django.db.backends.postgresql_psycopg2': + backend_name = 'django.db.backends.postgresql' try: - return import_module("%s.base" % backend_name) + return import_module('%s.base' % backend_name) except ImportError as e_user: # The database backend wasn't found. Display a helpful error message # listing all built-in database backends. import django.db.backends - builtin_backends = [ - name - for _, name, ispkg in pkgutil.iter_modules(django.db.backends.__path__) - if ispkg and name not in {"base", "dummy"} + name for _, name, ispkg in pkgutil.iter_modules(django.db.backends.__path__) + if ispkg and name not in {'base', 'dummy'} ] - if backend_name not in ["django.db.backends.%s" % b for b in builtin_backends]: + if backend_name not in ['django.db.backends.%s' % b for b in builtin_backends]: backend_reprs = map(repr, sorted(builtin_backends)) raise ImproperlyConfigured( "%r isn't an available database backend or couldn't be " @@ -136,7 +132,7 @@ def load_backend(backend_name): class ConnectionHandler(BaseConnectionHandler): - settings_name = "DATABASES" + settings_name = 'DATABASES' # Connections needs to still be an actual thread local, as it's truly # thread-critical. Database backends should use @async_unsafe to protect # their code from async contexts, but this will give those contexts @@ -147,13 +143,13 @@ class ConnectionHandler(BaseConnectionHandler): def configure_settings(self, databases): databases = super().configure_settings(databases) if databases == {}: - databases[DEFAULT_DB_ALIAS] = {"ENGINE": "django.db.backends.dummy"} + databases[DEFAULT_DB_ALIAS] = {'ENGINE': 'django.db.backends.dummy'} elif DEFAULT_DB_ALIAS not in databases: raise ImproperlyConfigured( f"You must define a '{DEFAULT_DB_ALIAS}' database." ) elif databases[DEFAULT_DB_ALIAS] == {}: - databases[DEFAULT_DB_ALIAS]["ENGINE"] = "django.db.backends.dummy" + databases[DEFAULT_DB_ALIAS]['ENGINE'] = 'django.db.backends.dummy' return databases @property @@ -170,16 +166,16 @@ class ConnectionHandler(BaseConnectionHandler): except KeyError: raise self.exception_class(f"The connection '{alias}' doesn't exist.") - conn.setdefault("ATOMIC_REQUESTS", False) - conn.setdefault("AUTOCOMMIT", True) - conn.setdefault("ENGINE", "django.db.backends.dummy") - if conn["ENGINE"] == "django.db.backends." or not conn["ENGINE"]: - conn["ENGINE"] = "django.db.backends.dummy" - conn.setdefault("CONN_MAX_AGE", 0) - conn.setdefault("OPTIONS", {}) - conn.setdefault("TIME_ZONE", None) - for setting in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: - conn.setdefault(setting, "") + conn.setdefault('ATOMIC_REQUESTS', False) + conn.setdefault('AUTOCOMMIT', True) + conn.setdefault('ENGINE', 'django.db.backends.dummy') + if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']: + conn['ENGINE'] = 'django.db.backends.dummy' + conn.setdefault('CONN_MAX_AGE', 0) + conn.setdefault('OPTIONS', {}) + conn.setdefault('TIME_ZONE', None) + for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']: + conn.setdefault(setting, '') def prepare_test_settings(self, alias): """ @@ -190,13 +186,13 @@ class ConnectionHandler(BaseConnectionHandler): except KeyError: raise self.exception_class(f"The connection '{alias}' doesn't exist.") - test_settings = conn.setdefault("TEST", {}) + test_settings = conn.setdefault('TEST', {}) default_test_settings = [ - ("CHARSET", None), - ("COLLATION", None), - ("MIGRATE", True), - ("MIRROR", None), - ("NAME", None), + ('CHARSET', None), + ('COLLATION', None), + ('MIGRATE', True), + ('MIRROR', None), + ('NAME', None), ] for key, value in default_test_settings: test_settings.setdefault(key, value) @@ -205,7 +201,7 @@ class ConnectionHandler(BaseConnectionHandler): self.ensure_defaults(alias) self.prepare_test_settings(alias) db = self.databases[alias] - backend = load_backend(db["ENGINE"]) + backend = load_backend(db['ENGINE']) return backend.DatabaseWrapper(db, alias) def close_all(self): @@ -250,15 +246,14 @@ class ConnectionRouter: chosen_db = method(model, **hints) if chosen_db: return chosen_db - instance = hints.get("instance") + instance = hints.get('instance') if instance is not None and instance._state.db: return instance._state.db return DEFAULT_DB_ALIAS - return _route_db - db_for_read = _router_func("db_for_read") - db_for_write = _router_func("db_for_write") + db_for_read = _router_func('db_for_read') + db_for_write = _router_func('db_for_write') def allow_relation(self, obj1, obj2, **hints): for router in self.routers: diff --git a/venv/Lib/site-packages/django/dispatch/dispatcher.py b/venv/Lib/site-packages/django/dispatch/dispatcher.py index 86eb1c3..5ad0659 100644 --- a/venv/Lib/site-packages/django/dispatch/dispatcher.py +++ b/venv/Lib/site-packages/django/dispatch/dispatcher.py @@ -1,14 +1,16 @@ import logging import threading +import warnings import weakref +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.inspect import func_accepts_kwargs -logger = logging.getLogger("django.dispatch") +logger = logging.getLogger('django.dispatch') def _make_id(target): - if hasattr(target, "__func__"): + if hasattr(target, '__func__'): return (id(target.__self__), id(target.__func__)) return id(target) @@ -28,12 +30,19 @@ class Signal: receivers { receiverkey (id) : weakref(receiver) } """ - - def __init__(self, use_caching=False): + def __init__(self, providing_args=None, use_caching=False): """ Create a new signal. """ self.receivers = [] + if providing_args is not None: + warnings.warn( + 'The providing_args argument is deprecated. As it is purely ' + 'documentational, it has no replacement. If you rely on this ' + 'argument as documentation, you can move the text to a code ' + 'comment or docstring.', + RemovedInDjango40Warning, stacklevel=2, + ) self.lock = threading.Lock() self.use_caching = use_caching # For convenience we create empty caches even if they are not used. @@ -81,13 +90,11 @@ class Signal: # If DEBUG is on, check that we got a good receiver if settings.configured and settings.DEBUG: - if not callable(receiver): - raise TypeError("Signal receivers must be callable.") + assert callable(receiver), "Signal receivers must be callable." + # Check for **kwargs if not func_accepts_kwargs(receiver): - raise ValueError( - "Signal receivers must accept keyword arguments (**kwargs)." - ) + raise ValueError("Signal receivers must accept keyword arguments (**kwargs).") if dispatch_uid: lookup_key = (dispatch_uid, _make_id(sender)) @@ -98,7 +105,7 @@ class Signal: ref = weakref.ref receiver_object = receiver # Check for bound methods - if hasattr(receiver, "__self__") and hasattr(receiver, "__func__"): + if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): ref = weakref.WeakMethod receiver_object = receiver.__self__ receiver = ref(receiver) @@ -167,10 +174,7 @@ class Signal: Return a list of tuple pairs [(receiver, response), ... ]. """ - if ( - not self.receivers - or self.sender_receivers_cache.get(sender) is NO_RECEIVERS - ): + if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return [] return [ @@ -197,10 +201,7 @@ class Signal: If any receiver raises an error (specifically any subclass of Exception), return the error instance as the result for that receiver. """ - if ( - not self.receivers - or self.sender_receivers_cache.get(sender) is NO_RECEIVERS - ): + if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return [] # Call each receiver with whatever arguments it can accept. @@ -211,7 +212,7 @@ class Signal: response = receiver(signal=self, sender=sender, **named) except Exception as err: logger.error( - "Error calling %s in Signal.send_robust() (%s)", + 'Error calling %s in Signal.send_robust() (%s)', receiver.__qualname__, err, exc_info=err, @@ -226,9 +227,8 @@ class Signal: if self._dead_receivers: self._dead_receivers = False self.receivers = [ - r - for r in self.receivers - if not (isinstance(r[1], weakref.ReferenceType) and r[1]() is None) + r for r in self.receivers + if not(isinstance(r[1], weakref.ReferenceType) and r[1]() is None) ] def _live_receivers(self, sender): @@ -293,7 +293,6 @@ def receiver(signal, **kwargs): def signals_receiver(sender, **kwargs): ... """ - def _decorator(func): if isinstance(signal, (list, tuple)): for s in signal: @@ -301,5 +300,4 @@ def receiver(signal, **kwargs): else: signal.connect(func, **kwargs) return func - return _decorator diff --git a/venv/Lib/site-packages/django/forms/boundfield.py b/venv/Lib/site-packages/django/forms/boundfield.py index 3c06cb5..66d065c 100644 --- a/venv/Lib/site-packages/django/forms/boundfield.py +++ b/venv/Lib/site-packages/django/forms/boundfield.py @@ -1,19 +1,19 @@ +import datetime import re -from django.core.exceptions import ValidationError -from django.forms.utils import pretty_name -from django.forms.widgets import MultiWidget, Textarea, TextInput +from django.forms.utils import flatatt, pretty_name +from django.forms.widgets import Textarea, TextInput from django.utils.functional import cached_property -from django.utils.html import format_html, html_safe +from django.utils.html import conditional_escape, format_html, html_safe +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -__all__ = ("BoundField",) +__all__ = ('BoundField',) @html_safe class BoundField: "A Field plus data" - def __init__(self, form, field, name): self.form = form self.field = field @@ -25,7 +25,7 @@ class BoundField: self.label = pretty_name(name) else: self.label = self.field.label - self.help_text = field.help_text or "" + self.help_text = field.help_text or '' def __str__(self): """Render this field as an HTML widget.""" @@ -42,14 +42,12 @@ class BoundField: This property is cached so that only one database query occurs when rendering ModelChoiceFields. """ - id_ = self.field.widget.attrs.get("id") or self.auto_id - attrs = {"id": id_} if id_ else {} + id_ = self.field.widget.attrs.get('id') or self.auto_id + attrs = {'id': id_} if id_ else {} attrs = self.build_widget_attrs(attrs) return [ BoundWidget(self.field.widget, widget, self.form.renderer) - for widget in self.field.widget.subwidgets( - self.html_name, self.value(), attrs=attrs - ) + for widget in self.field.widget.subwidgets(self.html_name, self.value(), attrs=attrs) ] def __bool__(self): @@ -67,7 +65,7 @@ class BoundField: # from templates. if not isinstance(idx, (int, slice)): raise TypeError( - "BoundField indices must be integers or slices, not %s." + 'BoundField indices must be integers or slices, not %s.' % type(idx).__name__ ) return self.subwidgets[idx] @@ -77,9 +75,7 @@ class BoundField: """ Return an ErrorList (empty if there are no errors) for this field. """ - return self.form.errors.get( - self.name, self.form.error_class(renderer=self.form.renderer) - ) + return self.form.errors.get(self.name, self.form.error_class()) def as_widget(self, widget=None, attrs=None, only_initial=False): """ @@ -92,10 +88,8 @@ class BoundField: widget.is_localized = True attrs = attrs or {} attrs = self.build_widget_attrs(attrs, widget) - if self.auto_id and "id" not in widget.attrs: - attrs.setdefault( - "id", self.html_initial_id if only_initial else self.auto_id - ) + if self.auto_id and 'id' not in widget.attrs: + attrs.setdefault('id', self.html_initial_id if only_initial else self.auto_id) return widget.render( name=self.html_initial_name if only_initial else self.html_name, value=self.value(), @@ -124,7 +118,7 @@ class BoundField: """ Return the data for this BoundField, or None if it wasn't given. """ - return self.form._widget_data_value(self.field.widget, self.html_name) + return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) def value(self): """ @@ -136,23 +130,6 @@ class BoundField: data = self.field.bound_data(self.data, data) return self.field.prepare_value(data) - def _has_changed(self): - field = self.field - if field.show_hidden_initial: - hidden_widget = field.hidden_widget() - initial_value = self.form._widget_data_value( - hidden_widget, - self.html_initial_name, - ) - try: - initial_value = field.to_python(initial_value) - except ValidationError: - # Always assume data has changed if validation fails. - return True - else: - initial_value = self.initial - return field.has_changed(initial_value, self.data) - def label_tag(self, contents=None, attrs=None, label_suffix=None): """ Wrap the given contents in a <label>, if the field has an ID attribute. @@ -165,48 +142,43 @@ class BoundField: """ contents = contents or self.label if label_suffix is None: - label_suffix = ( - self.field.label_suffix - if self.field.label_suffix is not None - else self.form.label_suffix - ) + label_suffix = (self.field.label_suffix if self.field.label_suffix is not None + else self.form.label_suffix) # Only add the suffix if the label does not end in punctuation. # Translators: If found as last label character, these punctuation # characters will prevent the default label_suffix to be appended to the label - if label_suffix and contents and contents[-1] not in _(":?.!"): - contents = format_html("{}{}", contents, label_suffix) + if label_suffix and contents and contents[-1] not in _(':?.!'): + contents = format_html('{}{}', contents, label_suffix) widget = self.field.widget - id_ = widget.attrs.get("id") or self.auto_id + id_ = widget.attrs.get('id') or self.auto_id if id_: id_for_label = widget.id_for_label(id_) if id_for_label: - attrs = {**(attrs or {}), "for": id_for_label} - if self.field.required and hasattr(self.form, "required_css_class"): + attrs = {**(attrs or {}), 'for': id_for_label} + if self.field.required and hasattr(self.form, 'required_css_class'): attrs = attrs or {} - if "class" in attrs: - attrs["class"] += " " + self.form.required_css_class + if 'class' in attrs: + attrs['class'] += ' ' + self.form.required_css_class else: - attrs["class"] = self.form.required_css_class - context = { - "field": self, - "label": contents, - "attrs": attrs, - "use_tag": bool(id_), - } - return self.form.render(self.form.template_name_label, context) + attrs['class'] = self.form.required_css_class + attrs = flatatt(attrs) if attrs else '' + contents = format_html('<label{}>{}</label>', attrs, contents) + else: + contents = conditional_escape(contents) + return mark_safe(contents) def css_classes(self, extra_classes=None): """ Return a string of space-separated CSS classes for this field. """ - if hasattr(extra_classes, "split"): + if hasattr(extra_classes, 'split'): extra_classes = extra_classes.split() extra_classes = set(extra_classes or []) - if self.errors and hasattr(self.form, "error_css_class"): + if self.errors and hasattr(self.form, 'error_css_class'): extra_classes.add(self.form.error_css_class) - if self.field.required and hasattr(self.form, "required_css_class"): + if self.field.required and hasattr(self.form, 'required_css_class'): extra_classes.add(self.form.required_css_class) - return " ".join(extra_classes) + return ' '.join(extra_classes) @property def is_hidden(self): @@ -220,11 +192,11 @@ class BoundField: associated Form has specified auto_id. Return an empty string otherwise. """ auto_id = self.form.auto_id # Boolean or string - if auto_id and "%s" in str(auto_id): + if auto_id and '%s' in str(auto_id): return auto_id % self.html_name elif auto_id: return self.html_name - return "" + return '' @property def id_for_label(self): @@ -234,44 +206,31 @@ class BoundField: it has a single widget or a MultiWidget. """ widget = self.field.widget - id_ = widget.attrs.get("id") or self.auto_id + id_ = widget.attrs.get('id') or self.auto_id return widget.id_for_label(id_) @cached_property def initial(self): - return self.form.get_initial_for_field(self.field, self.name) + data = self.form.get_initial_for_field(self.field, self.name) + # If this is an auto-generated default date, nix the microseconds for + # standardized handling. See #22502. + if (isinstance(data, (datetime.datetime, datetime.time)) and + not self.field.widget.supports_microseconds): + data = data.replace(microsecond=0) + return data def build_widget_attrs(self, attrs, widget=None): widget = widget or self.field.widget attrs = dict(attrs) # Copy attrs to avoid modifying the argument. - if ( - widget.use_required_attribute(self.initial) - and self.field.required - and self.form.use_required_attribute - ): - # MultiValueField has require_all_fields: if False, fall back - # on subfields. - if ( - hasattr(self.field, "require_all_fields") - and not self.field.require_all_fields - and isinstance(self.field.widget, MultiWidget) - ): - for subfield, subwidget in zip(self.field.fields, widget.widgets): - subwidget.attrs["required"] = ( - subwidget.use_required_attribute(self.initial) - and subfield.required - ) - else: - attrs["required"] = True + if widget.use_required_attribute(self.initial) and self.field.required and self.form.use_required_attribute: + attrs['required'] = True if self.field.disabled: - attrs["disabled"] = True + attrs['disabled'] = True return attrs @property def widget_type(self): - return re.sub( - r"widget$|input$", "", self.field.widget.__class__.__name__.lower() - ) + return re.sub(r'widget$|input$', '', self.field.widget.__class__.__name__.lower()) @html_safe @@ -288,7 +247,6 @@ class BoundWidget: </label> {% endfor %} """ - def __init__(self, parent_widget, data, renderer): self.parent_widget = parent_widget self.data = data @@ -298,19 +256,19 @@ class BoundWidget: return self.tag(wrap_label=True) def tag(self, wrap_label=False): - context = {"widget": {**self.data, "wrap_label": wrap_label}} + context = {'widget': {**self.data, 'wrap_label': wrap_label}} return self.parent_widget._render(self.template_name, context, self.renderer) @property def template_name(self): - if "template_name" in self.data: - return self.data["template_name"] + if 'template_name' in self.data: + return self.data['template_name'] return self.parent_widget.template_name @property def id_for_label(self): - return self.data["attrs"].get("id") + return 'id_%s_%s' % (self.data['name'], self.data['index']) @property def choice_label(self): - return self.data["label"] + return self.data['label'] diff --git a/venv/Lib/site-packages/django/forms/fields.py b/venv/Lib/site-packages/django/forms/fields.py index a703193..17c7956 100644 --- a/venv/Lib/site-packages/django/forms/fields.py +++ b/venv/Lib/site-packages/django/forms/fields.py @@ -19,94 +19,45 @@ from django.core.exceptions import ValidationError from django.forms.boundfield import BoundField from django.forms.utils import from_current_timezone, to_current_timezone from django.forms.widgets import ( - FILE_INPUT_CONTRADICTION, - CheckboxInput, - ClearableFileInput, - DateInput, - DateTimeInput, - EmailInput, - FileInput, - HiddenInput, - MultipleHiddenInput, - NullBooleanSelect, - NumberInput, - Select, - SelectMultiple, - SplitDateTimeWidget, - SplitHiddenDateTimeWidget, - Textarea, - TextInput, - TimeInput, - URLInput, + FILE_INPUT_CONTRADICTION, CheckboxInput, ClearableFileInput, DateInput, + DateTimeInput, EmailInput, FileInput, HiddenInput, MultipleHiddenInput, + NullBooleanSelect, NumberInput, Select, SelectMultiple, + SplitDateTimeWidget, SplitHiddenDateTimeWidget, Textarea, TextInput, + TimeInput, URLInput, ) from django.utils import formats from django.utils.dateparse import parse_datetime, parse_duration from django.utils.duration import duration_string from django.utils.ipv6 import clean_ipv6_address from django.utils.regex_helper import _lazy_re_compile -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import ngettext_lazy +from django.utils.translation import gettext_lazy as _, ngettext_lazy __all__ = ( - "Field", - "CharField", - "IntegerField", - "DateField", - "TimeField", - "DateTimeField", - "DurationField", - "RegexField", - "EmailField", - "FileField", - "ImageField", - "URLField", - "BooleanField", - "NullBooleanField", - "ChoiceField", - "MultipleChoiceField", - "ComboField", - "MultiValueField", - "FloatField", - "DecimalField", - "SplitDateTimeField", - "GenericIPAddressField", - "FilePathField", - "JSONField", - "SlugField", - "TypedChoiceField", - "TypedMultipleChoiceField", - "UUIDField", + 'Field', 'CharField', 'IntegerField', + 'DateField', 'TimeField', 'DateTimeField', 'DurationField', + 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', + 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', + 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', + 'SplitDateTimeField', 'GenericIPAddressField', 'FilePathField', + 'JSONField', 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField', + 'UUIDField', ) class Field: widget = TextInput # Default widget to use when rendering this type of Field. - hidden_widget = ( - HiddenInput # Default widget to use when rendering this as "hidden". - ) + hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". default_validators = [] # Default set of validators # Add an 'invalid' entry to default_error_message if you want a specific # field error message not raised by the field validators. default_error_messages = { - "required": _("This field is required."), + 'required': _('This field is required.'), } empty_values = list(validators.EMPTY_VALUES) - def __init__( - self, - *, - required=True, - widget=None, - label=None, - initial=None, - help_text="", - error_messages=None, - show_hidden_initial=False, - validators=(), - localize=False, - disabled=False, - label_suffix=None, - ): + def __init__(self, *, required=True, widget=None, label=None, initial=None, + help_text='', error_messages=None, show_hidden_initial=False, + validators=(), localize=False, disabled=False, label_suffix=None): # required -- Boolean that specifies whether the field is required. # True by default. # widget -- A Widget class, or instance of a Widget class, that should @@ -158,7 +109,7 @@ class Field: messages = {} for c in reversed(self.__class__.__mro__): - messages.update(getattr(c, "default_error_messages", {})) + messages.update(getattr(c, 'default_error_messages', {})) messages.update(error_messages or {}) self.error_messages = messages @@ -174,7 +125,7 @@ class Field: def validate(self, value): if value in self.empty_values and self.required: - raise ValidationError(self.error_messages["required"], code="required") + raise ValidationError(self.error_messages['required'], code='required') def run_validators(self, value): if value in self.empty_values: @@ -184,7 +135,7 @@ class Field: try: v(value) except ValidationError as e: - if hasattr(e, "code") and e.code in self.error_messages: + if hasattr(e, 'code') and e.code in self.error_messages: e.message = self.error_messages[e.code] errors.extend(e.error_list) if errors: @@ -229,15 +180,15 @@ class Field: return False try: data = self.to_python(data) - if hasattr(self, "_coerce"): + if hasattr(self, '_coerce'): return self._coerce(data) != self._coerce(initial) except ValidationError: return True # For purposes of seeing whether something has changed, None is # the same as an empty string, if the data or initial value we get # is None, replace it with ''. - initial_value = initial if initial is not None else "" - data_value = data if data is not None else "" + initial_value = initial if initial is not None else '' + data_value = data if data is not None else '' return initial_value != data_value def get_bound_field(self, form, field_name): @@ -257,9 +208,7 @@ class Field: class CharField(Field): - def __init__( - self, *, max_length=None, min_length=None, strip=True, empty_value="", **kwargs - ): + def __init__(self, *, max_length=None, min_length=None, strip=True, empty_value='', **kwargs): self.max_length = max_length self.min_length = min_length self.strip = strip @@ -285,25 +234,25 @@ class CharField(Field): attrs = super().widget_attrs(widget) if self.max_length is not None and not widget.is_hidden: # The HTML attribute is maxlength, not max_length. - attrs["maxlength"] = str(self.max_length) + attrs['maxlength'] = str(self.max_length) if self.min_length is not None and not widget.is_hidden: # The HTML attribute is minlength, not min_length. - attrs["minlength"] = str(self.min_length) + attrs['minlength'] = str(self.min_length) return attrs class IntegerField(Field): widget = NumberInput default_error_messages = { - "invalid": _("Enter a whole number."), + 'invalid': _('Enter a whole number.'), } - re_decimal = _lazy_re_compile(r"\.0*\s*$") + re_decimal = _lazy_re_compile(r'\.0*\s*$') def __init__(self, *, max_value=None, min_value=None, **kwargs): self.max_value, self.min_value = max_value, min_value - if kwargs.get("localize") and self.widget == NumberInput: + if kwargs.get('localize') and self.widget == NumberInput: # Localized number input is not well supported on most browsers - kwargs.setdefault("widget", super().widget) + kwargs.setdefault('widget', super().widget) super().__init__(**kwargs) if max_value is not None: @@ -323,24 +272,24 @@ class IntegerField(Field): value = formats.sanitize_separators(value) # Strip trailing decimal and zeros. try: - value = int(self.re_decimal.sub("", str(value))) + value = int(self.re_decimal.sub('', str(value))) except (ValueError, TypeError): - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') return value def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if isinstance(widget, NumberInput): if self.min_value is not None: - attrs["min"] = self.min_value + attrs['min'] = self.min_value if self.max_value is not None: - attrs["max"] = self.max_value + attrs['max'] = self.max_value return attrs class FloatField(IntegerField): default_error_messages = { - "invalid": _("Enter a number."), + 'invalid': _('Enter a number.'), } def to_python(self, value): @@ -356,7 +305,7 @@ class FloatField(IntegerField): try: value = float(value) except (ValueError, TypeError): - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') return value def validate(self, value): @@ -364,29 +313,21 @@ class FloatField(IntegerField): if value in self.empty_values: return if not math.isfinite(value): - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, NumberInput) and "step" not in widget.attrs: - attrs.setdefault("step", "any") + if isinstance(widget, NumberInput) and 'step' not in widget.attrs: + attrs.setdefault('step', 'any') return attrs class DecimalField(IntegerField): default_error_messages = { - "invalid": _("Enter a number."), + 'invalid': _('Enter a number.'), } - def __init__( - self, - *, - max_value=None, - min_value=None, - max_digits=None, - decimal_places=None, - **kwargs, - ): + def __init__(self, *, max_value=None, min_value=None, max_digits=None, decimal_places=None, **kwargs): self.max_digits, self.decimal_places = max_digits, decimal_places super().__init__(max_value=max_value, min_value=min_value, **kwargs) self.validators.append(validators.DecimalValidator(max_digits, decimal_places)) @@ -402,37 +343,28 @@ class DecimalField(IntegerField): return None if self.localize: value = formats.sanitize_separators(value) + value = str(value).strip() try: - value = Decimal(str(value)) + value = Decimal(value) except DecimalException: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') return value - def validate(self, value): - super().validate(value) - if value in self.empty_values: - return - if not value.is_finite(): - raise ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, - ) - def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, NumberInput) and "step" not in widget.attrs: + if isinstance(widget, NumberInput) and 'step' not in widget.attrs: if self.decimal_places is not None: # Use exponential notation for small values since they might # be parsed as 0 otherwise. ref #20765 step = str(Decimal(1).scaleb(-self.decimal_places)).lower() else: - step = "any" - attrs.setdefault("step", step) + step = 'any' + attrs.setdefault('step', step) return attrs class BaseTemporalField(Field): + def __init__(self, *, input_formats=None, **kwargs): super().__init__(**kwargs) if input_formats is not None: @@ -446,17 +378,17 @@ class BaseTemporalField(Field): return self.strptime(value, format) except (ValueError, TypeError): continue - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') def strptime(self, value, format): - raise NotImplementedError("Subclasses must define this method.") + raise NotImplementedError('Subclasses must define this method.') class DateField(BaseTemporalField): widget = DateInput - input_formats = formats.get_format_lazy("DATE_INPUT_FORMATS") + input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS') default_error_messages = { - "invalid": _("Enter a valid date."), + 'invalid': _('Enter a valid date.'), } def to_python(self, value): @@ -478,8 +410,10 @@ class DateField(BaseTemporalField): class TimeField(BaseTemporalField): widget = TimeInput - input_formats = formats.get_format_lazy("TIME_INPUT_FORMATS") - default_error_messages = {"invalid": _("Enter a valid time.")} + input_formats = formats.get_format_lazy('TIME_INPUT_FORMATS') + default_error_messages = { + 'invalid': _('Enter a valid time.') + } def to_python(self, value): """ @@ -498,15 +432,15 @@ class TimeField(BaseTemporalField): class DateTimeFormatsIterator: def __iter__(self): - yield from formats.get_format("DATETIME_INPUT_FORMATS") - yield from formats.get_format("DATE_INPUT_FORMATS") + yield from formats.get_format('DATETIME_INPUT_FORMATS') + yield from formats.get_format('DATE_INPUT_FORMATS') class DateTimeField(BaseTemporalField): widget = DateTimeInput input_formats = DateTimeFormatsIterator() default_error_messages = { - "invalid": _("Enter a valid date/time."), + 'invalid': _('Enter a valid date/time.'), } def prepare_value(self, value): @@ -529,7 +463,7 @@ class DateTimeField(BaseTemporalField): try: result = parse_datetime(value.strip()) except ValueError: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') if not result: result = super().to_python(value) return from_current_timezone(result) @@ -540,8 +474,8 @@ class DateTimeField(BaseTemporalField): class DurationField(Field): default_error_messages = { - "invalid": _("Enter a valid duration."), - "overflow": _("The number of days must be between {min_days} and {max_days}."), + 'invalid': _('Enter a valid duration.'), + 'overflow': _('The number of days must be between {min_days} and {max_days}.') } def prepare_value(self, value): @@ -557,15 +491,12 @@ class DurationField(Field): try: value = parse_duration(str(value)) except OverflowError: - raise ValidationError( - self.error_messages["overflow"].format( - min_days=datetime.timedelta.min.days, - max_days=datetime.timedelta.max.days, - ), - code="overflow", - ) + raise ValidationError(self.error_messages['overflow'].format( + min_days=datetime.timedelta.min.days, + max_days=datetime.timedelta.max.days, + ), code='overflow') if value is None: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') return value @@ -574,7 +505,7 @@ class RegexField(CharField): """ regex can be either a string or a compiled regular expression object. """ - kwargs.setdefault("strip", False) + kwargs.setdefault('strip', False) super().__init__(**kwargs) self._set_regex(regex) @@ -585,10 +516,7 @@ class RegexField(CharField): if isinstance(regex, str): regex = re.compile(regex) self._regex = regex - if ( - hasattr(self, "_regex_validator") - and self._regex_validator in self.validators - ): + if hasattr(self, '_regex_validator') and self._regex_validator in self.validators: self.validators.remove(self._regex_validator) self._regex_validator = validators.RegexValidator(regex=regex) self.validators.append(self._regex_validator) @@ -607,17 +535,14 @@ class EmailField(CharField): class FileField(Field): widget = ClearableFileInput default_error_messages = { - "invalid": _("No file was submitted. Check the encoding type on the form."), - "missing": _("No file was submitted."), - "empty": _("The submitted file is empty."), - "max_length": ngettext_lazy( - "Ensure this filename has at most %(max)d character (it has %(length)d).", - "Ensure this filename has at most %(max)d characters (it has %(length)d).", - "max", - ), - "contradiction": _( - "Please either submit a file or check the clear checkbox, not both." - ), + 'invalid': _("No file was submitted. Check the encoding type on the form."), + 'missing': _("No file was submitted."), + 'empty': _("The submitted file is empty."), + 'max_length': ngettext_lazy( + 'Ensure this filename has at most %(max)d character (it has %(length)d).', + 'Ensure this filename has at most %(max)d characters (it has %(length)d).', + 'max'), + 'contradiction': _('Please either submit a file or check the clear checkbox, not both.') } def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs): @@ -634,26 +559,22 @@ class FileField(Field): file_name = data.name file_size = data.size except AttributeError: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') if self.max_length is not None and len(file_name) > self.max_length: - params = {"max": self.max_length, "length": len(file_name)} - raise ValidationError( - self.error_messages["max_length"], code="max_length", params=params - ) + params = {'max': self.max_length, 'length': len(file_name)} + raise ValidationError(self.error_messages['max_length'], code='max_length', params=params) if not file_name: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') if not self.allow_empty_file and not file_size: - raise ValidationError(self.error_messages["empty"], code="empty") + raise ValidationError(self.error_messages['empty'], code='empty') return data def clean(self, data, initial=None): # If the widget got contradictory inputs, we raise a validation error if data is FILE_INPUT_CONTRADICTION: - raise ValidationError( - self.error_messages["contradiction"], code="contradiction" - ) + raise ValidationError(self.error_messages['contradiction'], code='contradiction') # False means the field value should be cleared; further validation is # not needed. if data is False: @@ -681,7 +602,7 @@ class FileField(Field): class ImageField(FileField): default_validators = [validators.validate_image_file_extension] default_error_messages = { - "invalid_image": _( + 'invalid_image': _( "Upload a valid image. The file you uploaded was either not an " "image or a corrupted image." ), @@ -700,13 +621,13 @@ class ImageField(FileField): # We need to get a file object for Pillow. We might have a path or we might # have to read the data into memory. - if hasattr(data, "temporary_file_path"): + if hasattr(data, 'temporary_file_path'): file = data.temporary_file_path() else: - if hasattr(data, "read"): + if hasattr(data, 'read'): file = BytesIO(data.read()) else: - file = BytesIO(data["content"]) + file = BytesIO(data['content']) try: # load() could spot a truncated JPEG, but it loads the entire @@ -723,24 +644,24 @@ class ImageField(FileField): except Exception as exc: # Pillow doesn't recognize it as an image. raise ValidationError( - self.error_messages["invalid_image"], - code="invalid_image", + self.error_messages['invalid_image'], + code='invalid_image', ) from exc - if hasattr(f, "seek") and callable(f.seek): + if hasattr(f, 'seek') and callable(f.seek): f.seek(0) return f def widget_attrs(self, widget): attrs = super().widget_attrs(widget) - if isinstance(widget, FileInput) and "accept" not in widget.attrs: - attrs.setdefault("accept", "image/*") + if isinstance(widget, FileInput) and 'accept' not in widget.attrs: + attrs.setdefault('accept', 'image/*') return attrs class URLField(CharField): widget = URLInput default_error_messages = { - "invalid": _("Enter a valid URL."), + 'invalid': _('Enter a valid URL.'), } default_validators = [validators.URLValidator()] @@ -748,6 +669,7 @@ class URLField(CharField): super().__init__(strip=True, **kwargs) def to_python(self, value): + def split_url(url): """ Return a list of url parts via urlparse.urlsplit(), or raise @@ -758,19 +680,19 @@ class URLField(CharField): except ValueError: # urlparse.urlsplit can raise a ValueError with some # misformatted URLs. - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') value = super().to_python(value) if value: url_fields = split_url(value) if not url_fields[0]: # If no URL scheme given, assume http:// - url_fields[0] = "http" + url_fields[0] = 'http' if not url_fields[1]: # Assume that if no domain is provided, that the path segment # contains the domain. url_fields[1] = url_fields[2] - url_fields[2] = "" + url_fields[2] = '' # Rebuild the url_fields list, since the domain segment may now # contain the path too. url_fields = split_url(urlunsplit(url_fields)) @@ -787,7 +709,7 @@ class BooleanField(Field): # will submit for False. Also check for '0', since this is what # RadioSelect will provide. Because bool("True") == bool('1') == True, # we don't need to handle that explicitly. - if isinstance(value, str) and value.lower() in ("false", "0"): + if isinstance(value, str) and value.lower() in ('false', '0'): value = False else: value = bool(value) @@ -795,7 +717,7 @@ class BooleanField(Field): def validate(self, value): if not value and self.required: - raise ValidationError(self.error_messages["required"], code="required") + raise ValidationError(self.error_messages['required'], code='required') def has_changed(self, initial, data): if self.disabled: @@ -810,7 +732,6 @@ class NullBooleanField(BooleanField): A field whose valid values are None, True, and False. Clean invalid values to None. """ - widget = NullBooleanSelect def to_python(self, value): @@ -822,9 +743,9 @@ class NullBooleanField(BooleanField): the Booleanfield, this field must check for True because it doesn't use the bool() function. """ - if value in (True, "True", "true", "1"): + if value in (True, 'True', 'true', '1'): return True - elif value in (False, "False", "false", "0"): + elif value in (False, 'False', 'false', '0'): return False else: return None @@ -844,9 +765,7 @@ class CallableChoiceIterator: class ChoiceField(Field): widget = Select default_error_messages = { - "invalid_choice": _( - "Select a valid choice. %(value)s is not one of the available choices." - ), + 'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'), } def __init__(self, *, choices=(), **kwargs): @@ -877,7 +796,7 @@ class ChoiceField(Field): def to_python(self, value): """Return a string.""" if value in self.empty_values: - return "" + return '' return str(value) def validate(self, value): @@ -885,9 +804,9 @@ class ChoiceField(Field): super().validate(value) if value and not self.valid_value(value): raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": value}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': value}, ) def valid_value(self, value): @@ -906,7 +825,7 @@ class ChoiceField(Field): class TypedChoiceField(ChoiceField): - def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs): + def __init__(self, *, coerce=lambda val: val, empty_value='', **kwargs): self.coerce = coerce self.empty_value = empty_value super().__init__(**kwargs) @@ -921,9 +840,9 @@ class TypedChoiceField(ChoiceField): value = self.coerce(value) except (ValueError, TypeError, ValidationError): raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": value}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': value}, ) return value @@ -936,32 +855,28 @@ class MultipleChoiceField(ChoiceField): hidden_widget = MultipleHiddenInput widget = SelectMultiple default_error_messages = { - "invalid_choice": _( - "Select a valid choice. %(value)s is not one of the available choices." - ), - "invalid_list": _("Enter a list of values."), + 'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'), + 'invalid_list': _('Enter a list of values.'), } def to_python(self, value): if not value: return [] elif not isinstance(value, (list, tuple)): - raise ValidationError( - self.error_messages["invalid_list"], code="invalid_list" - ) + raise ValidationError(self.error_messages['invalid_list'], code='invalid_list') return [str(val) for val in value] def validate(self, value): """Validate that the input is a list or tuple.""" if self.required and not value: - raise ValidationError(self.error_messages["required"], code="required") + raise ValidationError(self.error_messages['required'], code='required') # Validate that each value in the value list is in self.choices. for val in value: if not self.valid_value(val): raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": val}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': val}, ) def has_changed(self, initial, data): @@ -981,7 +896,7 @@ class MultipleChoiceField(ChoiceField): class TypedMultipleChoiceField(MultipleChoiceField): def __init__(self, *, coerce=lambda val: val, **kwargs): self.coerce = coerce - self.empty_value = kwargs.pop("empty_value", []) + self.empty_value = kwargs.pop('empty_value', []) super().__init__(**kwargs) def _coerce(self, value): @@ -997,9 +912,9 @@ class TypedMultipleChoiceField(MultipleChoiceField): new_value.append(self.coerce(choice)) except (ValueError, TypeError, ValidationError): raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": choice}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': choice}, ) return new_value @@ -1011,14 +926,13 @@ class TypedMultipleChoiceField(MultipleChoiceField): if value != self.empty_value: super().validate(value) elif self.required: - raise ValidationError(self.error_messages["required"], code="required") + raise ValidationError(self.error_messages['required'], code='required') class ComboField(Field): """ A Field whose clean() method calls multiple Field clean() methods. """ - def __init__(self, fields, **kwargs): super().__init__(**kwargs) # Set 'required' to False on the individual fields, because the @@ -1056,17 +970,17 @@ class MultiValueField(Field): You'll probably want to use this with MultiWidget. """ - default_error_messages = { - "invalid": _("Enter a list of values."), - "incomplete": _("Enter a complete value."), + 'invalid': _('Enter a list of values.'), + 'incomplete': _('Enter a complete value.'), } def __init__(self, fields, *, require_all_fields=True, **kwargs): self.require_all_fields = require_all_fields super().__init__(**kwargs) for f in fields: - f.error_messages.setdefault("incomplete", self.error_messages["incomplete"]) + f.error_messages.setdefault('incomplete', + self.error_messages['incomplete']) if self.disabled: f.disabled = True if self.require_all_fields: @@ -1100,13 +1014,11 @@ class MultiValueField(Field): if not value or isinstance(value, (list, tuple)): if not value or not [v for v in value if v not in self.empty_values]: if self.required: - raise ValidationError( - self.error_messages["required"], code="required" - ) + raise ValidationError(self.error_messages['required'], code='required') else: return self.compress([]) else: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') for i, field in enumerate(self.fields): try: field_value = value[i] @@ -1117,15 +1029,13 @@ class MultiValueField(Field): # Raise a 'required' error if the MultiValueField is # required and any field is empty. if self.required: - raise ValidationError( - self.error_messages["required"], code="required" - ) + raise ValidationError(self.error_messages['required'], code='required') elif field.required: # Otherwise, add an 'incomplete' error to the list of # collected errors and skip field cleaning, if a required # field is empty. - if field.error_messages["incomplete"] not in errors: - errors.append(field.error_messages["incomplete"]) + if field.error_messages['incomplete'] not in errors: + errors.append(field.error_messages['incomplete']) continue try: clean_data.append(field.clean(field_value)) @@ -1151,13 +1061,13 @@ class MultiValueField(Field): fields=(DateField(), TimeField()), this might return a datetime object created by combining the date and time in data_list. """ - raise NotImplementedError("Subclasses must implement this method.") + raise NotImplementedError('Subclasses must implement this method.') def has_changed(self, initial, data): if self.disabled: return False if initial is None: - initial = ["" for x in range(0, len(data))] + initial = ['' for x in range(0, len(data))] else: if not isinstance(initial, list): initial = self.widget.decompress(initial) @@ -1172,16 +1082,8 @@ class MultiValueField(Field): class FilePathField(ChoiceField): - def __init__( - self, - path, - *, - match=None, - recursive=False, - allow_files=True, - allow_folders=False, - **kwargs, - ): + def __init__(self, path, *, match=None, recursive=False, allow_files=True, + allow_folders=False, **kwargs): self.path, self.match, self.recursive = path, match, recursive self.allow_files, self.allow_folders = allow_files, allow_folders super().__init__(choices=(), **kwargs) @@ -1203,22 +1105,20 @@ class FilePathField(ChoiceField): self.choices.append((f, f.replace(path, "", 1))) if self.allow_folders: for f in sorted(dirs): - if f == "__pycache__": + if f == '__pycache__': continue if self.match is None or self.match_re.search(f): f = os.path.join(root, f) self.choices.append((f, f.replace(path, "", 1))) else: choices = [] - with os.scandir(self.path) as entries: - for f in entries: - if f.name == "__pycache__": - continue - if ( - (self.allow_files and f.is_file()) - or (self.allow_folders and f.is_dir()) - ) and (self.match is None or self.match_re.search(f.name)): - choices.append((f.path, f.name)) + for f in os.scandir(self.path): + if f.name == '__pycache__': + continue + if (((self.allow_files and f.is_file()) or + (self.allow_folders and f.is_dir())) and + (self.match is None or self.match_re.search(f.name))): + choices.append((f.path, f.name)) choices.sort(key=operator.itemgetter(1)) self.choices.extend(choices) @@ -1229,26 +1129,22 @@ class SplitDateTimeField(MultiValueField): widget = SplitDateTimeWidget hidden_widget = SplitHiddenDateTimeWidget default_error_messages = { - "invalid_date": _("Enter a valid date."), - "invalid_time": _("Enter a valid time."), + 'invalid_date': _('Enter a valid date.'), + 'invalid_time': _('Enter a valid time.'), } def __init__(self, *, input_date_formats=None, input_time_formats=None, **kwargs): errors = self.default_error_messages.copy() - if "error_messages" in kwargs: - errors.update(kwargs["error_messages"]) - localize = kwargs.get("localize", False) + if 'error_messages' in kwargs: + errors.update(kwargs['error_messages']) + localize = kwargs.get('localize', False) fields = ( - DateField( - input_formats=input_date_formats, - error_messages={"invalid": errors["invalid_date"]}, - localize=localize, - ), - TimeField( - input_formats=input_time_formats, - error_messages={"invalid": errors["invalid_time"]}, - localize=localize, - ), + DateField(input_formats=input_date_formats, + error_messages={'invalid': errors['invalid_date']}, + localize=localize), + TimeField(input_formats=input_time_formats, + error_messages={'invalid': errors['invalid_time']}, + localize=localize), ) super().__init__(fields, **kwargs) @@ -1257,31 +1153,25 @@ class SplitDateTimeField(MultiValueField): # Raise a validation error if time or date is empty # (possible if SplitDateTimeField has required=False). if data_list[0] in self.empty_values: - raise ValidationError( - self.error_messages["invalid_date"], code="invalid_date" - ) + raise ValidationError(self.error_messages['invalid_date'], code='invalid_date') if data_list[1] in self.empty_values: - raise ValidationError( - self.error_messages["invalid_time"], code="invalid_time" - ) + raise ValidationError(self.error_messages['invalid_time'], code='invalid_time') result = datetime.datetime.combine(*data_list) return from_current_timezone(result) return None class GenericIPAddressField(CharField): - def __init__(self, *, protocol="both", unpack_ipv4=False, **kwargs): + def __init__(self, *, protocol='both', unpack_ipv4=False, **kwargs): self.unpack_ipv4 = unpack_ipv4 - self.default_validators = validators.ip_address_validators( - protocol, unpack_ipv4 - )[0] + self.default_validators = validators.ip_address_validators(protocol, unpack_ipv4)[0] super().__init__(**kwargs) def to_python(self, value): if value in self.empty_values: - return "" + return '' value = value.strip() - if value and ":" in value: + if value and ':' in value: return clean_ipv6_address(value, self.unpack_ipv4) return value @@ -1298,7 +1188,7 @@ class SlugField(CharField): class UUIDField(CharField): default_error_messages = { - "invalid": _("Enter a valid UUID."), + 'invalid': _('Enter a valid UUID.'), } def prepare_value(self, value): @@ -1314,7 +1204,7 @@ class UUIDField(CharField): try: value = uuid.UUID(value) except ValueError: - raise ValidationError(self.error_messages["invalid"], code="invalid") + raise ValidationError(self.error_messages['invalid'], code='invalid') return value @@ -1328,7 +1218,7 @@ class JSONString(str): class JSONField(CharField): default_error_messages = { - "invalid": _("Enter a valid JSON."), + 'invalid': _('Enter a valid JSON.'), } widget = Textarea @@ -1348,9 +1238,9 @@ class JSONField(CharField): converted = json.loads(value, cls=self.decoder) except json.JSONDecodeError: raise ValidationError( - self.error_messages["invalid"], - code="invalid", - params={"value": value}, + self.error_messages['invalid'], + code='invalid', + params={'value': value}, ) if isinstance(converted, str): return JSONString(converted) @@ -1360,8 +1250,6 @@ class JSONField(CharField): def bound_data(self, data, initial): if self.disabled: return initial - if data is None: - return None try: return json.loads(data, cls=self.decoder) except json.JSONDecodeError: @@ -1377,6 +1265,7 @@ class JSONField(CharField): return True # For purposes of seeing whether something has changed, True isn't the # same as 1 and the order of keys doesn't matter. - return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps( - self.to_python(data), sort_keys=True, cls=self.encoder + return ( + json.dumps(initial, sort_keys=True, cls=self.encoder) != + json.dumps(self.to_python(data), sort_keys=True, cls=self.encoder) ) diff --git a/venv/Lib/site-packages/django/forms/forms.py b/venv/Lib/site-packages/django/forms/forms.py index 952b974..14f5dea 100644 --- a/venv/Lib/site-packages/django/forms/forms.py +++ b/venv/Lib/site-packages/django/forms/forms.py @@ -3,33 +3,28 @@ Form classes """ import copy -import datetime -import warnings from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.forms.fields import Field, FileField -from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin +from django.forms.utils import ErrorDict, ErrorList from django.forms.widgets import Media, MediaDefiningClass from django.utils.datastructures import MultiValueDict -from django.utils.deprecation import RemovedInDjango50Warning from django.utils.functional import cached_property -from django.utils.html import conditional_escape -from django.utils.safestring import SafeString, mark_safe +from django.utils.html import conditional_escape, html_safe +from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ from .renderers import get_default_renderer -__all__ = ("BaseForm", "Form") +__all__ = ('BaseForm', 'Form') class DeclarativeFieldsMetaclass(MediaDefiningClass): """Collect Fields declared on the base classes.""" - def __new__(mcs, name, bases, attrs): # Collect fields from current class and remove them from attrs. - attrs["declared_fields"] = { - key: attrs.pop(key) - for key, value in list(attrs.items()) + attrs['declared_fields'] = { + key: attrs.pop(key) for key, value in list(attrs.items()) if isinstance(value, Field) } @@ -39,7 +34,7 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass): declared_fields = {} for base in reversed(new_class.__mro__): # Collect fields from base class. - if hasattr(base, "declared_fields"): + if hasattr(base, 'declared_fields'): declared_fields.update(base.declared_fields) # Field shadowing. @@ -53,39 +48,22 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass): return new_class -class BaseForm(RenderableFormMixin): +@html_safe +class BaseForm: """ The main implementation of all the Form logic. Note that this class is different than Form. See the comments by the Form class for more info. Any improvements to the form API should be made to this class, not to the Form class. """ - default_renderer = None field_order = None prefix = None use_required_attribute = True - template_name = "django/forms/default.html" - template_name_p = "django/forms/p.html" - template_name_table = "django/forms/table.html" - template_name_ul = "django/forms/ul.html" - template_name_label = "django/forms/label.html" - - def __init__( - self, - data=None, - files=None, - auto_id="id_%s", - prefix=None, - initial=None, - error_class=ErrorList, - label_suffix=None, - empty_permitted=False, - field_order=None, - use_required_attribute=None, - renderer=None, - ): + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, label_suffix=None, + empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None): self.is_bound = data is not None or files is not None self.data = MultiValueDict() if data is None else data self.files = MultiValueDict() if files is None else files @@ -95,7 +73,7 @@ class BaseForm(RenderableFormMixin): self.initial = initial or {} self.error_class = error_class # Translators: This is the default suffix added to form field labels - self.label_suffix = label_suffix if label_suffix is not None else _(":") + self.label_suffix = label_suffix if label_suffix is not None else _(':') self.empty_permitted = empty_permitted self._errors = None # Stores the errors after clean() has been called. @@ -113,8 +91,8 @@ class BaseForm(RenderableFormMixin): if self.empty_permitted and self.use_required_attribute: raise ValueError( - "The empty_permitted and use_required_attribute arguments may " - "not both be True." + 'The empty_permitted and use_required_attribute arguments may ' + 'not both be True.' ) # Initialize form renderer. Use a global default if not specified @@ -150,48 +128,40 @@ class BaseForm(RenderableFormMixin): fields.update(self.fields) # add remaining fields in original order self.fields = fields + def __str__(self): + return self.as_table() + def __repr__(self): if self._errors is None: is_valid = "Unknown" else: is_valid = self.is_bound and not self._errors - return "<%(cls)s bound=%(bound)s, valid=%(valid)s, fields=(%(fields)s)>" % { - "cls": self.__class__.__name__, - "bound": self.is_bound, - "valid": is_valid, - "fields": ";".join(self.fields), + return '<%(cls)s bound=%(bound)s, valid=%(valid)s, fields=(%(fields)s)>' % { + 'cls': self.__class__.__name__, + 'bound': self.is_bound, + 'valid': is_valid, + 'fields': ';'.join(self.fields), } - def _bound_items(self): - """Yield (name, bf) pairs, where bf is a BoundField object.""" - for name in self.fields: - yield name, self[name] - def __iter__(self): - """Yield the form's fields as BoundField objects.""" for name in self.fields: yield self[name] def __getitem__(self, name): """Return a BoundField with the given name.""" - try: - return self._bound_fields_cache[name] - except KeyError: - pass try: field = self.fields[name] except KeyError: raise KeyError( - "Key '%s' not found in '%s'. Choices are: %s." - % ( + "Key '%s' not found in '%s'. Choices are: %s." % ( name, self.__class__.__name__, - ", ".join(sorted(self.fields)), + ', '.join(sorted(self.fields)), ) ) - bound_field = field.get_bound_field(self, name) - self._bound_fields_cache[name] = bound_field - return bound_field + if name not in self._bound_fields_cache: + self._bound_fields_cache[name] = field.get_bound_field(self, name) + return self._bound_fields_cache[name] @property def errors(self): @@ -211,45 +181,27 @@ class BaseForm(RenderableFormMixin): Subclasses may wish to override. """ - return "%s-%s" % (self.prefix, field_name) if self.prefix else field_name + return '%s-%s' % (self.prefix, field_name) if self.prefix else field_name def add_initial_prefix(self, field_name): """Add an 'initial' prefix for checking dynamic initial values.""" - return "initial-%s" % self.add_prefix(field_name) + return 'initial-%s' % self.add_prefix(field_name) - def _widget_data_value(self, widget, html_name): - # value_from_datadict() gets the data from the data dictionaries. - # Each widget type knows how to retrieve its own data, because some - # widgets split data over several HTML fields. - return widget.value_from_datadict(self.data, self.files, html_name) - - def _html_output( - self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row - ): + def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): "Output HTML. Used by as_table(), as_ul(), as_p()." - warnings.warn( - "django.forms.BaseForm._html_output() is deprecated. " - "Please use .render() and .get_context() instead.", - RemovedInDjango50Warning, - stacklevel=2, - ) # Errors that should be displayed above all fields. top_errors = self.non_field_errors().copy() output, hidden_fields = [], [] - for name, bf in self._bound_items(): - field = bf.field - html_class_attr = "" + for name, field in self.fields.items(): + html_class_attr = '' + bf = self[name] bf_errors = self.error_class(bf.errors) if bf.is_hidden: if bf_errors: top_errors.extend( - [ - _("(Hidden field %(name)s) %(error)s") - % {"name": name, "error": str(e)} - for e in bf_errors - ] - ) + [_('(Hidden field %(name)s) %(error)s') % {'name': name, 'error': str(e)} + for e in bf_errors]) hidden_fields.append(str(bf)) else: # Create a 'class="..."' attribute if the row should have any @@ -263,33 +215,30 @@ class BaseForm(RenderableFormMixin): if bf.label: label = conditional_escape(bf.label) - label = bf.label_tag(label) or "" + label = bf.label_tag(label) or '' else: - label = "" + label = '' if field.help_text: help_text = help_text_html % field.help_text else: - help_text = "" + help_text = '' - output.append( - normal_row - % { - "errors": bf_errors, - "label": label, - "field": bf, - "help_text": help_text, - "html_class_attr": html_class_attr, - "css_classes": css_classes, - "field_name": bf.html_name, - } - ) + output.append(normal_row % { + 'errors': bf_errors, + 'label': label, + 'field': bf, + 'help_text': help_text, + 'html_class_attr': html_class_attr, + 'css_classes': css_classes, + 'field_name': bf.html_name, + }) if top_errors: output.insert(0, error_row % top_errors) if hidden_fields: # Insert any hidden fields in the last row. - str_hidden = "".join(hidden_fields) + str_hidden = ''.join(hidden_fields) if output: last_row = output[-1] # Chop off the trailing row_ender (e.g. '</td></tr>') and @@ -299,55 +248,52 @@ class BaseForm(RenderableFormMixin): # that users write): if there are only top errors, we may # not be able to conscript the last row for our purposes, # so insert a new, empty row. - last_row = normal_row % { - "errors": "", - "label": "", - "field": "", - "help_text": "", - "html_class_attr": html_class_attr, - "css_classes": "", - "field_name": "", - } + last_row = (normal_row % { + 'errors': '', + 'label': '', + 'field': '', + 'help_text': '', + 'html_class_attr': html_class_attr, + 'css_classes': '', + 'field_name': '', + }) output.append(last_row) - output[-1] = last_row[: -len(row_ender)] + str_hidden + row_ender + output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender else: # If there aren't any rows in the output, just append the # hidden fields. output.append(str_hidden) - return mark_safe("\n".join(output)) + return mark_safe('\n'.join(output)) - def get_context(self): - fields = [] - hidden_fields = [] - top_errors = self.non_field_errors().copy() - for name, bf in self._bound_items(): - bf_errors = self.error_class(bf.errors, renderer=self.renderer) - if bf.is_hidden: - if bf_errors: - top_errors += [ - _("(Hidden field %(name)s) %(error)s") - % {"name": name, "error": str(e)} - for e in bf_errors - ] - hidden_fields.append(bf) - else: - errors_str = str(bf_errors) - # RemovedInDjango50Warning. - if not isinstance(errors_str, SafeString): - warnings.warn( - f"Returning a plain string from " - f"{self.error_class.__name__} is deprecated. Please " - f"customize via the template system instead.", - RemovedInDjango50Warning, - ) - errors_str = mark_safe(errors_str) - fields.append((bf, errors_str)) - return { - "form": self, - "fields": fields, - "hidden_fields": hidden_fields, - "errors": top_errors, - } + def as_table(self): + "Return this form rendered as HTML <tr>s -- excluding the <table></table>." + return self._html_output( + normal_row='<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', + error_row='<tr><td colspan="2">%s</td></tr>', + row_ender='</td></tr>', + help_text_html='<br><span class="helptext">%s</span>', + errors_on_separate_row=False, + ) + + def as_ul(self): + "Return this form rendered as HTML <li>s -- excluding the <ul></ul>." + return self._html_output( + normal_row='<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', + error_row='<li>%s</li>', + row_ender='</li>', + help_text_html=' <span class="helptext">%s</span>', + errors_on_separate_row=False, + ) + + def as_p(self): + "Return this form rendered as HTML <p>s." + return self._html_output( + normal_row='<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', + error_row='%s', + row_ender='</p>', + help_text_html=' <span class="helptext">%s</span>', + errors_on_separate_row=True, + ) def non_field_errors(self): """ @@ -355,10 +301,7 @@ class BaseForm(RenderableFormMixin): field -- i.e., from Form.clean(). Return an empty ErrorList if there are none. """ - return self.errors.get( - NON_FIELD_ERRORS, - self.error_class(error_class="nonfield", renderer=self.renderer), - ) + return self.errors.get(NON_FIELD_ERRORS, self.error_class(error_class='nonfield')) def add_error(self, field, error): """ @@ -383,7 +326,7 @@ class BaseForm(RenderableFormMixin): # do the hard work of making sense of the input. error = ValidationError(error) - if hasattr(error, "error_dict"): + if hasattr(error, 'error_dict'): if field is not None: raise TypeError( "The argument `field` must be `None` when the `error` " @@ -398,23 +341,19 @@ class BaseForm(RenderableFormMixin): if field not in self.errors: if field != NON_FIELD_ERRORS and field not in self.fields: raise ValueError( - "'%s' has no field named '%s'." - % (self.__class__.__name__, field) - ) + "'%s' has no field named '%s'." % (self.__class__.__name__, field)) if field == NON_FIELD_ERRORS: - self._errors[field] = self.error_class( - error_class="nonfield", renderer=self.renderer - ) + self._errors[field] = self.error_class(error_class='nonfield') else: - self._errors[field] = self.error_class(renderer=self.renderer) + self._errors[field] = self.error_class() self._errors[field].extend(error_list) if field in self.cleaned_data: del self.cleaned_data[field] def has_error(self, field, code=None): return field in self.errors and ( - code is None - or any(error.code == code for error in self.errors.as_data()[field]) + code is None or + any(error.code == code for error in self.errors.as_data()[field]) ) def full_clean(self): @@ -435,17 +374,23 @@ class BaseForm(RenderableFormMixin): self._post_clean() def _clean_fields(self): - for name, bf in self._bound_items(): - field = bf.field - value = bf.initial if field.disabled else bf.data + for name, field in self.fields.items(): + # value_from_datadict() gets the data from the data dictionaries. + # Each widget type knows how to retrieve its own data, because some + # widgets split data over several HTML fields. + if field.disabled: + value = self.get_initial_for_field(field, name) + else: + value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): - value = field.clean(value, bf.initial) + initial = self.get_initial_for_field(field, name) + value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value - if hasattr(self, "clean_%s" % name): - value = getattr(self, "clean_%s" % name)() + if hasattr(self, 'clean_%s' % name): + value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e) @@ -481,7 +426,27 @@ class BaseForm(RenderableFormMixin): @cached_property def changed_data(self): - return [name for name, bf in self._bound_items() if bf._has_changed()] + data = [] + for name, field in self.fields.items(): + prefixed_name = self.add_prefix(name) + data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name) + if not field.show_hidden_initial: + # Use the BoundField's initial as this is the value passed to + # the widget. + initial_value = self[name].initial + else: + initial_prefixed_name = self.add_initial_prefix(name) + hidden_widget = field.hidden_widget() + try: + initial_value = field.to_python(hidden_widget.value_from_datadict( + self.data, self.files, initial_prefixed_name)) + except ValidationError: + # Always assume data has changed if validation fails. + data.append(name) + continue + if field.has_changed(initial_value, data_value): + data.append(name) + return data @property def media(self): @@ -520,13 +485,6 @@ class BaseForm(RenderableFormMixin): value = self.initial.get(field_name, field.initial) if callable(value): value = value() - # If this is an auto-generated default date, nix the microseconds - # for standardized handling. See #22502. - if ( - isinstance(value, (datetime.datetime, datetime.time)) - and not field.widget.supports_microseconds - ): - value = value.replace(microsecond=0) return value diff --git a/venv/Lib/site-packages/django/forms/formsets.py b/venv/Lib/site-packages/django/forms/formsets.py index 1317217..a89c355 100644 --- a/venv/Lib/site-packages/django/forms/formsets.py +++ b/venv/Lib/site-packages/django/forms/formsets.py @@ -1,22 +1,22 @@ from django.core.exceptions import ValidationError from django.forms import Form from django.forms.fields import BooleanField, IntegerField -from django.forms.renderers import get_default_renderer -from django.forms.utils import ErrorList, RenderableFormMixin -from django.forms.widgets import CheckboxInput, HiddenInput, NumberInput +from django.forms.utils import ErrorList +from django.forms.widgets import HiddenInput, NumberInput from django.utils.functional import cached_property -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import ngettext +from django.utils.html import html_safe +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _, ngettext -__all__ = ("BaseFormSet", "formset_factory", "all_valid") +__all__ = ('BaseFormSet', 'formset_factory', 'all_valid') # special field names -TOTAL_FORM_COUNT = "TOTAL_FORMS" -INITIAL_FORM_COUNT = "INITIAL_FORMS" -MIN_NUM_FORM_COUNT = "MIN_NUM_FORMS" -MAX_NUM_FORM_COUNT = "MAX_NUM_FORMS" -ORDERING_FIELD_NAME = "ORDER" -DELETION_FIELD_NAME = "DELETE" +TOTAL_FORM_COUNT = 'TOTAL_FORMS' +INITIAL_FORM_COUNT = 'INITIAL_FORMS' +MIN_NUM_FORM_COUNT = 'MIN_NUM_FORMS' +MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS' +ORDERING_FIELD_NAME = 'ORDER' +DELETION_FIELD_NAME = 'DELETE' # default minimum number of forms in a formset DEFAULT_MIN_NUM = 0 @@ -31,19 +31,14 @@ class ManagementForm(Form): new forms via JavaScript, you should increment the count field of this form as well. """ - def __init__(self, *args, **kwargs): self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) # MIN_NUM_FORM_COUNT and MAX_NUM_FORM_COUNT are output with the rest of # the management form, but only for the convenience of client-side # code. The POST value of them returned from the client is not checked. - self.base_fields[MIN_NUM_FORM_COUNT] = IntegerField( - required=False, widget=HiddenInput - ) - self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField( - required=False, widget=HiddenInput - ) + self.base_fields[MIN_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) + self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) super().__init__(*args, **kwargs) def clean(self): @@ -55,35 +50,22 @@ class ManagementForm(Form): return cleaned_data -class BaseFormSet(RenderableFormMixin): +@html_safe +class BaseFormSet: """ A collection of instances of the same Form class. """ - - deletion_widget = CheckboxInput ordering_widget = NumberInput default_error_messages = { - "missing_management_form": _( - "ManagementForm data is missing or has been tampered with. Missing fields: " - "%(field_names)s. You may need to file a bug report if the issue persists." + 'missing_management_form': _( + 'ManagementForm data is missing or has been tampered with. Missing fields: ' + '%(field_names)s. You may need to file a bug report if the issue persists.' ), } - template_name = "django/forms/formsets/default.html" - template_name_p = "django/forms/formsets/p.html" - template_name_table = "django/forms/formsets/table.html" - template_name_ul = "django/forms/formsets/ul.html" - def __init__( - self, - data=None, - files=None, - auto_id="id_%s", - prefix=None, - initial=None, - error_class=ErrorList, - form_kwargs=None, - error_messages=None, - ): + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, form_kwargs=None, + error_messages=None): self.is_bound = data is not None or files is not None self.prefix = prefix or self.get_default_prefix() self.auto_id = auto_id @@ -97,11 +79,14 @@ class BaseFormSet(RenderableFormMixin): messages = {} for cls in reversed(type(self).__mro__): - messages.update(getattr(cls, "default_error_messages", {})) + messages.update(getattr(cls, 'default_error_messages', {})) if error_messages is not None: messages.update(error_messages) self.error_messages = messages + def __str__(self): + return self.as_table() + def __iter__(self): """Yield the forms in the order they should be rendered.""" return iter(self.forms) @@ -124,25 +109,15 @@ class BaseFormSet(RenderableFormMixin): def management_form(self): """Return the ManagementForm instance for this FormSet.""" if self.is_bound: - form = ManagementForm( - self.data, - auto_id=self.auto_id, - prefix=self.prefix, - renderer=self.renderer, - ) + form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix) form.full_clean() else: - form = ManagementForm( - auto_id=self.auto_id, - prefix=self.prefix, - initial={ - TOTAL_FORM_COUNT: self.total_form_count(), - INITIAL_FORM_COUNT: self.initial_form_count(), - MIN_NUM_FORM_COUNT: self.min_num, - MAX_NUM_FORM_COUNT: self.max_num, - }, - renderer=self.renderer, - ) + form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={ + TOTAL_FORM_COUNT: self.total_form_count(), + INITIAL_FORM_COUNT: self.initial_form_count(), + MIN_NUM_FORM_COUNT: self.min_num, + MAX_NUM_FORM_COUNT: self.max_num + }) return form def total_form_count(self): @@ -152,9 +127,7 @@ class BaseFormSet(RenderableFormMixin): # count in the data; this is DoS protection to prevent clients # from forcing the server to instantiate arbitrary numbers of # forms - return min( - self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max - ) + return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max) else: initial_forms = self.initial_form_count() total_forms = max(initial_forms, self.min_num) + self.extra @@ -196,27 +169,26 @@ class BaseFormSet(RenderableFormMixin): def _construct_form(self, i, **kwargs): """Instantiate and return the i-th form instance in a formset.""" defaults = { - "auto_id": self.auto_id, - "prefix": self.add_prefix(i), - "error_class": self.error_class, + 'auto_id': self.auto_id, + 'prefix': self.add_prefix(i), + 'error_class': self.error_class, # Don't render the HTML 'required' attribute as it may cause # incorrect validation for extra, optional, and deleted # forms in the formset. - "use_required_attribute": False, - "renderer": self.renderer, + 'use_required_attribute': False, } if self.is_bound: - defaults["data"] = self.data - defaults["files"] = self.files - if self.initial and "initial" not in kwargs: + defaults['data'] = self.data + defaults['files'] = self.files + if self.initial and 'initial' not in kwargs: try: - defaults["initial"] = self.initial[i] + defaults['initial'] = self.initial[i] except IndexError: pass # Allow extra forms to be empty, unless they're part of # the minimum forms. if i >= self.initial_form_count() and i >= self.min_num: - defaults["empty_permitted"] = True + defaults['empty_permitted'] = True defaults.update(kwargs) form = self.form(**defaults) self.add_fields(form, i) @@ -225,22 +197,21 @@ class BaseFormSet(RenderableFormMixin): @property def initial_forms(self): """Return a list of all the initial forms in this formset.""" - return self.forms[: self.initial_form_count()] + return self.forms[:self.initial_form_count()] @property def extra_forms(self): """Return a list of all the extra forms in this formset.""" - return self.forms[self.initial_form_count() :] + return self.forms[self.initial_form_count():] @property def empty_form(self): form = self.form( auto_id=self.auto_id, - prefix=self.add_prefix("__prefix__"), + prefix=self.add_prefix('__prefix__'), empty_permitted=True, use_required_attribute=False, - **self.get_form_kwargs(None), - renderer=self.renderer, + **self.get_form_kwargs(None) ) self.add_fields(form, None) return form @@ -251,9 +222,7 @@ class BaseFormSet(RenderableFormMixin): Return a list of form.cleaned_data dicts for every form in self.forms. """ if not self.is_valid(): - raise AttributeError( - "'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__ - ) + raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__) return [form.cleaned_data for form in self.forms] @property @@ -263,7 +232,7 @@ class BaseFormSet(RenderableFormMixin): return [] # construct _deleted_form_indexes which is just a list of form indexes # that have had their deletion widget set to True - if not hasattr(self, "_deleted_form_indexes"): + if not hasattr(self, '_deleted_form_indexes'): self._deleted_form_indexes = [] for i, form in enumerate(self.forms): # if this is an extra form and hasn't changed, don't consider it @@ -280,14 +249,12 @@ class BaseFormSet(RenderableFormMixin): Raise an AttributeError if ordering is not allowed. """ if not self.is_valid() or not self.can_order: - raise AttributeError( - "'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__ - ) + raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__) # Construct _ordering, which is a list of (form_index, order_field_value) # tuples. After constructing this list, we'll sort it by order_field_value # so we have a way to get to the form indexes in the order specified # by the form data. - if not hasattr(self, "_ordering"): + if not hasattr(self, '_ordering'): self._ordering = [] for i, form in enumerate(self.forms): # if this is an extra form and hasn't changed, don't consider it @@ -307,7 +274,6 @@ class BaseFormSet(RenderableFormMixin): if k[1] is None: return (1, 0) # +infinity, larger than any number return (0, k[1]) - self._ordering.sort(key=compare_ordering_key) # Return a list of form.cleaned_data dicts in the order specified by # the form data. @@ -315,11 +281,7 @@ class BaseFormSet(RenderableFormMixin): @classmethod def get_default_prefix(cls): - return "form" - - @classmethod - def get_deletion_widget(cls): - return cls.deletion_widget + return 'form' @classmethod def get_ordering_widget(cls): @@ -344,9 +306,8 @@ class BaseFormSet(RenderableFormMixin): def total_error_count(self): """Return the number of errors across all forms in the formset.""" - return len(self.non_form_errors()) + sum( - len(form_errors) for form_errors in self.errors - ) + return len(self.non_form_errors()) +\ + sum(len(form_errors) for form_errors in self.errors) def _should_delete_form(self, form): """Return whether or not the form was marked for deletion.""" @@ -360,13 +321,10 @@ class BaseFormSet(RenderableFormMixin): self.errors # List comprehension ensures is_valid() is called for all forms. # Forms due to be deleted shouldn't cause the formset to be invalid. - forms_valid = all( - [ - form.is_valid() - for form in self.forms - if not (self.can_delete and self._should_delete_form(form)) - ] - ) + forms_valid = all([ + form.is_valid() for form in self.forms + if not (self.can_delete and self._should_delete_form(form)) + ]) return forms_valid and not self.non_form_errors() def full_clean(self): @@ -375,9 +333,7 @@ class BaseFormSet(RenderableFormMixin): self._non_form_errors. """ self._errors = [] - self._non_form_errors = self.error_class( - error_class="nonform", renderer=self.renderer - ) + self._non_form_errors = self.error_class() empty_forms_count = 0 if not self.is_bound: # Stop further processing. @@ -385,14 +341,14 @@ class BaseFormSet(RenderableFormMixin): if not self.management_form.is_valid(): error = ValidationError( - self.error_messages["missing_management_form"], + self.error_messages['missing_management_form'], params={ - "field_names": ", ".join( + 'field_names': ', '.join( self.management_form.add_prefix(field_name) for field_name in self.management_form.errors ), }, - code="missing_management_form", + code='missing_management_form', ) self._non_form_errors.append(error) @@ -407,45 +363,24 @@ class BaseFormSet(RenderableFormMixin): continue self._errors.append(form_errors) try: - if ( - self.validate_max - and self.total_form_count() - len(self.deleted_forms) > self.max_num - ) or self.management_form.cleaned_data[ - TOTAL_FORM_COUNT - ] > self.absolute_max: - raise ValidationError( - ngettext( - "Please submit at most %d form.", - "Please submit at most %d forms.", - self.max_num, - ) - % self.max_num, - code="too_many_forms", - ) - if ( - self.validate_min - and self.total_form_count() - - len(self.deleted_forms) - - empty_forms_count - < self.min_num - ): - raise ValidationError( - ngettext( - "Please submit at least %d form.", - "Please submit at least %d forms.", - self.min_num, - ) - % self.min_num, - code="too_few_forms", + if (self.validate_max and + self.total_form_count() - len(self.deleted_forms) > self.max_num) or \ + self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max: + raise ValidationError(ngettext( + "Please submit at most %d form.", + "Please submit at most %d forms.", self.max_num) % self.max_num, + code='too_many_forms', ) + if (self.validate_min and + self.total_form_count() - len(self.deleted_forms) - empty_forms_count < self.min_num): + raise ValidationError(ngettext( + "Please submit at least %d form.", + "Please submit at least %d forms.", self.min_num) % self.min_num, + code='too_few_forms') # Give self.clean() a chance to do cross-form validation. self.clean() except ValidationError as e: - self._non_form_errors = self.error_class( - e.error_list, - error_class="nonform", - renderer=self.renderer, - ) + self._non_form_errors = self.error_class(e.error_list) def clean(self): """ @@ -467,26 +402,22 @@ class BaseFormSet(RenderableFormMixin): # Only pre-fill the ordering field for initial forms. if index is not None and index < initial_form_count: form.fields[ORDERING_FIELD_NAME] = IntegerField( - label=_("Order"), + label=_('Order'), initial=index + 1, required=False, widget=self.get_ordering_widget(), ) else: form.fields[ORDERING_FIELD_NAME] = IntegerField( - label=_("Order"), + label=_('Order'), required=False, widget=self.get_ordering_widget(), ) if self.can_delete and (self.can_delete_extra or index < initial_form_count): - form.fields[DELETION_FIELD_NAME] = BooleanField( - label=_("Delete"), - required=False, - widget=self.get_deletion_widget(), - ) + form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False) def add_prefix(self, index): - return "%s-%s" % (self.prefix, index) + return '%s-%s' % (self.prefix, index) def is_multipart(self): """ @@ -507,24 +438,29 @@ class BaseFormSet(RenderableFormMixin): else: return self.empty_form.media - def get_context(self): - return {"formset": self} + def as_table(self): + "Return this formset rendered as HTML <tr>s -- excluding the <table></table>." + # XXX: there is no semantic division between forms here, there + # probably should be. It might make sense to render each form as a + # table row with each field as a td. + forms = ' '.join(form.as_table() for form in self) + return mark_safe(str(self.management_form) + '\n' + forms) + + def as_p(self): + "Return this formset rendered as HTML <p>s." + forms = ' '.join(form.as_p() for form in self) + return mark_safe(str(self.management_form) + '\n' + forms) + + def as_ul(self): + "Return this formset rendered as HTML <li>s." + forms = ' '.join(form.as_ul() for form in self) + return mark_safe(str(self.management_form) + '\n' + forms) -def formset_factory( - form, - formset=BaseFormSet, - extra=1, - can_order=False, - can_delete=False, - max_num=None, - validate_max=False, - min_num=None, - validate_min=False, - absolute_max=None, - can_delete_extra=True, - renderer=None, -): +def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, + can_delete=False, max_num=None, validate_max=False, + min_num=None, validate_min=False, absolute_max=None, + can_delete_extra=True): """Return a FormSet for the given form class.""" if min_num is None: min_num = DEFAULT_MIN_NUM @@ -536,21 +472,22 @@ def formset_factory( if absolute_max is None: absolute_max = max_num + DEFAULT_MAX_NUM if max_num > absolute_max: - raise ValueError("'absolute_max' must be greater or equal to 'max_num'.") + raise ValueError( + "'absolute_max' must be greater or equal to 'max_num'." + ) attrs = { - "form": form, - "extra": extra, - "can_order": can_order, - "can_delete": can_delete, - "can_delete_extra": can_delete_extra, - "min_num": min_num, - "max_num": max_num, - "absolute_max": absolute_max, - "validate_min": validate_min, - "validate_max": validate_max, - "renderer": renderer or get_default_renderer(), + 'form': form, + 'extra': extra, + 'can_order': can_order, + 'can_delete': can_delete, + 'can_delete_extra': can_delete_extra, + 'min_num': min_num, + 'max_num': max_num, + 'absolute_max': absolute_max, + 'validate_min': validate_min, + 'validate_max': validate_max, } - return type(form.__name__ + "FormSet", (formset,), attrs) + return type(form.__name__ + 'FormSet', (formset,), attrs) def all_valid(formsets): diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/attrs.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/attrs.html deleted file mode 100644 index b7e3b8e..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/attrs.html +++ /dev/null @@ -1 +0,0 @@ -{% for name, value in attrs.items() %}{% if value is not sameas False %} {{ name }}{% if value is not sameas True %}="{{ value }}"{% endif %}{% endif %}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/default.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/default.html deleted file mode 100644 index d034b60..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/table.html" %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/default.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/default.html deleted file mode 100644 index 19e4fba..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/errors/dict/ul.html" %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/text.txt b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/text.txt deleted file mode 100644 index dc9fd80..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/text.txt +++ /dev/null @@ -1,3 +0,0 @@ -{% for field, errors in errors %}* {{ field }} -{% for error in errors %} * {{ error }} -{% endfor %}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/ul.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/ul.html deleted file mode 100644 index c16fd65..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/dict/ul.html +++ /dev/null @@ -1 +0,0 @@ -{% if errors %}<ul class="{{ error_class }}">{% for field, error in errors %}<li>{{ field }}{{ error }}</li>{% endfor %}</ul>{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/default.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/default.html deleted file mode 100644 index fccc328..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/errors/list/ul.html" %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/text.txt b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/text.txt deleted file mode 100644 index aa7f870..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/text.txt +++ /dev/null @@ -1,2 +0,0 @@ -{% for error in errors %}* {{ error }} -{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/ul.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/ul.html deleted file mode 100644 index 752f7c2..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/errors/list/ul.html +++ /dev/null @@ -1 +0,0 @@ -{% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/default.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/default.html deleted file mode 100644 index d8284c5..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/default.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/p.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/p.html deleted file mode 100644 index 3ed889e..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/p.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_p() }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/table.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/table.html deleted file mode 100644 index 2503377..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/table.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_table() }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/ul.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/ul.html deleted file mode 100644 index 335e91e..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/formsets/ul.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_ul() }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/label.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/label.html deleted file mode 100644 index 7ad5257..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/label.html +++ /dev/null @@ -1 +0,0 @@ -{% if use_tag %}<label{% if attrs %}{% include 'django/forms/attrs.html' %}{% endif %}>{{ label }}</label>{% else %}{{ label }}{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/p.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/p.html deleted file mode 100644 index a2872d9..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/p.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ errors }} -{% if errors and not fields %} - <p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p> -{% endif %} -{% for field, errors in fields %} - {{ errors }} - <p{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}> - {% if field.label %}{{ field.label_tag() }}{% endif %} - {{ field }} - {% if field.help_text %} - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if loop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </p> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/table.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/table.html deleted file mode 100644 index d1d51f2..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/table.html +++ /dev/null @@ -1,29 +0,0 @@ -{% if errors %} - <tr> - <td colspan="2"> - {{ errors }} - {% if not fields %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </td> - </tr> -{% endif %} -{% for field, errors in fields %} - <tr{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}> - <th>{% if field.label %}{{ field.label_tag() }}{% endif %}</th> - <td> - {{ errors }} - {{ field }} - {% if field.help_text %} - <br> - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if loop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </td> - </tr> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/ul.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/ul.html deleted file mode 100644 index cc4d893..0000000 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/ul.html +++ /dev/null @@ -1,24 +0,0 @@ -{% if errors %} - <li> - {{ errors }} - {% if not fields %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </li> -{% endif %} -{% for field, errors in fields %} - <li{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}> - {{ errors }} - {% if field.label %}{{ field.label_tag() }}{% endif %} - {{ field }} - {% if field.help_text %} - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if loop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </li> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/jinja2/django/forms/widgets/multiple_input.html b/venv/Lib/site-packages/django/forms/jinja2/django/forms/widgets/multiple_input.html index aee0bd6..21cd9b6 100644 --- a/venv/Lib/site-packages/django/forms/jinja2/django/forms/widgets/multiple_input.html +++ b/venv/Lib/site-packages/django/forms/jinja2/django/forms/widgets/multiple_input.html @@ -1,5 +1,5 @@ -{% set id = widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} - <div><label>{{ group }}</label>{% endif %}{% for widget in options %}<div> - {% include widget.template_name %}</div>{% endfor %}{% if group %} - </div>{% endif %}{% endfor %} -</div> +{% set id = widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} + <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for widget in options %} + <li>{% include widget.template_name %}</li>{% endfor %}{% if group %} + </ul></li>{% endif %}{% endfor %} +</ul> diff --git a/venv/Lib/site-packages/django/forms/models.py b/venv/Lib/site-packages/django/forms/models.py index 688b910..0591cdf 100644 --- a/venv/Lib/site-packages/django/forms/models.py +++ b/venv/Lib/site-packages/django/forms/models.py @@ -2,44 +2,31 @@ Helper functions for creating Form classes from Django models and database field objects. """ +import warnings from itertools import chain from django.core.exceptions import ( - NON_FIELD_ERRORS, - FieldError, - ImproperlyConfigured, - ValidationError, + NON_FIELD_ERRORS, FieldError, ImproperlyConfigured, ValidationError, ) from django.forms.fields import ChoiceField, Field from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass from django.forms.formsets import BaseFormSet, formset_factory from django.forms.utils import ErrorList from django.forms.widgets import ( - HiddenInput, - MultipleHiddenInput, - RadioSelect, - SelectMultiple, + HiddenInput, MultipleHiddenInput, RadioSelect, SelectMultiple, ) +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.text import capfirst, get_text_list -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ __all__ = ( - "ModelForm", - "BaseModelForm", - "model_to_dict", - "fields_for_model", - "ModelChoiceField", - "ModelMultipleChoiceField", - "ALL_FIELDS", - "BaseModelFormSet", - "modelformset_factory", - "BaseInlineFormSet", - "inlineformset_factory", - "modelform_factory", + 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', + 'ModelChoiceField', 'ModelMultipleChoiceField', 'ALL_FIELDS', + 'BaseModelFormSet', 'modelformset_factory', 'BaseInlineFormSet', + 'inlineformset_factory', 'modelform_factory', ) -ALL_FIELDS = "__all__" +ALL_FIELDS = '__all__' def construct_instance(form, instance, fields=None, exclude=None): @@ -48,17 +35,13 @@ def construct_instance(form, instance, fields=None, exclude=None): ``cleaned_data``, but do not save the returned instance to the database. """ from django.db import models - opts = instance._meta cleaned_data = form.cleaned_data file_field_list = [] for f in opts.fields: - if ( - not f.editable - or isinstance(f, models.AutoField) - or f.name not in cleaned_data - ): + if not f.editable or isinstance(f, models.AutoField) \ + or f.name not in cleaned_data: continue if fields is not None and f.name not in fields: continue @@ -67,11 +50,9 @@ def construct_instance(form, instance, fields=None, exclude=None): # Leave defaults for fields that aren't in POST data, except for # checkbox inputs because they don't appear in POST data if not checked. if ( - f.has_default() - and form[f.name].field.widget.value_omitted_from_data( - form.data, form.files, form.add_prefix(f.name) - ) - and cleaned_data.get(f.name) in form[f.name].field.empty_values + f.has_default() and + form[f.name].field.widget.value_omitted_from_data(form.data, form.files, form.add_prefix(f.name)) and + cleaned_data.get(f.name) in form[f.name].field.empty_values ): continue # Defer saving file-type fields until after the other fields, so a @@ -89,7 +70,6 @@ def construct_instance(form, instance, fields=None, exclude=None): # ModelForms ################################################################# - def model_to_dict(instance, fields=None, exclude=None): """ Return a dict containing the data in ``instance`` suitable for passing as @@ -105,7 +85,7 @@ def model_to_dict(instance, fields=None, exclude=None): opts = instance._meta data = {} for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many): - if not getattr(f, "editable", False): + if not getattr(f, 'editable', False): continue if fields is not None and f.name not in fields: continue @@ -118,34 +98,23 @@ def model_to_dict(instance, fields=None, exclude=None): def apply_limit_choices_to_to_formfield(formfield): """Apply limit_choices_to to the formfield's queryset if needed.""" from django.db.models import Exists, OuterRef, Q - - if hasattr(formfield, "queryset") and hasattr(formfield, "get_limit_choices_to"): + if hasattr(formfield, 'queryset') and hasattr(formfield, 'get_limit_choices_to'): limit_choices_to = formfield.get_limit_choices_to() if limit_choices_to: complex_filter = limit_choices_to if not isinstance(complex_filter, Q): complex_filter = Q(**limit_choices_to) - complex_filter &= Q(pk=OuterRef("pk")) + complex_filter &= Q(pk=OuterRef('pk')) # Use Exists() to avoid potential duplicates. formfield.queryset = formfield.queryset.filter( Exists(formfield.queryset.model._base_manager.filter(complex_filter)), ) -def fields_for_model( - model, - fields=None, - exclude=None, - widgets=None, - formfield_callback=None, - localized_fields=None, - labels=None, - help_texts=None, - error_messages=None, - field_classes=None, - *, - apply_limit_choices_to=True, -): +def fields_for_model(model, fields=None, exclude=None, widgets=None, + formfield_callback=None, localized_fields=None, + labels=None, help_texts=None, error_messages=None, + field_classes=None, *, apply_limit_choices_to=True): """ Return a dictionary containing form fields for the given model. @@ -181,22 +150,14 @@ def fields_for_model( opts = model._meta # Avoid circular import from django.db.models import Field as ModelField - - sortable_private_fields = [ - f for f in opts.private_fields if isinstance(f, ModelField) - ] - for f in sorted( - chain(opts.concrete_fields, sortable_private_fields, opts.many_to_many) - ): - if not getattr(f, "editable", False): - if ( - fields is not None - and f.name in fields - and (exclude is None or f.name not in exclude) - ): + sortable_private_fields = [f for f in opts.private_fields if isinstance(f, ModelField)] + for f in sorted(chain(opts.concrete_fields, sortable_private_fields, opts.many_to_many)): + if not getattr(f, 'editable', False): + if (fields is not None and f.name in fields and + (exclude is None or f.name not in exclude)): raise FieldError( - "'%s' cannot be specified for %s model form as it is a " - "non-editable field" % (f.name, model.__name__) + "'%s' cannot be specified for %s model form as it is a non-editable field" % ( + f.name, model.__name__) ) continue if fields is not None and f.name not in fields: @@ -206,24 +167,22 @@ def fields_for_model( kwargs = {} if widgets and f.name in widgets: - kwargs["widget"] = widgets[f.name] - if localized_fields == ALL_FIELDS or ( - localized_fields and f.name in localized_fields - ): - kwargs["localize"] = True + kwargs['widget'] = widgets[f.name] + if localized_fields == ALL_FIELDS or (localized_fields and f.name in localized_fields): + kwargs['localize'] = True if labels and f.name in labels: - kwargs["label"] = labels[f.name] + kwargs['label'] = labels[f.name] if help_texts and f.name in help_texts: - kwargs["help_text"] = help_texts[f.name] + kwargs['help_text'] = help_texts[f.name] if error_messages and f.name in error_messages: - kwargs["error_messages"] = error_messages[f.name] + kwargs['error_messages'] = error_messages[f.name] if field_classes and f.name in field_classes: - kwargs["form_class"] = field_classes[f.name] + kwargs['form_class'] = field_classes[f.name] if formfield_callback is None: formfield = f.formfield(**kwargs) elif not callable(formfield_callback): - raise TypeError("formfield_callback must be a function or callable") + raise TypeError('formfield_callback must be a function or callable') else: formfield = formfield_callback(f, **kwargs) @@ -235,8 +194,7 @@ def fields_for_model( ignored.append(f.name) if fields: field_dict = { - f: field_dict.get(f) - for f in fields + f: field_dict.get(f) for f in fields if (not exclude or f not in exclude) and f not in ignored } return field_dict @@ -244,49 +202,46 @@ def fields_for_model( class ModelFormOptions: def __init__(self, options=None): - self.model = getattr(options, "model", None) - self.fields = getattr(options, "fields", None) - self.exclude = getattr(options, "exclude", None) - self.widgets = getattr(options, "widgets", None) - self.localized_fields = getattr(options, "localized_fields", None) - self.labels = getattr(options, "labels", None) - self.help_texts = getattr(options, "help_texts", None) - self.error_messages = getattr(options, "error_messages", None) - self.field_classes = getattr(options, "field_classes", None) + self.model = getattr(options, 'model', None) + self.fields = getattr(options, 'fields', None) + self.exclude = getattr(options, 'exclude', None) + self.widgets = getattr(options, 'widgets', None) + self.localized_fields = getattr(options, 'localized_fields', None) + self.labels = getattr(options, 'labels', None) + self.help_texts = getattr(options, 'help_texts', None) + self.error_messages = getattr(options, 'error_messages', None) + self.field_classes = getattr(options, 'field_classes', None) class ModelFormMetaclass(DeclarativeFieldsMetaclass): def __new__(mcs, name, bases, attrs): base_formfield_callback = None for b in bases: - if hasattr(b, "Meta") and hasattr(b.Meta, "formfield_callback"): + if hasattr(b, 'Meta') and hasattr(b.Meta, 'formfield_callback'): base_formfield_callback = b.Meta.formfield_callback break - formfield_callback = attrs.pop("formfield_callback", base_formfield_callback) + formfield_callback = attrs.pop('formfield_callback', base_formfield_callback) new_class = super().__new__(mcs, name, bases, attrs) if bases == (BaseModelForm,): return new_class - opts = new_class._meta = ModelFormOptions(getattr(new_class, "Meta", None)) + opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) # We check if a string was passed to `fields` or `exclude`, # which is likely to be a mistake where the user typed ('foo') instead # of ('foo',) - for opt in ["fields", "exclude", "localized_fields"]: + for opt in ['fields', 'exclude', 'localized_fields']: value = getattr(opts, opt) if isinstance(value, str) and value != ALL_FIELDS: - msg = ( - "%(model)s.Meta.%(opt)s cannot be a string. " - "Did you mean to type: ('%(value)s',)?" - % { - "model": new_class.__name__, - "opt": opt, - "value": value, - } - ) + msg = ("%(model)s.Meta.%(opt)s cannot be a string. " + "Did you mean to type: ('%(value)s',)?" % { + 'model': new_class.__name__, + 'opt': opt, + 'value': value, + }) raise TypeError(msg) if opts.model: @@ -304,16 +259,9 @@ class ModelFormMetaclass(DeclarativeFieldsMetaclass): opts.fields = None fields = fields_for_model( - opts.model, - opts.fields, - opts.exclude, - opts.widgets, - formfield_callback, - opts.localized_fields, - opts.labels, - opts.help_texts, - opts.error_messages, - opts.field_classes, + opts.model, opts.fields, opts.exclude, opts.widgets, + formfield_callback, opts.localized_fields, opts.labels, + opts.help_texts, opts.error_messages, opts.field_classes, # limit_choices_to will be applied during ModelForm.__init__(). apply_limit_choices_to=False, ) @@ -322,8 +270,9 @@ class ModelFormMetaclass(DeclarativeFieldsMetaclass): none_model_fields = {k for k, v in fields.items() if not v} missing_fields = none_model_fields.difference(new_class.declared_fields) if missing_fields: - message = "Unknown field(s) (%s) specified for %s" - message = message % (", ".join(missing_fields), opts.model.__name__) + message = 'Unknown field(s) (%s) specified for %s' + message = message % (', '.join(missing_fields), + opts.model.__name__) raise FieldError(message) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). @@ -337,23 +286,13 @@ class ModelFormMetaclass(DeclarativeFieldsMetaclass): class BaseModelForm(BaseForm): - def __init__( - self, - data=None, - files=None, - auto_id="id_%s", - prefix=None, - initial=None, - error_class=ErrorList, - label_suffix=None, - empty_permitted=False, - instance=None, - use_required_attribute=None, - renderer=None, - ): + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, label_suffix=None, + empty_permitted=False, instance=None, use_required_attribute=None, + renderer=None): opts = self._meta if opts.model is None: - raise ValueError("ModelForm has no model class specified.") + raise ValueError('ModelForm has no model class specified.') if instance is None: # if we didn't get an instance, instantiate a new one self.instance = opts.model() @@ -369,15 +308,8 @@ class BaseModelForm(BaseForm): # super will stop validate_unique from being called. self._validate_unique = False super().__init__( - data, - files, - auto_id, - prefix, - object_data, - error_class, - label_suffix, - empty_permitted, - use_required_attribute=use_required_attribute, + data, files, auto_id, prefix, object_data, error_class, + label_suffix, empty_permitted, use_required_attribute=use_required_attribute, renderer=renderer, ) for formfield in self.fields.values(): @@ -420,11 +352,7 @@ class BaseModelForm(BaseForm): else: form_field = self.fields[field] field_value = self.cleaned_data.get(field) - if ( - not f.blank - and not form_field.required - and field_value in form_field.empty_values - ): + if not f.blank and not form_field.required and field_value in form_field.empty_values: exclude.append(f.name) return exclude @@ -439,17 +367,14 @@ class BaseModelForm(BaseForm): # Allow the model generated by construct_instance() to raise # ValidationError and have them handled in the same way as others. - if hasattr(errors, "error_dict"): + if hasattr(errors, 'error_dict'): error_dict = errors.error_dict else: error_dict = {NON_FIELD_ERRORS: errors} for field, messages in error_dict.items(): - if ( - field == NON_FIELD_ERRORS - and opts.error_messages - and NON_FIELD_ERRORS in opts.error_messages - ): + if (field == NON_FIELD_ERRORS and opts.error_messages and + NON_FIELD_ERRORS in opts.error_messages): error_messages = opts.error_messages[NON_FIELD_ERRORS] elif field in self.fields: error_messages = self.fields[field].error_messages @@ -457,10 +382,8 @@ class BaseModelForm(BaseForm): continue for message in messages: - if ( - isinstance(message, ValidationError) - and message.code in error_messages - ): + if (isinstance(message, ValidationError) and + message.code in error_messages): message.message = error_messages[message.code] self.add_error(None, errors) @@ -482,9 +405,7 @@ class BaseModelForm(BaseForm): exclude.append(name) try: - self.instance = construct_instance( - self, self.instance, opts.fields, opts.exclude - ) + self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) except ValidationError as e: self._update_errors(e) @@ -520,7 +441,7 @@ class BaseModelForm(BaseForm): # private_fields here. (GenericRelation was previously a fake # m2m field). for f in chain(opts.many_to_many, opts.private_fields): - if not hasattr(f, "save_form_data"): + if not hasattr(f, 'save_form_data'): continue if fields and f.name not in fields: continue @@ -537,10 +458,9 @@ class BaseModelForm(BaseForm): """ if self.errors: raise ValueError( - "The %s could not be %s because the data didn't validate." - % ( + "The %s could not be %s because the data didn't validate." % ( self.instance._meta.object_name, - "created" if self.instance._state.adding else "changed", + 'created' if self.instance._state.adding else 'changed', ) ) if commit: @@ -560,19 +480,10 @@ class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass): pass -def modelform_factory( - model, - form=ModelForm, - fields=None, - exclude=None, - formfield_callback=None, - widgets=None, - localized_fields=None, - labels=None, - help_texts=None, - error_messages=None, - field_classes=None, -): +def modelform_factory(model, form=ModelForm, fields=None, exclude=None, + formfield_callback=None, widgets=None, localized_fields=None, + labels=None, help_texts=None, error_messages=None, + field_classes=None): """ Return a ModelForm containing form fields for the given model. You can optionally pass a `form` argument to use as a starting point for @@ -608,37 +519,41 @@ def modelform_factory( # inner class. # Build up a list of attributes that the Meta object will have. - attrs = {"model": model} + attrs = {'model': model} if fields is not None: - attrs["fields"] = fields + attrs['fields'] = fields if exclude is not None: - attrs["exclude"] = exclude + attrs['exclude'] = exclude if widgets is not None: - attrs["widgets"] = widgets + attrs['widgets'] = widgets if localized_fields is not None: - attrs["localized_fields"] = localized_fields + attrs['localized_fields'] = localized_fields if labels is not None: - attrs["labels"] = labels + attrs['labels'] = labels if help_texts is not None: - attrs["help_texts"] = help_texts + attrs['help_texts'] = help_texts if error_messages is not None: - attrs["error_messages"] = error_messages + attrs['error_messages'] = error_messages if field_classes is not None: - attrs["field_classes"] = field_classes + attrs['field_classes'] = field_classes # If parent form class already has an inner Meta, the Meta we're # creating needs to inherit from the parent's inner meta. - bases = (form.Meta,) if hasattr(form, "Meta") else () - Meta = type("Meta", bases, attrs) + bases = (form.Meta,) if hasattr(form, 'Meta') else () + Meta = type('Meta', bases, attrs) if formfield_callback: Meta.formfield_callback = staticmethod(formfield_callback) # Give this new form class a reasonable name. - class_name = model.__name__ + "Form" + class_name = model.__name__ + 'Form' # Class attributes for the new form class. - form_class_attrs = {"Meta": Meta, "formfield_callback": formfield_callback} + form_class_attrs = { + 'Meta': Meta, + 'formfield_callback': formfield_callback + } - if getattr(Meta, "fields", None) is None and getattr(Meta, "exclude", None) is None: + if (getattr(Meta, 'fields', None) is None and + getattr(Meta, 'exclude', None) is None): raise ImproperlyConfigured( "Calling modelform_factory without defining 'fields' or " "'exclude' explicitly is prohibited." @@ -650,39 +565,20 @@ def modelform_factory( # ModelFormSets ############################################################## - class BaseModelFormSet(BaseFormSet): """ A ``FormSet`` for editing a queryset and/or adding new objects to it. """ - model = None # Set of fields that must be unique among forms of this set. unique_fields = set() - def __init__( - self, - data=None, - files=None, - auto_id="id_%s", - prefix=None, - queryset=None, - *, - initial=None, - **kwargs, - ): + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + queryset=None, *, initial=None, **kwargs): self.queryset = queryset self.initial_extra = initial - super().__init__( - **{ - "data": data, - "files": files, - "auto_id": auto_id, - "prefix": prefix, - **kwargs, - } - ) + super().__init__(**{'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix, **kwargs}) def initial_form_count(self): """Return the number of forms that are required in this FormSet.""" @@ -691,7 +587,7 @@ class BaseModelFormSet(BaseFormSet): return super().initial_form_count() def _existing_object(self, pk): - if not hasattr(self, "_object_dict"): + if not hasattr(self, '_object_dict'): self._object_dict = {o.pk: o for o in self.get_queryset()} return self._object_dict.get(pk) @@ -708,7 +604,7 @@ class BaseModelFormSet(BaseFormSet): pk_required = i < self.initial_form_count() if pk_required: if self.is_bound: - pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name) + pk_key = '%s-%s' % (self.add_prefix(i), self.model._meta.pk.name) try: pk = self.data[pk_key] except KeyError: @@ -724,13 +620,13 @@ class BaseModelFormSet(BaseFormSet): # user may have tampered with POST data. pass else: - kwargs["instance"] = self._existing_object(pk) + kwargs['instance'] = self._existing_object(pk) else: - kwargs["instance"] = self.get_queryset()[i] + kwargs['instance'] = self.get_queryset()[i] elif self.initial_extra: # Set initial values for extra forms try: - kwargs["initial"] = self.initial_extra[i - self.initial_form_count()] + kwargs['initial'] = self.initial_extra[i - self.initial_form_count()] except IndexError: pass form = super()._construct_form(i, **kwargs) @@ -739,7 +635,7 @@ class BaseModelFormSet(BaseFormSet): return form def get_queryset(self): - if not hasattr(self, "_queryset"): + if not hasattr(self, '_queryset'): if self.queryset is not None: qs = self.queryset else: @@ -781,7 +677,6 @@ class BaseModelFormSet(BaseFormSet): def save_m2m(): for form in self.saved_forms: form.save_m2m() - self.save_m2m = save_m2m return self.save_existing_objects(commit) + self.save_new_objects(commit) @@ -795,16 +690,10 @@ class BaseModelFormSet(BaseFormSet): all_unique_checks = set() all_date_checks = set() forms_to_delete = self.deleted_forms - valid_forms = [ - form - for form in self.forms - if form.is_valid() and form not in forms_to_delete - ] + valid_forms = [form for form in self.forms if form.is_valid() and form not in forms_to_delete] for form in valid_forms: exclude = form._get_validation_exclusions() - unique_checks, date_checks = form.instance._get_unique_checks( - exclude=exclude - ) + unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude) all_unique_checks.update(unique_checks) all_date_checks.update(date_checks) @@ -813,19 +702,17 @@ class BaseModelFormSet(BaseFormSet): for uclass, unique_check in all_unique_checks: seen_data = set() for form in valid_forms: - # Get the data for the set of fields that must be unique among - # the forms. + # Get the data for the set of fields that must be unique among the forms. row_data = ( field if field in self.unique_fields else form.cleaned_data[field] - for field in unique_check - if field in form.cleaned_data + for field in unique_check if field in form.cleaned_data ) # Reduce Model instances to their primary key values row_data = tuple( - d._get_pk_val() if hasattr(d, "_get_pk_val") + d._get_pk_val() if hasattr(d, '_get_pk_val') # Prevent "unhashable type: list" errors later on. - else tuple(d) if isinstance(d, list) else d - for d in row_data + else tuple(d) if isinstance(d, list) + else d for d in row_data ) if row_data and None not in row_data: # if we've already seen it then we have a uniqueness failure @@ -833,12 +720,8 @@ class BaseModelFormSet(BaseFormSet): # poke error messages into the right places and mark # the form as invalid errors.append(self.get_unique_error_message(unique_check)) - form._errors[NON_FIELD_ERRORS] = self.error_class( - [self.get_form_error()], - renderer=self.renderer, - ) - # Remove the data from the cleaned_data dict since it - # was invalid. + form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()]) + # remove the data from the cleaned_data dict since it was invalid for field in unique_check: if field in form.cleaned_data: del form.cleaned_data[field] @@ -850,13 +733,10 @@ class BaseModelFormSet(BaseFormSet): uclass, lookup, field, unique_for = date_check for form in valid_forms: # see if we have data for both fields - if ( - form.cleaned_data - and form.cleaned_data[field] is not None - and form.cleaned_data[unique_for] is not None - ): + if (form.cleaned_data and form.cleaned_data[field] is not None and + form.cleaned_data[unique_for] is not None): # if it's a date lookup we need to get the data for all the fields - if lookup == "date": + if lookup == 'date': date = form.cleaned_data[unique_for] date_data = (date.year, date.month, date.day) # otherwise it's just the attribute on the date/datetime @@ -869,12 +749,8 @@ class BaseModelFormSet(BaseFormSet): # poke error messages into the right places and mark # the form as invalid errors.append(self.get_date_error_message(date_check)) - form._errors[NON_FIELD_ERRORS] = self.error_class( - [self.get_form_error()], - renderer=self.renderer, - ) - # Remove the data from the cleaned_data dict since it - # was invalid. + form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()]) + # remove the data from the cleaned_data dict since it was invalid del form.cleaned_data[field] # mark the data as seen seen_data.add(data) @@ -888,9 +764,7 @@ class BaseModelFormSet(BaseFormSet): "field": unique_check[0], } else: - return gettext( - "Please correct the duplicate data for %(field)s, which must be unique." - ) % { + return gettext("Please correct the duplicate data for %(field)s, which must be unique.") % { "field": get_text_list(unique_check, _("and")), } @@ -899,9 +773,9 @@ class BaseModelFormSet(BaseFormSet): "Please correct the duplicate data for %(field_name)s " "which must be unique for the %(lookup)s in %(date_field)s." ) % { - "field_name": date_check[2], - "date_field": date_check[3], - "lookup": str(date_check[1]), + 'field_name': date_check[2], + 'date_field': date_check[3], + 'lookup': str(date_check[1]), } def get_form_error(self): @@ -950,7 +824,6 @@ class BaseModelFormSet(BaseFormSet): def add_fields(self, form, index): """Add a hidden field for the object's primary key.""" from django.db.models import AutoField, ForeignKey, OneToOneField - self._pk_field = pk = self.model._meta.pk # If a pk isn't editable, then it won't be on the form, so we need to # add it here so we can tell which object is which when we get the @@ -960,15 +833,11 @@ class BaseModelFormSet(BaseFormSet): def pk_is_not_editable(pk): return ( - (not pk.editable) - or (pk.auto_created or isinstance(pk, AutoField)) - or ( - pk.remote_field - and pk.remote_field.parent_link - and pk_is_not_editable(pk.remote_field.model._meta.pk) + (not pk.editable) or (pk.auto_created or isinstance(pk, AutoField)) or ( + pk.remote_field and pk.remote_field.parent_link and + pk_is_not_editable(pk.remote_field.model._meta.pk) ) ) - if pk_is_not_editable(pk) or pk.name not in form.fields: if form.is_bound: # If we're adding the related instance, ignore its primary key @@ -992,94 +861,45 @@ class BaseModelFormSet(BaseFormSet): widget = form._meta.widgets.get(self._pk_field.name, HiddenInput) else: widget = HiddenInput - form.fields[self._pk_field.name] = ModelChoiceField( - qs, initial=pk_value, required=False, widget=widget - ) + form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=widget) super().add_fields(form, index) -def modelformset_factory( - model, - form=ModelForm, - formfield_callback=None, - formset=BaseModelFormSet, - extra=1, - can_delete=False, - can_order=False, - max_num=None, - fields=None, - exclude=None, - widgets=None, - validate_max=False, - localized_fields=None, - labels=None, - help_texts=None, - error_messages=None, - min_num=None, - validate_min=False, - field_classes=None, - absolute_max=None, - can_delete_extra=True, - renderer=None, -): +def modelformset_factory(model, form=ModelForm, formfield_callback=None, + formset=BaseModelFormSet, extra=1, can_delete=False, + can_order=False, max_num=None, fields=None, exclude=None, + widgets=None, validate_max=False, localized_fields=None, + labels=None, help_texts=None, error_messages=None, + min_num=None, validate_min=False, field_classes=None, + absolute_max=None, can_delete_extra=True): """Return a FormSet class for the given Django model class.""" - meta = getattr(form, "Meta", None) - if ( - getattr(meta, "fields", fields) is None - and getattr(meta, "exclude", exclude) is None - ): + meta = getattr(form, 'Meta', None) + if (getattr(meta, 'fields', fields) is None and + getattr(meta, 'exclude', exclude) is None): raise ImproperlyConfigured( "Calling modelformset_factory without defining 'fields' or " "'exclude' explicitly is prohibited." ) - form = modelform_factory( - model, - form=form, - fields=fields, - exclude=exclude, - formfield_callback=formfield_callback, - widgets=widgets, - localized_fields=localized_fields, - labels=labels, - help_texts=help_texts, - error_messages=error_messages, - field_classes=field_classes, - ) - FormSet = formset_factory( - form, - formset, - extra=extra, - min_num=min_num, - max_num=max_num, - can_order=can_order, - can_delete=can_delete, - validate_min=validate_min, - validate_max=validate_max, - absolute_max=absolute_max, - can_delete_extra=can_delete_extra, - renderer=renderer, - ) + form = modelform_factory(model, form=form, fields=fields, exclude=exclude, + formfield_callback=formfield_callback, + widgets=widgets, localized_fields=localized_fields, + labels=labels, help_texts=help_texts, + error_messages=error_messages, field_classes=field_classes) + FormSet = formset_factory(form, formset, extra=extra, min_num=min_num, max_num=max_num, + can_order=can_order, can_delete=can_delete, + validate_min=validate_min, validate_max=validate_max, + absolute_max=absolute_max, can_delete_extra=can_delete_extra) FormSet.model = model return FormSet # InlineFormSets ############################################################# - class BaseInlineFormSet(BaseModelFormSet): """A formset for child objects related to a parent.""" - - def __init__( - self, - data=None, - files=None, - instance=None, - save_as_new=False, - prefix=None, - queryset=None, - **kwargs, - ): + def __init__(self, data=None, files=None, instance=None, + save_as_new=False, prefix=None, queryset=None, **kwargs): if instance is None: self.instance = self.fk.remote_field.model() else: @@ -1109,7 +929,7 @@ class BaseInlineFormSet(BaseModelFormSet): def _construct_form(self, i, **kwargs): form = super()._construct_form(i, **kwargs) if self.save_as_new: - mutable = getattr(form.data, "_mutable", None) + mutable = getattr(form.data, '_mutable', None) # Allow modifying an immutable QueryDict. if mutable is not None: form.data._mutable = True @@ -1125,13 +945,13 @@ class BaseInlineFormSet(BaseModelFormSet): fk_value = self.instance.pk if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name: fk_value = getattr(self.instance, self.fk.remote_field.field_name) - fk_value = getattr(fk_value, "pk", fk_value) + fk_value = getattr(fk_value, 'pk', fk_value) setattr(form.instance, self.fk.get_attname(), fk_value) return form @classmethod def get_default_prefix(cls): - return cls.fk.remote_field.get_accessor_name(model=cls.model).replace("+", "") + return cls.fk.remote_field.get_accessor_name(model=cls.model).replace('+', '') def save_new(self, form, commit=True): # Ensure the latest copy of the related instance is present on each @@ -1144,28 +964,26 @@ class BaseInlineFormSet(BaseModelFormSet): super().add_fields(form, index) if self._pk_field == self.fk: name = self._pk_field.name - kwargs = {"pk_field": True} + kwargs = {'pk_field': True} else: # The foreign key field might not be on the form, so we poke at the # Model field to get the label, since we need that for error messages. name = self.fk.name kwargs = { - "label": getattr( - form.fields.get(name), "label", capfirst(self.fk.verbose_name) - ) + 'label': getattr(form.fields.get(name), 'label', capfirst(self.fk.verbose_name)) } # The InlineForeignKeyField assumes that the foreign key relation is # based on the parent model's pk. If this isn't the case, set to_field # to correctly resolve the initial form value. if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name: - kwargs["to_field"] = self.fk.remote_field.field_name + kwargs['to_field'] = self.fk.remote_field.field_name # If we're adding a new object, ignore a parent's auto-generated key # as it will be regenerated on the save request. if self.instance._state.adding: - if kwargs.get("to_field") is not None: - to_field = self.instance._meta.get_field(kwargs["to_field"]) + if kwargs.get('to_field') is not None: + to_field = self.instance._meta.get_field(kwargs['to_field']) else: to_field = self.instance._meta.pk if to_field.has_default(): @@ -1188,30 +1006,16 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): """ # avoid circular import from django.db.models import ForeignKey - opts = model._meta if fk_name: fks_to_parent = [f for f in opts.fields if f.name == fk_name] if len(fks_to_parent) == 1: fk = fks_to_parent[0] - parent_list = parent_model._meta.get_parent_list() - if ( - not isinstance(fk, ForeignKey) - or ( - # ForeignKey to proxy models. - fk.remote_field.model._meta.proxy - and fk.remote_field.model._meta.proxy_for_model not in parent_list - ) - or ( - # ForeignKey to concrete models. - not fk.remote_field.model._meta.proxy - and fk.remote_field.model != parent_model - and fk.remote_field.model not in parent_list - ) - ): + if not isinstance(fk, ForeignKey) or \ + (fk.remote_field.model != parent_model and + fk.remote_field.model not in parent_model._meta.get_parent_list()): raise ValueError( - "fk_name '%s' is not a ForeignKey to '%s'." - % (fk_name, parent_model._meta.label) + "fk_name '%s' is not a ForeignKey to '%s'." % (fk_name, parent_model._meta.label) ) elif not fks_to_parent: raise ValueError( @@ -1219,18 +1023,11 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): ) else: # Try to discover what the ForeignKey from model to parent_model is - parent_list = parent_model._meta.get_parent_list() fks_to_parent = [ - f - for f in opts.fields - if isinstance(f, ForeignKey) - and ( - f.remote_field.model == parent_model - or f.remote_field.model in parent_list - or ( - f.remote_field.model._meta.proxy - and f.remote_field.model._meta.proxy_for_model in parent_list - ) + f for f in opts.fields + if isinstance(f, ForeignKey) and ( + f.remote_field.model == parent_model or + f.remote_field.model in parent_model._meta.get_parent_list() ) ] if len(fks_to_parent) == 1: @@ -1239,8 +1036,7 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): if can_fail: return raise ValueError( - "'%s' has no ForeignKey to '%s'." - % ( + "'%s' has no ForeignKey to '%s'." % ( model._meta.label, parent_model._meta.label, ) @@ -1248,8 +1044,7 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): else: raise ValueError( "'%s' has more than one ForeignKey to '%s'. You must specify " - "a 'fk_name' attribute." - % ( + "a 'fk_name' attribute." % ( model._meta.label, parent_model._meta.label, ) @@ -1257,32 +1052,14 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): return fk -def inlineformset_factory( - parent_model, - model, - form=ModelForm, - formset=BaseInlineFormSet, - fk_name=None, - fields=None, - exclude=None, - extra=3, - can_order=False, - can_delete=True, - max_num=None, - formfield_callback=None, - widgets=None, - validate_max=False, - localized_fields=None, - labels=None, - help_texts=None, - error_messages=None, - min_num=None, - validate_min=False, - field_classes=None, - absolute_max=None, - can_delete_extra=True, - renderer=None, -): +def inlineformset_factory(parent_model, model, form=ModelForm, + formset=BaseInlineFormSet, fk_name=None, + fields=None, exclude=None, extra=3, can_order=False, + can_delete=True, max_num=None, formfield_callback=None, + widgets=None, validate_max=False, localized_fields=None, + labels=None, help_texts=None, error_messages=None, + min_num=None, validate_min=False, field_classes=None, + absolute_max=None, can_delete_extra=True): """ Return an ``InlineFormSet`` for the given kwargs. @@ -1294,27 +1071,26 @@ def inlineformset_factory( if fk.unique: max_num = 1 kwargs = { - "form": form, - "formfield_callback": formfield_callback, - "formset": formset, - "extra": extra, - "can_delete": can_delete, - "can_order": can_order, - "fields": fields, - "exclude": exclude, - "min_num": min_num, - "max_num": max_num, - "widgets": widgets, - "validate_min": validate_min, - "validate_max": validate_max, - "localized_fields": localized_fields, - "labels": labels, - "help_texts": help_texts, - "error_messages": error_messages, - "field_classes": field_classes, - "absolute_max": absolute_max, - "can_delete_extra": can_delete_extra, - "renderer": renderer, + 'form': form, + 'formfield_callback': formfield_callback, + 'formset': formset, + 'extra': extra, + 'can_delete': can_delete, + 'can_order': can_order, + 'fields': fields, + 'exclude': exclude, + 'min_num': min_num, + 'max_num': max_num, + 'widgets': widgets, + 'validate_min': validate_min, + 'validate_max': validate_max, + 'localized_fields': localized_fields, + 'labels': labels, + 'help_texts': help_texts, + 'error_messages': error_messages, + 'field_classes': field_classes, + 'absolute_max': absolute_max, + 'can_delete_extra': can_delete_extra, } FormSet = modelformset_factory(model, **kwargs) FormSet.fk = fk @@ -1323,16 +1099,14 @@ def inlineformset_factory( # Fields ##################################################################### - class InlineForeignKeyField(Field): """ A basic integer field that deals with validating the given value to a given parent instance in an inline. """ - widget = HiddenInput default_error_messages = { - "invalid_choice": _("The inline value did not match the parent instance."), + 'invalid_choice': _('The inline value did not match the parent instance.'), } def __init__(self, parent_instance, *args, pk_field=False, to_field=None, **kwargs): @@ -1359,9 +1133,7 @@ class InlineForeignKeyField(Field): else: orig = self.parent_instance.pk if str(value) != str(orig): - raise ValidationError( - self.error_messages["invalid_choice"], code="invalid_choice" - ) + raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') return self.parent_instance def has_changed(self, initial, data): @@ -1376,9 +1148,6 @@ class ModelChoiceIteratorValue: def __str__(self): return str(self.value) - def __hash__(self): - return hash(self.value) - def __eq__(self, other): if isinstance(other, ModelChoiceIteratorValue): other = other.value @@ -1418,50 +1187,33 @@ class ModelChoiceIterator: class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" - # This class is a subclass of ChoiceField for purity, but it doesn't # actually use any of ChoiceField's implementation. default_error_messages = { - "invalid_choice": _( - "Select a valid choice. That choice is not one of the available choices." - ), + 'invalid_choice': _('Select a valid choice. That choice is not one of' + ' the available choices.'), } iterator = ModelChoiceIterator - def __init__( - self, - queryset, - *, - empty_label="---------", - required=True, - widget=None, - label=None, - initial=None, - help_text="", - to_field_name=None, - limit_choices_to=None, - blank=False, - **kwargs, - ): + def __init__(self, queryset, *, empty_label="---------", + required=True, widget=None, label=None, initial=None, + help_text='', to_field_name=None, limit_choices_to=None, + blank=False, **kwargs): # Call Field instead of ChoiceField __init__() because we don't need # ChoiceField.__init__(). Field.__init__( - self, - required=required, - widget=widget, - label=label, - initial=initial, - help_text=help_text, - **kwargs, + self, required=required, widget=widget, label=label, + initial=initial, help_text=help_text, **kwargs ) - if (required and initial is not None) or ( - isinstance(self.widget, RadioSelect) and not blank + if ( + (required and initial is not None) or + (isinstance(self.widget, RadioSelect) and not blank) ): self.empty_label = None else: self.empty_label = empty_label self.queryset = queryset - self.limit_choices_to = limit_choices_to # limit the queryset later. + self.limit_choices_to = limit_choices_to # limit the queryset later. self.to_field_name = to_field_name def get_limit_choices_to(self): @@ -1503,7 +1255,7 @@ class ModelChoiceField(ChoiceField): def _get_choices(self): # If self._choices is set, then somebody must have manually set # the property self.choices. In this case, just return self._choices. - if hasattr(self, "_choices"): + if hasattr(self, '_choices'): return self._choices # Otherwise, execute the QuerySet in self.queryset to determine the @@ -1518,7 +1270,7 @@ class ModelChoiceField(ChoiceField): choices = property(_get_choices, ChoiceField._set_choices) def prepare_value(self, value): - if hasattr(value, "_meta"): + if hasattr(value, '_meta'): if self.to_field_name: return value.serializable_value(self.to_field_name) else: @@ -1529,16 +1281,12 @@ class ModelChoiceField(ChoiceField): if value in self.empty_values: return None try: - key = self.to_field_name or "pk" + key = self.to_field_name or 'pk' if isinstance(value, self.queryset.model): value = getattr(value, key) value = self.queryset.get(**{key: value}) except (ValueError, TypeError, self.queryset.model.DoesNotExist): - raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": value}, - ) + raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') return value def validate(self, value): @@ -1547,26 +1295,31 @@ class ModelChoiceField(ChoiceField): def has_changed(self, initial, data): if self.disabled: return False - initial_value = initial if initial is not None else "" - data_value = data if data is not None else "" + initial_value = initial if initial is not None else '' + data_value = data if data is not None else '' return str(self.prepare_value(initial_value)) != str(data_value) class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" - widget = SelectMultiple hidden_widget = MultipleHiddenInput default_error_messages = { - "invalid_list": _("Enter a list of values."), - "invalid_choice": _( - "Select a valid choice. %(value)s is not one of the available choices." - ), - "invalid_pk_value": _("“%(pk)s” is not a valid value."), + 'invalid_list': _('Enter a list of values.'), + 'invalid_choice': _('Select a valid choice. %(value)s is not one of the' + ' available choices.'), + 'invalid_pk_value': _('“%(pk)s” is not a valid value.') } def __init__(self, queryset, **kwargs): super().__init__(queryset, empty_label=None, **kwargs) + if self.error_messages.get('list') is not None: + warnings.warn( + "The 'list' error message key is deprecated in favor of " + "'invalid_list'.", + RemovedInDjango40Warning, stacklevel=2, + ) + self.error_messages['invalid_list'] = self.error_messages['list'] def to_python(self, value): if not value: @@ -1576,13 +1329,13 @@ class ModelMultipleChoiceField(ModelChoiceField): def clean(self, value): value = self.prepare_value(value) if self.required and not value: - raise ValidationError(self.error_messages["required"], code="required") + raise ValidationError(self.error_messages['required'], code='required') elif not self.required and not value: return self.queryset.none() if not isinstance(value, (list, tuple)): raise ValidationError( - self.error_messages["invalid_list"], - code="invalid_list", + self.error_messages['invalid_list'], + code='invalid_list', ) qs = self._check_values(value) # Since this overrides the inherited ModelChoiceField.clean @@ -1596,7 +1349,7 @@ class ModelMultipleChoiceField(ModelChoiceField): corresponding objects. Raise a ValidationError if a given value is invalid (not a valid PK, not in the queryset, etc.) """ - key = self.to_field_name or "pk" + key = self.to_field_name or 'pk' # deduplicate given values to avoid creating many querysets or # requiring the database backend deduplicate efficiently. try: @@ -1604,35 +1357,33 @@ class ModelMultipleChoiceField(ModelChoiceField): except TypeError: # list of lists isn't hashable, for example raise ValidationError( - self.error_messages["invalid_list"], - code="invalid_list", + self.error_messages['invalid_list'], + code='invalid_list', ) for pk in value: try: self.queryset.filter(**{key: pk}) except (ValueError, TypeError): raise ValidationError( - self.error_messages["invalid_pk_value"], - code="invalid_pk_value", - params={"pk": pk}, + self.error_messages['invalid_pk_value'], + code='invalid_pk_value', + params={'pk': pk}, ) - qs = self.queryset.filter(**{"%s__in" % key: value}) + qs = self.queryset.filter(**{'%s__in' % key: value}) pks = {str(getattr(o, key)) for o in qs} for val in value: if str(val) not in pks: raise ValidationError( - self.error_messages["invalid_choice"], - code="invalid_choice", - params={"value": val}, + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': val}, ) return qs def prepare_value(self, value): - if ( - hasattr(value, "__iter__") - and not isinstance(value, str) - and not hasattr(value, "_meta") - ): + if (hasattr(value, '__iter__') and + not isinstance(value, str) and + not hasattr(value, '_meta')): prepare_value = super().prepare_value return [prepare_value(v) for v in value] return super().prepare_value(value) @@ -1652,6 +1403,7 @@ class ModelMultipleChoiceField(ModelChoiceField): def modelform_defines_fields(form_class): - return hasattr(form_class, "_meta") and ( - form_class._meta.fields is not None or form_class._meta.exclude is not None + return hasattr(form_class, '_meta') and ( + form_class._meta.fields is not None or + form_class._meta.exclude is not None ) diff --git a/venv/Lib/site-packages/django/forms/renderers.py b/venv/Lib/site-packages/django/forms/renderers.py index 8286c71..dcf3d92 100644 --- a/venv/Lib/site-packages/django/forms/renderers.py +++ b/venv/Lib/site-packages/django/forms/renderers.py @@ -7,6 +7,8 @@ from django.template.loader import get_template from django.utils.functional import cached_property from django.utils.module_loading import import_string +ROOT = Path(__file__).parent + @functools.lru_cache() def get_default_renderer(): @@ -16,7 +18,7 @@ def get_default_renderer(): class BaseRenderer: def get_template(self, template_name): - raise NotImplementedError("subclasses must implement get_template()") + raise NotImplementedError('subclasses must implement get_template()') def render(self, template_name, context, request=None): template = self.get_template(template_name) @@ -29,14 +31,12 @@ class EngineMixin: @cached_property def engine(self): - return self.backend( - { - "APP_DIRS": True, - "DIRS": [Path(__file__).parent / self.backend.app_dirname], - "NAME": "djangoforms", - "OPTIONS": {}, - } - ) + return self.backend({ + 'APP_DIRS': True, + 'DIRS': [ROOT / self.backend.app_dirname], + 'NAME': 'djangoforms', + 'OPTIONS': {}, + }) class DjangoTemplates(EngineMixin, BaseRenderer): @@ -44,7 +44,6 @@ class DjangoTemplates(EngineMixin, BaseRenderer): Load Django templates from the built-in widget templates in django/forms/templates and from apps' 'templates' directory. """ - backend = DjangoTemplates @@ -53,11 +52,9 @@ class Jinja2(EngineMixin, BaseRenderer): Load Jinja2 templates from the built-in widget templates in django/forms/jinja2 and from apps' 'jinja2' directory. """ - @cached_property def backend(self): from django.template.backends.jinja2 import Jinja2 - return Jinja2 @@ -66,6 +63,5 @@ class TemplatesSetting(BaseRenderer): Load templates using template.loader.get_template() which is configured based on settings.TEMPLATES. """ - def get_template(self, template_name): return get_template(template_name) diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/attrs.html b/venv/Lib/site-packages/django/forms/templates/django/forms/attrs.html deleted file mode 100644 index 50de36b..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/attrs.html +++ /dev/null @@ -1 +0,0 @@ -{% for name, value in attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %} \ No newline at end of file diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/default.html b/venv/Lib/site-packages/django/forms/templates/django/forms/default.html deleted file mode 100644 index d034b60..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/table.html" %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/default.html b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/default.html deleted file mode 100644 index 8a833c6..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/errors/dict/ul.html" %} \ No newline at end of file diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/text.txt b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/text.txt deleted file mode 100644 index dc9fd80..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/text.txt +++ /dev/null @@ -1,3 +0,0 @@ -{% for field, errors in errors %}* {{ field }} -{% for error in errors %} * {{ error }} -{% endfor %}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/ul.html b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/ul.html deleted file mode 100644 index c16fd65..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/dict/ul.html +++ /dev/null @@ -1 +0,0 @@ -{% if errors %}<ul class="{{ error_class }}">{% for field, error in errors %}<li>{{ field }}{{ error }}</li>{% endfor %}</ul>{% endif %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/default.html b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/default.html deleted file mode 100644 index b174f26..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/default.html +++ /dev/null @@ -1 +0,0 @@ -{% include "django/forms/errors/list/ul.html" %} \ No newline at end of file diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/text.txt b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/text.txt deleted file mode 100644 index aa7f870..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/text.txt +++ /dev/null @@ -1,2 +0,0 @@ -{% for error in errors %}* {{ error }} -{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/ul.html b/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/ul.html deleted file mode 100644 index 57b34cc..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/errors/list/ul.html +++ /dev/null @@ -1 +0,0 @@ -{% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} \ No newline at end of file diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/default.html b/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/default.html deleted file mode 100644 index d8284c5..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/default.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/p.html b/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/p.html deleted file mode 100644 index 00c2df6..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/p.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_p }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/table.html b/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/table.html deleted file mode 100644 index 4fa5e42..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/table.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_table }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/ul.html b/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/ul.html deleted file mode 100644 index 272e129..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/formsets/ul.html +++ /dev/null @@ -1 +0,0 @@ -{{ formset.management_form }}{% for form in formset %}{{ form.as_ul }}{% endfor %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/label.html b/venv/Lib/site-packages/django/forms/templates/django/forms/label.html deleted file mode 100644 index eb2a9f7..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/label.html +++ /dev/null @@ -1 +0,0 @@ -{% if use_tag %}<label{% include 'django/forms/attrs.html' %}>{{ label }}</label>{% else %}{{ label }}{% endif %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/p.html b/venv/Lib/site-packages/django/forms/templates/django/forms/p.html deleted file mode 100644 index 1346937..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/p.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ errors }} -{% if errors and not fields %} - <p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p> -{% endif %} -{% for field, errors in fields %} - {{ errors }} - <p{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}> - {% if field.label %}{{ field.label_tag }}{% endif %} - {{ field }} - {% if field.help_text %} - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if forloop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </p> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/table.html b/venv/Lib/site-packages/django/forms/templates/django/forms/table.html deleted file mode 100644 index d4aaafc..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/table.html +++ /dev/null @@ -1,29 +0,0 @@ -{% if errors %} - <tr> - <td colspan="2"> - {{ errors }} - {% if not fields %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </td> - </tr> -{% endif %} -{% for field, errors in fields %} - <tr{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}> - <th>{% if field.label %}{{ field.label_tag }}{% endif %}</th> - <td> - {{ errors }} - {{ field }} - {% if field.help_text %} - <br> - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if forloop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </td> - </tr> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/ul.html b/venv/Lib/site-packages/django/forms/templates/django/forms/ul.html deleted file mode 100644 index ae38af6..0000000 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/ul.html +++ /dev/null @@ -1,24 +0,0 @@ -{% if errors %} - <li> - {{ errors }} - {% if not fields %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </li> -{% endif %} -{% for field, errors in fields %} - <li{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}> - {{ errors }} - {% if field.label %}{{ field.label_tag }}{% endif %} - {{ field }} - {% if field.help_text %} - <span class="helptext">{{ field.help_text|safe }}</span> - {% endif %} - {% if forloop.last %} - {% for field in hidden_fields %}{{ field }}{% endfor %} - {% endif %} - </li> -{% endfor %} -{% if not fields and not errors %} - {% for field in hidden_fields %}{{ field }}{% endfor %} -{% endif %} diff --git a/venv/Lib/site-packages/django/forms/templates/django/forms/widgets/multiple_input.html b/venv/Lib/site-packages/django/forms/templates/django/forms/widgets/multiple_input.html index 2a0fec6..0ba9942 100644 --- a/venv/Lib/site-packages/django/forms/templates/django/forms/widgets/multiple_input.html +++ b/venv/Lib/site-packages/django/forms/templates/django/forms/widgets/multiple_input.html @@ -1,5 +1,5 @@ -{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} - <div><label>{{ group }}</label>{% endif %}{% for option in options %}<div> - {% include option.template_name with widget=option %}</div>{% endfor %}{% if group %} - </div>{% endif %}{% endfor %} -</div>{% endwith %} +{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} + <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %} + <li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %} + </ul></li>{% endif %}{% endfor %} +</ul>{% endwith %} diff --git a/venv/Lib/site-packages/django/forms/utils.py b/venv/Lib/site-packages/django/forms/utils.py index 7d3bb5a..50412f4 100644 --- a/venv/Lib/site-packages/django/forms/utils.py +++ b/venv/Lib/site-packages/django/forms/utils.py @@ -3,18 +3,16 @@ from collections import UserList from django.conf import settings from django.core.exceptions import ValidationError -from django.forms.renderers import get_default_renderer from django.utils import timezone -from django.utils.html import escape, format_html_join -from django.utils.safestring import mark_safe +from django.utils.html import escape, format_html, format_html_join, html_safe from django.utils.translation import gettext_lazy as _ def pretty_name(name): """Convert 'first_name' to 'First name'.""" if not name: - return "" - return name.replace("_", " ").capitalize() + return '' + return name.replace('_', ' ').capitalize() def flatatt(attrs): @@ -37,99 +35,59 @@ def flatatt(attrs): elif value is not None: key_value_attrs.append((attr, value)) - return format_html_join("", ' {}="{}"', sorted(key_value_attrs)) + format_html_join( - "", " {}", sorted(boolean_attrs) + return ( + format_html_join('', ' {}="{}"', sorted(key_value_attrs)) + + format_html_join('', ' {}', sorted(boolean_attrs)) ) -class RenderableMixin: - def get_context(self): - raise NotImplementedError( - "Subclasses of RenderableMixin must provide a get_context() method." - ) - - def render(self, template_name=None, context=None, renderer=None): - return mark_safe( - (renderer or self.renderer).render( - template_name or self.template_name, - context or self.get_context(), - ) - ) - - __str__ = render - __html__ = render - - -class RenderableFormMixin(RenderableMixin): - def as_p(self): - """Render as <p> elements.""" - return self.render(self.template_name_p) - - def as_table(self): - """Render as <tr> elements excluding the surrounding <table> tag.""" - return self.render(self.template_name_table) - - def as_ul(self): - """Render as <li> elements excluding the surrounding <ul> tag.""" - return self.render(self.template_name_ul) - - -class RenderableErrorMixin(RenderableMixin): - def as_json(self, escape_html=False): - return json.dumps(self.get_json_data(escape_html)) - - def as_text(self): - return self.render(self.template_name_text) - - def as_ul(self): - return self.render(self.template_name_ul) - - -class ErrorDict(dict, RenderableErrorMixin): +@html_safe +class ErrorDict(dict): """ A collection of errors that knows how to display itself in various formats. The dictionary keys are the field names, and the values are the errors. """ - - template_name = "django/forms/errors/dict/default.html" - template_name_text = "django/forms/errors/dict/text.txt" - template_name_ul = "django/forms/errors/dict/ul.html" - - def __init__(self, *args, renderer=None, **kwargs): - super().__init__(*args, **kwargs) - self.renderer = renderer or get_default_renderer() - def as_data(self): return {f: e.as_data() for f, e in self.items()} def get_json_data(self, escape_html=False): return {f: e.get_json_data(escape_html) for f, e in self.items()} - def get_context(self): - return { - "errors": self.items(), - "error_class": "errorlist", - } + def as_json(self, escape_html=False): + return json.dumps(self.get_json_data(escape_html)) + + def as_ul(self): + if not self: + return '' + return format_html( + '<ul class="errorlist">{}</ul>', + format_html_join('', '<li>{}{}</li>', self.items()) + ) + + def as_text(self): + output = [] + for field, errors in self.items(): + output.append('* %s' % field) + output.append('\n'.join(' * %s' % e for e in errors)) + return '\n'.join(output) + + def __str__(self): + return self.as_ul() -class ErrorList(UserList, list, RenderableErrorMixin): +@html_safe +class ErrorList(UserList, list): """ A collection of errors that knows how to display itself in various formats. """ - - template_name = "django/forms/errors/list/default.html" - template_name_text = "django/forms/errors/list/text.txt" - template_name_ul = "django/forms/errors/list/ul.html" - - def __init__(self, initlist=None, error_class=None, renderer=None): + def __init__(self, initlist=None, error_class=None): super().__init__(initlist) if error_class is None: - self.error_class = "errorlist" + self.error_class = 'errorlist' else: - self.error_class = "errorlist {}".format(error_class) - self.renderer = renderer or get_default_renderer() + self.error_class = 'errorlist {}'.format(error_class) def as_data(self): return ValidationError(self.data).error_list @@ -143,19 +101,30 @@ class ErrorList(UserList, list, RenderableErrorMixin): errors = [] for error in self.as_data(): message = next(iter(error)) - errors.append( - { - "message": escape(message) if escape_html else message, - "code": error.code or "", - } - ) + errors.append({ + 'message': escape(message) if escape_html else message, + 'code': error.code or '', + }) return errors - def get_context(self): - return { - "errors": self, - "error_class": self.error_class, - } + def as_json(self, escape_html=False): + return json.dumps(self.get_json_data(escape_html)) + + def as_ul(self): + if not self.data: + return '' + + return format_html( + '<ul class="{}">{}</ul>', + self.error_class, + format_html_join('', '<li>{}</li>', ((e,) for e in self)) + ) + + def as_text(self): + return '\n'.join('* %s' % e for e in self) + + def __str__(self): + return self.as_ul() def __repr__(self): return repr(list(self)) @@ -184,7 +153,6 @@ class ErrorList(UserList, list, RenderableErrorMixin): # Utilities for time zone support in DateTimeField et al. - def from_current_timezone(value): """ When time zone support is enabled, convert naive datetimes @@ -193,20 +161,19 @@ def from_current_timezone(value): if settings.USE_TZ and value is not None and timezone.is_naive(value): current_timezone = timezone.get_current_timezone() try: - if not timezone._is_pytz_zone( - current_timezone - ) and timezone._datetime_ambiguous_or_imaginary(value, current_timezone): - raise ValueError("Ambiguous or non-existent time.") + if ( + not timezone._is_pytz_zone(current_timezone) and + timezone._datetime_ambiguous_or_imaginary(value, current_timezone) + ): + raise ValueError('Ambiguous or non-existent time.') return timezone.make_aware(value, current_timezone) except Exception as exc: raise ValidationError( - _( - "%(datetime)s couldn’t be interpreted " - "in time zone %(current_timezone)s; it " - "may be ambiguous or it may not exist." - ), - code="ambiguous_timezone", - params={"datetime": value, "current_timezone": current_timezone}, + _('%(datetime)s couldn’t be interpreted ' + 'in time zone %(current_timezone)s; it ' + 'may be ambiguous or it may not exist.'), + code='ambiguous_timezone', + params={'datetime': value, 'current_timezone': current_timezone} ) from exc return value diff --git a/venv/Lib/site-packages/django/forms/widgets.py b/venv/Lib/site-packages/django/forms/widgets.py index 12b9bbb..1b1c143 100644 --- a/venv/Lib/site-packages/django/forms/widgets.py +++ b/venv/Lib/site-packages/django/forms/widgets.py @@ -10,48 +10,31 @@ from itertools import chain from django.forms.utils import to_current_timezone from django.templatetags.static import static -from django.utils import formats +from django.utils import datetime_safe, formats from django.utils.datastructures import OrderedSet from django.utils.dates import MONTHS from django.utils.formats import get_format from django.utils.html import format_html, html_safe from django.utils.regex_helper import _lazy_re_compile from django.utils.safestring import mark_safe -from django.utils.topological_sort import CyclicDependencyError, stable_topological_sort +from django.utils.topological_sort import ( + CyclicDependencyError, stable_topological_sort, +) from django.utils.translation import gettext_lazy as _ from .renderers import get_default_renderer __all__ = ( - "Media", - "MediaDefiningClass", - "Widget", - "TextInput", - "NumberInput", - "EmailInput", - "URLInput", - "PasswordInput", - "HiddenInput", - "MultipleHiddenInput", - "FileInput", - "ClearableFileInput", - "Textarea", - "DateInput", - "DateTimeInput", - "TimeInput", - "CheckboxInput", - "Select", - "NullBooleanSelect", - "SelectMultiple", - "RadioSelect", - "CheckboxSelectMultiple", - "MultiWidget", - "SplitDateTimeWidget", - "SplitHiddenDateTimeWidget", - "SelectDateWidget", + 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'NumberInput', + 'EmailInput', 'URLInput', 'PasswordInput', 'HiddenInput', + 'MultipleHiddenInput', 'FileInput', 'ClearableFileInput', 'Textarea', + 'DateInput', 'DateTimeInput', 'TimeInput', 'CheckboxInput', 'Select', + 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', + 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', + 'SplitHiddenDateTimeWidget', 'SelectDateWidget', ) -MEDIA_TYPES = ("css", "js") +MEDIA_TYPES = ('css', 'js') class MediaOrderConflictWarning(RuntimeWarning): @@ -62,8 +45,8 @@ class MediaOrderConflictWarning(RuntimeWarning): class Media: def __init__(self, media=None, css=None, js=None): if media is not None: - css = getattr(media, "css", {}) - js = getattr(media, "js", []) + css = getattr(media, 'css', {}) + js = getattr(media, 'js', []) else: if css is None: css = {} @@ -73,7 +56,7 @@ class Media: self._js_lists = [js] def __repr__(self): - return "Media(css=%r, js=%r)" % (self._css, self._js) + return 'Media(css=%r, js=%r)' % (self._css, self._js) def __str__(self): return self.render() @@ -91,35 +74,26 @@ class Media: return self.merge(*self._js_lists) def render(self): - return mark_safe( - "\n".join( - chain.from_iterable( - getattr(self, "render_" + name)() for name in MEDIA_TYPES - ) - ) - ) + return mark_safe('\n'.join(chain.from_iterable(getattr(self, 'render_' + name)() for name in MEDIA_TYPES))) def render_js(self): return [ - format_html('<script src="{}"></script>', self.absolute_path(path)) - for path in self._js + format_html( + '<script src="{}"></script>', + self.absolute_path(path) + ) for path in self._js ] def render_css(self): # To keep rendering order consistent, we can't just iterate over items(). # We need to sort the keys, and iterate over the sorted list. media = sorted(self._css) - return chain.from_iterable( - [ - format_html( - '<link href="{}" type="text/css" media="{}" rel="stylesheet">', - self.absolute_path(path), - medium, - ) - for path in self._css[medium] - ] - for medium in media - ) + return chain.from_iterable([ + format_html( + '<link href="{}" type="text/css" media="{}" rel="stylesheet">', + self.absolute_path(path), medium + ) for path in self._css[medium] + ] for medium in media) def absolute_path(self, path): """ @@ -127,14 +101,14 @@ class Media: path. An absolute path will be returned unchanged while a relative path will be passed to django.templatetags.static.static(). """ - if path.startswith(("http://", "https://", "/")): + if path.startswith(('http://', 'https://', '/')): return path return static(path) def __getitem__(self, name): """Return a Media object that only contains media of the given type.""" if name in MEDIA_TYPES: - return Media(**{str(name): getattr(self, "_" + name)}) + return Media(**{str(name): getattr(self, '_' + name)}) raise KeyError('Unknown media type "%s"' % name) @staticmethod @@ -164,10 +138,9 @@ class Media: return stable_topological_sort(all_items, dependency_graph) except CyclicDependencyError: warnings.warn( - "Detected duplicate Media files in an opposite order: {}".format( - ", ".join(repr(list_) for list_ in lists) - ), - MediaOrderConflictWarning, + 'Detected duplicate Media files in an opposite order: {}'.format( + ', '.join(repr(list_) for list_ in lists) + ), MediaOrderConflictWarning, ) return list(all_items) @@ -194,9 +167,9 @@ def media_property(cls): base = Media() # Get the media definition for this class - definition = getattr(cls, "Media", None) + definition = getattr(cls, 'Media', None) if definition: - extend = getattr(definition, "extend", True) + extend = getattr(definition, 'extend', True) if extend: if extend is True: m = base @@ -207,7 +180,6 @@ def media_property(cls): return m + Media(definition) return Media(definition) return base - return property(_media) @@ -215,11 +187,10 @@ class MediaDefiningClass(type): """ Metaclass for classes that can have media definitions. """ - def __new__(mcs, name, bases, attrs): new_class = super().__new__(mcs, name, bases, attrs) - if "media" not in attrs: + if 'media' not in attrs: new_class.media = media_property(new_class) return new_class @@ -242,17 +213,17 @@ class Widget(metaclass=MediaDefiningClass): @property def is_hidden(self): - return self.input_type == "hidden" if hasattr(self, "input_type") else False + return self.input_type == 'hidden' if hasattr(self, 'input_type') else False def subwidgets(self, name, value, attrs=None): context = self.get_context(name, value, attrs) - yield context["widget"] + yield context['widget'] def format_value(self, value): """ Return a value as it should appear when rendered in a template. """ - if value == "" or value is None: + if value == '' or value is None: return None if self.is_localized: return formats.localize_input(value) @@ -260,13 +231,13 @@ class Widget(metaclass=MediaDefiningClass): def get_context(self, name, value, attrs): return { - "widget": { - "name": name, - "is_hidden": self.is_hidden, - "required": self.is_required, - "value": self.format_value(value), - "attrs": self.build_attrs(self.attrs, attrs), - "template_name": self.template_name, + 'widget': { + 'name': name, + 'is_hidden': self.is_hidden, + 'required': self.is_required, + 'value': self.format_value(value), + 'attrs': self.build_attrs(self.attrs, attrs), + 'template_name': self.template_name, }, } @@ -314,45 +285,44 @@ class Input(Widget): """ Base class for all <input> widgets. """ - input_type = None # Subclasses must define this. - template_name = "django/forms/widgets/input.html" + template_name = 'django/forms/widgets/input.html' def __init__(self, attrs=None): if attrs is not None: attrs = attrs.copy() - self.input_type = attrs.pop("type", self.input_type) + self.input_type = attrs.pop('type', self.input_type) super().__init__(attrs) def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["widget"]["type"] = self.input_type + context['widget']['type'] = self.input_type return context class TextInput(Input): - input_type = "text" - template_name = "django/forms/widgets/text.html" + input_type = 'text' + template_name = 'django/forms/widgets/text.html' class NumberInput(Input): - input_type = "number" - template_name = "django/forms/widgets/number.html" + input_type = 'number' + template_name = 'django/forms/widgets/number.html' class EmailInput(Input): - input_type = "email" - template_name = "django/forms/widgets/email.html" + input_type = 'email' + template_name = 'django/forms/widgets/email.html' class URLInput(Input): - input_type = "url" - template_name = "django/forms/widgets/url.html" + input_type = 'url' + template_name = 'django/forms/widgets/url.html' class PasswordInput(Input): - input_type = "password" - template_name = "django/forms/widgets/password.html" + input_type = 'password' + template_name = 'django/forms/widgets/password.html' def __init__(self, attrs=None, render_value=False): super().__init__(attrs) @@ -365,8 +335,8 @@ class PasswordInput(Input): class HiddenInput(Input): - input_type = "hidden" - template_name = "django/forms/widgets/hidden.html" + input_type = 'hidden' + template_name = 'django/forms/widgets/hidden.html' class MultipleHiddenInput(HiddenInput): @@ -374,26 +344,25 @@ class MultipleHiddenInput(HiddenInput): Handle <input type="hidden"> for fields that have a list of values. """ - - template_name = "django/forms/widgets/multiple_hidden.html" + template_name = 'django/forms/widgets/multiple_hidden.html' def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - final_attrs = context["widget"]["attrs"] - id_ = context["widget"]["attrs"].get("id") + final_attrs = context['widget']['attrs'] + id_ = context['widget']['attrs'].get('id') subwidgets = [] - for index, value_ in enumerate(context["widget"]["value"]): + for index, value_ in enumerate(context['widget']['value']): widget_attrs = final_attrs.copy() if id_: # An ID attribute was given. Add a numeric index as a suffix # so that the inputs don't all have the same ID attribute. - widget_attrs["id"] = "%s_%s" % (id_, index) + widget_attrs['id'] = '%s_%s' % (id_, index) widget = HiddenInput() widget.is_required = self.is_required - subwidgets.append(widget.get_context(name, value_, widget_attrs)["widget"]) + subwidgets.append(widget.get_context(name, value_, widget_attrs)['widget']) - context["widget"]["subwidgets"] = subwidgets + context['widget']['subwidgets'] = subwidgets return context def value_from_datadict(self, data, files, name): @@ -408,9 +377,9 @@ class MultipleHiddenInput(HiddenInput): class FileInput(Input): - input_type = "file" + input_type = 'file' needs_multipart_form = True - template_name = "django/forms/widgets/file.html" + template_name = 'django/forms/widgets/file.html' def format_value(self, value): """File input never renders a value.""" @@ -431,29 +400,29 @@ FILE_INPUT_CONTRADICTION = object() class ClearableFileInput(FileInput): - clear_checkbox_label = _("Clear") - initial_text = _("Currently") - input_text = _("Change") - template_name = "django/forms/widgets/clearable_file_input.html" + clear_checkbox_label = _('Clear') + initial_text = _('Currently') + input_text = _('Change') + template_name = 'django/forms/widgets/clearable_file_input.html' def clear_checkbox_name(self, name): """ Given the name of the file input, return the name of the clear checkbox input. """ - return name + "-clear" + return name + '-clear' def clear_checkbox_id(self, name): """ Given the name of the clear checkbox input, return the HTML id for it. """ - return name + "_id" + return name + '_id' def is_initial(self, value): """ Return whether value is considered to be initial value. """ - return bool(value and getattr(value, "url", False)) + return bool(value and getattr(value, 'url', False)) def format_value(self, value): """ @@ -466,23 +435,20 @@ class ClearableFileInput(FileInput): context = super().get_context(name, value, attrs) checkbox_name = self.clear_checkbox_name(name) checkbox_id = self.clear_checkbox_id(checkbox_name) - context["widget"].update( - { - "checkbox_name": checkbox_name, - "checkbox_id": checkbox_id, - "is_initial": self.is_initial(value), - "input_text": self.input_text, - "initial_text": self.initial_text, - "clear_checkbox_label": self.clear_checkbox_label, - } - ) + context['widget'].update({ + 'checkbox_name': checkbox_name, + 'checkbox_id': checkbox_id, + 'is_initial': self.is_initial(value), + 'input_text': self.input_text, + 'initial_text': self.initial_text, + 'clear_checkbox_label': self.clear_checkbox_label, + }) return context def value_from_datadict(self, data, files, name): upload = super().value_from_datadict(data, files, name) if not self.is_required and CheckboxInput().value_from_datadict( - data, files, self.clear_checkbox_name(name) - ): + data, files, self.clear_checkbox_name(name)): if upload: # If the user contradicts themselves (uploads a new file AND @@ -495,24 +461,24 @@ class ClearableFileInput(FileInput): def value_omitted_from_data(self, data, files, name): return ( - super().value_omitted_from_data(data, files, name) - and self.clear_checkbox_name(name) not in data + super().value_omitted_from_data(data, files, name) and + self.clear_checkbox_name(name) not in data ) class Textarea(Widget): - template_name = "django/forms/widgets/textarea.html" + template_name = 'django/forms/widgets/textarea.html' def __init__(self, attrs=None): # Use slightly better defaults than HTML's 20x2 box - default_attrs = {"cols": "40", "rows": "10"} + default_attrs = {'cols': '40', 'rows': '10'} if attrs: default_attrs.update(attrs) super().__init__(default_attrs) class DateTimeBaseInput(TextInput): - format_key = "" + format_key = '' supports_microseconds = False def __init__(self, attrs=None, format=None): @@ -520,34 +486,32 @@ class DateTimeBaseInput(TextInput): self.format = format or None def format_value(self, value): - return formats.localize_input( - value, self.format or formats.get_format(self.format_key)[0] - ) + return formats.localize_input(value, self.format or formats.get_format(self.format_key)[0]) class DateInput(DateTimeBaseInput): - format_key = "DATE_INPUT_FORMATS" - template_name = "django/forms/widgets/date.html" + format_key = 'DATE_INPUT_FORMATS' + template_name = 'django/forms/widgets/date.html' class DateTimeInput(DateTimeBaseInput): - format_key = "DATETIME_INPUT_FORMATS" - template_name = "django/forms/widgets/datetime.html" + format_key = 'DATETIME_INPUT_FORMATS' + template_name = 'django/forms/widgets/datetime.html' class TimeInput(DateTimeBaseInput): - format_key = "TIME_INPUT_FORMATS" - template_name = "django/forms/widgets/time.html" + format_key = 'TIME_INPUT_FORMATS' + template_name = 'django/forms/widgets/time.html' # Defined at module level so that CheckboxInput is picklable (#17976) def boolean_check(v): - return not (v is False or v is None or v == "") + return not (v is False or v is None or v == '') class CheckboxInput(Input): - input_type = "checkbox" - template_name = "django/forms/widgets/checkbox.html" + input_type = 'checkbox' + template_name = 'django/forms/widgets/checkbox.html' def __init__(self, attrs=None, check_test=None): super().__init__(attrs) @@ -557,13 +521,13 @@ class CheckboxInput(Input): def format_value(self, value): """Only return the 'value' attribute if value isn't empty.""" - if value is True or value is False or value is None or value == "": + if value is True or value is False or value is None or value == '': return return str(value) def get_context(self, name, value, attrs): if self.check_test(value): - attrs = {**(attrs or {}), "checked": True} + attrs = {**(attrs or {}), 'checked': True} return super().get_context(name, value, attrs) def value_from_datadict(self, data, files, name): @@ -573,7 +537,7 @@ class CheckboxInput(Input): return False value = data.get(name) # Translate true and false strings to boolean values. - values = {"true": True, "false": False} + values = {'true': True, 'false': False} if isinstance(value, str): value = values.get(value.lower(), value) return bool(value) @@ -590,7 +554,7 @@ class ChoiceWidget(Widget): template_name = None option_template_name = None add_id_index = True - checked_attribute = {"checked": True} + checked_attribute = {'checked': True} option_inherits_attrs = True def __init__(self, attrs=None, choices=()): @@ -627,7 +591,7 @@ class ChoiceWidget(Widget): for index, (option_value, option_label) in enumerate(self.choices): if option_value is None: - option_value = "" + option_value = '' subgroup = [] if isinstance(option_label, (list, tuple)): @@ -641,62 +605,52 @@ class ChoiceWidget(Widget): groups.append((group_name, subgroup, index)) for subvalue, sublabel in choices: - selected = (not has_selected or self.allow_multiple_selected) and str( - subvalue - ) in value - has_selected |= selected - subgroup.append( - self.create_option( - name, - subvalue, - sublabel, - selected, - index, - subindex=subindex, - attrs=attrs, - ) + selected = ( + str(subvalue) in value and + (not has_selected or self.allow_multiple_selected) ) + has_selected |= selected + subgroup.append(self.create_option( + name, subvalue, sublabel, selected, index, + subindex=subindex, attrs=attrs, + )) if subindex is not None: subindex += 1 return groups - def create_option( - self, name, value, label, selected, index, subindex=None, attrs=None - ): + def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): index = str(index) if subindex is None else "%s_%s" % (index, subindex) - option_attrs = ( - self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {} - ) + if attrs is None: + attrs = {} + option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {} if selected: option_attrs.update(self.checked_attribute) - if "id" in option_attrs: - option_attrs["id"] = self.id_for_label(option_attrs["id"], index) + if 'id' in option_attrs: + option_attrs['id'] = self.id_for_label(option_attrs['id'], index) return { - "name": name, - "value": value, - "label": label, - "selected": selected, - "index": index, - "attrs": option_attrs, - "type": self.input_type, - "template_name": self.option_template_name, - "wrap_label": True, + 'name': name, + 'value': value, + 'label': label, + 'selected': selected, + 'index': index, + 'attrs': option_attrs, + 'type': self.input_type, + 'template_name': self.option_template_name, + 'wrap_label': True, } def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["widget"]["optgroups"] = self.optgroups( - name, context["widget"]["value"], attrs - ) + context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs) return context - def id_for_label(self, id_, index="0"): + def id_for_label(self, id_, index='0'): """ Use an incremented id for each option where the main widget references the zero index. """ if id_ and self.add_id_index: - id_ = "%s_%s" % (id_, index) + id_ = '%s_%s' % (id_, index) return id_ def value_from_datadict(self, data, files, name): @@ -714,28 +668,28 @@ class ChoiceWidget(Widget): return [] if not isinstance(value, (tuple, list)): value = [value] - return [str(v) if v is not None else "" for v in value] + return [str(v) if v is not None else '' for v in value] class Select(ChoiceWidget): - input_type = "select" - template_name = "django/forms/widgets/select.html" - option_template_name = "django/forms/widgets/select_option.html" + input_type = 'select' + template_name = 'django/forms/widgets/select.html' + option_template_name = 'django/forms/widgets/select_option.html' add_id_index = False - checked_attribute = {"selected": True} + checked_attribute = {'selected': True} option_inherits_attrs = False def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) if self.allow_multiple_selected: - context["widget"]["attrs"]["multiple"] = True + context['widget']['attrs']['multiple'] = True return context @staticmethod def _choice_has_empty_value(choice): """Return True if the choice's value is empty string or None.""" value, _ = choice - return value is None or value == "" + return value is None or value == '' def use_required_attribute(self, initial): """ @@ -748,52 +702,44 @@ class Select(ChoiceWidget): return use_required_attribute first_choice = next(iter(self.choices), None) - return ( - use_required_attribute - and first_choice is not None - and self._choice_has_empty_value(first_choice) - ) + return use_required_attribute and first_choice is not None and self._choice_has_empty_value(first_choice) class NullBooleanSelect(Select): """ A Select Widget intended to be used with NullBooleanField. """ - def __init__(self, attrs=None): choices = ( - ("unknown", _("Unknown")), - ("true", _("Yes")), - ("false", _("No")), + ('unknown', _('Unknown')), + ('true', _('Yes')), + ('false', _('No')), ) super().__init__(attrs, choices) def format_value(self, value): try: return { - True: "true", - False: "false", - "true": "true", - "false": "false", + True: 'true', False: 'false', + 'true': 'true', 'false': 'false', # For backwards compatibility with Django < 2.2. - "2": "true", - "3": "false", + '2': 'true', '3': 'false', }[value] except KeyError: - return "unknown" + return 'unknown' def value_from_datadict(self, data, files, name): value = data.get(name) return { True: True, - "True": True, - "False": False, + 'True': True, + 'False': False, False: False, - "true": True, - "false": False, + 'true': True, + 'false': False, # For backwards compatibility with Django < 2.2. - "2": True, - "3": False, + '2': True, + '3': False, }.get(value) @@ -814,26 +760,16 @@ class SelectMultiple(Select): class RadioSelect(ChoiceWidget): - input_type = "radio" - template_name = "django/forms/widgets/radio.html" - option_template_name = "django/forms/widgets/radio_option.html" - - def id_for_label(self, id_, index=None): - """ - Don't include for="field_0" in <label> to improve accessibility when - using a screen reader, in addition clicking such a label would toggle - the first input. - """ - if index is None: - return "" - return super().id_for_label(id_, index) + input_type = 'radio' + template_name = 'django/forms/widgets/radio.html' + option_template_name = 'django/forms/widgets/radio_option.html' -class CheckboxSelectMultiple(RadioSelect): +class CheckboxSelectMultiple(ChoiceWidget): allow_multiple_selected = True - input_type = "checkbox" - template_name = "django/forms/widgets/checkbox_select.html" - option_template_name = "django/forms/widgets/checkbox_option.html" + input_type = 'checkbox' + template_name = 'django/forms/widgets/checkbox_select.html' + option_template_name = 'django/forms/widgets/checkbox_option.html' def use_required_attribute(self, initial): # Don't use the 'required' attribute because browser validation would @@ -845,6 +781,15 @@ class CheckboxSelectMultiple(RadioSelect): # never known if the value is actually omitted. return False + def id_for_label(self, id_, index=None): + """ + Don't include for="field_0" in <label> because clicking such a label + would toggle the first checkbox. + """ + if index is None: + return '' + return super().id_for_label(id_, index) + class MultiWidget(Widget): """ @@ -856,15 +801,16 @@ class MultiWidget(Widget): You'll probably want to use this class with MultiValueField. """ - - template_name = "django/forms/widgets/multiwidget.html" + template_name = 'django/forms/widgets/multiwidget.html' def __init__(self, widgets, attrs=None): if isinstance(widgets, dict): - self.widgets_names = [("_%s" % name) if name else "" for name in widgets] + self.widgets_names = [ + ('_%s' % name) if name else '' for name in widgets + ] widgets = widgets.values() else: - self.widgets_names = ["_%s" % i for i in range(len(widgets))] + self.widgets_names = ['_%s' % i for i in range(len(widgets))] self.widgets = [w() if isinstance(w, type) else w for w in widgets] super().__init__(attrs) @@ -882,13 +828,11 @@ class MultiWidget(Widget): if not isinstance(value, list): value = self.decompress(value) - final_attrs = context["widget"]["attrs"] - input_type = final_attrs.pop("type", None) - id_ = final_attrs.get("id") + final_attrs = context['widget']['attrs'] + input_type = final_attrs.pop('type', None) + id_ = final_attrs.get('id') subwidgets = [] - for i, (widget_name, widget) in enumerate( - zip(self.widgets_names, self.widgets) - ): + for i, (widget_name, widget) in enumerate(zip(self.widgets_names, self.widgets)): if input_type is not None: widget.input_type = input_type widget_name = name + widget_name @@ -898,18 +842,16 @@ class MultiWidget(Widget): widget_value = None if id_: widget_attrs = final_attrs.copy() - widget_attrs["id"] = "%s_%s" % (id_, i) + widget_attrs['id'] = '%s_%s' % (id_, i) else: widget_attrs = final_attrs - subwidgets.append( - widget.get_context(widget_name, widget_value, widget_attrs)["widget"] - ) - context["widget"]["subwidgets"] = subwidgets + subwidgets.append(widget.get_context(widget_name, widget_value, widget_attrs)['widget']) + context['widget']['subwidgets'] = subwidgets return context def id_for_label(self, id_): if id_: - id_ += "_0" + id_ += '_0' return id_ def value_from_datadict(self, data, files, name): @@ -930,7 +872,7 @@ class MultiWidget(Widget): The given value can be assumed to be valid, but not necessarily non-empty. """ - raise NotImplementedError("Subclasses must implement this method.") + raise NotImplementedError('Subclasses must implement this method.') def _get_media(self): """ @@ -941,7 +883,6 @@ class MultiWidget(Widget): for w in self.widgets: media = media + w.media return media - media = property(_get_media) def __deepcopy__(self, memo): @@ -958,18 +899,10 @@ class SplitDateTimeWidget(MultiWidget): """ A widget that splits datetime input into two <input type="text"> boxes. """ - supports_microseconds = False - template_name = "django/forms/widgets/splitdatetime.html" + template_name = 'django/forms/widgets/splitdatetime.html' - def __init__( - self, - attrs=None, - date_format=None, - time_format=None, - date_attrs=None, - time_attrs=None, - ): + def __init__(self, attrs=None, date_format=None, time_format=None, date_attrs=None, time_attrs=None): widgets = ( DateInput( attrs=attrs if date_attrs is None else date_attrs, @@ -993,20 +926,12 @@ class SplitHiddenDateTimeWidget(SplitDateTimeWidget): """ A widget that splits datetime input into two <input type="hidden"> inputs. """ + template_name = 'django/forms/widgets/splithiddendatetime.html' - template_name = "django/forms/widgets/splithiddendatetime.html" - - def __init__( - self, - attrs=None, - date_format=None, - time_format=None, - date_attrs=None, - time_attrs=None, - ): + def __init__(self, attrs=None, date_format=None, time_format=None, date_attrs=None, time_attrs=None): super().__init__(attrs, date_format, time_format, date_attrs, time_attrs) for widget in self.widgets: - widget.input_type = "hidden" + widget.input_type = 'hidden' class SelectDateWidget(Widget): @@ -1016,15 +941,14 @@ class SelectDateWidget(Widget): This also serves as an example of a Widget that has more than one HTML element and hence implements value_from_datadict. """ - - none_value = ("", "---") - month_field = "%s_month" - day_field = "%s_day" - year_field = "%s_year" - template_name = "django/forms/widgets/select_date.html" - input_type = "select" + none_value = ('', '---') + month_field = '%s_month' + day_field = '%s_day' + year_field = '%s_year' + template_name = 'django/forms/widgets/select_date.html' + input_type = 'select' select_widget = Select - date_re = _lazy_re_compile(r"(\d{4}|0)-(\d\d?)-(\d\d?)$") + date_re = _lazy_re_compile(r'(\d{4}|0)-(\d\d?)-(\d\d?)$') def __init__(self, attrs=None, years=None, months=None, empty_label=None): self.attrs = attrs or {} @@ -1045,14 +969,14 @@ class SelectDateWidget(Widget): # Optional string, list, or tuple to use as empty_label. if isinstance(empty_label, (list, tuple)): if not len(empty_label) == 3: - raise ValueError("empty_label list/tuple must have 3 elements.") + raise ValueError('empty_label list/tuple must have 3 elements.') - self.year_none_value = ("", empty_label[0]) - self.month_none_value = ("", empty_label[1]) - self.day_none_value = ("", empty_label[2]) + self.year_none_value = ('', empty_label[0]) + self.month_none_value = ('', empty_label[1]) + self.day_none_value = ('', empty_label[2]) else: if empty_label is not None: - self.none_value = ("", empty_label) + self.none_value = ('', empty_label) self.year_none_value = self.none_value self.month_none_value = self.none_value @@ -1065,40 +989,33 @@ class SelectDateWidget(Widget): if not self.is_required: year_choices.insert(0, self.year_none_value) year_name = self.year_field % name - date_context["year"] = self.select_widget( - attrs, choices=year_choices - ).get_context( + date_context['year'] = self.select_widget(attrs, choices=year_choices).get_context( name=year_name, - value=context["widget"]["value"]["year"], - attrs={**context["widget"]["attrs"], "id": "id_%s" % year_name}, + value=context['widget']['value']['year'], + attrs={**context['widget']['attrs'], 'id': 'id_%s' % year_name}, ) month_choices = list(self.months.items()) if not self.is_required: month_choices.insert(0, self.month_none_value) month_name = self.month_field % name - date_context["month"] = self.select_widget( - attrs, choices=month_choices - ).get_context( + date_context['month'] = self.select_widget(attrs, choices=month_choices).get_context( name=month_name, - value=context["widget"]["value"]["month"], - attrs={**context["widget"]["attrs"], "id": "id_%s" % month_name}, + value=context['widget']['value']['month'], + attrs={**context['widget']['attrs'], 'id': 'id_%s' % month_name}, ) day_choices = [(i, i) for i in range(1, 32)] if not self.is_required: day_choices.insert(0, self.day_none_value) day_name = self.day_field % name - date_context["day"] = self.select_widget( - attrs, - choices=day_choices, - ).get_context( + date_context['day'] = self.select_widget(attrs, choices=day_choices,).get_context( name=day_name, - value=context["widget"]["value"]["day"], - attrs={**context["widget"]["attrs"], "id": "id_%s" % day_name}, + value=context['widget']['value']['day'], + attrs={**context['widget']['attrs'], 'id': 'id_%s' % day_name}, ) subwidgets = [] for field in self._parse_date_fmt(): - subwidgets.append(date_context[field]["widget"]) - context["widget"]["subwidgets"] = subwidgets + subwidgets.append(date_context[field]['widget']) + context['widget']['subwidgets'] = subwidgets return context def format_value(self, value): @@ -1115,58 +1032,58 @@ class SelectDateWidget(Widget): if match: # Convert any zeros in the date to empty strings to match the # empty option value. - year, month, day = [int(val) or "" for val in match.groups()] + year, month, day = [int(val) or '' for val in match.groups()] else: - input_format = get_format("DATE_INPUT_FORMATS")[0] + input_format = get_format('DATE_INPUT_FORMATS')[0] try: d = datetime.datetime.strptime(value, input_format) except ValueError: pass else: year, month, day = d.year, d.month, d.day - return {"year": year, "month": month, "day": day} + return {'year': year, 'month': month, 'day': day} @staticmethod def _parse_date_fmt(): - fmt = get_format("DATE_FORMAT") + fmt = get_format('DATE_FORMAT') escaped = False for char in fmt: if escaped: escaped = False - elif char == "\\": + elif char == '\\': escaped = True - elif char in "Yy": - yield "year" - elif char in "bEFMmNn": - yield "month" - elif char in "dj": - yield "day" + elif char in 'Yy': + yield 'year' + elif char in 'bEFMmNn': + yield 'month' + elif char in 'dj': + yield 'day' def id_for_label(self, id_): for first_select in self._parse_date_fmt(): - return "%s_%s" % (id_, first_select) - return "%s_month" % id_ + return '%s_%s' % (id_, first_select) + return '%s_month' % id_ def value_from_datadict(self, data, files, name): y = data.get(self.year_field % name) m = data.get(self.month_field % name) d = data.get(self.day_field % name) - if y == m == d == "": + if y == m == d == '': return None if y is not None and m is not None and d is not None: - input_format = get_format("DATE_INPUT_FORMATS")[0] - input_format = formats.sanitize_strftime_format(input_format) + input_format = get_format('DATE_INPUT_FORMATS')[0] try: date_value = datetime.date(int(y), int(m), int(d)) except ValueError: # Return pseudo-ISO dates with zeros for any unselected values, # e.g. '2017-0-23'. - return "%s-%s-%s" % (y or 0, m or 0, d or 0) + return '%s-%s-%s' % (y or 0, m or 0, d or 0) + date_value = datetime_safe.new_date(date_value) return date_value.strftime(input_format) return data.get(name) def value_omitted_from_data(self, data, files, name): return not any( - ("{}_{}".format(name, interval) in data) - for interval in ("year", "month", "day") + ('{}_{}'.format(name, interval) in data) + for interval in ('year', 'month', 'day') ) diff --git a/venv/Lib/site-packages/django/http/__init__.py b/venv/Lib/site-packages/django/http/__init__.py index 4c99715..491239b 100644 --- a/venv/Lib/site-packages/django/http/__init__.py +++ b/venv/Lib/site-packages/django/http/__init__.py @@ -1,48 +1,21 @@ from django.http.cookie import SimpleCookie, parse_cookie from django.http.request import ( - HttpRequest, - QueryDict, - RawPostDataException, - UnreadablePostError, + HttpRequest, QueryDict, RawPostDataException, UnreadablePostError, ) from django.http.response import ( - BadHeaderError, - FileResponse, - Http404, - HttpResponse, - HttpResponseBadRequest, - HttpResponseForbidden, - HttpResponseGone, - HttpResponseNotAllowed, - HttpResponseNotFound, - HttpResponseNotModified, - HttpResponsePermanentRedirect, - HttpResponseRedirect, - HttpResponseServerError, - JsonResponse, - StreamingHttpResponse, + BadHeaderError, FileResponse, Http404, HttpResponse, + HttpResponseBadRequest, HttpResponseForbidden, HttpResponseGone, + HttpResponseNotAllowed, HttpResponseNotFound, HttpResponseNotModified, + HttpResponsePermanentRedirect, HttpResponseRedirect, + HttpResponseServerError, JsonResponse, StreamingHttpResponse, ) __all__ = [ - "SimpleCookie", - "parse_cookie", - "HttpRequest", - "QueryDict", - "RawPostDataException", - "UnreadablePostError", - "HttpResponse", - "StreamingHttpResponse", - "HttpResponseRedirect", - "HttpResponsePermanentRedirect", - "HttpResponseNotModified", - "HttpResponseBadRequest", - "HttpResponseForbidden", - "HttpResponseNotFound", - "HttpResponseNotAllowed", - "HttpResponseGone", - "HttpResponseServerError", - "Http404", - "BadHeaderError", - "JsonResponse", - "FileResponse", + 'SimpleCookie', 'parse_cookie', 'HttpRequest', 'QueryDict', + 'RawPostDataException', 'UnreadablePostError', + 'HttpResponse', 'StreamingHttpResponse', 'HttpResponseRedirect', + 'HttpResponsePermanentRedirect', 'HttpResponseNotModified', + 'HttpResponseBadRequest', 'HttpResponseForbidden', 'HttpResponseNotFound', + 'HttpResponseNotAllowed', 'HttpResponseGone', 'HttpResponseServerError', + 'Http404', 'BadHeaderError', 'JsonResponse', 'FileResponse', ] diff --git a/venv/Lib/site-packages/django/http/cookie.py b/venv/Lib/site-packages/django/http/cookie.py index dce0cfd..5c418d7 100644 --- a/venv/Lib/site-packages/django/http/cookie.py +++ b/venv/Lib/site-packages/django/http/cookie.py @@ -3,19 +3,22 @@ from http import cookies # For backwards compatibility in Django 2.1. SimpleCookie = cookies.SimpleCookie +# Add support for the SameSite attribute (obsolete when PY37 is unsupported). +cookies.Morsel._reserved.setdefault('samesite', 'SameSite') + def parse_cookie(cookie): """ Return a dictionary parsed from a `Cookie:` header string. """ cookiedict = {} - for chunk in cookie.split(";"): - if "=" in chunk: - key, val = chunk.split("=", 1) + for chunk in cookie.split(';'): + if '=' in chunk: + key, val = chunk.split('=', 1) else: # Assume an empty name per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 - key, val = "", chunk + key, val = '', chunk key, val = key.strip(), val.strip() if key or val: # unquote using Python's algorithm. diff --git a/venv/Lib/site-packages/django/http/multipartparser.py b/venv/Lib/site-packages/django/http/multipartparser.py index 77a4b1f..f464caa 100644 --- a/venv/Lib/site-packages/django/http/multipartparser.py +++ b/venv/Lib/site-packages/django/http/multipartparser.py @@ -13,15 +13,15 @@ from urllib.parse import unquote from django.conf import settings from django.core.exceptions import ( - RequestDataTooBig, - SuspiciousMultipartForm, - TooManyFieldsSent, + RequestDataTooBig, SuspiciousMultipartForm, TooManyFieldsSent, +) +from django.core.files.uploadhandler import ( + SkipFile, StopFutureHandlers, StopUpload, ) -from django.core.files.uploadhandler import SkipFile, StopFutureHandlers, StopUpload from django.utils.datastructures import MultiValueDict from django.utils.encoding import force_str -__all__ = ("MultiPartParser", "MultiPartParserError", "InputStreamExhausted") +__all__ = ('MultiPartParser', 'MultiPartParserError', 'InputStreamExhausted') class MultiPartParserError(Exception): @@ -32,7 +32,6 @@ class InputStreamExhausted(Exception): """ No more reads are allowed from this device. """ - pass @@ -48,7 +47,6 @@ class MultiPartParser: ``MultiValueDict.parse()`` reads the input stream in ``chunk_size`` chunks and returns a tuple of ``(MultiValueDict(POST), MultiValueDict(FILES))``. """ - def __init__(self, META, input_data, upload_handlers, encoding=None): """ Initialize the MultiPartParser object. @@ -64,28 +62,23 @@ class MultiPartParser: The encoding with which to treat the incoming data. """ # Content-Type should contain multipart and the boundary information. - content_type = META.get("CONTENT_TYPE", "") - if not content_type.startswith("multipart/"): - raise MultiPartParserError("Invalid Content-Type: %s" % content_type) + content_type = META.get('CONTENT_TYPE', '') + if not content_type.startswith('multipart/'): + raise MultiPartParserError('Invalid Content-Type: %s' % content_type) # Parse the header to get the boundary to split the parts. try: - ctypes, opts = parse_header(content_type.encode("ascii")) + ctypes, opts = parse_header(content_type.encode('ascii')) except UnicodeEncodeError: - raise MultiPartParserError( - "Invalid non-ASCII Content-Type in multipart: %s" - % force_str(content_type) - ) - boundary = opts.get("boundary") + raise MultiPartParserError('Invalid non-ASCII Content-Type in multipart: %s' % force_str(content_type)) + boundary = opts.get('boundary') if not boundary or not cgi.valid_boundary(boundary): - raise MultiPartParserError( - "Invalid boundary in multipart: %s" % force_str(boundary) - ) + raise MultiPartParserError('Invalid boundary in multipart: %s' % force_str(boundary)) # Content-Length should contain the length of the body we are about # to receive. try: - content_length = int(META.get("CONTENT_LENGTH", 0)) + content_length = int(META.get('CONTENT_LENGTH', 0)) except (ValueError, TypeError): content_length = 0 @@ -94,14 +87,14 @@ class MultiPartParser: raise MultiPartParserError("Invalid content length: %r" % content_length) if isinstance(boundary, str): - boundary = boundary.encode("ascii") + boundary = boundary.encode('ascii') self._boundary = boundary self._input_data = input_data # For compatibility with low-level network APIs (with 32-bit integers), # the chunk size should be < 2^31, but still divisible by 4. possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] - self._chunk_size = min([2**31 - 4] + possible_sizes) + self._chunk_size = min([2 ** 31 - 4] + possible_sizes) self._meta = META self._encoding = encoding or settings.DEFAULT_CHARSET @@ -170,36 +163,32 @@ class MultiPartParser: uploaded_file = True try: - disposition = meta_data["content-disposition"][1] - field_name = disposition["name"].strip() + disposition = meta_data['content-disposition'][1] + field_name = disposition['name'].strip() except (KeyError, IndexError, AttributeError): continue - transfer_encoding = meta_data.get("content-transfer-encoding") + transfer_encoding = meta_data.get('content-transfer-encoding') if transfer_encoding is not None: transfer_encoding = transfer_encoding[0].strip() - field_name = force_str(field_name, encoding, errors="replace") + field_name = force_str(field_name, encoding, errors='replace') if item_type == FIELD: # Avoid storing more than DATA_UPLOAD_MAX_NUMBER_FIELDS. num_post_keys += 1 - if ( - settings.DATA_UPLOAD_MAX_NUMBER_FIELDS is not None - and settings.DATA_UPLOAD_MAX_NUMBER_FIELDS < num_post_keys - ): + if (settings.DATA_UPLOAD_MAX_NUMBER_FIELDS is not None and + settings.DATA_UPLOAD_MAX_NUMBER_FIELDS < num_post_keys): raise TooManyFieldsSent( - "The number of GET/POST parameters exceeded " - "settings.DATA_UPLOAD_MAX_NUMBER_FIELDS." + 'The number of GET/POST parameters exceeded ' + 'settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.' ) # Avoid reading more than DATA_UPLOAD_MAX_MEMORY_SIZE. if settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None: - read_size = ( - settings.DATA_UPLOAD_MAX_MEMORY_SIZE - num_bytes_read - ) + read_size = settings.DATA_UPLOAD_MAX_MEMORY_SIZE - num_bytes_read # This is a post field, we can just set it in the post - if transfer_encoding == "base64": + if transfer_encoding == 'base64': raw_data = field_stream.read(size=read_size) num_bytes_read += len(raw_data) try: @@ -213,35 +202,26 @@ class MultiPartParser: # Add two here to make the check consistent with the # x-www-form-urlencoded check that includes '&='. num_bytes_read += len(field_name) + 2 - if ( - settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None - and num_bytes_read > settings.DATA_UPLOAD_MAX_MEMORY_SIZE - ): - raise RequestDataTooBig( - "Request body exceeded " - "settings.DATA_UPLOAD_MAX_MEMORY_SIZE." - ) + if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and + num_bytes_read > settings.DATA_UPLOAD_MAX_MEMORY_SIZE): + raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.') - self._post.appendlist( - field_name, force_str(data, encoding, errors="replace") - ) + self._post.appendlist(field_name, force_str(data, encoding, errors='replace')) elif item_type == FILE: # This is a file, use the handler... - file_name = disposition.get("filename") + file_name = disposition.get('filename') if file_name: - file_name = force_str(file_name, encoding, errors="replace") + file_name = force_str(file_name, encoding, errors='replace') file_name = self.sanitize_file_name(file_name) if not file_name: continue - content_type, content_type_extra = meta_data.get( - "content-type", ("", {}) - ) + content_type, content_type_extra = meta_data.get('content-type', ('', {})) content_type = content_type.strip() - charset = content_type_extra.get("charset") + charset = content_type_extra.get('charset') try: - content_length = int(meta_data.get("content-length")[0]) + content_length = int(meta_data.get('content-length')[0]) except (IndexError, TypeError, ValueError): content_length = None @@ -251,40 +231,31 @@ class MultiPartParser: for handler in handlers: try: handler.new_file( - field_name, - file_name, - content_type, - content_length, - charset, - content_type_extra, + field_name, file_name, content_type, + content_length, charset, content_type_extra, ) except StopFutureHandlers: break for chunk in field_stream: - if transfer_encoding == "base64": + if transfer_encoding == 'base64': # We only special-case base64 transfer encoding - # We should always decode base64 chunks by - # multiple of 4, ignoring whitespace. + # We should always decode base64 chunks by multiple of 4, + # ignoring whitespace. stripped_chunk = b"".join(chunk.split()) remaining = len(stripped_chunk) % 4 while remaining != 0: over_chunk = field_stream.read(4 - remaining) - if not over_chunk: - break stripped_chunk += b"".join(over_chunk.split()) remaining = len(stripped_chunk) % 4 try: chunk = base64.b64decode(stripped_chunk) except Exception as exc: - # Since this is only a chunk, any error is - # an unfixable error. - raise MultiPartParserError( - "Could not decode base64 data." - ) from exc + # Since this is only a chunk, any error is an unfixable error. + raise MultiPartParserError("Could not decode base64 data.") from exc for i, handler in enumerate(handlers): chunk_length = len(chunk) @@ -330,10 +301,7 @@ class MultiPartParser: file_obj = handler.file_complete(counters[i]) if file_obj: # If it returns a file object, then set the files dict. - self._files.appendlist( - force_str(old_field_name, self._encoding, errors="replace"), - file_obj, - ) + self._files.appendlist(force_str(old_field_name, self._encoding, errors='replace'), file_obj) break def sanitize_file_name(self, file_name): @@ -350,10 +318,10 @@ class MultiPartParser: resulting filename should still be considered as untrusted user input. """ file_name = html.unescape(file_name) - file_name = file_name.rsplit("/")[-1] - file_name = file_name.rsplit("\\")[-1] + file_name = file_name.rsplit('/')[-1] + file_name = file_name.rsplit('\\')[-1] - if file_name in {"", ".", ".."}: + if file_name in {'', '.', '..'}: return None return file_name @@ -362,10 +330,9 @@ class MultiPartParser: def _close_files(self): # Free up all file handles. # FIXME: this currently assumes that upload handlers store the file as 'file' - # We should document that... - # (Maybe add handler.free_file to complement new_file) + # We should document that... (Maybe add handler.free_file to complement new_file) for handler in self._upload_handlers: - if hasattr(handler, "file"): + if hasattr(handler, 'file'): handler.file.close() @@ -377,7 +344,6 @@ class LazyStream: LazyStream object will support iteration, reading, and keeping a "look-back" variable in case you need to "unget" some bytes. """ - def __init__(self, producer, length=None): """ Every LazyStream must have a producer when instantiated. @@ -387,7 +353,7 @@ class LazyStream: """ self._producer = producer self._empty = False - self._leftover = b"" + self._leftover = b'' self.length = length self.position = 0 self._remaining = length @@ -401,14 +367,14 @@ class LazyStream: remaining = self._remaining if size is None else size # do the whole thing in one shot if no limit was provided. if remaining is None: - yield b"".join(self) + yield b''.join(self) return # otherwise do some bookkeeping to return exactly enough # of the stream and stashing any extra content we get from # the producer while remaining != 0: - assert remaining > 0, "remaining bytes to read should never go negative" + assert remaining > 0, 'remaining bytes to read should never go negative' try: chunk = next(self) @@ -420,7 +386,7 @@ class LazyStream: remaining -= len(emitting) yield emitting - return b"".join(parts()) + return b''.join(parts()) def __next__(self): """ @@ -431,7 +397,7 @@ class LazyStream: """ if self._leftover: output = self._leftover - self._leftover = b"" + self._leftover = b'' else: output = next(self._producer) self._unget_history = [] @@ -472,13 +438,10 @@ class LazyStream: maliciously-malformed MIME request. """ self._unget_history = [num_bytes] + self._unget_history[:49] - number_equal = len( - [ - current_number - for current_number in self._unget_history - if current_number == num_bytes - ] - ) + number_equal = len([ + current_number for current_number in self._unget_history + if current_number == num_bytes + ]) if number_equal > 40: raise SuspiciousMultipartForm( @@ -493,7 +456,6 @@ class ChunkIter: An iterable that will yield chunks of data. Given a file-like object as the constructor, yield chunks of read operations from that object. """ - def __init__(self, flo, chunk_size=64 * 1024): self.flo = flo self.chunk_size = chunk_size @@ -516,7 +478,6 @@ class InterBoundaryIter: """ A Producer that will iterate over boundaries. """ - def __init__(self, stream, boundary): self._stream = stream self._boundary = boundary @@ -583,7 +544,7 @@ class BoundaryIter: if not chunks: raise StopIteration() - chunk = b"".join(chunks) + chunk = b''.join(chunks) boundary = self._find_boundary(chunk) if boundary: @@ -619,10 +580,10 @@ class BoundaryIter: next = index + len(self._boundary) # backup over CRLF last = max(0, end - 1) - if data[last : last + 1] == b"\n": + if data[last:last + 1] == b'\n': end -= 1 last = max(0, end - 1) - if data[last : last + 1] == b"\r": + if data[last:last + 1] == b'\r': end -= 1 return end, next @@ -648,12 +609,12 @@ def parse_boundary_stream(stream, max_header_size): # 'find' returns the top of these four bytes, so we'll # need to munch them later to prevent them from polluting # the payload. - header_end = chunk.find(b"\r\n\r\n") + header_end = chunk.find(b'\r\n\r\n') def _parse_header(line): main_value_pair, params = parse_header(line) try: - name, value = main_value_pair.split(":", 1) + name, value = main_value_pair.split(':', 1) except ValueError: raise ValueError("Invalid header: %r" % line) return name, (value, params) @@ -668,13 +629,13 @@ def parse_boundary_stream(stream, max_header_size): # here we place any excess chunk back onto the stream, as # well as throwing away the CRLFCRLF bytes from above. - stream.unget(chunk[header_end + 4 :]) + stream.unget(chunk[header_end + 4:]) TYPE = RAW outdict = {} # Eliminate blank lines - for line in header.split(b"\r\n"): + for line in header.split(b'\r\n'): # This terminology ("main value" and "dictionary of # parameters") is from the Python docs. try: @@ -682,9 +643,9 @@ def parse_boundary_stream(stream, max_header_size): except ValueError: continue - if name == "content-disposition": + if name == 'content-disposition': TYPE = FIELD - if params.get("filename"): + if params.get('filename'): TYPE = FILE outdict[name] = value, params @@ -698,7 +659,7 @@ def parse_boundary_stream(stream, max_header_size): class Parser: def __init__(self, stream, boundary): self._stream = stream - self._separator = b"--" + boundary + self._separator = b'--' + boundary def __iter__(self): boundarystream = InterBoundaryIter(self._stream, self._separator) @@ -714,24 +675,24 @@ def parse_header(line): Input (line): bytes, output: str for key/name, bytes for values which will be decoded later. """ - plist = _parse_header_params(b";" + line) - key = plist.pop(0).lower().decode("ascii") + plist = _parse_header_params(b';' + line) + key = plist.pop(0).lower().decode('ascii') pdict = {} for p in plist: - i = p.find(b"=") + i = p.find(b'=') if i >= 0: has_encoding = False - name = p[:i].strip().lower().decode("ascii") - if name.endswith("*"): + name = p[:i].strip().lower().decode('ascii') + if name.endswith('*'): # Lang/encoding embedded in the value (like "filename*=UTF-8''file.ext") # http://tools.ietf.org/html/rfc2231#section-4 name = name[:-1] if p.count(b"'") == 2: has_encoding = True - value = p[i + 1 :].strip() + value = p[i + 1:].strip() if len(value) >= 2 and value[:1] == value[-1:] == b'"': value = value[1:-1] - value = value.replace(b"\\\\", b"\\").replace(b'\\"', b'"') + value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"') if has_encoding: encoding, lang, value = value.split(b"'") value = unquote(value.decode(), encoding=encoding.decode()) @@ -741,11 +702,11 @@ def parse_header(line): def _parse_header_params(s): plist = [] - while s[:1] == b";": + while s[:1] == b';': s = s[1:] - end = s.find(b";") + end = s.find(b';') while end > 0 and s.count(b'"', 0, end) % 2: - end = s.find(b";", end + 1) + end = s.find(b';', end + 1) if end < 0: end = len(s) f = s[:end] diff --git a/venv/Lib/site-packages/django/http/request.py b/venv/Lib/site-packages/django/http/request.py index f32d57b..195341e 100644 --- a/venv/Lib/site-packages/django/http/request.py +++ b/venv/Lib/site-packages/django/http/request.py @@ -1,6 +1,7 @@ import cgi import codecs import copy +import warnings from io import BytesIO from itertools import chain from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit @@ -8,29 +9,35 @@ from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit from django.conf import settings from django.core import signing from django.core.exceptions import ( - DisallowedHost, - ImproperlyConfigured, - RequestDataTooBig, - TooManyFieldsSent, + DisallowedHost, ImproperlyConfigured, RequestDataTooBig, TooManyFieldsSent, ) from django.core.files import uploadhandler from django.http.multipartparser import MultiPartParser, MultiPartParserError from django.utils.datastructures import ( - CaseInsensitiveMapping, - ImmutableList, - MultiValueDict, + CaseInsensitiveMapping, ImmutableList, MultiValueDict, ) +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.encoding import escape_uri_path, iri_to_uri from django.utils.functional import cached_property from django.utils.http import is_same_domain +from django.utils.inspect import func_supports_parameter from django.utils.regex_helper import _lazy_re_compile from .multipartparser import parse_header +# TODO: Remove when dropping support for PY37. inspect.signature() is used to +# detect whether the max_num_fields argument is available as this security fix +# was backported to Python 3.6.8 and 3.7.2, and may also have been applied by +# downstream package maintainers to other versions in their repositories. +if ( + not func_supports_parameter(parse_qsl, 'max_num_fields') or + not func_supports_parameter(parse_qsl, 'separator') +): + from django.utils.http import parse_qsl + + RAISE_ERROR = object() -host_validation_re = _lazy_re_compile( - r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9\.:]+\])(:\d+)?$" -) +host_validation_re = _lazy_re_compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9\.:]+\])(:\d+)?$") class UnreadablePostError(OSError): @@ -43,7 +50,6 @@ class RawPostDataException(Exception): multipart/* POST data if it has been accessed via POST, FILES, etc.. """ - pass @@ -65,8 +71,8 @@ class HttpRequest: self.META = {} self.FILES = MultiValueDict() - self.path = "" - self.path_info = "" + self.path = '' + self.path_info = '' self.method = None self.resolver_match = None self.content_type = None @@ -74,12 +80,8 @@ class HttpRequest: def __repr__(self): if self.method is None or not self.get_full_path(): - return "<%s>" % self.__class__.__name__ - return "<%s: %s %r>" % ( - self.__class__.__name__, - self.method, - self.get_full_path(), - ) + return '<%s>' % self.__class__.__name__ + return '<%s: %s %r>' % (self.__class__.__name__, self.method, self.get_full_path()) @cached_property def headers(self): @@ -88,25 +90,24 @@ class HttpRequest: @cached_property def accepted_types(self): """Return a list of MediaType instances.""" - return parse_accept_header(self.headers.get("Accept", "*/*")) + return parse_accept_header(self.headers.get('Accept', '*/*')) def accepts(self, media_type): return any( - accepted_type.match(media_type) for accepted_type in self.accepted_types + accepted_type.match(media_type) + for accepted_type in self.accepted_types ) def _set_content_type_params(self, meta): """Set content_type, content_params, and encoding.""" - self.content_type, self.content_params = cgi.parse_header( - meta.get("CONTENT_TYPE", "") - ) - if "charset" in self.content_params: + self.content_type, self.content_params = cgi.parse_header(meta.get('CONTENT_TYPE', '')) + if 'charset' in self.content_params: try: - codecs.lookup(self.content_params["charset"]) + codecs.lookup(self.content_params['charset']) except LookupError: pass else: - self.encoding = self.content_params["charset"] + self.encoding = self.content_params['charset'] def _get_raw_host(self): """ @@ -114,16 +115,17 @@ class HttpRequest: allowed hosts protection, so may return an insecure host. """ # We try three options, in order of decreasing preference. - if settings.USE_X_FORWARDED_HOST and ("HTTP_X_FORWARDED_HOST" in self.META): - host = self.META["HTTP_X_FORWARDED_HOST"] - elif "HTTP_HOST" in self.META: - host = self.META["HTTP_HOST"] + if settings.USE_X_FORWARDED_HOST and ( + 'HTTP_X_FORWARDED_HOST' in self.META): + host = self.META['HTTP_X_FORWARDED_HOST'] + elif 'HTTP_HOST' in self.META: + host = self.META['HTTP_HOST'] else: # Reconstruct the host using the algorithm from PEP 333. - host = self.META["SERVER_NAME"] + host = self.META['SERVER_NAME'] server_port = self.get_port() - if server_port != ("443" if self.is_secure() else "80"): - host = "%s:%s" % (host, server_port) + if server_port != ('443' if self.is_secure() else '80'): + host = '%s:%s' % (host, server_port) return host def get_host(self): @@ -133,7 +135,7 @@ class HttpRequest: # Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True. allowed_hosts = settings.ALLOWED_HOSTS if settings.DEBUG and not allowed_hosts: - allowed_hosts = [".localhost", "127.0.0.1", "[::1]"] + allowed_hosts = ['.localhost', '127.0.0.1', '[::1]'] domain, port = split_domain_port(host) if domain and validate_host(domain, allowed_hosts): @@ -143,17 +145,15 @@ class HttpRequest: if domain: msg += " You may need to add %r to ALLOWED_HOSTS." % domain else: - msg += ( - " The domain name provided is not valid according to RFC 1034/1035." - ) + msg += " The domain name provided is not valid according to RFC 1034/1035." raise DisallowedHost(msg) def get_port(self): """Return the port number for the request as a string.""" - if settings.USE_X_FORWARDED_PORT and "HTTP_X_FORWARDED_PORT" in self.META: - port = self.META["HTTP_X_FORWARDED_PORT"] + if settings.USE_X_FORWARDED_PORT and 'HTTP_X_FORWARDED_PORT' in self.META: + port = self.META['HTTP_X_FORWARDED_PORT'] else: - port = self.META["SERVER_PORT"] + port = self.META['SERVER_PORT'] return str(port) def get_full_path(self, force_append_slash=False): @@ -165,15 +165,13 @@ class HttpRequest: def _get_full_path(self, path, force_append_slash): # RFC 3986 requires query string arguments to be in the ASCII range. # Rather than crash if this doesn't happen, we encode defensively. - return "%s%s%s" % ( + return '%s%s%s' % ( escape_uri_path(path), - "/" if force_append_slash and not path.endswith("/") else "", - ("?" + iri_to_uri(self.META.get("QUERY_STRING", ""))) - if self.META.get("QUERY_STRING", "") - else "", + '/' if force_append_slash and not path.endswith('/') else '', + ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else '' ) - def get_signed_cookie(self, key, default=RAISE_ERROR, salt="", max_age=None): + def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None): """ Attempt to return a signed cookie. If the signature fails or the cookie has expired, raise an exception, unless the `default` argument @@ -188,8 +186,7 @@ class HttpRequest: raise try: value = signing.get_cookie_signer(salt=key + salt).unsign( - cookie_value, max_age=max_age - ) + cookie_value, max_age=max_age) except signing.BadSignature: if default is not RAISE_ERROR: return default @@ -197,6 +194,17 @@ class HttpRequest: raise return value + def get_raw_uri(self): + """ + Return an absolute URI from variables available in this request. Skip + allowed hosts protection, so may return insecure URI. + """ + return '{scheme}://{host}{path}'.format( + scheme=self.scheme, + host=self._get_raw_host(), + path=self.get_full_path(), + ) + def build_absolute_uri(self, location=None): """ Build an absolute URI from the location and the variables available in @@ -209,7 +217,7 @@ class HttpRequest: if location is None: # Make it an absolute url (but schemeless and domainless) for the # edge case that the path starts with '//'. - location = "//%s" % self.get_full_path() + location = '//%s' % self.get_full_path() else: # Coerce lazy locations. location = str(location) @@ -218,17 +226,12 @@ class HttpRequest: # Handle the simple, most common case. If the location is absolute # and a scheme or host (netloc) isn't provided, skip an expensive # urljoin() as long as no path segments are '.' or '..'. - if ( - bits.path.startswith("/") - and not bits.scheme - and not bits.netloc - and "/./" not in bits.path - and "/../" not in bits.path - ): + if (bits.path.startswith('/') and not bits.scheme and not bits.netloc and + '/./' not in bits.path and '/../' not in bits.path): # If location starts with '//' but has no netloc, reuse the # schema and netloc from the current request. Strip the double # slashes and continue as if it wasn't specified. - if location.startswith("//"): + if location.startswith('//'): location = location[2:] location = self._current_scheme_host + location else: @@ -240,14 +243,14 @@ class HttpRequest: @cached_property def _current_scheme_host(self): - return "{}://{}".format(self.scheme, self.get_host()) + return '{}://{}'.format(self.scheme, self.get_host()) def _get_scheme(self): """ Hook for subclasses like WSGIRequest to implement. Return 'http' by default. """ - return "http" + return 'http' @property def scheme(self): @@ -256,16 +259,24 @@ class HttpRequest: header, secure_value = settings.SECURE_PROXY_SSL_HEADER except ValueError: raise ImproperlyConfigured( - "The SECURE_PROXY_SSL_HEADER setting must be a tuple containing " - "two values." + 'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.' ) header_value = self.META.get(header) if header_value is not None: - return "https" if header_value == secure_value else "http" + return 'https' if header_value == secure_value else 'http' return self._get_scheme() def is_secure(self): - return self.scheme == "https" + return self.scheme == 'https' + + def is_ajax(self): + warnings.warn( + 'request.is_ajax() is deprecated. See Django 3.1 release notes ' + 'for more details about this deprecation.', + RemovedInDjango40Warning, + stacklevel=2, + ) + return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' @property def encoding(self): @@ -279,16 +290,14 @@ class HttpRequest: next access (so that it is decoded correctly). """ self._encoding = val - if hasattr(self, "GET"): + if hasattr(self, 'GET'): del self.GET - if hasattr(self, "_post"): + if hasattr(self, '_post'): del self._post def _initialize_handlers(self): - self._upload_handlers = [ - uploadhandler.load_handler(handler, self) - for handler in settings.FILE_UPLOAD_HANDLERS - ] + self._upload_handlers = [uploadhandler.load_handler(handler, self) + for handler in settings.FILE_UPLOAD_HANDLERS] @property def upload_handlers(self): @@ -299,42 +308,29 @@ class HttpRequest: @upload_handlers.setter def upload_handlers(self, upload_handlers): - if hasattr(self, "_files"): - raise AttributeError( - "You cannot set the upload handlers after the upload has been " - "processed." - ) + if hasattr(self, '_files'): + raise AttributeError("You cannot set the upload handlers after the upload has been processed.") self._upload_handlers = upload_handlers def parse_file_upload(self, META, post_data): """Return a tuple of (POST QueryDict, FILES MultiValueDict).""" self.upload_handlers = ImmutableList( self.upload_handlers, - warning=( - "You cannot alter upload handlers after the upload has been " - "processed." - ), + warning="You cannot alter upload handlers after the upload has been processed." ) parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) return parser.parse() @property def body(self): - if not hasattr(self, "_body"): + if not hasattr(self, '_body'): if self._read_started: - raise RawPostDataException( - "You cannot access body after reading from request's data stream" - ) + raise RawPostDataException("You cannot access body after reading from request's data stream") # Limit the maximum request data size that will be handled in-memory. - if ( - settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None - and int(self.META.get("CONTENT_LENGTH") or 0) - > settings.DATA_UPLOAD_MAX_MEMORY_SIZE - ): - raise RequestDataTooBig( - "Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE." - ) + if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and + int(self.META.get('CONTENT_LENGTH') or 0) > settings.DATA_UPLOAD_MAX_MEMORY_SIZE): + raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.') try: self._body = self.read() @@ -349,18 +345,15 @@ class HttpRequest: def _load_post_and_files(self): """Populate self._post and self._files if the content-type is a form type""" - if self.method != "POST": - self._post, self._files = ( - QueryDict(encoding=self._encoding), - MultiValueDict(), - ) + if self.method != 'POST': + self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict() return - if self._read_started and not hasattr(self, "_body"): + if self._read_started and not hasattr(self, '_body'): self._mark_post_parse_error() return - if self.content_type == "multipart/form-data": - if hasattr(self, "_body"): + if self.content_type == 'multipart/form-data': + if hasattr(self, '_body'): # Use already read data data = BytesIO(self._body) else: @@ -374,19 +367,13 @@ class HttpRequest: # attempts to parse POST data again. self._mark_post_parse_error() raise - elif self.content_type == "application/x-www-form-urlencoded": - self._post, self._files = ( - QueryDict(self.body, encoding=self._encoding), - MultiValueDict(), - ) + elif self.content_type == 'application/x-www-form-urlencoded': + self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() else: - self._post, self._files = ( - QueryDict(encoding=self._encoding), - MultiValueDict(), - ) + self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict() def close(self): - if hasattr(self, "_files"): + if hasattr(self, '_files'): for f in chain.from_iterable(list_[1] for list_ in self._files.lists()): f.close() @@ -413,16 +400,16 @@ class HttpRequest: raise UnreadablePostError(*e.args) from e def __iter__(self): - return iter(self.readline, b"") + return iter(self.readline, b'') def readlines(self): return list(self) class HttpHeaders(CaseInsensitiveMapping): - HTTP_PREFIX = "HTTP_" + HTTP_PREFIX = 'HTTP_' # PEP 333 gives two headers which aren't prepended with HTTP_. - UNPREFIXED_HEADERS = {"CONTENT_TYPE", "CONTENT_LENGTH"} + UNPREFIXED_HEADERS = {'CONTENT_TYPE', 'CONTENT_LENGTH'} def __init__(self, environ): headers = {} @@ -434,15 +421,15 @@ class HttpHeaders(CaseInsensitiveMapping): def __getitem__(self, key): """Allow header lookup using underscores in place of hyphens.""" - return super().__getitem__(key.replace("_", "-")) + return super().__getitem__(key.replace('_', '-')) @classmethod def parse_header_name(cls, header): if header.startswith(cls.HTTP_PREFIX): - header = header[len(cls.HTTP_PREFIX) :] + header = header[len(cls.HTTP_PREFIX):] elif header not in cls.UNPREFIXED_HEADERS: return None - return header.replace("_", "-").title() + return header.replace('_', '-').title() class QueryDict(MultiValueDict): @@ -468,11 +455,11 @@ class QueryDict(MultiValueDict): def __init__(self, query_string=None, mutable=False, encoding=None): super().__init__() self.encoding = encoding or settings.DEFAULT_CHARSET - query_string = query_string or "" + query_string = query_string or '' parse_qsl_kwargs = { - "keep_blank_values": True, - "encoding": self.encoding, - "max_num_fields": settings.DATA_UPLOAD_MAX_NUMBER_FIELDS, + 'keep_blank_values': True, + 'encoding': self.encoding, + 'max_num_fields': settings.DATA_UPLOAD_MAX_NUMBER_FIELDS, } if isinstance(query_string, bytes): # query_string normally contains URL-encoded data, a subset of ASCII. @@ -480,7 +467,7 @@ class QueryDict(MultiValueDict): query_string = query_string.decode(self.encoding) except UnicodeDecodeError: # ... but some user agents are misbehaving :-( - query_string = query_string.decode("iso-8859-1") + query_string = query_string.decode('iso-8859-1') try: for key, value in parse_qsl(query_string, **parse_qsl_kwargs): self.appendlist(key, value) @@ -490,18 +477,18 @@ class QueryDict(MultiValueDict): # the exception was raised by exceeding the value of max_num_fields # instead of fragile checks of exception message strings. raise TooManyFieldsSent( - "The number of GET/POST parameters exceeded " - "settings.DATA_UPLOAD_MAX_NUMBER_FIELDS." + 'The number of GET/POST parameters exceeded ' + 'settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.' ) from e self._mutable = mutable @classmethod - def fromkeys(cls, iterable, value="", mutable=False, encoding=None): + def fromkeys(cls, iterable, value='', mutable=False, encoding=None): """ Return a new QueryDict with keys (may be repeated) from an iterable and values from value. """ - q = cls("", mutable=True, encoding=encoding) + q = cls('', mutable=True, encoding=encoding) for key in iterable: q.appendlist(key, value) if not mutable: @@ -533,13 +520,13 @@ class QueryDict(MultiValueDict): super().__delitem__(key) def __copy__(self): - result = self.__class__("", mutable=True, encoding=self.encoding) + result = self.__class__('', mutable=True, encoding=self.encoding) for key, value in self.lists(): result.setlist(key, value) return result def __deepcopy__(self, memo): - result = self.__class__("", mutable=True, encoding=self.encoding) + result = self.__class__('', mutable=True, encoding=self.encoding) memo[id(self)] = result for key, value in self.lists(): result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo)) @@ -601,50 +588,48 @@ class QueryDict(MultiValueDict): safe = safe.encode(self.encoding) def encode(k, v): - return "%s=%s" % ((quote(k, safe), quote(v, safe))) - + return '%s=%s' % ((quote(k, safe), quote(v, safe))) else: - def encode(k, v): return urlencode({k: v}) - for k, list_ in self.lists(): output.extend( encode(k.encode(self.encoding), str(v).encode(self.encoding)) for v in list_ ) - return "&".join(output) + return '&'.join(output) class MediaType: def __init__(self, media_type_raw_line): full_type, self.params = parse_header( - media_type_raw_line.encode("ascii") if media_type_raw_line else b"" + media_type_raw_line.encode('ascii') if media_type_raw_line else b'' ) - self.main_type, _, self.sub_type = full_type.partition("/") + self.main_type, _, self.sub_type = full_type.partition('/') def __str__(self): - params_str = "".join( - "; %s=%s" % (k, v.decode("ascii")) for k, v in self.params.items() + params_str = ''.join( + '; %s=%s' % (k, v.decode('ascii')) + for k, v in self.params.items() ) - return "%s%s%s" % ( + return '%s%s%s' % ( self.main_type, - ("/%s" % self.sub_type) if self.sub_type else "", + ('/%s' % self.sub_type) if self.sub_type else '', params_str, ) def __repr__(self): - return "<%s: %s>" % (self.__class__.__qualname__, self) + return '<%s: %s>' % (self.__class__.__qualname__, self) @property def is_all_types(self): - return self.main_type == "*" and self.sub_type == "*" + return self.main_type == '*' and self.sub_type == '*' def match(self, other): if self.is_all_types: return True other = MediaType(other) - if self.main_type == other.main_type and self.sub_type in {"*", other.sub_type}: + if self.main_type == other.main_type and self.sub_type in {'*', other.sub_type}: return True return False @@ -661,7 +646,7 @@ def bytes_to_text(s, encoding): Return any non-bytes objects without change. """ if isinstance(s, bytes): - return str(s, encoding, "replace") + return str(s, encoding, 'replace') else: return s @@ -676,15 +661,15 @@ def split_domain_port(host): host = host.lower() if not host_validation_re.match(host): - return "", "" + return '', '' - if host[-1] == "]": + if host[-1] == ']': # It's an IPv6 address without a port. - return host, "" - bits = host.rsplit(":", 1) - domain, port = bits if len(bits) == 2 else (bits[0], "") + return host, '' + bits = host.rsplit(':', 1) + domain, port = bits if len(bits) == 2 else (bits[0], '') # Remove a trailing dot (if present) from the domain. - domain = domain[:-1] if domain.endswith(".") else domain + domain = domain[:-1] if domain.endswith('.') else domain return domain, port @@ -703,10 +688,8 @@ def validate_host(host, allowed_hosts): Return ``True`` for a valid host, ``False`` otherwise. """ - return any( - pattern == "*" or is_same_domain(host, pattern) for pattern in allowed_hosts - ) + return any(pattern == '*' or is_same_domain(host, pattern) for pattern in allowed_hosts) def parse_accept_header(header): - return [MediaType(token) for token in header.split(",") if token.strip()] + return [MediaType(token) for token in header.split(',') if token.strip()] diff --git a/venv/Lib/site-packages/django/http/response.py b/venv/Lib/site-packages/django/http/response.py index 7fbec9d..1c22eda 100644 --- a/venv/Lib/site-packages/django/http/response.py +++ b/venv/Lib/site-packages/django/http/response.py @@ -17,16 +17,13 @@ from django.core.serializers.json import DjangoJSONEncoder from django.http.cookie import SimpleCookie from django.utils import timezone from django.utils.datastructures import ( - CaseInsensitiveMapping, - _destruct_iterable_mapping_values, + CaseInsensitiveMapping, _destruct_iterable_mapping_values, ) from django.utils.encoding import iri_to_uri from django.utils.http import http_date from django.utils.regex_helper import _lazy_re_compile -_charset_from_content_type_re = _lazy_re_compile( - r";\s*charset=(?P<charset>[^\s;]+)", re.I -) +_charset_from_content_type_re = _lazy_re_compile(r';\s*charset=(?P<charset>[^\s;]+)', re.I) class ResponseHeaders(CaseInsensitiveMapping): @@ -49,12 +46,11 @@ class ResponseHeaders(CaseInsensitiveMapping): """ if not isinstance(value, (bytes, str)): value = str(value) - if (isinstance(value, bytes) and (b"\n" in value or b"\r" in value)) or ( - isinstance(value, str) and ("\n" in value or "\r" in value) + if ( + (isinstance(value, bytes) and (b'\n' in value or b'\r' in value)) or + (isinstance(value, str) and ('\n' in value or '\r' in value)) ): - raise BadHeaderError( - "Header values can't contain newlines (got %r)" % value - ) + raise BadHeaderError("Header values can't contain newlines (got %r)" % value) try: if isinstance(value, str): # Ensure string is valid in given charset @@ -64,9 +60,9 @@ class ResponseHeaders(CaseInsensitiveMapping): value = value.decode(charset) except UnicodeError as e: if mime_encode: - value = Header(value, "utf-8", maxlinelen=sys.maxsize).encode() + value = Header(value, 'utf-8', maxlinelen=sys.maxsize).encode() else: - e.reason += ", HTTP response headers must be in %s format" % charset + e.reason += ', HTTP response headers must be in %s format' % charset raise return value @@ -74,8 +70,8 @@ class ResponseHeaders(CaseInsensitiveMapping): self.pop(key) def __setitem__(self, key, value): - key = self._convert_to_charset(key, "ascii") - value = self._convert_to_charset(value, "latin-1", mime_encode=True) + key = self._convert_to_charset(key, 'ascii') + value = self._convert_to_charset(value, 'latin-1', mime_encode=True) self._store[key.lower()] = (key, value) def pop(self, key, default=None): @@ -100,20 +96,18 @@ class HttpResponseBase: status_code = 200 - def __init__( - self, content_type=None, status=None, reason=None, charset=None, headers=None - ): + def __init__(self, content_type=None, status=None, reason=None, charset=None, headers=None): self.headers = ResponseHeaders(headers or {}) self._charset = charset - if content_type and "Content-Type" in self.headers: + if content_type and 'Content-Type' in self.headers: raise ValueError( "'headers' must not contain 'Content-Type' when the " "'content_type' parameter is provided." ) - if "Content-Type" not in self.headers: + if 'Content-Type' not in self.headers: if content_type is None: - content_type = "text/html; charset=%s" % self.charset - self.headers["Content-Type"] = content_type + content_type = 'text/html; charset=%s' % self.charset + self.headers['Content-Type'] = content_type self._resource_closers = [] # This parameter is set by the handler. It's necessary to preserve the # historical behavior of request_finished. @@ -124,10 +118,10 @@ class HttpResponseBase: try: self.status_code = int(status) except (ValueError, TypeError): - raise TypeError("HTTP status code must be an integer.") + raise TypeError('HTTP status code must be an integer.') if not 100 <= self.status_code <= 599: - raise ValueError("HTTP status code must be an integer from 100 to 599.") + raise ValueError('HTTP status code must be an integer from 100 to 599.') self._reason_phrase = reason @property @@ -136,7 +130,7 @@ class HttpResponseBase: return self._reason_phrase # Leave self._reason_phrase unset in order to use the default # reason phrase for status code. - return responses.get(self.status_code, "Unknown Status Code") + return responses.get(self.status_code, 'Unknown Status Code') @reason_phrase.setter def reason_phrase(self, value): @@ -146,11 +140,11 @@ class HttpResponseBase: def charset(self): if self._charset is not None: return self._charset - content_type = self.get("Content-Type", "") + content_type = self.get('Content-Type', '') matched = _charset_from_content_type_re.search(content_type) if matched: # Extract the charset and strip its double quotes - return matched["charset"].replace('"', "") + return matched['charset'].replace('"', '') return settings.DEFAULT_CHARSET @charset.setter @@ -159,22 +153,20 @@ class HttpResponseBase: def serialize_headers(self): """HTTP headers as a bytestring.""" - return b"\r\n".join( - [ - key.encode("ascii") + b": " + value.encode("latin-1") - for key, value in self.headers.items() - ] - ) + def to_bytes(val, encoding): + return val if isinstance(val, bytes) else val.encode(encoding) + + headers = [ + (to_bytes(key, 'ascii') + b': ' + to_bytes(value, 'latin-1')) + for key, value in self.headers.items() + ] + return b'\r\n'.join(headers) __bytes__ = serialize_headers @property def _content_type_for_repr(self): - return ( - ', "%s"' % self.headers["Content-Type"] - if "Content-Type" in self.headers - else "" - ) + return ', "%s"' % self.headers['Content-Type'] if 'Content-Type' in self.headers else '' def __setitem__(self, header, value): self.headers[header] = value @@ -197,18 +189,8 @@ class HttpResponseBase: def get(self, header, alternate=None): return self.headers.get(header, alternate) - def set_cookie( - self, - key, - value="", - max_age=None, - expires=None, - path="/", - domain=None, - secure=False, - httponly=False, - samesite=None, - ): + def set_cookie(self, key, value='', max_age=None, expires=None, path='/', + domain=None, secure=False, httponly=False, samesite=None): """ Set a cookie. @@ -221,9 +203,9 @@ class HttpResponseBase: self.cookies[key] = value if expires is not None: if isinstance(expires, datetime.datetime): - if timezone.is_naive(expires): - expires = timezone.make_aware(expires, timezone.utc) - delta = expires - datetime.datetime.now(tz=timezone.utc) + if timezone.is_aware(expires): + expires = timezone.make_naive(expires, timezone.utc) + delta = expires - expires.utcnow() # Add one second so the date matches exactly (a fraction of # time gets lost between converting to a timedelta and # then the date string). @@ -232,51 +214,47 @@ class HttpResponseBase: expires = None max_age = max(0, delta.days * 86400 + delta.seconds) else: - self.cookies[key]["expires"] = expires + self.cookies[key]['expires'] = expires else: - self.cookies[key]["expires"] = "" + self.cookies[key]['expires'] = '' if max_age is not None: - self.cookies[key]["max-age"] = int(max_age) + self.cookies[key]['max-age'] = int(max_age) # IE requires expires, so set it if hasn't been already. if not expires: - self.cookies[key]["expires"] = http_date(time.time() + max_age) + self.cookies[key]['expires'] = http_date(time.time() + max_age) if path is not None: - self.cookies[key]["path"] = path + self.cookies[key]['path'] = path if domain is not None: - self.cookies[key]["domain"] = domain + self.cookies[key]['domain'] = domain if secure: - self.cookies[key]["secure"] = True + self.cookies[key]['secure'] = True if httponly: - self.cookies[key]["httponly"] = True + self.cookies[key]['httponly'] = True if samesite: - if samesite.lower() not in ("lax", "none", "strict"): + if samesite.lower() not in ('lax', 'none', 'strict'): raise ValueError('samesite must be "lax", "none", or "strict".') - self.cookies[key]["samesite"] = samesite + self.cookies[key]['samesite'] = samesite def setdefault(self, key, value): """Set a header unless it has already been set.""" self.headers.setdefault(key, value) - def set_signed_cookie(self, key, value, salt="", **kwargs): + def set_signed_cookie(self, key, value, salt='', **kwargs): value = signing.get_cookie_signer(salt=key + salt).sign(value) return self.set_cookie(key, value, **kwargs) - def delete_cookie(self, key, path="/", domain=None, samesite=None): + def delete_cookie(self, key, path='/', domain=None, samesite=None): # Browsers can ignore the Set-Cookie header if the cookie doesn't use # the secure flag and: # - the cookie name starts with "__Host-" or "__Secure-", or # - the samesite is "none". - secure = key.startswith(("__Secure-", "__Host-")) or ( - samesite and samesite.lower() == "none" + secure = ( + key.startswith(('__Secure-', '__Host-')) or + (samesite and samesite.lower() == 'none') ) self.set_cookie( - key, - max_age=0, - path=path, - domain=domain, - secure=secure, - expires="Thu, 01 Jan 1970 00:00:00 GMT", - samesite=samesite, + key, max_age=0, path=path, domain=domain, secure=secure, + expires='Thu, 01 Jan 1970 00:00:00 GMT', samesite=samesite, ) # Common methods used by subclasses @@ -314,15 +292,13 @@ class HttpResponseBase: signals.request_finished.send(sender=self._handler_class) def write(self, content): - raise OSError("This %s instance is not writable" % self.__class__.__name__) + raise OSError('This %s instance is not writable' % self.__class__.__name__) def flush(self): pass def tell(self): - raise OSError( - "This %s instance cannot tell its position" % self.__class__.__name__ - ) + raise OSError('This %s instance cannot tell its position' % self.__class__.__name__) # These methods partially implement a stream-like object interface. # See https://docs.python.org/library/io.html#io.IOBase @@ -337,7 +313,7 @@ class HttpResponseBase: return False def writelines(self, lines): - raise OSError("This %s instance is not writable" % self.__class__.__name__) + raise OSError('This %s instance is not writable' % self.__class__.__name__) class HttpResponse(HttpResponseBase): @@ -349,36 +325,37 @@ class HttpResponse(HttpResponseBase): streaming = False - def __init__(self, content=b"", *args, **kwargs): + def __init__(self, content=b'', *args, **kwargs): super().__init__(*args, **kwargs) # Content is a bytestring. See the `content` property methods. self.content = content def __repr__(self): - return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % { - "cls": self.__class__.__name__, - "status_code": self.status_code, - "content_type": self._content_type_for_repr, + return '<%(cls)s status_code=%(status_code)d%(content_type)s>' % { + 'cls': self.__class__.__name__, + 'status_code': self.status_code, + 'content_type': self._content_type_for_repr, } def serialize(self): """Full HTTP message, including headers, as a bytestring.""" - return self.serialize_headers() + b"\r\n\r\n" + self.content + return self.serialize_headers() + b'\r\n\r\n' + self.content __bytes__ = serialize @property def content(self): - return b"".join(self._container) + return b''.join(self._container) @content.setter def content(self, value): # Consume iterators upon assignment to allow repeated iteration. - if hasattr(value, "__iter__") and not isinstance( - value, (bytes, memoryview, str) + if ( + hasattr(value, '__iter__') and + not isinstance(value, (bytes, memoryview, str)) ): - content = b"".join(self.make_bytes(chunk) for chunk in value) - if hasattr(value, "close"): + content = b''.join(self.make_bytes(chunk) for chunk in value) + if hasattr(value, 'close'): try: value.close() except Exception: @@ -425,13 +402,6 @@ class StreamingHttpResponse(HttpResponseBase): # See the `streaming_content` property methods. self.streaming_content = streaming_content - def __repr__(self): - return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % { - "cls": self.__class__.__qualname__, - "status_code": self.status_code, - "content_type": self._content_type_for_repr, - } - @property def content(self): raise AttributeError( @@ -450,37 +420,36 @@ class StreamingHttpResponse(HttpResponseBase): def _set_streaming_content(self, value): # Ensure we can never iterate on "value" more than once. self._iterator = iter(value) - if hasattr(value, "close"): + if hasattr(value, 'close'): self._resource_closers.append(value.close) def __iter__(self): return self.streaming_content def getvalue(self): - return b"".join(self.streaming_content) + return b''.join(self.streaming_content) class FileResponse(StreamingHttpResponse): """ A streaming HTTP response class optimized for files. """ - block_size = 4096 - def __init__(self, *args, as_attachment=False, filename="", **kwargs): + def __init__(self, *args, as_attachment=False, filename='', **kwargs): self.as_attachment = as_attachment self.filename = filename super().__init__(*args, **kwargs) def _set_streaming_content(self, value): - if not hasattr(value, "read"): + if not hasattr(value, 'read'): self.file_to_stream = None return super()._set_streaming_content(value) self.file_to_stream = filelike = value - if hasattr(filelike, "close"): + if hasattr(filelike, 'close'): self._resource_closers.append(filelike.close) - value = iter(lambda: filelike.read(self.block_size), b"") + value = iter(lambda: filelike.read(self.block_size), b'') self.set_headers(filelike) super()._set_streaming_content(value) @@ -490,70 +459,59 @@ class FileResponse(StreamingHttpResponse): Content-Disposition) based on the `filelike` response content. """ encoding_map = { - "bzip2": "application/x-bzip", - "gzip": "application/gzip", - "xz": "application/x-xz", + 'bzip2': 'application/x-bzip', + 'gzip': 'application/gzip', + 'xz': 'application/x-xz', } - filename = getattr(filelike, "name", None) - filename = ( - filename if (isinstance(filename, str) and filename) else self.filename - ) + filename = getattr(filelike, 'name', None) + filename = filename if (isinstance(filename, str) and filename) else self.filename if os.path.isabs(filename): - self.headers["Content-Length"] = os.path.getsize(filelike.name) - elif hasattr(filelike, "getbuffer"): - self.headers["Content-Length"] = filelike.getbuffer().nbytes + self.headers['Content-Length'] = os.path.getsize(filelike.name) + elif hasattr(filelike, 'getbuffer'): + self.headers['Content-Length'] = filelike.getbuffer().nbytes - if self.headers.get("Content-Type", "").startswith("text/html"): + if self.headers.get('Content-Type', '').startswith('text/html'): if filename: content_type, encoding = mimetypes.guess_type(filename) # Encoding isn't set to prevent browsers from automatically # uncompressing files. content_type = encoding_map.get(encoding, content_type) - self.headers["Content-Type"] = ( - content_type or "application/octet-stream" - ) + self.headers['Content-Type'] = content_type or 'application/octet-stream' else: - self.headers["Content-Type"] = "application/octet-stream" + self.headers['Content-Type'] = 'application/octet-stream' filename = self.filename or os.path.basename(filename) if filename: - disposition = "attachment" if self.as_attachment else "inline" + disposition = 'attachment' if self.as_attachment else 'inline' try: - filename.encode("ascii") + filename.encode('ascii') file_expr = 'filename="{}"'.format(filename) except UnicodeEncodeError: file_expr = "filename*=utf-8''{}".format(quote(filename)) - self.headers["Content-Disposition"] = "{}; {}".format( - disposition, file_expr - ) + self.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr) elif self.as_attachment: - self.headers["Content-Disposition"] = "attachment" + self.headers['Content-Disposition'] = 'attachment' class HttpResponseRedirectBase(HttpResponse): - allowed_schemes = ["http", "https", "ftp"] + allowed_schemes = ['http', 'https', 'ftp'] def __init__(self, redirect_to, *args, **kwargs): super().__init__(*args, **kwargs) - self["Location"] = iri_to_uri(redirect_to) + self['Location'] = iri_to_uri(redirect_to) parsed = urlparse(str(redirect_to)) if parsed.scheme and parsed.scheme not in self.allowed_schemes: - raise DisallowedRedirect( - "Unsafe redirect to URL with protocol '%s'" % parsed.scheme - ) + raise DisallowedRedirect("Unsafe redirect to URL with protocol '%s'" % parsed.scheme) - url = property(lambda self: self["Location"]) + url = property(lambda self: self['Location']) def __repr__(self): - return ( - '<%(cls)s status_code=%(status_code)d%(content_type)s, url="%(url)s">' - % { - "cls": self.__class__.__name__, - "status_code": self.status_code, - "content_type": self._content_type_for_repr, - "url": self.url, - } - ) + return '<%(cls)s status_code=%(status_code)d%(content_type)s, url="%(url)s">' % { + 'cls': self.__class__.__name__, + 'status_code': self.status_code, + 'content_type': self._content_type_for_repr, + 'url': self.url, + } class HttpResponseRedirect(HttpResponseRedirectBase): @@ -569,14 +527,12 @@ class HttpResponseNotModified(HttpResponse): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - del self["content-type"] + del self['content-type'] @HttpResponse.content.setter def content(self, value): if value: - raise AttributeError( - "You cannot set content to a 304 (Not Modified) response" - ) + raise AttributeError("You cannot set content to a 304 (Not Modified) response") self._container = [] @@ -597,14 +553,14 @@ class HttpResponseNotAllowed(HttpResponse): def __init__(self, permitted_methods, *args, **kwargs): super().__init__(*args, **kwargs) - self["Allow"] = ", ".join(permitted_methods) + self['Allow'] = ', '.join(permitted_methods) def __repr__(self): - return "<%(cls)s [%(methods)s] status_code=%(status_code)d%(content_type)s>" % { - "cls": self.__class__.__name__, - "status_code": self.status_code, - "content_type": self._content_type_for_repr, - "methods": self["Allow"], + return '<%(cls)s [%(methods)s] status_code=%(status_code)d%(content_type)s>' % { + 'cls': self.__class__.__name__, + 'status_code': self.status_code, + 'content_type': self._content_type_for_repr, + 'methods': self['Allow'], } @@ -625,7 +581,7 @@ class JsonResponse(HttpResponse): An HTTP response class that consumes data to be serialized to JSON. :param data: Data to be dumped into json. By default only ``dict`` objects - are allowed to be passed due to a security flaw before ECMAScript 5. See + are allowed to be passed due to a security flaw before EcmaScript 5. See the ``safe`` parameter for more information. :param encoder: Should be a json encoder class. Defaults to ``django.core.serializers.json.DjangoJSONEncoder``. @@ -634,21 +590,15 @@ class JsonResponse(HttpResponse): :param json_dumps_params: A dictionary of kwargs passed to json.dumps(). """ - def __init__( - self, - data, - encoder=DjangoJSONEncoder, - safe=True, - json_dumps_params=None, - **kwargs, - ): + def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, + json_dumps_params=None, **kwargs): if safe and not isinstance(data, dict): raise TypeError( - "In order to allow non-dict objects to be serialized set the " - "safe parameter to False." + 'In order to allow non-dict objects to be serialized set the ' + 'safe parameter to False.' ) if json_dumps_params is None: json_dumps_params = {} - kwargs.setdefault("content_type", "application/json") + kwargs.setdefault('content_type', 'application/json') data = json.dumps(data, cls=encoder, **json_dumps_params) super().__init__(content=data, **kwargs) diff --git a/venv/Lib/site-packages/django/middleware/cache.py b/venv/Lib/site-packages/django/middleware/cache.py index f71b27f..97bb199 100644 --- a/venv/Lib/site-packages/django/middleware/cache.py +++ b/venv/Lib/site-packages/django/middleware/cache.py @@ -46,10 +46,7 @@ More details about how the caching works: from django.conf import settings from django.core.cache import DEFAULT_CACHE_ALIAS, caches from django.utils.cache import ( - get_cache_key, - get_max_age, - has_vary_header, - learn_cache_key, + get_cache_key, get_max_age, has_vary_header, learn_cache_key, patch_response_headers, ) from django.utils.deprecation import MiddlewareMixin @@ -64,8 +61,9 @@ class UpdateCacheMiddleware(MiddlewareMixin): UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE so that it'll get called last during the response phase. """ - - def __init__(self, get_response): + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response): + def __init__(self, get_response=None): super().__init__(get_response) self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS self.page_timeout = None @@ -74,7 +72,7 @@ class UpdateCacheMiddleware(MiddlewareMixin): self.cache = caches[self.cache_alias] def _should_update_cache(self, request, response): - return hasattr(request, "_cache_update_cache") and request._cache_update_cache + return hasattr(request, '_cache_update_cache') and request._cache_update_cache def process_response(self, request, response): """Set the cache, if needed.""" @@ -87,15 +85,11 @@ class UpdateCacheMiddleware(MiddlewareMixin): # Don't cache responses that set a user-specific (and maybe security # sensitive) cookie in response to a cookie-less request. - if ( - not request.COOKIES - and response.cookies - and has_vary_header(response, "Cookie") - ): + if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'): return response # Don't cache a response with 'Cache-Control: private' - if "private" in response.get("Cache-Control", ()): + if 'private' in response.get('Cache-Control', ()): return response # Page timeout takes precedence over the "max-age" and the default @@ -112,10 +106,8 @@ class UpdateCacheMiddleware(MiddlewareMixin): return response patch_response_headers(response, timeout) if timeout and response.status_code == 200: - cache_key = learn_cache_key( - request, response, timeout, self.key_prefix, cache=self.cache - ) - if hasattr(response, "render") and callable(response.render): + cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache) + if hasattr(response, 'render') and callable(response.render): response.add_post_render_callback( lambda r: self.cache.set(cache_key, r, timeout) ) @@ -132,8 +124,9 @@ class FetchFromCacheMiddleware(MiddlewareMixin): FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE so that it'll get called last during the request phase. """ - - def __init__(self, get_response): + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response): + def __init__(self, get_response=None): super().__init__(get_response) self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS @@ -144,21 +137,19 @@ class FetchFromCacheMiddleware(MiddlewareMixin): Check whether the page is already cached and return the cached version if available. """ - if request.method not in ("GET", "HEAD"): + if request.method not in ('GET', 'HEAD'): request._cache_update_cache = False return None # Don't bother checking the cache. # try and get the cached GET response - cache_key = get_cache_key(request, self.key_prefix, "GET", cache=self.cache) + cache_key = get_cache_key(request, self.key_prefix, 'GET', cache=self.cache) if cache_key is None: request._cache_update_cache = True return None # No cache information available, need to rebuild. response = self.cache.get(cache_key) # if it wasn't found and we are looking for a HEAD, try looking just for that - if response is None and request.method == "HEAD": - cache_key = get_cache_key( - request, self.key_prefix, "HEAD", cache=self.cache - ) + if response is None and request.method == 'HEAD': + cache_key = get_cache_key(request, self.key_prefix, 'HEAD', cache=self.cache) response = self.cache.get(cache_key) if response is None: @@ -177,8 +168,9 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): Also used as the hook point for the cache decorator, which is generated using the decorator-from-middleware utility. """ - - def __init__(self, get_response, cache_timeout=None, page_timeout=None, **kwargs): + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response, cache_timeout=None, page_timeout=None, **kwargs): + def __init__(self, get_response=None, cache_timeout=None, page_timeout=None, **kwargs): super().__init__(get_response) # We need to differentiate between "provided, but using default value", # and "not provided". If the value is provided using a default, then @@ -186,14 +178,14 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): # we need to use middleware defaults. try: - key_prefix = kwargs["key_prefix"] + key_prefix = kwargs['key_prefix'] if key_prefix is None: - key_prefix = "" + key_prefix = '' self.key_prefix = key_prefix except KeyError: pass try: - cache_alias = kwargs["cache_alias"] + cache_alias = kwargs['cache_alias'] if cache_alias is None: cache_alias = DEFAULT_CACHE_ALIAS self.cache_alias = cache_alias diff --git a/venv/Lib/site-packages/django/middleware/clickjacking.py b/venv/Lib/site-packages/django/middleware/clickjacking.py index 072f414..0161f8e 100644 --- a/venv/Lib/site-packages/django/middleware/clickjacking.py +++ b/venv/Lib/site-packages/django/middleware/clickjacking.py @@ -21,17 +21,16 @@ class XFrameOptionsMiddleware(MiddlewareMixin): response from being loaded in a frame in any site, set X_FRAME_OPTIONS in your project's Django settings to 'DENY'. """ - def process_response(self, request, response): # Don't set it if it's already in the response - if response.get("X-Frame-Options") is not None: + if response.get('X-Frame-Options') is not None: return response # Don't set it if they used @xframe_options_exempt - if getattr(response, "xframe_options_exempt", False): + if getattr(response, 'xframe_options_exempt', False): return response - response.headers["X-Frame-Options"] = self.get_xframe_options_value( + response.headers['X-Frame-Options'] = self.get_xframe_options_value( request, response, ) @@ -45,4 +44,4 @@ class XFrameOptionsMiddleware(MiddlewareMixin): This method can be overridden if needed, allowing it to vary based on the request or response. """ - return getattr(settings, "X_FRAME_OPTIONS", "DENY").upper() + return getattr(settings, 'X_FRAME_OPTIONS', 'DENY').upper() diff --git a/venv/Lib/site-packages/django/middleware/common.py b/venv/Lib/site-packages/django/middleware/common.py index c652374..e42d05e 100644 --- a/venv/Lib/site-packages/django/middleware/common.py +++ b/venv/Lib/site-packages/django/middleware/common.py @@ -38,16 +38,16 @@ class CommonMiddleware(MiddlewareMixin): """ # Check for denied User-Agents - user_agent = request.META.get("HTTP_USER_AGENT") + user_agent = request.META.get('HTTP_USER_AGENT') if user_agent is not None: for user_agent_regex in settings.DISALLOWED_USER_AGENTS: if user_agent_regex.search(user_agent): - raise PermissionDenied("Forbidden user agent") + raise PermissionDenied('Forbidden user agent') # Check for a redirect based on settings.PREPEND_WWW host = request.get_host() - must_prepend = settings.PREPEND_WWW and host and not host.startswith("www.") - redirect_url = ("%s://www.%s" % (request.scheme, host)) if must_prepend else "" + must_prepend = settings.PREPEND_WWW and host and not host.startswith('www.') + redirect_url = ('%s://www.%s' % (request.scheme, host)) if must_prepend else '' # Check if a slash should be appended if self.should_redirect_with_slash(request): @@ -65,13 +65,13 @@ class CommonMiddleware(MiddlewareMixin): Return True if settings.APPEND_SLASH is True and appending a slash to the request path turns an invalid path into a valid one. """ - if settings.APPEND_SLASH and not request.path_info.endswith("/"): - urlconf = getattr(request, "urlconf", None) + if settings.APPEND_SLASH and not request.path_info.endswith('/'): + urlconf = getattr(request, 'urlconf', None) if not is_valid_path(request.path_info, urlconf): - match = is_valid_path("%s/" % request.path_info, urlconf) + match = is_valid_path('%s/' % request.path_info, urlconf) if match: view = match.func - return getattr(view, "should_append_slash", True) + return getattr(view, 'should_append_slash', True) return False def get_full_path_with_slash(self, request): @@ -84,16 +84,15 @@ class CommonMiddleware(MiddlewareMixin): new_path = request.get_full_path(force_append_slash=True) # Prevent construction of scheme relative urls. new_path = escape_leading_slashes(new_path) - if settings.DEBUG and request.method in ("POST", "PUT", "PATCH"): + if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'): raise RuntimeError( "You called this URL via %(method)s, but the URL doesn't end " "in a slash and you have APPEND_SLASH set. Django can't " "redirect to the slash URL while maintaining %(method)s data. " "Change your form to point to %(url)s (note the trailing " - "slash), or set APPEND_SLASH=False in your Django settings." - % { - "method": request.method, - "url": request.get_host() + new_path, + "slash), or set APPEND_SLASH=False in your Django settings." % { + 'method': request.method, + 'url': request.get_host() + new_path, } ) return new_path @@ -110,32 +109,28 @@ class CommonMiddleware(MiddlewareMixin): # Add the Content-Length header to non-streaming responses if not # already set. - if not response.streaming and not response.has_header("Content-Length"): - response.headers["Content-Length"] = str(len(response.content)) + if not response.streaming and not response.has_header('Content-Length'): + response.headers['Content-Length'] = str(len(response.content)) return response class BrokenLinkEmailsMiddleware(MiddlewareMixin): + def process_response(self, request, response): """Send broken link emails for relevant 404 NOT FOUND responses.""" if response.status_code == 404 and not settings.DEBUG: domain = request.get_host() path = request.get_full_path() - referer = request.META.get("HTTP_REFERER", "") + referer = request.META.get('HTTP_REFERER', '') if not self.is_ignorable_request(request, path, domain, referer): - ua = request.META.get("HTTP_USER_AGENT", "<none>") - ip = request.META.get("REMOTE_ADDR", "<none>") + ua = request.META.get('HTTP_USER_AGENT', '<none>') + ip = request.META.get('REMOTE_ADDR', '<none>') mail_managers( - "Broken %slink on %s" - % ( - ( - "INTERNAL " - if self.is_internal_request(domain, referer) - else "" - ), - domain, + "Broken %slink on %s" % ( + ('INTERNAL ' if self.is_internal_request(domain, referer) else ''), + domain ), "Referrer: %s\nRequested URL: %s\nUser agent: %s\n" "IP address: %s\n" % (referer, path, ua, ip), @@ -163,17 +158,17 @@ class BrokenLinkEmailsMiddleware(MiddlewareMixin): # APPEND_SLASH is enabled and the referer is equal to the current URL # without a trailing slash indicating an internal redirect. - if settings.APPEND_SLASH and uri.endswith("/") and referer == uri[:-1]: + if settings.APPEND_SLASH and uri.endswith('/') and referer == uri[:-1]: return True # A '?' in referer is identified as a search engine source. - if not self.is_internal_request(domain, referer) and "?" in referer: + if not self.is_internal_request(domain, referer) and '?' in referer: return True # The referer is equal to the current URL, ignoring the scheme (assumed # to be a poorly implemented bot). parsed_referer = urlparse(referer) - if parsed_referer.netloc in ["", domain] and parsed_referer.path == uri: + if parsed_referer.netloc in ['', domain] and parsed_referer.path == uri: return True return any(pattern.search(uri) for pattern in settings.IGNORABLE_404_URLS) diff --git a/venv/Lib/site-packages/django/middleware/csrf.py b/venv/Lib/site-packages/django/middleware/csrf.py index 0a8a6f6..368b51f 100644 --- a/venv/Lib/site-packages/django/middleware/csrf.py +++ b/venv/Lib/site-packages/django/middleware/csrf.py @@ -5,46 +5,32 @@ This module provides a middleware that implements protection against request forgeries from other sites. """ import logging +import re import string -from collections import defaultdict from urllib.parse import urlparse from django.conf import settings from django.core.exceptions import DisallowedHost, ImproperlyConfigured -from django.http import UnreadablePostError -from django.http.request import HttpHeaders from django.urls import get_callable from django.utils.cache import patch_vary_headers from django.utils.crypto import constant_time_compare, get_random_string from django.utils.deprecation import MiddlewareMixin -from django.utils.functional import cached_property from django.utils.http import is_same_domain from django.utils.log import log_response -from django.utils.regex_helper import _lazy_re_compile -logger = logging.getLogger("django.security.csrf") -# This matches if any character is not in CSRF_ALLOWED_CHARS. -invalid_token_chars_re = _lazy_re_compile("[^a-zA-Z0-9]") +logger = logging.getLogger('django.security.csrf') -REASON_BAD_ORIGIN = "Origin checking failed - %s does not match any trusted origins." REASON_NO_REFERER = "Referer checking failed - no Referer." REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins." REASON_NO_CSRF_COOKIE = "CSRF cookie not set." -REASON_CSRF_TOKEN_MISSING = "CSRF token missing." +REASON_BAD_TOKEN = "CSRF token missing or incorrect." REASON_MALFORMED_REFERER = "Referer checking failed - Referer is malformed." -REASON_INSECURE_REFERER = ( - "Referer checking failed - Referer is insecure while host is secure." -) -# The reason strings below are for passing to InvalidTokenFormat. They are -# phrases without a subject because they can be in reference to either the CSRF -# cookie or non-cookie token. -REASON_INCORRECT_LENGTH = "has incorrect length" -REASON_INVALID_CHARACTERS = "has invalid characters" +REASON_INSECURE_REFERER = "Referer checking failed - Referer is insecure while host is secure." CSRF_SECRET_LENGTH = 32 CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits -CSRF_SESSION_KEY = "_csrftoken" +CSRF_SESSION_KEY = '_csrftoken' def _get_failure_view(): @@ -64,7 +50,7 @@ def _mask_cipher_secret(secret): mask = _get_new_csrf_string() chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in mask)) - cipher = "".join(chars[(x + y) % len(chars)] for x, y in pairs) + cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs) return mask + cipher @@ -78,19 +64,11 @@ def _unmask_cipher_token(token): token = token[CSRF_SECRET_LENGTH:] chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in mask)) - return "".join(chars[x - y] for x, y in pairs) # Note negative values are ok + return ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok -def _add_new_csrf_cookie(request): - """Generate a new random CSRF_COOKIE value, and add it to request.META.""" - csrf_secret = _get_new_csrf_string() - request.META.update( - { - "CSRF_COOKIE": _mask_cipher_secret(csrf_secret), - "CSRF_COOKIE_NEEDS_UPDATE": True, - } - ) - return csrf_secret +def _get_new_csrf_token(): + return _mask_cipher_secret(_get_new_csrf_string()) def get_token(request): @@ -103,14 +81,12 @@ def get_token(request): header to the outgoing response. For this reason, you may need to use this function lazily, as is done by the csrf context processor. """ - if "CSRF_COOKIE" in request.META: - csrf_secret = _unmask_cipher_token(request.META["CSRF_COOKIE"]) - # Since the cookie is being used, flag to send the cookie in - # process_response() (even if the client already has it) in order to - # renew the expiry timer. - request.META["CSRF_COOKIE_NEEDS_UPDATE"] = True + if "CSRF_COOKIE" not in request.META: + csrf_secret = _get_new_csrf_string() + request.META["CSRF_COOKIE"] = _mask_cipher_secret(csrf_secret) else: - csrf_secret = _add_new_csrf_cookie(request) + csrf_secret = _unmask_cipher_token(request.META["CSRF_COOKIE"]) + request.META["CSRF_COOKIE_USED"] = True return _mask_cipher_secret(csrf_secret) @@ -119,21 +95,20 @@ def rotate_token(request): Change the CSRF token in use for a request - should be done on login for security purposes. """ - _add_new_csrf_cookie(request) - - -class InvalidTokenFormat(Exception): - def __init__(self, reason): - self.reason = reason + request.META.update({ + "CSRF_COOKIE_USED": True, + "CSRF_COOKIE": _get_new_csrf_token(), + }) + request.csrf_cookie_needs_reset = True def _sanitize_token(token): - if len(token) not in (CSRF_TOKEN_LENGTH, CSRF_SECRET_LENGTH): - raise InvalidTokenFormat(REASON_INCORRECT_LENGTH) - # Make sure all characters are in CSRF_ALLOWED_CHARS. - if invalid_token_chars_re.search(token): - raise InvalidTokenFormat(REASON_INVALID_CHARACTERS) - if len(token) == CSRF_SECRET_LENGTH: + # Allow only ASCII alphanumerics + if re.search('[^a-zA-Z0-9]', token): + return _get_new_csrf_token() + elif len(token) == CSRF_TOKEN_LENGTH: + return token + elif len(token) == CSRF_SECRET_LENGTH: # Older Django versions set cookies to values of CSRF_SECRET_LENGTH # alphanumeric characters. For backwards compatibility, accept # such values as unmasked secrets. @@ -141,10 +116,10 @@ def _sanitize_token(token): # different code paths in the checks, although that might be a tad more # efficient. return _mask_cipher_secret(token) - return token + return _get_new_csrf_token() -def _does_token_match(request_csrf_token, csrf_token): +def _compare_masked_tokens(request_csrf_token, csrf_token): # Assume both arguments are sanitized -- that is, strings of # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS. return constant_time_compare( @@ -153,11 +128,6 @@ def _does_token_match(request_csrf_token, csrf_token): ) -class RejectRequest(Exception): - def __init__(self, reason): - self.reason = reason - - class CsrfViewMiddleware(MiddlewareMixin): """ Require a present and correct csrfmiddlewaretoken for POST requests that @@ -166,33 +136,6 @@ class CsrfViewMiddleware(MiddlewareMixin): This middleware should be used in conjunction with the {% csrf_token %} template tag. """ - - @cached_property - def csrf_trusted_origins_hosts(self): - return [ - urlparse(origin).netloc.lstrip("*") - for origin in settings.CSRF_TRUSTED_ORIGINS - ] - - @cached_property - def allowed_origins_exact(self): - return {origin for origin in settings.CSRF_TRUSTED_ORIGINS if "*" not in origin} - - @cached_property - def allowed_origin_subdomains(self): - """ - A mapping of allowed schemes to list of allowed netlocs, where all - subdomains of the netloc are allowed. - """ - allowed_origin_subdomains = defaultdict(list) - for parsed in ( - urlparse(origin) - for origin in settings.CSRF_TRUSTED_ORIGINS - if "*" in origin - ): - allowed_origin_subdomains[parsed.scheme].append(parsed.netloc.lstrip("*")) - return allowed_origin_subdomains - # The _accept and _reject methods currently only exist for the sake of the # requires_csrf_token decorator. def _accept(self, request): @@ -205,9 +148,7 @@ class CsrfViewMiddleware(MiddlewareMixin): def _reject(self, request, reason): response = _get_failure_view()(request, reason=reason) log_response( - "Forbidden (%s): %s", - reason, - request.path, + 'Forbidden (%s): %s', reason, request.path, response=response, request=request, logger=logger, @@ -220,9 +161,9 @@ class CsrfViewMiddleware(MiddlewareMixin): return request.session.get(CSRF_SESSION_KEY) except AttributeError: raise ImproperlyConfigured( - "CSRF_USE_SESSIONS is enabled, but request.session is not " - "set. SessionMiddleware must appear before CsrfViewMiddleware " - "in MIDDLEWARE." + 'CSRF_USE_SESSIONS is enabled, but request.session is not ' + 'set. SessionMiddleware must appear before CsrfViewMiddleware ' + 'in MIDDLEWARE.' ) else: try: @@ -230,23 +171,21 @@ class CsrfViewMiddleware(MiddlewareMixin): except KeyError: return None - # This can raise InvalidTokenFormat. csrf_token = _sanitize_token(cookie_token) - if csrf_token != cookie_token: - # Then the cookie token had length CSRF_SECRET_LENGTH, so flag - # to replace it with the masked version. - request.META["CSRF_COOKIE_NEEDS_UPDATE"] = True + # Cookie token needed to be replaced; + # the cookie needs to be reset. + request.csrf_cookie_needs_reset = True return csrf_token - def _set_csrf_cookie(self, request, response): + def _set_token(self, request, response): if settings.CSRF_USE_SESSIONS: - if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]: - request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"] + if request.session.get(CSRF_SESSION_KEY) != request.META['CSRF_COOKIE']: + request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE'] else: response.set_cookie( settings.CSRF_COOKIE_NAME, - request.META["CSRF_COOKIE"], + request.META['CSRF_COOKIE'], max_age=settings.CSRF_COOKIE_AGE, domain=settings.CSRF_COOKIE_DOMAIN, path=settings.CSRF_COOKIE_PATH, @@ -255,211 +194,136 @@ class CsrfViewMiddleware(MiddlewareMixin): samesite=settings.CSRF_COOKIE_SAMESITE, ) # Set the Vary header since content varies with the CSRF cookie. - patch_vary_headers(response, ("Cookie",)) - - def _origin_verified(self, request): - request_origin = request.META["HTTP_ORIGIN"] - try: - good_host = request.get_host() - except DisallowedHost: - pass - else: - good_origin = "%s://%s" % ( - "https" if request.is_secure() else "http", - good_host, - ) - if request_origin == good_origin: - return True - if request_origin in self.allowed_origins_exact: - return True - try: - parsed_origin = urlparse(request_origin) - except ValueError: - return False - request_scheme = parsed_origin.scheme - request_netloc = parsed_origin.netloc - return any( - is_same_domain(request_netloc, host) - for host in self.allowed_origin_subdomains.get(request_scheme, ()) - ) - - def _check_referer(self, request): - referer = request.META.get("HTTP_REFERER") - if referer is None: - raise RejectRequest(REASON_NO_REFERER) - - try: - referer = urlparse(referer) - except ValueError: - raise RejectRequest(REASON_MALFORMED_REFERER) - - # Make sure we have a valid URL for Referer. - if "" in (referer.scheme, referer.netloc): - raise RejectRequest(REASON_MALFORMED_REFERER) - - # Ensure that our Referer is also secure. - if referer.scheme != "https": - raise RejectRequest(REASON_INSECURE_REFERER) - - if any( - is_same_domain(referer.netloc, host) - for host in self.csrf_trusted_origins_hosts - ): - return - # Allow matching the configured cookie domain. - good_referer = ( - settings.SESSION_COOKIE_DOMAIN - if settings.CSRF_USE_SESSIONS - else settings.CSRF_COOKIE_DOMAIN - ) - if good_referer is None: - # If no cookie domain is configured, allow matching the current - # host:port exactly if it's permitted by ALLOWED_HOSTS. - try: - # request.get_host() includes the port. - good_referer = request.get_host() - except DisallowedHost: - raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) - else: - server_port = request.get_port() - if server_port not in ("443", "80"): - good_referer = "%s:%s" % (good_referer, server_port) - - if not is_same_domain(referer.netloc, good_referer): - raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) - - def _bad_token_message(self, reason, token_source): - if token_source != "POST": - # Assume it is a settings.CSRF_HEADER_NAME value. - header_name = HttpHeaders.parse_header_name(token_source) - token_source = f"the {header_name!r} HTTP header" - return f"CSRF token from {token_source} {reason}." - - def _check_token(self, request): - # Access csrf_token via self._get_token() as rotate_token() may have - # been called by an authentication middleware during the - # process_request() phase. - try: - csrf_token = self._get_token(request) - except InvalidTokenFormat as exc: - raise RejectRequest(f"CSRF cookie {exc.reason}.") - - if csrf_token is None: - # No CSRF cookie. For POST requests, we insist on a CSRF cookie, - # and in this way we can avoid all CSRF attacks, including login - # CSRF. - raise RejectRequest(REASON_NO_CSRF_COOKIE) - - # Check non-cookie token for match. - request_csrf_token = "" - if request.method == "POST": - try: - request_csrf_token = request.POST.get("csrfmiddlewaretoken", "") - except UnreadablePostError: - # Handle a broken connection before we've completed reading the - # POST data. process_view shouldn't raise any exceptions, so - # we'll ignore and serve the user a 403 (assuming they're still - # listening, which they probably aren't because of the error). - pass - - if request_csrf_token == "": - # Fall back to X-CSRFToken, to make things easier for AJAX, and - # possible for PUT/DELETE. - try: - request_csrf_token = request.META[settings.CSRF_HEADER_NAME] - except KeyError: - raise RejectRequest(REASON_CSRF_TOKEN_MISSING) - token_source = settings.CSRF_HEADER_NAME - else: - token_source = "POST" - - try: - request_csrf_token = _sanitize_token(request_csrf_token) - except InvalidTokenFormat as exc: - reason = self._bad_token_message(exc.reason, token_source) - raise RejectRequest(reason) - - if not _does_token_match(request_csrf_token, csrf_token): - reason = self._bad_token_message("incorrect", token_source) - raise RejectRequest(reason) + patch_vary_headers(response, ('Cookie',)) def process_request(self, request): - try: - csrf_token = self._get_token(request) - except InvalidTokenFormat: - _add_new_csrf_cookie(request) - else: - if csrf_token is not None: - # Use same token next time. - request.META["CSRF_COOKIE"] = csrf_token + csrf_token = self._get_token(request) + if csrf_token is not None: + # Use same token next time. + request.META['CSRF_COOKIE'] = csrf_token def process_view(self, request, callback, callback_args, callback_kwargs): - if getattr(request, "csrf_processing_done", False): + if getattr(request, 'csrf_processing_done', False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works - if getattr(callback, "csrf_exempt", False): + if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC7231 needs protection - if request.method in ("GET", "HEAD", "OPTIONS", "TRACE"): - return self._accept(request) + if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): + if getattr(request, '_dont_enforce_csrf_checks', False): + # Mechanism to turn off CSRF checks for test suite. + # It comes after the creation of CSRF cookies, so that + # everything else continues to work exactly the same + # (e.g. cookies are sent, etc.), but before any + # branches that call reject(). + return self._accept(request) - if getattr(request, "_dont_enforce_csrf_checks", False): - # Mechanism to turn off CSRF checks for test suite. It comes after - # the creation of CSRF cookies, so that everything else continues - # to work exactly the same (e.g. cookies are sent, etc.), but - # before any branches that call the _reject method. - return self._accept(request) + if request.is_secure(): + # Suppose user visits http://example.com/ + # An active network attacker (man-in-the-middle, MITM) sends a + # POST form that targets https://example.com/detonate-bomb/ and + # submits it via JavaScript. + # + # The attacker will need to provide a CSRF cookie and token, but + # that's no problem for a MITM and the session-independent + # secret we're using. So the MITM can circumvent the CSRF + # protection. This is true for any HTTP connection, but anyone + # using HTTPS expects better! For this reason, for + # https://example.com/ we need additional protection that treats + # http://example.com/ as completely untrusted. Under HTTPS, + # Barth et al. found that the Referer header is missing for + # same-domain requests in only about 0.2% of cases or less, so + # we can use strict Referer checking. + referer = request.META.get('HTTP_REFERER') + if referer is None: + return self._reject(request, REASON_NO_REFERER) - # Reject the request if the Origin header doesn't match an allowed - # value. - if "HTTP_ORIGIN" in request.META: - if not self._origin_verified(request): - return self._reject( - request, REASON_BAD_ORIGIN % request.META["HTTP_ORIGIN"] + referer = urlparse(referer) + + # Make sure we have a valid URL for Referer. + if '' in (referer.scheme, referer.netloc): + return self._reject(request, REASON_MALFORMED_REFERER) + + # Ensure that our Referer is also secure. + if referer.scheme != 'https': + return self._reject(request, REASON_INSECURE_REFERER) + + # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match + # match on host:port. If not, obey the cookie rules (or those + # for the session cookie, if CSRF_USE_SESSIONS). + good_referer = ( + settings.SESSION_COOKIE_DOMAIN + if settings.CSRF_USE_SESSIONS + else settings.CSRF_COOKIE_DOMAIN ) - elif request.is_secure(): - # If the Origin header wasn't provided, reject HTTPS requests if - # the Referer header doesn't match an allowed value. - # - # Suppose user visits http://example.com/ - # An active network attacker (man-in-the-middle, MITM) sends a - # POST form that targets https://example.com/detonate-bomb/ and - # submits it via JavaScript. - # - # The attacker will need to provide a CSRF cookie and token, but - # that's no problem for a MITM and the session-independent secret - # we're using. So the MITM can circumvent the CSRF protection. This - # is true for any HTTP connection, but anyone using HTTPS expects - # better! For this reason, for https://example.com/ we need - # additional protection that treats http://example.com/ as - # completely untrusted. Under HTTPS, Barth et al. found that the - # Referer header is missing for same-domain requests in only about - # 0.2% of cases or less, so we can use strict Referer checking. - try: - self._check_referer(request) - except RejectRequest as exc: - return self._reject(request, exc.reason) + if good_referer is not None: + server_port = request.get_port() + if server_port not in ('443', '80'): + good_referer = '%s:%s' % (good_referer, server_port) + else: + try: + # request.get_host() includes the port. + good_referer = request.get_host() + except DisallowedHost: + pass - try: - self._check_token(request) - except RejectRequest as exc: - return self._reject(request, exc.reason) + # Create a list of all acceptable HTTP referers, including the + # current host if it's permitted by ALLOWED_HOSTS. + good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) + if good_referer is not None: + good_hosts.append(good_referer) + + if not any(is_same_domain(referer.netloc, host) for host in good_hosts): + reason = REASON_BAD_REFERER % referer.geturl() + return self._reject(request, reason) + + # Access csrf_token via self._get_token() as rotate_token() may + # have been called by an authentication middleware during the + # process_request() phase. + csrf_token = self._get_token(request) + if csrf_token is None: + # No CSRF cookie. For POST requests, we insist on a CSRF cookie, + # and in this way we can avoid all CSRF attacks, including login + # CSRF. + return self._reject(request, REASON_NO_CSRF_COOKIE) + + # Check non-cookie token for match. + request_csrf_token = "" + if request.method == "POST": + try: + request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') + except OSError: + # Handle a broken connection before we've completed reading + # the POST data. process_view shouldn't raise any + # exceptions, so we'll ignore and serve the user a 403 + # (assuming they're still listening, which they probably + # aren't because of the error). + pass + + if request_csrf_token == "": + # Fall back to X-CSRFToken, to make things easier for AJAX, + # and possible for PUT/DELETE. + request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') + + request_csrf_token = _sanitize_token(request_csrf_token) + if not _compare_masked_tokens(request_csrf_token, csrf_token): + return self._reject(request, REASON_BAD_TOKEN) return self._accept(request) def process_response(self, request, response): - if request.META.get("CSRF_COOKIE_NEEDS_UPDATE"): - self._set_csrf_cookie(request, response) - # Unset the flag to prevent _set_csrf_cookie() from being - # unnecessarily called again in process_response() by other - # instances of CsrfViewMiddleware. This can happen e.g. when both a - # decorator and middleware are used. However, - # CSRF_COOKIE_NEEDS_UPDATE is still respected in subsequent calls - # e.g. in case rotate_token() is called in process_response() later - # by custom middleware but before those subsequent calls. - request.META["CSRF_COOKIE_NEEDS_UPDATE"] = False + if not getattr(request, 'csrf_cookie_needs_reset', False): + if getattr(response, 'csrf_cookie_set', False): + return response + if not request.META.get("CSRF_COOKIE_USED", False): + return response + + # Set the CSRF cookie even if it's already set, so we renew + # the expiry timer. + self._set_token(request, response) + response.csrf_cookie_set = True return response diff --git a/venv/Lib/site-packages/django/middleware/gzip.py b/venv/Lib/site-packages/django/middleware/gzip.py index 6d27c1e..3504661 100644 --- a/venv/Lib/site-packages/django/middleware/gzip.py +++ b/venv/Lib/site-packages/django/middleware/gzip.py @@ -3,7 +3,7 @@ from django.utils.deprecation import MiddlewareMixin from django.utils.regex_helper import _lazy_re_compile from django.utils.text import compress_sequence, compress_string -re_accepts_gzip = _lazy_re_compile(r"\bgzip\b") +re_accepts_gzip = _lazy_re_compile(r'\bgzip\b') class GZipMiddleware(MiddlewareMixin): @@ -12,19 +12,18 @@ class GZipMiddleware(MiddlewareMixin): Set the Vary header accordingly, so that caches will base their storage on the Accept-Encoding header. """ - def process_response(self, request, response): # It's not worth attempting to compress really short responses. if not response.streaming and len(response.content) < 200: return response # Avoid gzipping if we've already got a content-encoding. - if response.has_header("Content-Encoding"): + if response.has_header('Content-Encoding'): return response - patch_vary_headers(response, ("Accept-Encoding",)) + patch_vary_headers(response, ('Accept-Encoding',)) - ae = request.META.get("HTTP_ACCEPT_ENCODING", "") + ae = request.META.get('HTTP_ACCEPT_ENCODING', '') if not re_accepts_gzip.search(ae): return response @@ -32,21 +31,21 @@ class GZipMiddleware(MiddlewareMixin): # Delete the `Content-Length` header for streaming content, because # we won't know the compressed size until we stream it. response.streaming_content = compress_sequence(response.streaming_content) - del response.headers["Content-Length"] + del response.headers['Content-Length'] else: # Return the compressed content only if it's actually shorter. compressed_content = compress_string(response.content) if len(compressed_content) >= len(response.content): return response response.content = compressed_content - response.headers["Content-Length"] = str(len(response.content)) + response.headers['Content-Length'] = str(len(response.content)) # If there is a strong ETag, make it weak to fulfill the requirements # of RFC 7232 section-2.1 while also allowing conditional request # matches on ETags. - etag = response.get("ETag") + etag = response.get('ETag') if etag and etag.startswith('"'): - response.headers["ETag"] = "W/" + etag - response.headers["Content-Encoding"] = "gzip" + response.headers['ETag'] = 'W/' + etag + response.headers['Content-Encoding'] = 'gzip' return response diff --git a/venv/Lib/site-packages/django/middleware/http.py b/venv/Lib/site-packages/django/middleware/http.py index 84c5466..4fcde85 100644 --- a/venv/Lib/site-packages/django/middleware/http.py +++ b/venv/Lib/site-packages/django/middleware/http.py @@ -1,4 +1,6 @@ -from django.utils.cache import cc_delim_re, get_conditional_response, set_response_etag +from django.utils.cache import ( + cc_delim_re, get_conditional_response, set_response_etag, +) from django.utils.deprecation import MiddlewareMixin from django.utils.http import parse_http_date_safe @@ -9,19 +11,18 @@ class ConditionalGetMiddleware(MiddlewareMixin): Last-Modified header and the request has If-None-Match or If-Modified-Since, replace the response with HttpNotModified. Add an ETag header if needed. """ - def process_response(self, request, response): # It's too late to prevent an unsafe request with a 412 response, and # for a HEAD request, the response body is always empty so computing # an accurate ETag isn't possible. - if request.method != "GET": + if request.method != 'GET': return response - if self.needs_etag(response) and not response.has_header("ETag"): + if self.needs_etag(response) and not response.has_header('ETag'): set_response_etag(response) - etag = response.get("ETag") - last_modified = response.get("Last-Modified") + etag = response.get('ETag') + last_modified = response.get('Last-Modified') last_modified = last_modified and parse_http_date_safe(last_modified) if etag or last_modified: @@ -36,5 +37,5 @@ class ConditionalGetMiddleware(MiddlewareMixin): def needs_etag(self, response): """Return True if an ETag header should be added to response.""" - cache_control_headers = cc_delim_re.split(response.get("Cache-Control", "")) - return all(header.lower() != "no-store" for header in cache_control_headers) + cache_control_headers = cc_delim_re.split(response.get('Cache-Control', '')) + return all(header.lower() != 'no-store' for header in cache_control_headers) diff --git a/venv/Lib/site-packages/django/middleware/locale.py b/venv/Lib/site-packages/django/middleware/locale.py index 71db230..0bbdda3 100644 --- a/venv/Lib/site-packages/django/middleware/locale.py +++ b/venv/Lib/site-packages/django/middleware/locale.py @@ -13,24 +13,14 @@ class LocaleMiddleware(MiddlewareMixin): current thread context. This allows pages to be dynamically translated to the language the user desires (if the language is available). """ - response_redirect_class = HttpResponseRedirect def process_request(self, request): - urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) - ( - i18n_patterns_used, - prefixed_default_language, - ) = is_language_prefix_patterns_used(urlconf) - language = translation.get_language_from_request( - request, check_path=i18n_patterns_used - ) + urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) + i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) + language = translation.get_language_from_request(request, check_path=i18n_patterns_used) language_from_path = translation.get_language_from_path(request.path_info) - if ( - not language_from_path - and i18n_patterns_used - and not prefixed_default_language - ): + if not language_from_path and i18n_patterns_used and not prefixed_default_language: language = settings.LANGUAGE_CODE translation.activate(language) request.LANGUAGE_CODE = translation.get_language() @@ -38,43 +28,34 @@ class LocaleMiddleware(MiddlewareMixin): def process_response(self, request, response): language = translation.get_language() language_from_path = translation.get_language_from_path(request.path_info) - urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) - ( - i18n_patterns_used, - prefixed_default_language, - ) = is_language_prefix_patterns_used(urlconf) + urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) + i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) - if ( - response.status_code == 404 - and not language_from_path - and i18n_patterns_used - and prefixed_default_language - ): + if (response.status_code == 404 and not language_from_path and + i18n_patterns_used and prefixed_default_language): # Maybe the language code is missing in the URL? Try adding the # language prefix and redirecting to that URL. - language_path = "/%s%s" % (language, request.path_info) + language_path = '/%s%s' % (language, request.path_info) path_valid = is_valid_path(language_path, urlconf) - path_needs_slash = not path_valid and ( - settings.APPEND_SLASH - and not language_path.endswith("/") - and is_valid_path("%s/" % language_path, urlconf) + path_needs_slash = ( + not path_valid and ( + settings.APPEND_SLASH and not language_path.endswith('/') and + is_valid_path('%s/' % language_path, urlconf) + ) ) if path_valid or path_needs_slash: script_prefix = get_script_prefix() # Insert language after the script prefix and before the # rest of the URL - language_url = request.get_full_path( - force_append_slash=path_needs_slash - ).replace(script_prefix, "%s%s/" % (script_prefix, language), 1) - # Redirect to the language-specific URL as detected by - # get_language_from_request(). HTTP caches may cache this - # redirect, so add the Vary header. - redirect = self.response_redirect_class(language_url) - patch_vary_headers(redirect, ("Accept-Language", "Cookie")) - return redirect + language_url = request.get_full_path(force_append_slash=path_needs_slash).replace( + script_prefix, + '%s%s/' % (script_prefix, language), + 1 + ) + return self.response_redirect_class(language_url) if not (i18n_patterns_used and language_from_path): - patch_vary_headers(response, ("Accept-Language",)) - response.headers.setdefault("Content-Language", language) + patch_vary_headers(response, ('Accept-Language',)) + response.headers.setdefault('Content-Language', language) return response diff --git a/venv/Lib/site-packages/django/middleware/security.py b/venv/Lib/site-packages/django/middleware/security.py index 1dd2204..d923893 100644 --- a/venv/Lib/site-packages/django/middleware/security.py +++ b/venv/Lib/site-packages/django/middleware/security.py @@ -6,61 +6,52 @@ from django.utils.deprecation import MiddlewareMixin class SecurityMiddleware(MiddlewareMixin): - def __init__(self, get_response): + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response): + def __init__(self, get_response=None): super().__init__(get_response) self.sts_seconds = settings.SECURE_HSTS_SECONDS self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS self.sts_preload = settings.SECURE_HSTS_PRELOAD self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF + self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER self.redirect = settings.SECURE_SSL_REDIRECT self.redirect_host = settings.SECURE_SSL_HOST self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT] self.referrer_policy = settings.SECURE_REFERRER_POLICY - self.cross_origin_opener_policy = settings.SECURE_CROSS_ORIGIN_OPENER_POLICY def process_request(self, request): path = request.path.lstrip("/") - if ( - self.redirect - and not request.is_secure() - and not any(pattern.search(path) for pattern in self.redirect_exempt) - ): + if (self.redirect and not request.is_secure() and + not any(pattern.search(path) + for pattern in self.redirect_exempt)): host = self.redirect_host or request.get_host() return HttpResponsePermanentRedirect( "https://%s%s" % (host, request.get_full_path()) ) def process_response(self, request, response): - if ( - self.sts_seconds - and request.is_secure() - and "Strict-Transport-Security" not in response - ): + if (self.sts_seconds and request.is_secure() and + 'Strict-Transport-Security' not in response): sts_header = "max-age=%s" % self.sts_seconds if self.sts_include_subdomains: sts_header = sts_header + "; includeSubDomains" if self.sts_preload: sts_header = sts_header + "; preload" - response.headers["Strict-Transport-Security"] = sts_header + response.headers['Strict-Transport-Security'] = sts_header if self.content_type_nosniff: - response.headers.setdefault("X-Content-Type-Options", "nosniff") + response.headers.setdefault('X-Content-Type-Options', 'nosniff') + + if self.xss_filter: + response.headers.setdefault('X-XSS-Protection', '1; mode=block') if self.referrer_policy: # Support a comma-separated string or iterable of values to allow # fallback. - response.headers.setdefault( - "Referrer-Policy", - ",".join( - [v.strip() for v in self.referrer_policy.split(",")] - if isinstance(self.referrer_policy, str) - else self.referrer_policy - ), - ) + response.headers.setdefault('Referrer-Policy', ','.join( + [v.strip() for v in self.referrer_policy.split(',')] + if isinstance(self.referrer_policy, str) else self.referrer_policy + )) - if self.cross_origin_opener_policy: - response.setdefault( - "Cross-Origin-Opener-Policy", - self.cross_origin_opener_policy, - ) return response diff --git a/venv/Lib/site-packages/django/shortcuts.py b/venv/Lib/site-packages/django/shortcuts.py index 90ec1be..d810471 100644 --- a/venv/Lib/site-packages/django/shortcuts.py +++ b/venv/Lib/site-packages/django/shortcuts.py @@ -4,21 +4,16 @@ of MVC. In other words, these functions/classes introduce controlled coupling for convenience's sake. """ from django.http import ( - Http404, - HttpResponse, - HttpResponsePermanentRedirect, - HttpResponseRedirect, + Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, ) from django.template import loader from django.urls import NoReverseMatch, reverse from django.utils.functional import Promise -def render( - request, template_name, context=None, content_type=None, status=None, using=None -): +def render(request, template_name, context=None, content_type=None, status=None, using=None): """ - Return an HttpResponse whose content is filled with the result of calling + Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) @@ -42,9 +37,7 @@ def redirect(to, *args, permanent=False, **kwargs): Issues a temporary redirect by default; pass permanent=True to issue a permanent redirect. """ - redirect_class = ( - HttpResponsePermanentRedirect if permanent else HttpResponseRedirect - ) + redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect return redirect_class(resolve_url(to, *args, **kwargs)) @@ -56,14 +49,14 @@ def _get_queryset(klass): the job. """ # If it is a model class or anything else with ._default_manager - if hasattr(klass, "_default_manager"): + if hasattr(klass, '_default_manager'): return klass._default_manager.all() return klass def get_object_or_404(klass, *args, **kwargs): """ - Use get() to return an object, or raise an Http404 exception if the object + Use get() to return an object, or raise a Http404 exception if the object does not exist. klass may be a Model, Manager, or QuerySet object. All other passed @@ -73,10 +66,8 @@ def get_object_or_404(klass, *args, **kwargs): one object is found. """ queryset = _get_queryset(klass) - if not hasattr(queryset, "get"): - klass__name = ( - klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ - ) + if not hasattr(queryset, 'get'): + klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ raise ValueError( "First argument to get_object_or_404() must be a Model, Manager, " "or QuerySet, not '%s'." % klass__name @@ -84,33 +75,27 @@ def get_object_or_404(klass, *args, **kwargs): try: return queryset.get(*args, **kwargs) except queryset.model.DoesNotExist: - raise Http404( - "No %s matches the given query." % queryset.model._meta.object_name - ) + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) def get_list_or_404(klass, *args, **kwargs): """ - Use filter() to return a list of objects, or raise an Http404 exception if + Use filter() to return a list of objects, or raise a Http404 exception if the list is empty. klass may be a Model, Manager, or QuerySet object. All other passed arguments and keyword arguments are used in the filter() query. """ queryset = _get_queryset(klass) - if not hasattr(queryset, "filter"): - klass__name = ( - klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ - ) + if not hasattr(queryset, 'filter'): + klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ raise ValueError( "First argument to get_list_or_404() must be a Model, Manager, or " "QuerySet, not '%s'." % klass__name ) obj_list = list(queryset.filter(*args, **kwargs)) if not obj_list: - raise Http404( - "No %s matches the given query." % queryset.model._meta.object_name - ) + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) return obj_list @@ -128,7 +113,7 @@ def resolve_url(to, *args, **kwargs): * A URL, which will be returned as-is. """ # If it's a model, use get_absolute_url() - if hasattr(to, "get_absolute_url"): + if hasattr(to, 'get_absolute_url'): return to.get_absolute_url() if isinstance(to, Promise): @@ -137,7 +122,7 @@ def resolve_url(to, *args, **kwargs): to = str(to) # Handle relative URLs - if isinstance(to, str) and to.startswith(("./", "../")): + if isinstance(to, str) and to.startswith(('./', '../')): return to # Next try a reverse URL resolution. @@ -148,7 +133,7 @@ def resolve_url(to, *args, **kwargs): if callable(to): raise # If this doesn't "feel" like a URL, re-raise. - if "/" not in to and "." not in to: + if '/' not in to and '.' not in to: raise # Finally, fall back and assume it's a URL diff --git a/venv/Lib/site-packages/django/template/__init__.py b/venv/Lib/site-packages/django/template/__init__.py index adb431c..7414d0f 100644 --- a/venv/Lib/site-packages/django/template/__init__.py +++ b/venv/Lib/site-packages/django/template/__init__.py @@ -46,30 +46,26 @@ from .utils import EngineHandler engines = EngineHandler() -__all__ = ("Engine", "engines") +__all__ = ('Engine', 'engines') # Django Template Language # Public exceptions -from .base import VariableDoesNotExist # NOQA isort:skip -from .context import Context, ContextPopException, RequestContext # NOQA isort:skip -from .exceptions import TemplateDoesNotExist, TemplateSyntaxError # NOQA isort:skip +from .base import VariableDoesNotExist # NOQA isort:skip +from .context import Context, ContextPopException, RequestContext # NOQA isort:skip +from .exceptions import TemplateDoesNotExist, TemplateSyntaxError # NOQA isort:skip # Template parts -from .base import ( # NOQA isort:skip - Node, - NodeList, - Origin, - Template, - Variable, +from .base import ( # NOQA isort:skip + Node, NodeList, Origin, Template, Variable, ) # Library management -from .library import Library # NOQA isort:skip +from .library import Library # NOQA isort:skip # Import the .autoreload module to trigger the registrations of signals. -from . import autoreload # NOQA isort:skip +from . import autoreload # NOQA isort:skip -__all__ += ("Template", "Context", "RequestContext") +__all__ += ('Template', 'Context', 'RequestContext') diff --git a/venv/Lib/site-packages/django/template/autoreload.py b/venv/Lib/site-packages/django/template/autoreload.py index 60a9e5d..18570b5 100644 --- a/venv/Lib/site-packages/django/template/autoreload.py +++ b/venv/Lib/site-packages/django/template/autoreload.py @@ -4,7 +4,9 @@ from django.dispatch import receiver from django.template import engines from django.template.backends.django import DjangoTemplates from django.utils._os import to_path -from django.utils.autoreload import autoreload_started, file_changed, is_django_path +from django.utils.autoreload import ( + autoreload_started, file_changed, is_django_path, +) def get_template_directories(): @@ -16,15 +18,15 @@ def get_template_directories(): if not isinstance(backend, DjangoTemplates): continue - items.update(Path.cwd() / to_path(dir) for dir in backend.engine.dirs if dir) + items.update(Path.cwd() / to_path(dir) for dir in backend.engine.dirs) for loader in backend.engine.template_loaders: - if not hasattr(loader, "get_dirs"): + if not hasattr(loader, 'get_dirs'): continue items.update( Path.cwd() / to_path(directory) for directory in loader.get_dirs() - if directory and not is_django_path(directory) + if not is_django_path(directory) ) return items @@ -37,13 +39,13 @@ def reset_loaders(): loader.reset() -@receiver(autoreload_started, dispatch_uid="template_loaders_watch_changes") +@receiver(autoreload_started, dispatch_uid='template_loaders_watch_changes') def watch_for_template_changes(sender, **kwargs): for directory in get_template_directories(): - sender.watch_dir(directory, "**/*") + sender.watch_dir(directory, '**/*') -@receiver(file_changed, dispatch_uid="template_loaders_file_changed") +@receiver(file_changed, dispatch_uid='template_loaders_file_changed') def template_changed(sender, file_path, **kwargs): for template_dir in get_template_directories(): if template_dir in file_path.parents: diff --git a/venv/Lib/site-packages/django/template/backends/base.py b/venv/Lib/site-packages/django/template/backends/base.py index 240733e..22628c6 100644 --- a/venv/Lib/site-packages/django/template/backends/base.py +++ b/venv/Lib/site-packages/django/template/backends/base.py @@ -1,4 +1,6 @@ -from django.core.exceptions import ImproperlyConfigured, SuspiciousFileOperation +from django.core.exceptions import ( + ImproperlyConfigured, SuspiciousFileOperation, +) from django.template.utils import get_app_template_dirs from django.utils._os import safe_join from django.utils.functional import cached_property @@ -16,20 +18,18 @@ class BaseEngine: `params` is a dict of configuration settings. """ params = params.copy() - self.name = params.pop("NAME") - self.dirs = list(params.pop("DIRS")) - self.app_dirs = params.pop("APP_DIRS") + self.name = params.pop('NAME') + self.dirs = list(params.pop('DIRS')) + self.app_dirs = params.pop('APP_DIRS') if params: raise ImproperlyConfigured( - "Unknown parameters: {}".format(", ".join(params)) - ) + "Unknown parameters: {}".format(", ".join(params))) @property def app_dirname(self): raise ImproperlyConfigured( "{} doesn't support loading templates from installed " - "applications.".format(self.__class__.__name__) - ) + "applications.".format(self.__class__.__name__)) def from_string(self, template_code): """ @@ -38,8 +38,8 @@ class BaseEngine: This method is optional. """ raise NotImplementedError( - "subclasses of BaseEngine should provide a from_string() method" - ) + "subclasses of BaseEngine should provide " + "a from_string() method") def get_template(self, template_name): """ @@ -48,8 +48,8 @@ class BaseEngine: Raise TemplateDoesNotExist if no such template exists. """ raise NotImplementedError( - "subclasses of BaseEngine must provide a get_template() method" - ) + "subclasses of BaseEngine must provide " + "a get_template() method") # Utility methods: they are provided to minimize code duplication and # security issues in third-party backends. diff --git a/venv/Lib/site-packages/django/template/backends/django.py b/venv/Lib/site-packages/django/template/backends/django.py index a440674..d99631c 100644 --- a/venv/Lib/site-packages/django/template/backends/django.py +++ b/venv/Lib/site-packages/django/template/backends/django.py @@ -13,16 +13,16 @@ from .base import BaseEngine class DjangoTemplates(BaseEngine): - app_dirname = "templates" + app_dirname = 'templates' def __init__(self, params): params = params.copy() - options = params.pop("OPTIONS").copy() - options.setdefault("autoescape", True) - options.setdefault("debug", settings.DEBUG) - options.setdefault("file_charset", "utf-8") - libraries = options.get("libraries", {}) - options["libraries"] = self.get_templatetag_libraries(libraries) + options = params.pop('OPTIONS').copy() + options.setdefault('autoescape', True) + options.setdefault('debug', settings.DEBUG) + options.setdefault('file_charset', 'utf-8') + libraries = options.get('libraries', {}) + options['libraries'] = self.get_templatetag_libraries(libraries) super().__init__(params) self.engine = Engine(self.dirs, self.app_dirs, **options) @@ -46,6 +46,7 @@ class DjangoTemplates(BaseEngine): class Template: + def __init__(self, template, backend): self.template = template self.backend = backend @@ -55,9 +56,7 @@ class Template: return self.template.origin def render(self, context=None, request=None): - context = make_context( - context, request, autoescape=self.backend.engine.autoescape - ) + context = make_context(context, request, autoescape=self.backend.engine.autoescape) try: return self.template.render(context) except TemplateDoesNotExist as exc: @@ -72,7 +71,7 @@ def copy_exception(exc, backend=None): """ backend = backend or exc.backend new = exc.__class__(*exc.args, tried=exc.tried, backend=backend, chain=exc.chain) - if hasattr(exc, "template_debug"): + if hasattr(exc, 'template_debug'): new.template_debug = exc.template_debug return new @@ -93,10 +92,10 @@ def get_installed_libraries(): django.templatetags.i18n is stored as i18n. """ libraries = {} - candidates = ["django.templatetags"] + candidates = ['django.templatetags'] candidates.extend( - "%s.templatetags" % app_config.name for app_config in apps.get_app_configs() - ) + '%s.templatetags' % app_config.name + for app_config in apps.get_app_configs()) for candidate in candidates: try: @@ -105,9 +104,9 @@ def get_installed_libraries(): # No templatetags package defined. This is safe to ignore. continue - if hasattr(pkg, "__path__"): + if hasattr(pkg, '__path__'): for name in get_package_libraries(pkg): - libraries[name[len(candidate) + 1 :]] = name + libraries[name[len(candidate) + 1:]] = name return libraries @@ -117,7 +116,7 @@ def get_package_libraries(pkg): Recursively yield template tag libraries defined in submodules of a package. """ - for entry in walk_packages(pkg.__path__, pkg.__name__ + "."): + for entry in walk_packages(pkg.__path__, pkg.__name__ + '.'): try: module = import_module(entry[1]) except ImportError as e: @@ -126,5 +125,5 @@ def get_package_libraries(pkg): "trying to load '%s': %s" % (entry[1], e) ) from e - if hasattr(module, "register"): + if hasattr(module, 'register'): yield entry[1] diff --git a/venv/Lib/site-packages/django/template/backends/dummy.py b/venv/Lib/site-packages/django/template/backends/dummy.py index 692382b..6be05ca 100644 --- a/venv/Lib/site-packages/django/template/backends/dummy.py +++ b/venv/Lib/site-packages/django/template/backends/dummy.py @@ -10,13 +10,14 @@ from .utils import csrf_input_lazy, csrf_token_lazy class TemplateStrings(BaseEngine): - app_dirname = "template_strings" + app_dirname = 'template_strings' def __init__(self, params): params = params.copy() - options = params.pop("OPTIONS").copy() + options = params.pop('OPTIONS').copy() if options: - raise ImproperlyConfigured("Unknown options: {}".format(", ".join(options))) + raise ImproperlyConfigured( + "Unknown options: {}".format(", ".join(options))) super().__init__(params) def from_string(self, template_code): @@ -26,27 +27,26 @@ class TemplateStrings(BaseEngine): tried = [] for template_file in self.iter_template_filenames(template_name): try: - with open(template_file, encoding="utf-8") as fp: + with open(template_file, encoding='utf-8') as fp: template_code = fp.read() except FileNotFoundError: - tried.append( - ( - Origin(template_file, template_name, self), - "Source does not exist", - ) - ) + tried.append(( + Origin(template_file, template_name, self), + 'Source does not exist', + )) else: return Template(template_code) raise TemplateDoesNotExist(template_name, tried=tried, backend=self) class Template(string.Template): + def render(self, context=None, request=None): if context is None: context = {} else: context = {k: conditional_escape(v) for k, v in context.items()} if request is not None: - context["csrf_input"] = csrf_input_lazy(request) - context["csrf_token"] = csrf_token_lazy(request) + context['csrf_input'] = csrf_input_lazy(request) + context['csrf_token'] = csrf_token_lazy(request) return self.safe_substitute(context) diff --git a/venv/Lib/site-packages/django/template/backends/jinja2.py b/venv/Lib/site-packages/django/template/backends/jinja2.py index 92cc225..d1e5217 100644 --- a/venv/Lib/site-packages/django/template/backends/jinja2.py +++ b/venv/Lib/site-packages/django/template/backends/jinja2.py @@ -12,25 +12,24 @@ from .base import BaseEngine class Jinja2(BaseEngine): - app_dirname = "jinja2" + app_dirname = 'jinja2' def __init__(self, params): params = params.copy() - options = params.pop("OPTIONS").copy() + options = params.pop('OPTIONS').copy() super().__init__(params) - self.context_processors = options.pop("context_processors", []) + self.context_processors = options.pop('context_processors', []) - environment = options.pop("environment", "jinja2.Environment") + environment = options.pop('environment', 'jinja2.Environment') environment_cls = import_string(environment) - if "loader" not in options: - options["loader"] = jinja2.FileSystemLoader(self.template_dirs) - options.setdefault("autoescape", True) - options.setdefault("auto_reload", settings.DEBUG) - options.setdefault( - "undefined", jinja2.DebugUndefined if settings.DEBUG else jinja2.Undefined - ) + if 'loader' not in options: + options['loader'] = jinja2.FileSystemLoader(self.template_dirs) + options.setdefault('autoescape', True) + options.setdefault('auto_reload', settings.DEBUG) + options.setdefault('undefined', + jinja2.DebugUndefined if settings.DEBUG else jinja2.Undefined) self.env = environment_cls(**options) @@ -53,23 +52,22 @@ class Jinja2(BaseEngine): class Template: + def __init__(self, template, backend): self.template = template self.backend = backend self.origin = Origin( - name=template.filename, - template_name=template.name, + name=template.filename, template_name=template.name, ) def render(self, context=None, request=None): from .utils import csrf_input_lazy, csrf_token_lazy - if context is None: context = {} if request is not None: - context["request"] = request - context["csrf_input"] = csrf_input_lazy(request) - context["csrf_token"] = csrf_token_lazy(request) + context['request'] = request + context['csrf_input'] = csrf_input_lazy(request) + context['csrf_token'] = csrf_token_lazy(request) for context_processor in self.backend.template_context_processors: context.update(context_processor(request)) try: @@ -85,7 +83,6 @@ class Origin: A container to hold debug information as described in the template API documentation. """ - def __init__(self, name, template_name): self.name = name self.template_name = template_name @@ -102,27 +99,27 @@ def get_exception_info(exception): if source is None: exception_file = Path(exception.filename) if exception_file.exists(): - with open(exception_file, "r") as fp: + with open(exception_file, 'r') as fp: source = fp.read() if source is not None: - lines = list(enumerate(source.strip().split("\n"), start=1)) + lines = list(enumerate(source.strip().split('\n'), start=1)) during = lines[lineno - 1][1] total = len(lines) top = max(0, lineno - context_lines - 1) bottom = min(total, lineno + context_lines) else: - during = "" + during = '' lines = [] total = top = bottom = 0 return { - "name": exception.filename, - "message": exception.message, - "source_lines": lines[top:bottom], - "line": lineno, - "before": "", - "during": during, - "after": "", - "total": total, - "top": top, - "bottom": bottom, + 'name': exception.filename, + 'message': exception.message, + 'source_lines': lines[top:bottom], + 'line': lineno, + 'before': '', + 'during': during, + 'after': '', + 'total': total, + 'top': top, + 'bottom': bottom, } diff --git a/venv/Lib/site-packages/django/template/backends/utils.py b/venv/Lib/site-packages/django/template/backends/utils.py index 880959d..1396ae7 100644 --- a/venv/Lib/site-packages/django/template/backends/utils.py +++ b/venv/Lib/site-packages/django/template/backends/utils.py @@ -7,8 +7,7 @@ from django.utils.safestring import SafeString def csrf_input(request): return format_html( '<input type="hidden" name="csrfmiddlewaretoken" value="{}">', - get_token(request), - ) + get_token(request)) csrf_input_lazy = lazy(csrf_input, SafeString, str) diff --git a/venv/Lib/site-packages/django/template/base.py b/venv/Lib/site-packages/django/template/base.py index 8dfa803..85ce3c2 100644 --- a/venv/Lib/site-packages/django/template/base.py +++ b/venv/Lib/site-packages/django/template/base.py @@ -59,36 +59,41 @@ from django.template.context import BaseContext from django.utils.formats import localize from django.utils.html import conditional_escape, escape from django.utils.regex_helper import _lazy_re_compile -from django.utils.safestring import SafeData, SafeString, mark_safe -from django.utils.text import get_text_list, smart_split, unescape_string_literal +from django.utils.safestring import SafeData, mark_safe +from django.utils.text import ( + get_text_list, smart_split, unescape_string_literal, +) from django.utils.timezone import template_localtime from django.utils.translation import gettext_lazy, pgettext_lazy from .exceptions import TemplateSyntaxError # template syntax constants -FILTER_SEPARATOR = "|" -FILTER_ARGUMENT_SEPARATOR = ":" -VARIABLE_ATTRIBUTE_SEPARATOR = "." -BLOCK_TAG_START = "{%" -BLOCK_TAG_END = "%}" -VARIABLE_TAG_START = "{{" -VARIABLE_TAG_END = "}}" -COMMENT_TAG_START = "{#" -COMMENT_TAG_END = "#}" -SINGLE_BRACE_START = "{" -SINGLE_BRACE_END = "}" +FILTER_SEPARATOR = '|' +FILTER_ARGUMENT_SEPARATOR = ':' +VARIABLE_ATTRIBUTE_SEPARATOR = '.' +BLOCK_TAG_START = '{%' +BLOCK_TAG_END = '%}' +VARIABLE_TAG_START = '{{' +VARIABLE_TAG_END = '}}' +COMMENT_TAG_START = '{#' +COMMENT_TAG_END = '#}' +TRANSLATOR_COMMENT_MARK = 'Translators' +SINGLE_BRACE_START = '{' +SINGLE_BRACE_END = '}' # what to report as the origin for templates that come from non-loader sources # (e.g. strings) -UNKNOWN_SOURCE = "<unknown source>" +UNKNOWN_SOURCE = '<unknown source>' -# Match BLOCK_TAG_*, VARIABLE_TAG_*, and COMMENT_TAG_* tags and capture the -# entire tag, including start/end delimiters. Using re.compile() is faster -# than instantiating SimpleLazyObject with _lazy_re_compile(). -tag_re = re.compile(r"({%.*?%}|{{.*?}}|{#.*?#})") +# match a variable or block tag and capture the entire tag, including start/end +# delimiters +tag_re = (_lazy_re_compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % + (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), + re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), + re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))) -logger = logging.getLogger("django.template") +logger = logging.getLogger('django.template') class TokenType(Enum): @@ -99,6 +104,7 @@ class TokenType(Enum): class VariableDoesNotExist(Exception): + def __init__(self, msg, params=()): self.msg = msg self.params = params @@ -116,22 +122,18 @@ class Origin: def __str__(self): return self.name - def __repr__(self): - return "<%s name=%r>" % (self.__class__.__qualname__, self.name) - def __eq__(self, other): return ( - isinstance(other, Origin) - and self.name == other.name - and self.loader == other.loader + isinstance(other, Origin) and + self.name == other.name and + self.loader == other.loader ) @property def loader_name(self): if self.loader: - return "%s.%s" % ( - self.loader.__module__, - self.loader.__class__.__name__, + return '%s.%s' % ( + self.loader.__module__, self.loader.__class__.__name__, ) @@ -143,7 +145,6 @@ class Template: # e.g. Template('...').render(Context({...})) if engine is None: from .engine import Engine - engine = Engine.get_default() if origin is None: origin = Origin(UNKNOWN_SOURCE) @@ -157,12 +158,6 @@ class Template: for node in self.nodelist: yield from node - def __repr__(self): - return '<%s template_string="%s...">' % ( - self.__class__.__qualname__, - self.source[:20].replace("\n", ""), - ) - def _render(self, context): return self.nodelist.render(context) @@ -190,9 +185,7 @@ class Template: tokens = lexer.tokenize() parser = Parser( - tokens, - self.engine.template_libraries, - self.engine.template_builtins, + tokens, self.engine.template_libraries, self.engine.template_builtins, self.origin, ) @@ -264,30 +257,30 @@ class Template: try: message = str(exception.args[0]) except (IndexError, UnicodeDecodeError): - message = "(Could not get exception message)" + message = '(Could not get exception message)' return { - "message": message, - "source_lines": source_lines[top:bottom], - "before": before, - "during": during, - "after": after, - "top": top, - "bottom": bottom, - "total": total, - "line": line, - "name": self.origin.name, - "start": start, - "end": end, + 'message': message, + 'source_lines': source_lines[top:bottom], + 'before': before, + 'during': during, + 'after': after, + 'top': top, + 'bottom': bottom, + 'total': total, + 'line': line, + 'name': self.origin.name, + 'start': start, + 'end': end, } def linebreak_iter(template_source): yield 0 - p = template_source.find("\n") + p = template_source.find('\n') while p >= 0: yield p + 1 - p = template_source.find("\n", p + 1) + p = template_source.find('\n', p + 1) yield len(template_source) + 1 @@ -315,12 +308,10 @@ class Token: self.lineno = lineno self.position = position - def __repr__(self): + def __str__(self): token_name = self.token_type.name.capitalize() - return '<%s token: "%s...">' % ( - token_name, - self.contents[:20].replace("\n", ""), - ) + return ('<%s token: "%s...">' % + (token_name, self.contents[:20].replace('\n', ''))) def split_contents(self): split = [] @@ -328,12 +319,12 @@ class Token: for bit in bits: # Handle translation-marked template pieces if bit.startswith(('_("', "_('")): - sentinel = bit[2] + ")" + sentinel = bit[2] + ')' trans_bit = [bit] while not bit.endswith(sentinel): bit = next(bits) trans_bit.append(bit) - bit = " ".join(trans_bit) + bit = ' '.join(trans_bit) split.append(bit) return split @@ -343,13 +334,6 @@ class Lexer: self.template_string = template_string self.verbatim = False - def __repr__(self): - return '<%s template_string="%s...", verbatim=%s>' % ( - self.__class__.__qualname__, - self.template_string[:20].replace("\n", ""), - self.verbatim, - ) - def tokenize(self): """ Return a list of tokens from a given template_string. @@ -357,11 +341,11 @@ class Lexer: in_tag = False lineno = 1 result = [] - for token_string in tag_re.split(self.template_string): - if token_string: - result.append(self.create_token(token_string, None, lineno, in_tag)) - lineno += token_string.count("\n") + for bit in tag_re.split(self.template_string): + if bit: + result.append(self.create_token(bit, None, lineno, in_tag)) in_tag = not in_tag + lineno += bit.count('\n') return result def create_token(self, token_string, position, lineno, in_tag): @@ -370,66 +354,53 @@ class Lexer: If in_tag is True, we are processing something that matched a tag, otherwise it should be treated as a literal string. """ - if in_tag: - # The [0:2] and [2:-2] ranges below strip off *_TAG_START and - # *_TAG_END. The 2's are hard-coded for performance. Using - # len(BLOCK_TAG_START) would permit BLOCK_TAG_START to be - # different, but it's not likely that the TAG_START values will - # change anytime soon. - token_start = token_string[0:2] - if token_start == BLOCK_TAG_START: - content = token_string[2:-2].strip() - if self.verbatim: - # Then a verbatim block is being processed. - if content != self.verbatim: - return Token(TokenType.TEXT, token_string, position, lineno) - # Otherwise, the current verbatim block is ending. - self.verbatim = False - elif content[:9] in ("verbatim", "verbatim "): - # Then a verbatim block is starting. - self.verbatim = "end%s" % content - return Token(TokenType.BLOCK, content, position, lineno) - if not self.verbatim: - content = token_string[2:-2].strip() - if token_start == VARIABLE_TAG_START: - return Token(TokenType.VAR, content, position, lineno) - # BLOCK_TAG_START was handled above. - assert token_start == COMMENT_TAG_START + if in_tag and token_string.startswith(BLOCK_TAG_START): + # The [2:-2] ranges below strip off *_TAG_START and *_TAG_END. + # We could do len(BLOCK_TAG_START) to be more "correct", but we've + # hard-coded the 2s here for performance. And it's not like + # the TAG_START values are going to change anytime, anyway. + block_content = token_string[2:-2].strip() + if self.verbatim and block_content == self.verbatim: + self.verbatim = False + if in_tag and not self.verbatim: + if token_string.startswith(VARIABLE_TAG_START): + return Token(TokenType.VAR, token_string[2:-2].strip(), position, lineno) + elif token_string.startswith(BLOCK_TAG_START): + if block_content[:9] in ('verbatim', 'verbatim '): + self.verbatim = 'end%s' % block_content + return Token(TokenType.BLOCK, block_content, position, lineno) + elif token_string.startswith(COMMENT_TAG_START): + content = '' + if token_string.find(TRANSLATOR_COMMENT_MARK): + content = token_string[2:-2].strip() return Token(TokenType.COMMENT, content, position, lineno) - return Token(TokenType.TEXT, token_string, position, lineno) + else: + return Token(TokenType.TEXT, token_string, position, lineno) class DebugLexer(Lexer): - def _tag_re_split_positions(self): - last = 0 - for match in tag_re.finditer(self.template_string): - start, end = match.span() - yield last, start - yield start, end - last = end - yield last, len(self.template_string) - - # This parallels the use of tag_re.split() in Lexer.tokenize(). - def _tag_re_split(self): - for position in self._tag_re_split_positions(): - yield self.template_string[slice(*position)], position - def tokenize(self): """ Split a template string into tokens and annotates each token with its start and end position in the source. This is slower than the default lexer so only use it when debug is True. """ - # For maintainability, it is helpful if the implementation below can - # continue to closely parallel Lexer.tokenize()'s implementation. - in_tag = False lineno = 1 result = [] - for token_string, position in self._tag_re_split(): - if token_string: - result.append(self.create_token(token_string, position, lineno, in_tag)) - lineno += token_string.count("\n") - in_tag = not in_tag + upto = 0 + for match in tag_re.finditer(self.template_string): + start, end = match.span() + if start > upto: + token_string = self.template_string[upto:start] + result.append(self.create_token(token_string, (upto, start), lineno, in_tag=False)) + lineno += token_string.count('\n') + token_string = self.template_string[start:end] + result.append(self.create_token(token_string, (start, end), lineno, in_tag=True)) + lineno += token_string.count('\n') + upto = end + last_bit = self.template_string[upto:] + if last_bit: + result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), lineno, in_tag=False)) return result @@ -452,9 +423,6 @@ class Parser: self.add_library(builtin) self.origin = origin - def __repr__(self): - return "<%s tokens=%r>" % (self.__class__.__qualname__, self.tokens) - def parse(self, parse_until=None): """ Iterate through the parser tokens and compiles each one into a node. @@ -470,25 +438,22 @@ class Parser: while self.tokens: token = self.next_token() # Use the raw values here for TokenType.* for a tiny performance boost. - token_type = token.token_type.value - if token_type == 0: # TokenType.TEXT + if token.token_type.value == 0: # TokenType.TEXT self.extend_nodelist(nodelist, TextNode(token.contents), token) - elif token_type == 1: # TokenType.VAR + elif token.token_type.value == 1: # TokenType.VAR if not token.contents: - raise self.error( - token, "Empty variable tag on line %d" % token.lineno - ) + raise self.error(token, 'Empty variable tag on line %d' % token.lineno) try: filter_expression = self.compile_filter(token.contents) except TemplateSyntaxError as e: raise self.error(token, e) var_node = VariableNode(filter_expression) self.extend_nodelist(nodelist, var_node, token) - elif token_type == 2: # TokenType.BLOCK + elif token.token_type.value == 2: # TokenType.BLOCK try: command = token.contents.split()[0] except IndexError: - raise self.error(token, "Empty block tag on line %d" % token.lineno) + raise self.error(token, 'Empty block tag on line %d' % token.lineno) if command in parse_until: # A matching token has been reached. Return control to # the caller. Put the token back on the token list so the @@ -529,10 +494,9 @@ class Parser: # Check that non-text nodes don't appear before an extends tag. if node.must_be_first and nodelist.contains_nontext: raise self.error( - token, - "%r must be the first tag in the template." % node, + token, '%r must be the first tag in the template.' % node, ) - if not isinstance(node, TextNode): + if isinstance(nodelist, NodeList) and not isinstance(node, TextNode): nodelist.contains_nontext = True # Set origin and token here since we can't modify the node __init__() # method. @@ -549,7 +513,7 @@ class Parser: """ if not isinstance(e, Exception): e = TemplateSyntaxError(e) - if not hasattr(e, "token"): + if not hasattr(e, 'token'): e.token = token return e @@ -558,17 +522,16 @@ class Parser: raise self.error( token, "Invalid block tag on line %d: '%s', expected %s. Did you " - "forget to register or load this tag?" - % ( + "forget to register or load this tag?" % ( token.lineno, command, - get_text_list(["'%s'" % p for p in parse_until], "or"), + get_text_list(["'%s'" % p for p in parse_until], 'or'), ), ) raise self.error( token, "Invalid block tag on line %d: '%s'. Did you forget to register " - "or load this tag?" % (token.lineno, command), + "or load this tag?" % (token.lineno, command) ) def unclosed_block_tag(self, parse_until): @@ -576,7 +539,7 @@ class Parser: msg = "Unclosed tag on line %d: '%s'. Looking for one of: %s." % ( token.lineno, command, - ", ".join(parse_until), + ', '.join(parse_until), ) raise self.error(token, msg) @@ -615,10 +578,10 @@ constant_string = r""" %(strdq)s| %(strsq)s) """ % { - "strdq": r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string - "strsq": r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string - "i18n_open": re.escape("_("), - "i18n_close": re.escape(")"), + 'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string + 'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string + 'i18n_open': re.escape("_("), + 'i18n_close': re.escape(")"), } constant_string = constant_string.replace("\n", "") @@ -634,11 +597,11 @@ filter_raw_string = r""" ) )? )""" % { - "constant": constant_string, - "num": r"[-+\.]?\d[\d\.e]*", - "var_chars": r"\w\.", - "filter_sep": re.escape(FILTER_SEPARATOR), - "arg_sep": re.escape(FILTER_ARGUMENT_SEPARATOR), + 'constant': constant_string, + 'num': r'[-+\.]?\d[\d\.e]*', + 'var_chars': r'\w\.', + 'filter_sep': re.escape(FILTER_SEPARATOR), + 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR), } filter_re = _lazy_re_compile(filter_raw_string, re.VERBOSE) @@ -658,7 +621,6 @@ class FilterExpression: >>> fe.var <Variable: 'variable'> """ - def __init__(self, token, parser): self.token = token matches = filter_re.finditer(token) @@ -668,27 +630,26 @@ class FilterExpression: for match in matches: start = match.start() if upto != start: - raise TemplateSyntaxError( - "Could not parse some characters: " - "%s|%s|%s" % (token[:upto], token[upto:start], token[start:]) - ) + raise TemplateSyntaxError("Could not parse some characters: " + "%s|%s|%s" % + (token[:upto], token[upto:start], + token[start:])) if var_obj is None: - var, constant = match["var"], match["constant"] + var, constant = match['var'], match['constant'] if constant: try: var_obj = Variable(constant).resolve({}) except VariableDoesNotExist: var_obj = None elif var is None: - raise TemplateSyntaxError( - "Could not find variable at start of %s." % token - ) + raise TemplateSyntaxError("Could not find variable at " + "start of %s." % token) else: var_obj = Variable(var) else: - filter_name = match["filter_name"] + filter_name = match['filter_name'] args = [] - constant_arg, var_arg = match["constant_arg"], match["var_arg"] + constant_arg, var_arg = match['constant_arg'], match['var_arg'] if constant_arg: args.append((False, Variable(constant_arg).resolve({}))) elif var_arg: @@ -698,10 +659,8 @@ class FilterExpression: filters.append((filter_func, args)) upto = match.end() if upto != len(token): - raise TemplateSyntaxError( - "Could not parse the remainder: '%s' " - "from '%s'" % (token[upto:], token) - ) + raise TemplateSyntaxError("Could not parse the remainder: '%s' " + "from '%s'" % (token[upto:], token)) self.filters = filters self.var = var_obj @@ -716,7 +675,7 @@ class FilterExpression: else: string_if_invalid = context.template.engine.string_if_invalid if string_if_invalid: - if "%s" in string_if_invalid: + if '%s' in string_if_invalid: return string_if_invalid % self.var else: return string_if_invalid @@ -731,13 +690,13 @@ class FilterExpression: arg_vals.append(mark_safe(arg)) else: arg_vals.append(arg.resolve(context)) - if getattr(func, "expects_localtime", False): + if getattr(func, 'expects_localtime', False): obj = template_localtime(obj, context.use_tz) - if getattr(func, "needs_autoescape", False): + if getattr(func, 'needs_autoescape', False): new_obj = func(obj, autoescape=context.autoescape, *arg_vals) else: new_obj = func(obj, *arg_vals) - if getattr(func, "is_safe", False) and isinstance(obj, SafeData): + if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): obj = mark_safe(new_obj) else: obj = new_obj @@ -755,20 +714,15 @@ class FilterExpression: dlen = len(defaults or []) # Not enough OR Too many if plen < (alen - dlen) or plen > alen: - raise TemplateSyntaxError( - "%s requires %d arguments, %d provided" % (name, alen - dlen, plen) - ) + raise TemplateSyntaxError("%s requires %d arguments, %d provided" % + (name, alen - dlen, plen)) return True - args_check = staticmethod(args_check) def __str__(self): return self.token - def __repr__(self): - return "<%s %r>" % (self.__class__.__qualname__, self.token) - class Variable: """ @@ -797,7 +751,8 @@ class Variable: self.message_context = None if not isinstance(var, str): - raise TypeError("Variable must be a string or number, got %s" % type(var)) + raise TypeError( + "Variable must be a string or number, got %s" % type(var)) try: # First try to treat this variable as a number. # @@ -807,16 +762,16 @@ class Variable: # Try to interpret values containing a period or an 'e'/'E' # (possibly scientific notation) as a float; otherwise, try int. - if "." in var or "e" in var.lower(): + if '.' in var or 'e' in var.lower(): self.literal = float(var) # "2." is invalid - if var[-1] == ".": + if var.endswith('.'): raise ValueError else: self.literal = int(var) except ValueError: # A ValueError means that the variable isn't a number. - if var[0:2] == "_(" and var[-1] == ")": + if var.startswith('_(') and var.endswith(')'): # The result of the lookup should be translated at rendering # time. self.translate = True @@ -828,11 +783,10 @@ class Variable: except ValueError: # Otherwise we'll set self.lookups so that resolve() knows we're # dealing with a bonafide variable - if VARIABLE_ATTRIBUTE_SEPARATOR + "_" in var or var[0] == "_": - raise TemplateSyntaxError( - "Variables and attributes may " - "not begin with underscores: '%s'" % var - ) + if var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_': + raise TemplateSyntaxError("Variables and attributes may " + "not begin with underscores: '%s'" % + var) self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) def resolve(self, context): @@ -845,7 +799,7 @@ class Variable: value = self.literal if self.translate: is_safe = isinstance(value, SafeData) - msgid = value.replace("%", "%%") + msgid = value.replace('%', '%%') msgid = mark_safe(msgid) if is_safe else msgid if self.message_context: return pgettext_lazy(self.message_context, msgid) @@ -878,9 +832,7 @@ class Variable: except (TypeError, AttributeError, KeyError, ValueError, IndexError): try: # attribute lookup # Don't return class attributes if the class is the context: - if isinstance(current, BaseContext) and getattr( - type(current), bit - ): + if isinstance(current, BaseContext) and getattr(type(current), bit): raise AttributeError current = getattr(current, bit) except (TypeError, AttributeError): @@ -889,20 +841,17 @@ class Variable: raise try: # list-index lookup current = current[int(bit)] - except ( - IndexError, # list index out of range - ValueError, # invalid literal for int() - KeyError, # current is a dict without `int(bit)` key - TypeError, - ): # unsubscriptable object - raise VariableDoesNotExist( - "Failed lookup for key [%s] in %r", - (bit, current), - ) # missing attribute + except (IndexError, # list index out of range + ValueError, # invalid literal for int() + KeyError, # current is a dict without `int(bit)` key + TypeError): # unsubscriptable object + raise VariableDoesNotExist("Failed lookup for key " + "[%s] in %r", + (bit, current)) # missing attribute if callable(current): - if getattr(current, "do_not_call_in_templates", False): + if getattr(current, 'do_not_call_in_templates', False): pass - elif getattr(current, "alters_data", False): + elif getattr(current, 'alters_data', False): current = context.template.engine.string_if_invalid else: try: # method call (assuming no args required) @@ -912,13 +861,11 @@ class Variable: try: signature.bind() except TypeError: # arguments *were* required - current = ( - context.template.engine.string_if_invalid - ) # invalid method call + current = context.template.engine.string_if_invalid # invalid method call else: raise except Exception as e: - template_name = getattr(context, "template_name", None) or "unknown" + template_name = getattr(context, 'template_name', None) or 'unknown' logger.debug( "Exception while resolving variable '%s' in template '%s'.", bit, @@ -926,7 +873,7 @@ class Variable: exc_info=True, ) - if getattr(e, "silent_variable_failure", False): + if getattr(e, 'silent_variable_failure', False): current = context.template.engine.string_if_invalid else: raise @@ -938,7 +885,7 @@ class Node: # Set this to True for nodes that must be first in the template (although # they can be preceded by text nodes. must_be_first = False - child_nodelists = ("nodelist",) + child_nodelists = ('nodelist',) token = None def render(self, context): @@ -957,20 +904,8 @@ class Node: try: return self.render(context) except Exception as e: - if context.template.engine.debug: - # Store the actual node that caused the exception. - if not hasattr(e, "_culprit_node"): - e._culprit_node = self - if ( - not hasattr(e, "template_debug") - and context.render_context.template.origin == e._culprit_node.origin - ): - e.template_debug = ( - context.render_context.template.get_exception_info( - e, - e._culprit_node.token, - ) - ) + if context.template.engine.debug and not hasattr(e, 'template_debug'): + e.template_debug = context.render_context.template.get_exception_info(e, self.token) raise def __iter__(self): @@ -997,7 +932,14 @@ class NodeList(list): contains_nontext = False def render(self, context): - return SafeString("".join([node.render_annotated(context) for node in self])) + bits = [] + for node in self: + if isinstance(node, Node): + bit = node.render_annotated(context) + else: + bit = node + bits.append(str(bit)) + return mark_safe(''.join(bits)) def get_nodes_by_type(self, nodetype): "Return a list of all nodes of the given type" @@ -1008,8 +950,6 @@ class NodeList(list): class TextNode(Node): - child_nodelists = () - def __init__(self, s): self.s = s @@ -1019,15 +959,6 @@ class TextNode(Node): def render(self, context): return self.s - def render_annotated(self, context): - """ - Return the given value. - - The default implementation of this method handles exceptions raised - during rendering, which is not necessary for text nodes. - """ - return self.s - def render_value_in_context(value, context): """ @@ -1046,8 +977,6 @@ def render_value_in_context(value, context): class VariableNode(Node): - child_nodelists = () - def __init__(self, filter_expression): self.filter_expression = filter_expression @@ -1061,7 +990,7 @@ class VariableNode(Node): # Unicode conversion can fail sometimes for reasons out of our # control (e.g. exception rendering). In that case, we fail # quietly. - return "" + return '' return render_value_in_context(output, context) @@ -1092,7 +1021,7 @@ def token_kwargs(bits, parser, support_legacy=False): if not kwarg_format: if not support_legacy: return {} - if len(bits) < 3 or bits[1] != "as": + if len(bits) < 3 or bits[1] != 'as': return {} kwargs = {} @@ -1104,13 +1033,13 @@ def token_kwargs(bits, parser, support_legacy=False): key, value = match.groups() del bits[:1] else: - if len(bits) < 3 or bits[1] != "as": + if len(bits) < 3 or bits[1] != 'as': return kwargs key, value = bits[2], bits[0] del bits[:3] kwargs[key] = parser.compile_filter(value) if bits and not kwarg_format: - if bits[0] != "and": + if bits[0] != 'and': return kwargs del bits[:1] return kwargs diff --git a/venv/Lib/site-packages/django/template/context.py b/venv/Lib/site-packages/django/template/context.py index ccf0b43..f0a0cf2 100644 --- a/venv/Lib/site-packages/django/template/context.py +++ b/venv/Lib/site-packages/django/template/context.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from copy import copy # Hard-coded processor for easier use of CSRF protection. -_builtin_context_processors = ("django.template.context_processors.csrf",) +_builtin_context_processors = ('django.template.context_processors.csrf',) class ContextPopException(Exception): @@ -29,7 +29,7 @@ class BaseContext: self._reset_dicts(dict_) def _reset_dicts(self, value=None): - builtins = {"True": True, "False": False, "None": None} + builtins = {'True': True, 'False': False, 'None': None} self.dicts = [builtins] if value is not None: self.dicts.append(value) @@ -132,7 +132,6 @@ class BaseContext: class Context(BaseContext): "A stack container for variable context" - def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None): self.autoescape = autoescape self.use_l10n = use_l10n @@ -161,8 +160,8 @@ class Context(BaseContext): def update(self, other_dict): "Push other_dict to the stack of dictionaries in the Context" - if not hasattr(other_dict, "__getitem__"): - raise TypeError("other_dict must be a mapping (dictionary-like) object.") + if not hasattr(other_dict, '__getitem__'): + raise TypeError('other_dict must be a mapping (dictionary-like) object.') if isinstance(other_dict, BaseContext): other_dict = other_dict.dicts[1:].pop() return ContextDict(self, other_dict) @@ -183,7 +182,6 @@ class RenderContext(BaseContext): rendering of other templates as they would if they were stored in the normal template context. """ - template = None def __iter__(self): @@ -219,16 +217,7 @@ class RequestContext(Context): Additional processors can be specified as a list of callables using the "processors" keyword argument. """ - - def __init__( - self, - request, - dict_=None, - processors=None, - use_l10n=None, - use_tz=None, - autoescape=True, - ): + def __init__(self, request, dict_=None, processors=None, use_l10n=None, use_tz=None, autoescape=True): super().__init__(dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape) self.request = request self._processors = () if processors is None else tuple(processors) @@ -248,7 +237,8 @@ class RequestContext(Context): self.template = template # Set context processors according to the template engine's settings. - processors = template.engine.template_context_processors + self._processors + processors = (template.engine.template_context_processors + + self._processors) updates = {} for processor in processors: updates.update(processor(self.request)) @@ -265,7 +255,7 @@ class RequestContext(Context): new_context = super().new(values) # This is for backwards-compatibility: RequestContexts created via # Context.new don't include values from context processors. - if hasattr(new_context, "_processors_index"): + if hasattr(new_context, '_processors_index'): del new_context._processors_index return new_context @@ -275,9 +265,7 @@ def make_context(context, request=None, **kwargs): Create a suitable Context from a plain dict and optionally an HttpRequest. """ if context is not None and not isinstance(context, dict): - raise TypeError( - "context must be a dict rather than %s." % context.__class__.__name__ - ) + raise TypeError('context must be a dict rather than %s.' % context.__class__.__name__) if request is None: context = Context(context, **kwargs) else: diff --git a/venv/Lib/site-packages/django/template/context_processors.py b/venv/Lib/site-packages/django/template/context_processors.py index 3275303..25ac1f2 100644 --- a/venv/Lib/site-packages/django/template/context_processors.py +++ b/venv/Lib/site-packages/django/template/context_processors.py @@ -19,18 +19,17 @@ def csrf(request): Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if it has not been provided by either a view decorator or the middleware """ - def _get_val(): token = get_token(request) if token is None: # In order to be able to provide debugging info in the # case of misconfiguration, we use a sentinel value # instead of returning an empty dict. - return "NOTPROVIDED" + return 'NOTPROVIDED' else: return token - return {"csrf_token": SimpleLazyObject(_get_val)} + return {'csrf_token': SimpleLazyObject(_get_val)} def debug(request): @@ -38,52 +37,46 @@ def debug(request): Return context variables helpful for debugging. """ context_extras = {} - if settings.DEBUG and request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS: - context_extras["debug"] = True + if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: + context_extras['debug'] = True from django.db import connections # Return a lazy reference that computes connection.queries on access, # to ensure it contains queries triggered after this function runs. - context_extras["sql_queries"] = lazy( - lambda: list( - itertools.chain.from_iterable( - connections[x].queries for x in connections - ) - ), - list, + context_extras['sql_queries'] = lazy( + lambda: list(itertools.chain.from_iterable(connections[x].queries for x in connections)), + list ) return context_extras def i18n(request): from django.utils import translation - return { - "LANGUAGES": settings.LANGUAGES, - "LANGUAGE_CODE": translation.get_language(), - "LANGUAGE_BIDI": translation.get_language_bidi(), + 'LANGUAGES': settings.LANGUAGES, + 'LANGUAGE_CODE': translation.get_language(), + 'LANGUAGE_BIDI': translation.get_language_bidi(), } def tz(request): from django.utils import timezone - - return {"TIME_ZONE": timezone.get_current_timezone_name()} + return {'TIME_ZONE': timezone.get_current_timezone_name()} def static(request): """ Add static-related context variables to the context. """ - return {"STATIC_URL": settings.STATIC_URL} + return {'STATIC_URL': settings.STATIC_URL} def media(request): """ Add media-related context variables to the context. """ - return {"MEDIA_URL": settings.MEDIA_URL} + return {'MEDIA_URL': settings.MEDIA_URL} def request(request): - return {"request": request} + return {'request': request} diff --git a/venv/Lib/site-packages/django/template/defaultfilters.py b/venv/Lib/site-packages/django/template/defaultfilters.py index fa2b854..1c84458 100644 --- a/venv/Lib/site-packages/django/template/defaultfilters.py +++ b/venv/Lib/site-packages/django/template/defaultfilters.py @@ -11,18 +11,18 @@ from urllib.parse import quote from django.utils import formats from django.utils.dateformat import format, time_format from django.utils.encoding import iri_to_uri -from django.utils.html import avoid_wrapping, conditional_escape, escape, escapejs -from django.utils.html import json_script as _json_script -from django.utils.html import linebreaks, strip_tags -from django.utils.html import urlize as _urlize +from django.utils.html import ( + avoid_wrapping, conditional_escape, escape, escapejs, + json_script as _json_script, linebreaks, strip_tags, urlize as _urlize, +) from django.utils.safestring import SafeData, mark_safe -from django.utils.text import Truncator, normalize_newlines, phone2numeric -from django.utils.text import slugify as _slugify -from django.utils.text import wrap +from django.utils.text import ( + Truncator, normalize_newlines, phone2numeric, slugify as _slugify, wrap, +) from django.utils.timesince import timesince, timeuntil from django.utils.translation import gettext, ngettext -from .base import VARIABLE_ATTRIBUTE_SEPARATOR +from .base import Variable, VariableDoesNotExist from .library import Library register = Library() @@ -32,26 +32,23 @@ register = Library() # STRING DECORATOR # ####################### - def stringfilter(func): """ Decorator for filters which should only receive strings. The object passed as the first positional argument will be converted to a string. """ - def _dec(*args, **kwargs): args = list(args) args[0] = str(args[0]) - if isinstance(args[0], SafeData) and getattr( - _dec._decorated_function, "is_safe", False - ): + if (isinstance(args[0], SafeData) and + getattr(_dec._decorated_function, 'is_safe', False)): return mark_safe(func(*args, **kwargs)) return func(*args, **kwargs) # Include a reference to the real function (used to check original # arguments by the template parser, and to bear the 'is_safe' attribute # when multiple decorators are applied). - _dec._decorated_function = getattr(func, "_decorated_function", func) + _dec._decorated_function = getattr(func, '_decorated_function', func) return wraps(func)(_dec) @@ -60,7 +57,6 @@ def stringfilter(func): # STRINGS # ################### - @register.filter(is_safe=True) @stringfilter def addslashes(value): @@ -69,7 +65,7 @@ def addslashes(value): example. Less useful for escaping JavaScript; use the ``escapejs`` filter instead. """ - return value.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'") + return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") @register.filter(is_safe=True) @@ -130,29 +126,13 @@ def floatformat(text, arg=-1): * {{ 6666.6666|floatformat:"2g" }} displays "6,666.67" * {{ 10000|floatformat:"g" }} displays "10,000" - If arg has the 'u' suffix, force the result to be unlocalized. When the - active locale is pl (Polish): - - * {{ 66666.6666|floatformat:"2" }} displays "66666,67" - * {{ 66666.6666|floatformat:"2u" }} displays "66666.67" - If the input float is infinity or NaN, display the string representation of that value. """ force_grouping = False - use_l10n = True - if isinstance(arg, str): - last_char = arg[-1] - if arg[-2:] in {"gu", "ug"}: - force_grouping = True - use_l10n = False - arg = arg[:-2] or -1 - elif last_char == "g": - force_grouping = True - arg = arg[:-1] or -1 - elif last_char == "u": - use_l10n = False - arg = arg[:-1] or -1 + if isinstance(arg, str) and arg.endswith('g'): + force_grouping = True + arg = arg[:-1] or -1 try: input_val = repr(text) d = Decimal(input_val) @@ -160,7 +140,7 @@ def floatformat(text, arg=-1): try: d = Decimal(str(float(text))) except (ValueError, InvalidOperation, TypeError): - return "" + return '' try: p = int(arg) except ValueError: @@ -173,12 +153,7 @@ def floatformat(text, arg=-1): if not m and p < 0: return mark_safe( - formats.number_format( - "%d" % (int(d)), - 0, - use_l10n=use_l10n, - force_grouping=force_grouping, - ) + formats.number_format('%d' % (int(d)), 0, force_grouping=force_grouping), ) exp = Decimal(1).scaleb(-abs(p)) @@ -194,18 +169,13 @@ def floatformat(text, arg=-1): sign, digits, exponent = rounded_d.as_tuple() digits = [str(digit) for digit in reversed(digits)] while len(digits) <= abs(exponent): - digits.append("0") - digits.insert(-exponent, ".") + digits.append('0') + digits.insert(-exponent, '.') if sign and rounded_d: - digits.append("-") - number = "".join(reversed(digits)) + digits.append('-') + number = ''.join(reversed(digits)) return mark_safe( - formats.number_format( - number, - abs(p), - use_l10n=use_l10n, - force_grouping=force_grouping, - ) + formats.number_format(number, abs(p), force_grouping=force_grouping), ) @@ -220,7 +190,7 @@ def iriencode(value): @stringfilter def linenumbers(value, autoescape=True): """Display text with line numbers.""" - lines = value.split("\n") + lines = value.split('\n') # Find the maximum width of the line count, for use with zero padding # string format command width = str(len(str(len(lines)))) @@ -230,7 +200,7 @@ def linenumbers(value, autoescape=True): else: for i, line in enumerate(lines): lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) - return mark_safe("\n".join(lines)) + return mark_safe('\n'.join(lines)) @register.filter(is_safe=True) @@ -287,7 +257,7 @@ def stringformat(value, arg): def title(value): """Convert a string into titlecase.""" t = re.sub("([a-z])'([A-Z])", lambda m: m[0].lower(), value.title()) - return re.sub(r"\d([A-Z])", lambda m: m[0].lower(), t) + return re.sub(r'\d([A-Z])', lambda m: m[0].lower(), t) @register.filter(is_safe=True) @@ -326,7 +296,7 @@ def truncatewords(value, arg): length = int(arg) except ValueError: # Invalid literal for int(). return value # Fail silently. - return Truncator(value).words(length, truncate=" …") + return Truncator(value).words(length, truncate=' …') @register.filter(is_safe=True) @@ -340,7 +310,7 @@ def truncatewords_html(value, arg): length = int(arg) except ValueError: # invalid literal for int() return value # Fail silently. - return Truncator(value).words(length, html=True, truncate=" …") + return Truncator(value).words(length, html=True, truncate=' …') @register.filter(is_safe=False) @@ -363,7 +333,7 @@ def urlencode(value, safe=None): """ kwargs = {} if safe is not None: - kwargs["safe"] = safe + kwargs['safe'] = safe return quote(value, **kwargs) @@ -383,9 +353,7 @@ def urlizetrunc(value, limit, autoescape=True): Argument: Length to truncate URLs to. """ - return mark_safe( - _urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape) - ) + return mark_safe(_urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape)) @register.filter(is_safe=False) @@ -428,8 +396,8 @@ def center(value, arg): def cut(value, arg): """Remove all values of arg from the given string.""" safe = isinstance(value, SafeData) - value = value.replace(arg, "") - if safe and arg != ";": + value = value.replace(arg, '') + if safe and arg != ';': return mark_safe(value) return value @@ -438,7 +406,6 @@ def cut(value, arg): # HTML STRINGS # ################### - @register.filter("escape", is_safe=True) @stringfilter def escape_filter(value): @@ -480,7 +447,7 @@ def linebreaksbr(value, autoescape=True): value = normalize_newlines(value) if autoescape: value = escape(value) - return mark_safe(value.replace("\n", "<br>")) + return mark_safe(value.replace('\n', '<br>')) @register.filter(is_safe=True) @@ -511,11 +478,10 @@ def striptags(value): # LISTS # ################### - def _property_resolver(arg): """ When arg is convertible to float, behave like operator.itemgetter(arg) - Otherwise, chain __getitem__() and getattr(). + Otherwise, behave like Variable(arg).resolve >>> _property_resolver(1)('abc') 'b' @@ -533,19 +499,7 @@ def _property_resolver(arg): try: float(arg) except ValueError: - if VARIABLE_ATTRIBUTE_SEPARATOR + "_" in arg or arg[0] == "_": - raise AttributeError("Access to private variables is forbidden.") - parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR) - - def resolve(value): - for part in parts: - try: - value = value[part] - except (AttributeError, IndexError, KeyError, TypeError, ValueError): - value = getattr(value, part) - return value - - return resolve + return Variable(arg).resolve else: return itemgetter(arg) @@ -558,8 +512,8 @@ def dictsort(value, arg): """ try: return sorted(value, key=_property_resolver(arg)) - except (AttributeError, TypeError): - return "" + except (TypeError, VariableDoesNotExist): + return '' @register.filter(is_safe=False) @@ -570,8 +524,8 @@ def dictsortreversed(value, arg): """ try: return sorted(value, key=_property_resolver(arg), reverse=True) - except (AttributeError, TypeError): - return "" + except (TypeError, VariableDoesNotExist): + return '' @register.filter(is_safe=False) @@ -580,7 +534,7 @@ def first(value): try: return value[0] except IndexError: - return "" + return '' @register.filter(is_safe=True, needs_autoescape=True) @@ -601,7 +555,7 @@ def last(value): try: return value[-1] except IndexError: - return "" + return '' @register.filter(is_safe=False) @@ -619,7 +573,7 @@ def length_is(value, arg): try: return len(value) == int(arg) except (ValueError, TypeError): - return "" + return '' @register.filter(is_safe=True) @@ -635,7 +589,7 @@ def slice_filter(value, arg): """ try: bits = [] - for x in str(arg).split(":"): + for x in str(arg).split(':'): if not x: bits.append(None) else: @@ -671,7 +625,6 @@ def unordered_list(value, autoescape=True): if autoescape: escaper = conditional_escape else: - def escaper(x): return x @@ -700,19 +653,16 @@ def unordered_list(value, autoescape=True): pass def list_formatter(item_list, tabs=1): - indent = "\t" * tabs + indent = '\t' * tabs output = [] for item, children in walk_items(item_list): - sublist = "" + sublist = '' if children: - sublist = "\n%s<ul>\n%s\n%s</ul>\n%s" % ( - indent, - list_formatter(children, tabs + 1), - indent, - indent, - ) - output.append("%s<li>%s%s</li>" % (indent, escaper(item), sublist)) - return "\n".join(output) + sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % ( + indent, list_formatter(children, tabs + 1), indent, indent) + output.append('%s<li>%s%s</li>' % ( + indent, escaper(item), sublist)) + return '\n'.join(output) return mark_safe(list_formatter(value)) @@ -721,7 +671,6 @@ def unordered_list(value, autoescape=True): # INTEGERS # ################### - @register.filter(is_safe=False) def add(value, arg): """Add the arg to the value.""" @@ -731,7 +680,7 @@ def add(value, arg): try: return value + arg except Exception: - return "" + return '' @register.filter(is_safe=False) @@ -759,64 +708,62 @@ def get_digit(value, arg): # DATES # ################### - @register.filter(expects_localtime=True, is_safe=False) def date(value, arg=None): """Format a date according to the given format.""" - if value in (None, ""): - return "" + if value in (None, ''): + return '' try: return formats.date_format(value, arg) except AttributeError: try: return format(value, arg) except AttributeError: - return "" + return '' @register.filter(expects_localtime=True, is_safe=False) def time(value, arg=None): """Format a time according to the given format.""" - if value in (None, ""): - return "" + if value in (None, ''): + return '' try: return formats.time_format(value, arg) except (AttributeError, TypeError): try: return time_format(value, arg) except (AttributeError, TypeError): - return "" + return '' @register.filter("timesince", is_safe=False) def timesince_filter(value, arg=None): """Format a date as the time since that date (i.e. "4 days, 6 hours").""" if not value: - return "" + return '' try: if arg: return timesince(value, arg) return timesince(value) except (ValueError, TypeError): - return "" + return '' @register.filter("timeuntil", is_safe=False) def timeuntil_filter(value, arg=None): """Format a date as the time until that date (i.e. "4 days, 6 hours").""" if not value: - return "" + return '' try: return timeuntil(value, arg) except (ValueError, TypeError): - return "" + return '' ################### # LOGIC # ################### - @register.filter(is_safe=False) def default(value, arg): """If value is unavailable, use given default.""" @@ -855,8 +802,8 @@ def yesno(value, arg=None): """ if arg is None: # Translators: Please do not add spaces around commas. - arg = gettext("yes,no,maybe") - bits = arg.split(",") + arg = gettext('yes,no,maybe') + bits = arg.split(',') if len(bits) < 2: return value # Invalid arg. try: @@ -875,7 +822,6 @@ def yesno(value, arg=None): # MISC # ################### - @register.filter(is_safe=True) def filesizeformat(bytes_): """ @@ -885,7 +831,7 @@ def filesizeformat(bytes_): try: bytes_ = int(bytes_) except (TypeError, ValueError, UnicodeDecodeError): - value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {"size": 0} + value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0} return avoid_wrapping(value) def filesize_number_format(value): @@ -902,7 +848,7 @@ def filesizeformat(bytes_): bytes_ = -bytes_ # Allow formatting of negative numbers. if bytes_ < KB: - value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {"size": bytes_} + value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_} elif bytes_ < MB: value = gettext("%s KB") % filesize_number_format(bytes_ / KB) elif bytes_ < GB: @@ -920,7 +866,7 @@ def filesizeformat(bytes_): @register.filter(is_safe=False) -def pluralize(value, arg="s"): +def pluralize(value, arg='s'): """ Return a plural suffix if the value is not 1, '1', or an object of length 1. By default, use 's' as the suffix: @@ -942,11 +888,11 @@ def pluralize(value, arg="s"): * If value is 1, cand{{ value|pluralize:"y,ies" }} display "candy". * If value is 2, cand{{ value|pluralize:"y,ies" }} display "candies". """ - if "," not in arg: - arg = "," + arg - bits = arg.split(",") + if ',' not in arg: + arg = ',' + arg + bits = arg.split(',') if len(bits) > 2: - return "" + return '' singular_suffix, plural_suffix = bits[:2] try: @@ -958,7 +904,7 @@ def pluralize(value, arg="s"): return singular_suffix if len(value) == 1 else plural_suffix except TypeError: # len() of unsized object. pass - return "" + return '' @register.filter("phone2numeric", is_safe=True) diff --git a/venv/Lib/site-packages/django/template/defaulttags.py b/venv/Lib/site-packages/django/template/defaulttags.py index 5904906..4084189 100644 --- a/venv/Lib/site-packages/django/template/defaulttags.py +++ b/venv/Lib/site-packages/django/template/defaulttags.py @@ -4,33 +4,21 @@ import sys import warnings from collections import namedtuple from datetime import datetime -from itertools import cycle as itertools_cycle -from itertools import groupby +from itertools import cycle as itertools_cycle, groupby from django.conf import settings from django.utils import timezone -from django.utils.html import conditional_escape, escape, format_html +from django.utils.deprecation import RemovedInDjango40Warning +from django.utils.html import conditional_escape, format_html from django.utils.lorem_ipsum import paragraphs, words from django.utils.safestring import mark_safe from .base import ( - BLOCK_TAG_END, - BLOCK_TAG_START, - COMMENT_TAG_END, - COMMENT_TAG_START, - FILTER_SEPARATOR, - SINGLE_BRACE_END, - SINGLE_BRACE_START, - VARIABLE_ATTRIBUTE_SEPARATOR, - VARIABLE_TAG_END, - VARIABLE_TAG_START, - Node, - NodeList, - TemplateSyntaxError, - VariableDoesNotExist, - kwarg_re, - render_value_in_context, - token_kwargs, + BLOCK_TAG_END, BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START, + FILTER_SEPARATOR, SINGLE_BRACE_END, SINGLE_BRACE_START, + VARIABLE_ATTRIBUTE_SEPARATOR, VARIABLE_TAG_END, VARIABLE_TAG_START, Node, + NodeList, TemplateSyntaxError, VariableDoesNotExist, kwarg_re, + render_value_in_context, token_kwargs, ) from .context import Context from .defaultfilters import date @@ -42,7 +30,6 @@ register = Library() class AutoEscapeControlNode(Node): """Implement the actions of the autoescape tag.""" - def __init__(self, setting, nodelist): self.setting, self.nodelist = setting, nodelist @@ -58,25 +45,18 @@ class AutoEscapeControlNode(Node): class CommentNode(Node): - child_nodelists = () - def render(self, context): - return "" + return '' class CsrfTokenNode(Node): - child_nodelists = () - def render(self, context): - csrf_token = context.get("csrf_token") + csrf_token = context.get('csrf_token') if csrf_token: - if csrf_token == "NOTPROVIDED": + if csrf_token == 'NOTPROVIDED': return format_html("") else: - return format_html( - '<input type="hidden" name="csrfmiddlewaretoken" value="{}">', - csrf_token, - ) + return format_html('<input type="hidden" name="csrfmiddlewaretoken" value="{}">', csrf_token) else: # It's very probable that the token is missing because of # misconfiguration, so we raise a warning @@ -86,7 +66,7 @@ class CsrfTokenNode(Node): "did not provide the value. This is usually caused by not " "using RequestContext." ) - return "" + return '' class CycleNode(Node): @@ -104,7 +84,7 @@ class CycleNode(Node): if self.variable_name: context.set_upward(self.variable_name, value) if self.silent: - return "" + return '' return render_value_in_context(value, context) def reset(self, context): @@ -116,15 +96,11 @@ class CycleNode(Node): class DebugNode(Node): def render(self, context): - if not settings.DEBUG: - return "" - from pprint import pformat - - output = [escape(pformat(val)) for val in context] - output.append("\n\n") - output.append(escape(pformat(sys.modules))) - return "".join(output) + output = [pformat(val) for val in context] + output.append('\n\n') + output.append(pformat(sys.modules)) + return ''.join(output) class FilterNode(Node): @@ -144,7 +120,7 @@ class FirstOfNode(Node): self.asvar = asvar def render(self, context): - first = "" + first = '' for var in self.vars: value = var.resolve(context, ignore_failures=True) if value: @@ -152,16 +128,14 @@ class FirstOfNode(Node): break if self.asvar: context[self.asvar] = first - return "" + return '' return first class ForNode(Node): - child_nodelists = ("nodelist_loop", "nodelist_empty") + child_nodelists = ('nodelist_loop', 'nodelist_empty') - def __init__( - self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None - ): + def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None): self.loopvars, self.sequence = loopvars, sequence self.is_reversed = is_reversed self.nodelist_loop = nodelist_loop @@ -171,25 +145,25 @@ class ForNode(Node): self.nodelist_empty = nodelist_empty def __repr__(self): - reversed_text = " reversed" if self.is_reversed else "" - return "<%s: for %s in %s, tail_len: %d%s>" % ( + reversed_text = ' reversed' if self.is_reversed else '' + return '<%s: for %s in %s, tail_len: %d%s>' % ( self.__class__.__name__, - ", ".join(self.loopvars), + ', '.join(self.loopvars), self.sequence, len(self.nodelist_loop), reversed_text, ) def render(self, context): - if "forloop" in context: - parentloop = context["forloop"] + if 'forloop' in context: + parentloop = context['forloop'] else: parentloop = {} with context.push(): values = self.sequence.resolve(context, ignore_failures=True) if values is None: values = [] - if not hasattr(values, "__len__"): + if not hasattr(values, '__len__'): values = list(values) len_values = len(values) if len_values < 1: @@ -201,17 +175,17 @@ class ForNode(Node): unpack = num_loopvars > 1 # Create a forloop value in the context. We'll update counters on each # iteration just below. - loop_dict = context["forloop"] = {"parentloop": parentloop} + loop_dict = context['forloop'] = {'parentloop': parentloop} for i, item in enumerate(values): # Shortcuts for current loop iteration number. - loop_dict["counter0"] = i - loop_dict["counter"] = i + 1 + loop_dict['counter0'] = i + loop_dict['counter'] = i + 1 # Reverse counter iteration numbers. - loop_dict["revcounter"] = len_values - i - loop_dict["revcounter0"] = len_values - i - 1 + loop_dict['revcounter'] = len_values - i + loop_dict['revcounter0'] = len_values - i - 1 # Boolean values designating first and last times through loop. - loop_dict["first"] = i == 0 - loop_dict["last"] = i == len_values - 1 + loop_dict['first'] = (i == 0) + loop_dict['last'] = (i == len_values - 1) pop_context = False if unpack: @@ -224,9 +198,8 @@ class ForNode(Node): # Check loop variable count before unpacking if num_loopvars != len_item: raise ValueError( - "Need {} values to unpack in for loop; got {}. ".format( - num_loopvars, len_item - ), + "Need {} values to unpack in for loop; got {}. " + .format(num_loopvars, len_item), ) unpacked_vars = dict(zip(self.loopvars, item)) pop_context = True @@ -242,11 +215,11 @@ class ForNode(Node): # the context ending up in an inconsistent state when other # tags (e.g., include and with) push data to context. context.pop() - return mark_safe("".join(nodelist)) + return mark_safe(''.join(nodelist)) class IfChangedNode(Node): - child_nodelists = ("nodelist_true", "nodelist_false") + child_nodelists = ('nodelist_true', 'nodelist_false') def __init__(self, nodelist_true, nodelist_false, *varlist): self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false @@ -261,9 +234,7 @@ class IfChangedNode(Node): if self._varlist: # Consider multiple parameters. This behaves like an OR evaluation # of the multiple variables. - compare_to = [ - var.resolve(context, ignore_failures=True) for var in self._varlist - ] + compare_to = [var.resolve(context, ignore_failures=True) for var in self._varlist] else: # The "{% ifchanged %}" syntax (without any variables) compares # the rendered output. @@ -275,29 +246,48 @@ class IfChangedNode(Node): return nodelist_true_output or self.nodelist_true.render(context) elif self.nodelist_false: return self.nodelist_false.render(context) - return "" + return '' def _get_context_stack_frame(self, context): - # The Context object behaves like a stack where each template tag can - # create a new scope. Find the place where to store the state to detect - # changes. - if "forloop" in context: + # The Context object behaves like a stack where each template tag can create a new scope. + # Find the place where to store the state to detect changes. + if 'forloop' in context: # Ifchanged is bound to the local for loop. # When there is a loop-in-loop, the state is bound to the inner loop, # so it resets when the outer loop continues. - return context["forloop"] + return context['forloop'] else: - # Using ifchanged outside loops. Effectively this is a no-op - # because the state is associated with 'self'. + # Using ifchanged outside loops. Effectively this is a no-op because the state is associated with 'self'. return context.render_context +class IfEqualNode(Node): + # RemovedInDjango40Warning. + child_nodelists = ('nodelist_true', 'nodelist_false') + + def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): + self.var1, self.var2 = var1, var2 + self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false + self.negate = negate + + def __repr__(self): + return '<%s>' % self.__class__.__name__ + + def render(self, context): + val1 = self.var1.resolve(context, ignore_failures=True) + val2 = self.var2.resolve(context, ignore_failures=True) + if (self.negate and val1 != val2) or (not self.negate and val1 == val2): + return self.nodelist_true.render(context) + return self.nodelist_false.render(context) + + class IfNode(Node): + def __init__(self, conditions_nodelists): self.conditions_nodelists = conditions_nodelists def __repr__(self): - return "<%s>" % self.__class__.__name__ + return '<%s>' % self.__class__.__name__ def __iter__(self): for _, nodelist in self.conditions_nodelists: @@ -310,18 +300,18 @@ class IfNode(Node): def render(self, context): for condition, nodelist in self.conditions_nodelists: - if condition is not None: # if / elif clause + if condition is not None: # if / elif clause try: match = condition.eval(context) except VariableDoesNotExist: match = None - else: # else clause + else: # else clause match = True if match: return nodelist.render(context) - return "" + return '' class LoremNode(Node): @@ -333,16 +323,16 @@ class LoremNode(Node): count = int(self.count.resolve(context)) except (ValueError, TypeError): count = 1 - if self.method == "w": + if self.method == 'w': return words(count, common=self.common) else: paras = paragraphs(count, common=self.common) - if self.method == "p": - paras = ["<p>%s</p>" % p for p in paras] - return "\n\n".join(paras) + if self.method == 'p': + paras = ['<p>%s</p>' % p for p in paras] + return '\n\n'.join(paras) -GroupedResult = namedtuple("GroupedResult", ["grouper", "list"]) +GroupedResult = namedtuple('GroupedResult', ['grouper', 'list']) class RegroupNode(Node): @@ -361,23 +351,20 @@ class RegroupNode(Node): if obj_list is None: # target variable wasn't found in context; fail silently. context[self.var_name] = [] - return "" + return '' # List of dictionaries in the format: # {'grouper': 'key', 'list': [list of contents]}. context[self.var_name] = [ GroupedResult(grouper=key, list=list(val)) - for key, val in groupby( - obj_list, lambda obj: self.resolve_expression(obj, context) - ) + for key, val in + groupby(obj_list, lambda obj: self.resolve_expression(obj, context)) ] - return "" + return '' class LoadNode(Node): - child_nodelists = () - def render(self, context): - return "" + return '' class NowNode(Node): @@ -391,7 +378,7 @@ class NowNode(Node): if self.asvar: context[self.asvar] = formatted - return "" + return '' else: return formatted @@ -402,7 +389,7 @@ class ResetCycleNode(Node): def render(self, context): self.node.reset(context) - return "" + return '' class SpacelessNode(Node): @@ -411,50 +398,37 @@ class SpacelessNode(Node): def render(self, context): from django.utils.html import strip_spaces_between_tags - return strip_spaces_between_tags(self.nodelist.render(context).strip()) class TemplateTagNode(Node): mapping = { - "openblock": BLOCK_TAG_START, - "closeblock": BLOCK_TAG_END, - "openvariable": VARIABLE_TAG_START, - "closevariable": VARIABLE_TAG_END, - "openbrace": SINGLE_BRACE_START, - "closebrace": SINGLE_BRACE_END, - "opencomment": COMMENT_TAG_START, - "closecomment": COMMENT_TAG_END, + 'openblock': BLOCK_TAG_START, + 'closeblock': BLOCK_TAG_END, + 'openvariable': VARIABLE_TAG_START, + 'closevariable': VARIABLE_TAG_END, + 'openbrace': SINGLE_BRACE_START, + 'closebrace': SINGLE_BRACE_END, + 'opencomment': COMMENT_TAG_START, + 'closecomment': COMMENT_TAG_END, } def __init__(self, tagtype): self.tagtype = tagtype def render(self, context): - return self.mapping.get(self.tagtype, "") + return self.mapping.get(self.tagtype, '') class URLNode(Node): - child_nodelists = () - def __init__(self, view_name, args, kwargs, asvar): self.view_name = view_name self.args = args self.kwargs = kwargs self.asvar = asvar - def __repr__(self): - return "<%s view_name='%s' args=%s kwargs=%s as=%s>" % ( - self.__class__.__qualname__, - self.view_name, - repr(self.args), - repr(self.kwargs), - repr(self.asvar), - ) - def render(self, context): from django.urls import NoReverseMatch, reverse - args = [arg.resolve(context) for arg in self.args] kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()} view_name = self.view_name.resolve(context) @@ -467,7 +441,7 @@ class URLNode(Node): current_app = None # Try to look up the URL. If it fails, raise NoReverseMatch unless the # {% url ... as var %} construct is used, in which case return nothing. - url = "" + url = '' try: url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app) except NoReverseMatch: @@ -476,7 +450,7 @@ class URLNode(Node): if self.asvar: context[self.asvar] = url - return "" + return '' else: if context.autoescape: url = conditional_escape(url) @@ -504,7 +478,7 @@ class WidthRatioNode(Node): max_value = self.max_expr.resolve(context) max_width = int(self.max_width.resolve(context)) except VariableDoesNotExist: - return "" + return '' except (ValueError, TypeError): raise TemplateSyntaxError("widthratio final argument must be a number") try: @@ -513,13 +487,13 @@ class WidthRatioNode(Node): ratio = (value / max_value) * max_width result = str(round(ratio)) except ZeroDivisionError: - result = "0" + result = '0' except (ValueError, TypeError, OverflowError): - result = "" + result = '' if self.asvar: context[self.asvar] = result - return "" + return '' else: return result @@ -534,7 +508,7 @@ class WithNode(Node): self.extra_context[name] = var def __repr__(self): - return "<%s>" % self.__class__.__name__ + return '<%s>' % self.__class__.__name__ def render(self, context): values = {key: val.resolve(context) for key, val in self.extra_context.items()} @@ -547,17 +521,16 @@ def autoescape(parser, token): """ Force autoescape behavior for this block. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments args = token.contents.split() if len(args) != 2: raise TemplateSyntaxError("'autoescape' tag requires exactly one argument.") arg = args[1] - if arg not in ("on", "off"): + if arg not in ('on', 'off'): raise TemplateSyntaxError("'autoescape' argument should be 'on' or 'off'") - nodelist = parser.parse(("endautoescape",)) + nodelist = parser.parse(('endautoescape',)) parser.delete_first_token() - return AutoEscapeControlNode((arg == "on"), nodelist) + return AutoEscapeControlNode((arg == 'on'), nodelist) @register.tag @@ -565,7 +538,7 @@ def comment(parser, token): """ Ignore everything between ``{% comment %}`` and ``{% endcomment %}``. """ - parser.skip_past("endcomment") + parser.skip_past('endcomment') return CommentNode() @@ -623,10 +596,8 @@ def cycle(parser, token): if len(args) == 2: # {% cycle foo %} case. name = args[1] - if not hasattr(parser, "_named_cycle_nodes"): - raise TemplateSyntaxError( - "No named cycles in template. '%s' is not defined" % name - ) + if not hasattr(parser, '_named_cycle_nodes'): + raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name) if name not in parser._named_cycle_nodes: raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) return parser._named_cycle_nodes[name] @@ -637,10 +608,7 @@ def cycle(parser, token): # {% cycle ... as foo [silent] %} case. if args[-3] == "as": if args[-1] != "silent": - raise TemplateSyntaxError( - "Only 'silent' flag is allowed after cycle's name, not '%s'." - % args[-1] - ) + raise TemplateSyntaxError("Only 'silent' flag is allowed after cycle's name, not '%s'." % args[-1]) as_form = True silent = True args = args[:-1] @@ -652,7 +620,7 @@ def cycle(parser, token): name = args[-1] values = [parser.compile_filter(arg) for arg in args[1:-2]] node = CycleNode(values, name, silent=silent) - if not hasattr(parser, "_named_cycle_nodes"): + if not hasattr(parser, '_named_cycle_nodes'): parser._named_cycle_nodes = {} parser._named_cycle_nodes[name] = node else: @@ -682,7 +650,7 @@ def debug(parser, token): return DebugNode() -@register.tag("filter") +@register.tag('filter') def do_filter(parser, token): """ Filter the contents of the block through variable filters. @@ -700,18 +668,14 @@ def do_filter(parser, token): Instead, use the ``autoescape`` tag to manage autoescaping for blocks of template code. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments _, rest = token.contents.split(None, 1) filter_expr = parser.compile_filter("var|%s" % (rest)) for func, unused in filter_expr.filters: - filter_name = getattr(func, "_filter_name", None) - if filter_name in ("escape", "safe"): - raise TemplateSyntaxError( - '"filter %s" is not permitted. Use the "autoescape" tag instead.' - % filter_name - ) - nodelist = parser.parse(("endfilter",)) + filter_name = getattr(func, '_filter_name', None) + if filter_name in ('escape', 'safe'): + raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % filter_name) + nodelist = parser.parse(('endfilter',)) parser.delete_first_token() return FilterNode(filter_expr, nodelist) @@ -759,13 +723,13 @@ def firstof(parser, token): if not bits: raise TemplateSyntaxError("'firstof' statement requires at least one argument") - if len(bits) >= 2 and bits[-2] == "as": + if len(bits) >= 2 and bits[-2] == 'as': asvar = bits[-1] bits = bits[:-2] return FirstOfNode([parser.compile_filter(bit) for bit in bits], asvar) -@register.tag("for") +@register.tag('for') def do_for(parser, token): """ Loop over each item in an array. @@ -830,42 +794,89 @@ def do_for(parser, token): """ bits = token.split_contents() if len(bits) < 4: - raise TemplateSyntaxError( - "'for' statements should have at least four words: %s" % token.contents - ) + raise TemplateSyntaxError("'for' statements should have at least four" + " words: %s" % token.contents) - is_reversed = bits[-1] == "reversed" + is_reversed = bits[-1] == 'reversed' in_index = -3 if is_reversed else -2 - if bits[in_index] != "in": - raise TemplateSyntaxError( - "'for' statements should use the format" - " 'for x in y': %s" % token.contents - ) + if bits[in_index] != 'in': + raise TemplateSyntaxError("'for' statements should use the format" + " 'for x in y': %s" % token.contents) - invalid_chars = frozenset((" ", '"', "'", FILTER_SEPARATOR)) - loopvars = re.split(r" *, *", " ".join(bits[1:in_index])) + invalid_chars = frozenset((' ', '"', "'", FILTER_SEPARATOR)) + loopvars = re.split(r' *, *', ' '.join(bits[1:in_index])) for var in loopvars: if not var or not invalid_chars.isdisjoint(var): - raise TemplateSyntaxError( - "'for' tag received an invalid argument: %s" % token.contents - ) + raise TemplateSyntaxError("'for' tag received an invalid argument:" + " %s" % token.contents) sequence = parser.compile_filter(bits[in_index + 1]) - nodelist_loop = parser.parse( - ( - "empty", - "endfor", - ) - ) + nodelist_loop = parser.parse(('empty', 'endfor',)) token = parser.next_token() - if token.contents == "empty": - nodelist_empty = parser.parse(("endfor",)) + if token.contents == 'empty': + nodelist_empty = parser.parse(('endfor',)) parser.delete_first_token() else: nodelist_empty = None return ForNode(loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty) +def do_ifequal(parser, token, negate): + # RemovedInDjango40Warning. + bits = list(token.split_contents()) + if len(bits) != 3: + raise TemplateSyntaxError("%r takes two arguments" % bits[0]) + end_tag = 'end' + bits[0] + nodelist_true = parser.parse(('else', end_tag)) + token = parser.next_token() + if token.contents == 'else': + nodelist_false = parser.parse((end_tag,)) + parser.delete_first_token() + else: + nodelist_false = NodeList() + val1 = parser.compile_filter(bits[1]) + val2 = parser.compile_filter(bits[2]) + return IfEqualNode(val1, val2, nodelist_true, nodelist_false, negate) + + +@register.tag +def ifequal(parser, token): + """ + Output the contents of the block if the two arguments equal each other. + + Examples:: + + {% ifequal user.id comment.user_id %} + ... + {% endifequal %} + + {% ifnotequal user.id comment.user_id %} + ... + {% else %} + ... + {% endifnotequal %} + """ + warnings.warn( + 'The {% ifequal %} template tag is deprecated in favor of {% if %}.', + RemovedInDjango40Warning, + ) + return do_ifequal(parser, token, False) + + +@register.tag +def ifnotequal(parser, token): + """ + Output the contents of the block if the two arguments are not equal. + See ifequal. + """ + warnings.warn( + 'The {% ifnotequal %} template tag is deprecated in favor of ' + '{% if %}.', + RemovedInDjango40Warning, + ) + return do_ifequal(parser, token, True) + + class TemplateLiteral(Literal): def __init__(self, value, text): self.value = value @@ -889,7 +900,7 @@ class TemplateIfParser(IfParser): return TemplateLiteral(self.template_parser.compile_filter(value), value) -@register.tag("if") +@register.tag('if') def do_if(parser, token): """ Evaluate a variable, and if that variable is "true" (i.e., exists, is not @@ -951,31 +962,27 @@ def do_if(parser, token): # {% if ... %} bits = token.split_contents()[1:] condition = TemplateIfParser(parser, bits).parse() - nodelist = parser.parse(("elif", "else", "endif")) + nodelist = parser.parse(('elif', 'else', 'endif')) conditions_nodelists = [(condition, nodelist)] token = parser.next_token() # {% elif ... %} (repeatable) - while token.contents.startswith("elif"): + while token.contents.startswith('elif'): bits = token.split_contents()[1:] condition = TemplateIfParser(parser, bits).parse() - nodelist = parser.parse(("elif", "else", "endif")) + nodelist = parser.parse(('elif', 'else', 'endif')) conditions_nodelists.append((condition, nodelist)) token = parser.next_token() # {% else %} (optional) - if token.contents == "else": - nodelist = parser.parse(("endif",)) + if token.contents == 'else': + nodelist = parser.parse(('endif',)) conditions_nodelists.append((None, nodelist)) token = parser.next_token() # {% endif %} - if token.contents != "endif": - raise TemplateSyntaxError( - 'Malformed template tag at line {}: "{}"'.format( - token.lineno, token.contents - ) - ) + if token.contents != 'endif': + raise TemplateSyntaxError('Malformed template tag at line {}: "{}"'.format(token.lineno, token.contents)) return IfNode(conditions_nodelists) @@ -1011,10 +1018,10 @@ def ifchanged(parser, token): {% endfor %} """ bits = token.split_contents() - nodelist_true = parser.parse(("else", "endifchanged")) + nodelist_true = parser.parse(('else', 'endifchanged')) token = parser.next_token() - if token.contents == "else": - nodelist_false = parser.parse(("endifchanged",)) + if token.contents == 'else': + nodelist_false = parser.parse(('endifchanged',)) parser.delete_first_token() else: nodelist_false = NodeList() @@ -1027,10 +1034,8 @@ def find_library(parser, name): return parser.libraries[name] except KeyError: raise TemplateSyntaxError( - "'%s' is not a registered tag library. Must be one of:\n%s" - % ( - name, - "\n".join(sorted(parser.libraries)), + "'%s' is not a registered tag library. Must be one of:\n%s" % ( + name, "\n".join(sorted(parser.libraries)), ), ) @@ -1050,10 +1055,8 @@ def load_from_library(library, label, names): subset.filters[name] = library.filters[name] if found is False: raise TemplateSyntaxError( - "'%s' is not a valid tag or filter in tag library '%s'" - % ( - name, - label, + "'%s' is not a valid tag or filter in tag library '%s'" % ( + name, label, ), ) return subset @@ -1074,8 +1077,7 @@ def load(parser, token): {% load byline from news %} """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments bits = token.contents.split() if len(bits) >= 4 and bits[-2] == "from": # from syntax is used; load individual tags from the library @@ -1119,19 +1121,19 @@ def lorem(parser, token): bits = list(token.split_contents()) tagname = bits[0] # Random bit - common = bits[-1] != "random" + common = bits[-1] != 'random' if not common: bits.pop() # Method bit - if bits[-1] in ("w", "p", "b"): + if bits[-1] in ('w', 'p', 'b'): method = bits.pop() else: - method = "b" + method = 'b' # Count bit if len(bits) > 1: count = bits.pop() else: - count = "1" + count = '1' count = parser.compile_filter(count) if len(bits) != 1: raise TemplateSyntaxError("Incorrect format for %r tag" % tagname) @@ -1152,7 +1154,7 @@ def now(parser, token): """ bits = token.split_contents() asvar = None - if len(bits) == 4 and bits[-2] == "as": + if len(bits) == 4 and bits[-2] == 'as': asvar = bits[-1] bits = bits[:-2] if len(bits) != 2: @@ -1212,10 +1214,11 @@ def regroup(parser, token): if len(bits) != 6: raise TemplateSyntaxError("'regroup' tag takes five arguments") target = parser.compile_filter(bits[1]) - if bits[2] != "by": + if bits[2] != 'by': raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'") - if bits[4] != "as": - raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must be 'as'") + if bits[4] != 'as': + raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must" + " be 'as'") var_name = bits[5] # RegroupNode will take each item in 'target', put it in the context under # 'var_name', evaluate 'var_name'.'expression' in the current context, and @@ -1223,9 +1226,9 @@ def regroup(parser, token): # save the final result in the context under 'var_name', thus clearing the # temporary values. This hack is necessary because the template engine # doesn't provide a context-aware equivalent of Python's getattr. - expression = parser.compile_filter( - var_name + VARIABLE_ATTRIBUTE_SEPARATOR + bits[3] - ) + expression = parser.compile_filter(var_name + + VARIABLE_ATTRIBUTE_SEPARATOR + + bits[3]) return RegroupNode(target, expression, var_name) @@ -1281,7 +1284,7 @@ def spaceless(parser, token): </strong> {% endspaceless %} """ - nodelist = parser.parse(("endspaceless",)) + nodelist = parser.parse(('endspaceless',)) parser.delete_first_token() return SpacelessNode(nodelist) @@ -1309,17 +1312,15 @@ def templatetag(parser, token): ``closecomment`` ``#}`` ================== ======= """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments bits = token.contents.split() if len(bits) != 2: raise TemplateSyntaxError("'templatetag' statement takes one argument") tag = bits[1] if tag not in TemplateTagNode.mapping: - raise TemplateSyntaxError( - "Invalid templatetag argument: '%s'." - " Must be one of: %s" % (tag, list(TemplateTagNode.mapping)) - ) + raise TemplateSyntaxError("Invalid templatetag argument: '%s'." + " Must be one of: %s" % + (tag, list(TemplateTagNode.mapping))) return TemplateTagNode(tag) @@ -1367,15 +1368,13 @@ def url(parser, token): """ bits = token.split_contents() if len(bits) < 2: - raise TemplateSyntaxError( - "'%s' takes at least one argument, a URL pattern name." % bits[0] - ) + raise TemplateSyntaxError("'%s' takes at least one argument, a URL pattern name." % bits[0]) viewname = parser.compile_filter(bits[1]) args = [] kwargs = {} asvar = None bits = bits[2:] - if len(bits) >= 2 and bits[-2] == "as": + if len(bits) >= 2 and bits[-2] == 'as': asvar = bits[-1] bits = bits[:-2] @@ -1410,7 +1409,7 @@ def verbatim(parser, token): ... {% endverbatim myblock %} """ - nodelist = parser.parse(("endverbatim",)) + nodelist = parser.parse(('endverbatim',)) parser.delete_first_token() return VerbatimNode(nodelist.render(Context())) @@ -1442,22 +1441,18 @@ def widthratio(parser, token): asvar = None elif len(bits) == 6: tag, this_value_expr, max_value_expr, max_width, as_, asvar = bits - if as_ != "as": - raise TemplateSyntaxError( - "Invalid syntax in widthratio tag. Expecting 'as' keyword" - ) + if as_ != 'as': + raise TemplateSyntaxError("Invalid syntax in widthratio tag. Expecting 'as' keyword") else: raise TemplateSyntaxError("widthratio takes at least three arguments") - return WidthRatioNode( - parser.compile_filter(this_value_expr), - parser.compile_filter(max_value_expr), - parser.compile_filter(max_width), - asvar=asvar, - ) + return WidthRatioNode(parser.compile_filter(this_value_expr), + parser.compile_filter(max_value_expr), + parser.compile_filter(max_width), + asvar=asvar) -@register.tag("with") +@register.tag('with') def do_with(parser, token): """ Add one or more values to the context (inside of this block) for caching @@ -1482,13 +1477,11 @@ def do_with(parser, token): remaining_bits = bits[1:] extra_context = token_kwargs(remaining_bits, parser, support_legacy=True) if not extra_context: - raise TemplateSyntaxError( - "%r expected at least one variable assignment" % bits[0] - ) + raise TemplateSyntaxError("%r expected at least one variable " + "assignment" % bits[0]) if remaining_bits: - raise TemplateSyntaxError( - "%r received an invalid token: %r" % (bits[0], remaining_bits[0]) - ) - nodelist = parser.parse(("endwith",)) + raise TemplateSyntaxError("%r received an invalid token: %r" % + (bits[0], remaining_bits[0])) + nodelist = parser.parse(('endwith',)) parser.delete_first_token() return WithNode(None, None, nodelist, extra_context=extra_context) diff --git a/venv/Lib/site-packages/django/template/engine.py b/venv/Lib/site-packages/django/template/engine.py index eeb22ff..23a66e7 100644 --- a/venv/Lib/site-packages/django/template/engine.py +++ b/venv/Lib/site-packages/django/template/engine.py @@ -12,39 +12,28 @@ from .library import import_library class Engine: default_builtins = [ - "django.template.defaulttags", - "django.template.defaultfilters", - "django.template.loader_tags", + 'django.template.defaulttags', + 'django.template.defaultfilters', + 'django.template.loader_tags', ] - def __init__( - self, - dirs=None, - app_dirs=False, - context_processors=None, - debug=False, - loaders=None, - string_if_invalid="", - file_charset="utf-8", - libraries=None, - builtins=None, - autoescape=True, - ): + def __init__(self, dirs=None, app_dirs=False, context_processors=None, + debug=False, loaders=None, string_if_invalid='', + file_charset='utf-8', libraries=None, builtins=None, autoescape=True): if dirs is None: dirs = [] if context_processors is None: context_processors = [] if loaders is None: - loaders = ["django.template.loaders.filesystem.Loader"] + loaders = ['django.template.loaders.filesystem.Loader'] if app_dirs: - loaders += ["django.template.loaders.app_directories.Loader"] + loaders += ['django.template.loaders.app_directories.Loader'] if not debug: - loaders = [("django.template.loaders.cached.Loader", loaders)] + loaders = [('django.template.loaders.cached.Loader', loaders)] else: if app_dirs: raise ImproperlyConfigured( - "app_dirs must not be set when loaders is defined." - ) + "app_dirs must not be set when loaders is defined.") if libraries is None: libraries = {} if builtins is None: @@ -63,26 +52,6 @@ class Engine: self.builtins = self.default_builtins + builtins self.template_builtins = self.get_template_builtins(self.builtins) - def __repr__(self): - return ( - "<%s:%s app_dirs=%s%s debug=%s loaders=%s string_if_invalid=%s " - "file_charset=%s%s%s autoescape=%s>" - ) % ( - self.__class__.__qualname__, - "" if not self.dirs else " dirs=%s" % repr(self.dirs), - self.app_dirs, - "" - if not self.context_processors - else " context_processors=%s" % repr(self.context_processors), - self.debug, - repr(self.loaders), - repr(self.string_if_invalid), - repr(self.file_charset), - "" if not self.libraries else " libraries=%s" % repr(self.libraries), - "" if not self.builtins else " builtins=%s" % repr(self.builtins), - repr(self.autoescape), - ) - @staticmethod @functools.lru_cache() def get_default(): @@ -104,11 +73,10 @@ class Engine: # local imports are required to avoid import loops. from django.template import engines from django.template.backends.django import DjangoTemplates - for engine in engines.all(): if isinstance(engine, DjangoTemplates): return engine.engine - raise ImproperlyConfigured("No DjangoTemplates backend is configured.") + raise ImproperlyConfigured('No DjangoTemplates backend is configured.') @cached_property def template_context_processors(self): @@ -148,8 +116,7 @@ class Engine: return loader_class(self, *args) else: raise ImproperlyConfigured( - "Invalid value in template loaders configuration: %r" % loader - ) + "Invalid value in template loaders configuration: %r" % loader) def find_template(self, name, dirs=None, skip=None): tried = [] @@ -174,7 +141,7 @@ class Engine: handling template inheritance recursively. """ template, origin = self.find_template(template_name) - if not hasattr(template, "render"): + if not hasattr(template, 'render'): # template needs to be compiled template = Template(template, origin, template_name, engine=self) return template @@ -210,4 +177,4 @@ class Engine: not_found.append(exc.args[0]) continue # If we get here, none of the templates could be loaded - raise TemplateDoesNotExist(", ".join(not_found)) + raise TemplateDoesNotExist(', '.join(not_found)) diff --git a/venv/Lib/site-packages/django/template/exceptions.py b/venv/Lib/site-packages/django/template/exceptions.py index 2a9c92f..97edc9e 100644 --- a/venv/Lib/site-packages/django/template/exceptions.py +++ b/venv/Lib/site-packages/django/template/exceptions.py @@ -24,7 +24,6 @@ class TemplateDoesNotExist(Exception): encapsulate multiple exceptions when loading templates from multiple engines. """ - def __init__(self, msg, tried=None, backend=None, chain=None): self.backend = backend if tried is None: @@ -40,5 +39,4 @@ class TemplateSyntaxError(Exception): """ The exception used for syntax errors during parsing or rendering. """ - pass diff --git a/venv/Lib/site-packages/django/template/library.py b/venv/Lib/site-packages/django/template/library.py index cff8025..2f74556 100644 --- a/venv/Lib/site-packages/django/template/library.py +++ b/venv/Lib/site-packages/django/template/library.py @@ -20,7 +20,6 @@ class Library: The filter, simple_tag, and inclusion_tag methods provide a convenient way to register callables as tags. """ - def __init__(self): self.filters = {} self.tags = {} @@ -37,7 +36,6 @@ class Library: # @register.tag('somename') or @register.tag(name='somename') def dec(func): return self.tag(name, func) - return dec elif name is not None and compile_function is not None: # register.tag('somename', somefunc) @@ -45,8 +43,8 @@ class Library: return compile_function else: raise ValueError( - "Unsupported arguments to Library.tag: (%r, %r)" - % (name, compile_function), + "Unsupported arguments to Library.tag: (%r, %r)" % + (name, compile_function), ) def tag_function(self, func): @@ -65,7 +63,6 @@ class Library: # @register.filter() def dec(func): return self.filter_function(func, **flags) - return dec elif name is not None and filter_func is None: if callable(name): @@ -75,12 +72,11 @@ class Library: # @register.filter('somename') or @register.filter(name='somename') def dec(func): return self.filter(name, func, **flags) - return dec elif name is not None and filter_func is not None: # register.filter('somename', somefunc) self.filters[name] = filter_func - for attr in ("expects_localtime", "is_safe", "needs_autoescape"): + for attr in ('expects_localtime', 'is_safe', 'needs_autoescape'): if attr in flags: value = flags[attr] # set the flag on the filter for FilterExpression.resolve @@ -93,8 +89,8 @@ class Library: return filter_func else: raise ValueError( - "Unsupported arguments to Library.filter: (%r, %r)" - % (name, filter_func), + "Unsupported arguments to Library.filter: (%r, %r)" % + (name, filter_func), ) def filter_function(self, func, **flags): @@ -109,40 +105,22 @@ class Library: def hello(*args, **kwargs): return 'world' """ - def dec(func): - ( - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - _, - ) = getfullargspec(unwrap(func)) - function_name = name or getattr(func, "_decorated_function", func).__name__ + params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func)) + function_name = (name or getattr(func, '_decorated_function', func).__name__) @functools.wraps(func) def compile_func(parser, token): bits = token.split_contents()[1:] target_var = None - if len(bits) >= 2 and bits[-2] == "as": + if len(bits) >= 2 and bits[-2] == 'as': target_var = bits[-1] bits = bits[:-2] args, kwargs = parse_bits( - parser, - bits, - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - takes_context, - function_name, + parser, bits, params, varargs, varkw, defaults, + kwonly, kwonly_defaults, takes_context, function_name, ) return SimpleNode(func, takes_context, args, kwargs, target_var) - self.tag(function_name, compile_func) return func @@ -164,45 +142,22 @@ class Library: choices = poll.choice_set.all() return {'choices': choices} """ - def dec(func): - ( - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - _, - ) = getfullargspec(unwrap(func)) - function_name = name or getattr(func, "_decorated_function", func).__name__ + params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func)) + function_name = (name or getattr(func, '_decorated_function', func).__name__) @functools.wraps(func) def compile_func(parser, token): bits = token.split_contents()[1:] args, kwargs = parse_bits( - parser, - bits, - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - takes_context, - function_name, + parser, bits, params, varargs, varkw, defaults, + kwonly, kwonly_defaults, takes_context, function_name, ) return InclusionNode( - func, - takes_context, - args, - kwargs, - filename, + func, takes_context, args, kwargs, filename, ) - self.tag(function_name, compile_func) return func - return dec @@ -212,7 +167,6 @@ class TagHelperNode(Node): Manages the positional and keyword arguments to be passed to the decorated function. """ - def __init__(self, func, takes_context, args, kwargs): self.func = func self.takes_context = takes_context @@ -228,7 +182,6 @@ class TagHelperNode(Node): class SimpleNode(TagHelperNode): - child_nodelists = () def __init__(self, func, takes_context, args, kwargs, target_var): super().__init__(func, takes_context, args, kwargs) @@ -239,13 +192,14 @@ class SimpleNode(TagHelperNode): output = self.func(*resolved_args, **resolved_kwargs) if self.target_var is not None: context[self.target_var] = output - return "" + return '' if context.autoescape: output = conditional_escape(output) return output class InclusionNode(TagHelperNode): + def __init__(self, func, takes_context, args, kwargs, filename): super().__init__(func, takes_context, args, kwargs) self.filename = filename @@ -263,7 +217,7 @@ class InclusionNode(TagHelperNode): if t is None: if isinstance(self.filename, Template): t = self.filename - elif isinstance(getattr(self.filename, "template", None), Template): + elif isinstance(getattr(self.filename, 'template', None), Template): t = self.filename.template elif not isinstance(self.filename, str) and is_iterable(self.filename): t = context.template.engine.select_template(self.filename) @@ -274,42 +228,32 @@ class InclusionNode(TagHelperNode): # Copy across the CSRF token, if present, because inclusion tags are # often used for forms, and we need instructions for using CSRF # protection to be as simple as possible. - csrf_token = context.get("csrf_token") + csrf_token = context.get('csrf_token') if csrf_token is not None: - new_context["csrf_token"] = csrf_token + new_context['csrf_token'] = csrf_token return t.render(new_context) -def parse_bits( - parser, - bits, - params, - varargs, - varkw, - defaults, - kwonly, - kwonly_defaults, - takes_context, - name, -): +def parse_bits(parser, bits, params, varargs, varkw, defaults, + kwonly, kwonly_defaults, takes_context, name): """ Parse bits for template tag helpers simple_tag and inclusion_tag, in particular by detecting syntax errors and by extracting positional and keyword arguments. """ if takes_context: - if params and params[0] == "context": + if params[0] == 'context': params = params[1:] else: raise TemplateSyntaxError( "'%s' is decorated with takes_context=True so it must " - "have a first argument of 'context'" % name - ) + "have a first argument of 'context'" % name) args = [] kwargs = {} unhandled_params = list(params) unhandled_kwargs = [ - kwarg for kwarg in kwonly if not kwonly_defaults or kwarg not in kwonly_defaults + kwarg for kwarg in kwonly + if not kwonly_defaults or kwarg not in kwonly_defaults ] for bit in bits: # First we try to extract a potential kwarg from the bit @@ -320,14 +264,13 @@ def parse_bits( if param not in params and param not in kwonly and varkw is None: # An unexpected keyword argument was supplied raise TemplateSyntaxError( - "'%s' received unexpected keyword argument '%s'" % (name, param) - ) + "'%s' received unexpected keyword argument '%s'" % + (name, param)) elif param in kwargs: # The keyword argument has already been supplied once raise TemplateSyntaxError( - "'%s' received multiple values for keyword argument '%s'" - % (name, param) - ) + "'%s' received multiple values for keyword argument '%s'" % + (name, param)) else: # All good, record the keyword argument kwargs[str(param)] = value @@ -342,8 +285,7 @@ def parse_bits( if kwargs: raise TemplateSyntaxError( "'%s' received some positional argument(s) after some " - "keyword argument(s)" % name - ) + "keyword argument(s)" % name) else: # Record the positional argument args.append(parser.compile_filter(bit)) @@ -353,18 +295,17 @@ def parse_bits( except IndexError: if varargs is None: raise TemplateSyntaxError( - "'%s' received too many positional arguments" % name - ) + "'%s' received too many positional arguments" % + name) if defaults is not None: # Consider the last n params handled, where n is the # number of defaults. - unhandled_params = unhandled_params[: -len(defaults)] + unhandled_params = unhandled_params[:-len(defaults)] if unhandled_params or unhandled_kwargs: # Some positional arguments were not supplied raise TemplateSyntaxError( - "'%s' did not receive value(s) for the argument(s): %s" - % (name, ", ".join("'%s'" % p for p in unhandled_params + unhandled_kwargs)) - ) + "'%s' did not receive value(s) for the argument(s): %s" % + (name, ", ".join("'%s'" % p for p in unhandled_params + unhandled_kwargs))) return args, kwargs diff --git a/venv/Lib/site-packages/django/template/loader.py b/venv/Lib/site-packages/django/template/loader.py index 9b108f3..2492aee 100644 --- a/venv/Lib/site-packages/django/template/loader.py +++ b/venv/Lib/site-packages/django/template/loader.py @@ -29,9 +29,9 @@ def select_template(template_name_list, using=None): """ if isinstance(template_name_list, str): raise TypeError( - "select_template() takes an iterable of template names but got a " - "string: %r. Use get_template() if you want to load a single " - "template by name." % template_name_list + 'select_template() takes an iterable of template names but got a ' + 'string: %r. Use get_template() if you want to load a single ' + 'template by name.' % template_name_list ) chain = [] @@ -44,7 +44,7 @@ def select_template(template_name_list, using=None): chain.append(e) if template_name_list: - raise TemplateDoesNotExist(", ".join(template_name_list), chain=chain) + raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain) else: raise TemplateDoesNotExist("No template names provided") diff --git a/venv/Lib/site-packages/django/template/loader_tags.py b/venv/Lib/site-packages/django/template/loader_tags.py index dd80d1f..07b614c 100644 --- a/venv/Lib/site-packages/django/template/loader_tags.py +++ b/venv/Lib/site-packages/django/template/loader_tags.py @@ -3,12 +3,14 @@ from collections import defaultdict from django.utils.safestring import mark_safe -from .base import Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs +from .base import ( + Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs, +) from .library import Library register = Library() -BLOCK_CONTEXT_KEY = "block_context" +BLOCK_CONTEXT_KEY = 'block_context' class BlockContext: @@ -16,9 +18,6 @@ class BlockContext: # Dictionary of FIFO queues. self.blocks = defaultdict(list) - def __repr__(self): - return f"<{self.__class__.__qualname__}: blocks={self.blocks!r}>" - def add_blocks(self, blocks): for name, block in blocks.items(): self.blocks[name].insert(0, block) @@ -50,7 +49,7 @@ class BlockNode(Node): block_context = context.render_context.get(BLOCK_CONTEXT_KEY) with context.push(): if block_context is None: - context["block"] = self + context['block'] = self result = self.nodelist.render(context) else: push = block = block_context.pop(self.name) @@ -59,30 +58,28 @@ class BlockNode(Node): # Create new block so we can store context without thread-safety issues. block = type(self)(block.name, block.nodelist) block.context = context - context["block"] = block + context['block'] = block result = block.nodelist.render(context) if push is not None: block_context.push(self.name, push) return result def super(self): - if not hasattr(self, "context"): + if not hasattr(self, 'context'): raise TemplateSyntaxError( "'%s' object has no attribute 'context'. Did you use " "{{ block.super }} in a base template?" % self.__class__.__name__ ) render_context = self.context.render_context - if ( - BLOCK_CONTEXT_KEY in render_context - and render_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None - ): + if (BLOCK_CONTEXT_KEY in render_context and + render_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None): return mark_safe(self.render(self.context)) - return "" + return '' class ExtendsNode(Node): must_be_first = True - context_key = "extends_context" + context_key = 'extends_context' def __init__(self, nodelist, parent_name, template_dirs=None): self.nodelist = nodelist @@ -91,7 +88,7 @@ class ExtendsNode(Node): self.blocks = {n.name: n for n in nodelist.get_nodes_by_type(BlockNode)} def __repr__(self): - return "<%s: extends %s>" % (self.__class__.__name__, self.parent_name.token) + return '<%s: extends %s>' % (self.__class__.__name__, self.parent_name.token) def find_template(self, template_name, context): """ @@ -101,12 +98,10 @@ class ExtendsNode(Node): without extending the same template twice. """ history = context.render_context.setdefault( - self.context_key, - [self.origin], + self.context_key, [self.origin], ) template, origin = context.template.engine.find_template( - template_name, - skip=history, + template_name, skip=history, ) history.append(origin) return template @@ -115,15 +110,15 @@ class ExtendsNode(Node): parent = self.parent_name.resolve(context) if not parent: error_msg = "Invalid template name in 'extends' tag: %r." % parent - if self.parent_name.filters or isinstance(self.parent_name.var, Variable): - error_msg += ( - " Got this from the '%s' variable." % self.parent_name.token - ) + if self.parent_name.filters or\ + isinstance(self.parent_name.var, Variable): + error_msg += " Got this from the '%s' variable." %\ + self.parent_name.token raise TemplateSyntaxError(error_msg) if isinstance(parent, Template): # parent is a django.template.Template return parent - if isinstance(getattr(parent, "template", None), Template): + if isinstance(getattr(parent, 'template', None), Template): # parent is a django.template.backends.django.Template return parent.template return self.find_template(parent, context) @@ -144,10 +139,8 @@ class ExtendsNode(Node): # The ExtendsNode has to be the first non-text node. if not isinstance(node, TextNode): if not isinstance(node, ExtendsNode): - blocks = { - n.name: n - for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode) - } + blocks = {n.name: n for n in + compiled_parent.nodelist.get_nodes_by_type(BlockNode)} block_context.add_blocks(blocks) break @@ -158,19 +151,14 @@ class ExtendsNode(Node): class IncludeNode(Node): - context_key = "__include_context" + context_key = '__include_context' - def __init__( - self, template, *args, extra_context=None, isolated_context=False, **kwargs - ): + def __init__(self, template, *args, extra_context=None, isolated_context=False, **kwargs): self.template = template self.extra_context = extra_context or {} self.isolated_context = isolated_context super().__init__(*args, **kwargs) - def __repr__(self): - return f"<{self.__class__.__qualname__}: template={self.template!r}>" - def render(self, context): """ Render the specified template and context. Cache the template object @@ -179,16 +167,14 @@ class IncludeNode(Node): """ template = self.template.resolve(context) # Does this quack like a Template? - if not callable(getattr(template, "render", None)): + if not callable(getattr(template, 'render', None)): # If not, try the cache and select_template(). template_name = template or () if isinstance(template_name, str): - template_name = ( - construct_relative_path( - self.origin.template_name, - template_name, - ), - ) + template_name = (construct_relative_path( + self.origin.template_name, + template_name, + ),) else: template_name = tuple(template_name) cache = context.render_context.dicts[0].setdefault(self, {}) @@ -197,10 +183,11 @@ class IncludeNode(Node): template = context.template.engine.select_template(template_name) cache[template_name] = template # Use the base.Template of a backends.django.Template. - elif hasattr(template, "template"): + elif hasattr(template, 'template'): template = template.template values = { - name: var.resolve(context) for name, var in self.extra_context.items() + name: var.resolve(context) + for name, var in self.extra_context.items() } if self.isolated_context: return template.render(context.new(values)) @@ -208,13 +195,12 @@ class IncludeNode(Node): return template.render(context) -@register.tag("block") +@register.tag('block') def do_block(parser, token): """ Define a block that can be overridden by child templates. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments bits = token.contents.split() if len(bits) != 2: raise TemplateSyntaxError("'%s' tag takes only one argument" % bits[0]) @@ -223,19 +209,17 @@ def do_block(parser, token): # check for duplication. try: if block_name in parser.__loaded_blocks: - raise TemplateSyntaxError( - "'%s' tag with name '%s' appears more than once" % (bits[0], block_name) - ) + raise TemplateSyntaxError("'%s' tag with name '%s' appears more than once" % (bits[0], block_name)) parser.__loaded_blocks.append(block_name) except AttributeError: # parser.__loaded_blocks isn't a list yet parser.__loaded_blocks = [block_name] - nodelist = parser.parse(("endblock",)) + nodelist = parser.parse(('endblock',)) # This check is kept for backwards-compatibility. See #3100. endblock = parser.next_token() - acceptable_endblocks = ("endblock", "endblock %s" % block_name) + acceptable_endblocks = ('endblock', 'endblock %s' % block_name) if endblock.contents not in acceptable_endblocks: - parser.invalid_block_tag(endblock, "endblock", acceptable_endblocks) + parser.invalid_block_tag(endblock, 'endblock', acceptable_endblocks) return BlockNode(block_name, nodelist) @@ -245,27 +229,28 @@ def construct_relative_path(current_template_name, relative_name): Convert a relative path (starting with './' or '../') to the full template name based on the current_template_name. """ - has_quotes = (relative_name.startswith('"') and relative_name.endswith('"')) or ( - relative_name.startswith("'") and relative_name.endswith("'") + has_quotes = ( + (relative_name.startswith('"') and relative_name.endswith('"')) or + (relative_name.startswith("'") and relative_name.endswith("'")) ) - new_name = relative_name.strip("'\"") - if not new_name.startswith(("./", "../")): + new_name = relative_name.strip('\'"') + if not new_name.startswith(('./', '../')): # relative_name is a variable or a literal that doesn't contain a # relative path. return relative_name new_name = posixpath.normpath( posixpath.join( - posixpath.dirname(current_template_name.lstrip("/")), + posixpath.dirname(current_template_name.lstrip('/')), new_name, ) ) - if new_name.startswith("../"): + if new_name.startswith('../'): raise TemplateSyntaxError( "The relative path '%s' points outside the file hierarchy that " "template '%s' is in." % (relative_name, current_template_name) ) - if current_template_name.lstrip("/") == new_name: + if current_template_name.lstrip('/') == new_name: raise TemplateSyntaxError( "The relative path '%s' was translated to template name '%s', the " "same template in which the tag appears." @@ -274,7 +259,7 @@ def construct_relative_path(current_template_name, relative_name): return f'"{new_name}"' if has_quotes else new_name -@register.tag("extends") +@register.tag('extends') def do_extends(parser, token): """ Signal that this template extends a parent template. @@ -292,13 +277,11 @@ def do_extends(parser, token): parent_name = parser.compile_filter(bits[1]) nodelist = parser.parse() if nodelist.get_nodes_by_type(ExtendsNode): - raise TemplateSyntaxError( - "'%s' cannot appear more than once in the same template" % bits[0] - ) + raise TemplateSyntaxError("'%s' cannot appear more than once in the same template" % bits[0]) return ExtendsNode(nodelist, parent_name) -@register.tag("include") +@register.tag('include') def do_include(parser, token): """ Load a template and render it with the current context. You can pass @@ -326,27 +309,21 @@ def do_include(parser, token): while remaining_bits: option = remaining_bits.pop(0) if option in options: - raise TemplateSyntaxError( - "The %r option was specified more than once." % option - ) - if option == "with": + raise TemplateSyntaxError('The %r option was specified more ' + 'than once.' % option) + if option == 'with': value = token_kwargs(remaining_bits, parser, support_legacy=False) if not value: - raise TemplateSyntaxError( - '"with" in %r tag needs at least one keyword argument.' % bits[0] - ) - elif option == "only": + raise TemplateSyntaxError('"with" in %r tag needs at least ' + 'one keyword argument.' % bits[0]) + elif option == 'only': value = True else: - raise TemplateSyntaxError( - "Unknown argument for %r tag: %r." % (bits[0], option) - ) + raise TemplateSyntaxError('Unknown argument for %r tag: %r.' % + (bits[0], option)) options[option] = value - isolated_context = options.get("only", False) - namemap = options.get("with", {}) + isolated_context = options.get('only', False) + namemap = options.get('with', {}) bits[1] = construct_relative_path(parser.origin.template_name, bits[1]) - return IncludeNode( - parser.compile_filter(bits[1]), - extra_context=namemap, - isolated_context=isolated_context, - ) + return IncludeNode(parser.compile_filter(bits[1]), extra_context=namemap, + isolated_context=isolated_context) diff --git a/venv/Lib/site-packages/django/template/loaders/app_directories.py b/venv/Lib/site-packages/django/template/loaders/app_directories.py index 0bd7dc8..c9a8adf 100644 --- a/venv/Lib/site-packages/django/template/loaders/app_directories.py +++ b/venv/Lib/site-packages/django/template/loaders/app_directories.py @@ -9,5 +9,6 @@ from .filesystem import Loader as FilesystemLoader class Loader(FilesystemLoader): + def get_dirs(self): - return get_app_template_dirs("templates") + return get_app_template_dirs('templates') diff --git a/venv/Lib/site-packages/django/template/loaders/base.py b/venv/Lib/site-packages/django/template/loaders/base.py index 9168c26..b77ea9e 100644 --- a/venv/Lib/site-packages/django/template/loaders/base.py +++ b/venv/Lib/site-packages/django/template/loaders/base.py @@ -2,6 +2,7 @@ from django.template import Template, TemplateDoesNotExist class Loader: + def __init__(self, engine): self.engine = engine @@ -16,20 +17,17 @@ class Loader: for origin in self.get_template_sources(template_name): if skip is not None and origin in skip: - tried.append((origin, "Skipped to avoid recursion")) + tried.append((origin, 'Skipped to avoid recursion')) continue try: contents = self.get_contents(origin) except TemplateDoesNotExist: - tried.append((origin, "Source does not exist")) + tried.append((origin, 'Source does not exist')) continue else: return Template( - contents, - origin, - origin.template_name, - self.engine, + contents, origin, origin.template_name, self.engine, ) raise TemplateDoesNotExist(template_name, tried=tried) @@ -40,7 +38,7 @@ class Loader: template name. """ raise NotImplementedError( - "subclasses of Loader must provide a get_template_sources() method" + 'subclasses of Loader must provide a get_template_sources() method' ) def reset(self): diff --git a/venv/Lib/site-packages/django/template/loaders/cached.py b/venv/Lib/site-packages/django/template/loaders/cached.py index 4f40953..bb47682 100644 --- a/venv/Lib/site-packages/django/template/loaders/cached.py +++ b/venv/Lib/site-packages/django/template/loaders/cached.py @@ -12,6 +12,7 @@ from .base import Loader as BaseLoader class Loader(BaseLoader): + def __init__(self, engine, loaders): self.get_template_cache = {} self.loaders = engine.get_template_loaders(loaders) @@ -56,9 +57,7 @@ class Loader(BaseLoader): try: template = super().get_template(template_name, skip) except TemplateDoesNotExist as e: - self.get_template_cache[key] = ( - copy_exception(e) if self.engine.debug else TemplateDoesNotExist - ) + self.get_template_cache[key] = copy_exception(e) if self.engine.debug else TemplateDoesNotExist raise else: self.get_template_cache[key] = template @@ -81,19 +80,17 @@ class Loader(BaseLoader): y -> a -> a z -> a -> a """ - skip_prefix = "" + skip_prefix = '' if skip: - matching = [ - origin.name for origin in skip if origin.template_name == template_name - ] + matching = [origin.name for origin in skip if origin.template_name == template_name] if matching: skip_prefix = self.generate_hash(matching) - return "-".join(s for s in (str(template_name), skip_prefix) if s) + return '-'.join(s for s in (str(template_name), skip_prefix) if s) def generate_hash(self, values): - return hashlib.sha1("|".join(values).encode()).hexdigest() + return hashlib.sha1('|'.join(values).encode()).hexdigest() def reset(self): "Empty the template cache." diff --git a/venv/Lib/site-packages/django/template/loaders/filesystem.py b/venv/Lib/site-packages/django/template/loaders/filesystem.py index a2474a3..2e49e3d 100644 --- a/venv/Lib/site-packages/django/template/loaders/filesystem.py +++ b/venv/Lib/site-packages/django/template/loaders/filesystem.py @@ -10,6 +10,7 @@ from .base import Loader as BaseLoader class Loader(BaseLoader): + def __init__(self, engine, dirs=None): super().__init__(engine) self.dirs = dirs diff --git a/venv/Lib/site-packages/django/template/loaders/locmem.py b/venv/Lib/site-packages/django/template/loaders/locmem.py index 432de62..25d7672 100644 --- a/venv/Lib/site-packages/django/template/loaders/locmem.py +++ b/venv/Lib/site-packages/django/template/loaders/locmem.py @@ -8,6 +8,7 @@ from .base import Loader as BaseLoader class Loader(BaseLoader): + def __init__(self, engine, templates_dict): self.templates_dict = templates_dict super().__init__(engine) diff --git a/venv/Lib/site-packages/django/template/response.py b/venv/Lib/site-packages/django/template/response.py index c38b95e..9efadcd 100644 --- a/venv/Lib/site-packages/django/template/response.py +++ b/venv/Lib/site-packages/django/template/response.py @@ -8,18 +8,10 @@ class ContentNotRenderedError(Exception): class SimpleTemplateResponse(HttpResponse): - rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"] + rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks'] - def __init__( - self, - template, - context=None, - content_type=None, - status=None, - charset=None, - using=None, - headers=None, - ): + def __init__(self, template, context=None, content_type=None, status=None, + charset=None, using=None, headers=None): # It would seem obvious to call these next two members 'template' and # 'context', but those names are reserved as part of the test Client # API. To avoid the name collision, we use different names. @@ -41,7 +33,7 @@ class SimpleTemplateResponse(HttpResponse): # content argument doesn't make sense here because it will be replaced # with rendered template so we always pass empty string in order to # prevent errors and provide shorter signature. - super().__init__("", content_type, status, charset=charset, headers=headers) + super().__init__('', content_type, status, charset=charset, headers=headers) # _is_rendered tracks whether the template and context has been baked # into a final response. @@ -57,9 +49,8 @@ class SimpleTemplateResponse(HttpResponse): """ obj_dict = self.__dict__.copy() if not self._is_rendered: - raise ContentNotRenderedError( - "The response content must be rendered before it can be pickled." - ) + raise ContentNotRenderedError('The response content must be ' + 'rendered before it can be pickled.') for attr in self.rendering_attrs: if attr in obj_dict: del obj_dict[attr] @@ -125,7 +116,7 @@ class SimpleTemplateResponse(HttpResponse): def __iter__(self): if not self._is_rendered: raise ContentNotRenderedError( - "The response content must be rendered before it can be iterated over." + 'The response content must be rendered before it can be iterated over.' ) return super().__iter__() @@ -133,7 +124,7 @@ class SimpleTemplateResponse(HttpResponse): def content(self): if not self._is_rendered: raise ContentNotRenderedError( - "The response content must be rendered before it can be accessed." + 'The response content must be rendered before it can be accessed.' ) return super().content @@ -145,20 +136,9 @@ class SimpleTemplateResponse(HttpResponse): class TemplateResponse(SimpleTemplateResponse): - rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"] + rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request'] - def __init__( - self, - request, - template, - context=None, - content_type=None, - status=None, - charset=None, - using=None, - headers=None, - ): - super().__init__( - template, context, content_type, status, charset, using, headers=headers - ) + def __init__(self, request, template, context=None, content_type=None, + status=None, charset=None, using=None, headers=None): + super().__init__(template, context, content_type, status, charset, using, headers=headers) self._request = request diff --git a/venv/Lib/site-packages/django/template/smartif.py b/venv/Lib/site-packages/django/template/smartif.py index 5b15a5a..96a1af8 100644 --- a/venv/Lib/site-packages/django/template/smartif.py +++ b/venv/Lib/site-packages/django/template/smartif.py @@ -13,7 +13,6 @@ class TokenBase: Base class for operators and literals, mainly for debugging and for throwing syntax errors. """ - id = None # node/token type name value = None # used by literals first = second = None # used by tree nodes @@ -46,7 +45,6 @@ def infix(bp, func): Create an infix operator, given a binding power and a function that evaluates the node. """ - class Operator(TokenBase): lbp = bp @@ -72,7 +70,6 @@ def prefix(bp, func): Create a prefix operator, given a binding power and a function that evaluates the node. """ - class Operator(TokenBase): lbp = bp @@ -94,19 +91,19 @@ def prefix(bp, func): # We defer variable evaluation to the lambda to ensure that terms are # lazily evaluated using Python's boolean parsing logic. OPERATORS = { - "or": infix(6, lambda context, x, y: x.eval(context) or y.eval(context)), - "and": infix(7, lambda context, x, y: x.eval(context) and y.eval(context)), - "not": prefix(8, lambda context, x: not x.eval(context)), - "in": infix(9, lambda context, x, y: x.eval(context) in y.eval(context)), - "not in": infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)), - "is": infix(10, lambda context, x, y: x.eval(context) is y.eval(context)), - "is not": infix(10, lambda context, x, y: x.eval(context) is not y.eval(context)), - "==": infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), - "!=": infix(10, lambda context, x, y: x.eval(context) != y.eval(context)), - ">": infix(10, lambda context, x, y: x.eval(context) > y.eval(context)), - ">=": infix(10, lambda context, x, y: x.eval(context) >= y.eval(context)), - "<": infix(10, lambda context, x, y: x.eval(context) < y.eval(context)), - "<=": infix(10, lambda context, x, y: x.eval(context) <= y.eval(context)), + 'or': infix(6, lambda context, x, y: x.eval(context) or y.eval(context)), + 'and': infix(7, lambda context, x, y: x.eval(context) and y.eval(context)), + 'not': prefix(8, lambda context, x: not x.eval(context)), + 'in': infix(9, lambda context, x, y: x.eval(context) in y.eval(context)), + 'not in': infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)), + 'is': infix(10, lambda context, x, y: x.eval(context) is y.eval(context)), + 'is not': infix(10, lambda context, x, y: x.eval(context) is not y.eval(context)), + '==': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), + '!=': infix(10, lambda context, x, y: x.eval(context) != y.eval(context)), + '>': infix(10, lambda context, x, y: x.eval(context) > y.eval(context)), + '>=': infix(10, lambda context, x, y: x.eval(context) >= y.eval(context)), + '<': infix(10, lambda context, x, y: x.eval(context) < y.eval(context)), + '<=': infix(10, lambda context, x, y: x.eval(context) <= y.eval(context)), } # Assign 'id' to each: @@ -118,7 +115,6 @@ class Literal(TokenBase): """ A basic self-resolvable object similar to a Django template variable. """ - # IfParser uses Literal in create_var, but TemplateIfParser overrides # create_var so that a proper implementation that actually resolves # variables, filters etc. is used. @@ -194,9 +190,8 @@ class IfParser: retval = self.expression() # Check that we have exhausted all the tokens if self.current_token is not EndToken: - raise self.error_class( - "Unused '%s' at end of if expression." % self.current_token.display() - ) + raise self.error_class("Unused '%s' at end of if expression." % + self.current_token.display()) return retval def expression(self, rbp=0): diff --git a/venv/Lib/site-packages/django/template/utils.py b/venv/Lib/site-packages/django/template/utils.py index eb27154..2d30e16 100644 --- a/venv/Lib/site-packages/django/template/utils.py +++ b/venv/Lib/site-packages/django/template/utils.py @@ -33,34 +33,31 @@ class EngineHandler: try: # This will raise an exception if 'BACKEND' doesn't exist or # isn't a string containing at least one dot. - default_name = tpl["BACKEND"].rsplit(".", 2)[-2] + default_name = tpl['BACKEND'].rsplit('.', 2)[-2] except Exception: - invalid_backend = tpl.get("BACKEND", "<not defined>") + invalid_backend = tpl.get('BACKEND', '<not defined>') raise ImproperlyConfigured( "Invalid BACKEND for a template engine: {}. Check " - "your TEMPLATES setting.".format(invalid_backend) - ) + "your TEMPLATES setting.".format(invalid_backend)) tpl = { - "NAME": default_name, - "DIRS": [], - "APP_DIRS": False, - "OPTIONS": {}, + 'NAME': default_name, + 'DIRS': [], + 'APP_DIRS': False, + 'OPTIONS': {}, **tpl, } - templates[tpl["NAME"]] = tpl - backend_names.append(tpl["NAME"]) + templates[tpl['NAME']] = tpl + backend_names.append(tpl['NAME']) counts = Counter(backend_names) duplicates = [alias for alias, count in counts.most_common() if count > 1] if duplicates: raise ImproperlyConfigured( "Template engine aliases aren't unique, duplicates: {}. " - "Set a unique NAME for each engine in settings.TEMPLATES.".format( - ", ".join(duplicates) - ) - ) + "Set a unique NAME for each engine in settings.TEMPLATES." + .format(", ".join(duplicates))) return templates @@ -73,14 +70,13 @@ class EngineHandler: except KeyError: raise InvalidTemplateEngineError( "Could not find config for '{}' " - "in settings.TEMPLATES".format(alias) - ) + "in settings.TEMPLATES".format(alias)) # If importing or initializing the backend raises an exception, # self._engines[alias] isn't set and this code may get executed # again, so we must preserve the original params. See #24265. params = params.copy() - backend = params.pop("BACKEND") + backend = params.pop('BACKEND') engine_cls = import_string(backend) engine = engine_cls(params) diff --git a/venv/Lib/site-packages/django/templatetags/cache.py b/venv/Lib/site-packages/django/templatetags/cache.py index 4a60cd8..9e402a1 100644 --- a/venv/Lib/site-packages/django/templatetags/cache.py +++ b/venv/Lib/site-packages/django/templatetags/cache.py @@ -1,6 +1,8 @@ from django.core.cache import InvalidCacheBackendError, caches from django.core.cache.utils import make_template_fragment_key -from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist +from django.template import ( + Library, Node, TemplateSyntaxError, VariableDoesNotExist, +) register = Library() @@ -17,34 +19,26 @@ class CacheNode(Node): try: expire_time = self.expire_time_var.resolve(context) except VariableDoesNotExist: - raise TemplateSyntaxError( - '"cache" tag got an unknown variable: %r' % self.expire_time_var.var - ) + raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.expire_time_var.var) if expire_time is not None: try: expire_time = int(expire_time) except (ValueError, TypeError): - raise TemplateSyntaxError( - '"cache" tag got a non-integer timeout value: %r' % expire_time - ) + raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time) if self.cache_name: try: cache_name = self.cache_name.resolve(context) except VariableDoesNotExist: - raise TemplateSyntaxError( - '"cache" tag got an unknown variable: %r' % self.cache_name.var - ) + raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.cache_name.var) try: fragment_cache = caches[cache_name] except InvalidCacheBackendError: - raise TemplateSyntaxError( - "Invalid cache name specified for cache tag: %r" % cache_name - ) + raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name) else: try: - fragment_cache = caches["template_fragments"] + fragment_cache = caches['template_fragments'] except InvalidCacheBackendError: - fragment_cache = caches["default"] + fragment_cache = caches['default'] vary_on = [var.resolve(context) for var in self.vary_on] cache_key = make_template_fragment_key(self.fragment_name, vary_on) @@ -55,7 +49,7 @@ class CacheNode(Node): return value -@register.tag("cache") +@register.tag('cache') def do_cache(parser, token): """ This will cache the contents of a template fragment for a given amount @@ -81,19 +75,18 @@ def do_cache(parser, token): Each unique set of arguments will result in a unique cache entry. """ - nodelist = parser.parse(("endcache",)) + nodelist = parser.parse(('endcache',)) parser.delete_first_token() tokens = token.split_contents() if len(tokens) < 3: raise TemplateSyntaxError("'%r' tag requires at least 2 arguments." % tokens[0]) - if len(tokens) > 3 and tokens[-1].startswith("using="): - cache_name = parser.compile_filter(tokens[-1][len("using=") :]) + if len(tokens) > 3 and tokens[-1].startswith('using='): + cache_name = parser.compile_filter(tokens[-1][len('using='):]) tokens = tokens[:-1] else: cache_name = None return CacheNode( - nodelist, - parser.compile_filter(tokens[1]), + nodelist, parser.compile_filter(tokens[1]), tokens[2], # fragment_name can't be a variable. [parser.compile_filter(t) for t in tokens[3:]], cache_name, diff --git a/venv/Lib/site-packages/django/templatetags/i18n.py b/venv/Lib/site-packages/django/templatetags/i18n.py index cb1e2be..93023f3 100644 --- a/venv/Lib/site-packages/django/templatetags/i18n.py +++ b/venv/Lib/site-packages/django/templatetags/i18n.py @@ -15,10 +15,8 @@ class GetAvailableLanguagesNode(Node): self.variable = variable def render(self, context): - context[self.variable] = [ - (k, translation.gettext(v)) for k, v in settings.LANGUAGES - ] - return "" + context[self.variable] = [(k, translation.gettext(v)) for k, v in settings.LANGUAGES] + return '' class GetLanguageInfoNode(Node): @@ -29,7 +27,7 @@ class GetLanguageInfoNode(Node): def render(self, context): lang_code = self.lang_code.resolve(context) context[self.variable] = translation.get_language_info(lang_code) - return "" + return '' class GetLanguageInfoListNode(Node): @@ -48,7 +46,7 @@ class GetLanguageInfoListNode(Node): def render(self, context): langs = self.languages.resolve(context) context[self.variable] = [self.get_language_info(lang) for lang in langs] - return "" + return '' class GetCurrentLanguageNode(Node): @@ -57,7 +55,7 @@ class GetCurrentLanguageNode(Node): def render(self, context): context[self.variable] = translation.get_language() - return "" + return '' class GetCurrentLanguageBidiNode(Node): @@ -66,53 +64,44 @@ class GetCurrentLanguageBidiNode(Node): def render(self, context): context[self.variable] = translation.get_language_bidi() - return "" + return '' class TranslateNode(Node): - child_nodelists = () - - def __init__(self, filter_expression, noop, asvar=None, message_context=None): + def __init__(self, filter_expression, noop, asvar=None, + message_context=None): self.noop = noop self.asvar = asvar self.message_context = message_context self.filter_expression = filter_expression if isinstance(self.filter_expression.var, str): - self.filter_expression.var = Variable("'%s'" % self.filter_expression.var) + self.filter_expression.var = Variable("'%s'" % + self.filter_expression.var) def render(self, context): self.filter_expression.var.translate = not self.noop if self.message_context: - self.filter_expression.var.message_context = self.message_context.resolve( - context - ) + self.filter_expression.var.message_context = ( + self.message_context.resolve(context)) output = self.filter_expression.resolve(context) value = render_value_in_context(output, context) # Restore percent signs. Percent signs in template text are doubled # so they are not interpreted as string format flags. is_safe = isinstance(value, SafeData) - value = value.replace("%%", "%") + value = value.replace('%%', '%') value = mark_safe(value) if is_safe else value if self.asvar: context[self.asvar] = value - return "" + return '' else: return value class BlockTranslateNode(Node): - def __init__( - self, - extra_context, - singular, - plural=None, - countervar=None, - counter=None, - message_context=None, - trimmed=False, - asvar=None, - tag_name="blocktranslate", - ): + + def __init__(self, extra_context, singular, plural=None, countervar=None, + counter=None, message_context=None, trimmed=False, asvar=None, + tag_name='blocktranslate'): self.extra_context = extra_context self.singular = singular self.plural = plural @@ -123,23 +112,16 @@ class BlockTranslateNode(Node): self.asvar = asvar self.tag_name = tag_name - def __repr__(self): - return ( - f"<{self.__class__.__qualname__}: " - f"extra_context={self.extra_context!r} " - f"singular={self.singular!r} plural={self.plural!r}>" - ) - def render_token_list(self, tokens): result = [] vars = [] for token in tokens: if token.token_type == TokenType.TEXT: - result.append(token.contents.replace("%", "%%")) + result.append(token.contents.replace('%', '%%')) elif token.token_type == TokenType.VAR: - result.append("%%(%s)s" % token.contents) + result.append('%%(%s)s' % token.contents) vars.append(token.contents) - msg = "".join(result) + msg = ''.join(result) if self.trimmed: msg = translation.trim_whitespace(msg) return msg, vars @@ -151,9 +133,7 @@ class BlockTranslateNode(Node): message_context = None # Update() works like a push(), so corresponding context.pop() is at # the end of function - context.update( - {var: val.resolve(context) for var, val in self.extra_context.items()} - ) + context.update({var: val.resolve(context) for var, val in self.extra_context.items()}) singular, vars = self.render_token_list(self.singular) if self.plural and self.countervar and self.counter: count = self.counter.resolve(context) @@ -165,7 +145,8 @@ class BlockTranslateNode(Node): context[self.countervar] = count plural, plural_vars = self.render_token_list(self.plural) if message_context: - result = translation.npgettext(message_context, singular, plural, count) + result = translation.npgettext(message_context, singular, + plural, count) else: result = translation.ngettext(singular, plural, count) vars.extend(plural_vars) @@ -180,7 +161,7 @@ class BlockTranslateNode(Node): if key in context: val = context[key] else: - val = default_value % key if "%s" in default_value else default_value + val = default_value % key if '%s' in default_value else default_value return render_value_in_context(val, context) data = {v: render_value(v) for v in vars} @@ -191,14 +172,14 @@ class BlockTranslateNode(Node): if nested: # Either string is malformed, or it's a bug raise TemplateSyntaxError( - "%r is unable to format string returned by gettext: %r " - "using %r" % (self.tag_name, result, data) + '%r is unable to format string returned by gettext: %r ' + 'using %r' % (self.tag_name, result, data) ) with translation.override(None): result = self.render(context, nested=True) if self.asvar: context[self.asvar] = result - return "" + return '' else: return result @@ -228,13 +209,10 @@ def do_get_available_languages(parser, token): This puts settings.LANGUAGES into the named variable. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments args = token.contents.split() - if len(args) != 3 or args[1] != "as": - raise TemplateSyntaxError( - "'get_available_languages' requires 'as variable' (got %r)" % args - ) + if len(args) != 3 or args[1] != 'as': + raise TemplateSyntaxError("'get_available_languages' requires 'as variable' (got %r)" % args) return GetAvailableLanguagesNode(args[2]) @@ -254,10 +232,8 @@ def do_get_language_info(parser, token): {{ l.bidi|yesno:"bi-directional,uni-directional" }} """ args = token.split_contents() - if len(args) != 5 or args[1] != "for" or args[3] != "as": - raise TemplateSyntaxError( - "'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:]) - ) + if len(args) != 5 or args[1] != 'for' or args[3] != 'as': + raise TemplateSyntaxError("'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:])) return GetLanguageInfoNode(parser.compile_filter(args[2]), args[4]) @@ -281,32 +257,30 @@ def do_get_language_info_list(parser, token): {% endfor %} """ args = token.split_contents() - if len(args) != 5 or args[1] != "for" or args[3] != "as": - raise TemplateSyntaxError( - "'%s' requires 'for sequence as variable' (got %r)" % (args[0], args[1:]) - ) + if len(args) != 5 or args[1] != 'for' or args[3] != 'as': + raise TemplateSyntaxError("'%s' requires 'for sequence as variable' (got %r)" % (args[0], args[1:])) return GetLanguageInfoListNode(parser.compile_filter(args[2]), args[4]) @register.filter def language_name(lang_code): - return translation.get_language_info(lang_code)["name"] + return translation.get_language_info(lang_code)['name'] @register.filter def language_name_translated(lang_code): - english_name = translation.get_language_info(lang_code)["name"] + english_name = translation.get_language_info(lang_code)['name'] return translation.gettext(english_name) @register.filter def language_name_local(lang_code): - return translation.get_language_info(lang_code)["name_local"] + return translation.get_language_info(lang_code)['name_local'] @register.filter def language_bidi(lang_code): - return translation.get_language_info(lang_code)["bidi"] + return translation.get_language_info(lang_code)['bidi'] @register.tag("get_current_language") @@ -321,13 +295,10 @@ def do_get_current_language(parser, token): This fetches the currently active language and puts its value into the ``language`` context variable. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments args = token.contents.split() - if len(args) != 3 or args[1] != "as": - raise TemplateSyntaxError( - "'get_current_language' requires 'as variable' (got %r)" % args - ) + if len(args) != 3 or args[1] != 'as': + raise TemplateSyntaxError("'get_current_language' requires 'as variable' (got %r)" % args) return GetCurrentLanguageNode(args[2]) @@ -344,13 +315,10 @@ def do_get_current_language_bidi(parser, token): the ``bidi`` context variable. True indicates right-to-left layout, otherwise left-to-right. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments args = token.contents.split() - if len(args) != 3 or args[1] != "as": - raise TemplateSyntaxError( - "'get_current_language_bidi' requires 'as variable' (got %r)" % args - ) + if len(args) != 3 or args[1] != 'as': + raise TemplateSyntaxError("'get_current_language_bidi' requires 'as variable' (got %r)" % args) return GetCurrentLanguageBidiNode(args[2]) @@ -406,7 +374,7 @@ def do_translate(parser, token): asvar = None message_context = None seen = set() - invalid_context = {"as", "noop"} + invalid_context = {'as', 'noop'} while remaining: option = remaining.pop(0) @@ -414,23 +382,21 @@ def do_translate(parser, token): raise TemplateSyntaxError( "The '%s' option was specified more than once." % option, ) - elif option == "noop": + elif option == 'noop': noop = True - elif option == "context": + elif option == 'context': try: value = remaining.pop(0) except IndexError: raise TemplateSyntaxError( - "No argument provided to the '%s' tag for the context option." - % bits[0] + "No argument provided to the '%s' tag for the context option." % bits[0] ) if value in invalid_context: raise TemplateSyntaxError( - "Invalid argument '%s' provided to the '%s' tag for the context " - "option" % (value, bits[0]), + "Invalid argument '%s' provided to the '%s' tag for the context option" % (value, bits[0]), ) message_context = parser.compile_filter(value) - elif option == "as": + elif option == 'as': try: value = remaining.pop(0) except IndexError: @@ -441,10 +407,8 @@ def do_translate(parser, token): else: raise TemplateSyntaxError( "Unknown argument for '%s' tag: '%s'. The only options " - "available are 'noop', 'context' \"xxx\", and 'as VAR'." - % ( - bits[0], - option, + "available are 'noop', 'context' \"xxx\", and 'as VAR'." % ( + bits[0], option, ) ) seen.add(option) @@ -503,22 +467,18 @@ def do_block_translate(parser, token): while remaining_bits: option = remaining_bits.pop(0) if option in options: - raise TemplateSyntaxError( - "The %r option was specified more than once." % option - ) - if option == "with": + raise TemplateSyntaxError('The %r option was specified more ' + 'than once.' % option) + if option == 'with': value = token_kwargs(remaining_bits, parser, support_legacy=True) if not value: - raise TemplateSyntaxError( - '"with" in %r tag needs at least one keyword argument.' % bits[0] - ) - elif option == "count": + raise TemplateSyntaxError('"with" in %r tag needs at least ' + 'one keyword argument.' % bits[0]) + elif option == 'count': value = token_kwargs(remaining_bits, parser, support_legacy=True) if len(value) != 1: - raise TemplateSyntaxError( - '"count" in %r tag expected exactly ' - "one keyword argument." % bits[0] - ) + raise TemplateSyntaxError('"count" in %r tag expected exactly ' + 'one keyword argument.' % bits[0]) elif option == "context": try: value = remaining_bits.pop(0) @@ -534,25 +494,23 @@ def do_block_translate(parser, token): value = remaining_bits.pop(0) except IndexError: raise TemplateSyntaxError( - "No argument provided to the '%s' tag for the asvar option." - % bits[0] + "No argument provided to the '%s' tag for the asvar option." % bits[0] ) asvar = value else: - raise TemplateSyntaxError( - "Unknown argument for %r tag: %r." % (bits[0], option) - ) + raise TemplateSyntaxError('Unknown argument for %r tag: %r.' % + (bits[0], option)) options[option] = value - if "count" in options: - countervar, counter = next(iter(options["count"].items())) + if 'count' in options: + countervar, counter = next(iter(options['count'].items())) else: countervar, counter = None, None - if "context" in options: - message_context = options["context"] + if 'context' in options: + message_context = options['context'] else: message_context = None - extra_context = options.get("with", {}) + extra_context = options.get('with', {}) trimmed = options.get("trimmed", False) @@ -565,34 +523,21 @@ def do_block_translate(parser, token): else: break if countervar and counter: - if token.contents.strip() != "plural": - raise TemplateSyntaxError( - "%r doesn't allow other block tags inside it" % bits[0] - ) + if token.contents.strip() != 'plural': + raise TemplateSyntaxError("%r doesn't allow other block tags inside it" % bits[0]) while parser.tokens: token = parser.next_token() if token.token_type in (TokenType.VAR, TokenType.TEXT): plural.append(token) else: break - end_tag_name = "end%s" % bits[0] + end_tag_name = 'end%s' % bits[0] if token.contents.strip() != end_tag_name: - raise TemplateSyntaxError( - "%r doesn't allow other block tags (seen %r) inside it" - % (bits[0], token.contents) - ) + raise TemplateSyntaxError("%r doesn't allow other block tags (seen %r) inside it" % (bits[0], token.contents)) - return BlockTranslateNode( - extra_context, - singular, - plural, - countervar, - counter, - message_context, - trimmed=trimmed, - asvar=asvar, - tag_name=bits[0], - ) + return BlockTranslateNode(extra_context, singular, plural, countervar, + counter, message_context, trimmed=trimmed, + asvar=asvar, tag_name=bits[0]) @register.tag @@ -610,6 +555,6 @@ def language(parser, token): if len(bits) != 2: raise TemplateSyntaxError("'%s' takes one argument (language)" % bits[0]) language = parser.compile_filter(bits[1]) - nodelist = parser.parse(("endlanguage",)) + nodelist = parser.parse(('endlanguage',)) parser.delete_first_token() return LanguageNode(nodelist, language) diff --git a/venv/Lib/site-packages/django/templatetags/l10n.py b/venv/Lib/site-packages/django/templatetags/l10n.py index 45e9a6a..9212753 100644 --- a/venv/Lib/site-packages/django/templatetags/l10n.py +++ b/venv/Lib/site-packages/django/templatetags/l10n.py @@ -28,7 +28,7 @@ class LocalizeNode(Node): self.use_l10n = use_l10n def __repr__(self): - return "<%s>" % self.__class__.__name__ + return '<%s>' % self.__class__.__name__ def render(self, context): old_setting = context.use_l10n @@ -38,7 +38,7 @@ class LocalizeNode(Node): return output -@register.tag("localize") +@register.tag('localize') def localize_tag(parser, token): """ Force or prevents localization of values, regardless of the value of @@ -54,10 +54,10 @@ def localize_tag(parser, token): bits = list(token.split_contents()) if len(bits) == 1: use_l10n = True - elif len(bits) > 2 or bits[1] not in ("on", "off"): + elif len(bits) > 2 or bits[1] not in ('on', 'off'): raise TemplateSyntaxError("%r argument should be 'on' or 'off'" % bits[0]) else: - use_l10n = bits[1] == "on" - nodelist = parser.parse(("endlocalize",)) + use_l10n = bits[1] == 'on' + nodelist = parser.parse(('endlocalize',)) parser.delete_first_token() return LocalizeNode(nodelist, use_l10n) diff --git a/venv/Lib/site-packages/django/templatetags/static.py b/venv/Lib/site-packages/django/templatetags/static.py index a68e44a..39712aa 100644 --- a/venv/Lib/site-packages/django/templatetags/static.py +++ b/venv/Lib/site-packages/django/templatetags/static.py @@ -9,14 +9,14 @@ register = template.Library() class PrefixNode(template.Node): + def __repr__(self): return "<PrefixNode for %r>" % self.name def __init__(self, varname=None, name=None): if name is None: raise template.TemplateSyntaxError( - "Prefix nodes must be given a name to return." - ) + "Prefix nodes must be given a name to return.") self.varname = varname self.name = name @@ -25,13 +25,11 @@ class PrefixNode(template.Node): """ Class method to parse prefix node and return a Node. """ - # token.split_contents() isn't useful here because tags using this - # method don't accept variable as arguments. + # token.split_contents() isn't useful here because tags using this method don't accept variable as arguments tokens = token.contents.split() - if len(tokens) > 1 and tokens[1] != "as": + if len(tokens) > 1 and tokens[1] != 'as': raise template.TemplateSyntaxError( - "First argument in '%s' must be 'as'" % tokens[0] - ) + "First argument in '%s' must be 'as'" % tokens[0]) if len(tokens) > 1: varname = tokens[2] else: @@ -43,9 +41,9 @@ class PrefixNode(template.Node): try: from django.conf import settings except ImportError: - prefix = "" + prefix = '' else: - prefix = iri_to_uri(getattr(settings, name, "")) + prefix = iri_to_uri(getattr(settings, name, '')) return prefix def render(self, context): @@ -53,7 +51,7 @@ class PrefixNode(template.Node): if self.varname is None: return prefix context[self.varname] = prefix - return "" + return '' @register.tag @@ -93,21 +91,13 @@ def get_media_prefix(parser, token): class StaticNode(template.Node): - child_nodelists = () - def __init__(self, varname=None, path=None): if path is None: raise template.TemplateSyntaxError( - "Static template nodes must be given a path to return." - ) + "Static template nodes must be given a path to return.") self.path = path self.varname = varname - def __repr__(self): - return ( - f"{self.__class__.__name__}(varname={self.varname!r}, path={self.path!r})" - ) - def url(self, context): path = self.path.resolve(context) return self.handle_simple(path) @@ -119,13 +109,12 @@ class StaticNode(template.Node): if self.varname is None: return url context[self.varname] = url - return "" + return '' @classmethod def handle_simple(cls, path): - if apps.is_installed("django.contrib.staticfiles"): + if apps.is_installed('django.contrib.staticfiles'): from django.contrib.staticfiles.storage import staticfiles_storage - return staticfiles_storage.url(path) else: return urljoin(PrefixNode.handle_simple("STATIC_URL"), quote(path)) @@ -139,12 +128,11 @@ class StaticNode(template.Node): if len(bits) < 2: raise template.TemplateSyntaxError( - "'%s' takes at least one argument (path to file)" % bits[0] - ) + "'%s' takes at least one argument (path to file)" % bits[0]) path = parser.compile_filter(bits[1]) - if len(bits) >= 2 and bits[-2] == "as": + if len(bits) >= 2 and bits[-2] == 'as': varname = bits[3] else: varname = None @@ -152,7 +140,7 @@ class StaticNode(template.Node): return cls(varname, path) -@register.tag("static") +@register.tag('static') def do_static(parser, token): """ Join the given path with the STATIC_URL setting. diff --git a/venv/Lib/site-packages/django/templatetags/tz.py b/venv/Lib/site-packages/django/templatetags/tz.py index de0432c..e1d2fe8 100644 --- a/venv/Lib/site-packages/django/templatetags/tz.py +++ b/venv/Lib/site-packages/django/templatetags/tz.py @@ -1,38 +1,13 @@ from datetime import datetime, tzinfo -try: - import zoneinfo -except ImportError: - from backports import zoneinfo +import pytz -from django.conf import settings from django.template import Library, Node, TemplateSyntaxError from django.utils import timezone register = Library() -# RemovedInDjango50Warning: shim to allow catching the exception in the calling -# scope if pytz is not installed. -class UnknownTimezoneException(BaseException): - pass - - -# RemovedInDjango50Warning -def timezone_constructor(tzname): - if settings.USE_DEPRECATED_PYTZ: - import pytz - - try: - return pytz.timezone(tzname) - except pytz.UnknownTimeZoneError: - raise UnknownTimezoneException - try: - return zoneinfo.ZoneInfo(tzname) - except zoneinfo.ZoneInfoNotFoundError: - raise UnknownTimezoneException - - # HACK: datetime instances cannot be assigned new attributes. Define a subclass # in order to define new attributes in do_timezone(). class datetimeobject(datetime): @@ -41,7 +16,6 @@ class datetimeobject(datetime): # Template filters - @register.filter def localtime(value): """ @@ -60,7 +34,7 @@ def utc(value): return do_timezone(value, timezone.utc) -@register.filter("timezone") +@register.filter('timezone') def do_timezone(value, arg): """ Convert a datetime to local time in a given time zone. @@ -70,7 +44,7 @@ def do_timezone(value, arg): Naive datetimes are assumed to be in local time in the default time zone. """ if not isinstance(value, datetime): - return "" + return '' # Obtain a timezone-aware datetime try: @@ -80,45 +54,36 @@ def do_timezone(value, arg): # Filters must never raise exceptions, and pytz' exceptions inherit # Exception directly, not a specific subclass. So catch everything. except Exception: - return "" + return '' # Obtain a tzinfo instance if isinstance(arg, tzinfo): tz = arg elif isinstance(arg, str): try: - tz = timezone_constructor(arg) - except UnknownTimezoneException: - return "" + tz = pytz.timezone(arg) + except pytz.UnknownTimeZoneError: + return '' else: - return "" + return '' result = timezone.localtime(value, tz) # HACK: the convert_to_local_time flag will prevent # automatic conversion of the value to local time. - result = datetimeobject( - result.year, - result.month, - result.day, - result.hour, - result.minute, - result.second, - result.microsecond, - result.tzinfo, - ) + result = datetimeobject(result.year, result.month, result.day, + result.hour, result.minute, result.second, + result.microsecond, result.tzinfo) result.convert_to_local_time = False return result # Template tags - class LocalTimeNode(Node): """ Template node class used by ``localtime_tag``. """ - def __init__(self, nodelist, use_tz): self.nodelist = nodelist self.use_tz = use_tz @@ -135,7 +100,6 @@ class TimezoneNode(Node): """ Template node class used by ``timezone_tag``. """ - def __init__(self, nodelist, tz): self.nodelist = nodelist self.tz = tz @@ -150,16 +114,15 @@ class GetCurrentTimezoneNode(Node): """ Template node class used by ``get_current_timezone_tag``. """ - def __init__(self, variable): self.variable = variable def render(self, context): context[self.variable] = timezone.get_current_timezone_name() - return "" + return '' -@register.tag("localtime") +@register.tag('localtime') def localtime_tag(parser, token): """ Force or prevent conversion of datetime objects to local time, @@ -172,16 +135,17 @@ def localtime_tag(parser, token): bits = token.split_contents() if len(bits) == 1: use_tz = True - elif len(bits) > 2 or bits[1] not in ("on", "off"): - raise TemplateSyntaxError("%r argument should be 'on' or 'off'" % bits[0]) + elif len(bits) > 2 or bits[1] not in ('on', 'off'): + raise TemplateSyntaxError("%r argument should be 'on' or 'off'" % + bits[0]) else: - use_tz = bits[1] == "on" - nodelist = parser.parse(("endlocaltime",)) + use_tz = bits[1] == 'on' + nodelist = parser.parse(('endlocaltime',)) parser.delete_first_token() return LocalTimeNode(nodelist, use_tz) -@register.tag("timezone") +@register.tag('timezone') def timezone_tag(parser, token): """ Enable a given time zone just for this block. @@ -198,9 +162,10 @@ def timezone_tag(parser, token): """ bits = token.split_contents() if len(bits) != 2: - raise TemplateSyntaxError("'%s' takes one argument (timezone)" % bits[0]) + raise TemplateSyntaxError("'%s' takes one argument (timezone)" % + bits[0]) tz = parser.compile_filter(bits[1]) - nodelist = parser.parse(("endtimezone",)) + nodelist = parser.parse(('endtimezone',)) parser.delete_first_token() return TimezoneNode(nodelist, tz) @@ -217,11 +182,9 @@ def get_current_timezone_tag(parser, token): This will fetch the currently active time zone and put its name into the ``TIME_ZONE`` context variable. """ - # token.split_contents() isn't useful here because this tag doesn't accept - # variable as arguments. + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments args = token.contents.split() - if len(args) != 3 or args[1] != "as": - raise TemplateSyntaxError( - "'get_current_timezone' requires 'as variable' (got %r)" % args - ) + if len(args) != 3 or args[1] != 'as': + raise TemplateSyntaxError("'get_current_timezone' requires " + "'as variable' (got %r)" % args) return GetCurrentTimezoneNode(args[2]) diff --git a/venv/Lib/site-packages/django/test/__init__.py b/venv/Lib/site-packages/django/test/__init__.py index 485298e..d1f953a 100644 --- a/venv/Lib/site-packages/django/test/__init__.py +++ b/venv/Lib/site-packages/django/test/__init__.py @@ -1,38 +1,21 @@ """Django Unit Test framework.""" -from django.test.client import AsyncClient, AsyncRequestFactory, Client, RequestFactory +from django.test.client import ( + AsyncClient, AsyncRequestFactory, Client, RequestFactory, +) from django.test.testcases import ( - LiveServerTestCase, - SimpleTestCase, - TestCase, - TransactionTestCase, - skipIfDBFeature, - skipUnlessAnyDBFeature, - skipUnlessDBFeature, + LiveServerTestCase, SimpleTestCase, TestCase, TransactionTestCase, + skipIfDBFeature, skipUnlessAnyDBFeature, skipUnlessDBFeature, ) from django.test.utils import ( - ignore_warnings, - modify_settings, - override_settings, - override_system_checks, - tag, + ignore_warnings, modify_settings, override_settings, + override_system_checks, tag, ) __all__ = [ - "AsyncClient", - "AsyncRequestFactory", - "Client", - "RequestFactory", - "TestCase", - "TransactionTestCase", - "SimpleTestCase", - "LiveServerTestCase", - "skipIfDBFeature", - "skipUnlessAnyDBFeature", - "skipUnlessDBFeature", - "ignore_warnings", - "modify_settings", - "override_settings", - "override_system_checks", - "tag", + 'AsyncClient', 'AsyncRequestFactory', 'Client', 'RequestFactory', + 'TestCase', 'TransactionTestCase', 'SimpleTestCase', 'LiveServerTestCase', + 'skipIfDBFeature', 'skipUnlessAnyDBFeature', 'skipUnlessDBFeature', + 'ignore_warnings', 'modify_settings', 'override_settings', + 'override_system_checks', 'tag', ] diff --git a/venv/Lib/site-packages/django/test/client.py b/venv/Lib/site-packages/django/test/client.py index 772fe11..2d501e0 100644 --- a/venv/Lib/site-packages/django/test/client.py +++ b/venv/Lib/site-packages/django/test/client.py @@ -16,7 +16,9 @@ from django.core.handlers.asgi import ASGIRequest from django.core.handlers.base import BaseHandler from django.core.handlers.wsgi import WSGIRequest from django.core.serializers.json import DjangoJSONEncoder -from django.core.signals import got_request_exception, request_finished, request_started +from django.core.signals import ( + got_request_exception, request_finished, request_started, +) from django.db import close_old_connections from django.http import HttpRequest, QueryDict, SimpleCookie from django.test import signals @@ -29,26 +31,20 @@ from django.utils.itercompat import is_iterable from django.utils.regex_helper import _lazy_re_compile __all__ = ( - "AsyncClient", - "AsyncRequestFactory", - "Client", - "RedirectCycleError", - "RequestFactory", - "encode_file", - "encode_multipart", + 'AsyncClient', 'AsyncRequestFactory', 'Client', 'RedirectCycleError', + 'RequestFactory', 'encode_file', 'encode_multipart', ) -BOUNDARY = "BoUnDaRyStRiNg" -MULTIPART_CONTENT = "multipart/form-data; boundary=%s" % BOUNDARY -CONTENT_TYPE_RE = _lazy_re_compile(r".*; charset=([\w\d-]+);?") +BOUNDARY = 'BoUnDaRyStRiNg' +MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY +CONTENT_TYPE_RE = _lazy_re_compile(r'.*; charset=([\w\d-]+);?') # Structured suffix spec: https://tools.ietf.org/html/rfc6838#section-4.2.8 -JSON_CONTENT_TYPE_RE = _lazy_re_compile(r"^application\/(.+\+)?json") +JSON_CONTENT_TYPE_RE = _lazy_re_compile(r'^application\/(.+\+)?json') class RedirectCycleError(Exception): """The test client has been asked to follow a redirect loop.""" - def __init__(self, message, last_response): super().__init__(message) self.last_response = last_response @@ -62,7 +58,6 @@ class FakePayload: length. This makes sure that views can't do anything under the test client that wouldn't work in real life. """ - def __init__(self, content=None): self.__content = BytesIO() self.__len = 0 @@ -79,9 +74,7 @@ class FakePayload: self.read_started = True if num_bytes is None: num_bytes = self.__len or 0 - assert ( - self.__len >= num_bytes - ), "Cannot read more than the available bytes from the HTTP incoming data." + assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." content = self.__content.read(num_bytes) self.__len -= num_bytes return content @@ -99,13 +92,13 @@ def closing_iterator_wrapper(iterable, close): yield from iterable finally: request_finished.disconnect(close_old_connections) - close() # will fire request_finished + close() # will fire request_finished request_finished.connect(close_old_connections) def conditional_content_removal(request, response): """ - Simulate the behavior of most web servers by removing the content of + Simulate the behavior of most Web servers by removing the content of responses for HEAD requests, 1xx, 204, and 304 responses. Ensure compliance with RFC 7230, section 3.3.3. """ @@ -113,22 +106,21 @@ def conditional_content_removal(request, response): if response.streaming: response.streaming_content = [] else: - response.content = b"" - if request.method == "HEAD": + response.content = b'' + if request.method == 'HEAD': if response.streaming: response.streaming_content = [] else: - response.content = b"" + response.content = b'' return response class ClientHandler(BaseHandler): """ - An HTTP Handler that can be used for testing purposes. Use the WSGI + A HTTP Handler that can be used for testing purposes. Use the WSGI interface to compose requests, but return the raw HttpResponse object with the originating WSGIRequest attached to its ``wsgi_request`` attribute. """ - def __init__(self, enforce_csrf_checks=True, *args, **kwargs): self.enforce_csrf_checks = enforce_csrf_checks super().__init__(*args, **kwargs) @@ -152,7 +144,7 @@ class ClientHandler(BaseHandler): # Request goes through middleware. response = self.get_response(request) - # Simulate behaviors of most web servers. + # Simulate behaviors of most Web servers. conditional_content_removal(request, response) # Attach the originating request to the response so that it could be @@ -162,11 +154,10 @@ class ClientHandler(BaseHandler): # Emulate a WSGI server by calling the close method on completion. if response.streaming: response.streaming_content = closing_iterator_wrapper( - response.streaming_content, response.close - ) + response.streaming_content, response.close) else: request_finished.disconnect(close_old_connections) - response.close() # will fire request_finished + response.close() # will fire request_finished request_finished.connect(close_old_connections) return response @@ -174,7 +165,6 @@ class ClientHandler(BaseHandler): class AsyncClientHandler(BaseHandler): """An async version of ClientHandler.""" - def __init__(self, enforce_csrf_checks=True, *args, **kwargs): self.enforce_csrf_checks = enforce_csrf_checks super().__init__(*args, **kwargs) @@ -185,15 +175,13 @@ class AsyncClientHandler(BaseHandler): if self._middleware_chain is None: self.load_middleware(is_async=True) # Extract body file from the scope, if provided. - if "_body_file" in scope: - body_file = scope.pop("_body_file") + if '_body_file' in scope: + body_file = scope.pop('_body_file') else: - body_file = FakePayload("") + body_file = FakePayload('') request_started.disconnect(close_old_connections) - await sync_to_async(request_started.send, thread_sensitive=False)( - sender=self.__class__, scope=scope - ) + await sync_to_async(request_started.send, thread_sensitive=False)(sender=self.__class__, scope=scope) request_started.connect(close_old_connections) request = ASGIRequest(scope, body_file) # Sneaky little hack so that we can easily get round @@ -202,16 +190,14 @@ class AsyncClientHandler(BaseHandler): request._dont_enforce_csrf_checks = not self.enforce_csrf_checks # Request goes through middleware. response = await self.get_response_async(request) - # Simulate behaviors of most web servers. + # Simulate behaviors of most Web servers. conditional_content_removal(request, response) # Attach the originating ASGI request to the response so that it could # be later retrieved. response.asgi_request = request # Emulate a server by calling the close method on completion. if response.streaming: - response.streaming_content = await sync_to_async( - closing_iterator_wrapper, thread_sensitive=False - )( + response.streaming_content = await sync_to_async(closing_iterator_wrapper, thread_sensitive=False)( response.streaming_content, response.close, ) @@ -230,10 +216,10 @@ def store_rendered_templates(store, signal, sender, template, context, **kwargs) The context is copied so that it is an accurate representation at the time of rendering. """ - store.setdefault("templates", []).append(template) - if "context" not in store: - store["context"] = ContextList() - store["context"].append(copy(context)) + store.setdefault('templates', []).append(template) + if 'context' not in store: + store['context'] = ContextList() + store['context'].append(copy(context)) def encode_multipart(boundary, data): @@ -269,33 +255,25 @@ def encode_multipart(boundary, data): if is_file(item): lines.extend(encode_file(boundary, key, item)) else: - lines.extend( - to_bytes(val) - for val in [ - "--%s" % boundary, - 'Content-Disposition: form-data; name="%s"' % key, - "", - item, - ] - ) + lines.extend(to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + '', + item + ]) else: - lines.extend( - to_bytes(val) - for val in [ - "--%s" % boundary, - 'Content-Disposition: form-data; name="%s"' % key, - "", - value, - ] - ) + lines.extend(to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value + ]) - lines.extend( - [ - to_bytes("--%s--" % boundary), - b"", - ] - ) - return b"\r\n".join(lines) + lines.extend([ + to_bytes('--%s--' % boundary), + b'', + ]) + return b'\r\n'.join(lines) def encode_file(boundary, key, file): @@ -304,10 +282,10 @@ def encode_file(boundary, key, file): # file.name might not be a string. For example, it's an int for # tempfile.TemporaryFile(). - file_has_string_name = hasattr(file, "name") and isinstance(file.name, str) - filename = os.path.basename(file.name) if file_has_string_name else "" + file_has_string_name = hasattr(file, 'name') and isinstance(file.name, str) + filename = os.path.basename(file.name) if file_has_string_name else '' - if hasattr(file, "content_type"): + if hasattr(file, 'content_type'): content_type = file.content_type elif filename: content_type = mimetypes.guess_type(filename)[0] @@ -315,16 +293,15 @@ def encode_file(boundary, key, file): content_type = None if content_type is None: - content_type = "application/octet-stream" + content_type = 'application/octet-stream' filename = filename or key return [ - to_bytes("--%s" % boundary), - to_bytes( - 'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename) - ), - to_bytes("Content-Type: %s" % content_type), - b"", - to_bytes(file.read()), + to_bytes('--%s' % boundary), + to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' + % (key, filename)), + to_bytes('Content-Type: %s' % content_type), + b'', + to_bytes(file.read()) ] @@ -341,7 +318,6 @@ class RequestFactory: Once you have a request object you can pass it to any view function, just as if that view had been hooked up using a URLconf. """ - def __init__(self, *, json_encoder=DjangoJSONEncoder, **defaults): self.json_encoder = json_encoder self.defaults = defaults @@ -357,26 +333,24 @@ class RequestFactory: # - REMOTE_ADDR: often useful, see #8551. # See https://www.python.org/dev/peps/pep-3333/#environ-variables return { - "HTTP_COOKIE": "; ".join( - sorted( - "%s=%s" % (morsel.key, morsel.coded_value) - for morsel in self.cookies.values() - ) - ), - "PATH_INFO": "/", - "REMOTE_ADDR": "127.0.0.1", - "REQUEST_METHOD": "GET", - "SCRIPT_NAME": "", - "SERVER_NAME": "testserver", - "SERVER_PORT": "80", - "SERVER_PROTOCOL": "HTTP/1.1", - "wsgi.version": (1, 0), - "wsgi.url_scheme": "http", - "wsgi.input": FakePayload(b""), - "wsgi.errors": self.errors, - "wsgi.multiprocess": True, - "wsgi.multithread": False, - "wsgi.run_once": False, + 'HTTP_COOKIE': '; '.join(sorted( + '%s=%s' % (morsel.key, morsel.coded_value) + for morsel in self.cookies.values() + )), + 'PATH_INFO': '/', + 'REMOTE_ADDR': '127.0.0.1', + 'REQUEST_METHOD': 'GET', + 'SCRIPT_NAME': '', + 'SERVER_NAME': 'testserver', + 'SERVER_PORT': '80', + 'SERVER_PROTOCOL': 'HTTP/1.1', + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': 'http', + 'wsgi.input': FakePayload(b''), + 'wsgi.errors': self.errors, + 'wsgi.multiprocess': True, + 'wsgi.multithread': False, + 'wsgi.run_once': False, **self.defaults, **request, } @@ -402,9 +376,7 @@ class RequestFactory: Return encoded JSON if data is a dict, list, or tuple and content_type is application/json. """ - should_encode = JSON_CONTENT_TYPE_RE.match(content_type) and isinstance( - data, (dict, list, tuple) - ) + should_encode = JSON_CONTENT_TYPE_RE.match(content_type) and isinstance(data, (dict, list, tuple)) return json.dumps(data, cls=self.json_encoder) if should_encode else data def _get_path(self, parsed): @@ -416,128 +388,88 @@ class RequestFactory: # Replace the behavior where non-ASCII values in the WSGI environ are # arbitrarily decoded with ISO-8859-1. # Refs comment in `get_bytes_from_wsgi()`. - return path.decode("iso-8859-1") + return path.decode('iso-8859-1') def get(self, path, data=None, secure=False, **extra): """Construct a GET request.""" data = {} if data is None else data - return self.generic( - "GET", - path, - secure=secure, - **{ - "QUERY_STRING": urlencode(data, doseq=True), - **extra, - }, - ) + return self.generic('GET', path, secure=secure, **{ + 'QUERY_STRING': urlencode(data, doseq=True), + **extra, + }) - def post( - self, path, data=None, content_type=MULTIPART_CONTENT, secure=False, **extra - ): + def post(self, path, data=None, content_type=MULTIPART_CONTENT, + secure=False, **extra): """Construct a POST request.""" data = self._encode_json({} if data is None else data, content_type) post_data = self._encode_data(data, content_type) - return self.generic( - "POST", path, post_data, content_type, secure=secure, **extra - ) + return self.generic('POST', path, post_data, content_type, + secure=secure, **extra) def head(self, path, data=None, secure=False, **extra): """Construct a HEAD request.""" data = {} if data is None else data - return self.generic( - "HEAD", - path, - secure=secure, - **{ - "QUERY_STRING": urlencode(data, doseq=True), - **extra, - }, - ) + return self.generic('HEAD', path, secure=secure, **{ + 'QUERY_STRING': urlencode(data, doseq=True), + **extra, + }) def trace(self, path, secure=False, **extra): """Construct a TRACE request.""" - return self.generic("TRACE", path, secure=secure, **extra) + return self.generic('TRACE', path, secure=secure, **extra) - def options( - self, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, - ): + def options(self, path, data='', content_type='application/octet-stream', + secure=False, **extra): "Construct an OPTIONS request." - return self.generic("OPTIONS", path, data, content_type, secure=secure, **extra) + return self.generic('OPTIONS', path, data, content_type, + secure=secure, **extra) - def put( - self, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, - ): + def put(self, path, data='', content_type='application/octet-stream', + secure=False, **extra): """Construct a PUT request.""" data = self._encode_json(data, content_type) - return self.generic("PUT", path, data, content_type, secure=secure, **extra) + return self.generic('PUT', path, data, content_type, + secure=secure, **extra) - def patch( - self, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, - ): + def patch(self, path, data='', content_type='application/octet-stream', + secure=False, **extra): """Construct a PATCH request.""" data = self._encode_json(data, content_type) - return self.generic("PATCH", path, data, content_type, secure=secure, **extra) + return self.generic('PATCH', path, data, content_type, + secure=secure, **extra) - def delete( - self, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, - ): + def delete(self, path, data='', content_type='application/octet-stream', + secure=False, **extra): """Construct a DELETE request.""" data = self._encode_json(data, content_type) - return self.generic("DELETE", path, data, content_type, secure=secure, **extra) + return self.generic('DELETE', path, data, content_type, + secure=secure, **extra) - def generic( - self, - method, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, - ): + def generic(self, method, path, data='', + content_type='application/octet-stream', secure=False, + **extra): """Construct an arbitrary HTTP request.""" parsed = urlparse(str(path)) # path can be lazy data = force_bytes(data, settings.DEFAULT_CHARSET) r = { - "PATH_INFO": self._get_path(parsed), - "REQUEST_METHOD": method, - "SERVER_PORT": "443" if secure else "80", - "wsgi.url_scheme": "https" if secure else "http", + 'PATH_INFO': self._get_path(parsed), + 'REQUEST_METHOD': method, + 'SERVER_PORT': '443' if secure else '80', + 'wsgi.url_scheme': 'https' if secure else 'http', } if data: - r.update( - { - "CONTENT_LENGTH": str(len(data)), - "CONTENT_TYPE": content_type, - "wsgi.input": FakePayload(data), - } - ) + r.update({ + 'CONTENT_LENGTH': str(len(data)), + 'CONTENT_TYPE': content_type, + 'wsgi.input': FakePayload(data), + }) r.update(extra) # If QUERY_STRING is absent or empty, we want to extract it from the URL. - if not r.get("QUERY_STRING"): + if not r.get('QUERY_STRING'): # WSGI requires latin-1 encoded strings. See get_path_info(). - query_string = parsed[4].encode().decode("iso-8859-1") - r["QUERY_STRING"] = query_string + query_string = parsed[4].encode().decode('iso-8859-1') + r['QUERY_STRING'] = query_string return self.request(**r) @@ -555,35 +487,30 @@ class AsyncRequestFactory(RequestFactory): a) this makes ASGIRequest subclasses, and b) AsyncTestClient can subclass it. """ - def _base_scope(self, **request): """The base scope for a request.""" # This is a minimal valid ASGI scope, plus: # - headers['cookie'] for cookie support, # - 'client' often useful, see #8551. scope = { - "asgi": {"version": "3.0"}, - "type": "http", - "http_version": "1.1", - "client": ["127.0.0.1", 0], - "server": ("testserver", "80"), - "scheme": "http", - "method": "GET", - "headers": [], + 'asgi': {'version': '3.0'}, + 'type': 'http', + 'http_version': '1.1', + 'client': ['127.0.0.1', 0], + 'server': ('testserver', '80'), + 'scheme': 'http', + 'method': 'GET', + 'headers': [], **self.defaults, **request, } - scope["headers"].append( - ( - b"cookie", - b"; ".join( - sorted( - ("%s=%s" % (morsel.key, morsel.coded_value)).encode("ascii") - for morsel in self.cookies.values() - ) - ), - ) - ) + scope['headers'].append(( + b'cookie', + b'; '.join(sorted( + ('%s=%s' % (morsel.key, morsel.coded_value)).encode('ascii') + for morsel in self.cookies.values() + )), + )) return scope def request(self, **request): @@ -591,52 +518,43 @@ class AsyncRequestFactory(RequestFactory): # This is synchronous, which means all methods on this class are. # AsyncClient, however, has an async request function, which makes all # its methods async. - if "_body_file" in request: - body_file = request.pop("_body_file") + if '_body_file' in request: + body_file = request.pop('_body_file') else: - body_file = FakePayload("") + body_file = FakePayload('') return ASGIRequest(self._base_scope(**request), body_file) def generic( - self, - method, - path, - data="", - content_type="application/octet-stream", - secure=False, - **extra, + self, method, path, data='', content_type='application/octet-stream', + secure=False, **extra, ): """Construct an arbitrary HTTP request.""" parsed = urlparse(str(path)) # path can be lazy. data = force_bytes(data, settings.DEFAULT_CHARSET) s = { - "method": method, - "path": self._get_path(parsed), - "server": ("127.0.0.1", "443" if secure else "80"), - "scheme": "https" if secure else "http", - "headers": [(b"host", b"testserver")], + 'method': method, + 'path': self._get_path(parsed), + 'server': ('127.0.0.1', '443' if secure else '80'), + 'scheme': 'https' if secure else 'http', + 'headers': [(b'host', b'testserver')], } if data: - s["headers"].extend( - [ - (b"content-length", str(len(data)).encode("ascii")), - (b"content-type", content_type.encode("ascii")), - ] - ) - s["_body_file"] = FakePayload(data) - follow = extra.pop("follow", None) + s['headers'].extend([ + (b'content-length', str(len(data)).encode('ascii')), + (b'content-type', content_type.encode('ascii')), + ]) + s['_body_file'] = FakePayload(data) + follow = extra.pop('follow', None) if follow is not None: - s["follow"] = follow - if query_string := extra.pop("QUERY_STRING", None): - s["query_string"] = query_string - s["headers"] += [ - (key.lower().encode("ascii"), value.encode("latin1")) + s['follow'] = follow + s['headers'] += [ + (key.lower().encode('ascii'), value.encode('latin1')) for key, value in extra.items() ] # If QUERY_STRING is absent or empty, we want to extract it from the # URL. - if not s.get("query_string"): - s["query_string"] = parsed[4] + if not s.get('query_string'): + s['query_string'] = parsed[4] return self.request(**s) @@ -644,7 +562,6 @@ class ClientMixin: """ Mixin with common methods between Client and AsyncClient. """ - def store_exc_info(self, **kwargs): """Store exceptions when they are generated by a view.""" self.exc_info = sys.exc_info() @@ -682,7 +599,6 @@ class ClientMixin: are incorrect. """ from django.contrib.auth import authenticate - user = authenticate(**credentials) if user: self._login(user) @@ -692,10 +608,9 @@ class ClientMixin: def force_login(self, user, backend=None): def get_backend(): from django.contrib.auth import load_backend - for backend_path in settings.AUTHENTICATION_BACKENDS: backend = load_backend(backend_path) - if hasattr(backend, "get_user"): + if hasattr(backend, 'get_user'): return backend_path if backend is None: @@ -720,18 +635,17 @@ class ClientMixin: session_cookie = settings.SESSION_COOKIE_NAME self.cookies[session_cookie] = request.session.session_key cookie_data = { - "max-age": None, - "path": "/", - "domain": settings.SESSION_COOKIE_DOMAIN, - "secure": settings.SESSION_COOKIE_SECURE or None, - "expires": None, + 'max-age': None, + 'path': '/', + 'domain': settings.SESSION_COOKIE_DOMAIN, + 'secure': settings.SESSION_COOKIE_SECURE or None, + 'expires': None, } self.cookies[session_cookie].update(cookie_data) def logout(self): """Log out the user by removing the cookies and session object.""" from django.contrib.auth import get_user, logout - request = HttpRequest() if self.session: request.session = self.session @@ -743,15 +657,13 @@ class ClientMixin: self.cookies = SimpleCookie() def _parse_json(self, response, **extra): - if not hasattr(response, "_json"): - if not JSON_CONTENT_TYPE_RE.match(response.get("Content-Type")): + if not hasattr(response, '_json'): + if not JSON_CONTENT_TYPE_RE.match(response.get('Content-Type')): raise ValueError( 'Content-Type header is "%s", not "application/json"' - % response.get("Content-Type") + % response.get('Content-Type') ) - response._json = json.loads( - response.content.decode(response.charset), **extra - ) + response._json = json.loads(response.content.decode(response.charset), **extra) return response._json @@ -773,10 +685,7 @@ class Client(ClientMixin, RequestFactory): contexts and templates produced by a view, rather than the HTML rendered to the end-user. """ - - def __init__( - self, enforce_csrf_checks=False, raise_request_exception=True, **defaults - ): + def __init__(self, enforce_csrf_checks=False, raise_request_exception=True, **defaults): super().__init__(**defaults) self.handler = ClientHandler(enforce_csrf_checks) self.raise_request_exception = raise_request_exception @@ -812,14 +721,11 @@ class Client(ClientMixin, RequestFactory): response.client = self response.request = request # Add any rendered template detail to the response. - response.templates = data.get("templates", []) - response.context = data.get("context") + response.templates = data.get('templates', []) + response.context = data.get('context') response.json = partial(self._parse_json, response) # Attach the ResolverMatch instance to the response. - urlconf = getattr(response.wsgi_request, "urlconf", None) - response.resolver_match = SimpleLazyObject( - lambda: resolve(request["PATH_INFO"], urlconf=urlconf), - ) + response.resolver_match = SimpleLazyObject(lambda: resolve(request['PATH_INFO'])) # Flatten a single context. Not really necessary anymore thanks to the # __getattr__ flattening in ContextList, but has some edge case # backwards compatibility implications. @@ -838,24 +744,13 @@ class Client(ClientMixin, RequestFactory): response = self._handle_redirects(response, data=data, **extra) return response - def post( - self, - path, - data=None, - content_type=MULTIPART_CONTENT, - follow=False, - secure=False, - **extra, - ): + def post(self, path, data=None, content_type=MULTIPART_CONTENT, + follow=False, secure=False, **extra): """Request a response from the server using POST.""" self.extra = extra - response = super().post( - path, data=data, content_type=content_type, secure=secure, **extra - ) + response = super().post(path, data=data, content_type=content_type, secure=secure, **extra) if follow: - response = self._handle_redirects( - response, data=data, content_type=content_type, **extra - ) + response = self._handle_redirects(response, data=data, content_type=content_type, **extra) return response def head(self, path, data=None, follow=False, secure=False, **extra): @@ -866,87 +761,43 @@ class Client(ClientMixin, RequestFactory): response = self._handle_redirects(response, data=data, **extra) return response - def options( - self, - path, - data="", - content_type="application/octet-stream", - follow=False, - secure=False, - **extra, - ): + def options(self, path, data='', content_type='application/octet-stream', + follow=False, secure=False, **extra): """Request a response from the server using OPTIONS.""" self.extra = extra - response = super().options( - path, data=data, content_type=content_type, secure=secure, **extra - ) + response = super().options(path, data=data, content_type=content_type, secure=secure, **extra) if follow: - response = self._handle_redirects( - response, data=data, content_type=content_type, **extra - ) + response = self._handle_redirects(response, data=data, content_type=content_type, **extra) return response - def put( - self, - path, - data="", - content_type="application/octet-stream", - follow=False, - secure=False, - **extra, - ): + def put(self, path, data='', content_type='application/octet-stream', + follow=False, secure=False, **extra): """Send a resource to the server using PUT.""" self.extra = extra - response = super().put( - path, data=data, content_type=content_type, secure=secure, **extra - ) + response = super().put(path, data=data, content_type=content_type, secure=secure, **extra) if follow: - response = self._handle_redirects( - response, data=data, content_type=content_type, **extra - ) + response = self._handle_redirects(response, data=data, content_type=content_type, **extra) return response - def patch( - self, - path, - data="", - content_type="application/octet-stream", - follow=False, - secure=False, - **extra, - ): + def patch(self, path, data='', content_type='application/octet-stream', + follow=False, secure=False, **extra): """Send a resource to the server using PATCH.""" self.extra = extra - response = super().patch( - path, data=data, content_type=content_type, secure=secure, **extra - ) + response = super().patch(path, data=data, content_type=content_type, secure=secure, **extra) if follow: - response = self._handle_redirects( - response, data=data, content_type=content_type, **extra - ) + response = self._handle_redirects(response, data=data, content_type=content_type, **extra) return response - def delete( - self, - path, - data="", - content_type="application/octet-stream", - follow=False, - secure=False, - **extra, - ): + def delete(self, path, data='', content_type='application/octet-stream', + follow=False, secure=False, **extra): """Send a DELETE request to the server.""" self.extra = extra - response = super().delete( - path, data=data, content_type=content_type, secure=secure, **extra - ) + response = super().delete(path, data=data, content_type=content_type, secure=secure, **extra) if follow: - response = self._handle_redirects( - response, data=data, content_type=content_type, **extra - ) + response = self._handle_redirects(response, data=data, content_type=content_type, **extra) return response - def trace(self, path, data="", follow=False, secure=False, **extra): + def trace(self, path, data='', follow=False, secure=False, **extra): """Send a TRACE request to the server.""" self.extra = extra response = super().trace(path, data=data, secure=secure, **extra) @@ -954,7 +805,7 @@ class Client(ClientMixin, RequestFactory): response = self._handle_redirects(response, data=data, **extra) return response - def _handle_redirects(self, response, data="", content_type="", **extra): + def _handle_redirects(self, response, data='', content_type='', **extra): """ Follow any redirects by requesting responses from the server using GET. """ @@ -973,46 +824,36 @@ class Client(ClientMixin, RequestFactory): url = urlsplit(response_url) if url.scheme: - extra["wsgi.url_scheme"] = url.scheme + extra['wsgi.url_scheme'] = url.scheme if url.hostname: - extra["SERVER_NAME"] = url.hostname + extra['SERVER_NAME'] = url.hostname if url.port: - extra["SERVER_PORT"] = str(url.port) + extra['SERVER_PORT'] = str(url.port) - path = url.path - # RFC 2616: bare domains without path are treated as the root. - if not path and url.netloc: - path = "/" # Prepend the request path to handle relative path redirects - if not path.startswith("/"): - path = urljoin(response.request["PATH_INFO"], path) + path = url.path + if not path.startswith('/'): + path = urljoin(response.request['PATH_INFO'], path) - if response.status_code in ( - HTTPStatus.TEMPORARY_REDIRECT, - HTTPStatus.PERMANENT_REDIRECT, - ): + if response.status_code in (HTTPStatus.TEMPORARY_REDIRECT, HTTPStatus.PERMANENT_REDIRECT): # Preserve request method and query string (if needed) # post-redirect for 307/308 responses. - request_method = response.request["REQUEST_METHOD"].lower() - if request_method not in ("get", "head"): - extra["QUERY_STRING"] = url.query + request_method = response.request['REQUEST_METHOD'].lower() + if request_method not in ('get', 'head'): + extra['QUERY_STRING'] = url.query request_method = getattr(self, request_method) else: request_method = self.get data = QueryDict(url.query) content_type = None - response = request_method( - path, data=data, content_type=content_type, follow=False, **extra - ) + response = request_method(path, data=data, content_type=content_type, follow=False, **extra) response.redirect_chain = redirect_chain if redirect_chain[-1] in redirect_chain[:-1]: # Check that we're not redirecting to somewhere we've already # been to, to prevent loops. - raise RedirectCycleError( - "Redirect loop detected.", last_response=response - ) + raise RedirectCycleError("Redirect loop detected.", last_response=response) if len(redirect_chain) > 20: # Such a lengthy chain likely also means a loop, but one with # a growing path, changing view, or changing query argument; @@ -1029,10 +870,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory): Does not currently support "follow" on its methods. """ - - def __init__( - self, enforce_csrf_checks=False, raise_request_exception=True, **defaults - ): + def __init__(self, enforce_csrf_checks=False, raise_request_exception=True, **defaults): super().__init__(**defaults) self.handler = AsyncClientHandler(enforce_csrf_checks) self.raise_request_exception = raise_request_exception @@ -1046,19 +884,20 @@ class AsyncClient(ClientMixin, AsyncRequestFactory): query environment, which can be overridden using the arguments to the request. """ - if "follow" in request: + if 'follow' in request: raise NotImplementedError( - "AsyncClient request methods do not accept the follow parameter." + 'AsyncClient request methods do not accept the follow ' + 'parameter.' ) scope = self._base_scope(**request) # Curry a data dictionary into an instance of the template renderer # callback function. data = {} on_template_render = partial(store_rendered_templates, data) - signal_uid = "template-render-%s" % id(request) + signal_uid = 'template-render-%s' % id(request) signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) # Capture exceptions created by the handler. - exception_uid = "request-exception-%s" % id(request) + exception_uid = 'request-exception-%s' % id(request) got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) try: response = await self.handler(scope) @@ -1071,14 +910,11 @@ class AsyncClient(ClientMixin, AsyncRequestFactory): response.client = self response.request = request # Add any rendered template detail to the response. - response.templates = data.get("templates", []) - response.context = data.get("context") + response.templates = data.get('templates', []) + response.context = data.get('context') response.json = partial(self._parse_json, response) # Attach the ResolverMatch instance to the response. - urlconf = getattr(response.asgi_request, "urlconf", None) - response.resolver_match = SimpleLazyObject( - lambda: resolve(request["path"], urlconf=urlconf), - ) + response.resolver_match = SimpleLazyObject(lambda: resolve(request['path'])) # Flatten a single context. Not really necessary anymore thanks to the # __getattr__ flattening in ContextList, but has some edge case # backwards compatibility implications. diff --git a/venv/Lib/site-packages/django/test/html.py b/venv/Lib/site-packages/django/test/html.py index cf8a27d..76ebe0d 100644 --- a/venv/Lib/site-packages/django/test/html.py +++ b/venv/Lib/site-packages/django/test/html.py @@ -7,62 +7,11 @@ from django.utils.regex_helper import _lazy_re_compile # ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 # SPACE. # https://infra.spec.whatwg.org/#ascii-whitespace -ASCII_WHITESPACE = _lazy_re_compile(r"[\t\n\f\r ]+") - -# https://html.spec.whatwg.org/#attributes-3 -BOOLEAN_ATTRIBUTES = { - "allowfullscreen", - "async", - "autofocus", - "autoplay", - "checked", - "controls", - "default", - "defer ", - "disabled", - "formnovalidate", - "hidden", - "ismap", - "itemscope", - "loop", - "multiple", - "muted", - "nomodule", - "novalidate", - "open", - "playsinline", - "readonly", - "required", - "reversed", - "selected", - # Attributes for deprecated tags. - "truespeed", -} +ASCII_WHITESPACE = _lazy_re_compile(r'[\t\n\f\r ]+') def normalize_whitespace(string): - return ASCII_WHITESPACE.sub(" ", string) - - -def normalize_attributes(attributes): - normalized = [] - for name, value in attributes: - if name == "class" and value: - # Special case handling of 'class' attribute, so that comparisons - # of DOM instances are not sensitive to ordering of classes. - value = " ".join( - sorted(value for value in ASCII_WHITESPACE.split(value) if value) - ) - # Boolean attributes without a value is same as attribute with value - # that equals the attributes name. For example: - # <input checked> == <input checked="checked"> - if name in BOOLEAN_ATTRIBUTES: - if not value or value == name: - value = None - elif value is None: - value = "" - normalized.append((name, value)) - return normalized + return ASCII_WHITESPACE.sub(' ', string) class Element: @@ -100,14 +49,27 @@ class Element: for i, child in enumerate(self.children): if isinstance(child, str): self.children[i] = child.strip() - elif hasattr(child, "finalize"): + elif hasattr(child, 'finalize'): child.finalize() def __eq__(self, element): - if not hasattr(element, "name") or self.name != element.name: + if not hasattr(element, 'name') or self.name != element.name: + return False + if len(self.attributes) != len(element.attributes): return False if self.attributes != element.attributes: - return False + # attributes without a value is same as attribute with value that + # equals the attributes name: + # <input checked> == <input checked="checked"> + for i in range(len(self.attributes)): + attr, value = self.attributes[i] + other_attr, other_value = element.attributes[i] + if value is None: + value = attr + if other_value is None: + other_value = other_attr + if attr != other_attr or value != other_value: + return False return self.children == element.children def __hash__(self): @@ -162,18 +124,18 @@ class Element: return self.children[key] def __str__(self): - output = "<%s" % self.name + output = '<%s' % self.name for key, value in self.attributes: - if value is not None: + if value: output += ' %s="%s"' % (key, value) else: - output += " %s" % key + output += ' %s' % key if self.children: - output += ">\n" - output += "".join(str(c) for c in self.children) - output += "\n</%s>" % self.name + output += '>\n' + output += ''.join(str(c) for c in self.children) + output += '\n</%s>' % self.name else: - output += ">" + output += '>' return output def __repr__(self): @@ -185,7 +147,7 @@ class RootElement(Element): super().__init__(None, ()) def __str__(self): - return "".join(str(c) for c in self.children) + return ''.join(str(c) for c in self.children) class HTMLParseError(Exception): @@ -195,23 +157,10 @@ class HTMLParseError(Exception): class Parser(HTMLParser): # https://html.spec.whatwg.org/#void-elements SELF_CLOSING_TAGS = { - "area", - "base", - "br", - "col", - "embed", - "hr", - "img", - "input", - "link", - "meta", - "param", - "source", - "track", - "wbr", + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', + 'param', 'source', 'track', 'wbr', # Deprecated tags - "frame", - "spacer", + 'frame', 'spacer', } def __init__(self): @@ -228,9 +177,9 @@ class Parser(HTMLParser): position = self.element_positions[element] if position is None: position = self.getpos() - if hasattr(position, "lineno"): + if hasattr(position, 'lineno'): position = position.lineno, position.offset - return "Line %d, Column %d" % position + return 'Line %d, Column %d' % position @property def current(self): @@ -245,7 +194,14 @@ class Parser(HTMLParser): self.handle_endtag(tag) def handle_starttag(self, tag, attrs): - attrs = normalize_attributes(attrs) + # Special case handling of 'class' attribute, so that comparisons of DOM + # instances are not sensitive to ordering of classes. + attrs = [ + (name, ' '.join(sorted(value for value in ASCII_WHITESPACE.split(value) if value))) + if name == "class" + else (name, value) + for name, value in attrs + ] element = Element(tag, attrs) self.current.append(element) if tag not in self.SELF_CLOSING_TAGS: @@ -254,13 +210,13 @@ class Parser(HTMLParser): def handle_endtag(self, tag): if not self.open_tags: - self.error("Unexpected end tag `%s` (%s)" % (tag, self.format_position())) + self.error("Unexpected end tag `%s` (%s)" % ( + tag, self.format_position())) element = self.open_tags.pop() while element.name != tag: if not self.open_tags: - self.error( - "Unexpected end tag `%s` (%s)" % (tag, self.format_position()) - ) + self.error("Unexpected end tag `%s` (%s)" % ( + tag, self.format_position())) element = self.open_tags.pop() def handle_data(self, data): diff --git a/venv/Lib/site-packages/django/test/runner.py b/venv/Lib/site-packages/django/test/runner.py index 99afd20..a5fe7b7 100644 --- a/venv/Lib/site-packages/django/test/runner.py +++ b/venv/Lib/site-packages/django/test/runner.py @@ -1,33 +1,27 @@ -import argparse import ctypes import faulthandler -import hashlib import io import itertools import logging import multiprocessing import os import pickle -import random import sys import textwrap import unittest -import warnings -from collections import defaultdict -from contextlib import contextmanager from importlib import import_module from io import StringIO from django.core.management import call_command from django.db import connections from django.test import SimpleTestCase, TestCase -from django.test.utils import NullTimeKeeper, TimeKeeper, iter_test_cases -from django.test.utils import setup_databases as _setup_databases -from django.test.utils import setup_test_environment -from django.test.utils import teardown_databases as _teardown_databases -from django.test.utils import teardown_test_environment +from django.test.utils import ( + NullTimeKeeper, TimeKeeper, setup_databases as _setup_databases, + setup_test_environment, teardown_databases as _teardown_databases, + teardown_test_environment, +) from django.utils.datastructures import OrderedSet -from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.version import PY37 try: import ipdb as pdb @@ -42,7 +36,7 @@ except ImportError: class DebugSQLTextTestResult(unittest.TextTestResult): def __init__(self, stream, descriptions, verbosity): - self.logger = logging.getLogger("django.db.backends") + self.logger = logging.getLogger('django.db.backends') self.logger.setLevel(logging.DEBUG) self.debug_sql_stream = None super().__init__(stream, descriptions, verbosity) @@ -65,7 +59,7 @@ class DebugSQLTextTestResult(unittest.TextTestResult): super().addError(test, err) if self.debug_sql_stream is None: # Error before tests e.g. in setUpTestData(). - sql = "" + sql = '' else: self.debug_sql_stream.seek(0) sql = self.debug_sql_stream.read() @@ -80,11 +74,7 @@ class DebugSQLTextTestResult(unittest.TextTestResult): super().addSubTest(test, subtest, err) if err is not None: self.debug_sql_stream.seek(0) - errors = ( - self.failures - if issubclass(err[0], test.failureException) - else self.errors - ) + errors = self.failures if issubclass(err[0], test.failureException) else self.errors errors[-1] = errors[-1] + (self.debug_sql_stream.read(),) def printErrorList(self, flavour, errors): @@ -111,11 +101,6 @@ class PDBDebugResult(unittest.TextTestResult): super().addFailure(test, err) self.debug(err) - def addSubTest(self, test, subtest, err): - if err is not None: - self.debug(err) - super().addSubTest(test, subtest, err) - def debug(self, error): self._restoreStdout() self.buffer = False @@ -124,49 +109,25 @@ class PDBDebugResult(unittest.TextTestResult): pdb.post_mortem(traceback) -class DummyList: +class RemoteTestResult: """ - Dummy list class for faking storage of results in unittest.TestResult. + Record information about which tests have succeeded and which have failed. + + The sole purpose of this class is to record events in the child processes + so they can be replayed in the master process. As a consequence it doesn't + inherit unittest.TestResult and doesn't attempt to implement all its API. + + The implementation matches the unpythonic coding style of unittest2. """ - __slots__ = () - - def append(self, item): - pass - - -class RemoteTestResult(unittest.TestResult): - """ - Extend unittest.TestResult to record events in the child processes so they - can be replayed in the parent process. Events include things like which - tests succeeded or failed. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Fake storage of results to reduce memory usage. These are used by the - # unittest default methods, but here 'events' is used instead. - dummy_list = DummyList() - self.failures = dummy_list - self.errors = dummy_list - self.skipped = dummy_list - self.expectedFailures = dummy_list - self.unexpectedSuccesses = dummy_list - + def __init__(self): if tblib is not None: tblib.pickling_support.install() - self.events = [] - def __getstate__(self): - # Make this class picklable by removing the file-like buffer - # attributes. This is possible since they aren't used after unpickling - # after being sent to ParallelTestSuite. - state = self.__dict__.copy() - state.pop("_stdout_buffer", None) - state.pop("_stderr_buffer", None) - state.pop("_original_stdout", None) - state.pop("_original_stderr", None) - return state + self.events = [] + self.failfast = False + self.shouldStop = False + self.testsRun = 0 @property def test_index(self): @@ -181,8 +142,7 @@ class RemoteTestResult(unittest.TestResult): pickle.loads(pickle.dumps(obj)) def _print_unpicklable_subtest(self, test, subtest, pickle_exc): - print( - """ + print(""" Subtest failed: test: {} @@ -195,10 +155,7 @@ test runner cannot handle it cleanly. Here is the pickling error: You should re-run this test with --parallel=1 to reproduce the failure with a cleaner failure message. -""".format( - test, subtest, pickle_exc - ) - ) +""".format(test, subtest, pickle_exc)) def check_picklable(self, test, err): # Ensure that sys.exc_info() tuples are picklable. This displays a @@ -211,16 +168,11 @@ with a cleaner failure message. self._confirm_picklable(err) except Exception as exc: original_exc_txt = repr(err[1]) - original_exc_txt = textwrap.fill( - original_exc_txt, 75, initial_indent=" ", subsequent_indent=" " - ) + original_exc_txt = textwrap.fill(original_exc_txt, 75, initial_indent=' ', subsequent_indent=' ') pickle_exc_txt = repr(exc) - pickle_exc_txt = textwrap.fill( - pickle_exc_txt, 75, initial_indent=" ", subsequent_indent=" " - ) + pickle_exc_txt = textwrap.fill(pickle_exc_txt, 75, initial_indent=' ', subsequent_indent=' ') if tblib is None: - print( - """ + print(""" {} failed: @@ -232,13 +184,9 @@ parallel test runner to handle this exception cleanly. In order to see the traceback, you should install tblib: python -m pip install tblib -""".format( - test, original_exc_txt - ) - ) +""".format(test, original_exc_txt)) else: - print( - """ + print(""" {} failed: @@ -253,10 +201,7 @@ Here's the error encountered while trying to pickle the exception: You should re-run this test with the --parallel=1 option to reproduce the failure and get a correct traceback. -""".format( - test, original_exc_txt, pickle_exc_txt - ) - ) +""".format(test, original_exc_txt, pickle_exc_txt)) raise def check_subtest_picklable(self, test, subtest): @@ -266,50 +211,52 @@ failure and get a correct traceback. self._print_unpicklable_subtest(test, subtest, exc) raise + def stop_if_failfast(self): + if self.failfast: + self.stop() + + def stop(self): + self.shouldStop = True + def startTestRun(self): - super().startTestRun() - self.events.append(("startTestRun",)) + self.events.append(('startTestRun',)) def stopTestRun(self): - super().stopTestRun() - self.events.append(("stopTestRun",)) + self.events.append(('stopTestRun',)) def startTest(self, test): - super().startTest(test) - self.events.append(("startTest", self.test_index)) + self.testsRun += 1 + self.events.append(('startTest', self.test_index)) def stopTest(self, test): - super().stopTest(test) - self.events.append(("stopTest", self.test_index)) + self.events.append(('stopTest', self.test_index)) def addError(self, test, err): self.check_picklable(test, err) - self.events.append(("addError", self.test_index, err)) - super().addError(test, err) + self.events.append(('addError', self.test_index, err)) + self.stop_if_failfast() def addFailure(self, test, err): self.check_picklable(test, err) - self.events.append(("addFailure", self.test_index, err)) - super().addFailure(test, err) + self.events.append(('addFailure', self.test_index, err)) + self.stop_if_failfast() def addSubTest(self, test, subtest, err): - # Follow Python's implementation of unittest.TestResult.addSubTest() by - # not doing anything when a subtest is successful. + # Follow Python 3.5's implementation of unittest.TestResult.addSubTest() + # by not doing anything when a subtest is successful. if err is not None: # Call check_picklable() before check_subtest_picklable() since # check_picklable() performs the tblib check. self.check_picklable(test, err) self.check_subtest_picklable(test, subtest) - self.events.append(("addSubTest", self.test_index, subtest, err)) - super().addSubTest(test, subtest, err) + self.events.append(('addSubTest', self.test_index, subtest, err)) + self.stop_if_failfast() def addSuccess(self, test): - self.events.append(("addSuccess", self.test_index)) - super().addSuccess(test) + self.events.append(('addSuccess', self.test_index)) def addSkip(self, test, reason): - self.events.append(("addSkip", self.test_index, reason)) - super().addSkip(test, reason) + self.events.append(('addSkip', self.test_index, reason)) def addExpectedFailure(self, test, err): # If tblib isn't installed, pickling the traceback will always fail. @@ -319,23 +266,11 @@ failure and get a correct traceback. if tblib is None: err = err[0], err[1], None self.check_picklable(test, err) - self.events.append(("addExpectedFailure", self.test_index, err)) - super().addExpectedFailure(test, err) + self.events.append(('addExpectedFailure', self.test_index, err)) def addUnexpectedSuccess(self, test): - self.events.append(("addUnexpectedSuccess", self.test_index)) - super().addUnexpectedSuccess(test) - - def wasSuccessful(self): - """Tells whether or not this result was a success.""" - failure_types = {"addError", "addFailure", "addSubTest", "addUnexpectedSuccess"} - return all(e[0] not in failure_types for e in self.events) - - def _exc_info_to_string(self, err, test): - # Make this method no-op. It only powers the default unittest behavior - # for recording errors, but this class pickles errors into 'events' - # instead. - return "" + self.events.append(('addUnexpectedSuccess', self.test_index)) + self.stop_if_failfast() class RemoteTestRunner: @@ -347,9 +282,8 @@ class RemoteTestRunner: resultclass = RemoteTestResult - def __init__(self, failfast=False, resultclass=None, buffer=False): + def __init__(self, failfast=False, resultclass=None): self.failfast = failfast - self.buffer = buffer if resultclass is not None: self.resultclass = resultclass @@ -357,37 +291,22 @@ class RemoteTestRunner: result = self.resultclass() unittest.registerResult(result) result.failfast = self.failfast - result.buffer = self.buffer test(result) return result -def get_max_test_processes(): - """ - The maximum number of test processes when using the --parallel option. - """ +def default_test_processes(): + """Default number of test processes when using the --parallel option.""" # The current implementation of the parallel test runner requires # multiprocessing to start subprocesses with fork(). - if multiprocessing.get_start_method() != "fork": + if multiprocessing.get_start_method() != 'fork': return 1 try: - return int(os.environ["DJANGO_TEST_PROCESSES"]) + return int(os.environ['DJANGO_TEST_PROCESSES']) except KeyError: return multiprocessing.cpu_count() -def parallel_type(value): - """Parse value passed to the --parallel option.""" - if value == "auto": - return value - try: - return int(value) - except ValueError: - raise argparse.ArgumentTypeError( - f"{value!r} is not an integer or the string 'auto'" - ) - - _worker_id = 0 @@ -423,8 +342,8 @@ def _run_subsuite(args): This helper lives at module-level and its arguments are wrapped in a tuple because of the multiprocessing module's requirements. """ - runner_class, subsuite_index, subsuite, failfast, buffer = args - runner = runner_class(failfast=failfast, buffer=buffer) + runner_class, subsuite_index, subsuite, failfast = args + runner = runner_class(failfast=failfast) result = runner.run(subsuite) return subsuite_index, result.events @@ -450,11 +369,10 @@ class ParallelTestSuite(unittest.TestSuite): run_subsuite = _run_subsuite runner_class = RemoteTestRunner - def __init__(self, subsuites, processes, failfast=False, buffer=False): - self.subsuites = subsuites + def __init__(self, suite, processes, failfast=False): + self.subsuites = partition_suite_by_case(suite) self.processes = processes self.failfast = failfast - self.buffer = buffer super().__init__() def run(self, result): @@ -479,7 +397,7 @@ class ParallelTestSuite(unittest.TestSuite): initargs=[counter], ) args = [ - (self.runner_class, index, subsuite, self.failfast, self.buffer) + (self.runner_class, index, subsuite, self.failfast) for index, subsuite in enumerate(self.subsuites) ] test_results = pool.imap_unordered(self.run_subsuite.__func__, args) @@ -515,66 +433,6 @@ class ParallelTestSuite(unittest.TestSuite): return iter(self.subsuites) -class Shuffler: - """ - This class implements shuffling with a special consistency property. - Consistency means that, for a given seed and key function, if two sets of - items are shuffled, the resulting order will agree on the intersection of - the two sets. For example, if items are removed from an original set, the - shuffled order for the new set will be the shuffled order of the original - set restricted to the smaller set. - """ - - # This doesn't need to be cryptographically strong, so use what's fastest. - hash_algorithm = "md5" - - @classmethod - def _hash_text(cls, text): - h = hashlib.new(cls.hash_algorithm) - h.update(text.encode("utf-8")) - return h.hexdigest() - - def __init__(self, seed=None): - if seed is None: - # Limit seeds to 10 digits for simpler output. - seed = random.randint(0, 10**10 - 1) - seed_source = "generated" - else: - seed_source = "given" - self.seed = seed - self.seed_source = seed_source - - @property - def seed_display(self): - return f"{self.seed!r} ({self.seed_source})" - - def _hash_item(self, item, key): - text = "{}{}".format(self.seed, key(item)) - return self._hash_text(text) - - def shuffle(self, items, key): - """ - Return a new list of the items in a shuffled order. - - The `key` is a function that accepts an item in `items` and returns - a string unique for that item that can be viewed as a string id. The - order of the return value is deterministic. It depends on the seed - and key function but not on the original order. - """ - hashes = {} - for item in items: - hashed = self._hash_item(item, key) - if hashed in hashes: - msg = "item {!r} has same hash {!r} as item {!r}".format( - item, - hashed, - hashes[hashed], - ) - raise RuntimeError(msg) - hashes[hashed] = item - return [hashes[hashed] for hashed in sorted(hashes)] - - class DiscoverRunner: """A Django test runner that uses unittest2 test discovery.""" @@ -584,29 +442,12 @@ class DiscoverRunner: test_loader = unittest.defaultTestLoader reorder_by = (TestCase, SimpleTestCase) - def __init__( - self, - pattern=None, - top_level=None, - verbosity=1, - interactive=True, - failfast=False, - keepdb=False, - reverse=False, - debug_mode=False, - debug_sql=False, - parallel=0, - tags=None, - exclude_tags=None, - test_name_patterns=None, - pdb=False, - buffer=False, - enable_faulthandler=True, - timing=False, - shuffle=False, - logger=None, - **kwargs, - ): + def __init__(self, pattern=None, top_level=None, verbosity=1, + interactive=True, failfast=False, keepdb=False, + reverse=False, debug_mode=False, debug_sql=False, parallel=0, + tags=None, exclude_tags=None, test_name_patterns=None, + pdb=False, buffer=False, enable_faulthandler=True, + timing=False, **kwargs): self.pattern = pattern self.top_level = top_level @@ -627,282 +468,181 @@ class DiscoverRunner: faulthandler.enable(file=sys.__stderr__.fileno()) self.pdb = pdb if self.pdb and self.parallel > 1: - raise ValueError( - "You cannot use --pdb with parallel tests; pass --parallel=1 to use it." - ) + raise ValueError('You cannot use --pdb with parallel tests; pass --parallel=1 to use it.') self.buffer = buffer + if self.buffer and self.parallel > 1: + raise ValueError( + 'You cannot use -b/--buffer with parallel tests; pass ' + '--parallel=1 to use it.' + ) self.test_name_patterns = None self.time_keeper = TimeKeeper() if timing else NullTimeKeeper() if test_name_patterns: # unittest does not export the _convert_select_pattern function # that converts command-line arguments to patterns. self.test_name_patterns = { - pattern if "*" in pattern else "*%s*" % pattern + pattern if '*' in pattern else '*%s*' % pattern for pattern in test_name_patterns } - self.shuffle = shuffle - self._shuffler = None - self.logger = logger @classmethod def add_arguments(cls, parser): parser.add_argument( - "-t", - "--top-level-directory", - dest="top_level", - help="Top level of project for unittest discovery.", + '-t', '--top-level-directory', dest='top_level', + help='Top level of project for unittest discovery.', ) parser.add_argument( - "-p", - "--pattern", - default="test*.py", - help="The test matching pattern. Defaults to test*.py.", + '-p', '--pattern', default="test*.py", + help='The test matching pattern. Defaults to test*.py.', ) parser.add_argument( - "--keepdb", action="store_true", help="Preserves the test DB between runs." + '--keepdb', action='store_true', + help='Preserves the test DB between runs.' ) parser.add_argument( - "--shuffle", - nargs="?", - default=False, - type=int, - metavar="SEED", - help="Shuffles test case order.", + '-r', '--reverse', action='store_true', + help='Reverses test cases order.', ) parser.add_argument( - "-r", - "--reverse", - action="store_true", - help="Reverses test case order.", + '--debug-mode', action='store_true', + help='Sets settings.DEBUG to True.', ) parser.add_argument( - "--debug-mode", - action="store_true", - help="Sets settings.DEBUG to True.", + '-d', '--debug-sql', action='store_true', + help='Prints logged SQL queries on failure.', ) parser.add_argument( - "-d", - "--debug-sql", - action="store_true", - help="Prints logged SQL queries on failure.", + '--parallel', nargs='?', default=1, type=int, + const=default_test_processes(), metavar='N', + help='Run tests using up to N parallel processes.', ) parser.add_argument( - "--parallel", - nargs="?", - const="auto", - default=0, - type=parallel_type, - metavar="N", + '--tag', action='append', dest='tags', + help='Run only tests with the specified tag. Can be used multiple times.', + ) + parser.add_argument( + '--exclude-tag', action='append', dest='exclude_tags', + help='Do not run tests with the specified tag. Can be used multiple times.', + ) + parser.add_argument( + '--pdb', action='store_true', + help='Runs a debugger (pdb, or ipdb if installed) on error or failure.' + ) + parser.add_argument( + '-b', '--buffer', action='store_true', + help='Discard output from passing tests.', + ) + parser.add_argument( + '--no-faulthandler', action='store_false', dest='enable_faulthandler', + help='Disables the Python faulthandler module during tests.', + ) + parser.add_argument( + '--timing', action='store_true', help=( - "Run tests using up to N parallel processes. Use the value " - '"auto" to run one test process for each processor core.' + 'Output timings, including database set up and total run time.' ), ) - parser.add_argument( - "--tag", - action="append", - dest="tags", - help="Run only tests with the specified tag. Can be used multiple times.", - ) - parser.add_argument( - "--exclude-tag", - action="append", - dest="exclude_tags", - help="Do not run tests with the specified tag. Can be used multiple times.", - ) - parser.add_argument( - "--pdb", - action="store_true", - help="Runs a debugger (pdb, or ipdb if installed) on error or failure.", - ) - parser.add_argument( - "-b", - "--buffer", - action="store_true", - help="Discard output from passing tests.", - ) - parser.add_argument( - "--no-faulthandler", - action="store_false", - dest="enable_faulthandler", - help="Disables the Python faulthandler module during tests.", - ) - parser.add_argument( - "--timing", - action="store_true", - help=("Output timings, including database set up and total run time."), - ) - parser.add_argument( - "-k", - action="append", - dest="test_name_patterns", - help=( - "Only run test methods and classes that match the pattern " - "or substring. Can be used multiple times. Same as " - "unittest -k option." - ), - ) - - @property - def shuffle_seed(self): - if self._shuffler is None: - return None - return self._shuffler.seed - - def log(self, msg, level=None): - """ - Log the message at the given logging level (the default is INFO). - - If a logger isn't set, the message is instead printed to the console, - respecting the configured verbosity. A verbosity of 0 prints no output, - a verbosity of 1 prints INFO and above, and a verbosity of 2 or higher - prints all levels. - """ - if level is None: - level = logging.INFO - if self.logger is None: - if self.verbosity <= 0 or (self.verbosity == 1 and level < logging.INFO): - return - print(msg) - else: - self.logger.log(level, msg) + if PY37: + parser.add_argument( + '-k', action='append', dest='test_name_patterns', + help=( + 'Only run test methods and classes that match the pattern ' + 'or substring. Can be used multiple times. Same as ' + 'unittest -k option.' + ), + ) def setup_test_environment(self, **kwargs): setup_test_environment(debug=self.debug_mode) unittest.installHandler() - def setup_shuffler(self): - if self.shuffle is False: - return - shuffler = Shuffler(seed=self.shuffle) - self.log(f"Using shuffle seed: {shuffler.seed_display}") - self._shuffler = shuffler - - @contextmanager - def load_with_patterns(self): - original_test_name_patterns = self.test_loader.testNamePatterns - self.test_loader.testNamePatterns = self.test_name_patterns - try: - yield - finally: - # Restore the original patterns. - self.test_loader.testNamePatterns = original_test_name_patterns - - def load_tests_for_label(self, label, discover_kwargs): - label_as_path = os.path.abspath(label) - tests = None - - # If a module, or "module.ClassName[.method_name]", just run those. - if not os.path.exists(label_as_path): - with self.load_with_patterns(): - tests = self.test_loader.loadTestsFromName(label) - if tests.countTestCases(): - return tests - # Try discovery if "label" is a package or directory. - is_importable, is_package = try_importing(label) - if is_importable: - if not is_package: - return tests - elif not os.path.isdir(label_as_path): - if os.path.exists(label_as_path): - assert tests is None - raise RuntimeError( - f"One of the test labels is a path to a file: {label!r}, " - f"which is not supported. Use a dotted module name or " - f"path to a directory instead." - ) - return tests - - kwargs = discover_kwargs.copy() - if os.path.isdir(label_as_path) and not self.top_level: - kwargs["top_level_dir"] = find_top_level(label_as_path) - - with self.load_with_patterns(): - tests = self.test_loader.discover(start_dir=label, **kwargs) - - # Make unittest forget the top-level dir it calculated from this run, - # to support running tests from two different top-levels. - self.test_loader._top_level_dir = None - return tests - def build_suite(self, test_labels=None, extra_tests=None, **kwargs): - if extra_tests is not None: - warnings.warn( - "The extra_tests argument is deprecated.", - RemovedInDjango50Warning, - stacklevel=2, - ) - test_labels = test_labels or ["."] + suite = self.test_suite() + test_labels = test_labels or ['.'] extra_tests = extra_tests or [] + self.test_loader.testNamePatterns = self.test_name_patterns discover_kwargs = {} if self.pattern is not None: - discover_kwargs["pattern"] = self.pattern + discover_kwargs['pattern'] = self.pattern if self.top_level is not None: - discover_kwargs["top_level_dir"] = self.top_level - self.setup_shuffler() + discover_kwargs['top_level_dir'] = self.top_level - all_tests = [] for label in test_labels: - tests = self.load_tests_for_label(label, discover_kwargs) - all_tests.extend(iter_test_cases(tests)) + kwargs = discover_kwargs.copy() + tests = None - all_tests.extend(iter_test_cases(extra_tests)) + label_as_path = os.path.abspath(label) + + # if a module, or "module.ClassName[.method_name]", just run those + if not os.path.exists(label_as_path): + tests = self.test_loader.loadTestsFromName(label) + elif os.path.isdir(label_as_path) and not self.top_level: + # Try to be a bit smarter than unittest about finding the + # default top-level for a given directory path, to avoid + # breaking relative imports. (Unittest's default is to set + # top-level equal to the path, which means relative imports + # will result in "Attempted relative import in non-package."). + + # We'd be happy to skip this and require dotted module paths + # (which don't cause this problem) instead of file paths (which + # do), but in the case of a directory in the cwd, which would + # be equally valid if considered as a top-level module or as a + # directory path, unittest unfortunately prefers the latter. + + top_level = label_as_path + while True: + init_py = os.path.join(top_level, '__init__.py') + if os.path.exists(init_py): + try_next = os.path.dirname(top_level) + if try_next == top_level: + # __init__.py all the way down? give up. + break + top_level = try_next + continue + break + kwargs['top_level_dir'] = top_level + + if not (tests and tests.countTestCases()) and is_discoverable(label): + # Try discovery if path is a package or directory + tests = self.test_loader.discover(start_dir=label, **kwargs) + + # Make unittest forget the top-level dir it calculated from this + # run, to support running tests from two different top-levels. + self.test_loader._top_level_dir = None + + suite.addTests(tests) + + for test in extra_tests: + suite.addTest(test) if self.tags or self.exclude_tags: - if self.tags: - self.log( - "Including test tag(s): %s." % ", ".join(sorted(self.tags)), - level=logging.DEBUG, - ) - if self.exclude_tags: - self.log( - "Excluding test tag(s): %s." % ", ".join(sorted(self.exclude_tags)), - level=logging.DEBUG, - ) - all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) - - # Put the failures detected at load time first for quicker feedback. - # _FailedTest objects include things like test modules that couldn't be - # found or that couldn't be loaded due to syntax errors. - test_types = (unittest.loader._FailedTest, *self.reorder_by) - all_tests = list( - reorder_tests( - all_tests, - test_types, - shuffler=self._shuffler, - reverse=self.reverse, - ) - ) - self.log("Found %d test(s)." % len(all_tests)) - suite = self.test_suite(all_tests) + if self.verbosity >= 2: + if self.tags: + print('Including test tag(s): %s.' % ', '.join(sorted(self.tags))) + if self.exclude_tags: + print('Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags))) + suite = filter_tests_by_tags(suite, self.tags, self.exclude_tags) + suite = reorder_suite(suite, self.reorder_by, self.reverse) if self.parallel > 1: - subsuites = partition_suite_by_case(suite) + parallel_suite = self.parallel_test_suite(suite, self.parallel, self.failfast) + # Since tests are distributed across processes on a per-TestCase # basis, there's no need for more processes than TestCases. - processes = min(self.parallel, len(subsuites)) - # Update also "parallel" because it's used to determine the number - # of test databases. - self.parallel = processes - if processes > 1: - suite = self.parallel_test_suite( - subsuites, - processes, - self.failfast, - self.buffer, - ) + parallel_units = len(parallel_suite.subsuites) + self.parallel = min(self.parallel, parallel_units) + + # If there's only one TestCase, parallelization isn't needed. + if self.parallel > 1: + suite = parallel_suite + return suite def setup_databases(self, **kwargs): return _setup_databases( - self.verbosity, - self.interactive, - time_keeper=self.time_keeper, - keepdb=self.keepdb, - debug_sql=self.debug_sql, - parallel=self.parallel, - **kwargs, + self.verbosity, self.interactive, time_keeper=self.time_keeper, keepdb=self.keepdb, + debug_sql=self.debug_sql, parallel=self.parallel, **kwargs ) def get_resultclass(self): @@ -913,26 +653,21 @@ class DiscoverRunner: def get_test_runner_kwargs(self): return { - "failfast": self.failfast, - "resultclass": self.get_resultclass(), - "verbosity": self.verbosity, - "buffer": self.buffer, + 'failfast': self.failfast, + 'resultclass': self.get_resultclass(), + 'verbosity': self.verbosity, + 'buffer': self.buffer, } def run_checks(self, databases): # Checks are run after database creation since some checks require # database access. - call_command("check", verbosity=self.verbosity, databases=databases) + call_command('check', verbosity=self.verbosity, databases=databases) def run_suite(self, suite, **kwargs): kwargs = self.get_test_runner_kwargs() runner = self.test_runner(**kwargs) - try: - return runner.run(suite) - finally: - if self._shuffler is not None: - seed_display = self._shuffler.seed_display - self.log(f"Used shuffle seed: {seed_display}") + return runner.run(suite) def teardown_databases(self, old_config, **kwargs): """Destroy all the non-mirror databases.""" @@ -951,28 +686,24 @@ class DiscoverRunner: return len(result.failures) + len(result.errors) def _get_databases(self, suite): - databases = {} - for test in iter_test_cases(suite): - test_databases = getattr(test, "databases", None) - if test_databases == "__all__": - test_databases = connections - if test_databases: - serialized_rollback = getattr(test, "serialized_rollback", False) - databases.update( - (alias, serialized_rollback or databases.get(alias, False)) - for alias in test_databases - ) + databases = set() + for test in suite: + if isinstance(test, unittest.TestCase): + test_databases = getattr(test, 'databases', None) + if test_databases == '__all__': + return set(connections) + if test_databases: + databases.update(test_databases) + else: + databases.update(self._get_databases(test)) return databases def get_databases(self, suite): databases = self._get_databases(suite) - unused_databases = [alias for alias in connections if alias not in databases] - if unused_databases: - self.log( - "Skipping setup of unused database(s): %s." - % ", ".join(sorted(unused_databases)), - level=logging.DEBUG, - ) + if self.verbosity >= 2: + unused_databases = [alias for alias in connections if alias not in databases] + if unused_databases: + print('Skipping setup of unused database(s): %s.' % ', '.join(sorted(unused_databases))) return databases def run_tests(self, test_labels, extra_tests=None, **kwargs): @@ -982,25 +713,16 @@ class DiscoverRunner: Test labels should be dotted Python paths to test modules, test classes, or test methods. + A list of 'extra' tests may also be provided; these tests + will be added to the test suite. + Return the number of tests that failed. """ - if extra_tests is not None: - warnings.warn( - "The extra_tests argument is deprecated.", - RemovedInDjango50Warning, - stacklevel=2, - ) self.setup_test_environment() suite = self.build_suite(test_labels, extra_tests) databases = self.get_databases(suite) - serialized_aliases = set( - alias for alias, serialize in databases.items() if serialize - ) - with self.time_keeper.timed("Total database setup"): - old_config = self.setup_databases( - aliases=databases, - serialized_aliases=serialized_aliases, - ) + with self.time_keeper.timed('Total database setup'): + old_config = self.setup_databases(aliases=databases) run_failed = False try: self.run_checks(databases) @@ -1010,7 +732,7 @@ class DiscoverRunner: raise finally: try: - with self.time_keeper.timed("Total database teardown"): + with self.time_keeper.timed('Total database teardown'): self.teardown_databases(old_config) self.teardown_test_environment() except Exception: @@ -1022,149 +744,98 @@ class DiscoverRunner: return self.suite_result(suite, result) -def try_importing(label): +def is_discoverable(label): """ - Try importing a test label, and return (is_importable, is_package). + Check if a test label points to a Python package or file directory. Relative labels like "." and ".." are seen as directories. """ try: mod = import_module(label) except (ImportError, TypeError): - return (False, False) + pass + else: + return hasattr(mod, '__path__') - return (True, hasattr(mod, "__path__")) + return os.path.isdir(os.path.abspath(label)) -def find_top_level(top_level): - # Try to be a bit smarter than unittest about finding the default top-level - # for a given directory path, to avoid breaking relative imports. - # (Unittest's default is to set top-level equal to the path, which means - # relative imports will result in "Attempted relative import in - # non-package."). - - # We'd be happy to skip this and require dotted module paths (which don't - # cause this problem) instead of file paths (which do), but in the case of - # a directory in the cwd, which would be equally valid if considered as a - # top-level module or as a directory path, unittest unfortunately prefers - # the latter. - while True: - init_py = os.path.join(top_level, "__init__.py") - if not os.path.exists(init_py): - break - try_next = os.path.dirname(top_level) - if try_next == top_level: - # __init__.py all the way down? give up. - break - top_level = try_next - return top_level - - -def _class_shuffle_key(cls): - return f"{cls.__module__}.{cls.__qualname__}" - - -def shuffle_tests(tests, shuffler): +def reorder_suite(suite, classes, reverse=False): """ - Return an iterator over the given tests in a shuffled order, keeping tests - next to other tests of their class. + Reorder a test suite by test type. - `tests` should be an iterable of tests. + `classes` is a sequence of types + + All tests of type classes[0] are placed first, then tests of type + classes[1], etc. Tests with no match in classes are placed last. + + If `reverse` is True, sort tests within classes in opposite order but + don't reverse test classes. """ - tests_by_type = {} - for _, class_tests in itertools.groupby(tests, type): - class_tests = list(class_tests) - test_type = type(class_tests[0]) - class_tests = shuffler.shuffle(class_tests, key=lambda test: test.id()) - tests_by_type[test_type] = class_tests - - classes = shuffler.shuffle(tests_by_type, key=_class_shuffle_key) - - return itertools.chain(*(tests_by_type[cls] for cls in classes)) + class_count = len(classes) + suite_class = type(suite) + bins = [OrderedSet() for i in range(class_count + 1)] + partition_suite_by_type(suite, classes, bins, reverse=reverse) + reordered_suite = suite_class() + for i in range(class_count + 1): + reordered_suite.addTests(bins[i]) + return reordered_suite -def reorder_test_bin(tests, shuffler=None, reverse=False): +def partition_suite_by_type(suite, classes, bins, reverse=False): """ - Return an iterator that reorders the given tests, keeping tests next to - other tests of their class. + Partition a test suite by test type. Also prevent duplicated tests. - `tests` should be an iterable of tests that supports reversed(). + classes is a sequence of types + bins is a sequence of TestSuites, one more than classes + reverse changes the ordering of tests within bins + + Tests of type classes[i] are added to bins[i], + tests with no match found in classes are place in bins[-1] """ - if shuffler is None: - if reverse: - return reversed(tests) - # The function must return an iterator. - return iter(tests) - - tests = shuffle_tests(tests, shuffler) - if not reverse: - return tests - # Arguments to reversed() must be reversible. - return reversed(list(tests)) - - -def reorder_tests(tests, classes, reverse=False, shuffler=None): - """ - Reorder an iterable of tests, grouping by the given TestCase classes. - - This function also removes any duplicates and reorders so that tests of the - same type are consecutive. - - The result is returned as an iterator. `classes` is a sequence of types. - Tests that are instances of `classes[0]` are grouped first, followed by - instances of `classes[1]`, etc. Tests that are not instances of any of the - classes are grouped last. - - If `reverse` is True, the tests within each `classes` group are reversed, - but without reversing the order of `classes` itself. - - The `shuffler` argument is an optional instance of this module's `Shuffler` - class. If provided, tests will be shuffled within each `classes` group, but - keeping tests with other tests of their TestCase class. Reversing is - applied after shuffling to allow reversing the same random order. - """ - # Each bin maps TestCase class to OrderedSet of tests. This permits tests - # to be grouped by TestCase class even if provided non-consecutively. - bins = [defaultdict(OrderedSet) for i in range(len(classes) + 1)] - *class_bins, last_bin = bins - - for test in tests: - for test_bin, test_class in zip(class_bins, classes): - if isinstance(test, test_class): - break + suite_class = type(suite) + if reverse: + suite = reversed(tuple(suite)) + for test in suite: + if isinstance(test, suite_class): + partition_suite_by_type(test, classes, bins, reverse=reverse) else: - test_bin = last_bin - test_bin[type(test)].add(test) - - for test_bin in bins: - # Call list() since reorder_test_bin()'s input must support reversed(). - tests = list(itertools.chain.from_iterable(test_bin.values())) - yield from reorder_test_bin(tests, shuffler=shuffler, reverse=reverse) + for i in range(len(classes)): + if isinstance(test, classes[i]): + bins[i].add(test) + break + else: + bins[-1].add(test) def partition_suite_by_case(suite): """Partition a test suite by test case, preserving the order of tests.""" + groups = [] suite_class = type(suite) - all_tests = iter_test_cases(suite) - return [suite_class(tests) for _, tests in itertools.groupby(all_tests, type)] + for test_type, test_group in itertools.groupby(suite, type): + if issubclass(test_type, unittest.TestCase): + groups.append(suite_class(test_group)) + else: + for item in test_group: + groups.extend(partition_suite_by_case(item)) + return groups -def test_match_tags(test, tags, exclude_tags): - if isinstance(test, unittest.loader._FailedTest): - # Tests that couldn't load always match to prevent tests from falsely - # passing due e.g. to syntax errors. - return True - test_tags = set(getattr(test, "tags", [])) - test_fn_name = getattr(test, "_testMethodName", str(test)) - if hasattr(test, test_fn_name): - test_fn = getattr(test, test_fn_name) - test_fn_tags = list(getattr(test_fn, "tags", [])) - test_tags = test_tags.union(test_fn_tags) - if tags and test_tags.isdisjoint(tags): - return False - return test_tags.isdisjoint(exclude_tags) +def filter_tests_by_tags(suite, tags, exclude_tags): + suite_class = type(suite) + filtered_suite = suite_class() + for test in suite: + if isinstance(test, suite_class): + filtered_suite.addTests(filter_tests_by_tags(test, tags, exclude_tags)) + else: + test_tags = set(getattr(test, 'tags', set())) + test_fn_name = getattr(test, '_testMethodName', str(test)) + test_fn = getattr(test, test_fn_name, test) + test_fn_tags = set(getattr(test_fn, 'tags', set())) + all_tags = test_tags.union(test_fn_tags) + matched_tags = all_tags.intersection(tags) + if (matched_tags or not tags) and not all_tags.intersection(exclude_tags): + filtered_suite.addTest(test) -def filter_tests_by_tags(tests, tags, exclude_tags): - """Return the matching tests as an iterator.""" - return (test for test in tests if test_match_tags(test, tags, exclude_tags)) + return filtered_suite diff --git a/venv/Lib/site-packages/django/test/selenium.py b/venv/Lib/site-packages/django/test/selenium.py index aa714ad..97a7840 100644 --- a/venv/Lib/site-packages/django/test/selenium.py +++ b/venv/Lib/site-packages/django/test/selenium.py @@ -27,9 +27,7 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): """ test_class = super().__new__(cls, name, bases, attrs) # If the test class is either browser-specific or a test base, return it. - if test_class.browser or not any( - name.startswith("test") and callable(value) for name, value in attrs.items() - ): + if test_class.browser or not any(name.startswith('test') and callable(value) for name, value in attrs.items()): return test_class elif test_class.browsers: # Reuse the created test class to make it browser-specific. @@ -39,7 +37,7 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): first_browser = test_class.browsers[0] test_class.browser = first_browser # Listen on an external interface if using a selenium hub. - host = test_class.host if not test_class.selenium_hub else "0.0.0.0" + host = test_class.host if not test_class.selenium_hub else '0.0.0.0' test_class.host = host test_class.external_host = cls.external_host # Create subclasses for each of the remaining browsers and expose @@ -51,16 +49,16 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): "%s%s" % (capfirst(browser), name), (test_class,), { - "browser": browser, - "host": host, - "external_host": cls.external_host, - "__module__": test_class.__module__, - }, + 'browser': browser, + 'host': host, + 'external_host': cls.external_host, + '__module__': test_class.__module__, + } ) setattr(module, browser_test_class.__name__, browser_test_class) return test_class # If no browsers were specified, skip this class (it'll still be discovered). - return unittest.skip("No browsers specified.")(test_class) + return unittest.skip('No browsers specified.')(test_class) @classmethod def import_webdriver(cls, browser): @@ -68,12 +66,13 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): @classmethod def import_options(cls, browser): - return import_string("selenium.webdriver.%s.options.Options" % browser) + return import_string('selenium.webdriver.%s.options.Options' % browser) @classmethod def get_capability(cls, browser): - from selenium.webdriver.common.desired_capabilities import DesiredCapabilities - + from selenium.webdriver.common.desired_capabilities import ( + DesiredCapabilities, + ) return getattr(DesiredCapabilities, browser.upper()) def create_options(self): @@ -88,7 +87,6 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): def create_webdriver(self): if self.selenium_hub: from selenium import webdriver - return webdriver.Remote( command_executor=self.selenium_hub, desired_capabilities=self.get_capability(self.browser), @@ -96,14 +94,14 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)): return self.import_webdriver(self.browser)(options=self.create_options()) -@tag("selenium") +@tag('selenium') class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase): implicit_wait = 10 external_host = None @classproperty def live_server_url(cls): - return "http://%s:%s" % (cls.external_host or cls.host, cls.server_thread.port) + return 'http://%s:%s' % (cls.external_host or cls.host, cls.server_thread.port) @classproperty def allowed_host(cls): @@ -120,7 +118,7 @@ class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase): # quit() the WebDriver before attempting to terminate and join the # single-threaded LiveServerThread to avoid a dead lock if the browser # kept a connection alive. - if hasattr(cls, "selenium"): + if hasattr(cls, 'selenium'): cls.selenium.quit() super()._tearDownClassInternal() diff --git a/venv/Lib/site-packages/django/test/signals.py b/venv/Lib/site-packages/django/test/signals.py index b190a5b..4d8c43e 100644 --- a/venv/Lib/site-packages/django/test/signals.py +++ b/venv/Lib/site-packages/django/test/signals.py @@ -20,14 +20,13 @@ template_rendered = Signal() # except for cases where the receiver is related to a contrib app. # Settings that may not work well when using 'override_settings' (#19031) -COMPLEX_OVERRIDE_SETTINGS = {"DATABASES"} +COMPLEX_OVERRIDE_SETTINGS = {'DATABASES'} @receiver(setting_changed) def clear_cache_handlers(**kwargs): - if kwargs["setting"] == "CACHES": + if kwargs['setting'] == 'CACHES': from django.core.cache import caches, close_caches - close_caches() caches._settings = caches.settings = caches.configure_settings(None) caches._connections = Local() @@ -35,41 +34,37 @@ def clear_cache_handlers(**kwargs): @receiver(setting_changed) def update_installed_apps(**kwargs): - if kwargs["setting"] == "INSTALLED_APPS": + if kwargs['setting'] == 'INSTALLED_APPS': # Rebuild any AppDirectoriesFinder instance. from django.contrib.staticfiles.finders import get_finder - get_finder.cache_clear() # Rebuild management commands cache from django.core.management import get_commands - get_commands.cache_clear() # Rebuild get_app_template_dirs cache. from django.template.utils import get_app_template_dirs - get_app_template_dirs.cache_clear() # Rebuild translations cache. from django.utils.translation import trans_real - trans_real._translations = {} @receiver(setting_changed) def update_connections_time_zone(**kwargs): - if kwargs["setting"] == "TIME_ZONE": + if kwargs['setting'] == 'TIME_ZONE': # Reset process time zone - if hasattr(time, "tzset"): - if kwargs["value"]: - os.environ["TZ"] = kwargs["value"] + if hasattr(time, 'tzset'): + if kwargs['value']: + os.environ['TZ'] = kwargs['value'] else: - os.environ.pop("TZ", None) + os.environ.pop('TZ', None) time.tzset() # Reset local time zone cache timezone.get_default_timezone.cache_clear() # Reset the database connections' time zone - if kwargs["setting"] in {"TIME_ZONE", "USE_TZ"}: + if kwargs['setting'] in {'TIME_ZONE', 'USE_TZ'}: for conn in connections.all(): try: del conn.timezone @@ -84,19 +79,18 @@ def update_connections_time_zone(**kwargs): @receiver(setting_changed) def clear_routers_cache(**kwargs): - if kwargs["setting"] == "DATABASE_ROUTERS": + if kwargs['setting'] == 'DATABASE_ROUTERS': router.routers = ConnectionRouter().routers @receiver(setting_changed) def reset_template_engines(**kwargs): - if kwargs["setting"] in { - "TEMPLATES", - "DEBUG", - "INSTALLED_APPS", + if kwargs['setting'] in { + 'TEMPLATES', + 'DEBUG', + 'INSTALLED_APPS', }: from django.template import engines - try: del engines.templates except AttributeError: @@ -104,134 +98,112 @@ def reset_template_engines(**kwargs): engines._templates = None engines._engines = {} from django.template.engine import Engine - Engine.get_default.cache_clear() from django.forms.renderers import get_default_renderer - get_default_renderer.cache_clear() @receiver(setting_changed) def clear_serializers_cache(**kwargs): - if kwargs["setting"] == "SERIALIZATION_MODULES": + if kwargs['setting'] == 'SERIALIZATION_MODULES': from django.core import serializers - serializers._serializers = {} @receiver(setting_changed) def language_changed(**kwargs): - if kwargs["setting"] in {"LANGUAGES", "LANGUAGE_CODE", "LOCALE_PATHS"}: + if kwargs['setting'] in {'LANGUAGES', 'LANGUAGE_CODE', 'LOCALE_PATHS'}: from django.utils.translation import trans_real - trans_real._default = None trans_real._active = Local() - if kwargs["setting"] in {"LANGUAGES", "LOCALE_PATHS"}: + if kwargs['setting'] in {'LANGUAGES', 'LOCALE_PATHS'}: from django.utils.translation import trans_real - trans_real._translations = {} trans_real.check_for_language.cache_clear() @receiver(setting_changed) def localize_settings_changed(**kwargs): - if ( - kwargs["setting"] in FORMAT_SETTINGS - or kwargs["setting"] == "USE_THOUSAND_SEPARATOR" - ): + if kwargs['setting'] in FORMAT_SETTINGS or kwargs['setting'] == 'USE_THOUSAND_SEPARATOR': reset_format_cache() @receiver(setting_changed) def file_storage_changed(**kwargs): - if kwargs["setting"] == "DEFAULT_FILE_STORAGE": + if kwargs['setting'] == 'DEFAULT_FILE_STORAGE': from django.core.files.storage import default_storage - default_storage._wrapped = empty @receiver(setting_changed) def complex_setting_changed(**kwargs): - if kwargs["enter"] and kwargs["setting"] in COMPLEX_OVERRIDE_SETTINGS: + if kwargs['enter'] and kwargs['setting'] in COMPLEX_OVERRIDE_SETTINGS: # Considering the current implementation of the signals framework, # this stacklevel shows the line containing the override_settings call. - warnings.warn( - "Overriding setting %s can lead to unexpected behavior." - % kwargs["setting"], - stacklevel=6, - ) + warnings.warn("Overriding setting %s can lead to unexpected behavior." + % kwargs['setting'], stacklevel=6) @receiver(setting_changed) def root_urlconf_changed(**kwargs): - if kwargs["setting"] == "ROOT_URLCONF": + if kwargs['setting'] == 'ROOT_URLCONF': from django.urls import clear_url_caches, set_urlconf - clear_url_caches() set_urlconf(None) @receiver(setting_changed) def static_storage_changed(**kwargs): - if kwargs["setting"] in { - "STATICFILES_STORAGE", - "STATIC_ROOT", - "STATIC_URL", + if kwargs['setting'] in { + 'STATICFILES_STORAGE', + 'STATIC_ROOT', + 'STATIC_URL', }: from django.contrib.staticfiles.storage import staticfiles_storage - staticfiles_storage._wrapped = empty @receiver(setting_changed) def static_finders_changed(**kwargs): - if kwargs["setting"] in { - "STATICFILES_DIRS", - "STATIC_ROOT", + if kwargs['setting'] in { + 'STATICFILES_DIRS', + 'STATIC_ROOT', }: from django.contrib.staticfiles.finders import get_finder - get_finder.cache_clear() @receiver(setting_changed) def auth_password_validators_changed(**kwargs): - if kwargs["setting"] == "AUTH_PASSWORD_VALIDATORS": + if kwargs['setting'] == 'AUTH_PASSWORD_VALIDATORS': from django.contrib.auth.password_validation import ( get_default_password_validators, ) - get_default_password_validators.cache_clear() @receiver(setting_changed) def user_model_swapped(**kwargs): - if kwargs["setting"] == "AUTH_USER_MODEL": + if kwargs['setting'] == 'AUTH_USER_MODEL': apps.clear_cache() try: from django.contrib.auth import get_user_model - UserModel = get_user_model() except ImproperlyConfigured: # Some tests set an invalid AUTH_USER_MODEL. pass else: from django.contrib.auth import backends - backends.UserModel = UserModel from django.contrib.auth import forms - forms.UserModel = UserModel from django.contrib.auth.handlers import modwsgi - modwsgi.UserModel = UserModel from django.contrib.auth.management.commands import changepassword - changepassword.UserModel = UserModel from django.contrib.auth import views - views.UserModel = UserModel diff --git a/venv/Lib/site-packages/django/test/testcases.py b/venv/Lib/site-packages/django/test/testcases.py index 2bf7b98..27196cd 100644 --- a/venv/Lib/site-packages/django/test/testcases.py +++ b/venv/Lib/site-packages/django/test/testcases.py @@ -1,7 +1,6 @@ import asyncio import difflib import json -import logging import posixpath import sys import threading @@ -15,13 +14,7 @@ from functools import wraps from unittest.suite import _DebugResult from unittest.util import safe_repr from urllib.parse import ( - parse_qsl, - unquote, - urlencode, - urljoin, - urlparse, - urlsplit, - urlunparse, + parse_qsl, unquote, urlencode, urljoin, urlparse, urlsplit, urlunparse, ) from urllib.request import url2pathname @@ -45,24 +38,15 @@ from django.test.client import AsyncClient, Client from django.test.html import HTMLParseError, parse_html from django.test.signals import setting_changed, template_rendered from django.test.utils import ( - CaptureQueriesContext, - ContextList, - compare_xml, - modify_settings, + CaptureQueriesContext, ContextList, compare_xml, modify_settings, override_settings, ) from django.utils.deprecation import RemovedInDjango41Warning from django.utils.functional import classproperty -from django.utils.version import PY310 from django.views.static import serve -__all__ = ( - "TestCase", - "TransactionTestCase", - "SimpleTestCase", - "skipIfDBFeature", - "skipUnlessDBFeature", -) +__all__ = ('TestCase', 'TransactionTestCase', + 'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature') def to_list(value): @@ -81,7 +65,7 @@ def assert_and_parse_html(self, html, user_msg, msg): try: dom = parse_html(html) except HTMLParseError as e: - standardMsg = "%s\n%s" % (msg, e) + standardMsg = '%s\n%s' % (msg, e) self.fail(self._formatMessage(user_msg, standardMsg)) return dom @@ -98,17 +82,13 @@ class _AssertNumQueriesContext(CaptureQueriesContext): return executed = len(self) self.test_case.assertEqual( - executed, - self.num, - "%d queries executed, %d expected\nCaptured queries were:\n%s" - % ( - executed, - self.num, - "\n".join( - "%d. %s" % (i, query["sql"]) - for i, query in enumerate(self.captured_queries, start=1) - ), - ), + executed, self.num, + "%d queries executed, %d expected\nCaptured queries were:\n%s" % ( + executed, self.num, + '\n'.join( + '%d. %s' % (i, query['sql']) for i, query in enumerate(self.captured_queries, start=1) + ) + ) ) @@ -129,7 +109,7 @@ class _AssertTemplateUsedContext: return self.template_name in self.rendered_template_names def message(self): - return "%s was not rendered." % self.template_name + return '%s was not rendered.' % self.template_name def __enter__(self): template_rendered.connect(self.on_template_render) @@ -143,11 +123,11 @@ class _AssertTemplateUsedContext: if not self.test(): message = self.message() if self.rendered_templates: - message += " Following templates were rendered: %s" % ( - ", ".join(self.rendered_template_names) + message += ' Following templates were rendered: %s' % ( + ', '.join(self.rendered_template_names) ) else: - message += " No template was rendered." + message += ' No template was rendered.' self.test_case.fail(message) @@ -156,7 +136,7 @@ class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext): return self.template_name not in self.rendered_template_names def message(self): - return "%s was rendered." % self.template_name + return '%s was rendered.' % self.template_name class _DatabaseFailure: @@ -179,16 +159,16 @@ class SimpleTestCase(unittest.TestCase): databases = set() _disallowed_database_msg = ( - "Database %(operation)s to %(alias)r are not allowed in SimpleTestCase " - "subclasses. Either subclass TestCase or TransactionTestCase to ensure " - "proper test isolation or add %(alias)r to %(test)s.databases to silence " - "this failure." + 'Database %(operation)s to %(alias)r are not allowed in SimpleTestCase ' + 'subclasses. Either subclass TestCase or TransactionTestCase to ensure ' + 'proper test isolation or add %(alias)r to %(test)s.databases to silence ' + 'this failure.' ) _disallowed_connection_methods = [ - ("connect", "connections"), - ("temporary_connection", "connections"), - ("cursor", "queries"), - ("chunked_cursor", "queries"), + ('connect', 'connections'), + ('temporary_connection', 'connections'), + ('cursor', 'queries'), + ('chunked_cursor', 'queries'), ] @classmethod @@ -197,32 +177,25 @@ class SimpleTestCase(unittest.TestCase): if cls._overridden_settings: cls._cls_overridden_context = override_settings(**cls._overridden_settings) cls._cls_overridden_context.enable() - cls.addClassCleanup(cls._cls_overridden_context.disable) if cls._modified_settings: cls._cls_modified_context = modify_settings(cls._modified_settings) cls._cls_modified_context.enable() - cls.addClassCleanup(cls._cls_modified_context.disable) cls._add_databases_failures() - cls.addClassCleanup(cls._remove_databases_failures) @classmethod def _validate_databases(cls): - if cls.databases == "__all__": + if cls.databases == '__all__': return frozenset(connections) for alias in cls.databases: if alias not in connections: - message = ( - "%s.%s.databases refers to %r which is not defined in " - "settings.DATABASES." - % ( - cls.__module__, - cls.__qualname__, - alias, - ) + message = '%s.%s.databases refers to %r which is not defined in settings.DATABASES.' % ( + cls.__module__, + cls.__qualname__, + alias, ) close_matches = get_close_matches(alias, list(connections)) if close_matches: - message += " Did you mean %r?" % close_matches[0] + message += ' Did you mean %r?' % close_matches[0] raise ImproperlyConfigured(message) return frozenset(cls.databases) @@ -235,9 +208,9 @@ class SimpleTestCase(unittest.TestCase): connection = connections[alias] for name, operation in cls._disallowed_connection_methods: message = cls._disallowed_database_msg % { - "test": "%s.%s" % (cls.__module__, cls.__qualname__), - "alias": alias, - "operation": operation, + 'test': '%s.%s' % (cls.__module__, cls.__qualname__), + 'alias': alias, + 'operation': operation, } method = getattr(connection, name) setattr(connection, name, _DatabaseFailure(method, message)) @@ -252,6 +225,17 @@ class SimpleTestCase(unittest.TestCase): method = getattr(connection, name) setattr(connection, name, method.wrapped) + @classmethod + def tearDownClass(cls): + cls._remove_databases_failures() + if hasattr(cls, '_cls_modified_context'): + cls._cls_modified_context.disable() + delattr(cls, '_cls_modified_context') + if hasattr(cls, '_cls_overridden_context'): + cls._cls_overridden_context.disable() + delattr(cls, '_cls_overridden_context') + super().tearDownClass() + def __call__(self, result=None): """ Wrapper around default __call__ method to perform common Django test @@ -274,8 +258,9 @@ class SimpleTestCase(unittest.TestCase): instead of __call__() to run the test. """ testMethod = getattr(self, self._testMethodName) - skipped = getattr(self.__class__, "__unittest_skip__", False) or getattr( - testMethod, "__unittest_skip__", False + skipped = ( + getattr(self.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False) ) # Convert async test methods. @@ -331,15 +316,9 @@ class SimpleTestCase(unittest.TestCase): """ return modify_settings(**kwargs) - def assertRedirects( - self, - response, - expected_url, - status_code=302, - target_status_code=200, - msg_prefix="", - fetch_redirect_response=True, - ): + def assertRedirects(self, response, expected_url, status_code=302, + target_status_code=200, msg_prefix='', + fetch_redirect_response=True): """ Assert that a response redirected to a specific URL and that the redirect URL can be loaded. @@ -351,62 +330,43 @@ class SimpleTestCase(unittest.TestCase): if msg_prefix: msg_prefix += ": " - if hasattr(response, "redirect_chain"): + if hasattr(response, 'redirect_chain'): # The request was a followed redirect self.assertTrue( response.redirect_chain, - msg_prefix - + ( - "Response didn't redirect as expected: Response code was %d " - "(expected %d)" - ) - % (response.status_code, status_code), + msg_prefix + "Response didn't redirect as expected: Response code was %d (expected %d)" + % (response.status_code, status_code) ) self.assertEqual( - response.redirect_chain[0][1], - status_code, - msg_prefix - + ( - "Initial response didn't redirect as expected: Response code was " - "%d (expected %d)" - ) - % (response.redirect_chain[0][1], status_code), + response.redirect_chain[0][1], status_code, + msg_prefix + "Initial response didn't redirect as expected: Response code was %d (expected %d)" + % (response.redirect_chain[0][1], status_code) ) url, status_code = response.redirect_chain[-1] self.assertEqual( - response.status_code, - target_status_code, - msg_prefix - + ( - "Response didn't redirect as expected: Final Response code was %d " - "(expected %d)" - ) - % (response.status_code, target_status_code), + response.status_code, target_status_code, + msg_prefix + "Response didn't redirect as expected: Final Response code was %d (expected %d)" + % (response.status_code, target_status_code) ) else: # Not a followed redirect self.assertEqual( - response.status_code, - status_code, - msg_prefix - + ( - "Response didn't redirect as expected: Response code was %d " - "(expected %d)" - ) - % (response.status_code, status_code), + response.status_code, status_code, + msg_prefix + "Response didn't redirect as expected: Response code was %d (expected %d)" + % (response.status_code, status_code) ) url = response.url scheme, netloc, path, query, fragment = urlsplit(url) # Prepend the request path to handle relative path redirects. - if not path.startswith("/"): - url = urljoin(response.request["PATH_INFO"], url) - path = urljoin(response.request["PATH_INFO"], path) + if not path.startswith('/'): + url = urljoin(response.request['PATH_INFO'], url) + path = urljoin(response.request['PATH_INFO'], path) if fetch_redirect_response: # netloc might be empty, or in cases where Django tests the @@ -417,8 +377,7 @@ class SimpleTestCase(unittest.TestCase): raise ValueError( "The test client is unable to fetch remote URLs (got %s). " "If the host is served by Django, add '%s' to ALLOWED_HOSTS. " - "Otherwise, use " - "assertRedirects(..., fetch_redirect_response=False)." + "Otherwise, use assertRedirects(..., fetch_redirect_response=False)." % (url, domain) ) # Get the redirection page, using the same client that was used @@ -427,28 +386,21 @@ class SimpleTestCase(unittest.TestCase): redirect_response = response.client.get( path, QueryDict(query), - secure=(scheme == "https"), + secure=(scheme == 'https'), **extra, ) self.assertEqual( - redirect_response.status_code, - target_status_code, - msg_prefix - + ( - "Couldn't retrieve redirection page '%s': response code was %d " - "(expected %d)" - ) - % (path, redirect_response.status_code, target_status_code), + redirect_response.status_code, target_status_code, + msg_prefix + "Couldn't retrieve redirection page '%s': response code was %d (expected %d)" + % (path, redirect_response.status_code, target_status_code) ) self.assertURLEqual( - url, - expected_url, - msg_prefix - + "Response redirected to '%s', expected '%s'" % (url, expected_url), + url, expected_url, + msg_prefix + "Response redirected to '%s', expected '%s'" % (url, expected_url) ) - def assertURLEqual(self, url1, url2, msg_prefix=""): + def assertURLEqual(self, url1, url2, msg_prefix=''): """ Assert that two URLs are the same, ignoring the order of query string parameters except for parameters with the same name. @@ -456,44 +408,35 @@ class SimpleTestCase(unittest.TestCase): For example, /path/?x=1&y=2 is equal to /path/?y=2&x=1, but /path/?a=1&a=2 isn't equal to /path/?a=2&a=1. """ - def normalize(url): """Sort the URL's query string parameters.""" url = str(url) # Coerce reverse_lazy() URLs. scheme, netloc, path, params, query, fragment = urlparse(url) query_parts = sorted(parse_qsl(query)) - return urlunparse( - (scheme, netloc, path, params, urlencode(query_parts), fragment) - ) + return urlunparse((scheme, netloc, path, params, urlencode(query_parts), fragment)) self.assertEqual( - normalize(url1), - normalize(url2), - msg_prefix + "Expected '%s' to equal '%s'." % (url1, url2), + normalize(url1), normalize(url2), + msg_prefix + "Expected '%s' to equal '%s'." % (url1, url2) ) def _assert_contains(self, response, text, status_code, msg_prefix, html): # If the response supports deferred rendering and hasn't been rendered # yet, then ensure that it does get rendered before proceeding further. - if ( - hasattr(response, "render") - and callable(response.render) - and not response.is_rendered - ): + if hasattr(response, 'render') and callable(response.render) and not response.is_rendered: response.render() if msg_prefix: msg_prefix += ": " self.assertEqual( - response.status_code, - status_code, + response.status_code, status_code, msg_prefix + "Couldn't retrieve content: Response code was %d" - " (expected %d)" % (response.status_code, status_code), + " (expected %d)" % (response.status_code, status_code) ) if response.streaming: - content = b"".join(response.streaming_content) + content = b''.join(response.streaming_content) else: content = response.content if not isinstance(text, bytes) or html: @@ -503,18 +446,12 @@ class SimpleTestCase(unittest.TestCase): else: text_repr = repr(text) if html: - content = assert_and_parse_html( - self, content, None, "Response's content is not valid HTML:" - ) - text = assert_and_parse_html( - self, text, None, "Second argument is not valid HTML:" - ) + content = assert_and_parse_html(self, content, None, "Response's content is not valid HTML:") + text = assert_and_parse_html(self, text, None, "Second argument is not valid HTML:") real_count = content.count(text) return (text_repr, real_count, msg_prefix) - def assertContains( - self, response, text, count=None, status_code=200, msg_prefix="", html=False - ): + def assertContains(self, response, text, count=None, status_code=200, msg_prefix='', html=False): """ Assert that a response indicates that some content was retrieved successfully, (i.e., the HTTP status code was as expected) and that @@ -523,39 +460,28 @@ class SimpleTestCase(unittest.TestCase): if the text occurs at least once in the response. """ text_repr, real_count, msg_prefix = self._assert_contains( - response, text, status_code, msg_prefix, html - ) + response, text, status_code, msg_prefix, html) if count is not None: self.assertEqual( - real_count, - count, - msg_prefix - + "Found %d instances of %s in response (expected %d)" - % (real_count, text_repr, count), + real_count, count, + msg_prefix + "Found %d instances of %s in response (expected %d)" % (real_count, text_repr, count) ) else: - self.assertTrue( - real_count != 0, msg_prefix + "Couldn't find %s in response" % text_repr - ) + self.assertTrue(real_count != 0, msg_prefix + "Couldn't find %s in response" % text_repr) - def assertNotContains( - self, response, text, status_code=200, msg_prefix="", html=False - ): + def assertNotContains(self, response, text, status_code=200, msg_prefix='', html=False): """ Assert that a response indicates that some content was retrieved successfully, (i.e., the HTTP status code was as expected) and that - ``text`` doesn't occur in the content of the response. + ``text`` doesn't occurs in the content of the response. """ text_repr, real_count, msg_prefix = self._assert_contains( - response, text, status_code, msg_prefix, html - ) + response, text, status_code, msg_prefix, html) - self.assertEqual( - real_count, 0, msg_prefix + "Response should not contain %s" % text_repr - ) + self.assertEqual(real_count, 0, msg_prefix + "Response should not contain %s" % text_repr) - def assertFormError(self, response, form, field, errors, msg_prefix=""): + def assertFormError(self, response, form, field, errors, msg_prefix=''): """ Assert that a form used to render the response has a specific field error. @@ -566,9 +492,7 @@ class SimpleTestCase(unittest.TestCase): # Put context(s) into a list to simplify processing. contexts = to_list(response.context) if not contexts: - self.fail( - msg_prefix + "Response did not use any contexts to render the response" - ) + self.fail(msg_prefix + "Response did not use any contexts to render the response") # Put error(s) into a list to simplify processing. errors = to_list(errors) @@ -587,26 +511,18 @@ class SimpleTestCase(unittest.TestCase): err in field_errors, msg_prefix + "The field '%s' on form '%s' in" " context %d does not contain the error '%s'" - " (actual errors: %s)" - % (field, form, i, err, repr(field_errors)), + " (actual errors: %s)" % + (field, form, i, err, repr(field_errors)) ) elif field in context[form].fields: self.fail( - msg_prefix - + ( - "The field '%s' on form '%s' in context %d contains no " - "errors" - ) - % (field, form, i) + msg_prefix + "The field '%s' on form '%s' in context %d contains no errors" % + (field, form, i) ) else: self.fail( - msg_prefix - + ( - "The form '%s' in context %d does not contain the " - "field '%s'" - ) - % (form, i, field) + msg_prefix + "The form '%s' in context %d does not contain the field '%s'" % + (form, i, field) ) else: non_field_errors = context[form].non_field_errors() @@ -614,17 +530,14 @@ class SimpleTestCase(unittest.TestCase): err in non_field_errors, msg_prefix + "The form '%s' in context %d does not" " contain the non-field error '%s'" - " (actual errors: %s)" - % (form, i, err, non_field_errors or "none"), + " (actual errors: %s)" % + (form, i, err, non_field_errors or 'none') ) if not found_form: - self.fail( - msg_prefix + "The form '%s' was not used to render the response" % form - ) + self.fail(msg_prefix + "The form '%s' was not used to render the response" % form) - def assertFormsetError( - self, response, formset, form_index, field, errors, msg_prefix="" - ): + def assertFormsetError(self, response, formset, form_index, field, errors, + msg_prefix=''): """ Assert that a formset used to render the response has a specific error. @@ -641,10 +554,8 @@ class SimpleTestCase(unittest.TestCase): # Put context(s) into a list to simplify processing. contexts = to_list(response.context) if not contexts: - self.fail( - msg_prefix + "Response did not use any contexts to " - "render the response" - ) + self.fail(msg_prefix + 'Response did not use any contexts to ' + 'render the response') # Put error(s) into a list to simplify processing. errors = to_list(errors) @@ -652,7 +563,7 @@ class SimpleTestCase(unittest.TestCase): # Search all contexts for the error. found_formset = False for i, context in enumerate(contexts): - if formset not in context or not hasattr(context[formset], "forms"): + if formset not in context: continue found_formset = True for err in errors: @@ -663,81 +574,63 @@ class SimpleTestCase(unittest.TestCase): err in field_errors, msg_prefix + "The field '%s' on formset '%s', " "form %d in context %d does not contain the " - "error '%s' (actual errors: %s)" - % (field, formset, form_index, i, err, repr(field_errors)), + "error '%s' (actual errors: %s)" % + (field, formset, form_index, i, err, repr(field_errors)) ) elif field in context[formset].forms[form_index].fields: self.fail( - msg_prefix - + ( - "The field '%s' on formset '%s', form %d in context " - "%d contains no errors" - ) + msg_prefix + "The field '%s' on formset '%s', form %d in context %d contains no errors" % (field, formset, form_index, i) ) else: self.fail( - msg_prefix - + ( - "The formset '%s', form %d in context %d does not " - "contain the field '%s'" - ) + msg_prefix + "The formset '%s', form %d in context %d does not contain the field '%s'" % (formset, form_index, i, field) ) elif form_index is not None: - non_field_errors = ( - context[formset].forms[form_index].non_field_errors() - ) + non_field_errors = context[formset].forms[form_index].non_field_errors() self.assertFalse( not non_field_errors, msg_prefix + "The formset '%s', form %d in context %d " - "does not contain any non-field errors." - % (formset, form_index, i), + "does not contain any non-field errors." % (formset, form_index, i) ) self.assertTrue( err in non_field_errors, msg_prefix + "The formset '%s', form %d in context %d " "does not contain the non-field error '%s' (actual errors: %s)" - % (formset, form_index, i, err, repr(non_field_errors)), + % (formset, form_index, i, err, repr(non_field_errors)) ) else: non_form_errors = context[formset].non_form_errors() self.assertFalse( not non_form_errors, msg_prefix + "The formset '%s' in context %d does not " - "contain any non-form errors." % (formset, i), + "contain any non-form errors." % (formset, i) ) self.assertTrue( err in non_form_errors, msg_prefix + "The formset '%s' in context %d does not " "contain the non-form error '%s' (actual errors: %s)" - % (formset, i, err, repr(non_form_errors)), + % (formset, i, err, repr(non_form_errors)) ) if not found_formset: - self.fail( - msg_prefix - + "The formset '%s' was not used to render the response" % formset - ) + self.fail(msg_prefix + "The formset '%s' was not used to render the response" % formset) def _assert_template_used(self, response, template_name, msg_prefix): if response is None and template_name is None: - raise TypeError("response and/or template_name argument must be provided") + raise TypeError('response and/or template_name argument must be provided') if msg_prefix: msg_prefix += ": " - if ( - template_name is not None - and response is not None - and not hasattr(response, "templates") - ): + if template_name is not None and response is not None and not hasattr(response, 'templates'): raise ValueError( "assertTemplateUsed() and assertTemplateNotUsed() are only " "usable on responses fetched using the Django test Client." ) - if not hasattr(response, "templates") or (response is None and template_name): + if not hasattr(response, 'templates') or (response is None and template_name): if response: template_name = response response = None @@ -747,16 +640,13 @@ class SimpleTestCase(unittest.TestCase): template_names = [t.name for t in response.templates if t.name is not None] return None, template_names, msg_prefix - def assertTemplateUsed( - self, response=None, template_name=None, msg_prefix="", count=None - ): + def assertTemplateUsed(self, response=None, template_name=None, msg_prefix='', count=None): """ Assert that the template with the provided name was used in rendering the response. Also usable as context manager. """ context_mgr_template, template_names, msg_prefix = self._assert_template_used( - response, template_name, msg_prefix - ) + response, template_name, msg_prefix) if context_mgr_template: # Use assertTemplateUsed as context manager. @@ -768,19 +658,18 @@ class SimpleTestCase(unittest.TestCase): template_name in template_names, msg_prefix + "Template '%s' was not a template used to render" " the response. Actual template(s) used: %s" - % (template_name, ", ".join(template_names)), + % (template_name, ', '.join(template_names)) ) if count is not None: self.assertEqual( - template_names.count(template_name), - count, + template_names.count(template_name), count, msg_prefix + "Template '%s' was expected to be rendered %d " "time(s) but was actually rendered %d time(s)." - % (template_name, count, template_names.count(template_name)), + % (template_name, count, template_names.count(template_name)) ) - def assertTemplateNotUsed(self, response=None, template_name=None, msg_prefix=""): + def assertTemplateNotUsed(self, response=None, template_name=None, msg_prefix=''): """ Assert that the template with the provided name was NOT used in rendering the response. Also usable as context manager. @@ -794,28 +683,20 @@ class SimpleTestCase(unittest.TestCase): self.assertFalse( template_name in template_names, - msg_prefix - + "Template '%s' was used unexpectedly in rendering the response" - % template_name, + msg_prefix + "Template '%s' was used unexpectedly in rendering the response" % template_name ) @contextmanager - def _assert_raises_or_warns_cm( - self, func, cm_attr, expected_exception, expected_message - ): + def _assert_raises_or_warns_cm(self, func, cm_attr, expected_exception, expected_message): with func(expected_exception) as cm: yield cm self.assertIn(expected_message, str(getattr(cm, cm_attr))) - def _assertFooMessage( - self, func, cm_attr, expected_exception, expected_message, *args, **kwargs - ): + def _assertFooMessage(self, func, cm_attr, expected_exception, expected_message, *args, **kwargs): callable_obj = None if args: callable_obj, *args = args - cm = self._assert_raises_or_warns_cm( - func, cm_attr, expected_exception, expected_message - ) + cm = self._assert_raises_or_warns_cm(func, cm_attr, expected_exception, expected_message) # Assertion used in context manager fashion. if callable_obj is None: return cm @@ -823,9 +704,7 @@ class SimpleTestCase(unittest.TestCase): with cm: callable_obj(*args, **kwargs) - def assertRaisesMessage( - self, expected_exception, expected_message, *args, **kwargs - ): + def assertRaisesMessage(self, expected_exception, expected_message, *args, **kwargs): """ Assert that expected_message is found in the message of a raised exception. @@ -837,12 +716,8 @@ class SimpleTestCase(unittest.TestCase): kwargs: Extra kwargs. """ return self._assertFooMessage( - self.assertRaises, - "exception", - expected_exception, - expected_message, - *args, - **kwargs, + self.assertRaises, 'exception', expected_exception, expected_message, + *args, **kwargs ) def assertWarnsMessage(self, expected_warning, expected_message, *args, **kwargs): @@ -851,49 +726,12 @@ class SimpleTestCase(unittest.TestCase): assertRaises(). """ return self._assertFooMessage( - self.assertWarns, - "warning", - expected_warning, - expected_message, - *args, - **kwargs, + self.assertWarns, 'warning', expected_warning, expected_message, + *args, **kwargs ) - # A similar method is available in Python 3.10+. - if not PY310: - - @contextmanager - def assertNoLogs(self, logger, level=None): - """ - Assert no messages are logged on the logger, with at least the - given level. - """ - if isinstance(level, int): - level = logging.getLevelName(level) - elif level is None: - level = "INFO" - try: - with self.assertLogs(logger, level) as cm: - yield - except AssertionError as e: - msg = e.args[0] - expected_msg = ( - f"no logs of level {level} or higher triggered on {logger}" - ) - if msg != expected_msg: - raise e - else: - self.fail(f"Unexpected logs found: {cm.output!r}") - - def assertFieldOutput( - self, - fieldclass, - valid, - invalid, - field_args=None, - field_kwargs=None, - empty_value="", - ): + def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None, + field_kwargs=None, empty_value=''): """ Assert that a form field behaves correctly with various inputs. @@ -912,7 +750,7 @@ class SimpleTestCase(unittest.TestCase): if field_kwargs is None: field_kwargs = {} required = fieldclass(*field_args, **field_kwargs) - optional = fieldclass(*field_args, **{**field_kwargs, "required": False}) + optional = fieldclass(*field_args, **{**field_kwargs, 'required': False}) # test valid inputs for input, output in valid.items(): self.assertEqual(required.clean(input), output) @@ -927,7 +765,7 @@ class SimpleTestCase(unittest.TestCase): optional.clean(input) self.assertEqual(context_manager.exception.messages, errors) # test required inputs - error_required = [required.error_messages["required"]] + error_required = [required.error_messages['required']] for e in required.empty_values: with self.assertRaises(ValidationError) as context_manager: required.clean(e) @@ -935,7 +773,7 @@ class SimpleTestCase(unittest.TestCase): self.assertEqual(optional.clean(e), empty_value) # test that max_length and min_length are always accepted if issubclass(fieldclass, CharField): - field_kwargs.update({"min_length": 2, "max_length": 20}) + field_kwargs.update({'min_length': 2, 'max_length': 20}) self.assertIsInstance(fieldclass(*field_args, **field_kwargs), fieldclass) def assertHTMLEqual(self, html1, html2, msg=None): @@ -944,57 +782,39 @@ class SimpleTestCase(unittest.TestCase): Whitespace in most cases is ignored, and attribute ordering is not significant. The arguments must be valid HTML. """ - dom1 = assert_and_parse_html( - self, html1, msg, "First argument is not valid HTML:" - ) - dom2 = assert_and_parse_html( - self, html2, msg, "Second argument is not valid HTML:" - ) + dom1 = assert_and_parse_html(self, html1, msg, 'First argument is not valid HTML:') + dom2 = assert_and_parse_html(self, html2, msg, 'Second argument is not valid HTML:') if dom1 != dom2: - standardMsg = "%s != %s" % (safe_repr(dom1, True), safe_repr(dom2, True)) - diff = "\n" + "\n".join( - difflib.ndiff( - str(dom1).splitlines(), - str(dom2).splitlines(), - ) - ) + standardMsg = '%s != %s' % ( + safe_repr(dom1, True), safe_repr(dom2, True)) + diff = ('\n' + '\n'.join(difflib.ndiff( + str(dom1).splitlines(), str(dom2).splitlines(), + ))) standardMsg = self._truncateMessage(standardMsg, diff) self.fail(self._formatMessage(msg, standardMsg)) def assertHTMLNotEqual(self, html1, html2, msg=None): """Assert that two HTML snippets are not semantically equivalent.""" - dom1 = assert_and_parse_html( - self, html1, msg, "First argument is not valid HTML:" - ) - dom2 = assert_and_parse_html( - self, html2, msg, "Second argument is not valid HTML:" - ) + dom1 = assert_and_parse_html(self, html1, msg, 'First argument is not valid HTML:') + dom2 = assert_and_parse_html(self, html2, msg, 'Second argument is not valid HTML:') if dom1 == dom2: - standardMsg = "%s == %s" % (safe_repr(dom1, True), safe_repr(dom2, True)) + standardMsg = '%s == %s' % ( + safe_repr(dom1, True), safe_repr(dom2, True)) self.fail(self._formatMessage(msg, standardMsg)) - def assertInHTML(self, needle, haystack, count=None, msg_prefix=""): - needle = assert_and_parse_html( - self, needle, None, "First argument is not valid HTML:" - ) - haystack = assert_and_parse_html( - self, haystack, None, "Second argument is not valid HTML:" - ) + def assertInHTML(self, needle, haystack, count=None, msg_prefix=''): + needle = assert_and_parse_html(self, needle, None, 'First argument is not valid HTML:') + haystack = assert_and_parse_html(self, haystack, None, 'Second argument is not valid HTML:') real_count = haystack.count(needle) if count is not None: self.assertEqual( - real_count, - count, - msg_prefix - + "Found %d instances of '%s' in response (expected %d)" - % (real_count, needle, count), + real_count, count, + msg_prefix + "Found %d instances of '%s' in response (expected %d)" % (real_count, needle, count) ) else: - self.assertTrue( - real_count != 0, msg_prefix + "Couldn't find '%s' in response" % needle - ) + self.assertTrue(real_count != 0, msg_prefix + "Couldn't find '%s' in response" % needle) def assertJSONEqual(self, raw, expected_data, msg=None): """ @@ -1039,17 +859,14 @@ class SimpleTestCase(unittest.TestCase): try: result = compare_xml(xml1, xml2) except Exception as e: - standardMsg = "First or second argument is not valid XML\n%s" % e + standardMsg = 'First or second argument is not valid XML\n%s' % e self.fail(self._formatMessage(msg, standardMsg)) else: if not result: - standardMsg = "%s != %s" % ( - safe_repr(xml1, True), - safe_repr(xml2, True), - ) - diff = "\n" + "\n".join( + standardMsg = '%s != %s' % (safe_repr(xml1, True), safe_repr(xml2, True)) + diff = ('\n' + '\n'.join( difflib.ndiff(xml1.splitlines(), xml2.splitlines()) - ) + )) standardMsg = self._truncateMessage(standardMsg, diff) self.fail(self._formatMessage(msg, standardMsg)) @@ -1062,14 +879,11 @@ class SimpleTestCase(unittest.TestCase): try: result = compare_xml(xml1, xml2) except Exception as e: - standardMsg = "First or second argument is not valid XML\n%s" % e + standardMsg = 'First or second argument is not valid XML\n%s' % e self.fail(self._formatMessage(msg, standardMsg)) else: if result: - standardMsg = "%s == %s" % ( - safe_repr(xml1, True), - safe_repr(xml2, True), - ) + standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True)) self.fail(self._formatMessage(msg, standardMsg)) @@ -1087,9 +901,9 @@ class TransactionTestCase(SimpleTestCase): databases = {DEFAULT_DB_ALIAS} _disallowed_database_msg = ( - "Database %(operation)s to %(alias)r are not allowed in this test. " - "Add %(alias)r to %(test)s.databases to ensure proper test isolation " - "and silence this failure." + 'Database %(operation)s to %(alias)r are not allowed in this test. ' + 'Add %(alias)r to %(test)s.databases to ensure proper test isolation ' + 'and silence this failure.' ) # If transactions aren't available, Django will serialize the database @@ -1111,7 +925,7 @@ class TransactionTestCase(SimpleTestCase): apps.set_available_apps(self.available_apps) setting_changed.send( sender=settings._wrapped.__class__, - setting="INSTALLED_APPS", + setting='INSTALLED_APPS', value=self.available_apps, enter=True, ) @@ -1124,7 +938,7 @@ class TransactionTestCase(SimpleTestCase): apps.unset_available_apps() setting_changed.send( sender=settings._wrapped.__class__, - setting="INSTALLED_APPS", + setting='INSTALLED_APPS', value=settings.INSTALLED_APPS, enter=False, ) @@ -1139,12 +953,9 @@ class TransactionTestCase(SimpleTestCase): def _databases_names(cls, include_mirrors=True): # Only consider allowed database aliases, including mirrors or not. return [ - alias - for alias in connections - if alias in cls.databases - and ( - include_mirrors - or not connections[alias].settings_dict["TEST"]["MIRROR"] + alias for alias in connections + if alias in cls.databases and ( + include_mirrors or not connections[alias].settings_dict['TEST']['MIRROR'] ) ] @@ -1152,8 +963,7 @@ class TransactionTestCase(SimpleTestCase): conn = connections[db_name] if conn.features.supports_sequence_reset: sql_list = conn.ops.sequence_reset_by_name_sql( - no_style(), conn.introspection.sequence_list() - ) + no_style(), conn.introspection.sequence_list()) if sql_list: with transaction.atomic(using=db_name): with conn.cursor() as cursor: @@ -1167,9 +977,7 @@ class TransactionTestCase(SimpleTestCase): self._reset_sequences(db_name) # Provide replica initial data from migrated apps, if needed. - if self.serialized_rollback and hasattr( - connections[db_name], "_test_serialized_contents" - ): + if self.serialized_rollback and hasattr(connections[db_name], "_test_serialized_contents"): if self.available_apps is not None: apps.unset_available_apps() connections[db_name].creation.deserialize_db_from_string( @@ -1181,9 +989,8 @@ class TransactionTestCase(SimpleTestCase): if self.fixtures: # We have to use this slightly awkward syntax due to the fact # that we're using *args and **kwargs together. - call_command( - "loaddata", *self.fixtures, **{"verbosity": 0, "database": db_name} - ) + call_command('loaddata', *self.fixtures, + **{'verbosity': 0, 'database': db_name}) def _should_reload_connections(self): return True @@ -1210,12 +1017,10 @@ class TransactionTestCase(SimpleTestCase): finally: if self.available_apps is not None: apps.unset_available_apps() - setting_changed.send( - sender=settings._wrapped.__class__, - setting="INSTALLED_APPS", - value=settings.INSTALLED_APPS, - enter=False, - ) + setting_changed.send(sender=settings._wrapped.__class__, + setting='INSTALLED_APPS', + value=settings.INSTALLED_APPS, + enter=False) def _fixture_teardown(self): # Allow TRUNCATE ... CASCADE and don't emit the post_migrate signal @@ -1223,32 +1028,25 @@ class TransactionTestCase(SimpleTestCase): for db_name in self._databases_names(include_mirrors=False): # Flush the database inhibit_post_migrate = ( - self.available_apps is not None - or ( # Inhibit the post_migrate signal when using serialized + self.available_apps is not None or + ( # Inhibit the post_migrate signal when using serialized # rollback to avoid trying to recreate the serialized data. - self.serialized_rollback - and hasattr(connections[db_name], "_test_serialized_contents") + self.serialized_rollback and + hasattr(connections[db_name], '_test_serialized_contents') ) ) - call_command( - "flush", - verbosity=0, - interactive=False, - database=db_name, - reset_sequences=False, - allow_cascade=self.available_apps is not None, - inhibit_post_migrate=inhibit_post_migrate, - ) + call_command('flush', verbosity=0, interactive=False, + database=db_name, reset_sequences=False, + allow_cascade=self.available_apps is not None, + inhibit_post_migrate=inhibit_post_migrate) def assertQuerysetEqual(self, qs, values, transform=None, ordered=True, msg=None): values = list(values) # RemovedInDjango41Warning. if transform is None: if ( - values - and isinstance(values[0], str) - and qs - and not isinstance(qs[0], str) + values and isinstance(values[0], str) and + qs and not isinstance(qs[0], str) ): # Transform qs using repr() if the first element of values is a # string and the first element of qs is not (which would be the @@ -1265,14 +1063,12 @@ class TransactionTestCase(SimpleTestCase): if transform is not None: items = map(transform, items) if not ordered: - return self.assertDictEqual(Counter(items), Counter(values), msg=msg) + return self.assertEqual(Counter(items), Counter(values), msg=msg) # For example qs.iterator() could be passed as qs, but it does not # have 'ordered' attribute. - if len(values) > 1 and hasattr(qs, "ordered") and not qs.ordered: - raise ValueError( - "Trying to compare non-ordered queryset against more than one " - "ordered value." - ) + if len(values) > 1 and hasattr(qs, 'ordered') and not qs.ordered: + raise ValueError("Trying to compare non-ordered queryset " + "against more than one ordered values") return self.assertEqual(list(items), values, msg=msg) def assertNumQueries(self, num, func=None, *args, using=DEFAULT_DB_ALIAS, **kwargs): @@ -1291,11 +1087,7 @@ def connections_support_transactions(aliases=None): Return whether or not all (or specified) connections support transactions. """ - conns = ( - connections.all() - if aliases is None - else (connections[alias] for alias in aliases) - ) + conns = connections.all() if aliases is None else (connections[alias] for alias in aliases) return all(conn.features.supports_transactions for conn in conns) @@ -1310,8 +1102,7 @@ class TestData: Objects are deep copied using a memo kept on the test case instance in order to maintain their original relationships. """ - - memo_attr = "_testdata_memo" + memo_attr = '_testdata_memo' def __init__(self, name, data): self.name = name @@ -1350,7 +1141,7 @@ class TestData: return data def __repr__(self): - return "<TestData: name=%r, data=%r>" % (self.name, self.data) + return '<TestData: name=%r, data=%r>' % (self.name, self.data) class TestCase(TransactionTestCase): @@ -1366,7 +1157,6 @@ class TestCase(TransactionTestCase): On database backends with no transaction support, TestCase behaves as TransactionTestCase. """ - @classmethod def _enter_atomics(cls): """Open atomic blocks for multiple databases.""" @@ -1401,19 +1191,17 @@ class TestCase(TransactionTestCase): if cls.fixtures: for db_name in cls._databases_names(include_mirrors=False): try: - call_command( - "loaddata", - *cls.fixtures, - **{"verbosity": 0, "database": db_name}, - ) + call_command('loaddata', *cls.fixtures, **{'verbosity': 0, 'database': db_name}) except Exception: cls._rollback_atomics(cls.cls_atomics) + cls._remove_databases_failures() raise pre_attrs = cls.__dict__.copy() try: cls.setUpTestData() except Exception: cls._rollback_atomics(cls.cls_atomics) + cls._remove_databases_failures() raise for name, value in cls.__dict__.items(): if value is not pre_attrs.get(name): @@ -1448,8 +1236,7 @@ class TestCase(TransactionTestCase): self.setUpTestData() return super()._fixture_setup() - if self.reset_sequences: - raise TypeError("reset_sequences cannot be used on TestCase instances") + assert not self.reset_sequences, 'reset_sequences cannot be used on TestCase instances' self.atomics = self._enter_atomics() def _fixture_teardown(self): @@ -1464,9 +1251,8 @@ class TestCase(TransactionTestCase): def _should_check_constraints(self, connection): return ( - connection.features.can_defer_constraint_checks - and not connection.needs_rollback - and connection.is_usable() + connection.features.can_defer_constraint_checks and + not connection.needs_rollback and connection.is_usable() ) @classmethod @@ -1478,21 +1264,15 @@ class TestCase(TransactionTestCase): try: yield callbacks finally: - while True: - callback_count = len(connections[using].run_on_commit) - for _, callback in connections[using].run_on_commit[start_count:]: - callbacks.append(callback) - if execute: - callback() - - if callback_count == len(connections[using].run_on_commit): - break - start_count = callback_count + run_on_commit = connections[using].run_on_commit[start_count:] + callbacks[:] = [func for sids, func in run_on_commit] + if execute: + for callback in callbacks: + callback() class CheckCondition: """Descriptor class for deferred condition checking.""" - def __init__(self, *conditions): self.conditions = conditions @@ -1501,7 +1281,7 @@ class CheckCondition: def __get__(self, instance, cls=None): # Trigger access for all bases. - if any(getattr(base, "__unittest_skip__", False) for base in cls.__bases__): + if any(getattr(base, '__unittest_skip__', False) for base in cls.__bases__): return True for condition, reason in self.conditions: if condition(): @@ -1515,21 +1295,15 @@ class CheckCondition: def _deferredSkip(condition, reason, name): def decorator(test_func): nonlocal condition - if not ( - isinstance(test_func, type) and issubclass(test_func, unittest.TestCase) - ): - + if not (isinstance(test_func, type) and + issubclass(test_func, unittest.TestCase)): @wraps(test_func) def skip_wrapper(*args, **kwargs): - if ( - args - and isinstance(args[0], unittest.TestCase) - and connection.alias not in getattr(args[0], "databases", {}) - ): + if (args and isinstance(args[0], unittest.TestCase) and + connection.alias not in getattr(args[0], 'databases', {})): raise ValueError( "%s cannot be used on %s as %s doesn't allow queries " - "against the %r database." - % ( + "against the %r database." % ( name, args[0], args[0].__class__.__qualname__, @@ -1539,67 +1313,55 @@ def _deferredSkip(condition, reason, name): if condition(): raise unittest.SkipTest(reason) return test_func(*args, **kwargs) - test_item = skip_wrapper else: # Assume a class is decorated test_item = test_func - databases = getattr(test_item, "databases", None) + databases = getattr(test_item, 'databases', None) if not databases or connection.alias not in databases: # Defer raising to allow importing test class's module. def condition(): raise ValueError( "%s cannot be used on %s as it doesn't allow queries " - "against the '%s' database." - % ( - name, - test_item, - connection.alias, + "against the '%s' database." % ( + name, test_item, connection.alias, ) ) - # Retrieve the possibly existing value from the class's dict to # avoid triggering the descriptor. - skip = test_func.__dict__.get("__unittest_skip__") + skip = test_func.__dict__.get('__unittest_skip__') if isinstance(skip, CheckCondition): test_item.__unittest_skip__ = skip.add_condition(condition, reason) elif skip is not True: test_item.__unittest_skip__ = CheckCondition((condition, reason)) return test_item - return decorator def skipIfDBFeature(*features): """Skip a test if a database has at least one of the named features.""" return _deferredSkip( - lambda: any( - getattr(connection.features, feature, False) for feature in features - ), + lambda: any(getattr(connection.features, feature, False) for feature in features), "Database has feature(s) %s" % ", ".join(features), - "skipIfDBFeature", + 'skipIfDBFeature', ) def skipUnlessDBFeature(*features): """Skip a test unless a database has all the named features.""" return _deferredSkip( - lambda: not all( - getattr(connection.features, feature, False) for feature in features - ), + lambda: not all(getattr(connection.features, feature, False) for feature in features), "Database doesn't support feature(s): %s" % ", ".join(features), - "skipUnlessDBFeature", + 'skipUnlessDBFeature', ) def skipUnlessAnyDBFeature(*features): """Skip a test unless a database has any of the named features.""" return _deferredSkip( - lambda: not any( - getattr(connection.features, feature, False) for feature in features - ), + lambda: not any(getattr(connection.features, feature, False) for feature in features), "Database doesn't support any of the feature(s): %s" % ", ".join(features), - "skipUnlessAnyDBFeature", + 'skipUnlessAnyDBFeature', ) @@ -1608,7 +1370,6 @@ class QuietWSGIRequestHandler(WSGIRequestHandler): A WSGIRequestHandler that doesn't log to standard output any of the requests received, so as to not clutter the test result output. """ - def log_message(*args): pass @@ -1618,7 +1379,6 @@ class FSFilesHandler(WSGIHandler): WSGI middleware that intercepts calls to a directory, as defined by one of the *_ROOT settings, and serves those files, publishing them under *_URL. """ - def __init__(self, application): self.application = application self.base_url = urlparse(self.get_base_url()) @@ -1634,7 +1394,7 @@ class FSFilesHandler(WSGIHandler): def file_path(self, url): """Return the relative path to the file on disk for the given URL.""" - relative_url = url[len(self.base_url[2]) :] + relative_url = url[len(self.base_url[2]):] return url2pathname(relative_url) def get_response(self, request): @@ -1653,7 +1413,7 @@ class FSFilesHandler(WSGIHandler): # Emulate behavior of django.contrib.staticfiles.views.serve() when it # invokes staticfiles' finders functionality. # TODO: Modify if/when that internal API is refactored - final_rel_path = os_rel_path.replace("\\", "/").lstrip("/") + final_rel_path = os_rel_path.replace('\\', '/').lstrip('/') return serve(request, final_rel_path, document_root=self.get_base_dir()) def __call__(self, environ, start_response): @@ -1667,7 +1427,6 @@ class _StaticFilesHandler(FSFilesHandler): Handler for serving static files. A private class that is meant to be used solely as a convenience by LiveServerThread. """ - def get_base_dir(self): return settings.STATIC_ROOT @@ -1680,7 +1439,6 @@ class _MediaFilesHandler(FSFilesHandler): Handler for serving the media files. A private class that is meant to be used solely as a convenience by LiveServerThread. """ - def get_base_dir(self): return settings.MEDIA_ROOT @@ -1689,9 +1447,7 @@ class _MediaFilesHandler(FSFilesHandler): class LiveServerThread(threading.Thread): - """Thread for running a live HTTP server while the tests are running.""" - - server_class = ThreadedWSGIServer + """Thread for running a live http server while the tests are running.""" def __init__(self, host, static_handler, connections_override=None, port=0): self.host = host @@ -1728,16 +1484,11 @@ class LiveServerThread(threading.Thread): finally: connections.close_all() - def _create_server(self, connections_override=None): - return self.server_class( - (self.host, self.port), - QuietWSGIRequestHandler, - allow_reuse_address=False, - connections_override=connections_override, - ) + def _create_server(self): + return ThreadedWSGIServer((self.host, self.port), QuietWSGIRequestHandler, allow_reuse_address=False) def terminate(self): - if hasattr(self, "httpd"): + if hasattr(self, 'httpd'): # Stop the WSGI server self.httpd.shutdown() self.httpd.server_close() @@ -1755,43 +1506,35 @@ class LiveServerTestCase(TransactionTestCase): and each thread needs to commit all their transactions so that the other thread can see the changes. """ - - host = "localhost" + host = 'localhost' port = 0 server_thread_class = LiveServerThread static_handler = _StaticFilesHandler @classproperty def live_server_url(cls): - return "http://%s:%s" % (cls.host, cls.server_thread.port) + return 'http://%s:%s' % (cls.host, cls.server_thread.port) @classproperty def allowed_host(cls): return cls.host @classmethod - def _make_connections_override(cls): + def setUpClass(cls): + super().setUpClass() connections_override = {} for conn in connections.all(): # If using in-memory sqlite databases, pass the connections to # the server thread. - if conn.vendor == "sqlite" and conn.is_in_memory_db(): + if conn.vendor == 'sqlite' and conn.is_in_memory_db(): + # Explicitly enable thread-shareability for this connection + conn.inc_thread_sharing() connections_override[conn.alias] = conn - return connections_override - @classmethod - def setUpClass(cls): - super().setUpClass() cls._live_server_modified_settings = modify_settings( - ALLOWED_HOSTS={"append": cls.allowed_host}, + ALLOWED_HOSTS={'append': cls.allowed_host}, ) cls._live_server_modified_settings.enable() - - connections_override = cls._make_connections_override() - for conn in connections_override.values(): - # Explicitly enable thread-shareability for this connection. - conn.inc_thread_sharing() - cls.server_thread = cls._create_server_thread(connections_override) cls.server_thread.daemon = True cls.server_thread.start() @@ -1815,14 +1558,18 @@ class LiveServerTestCase(TransactionTestCase): @classmethod def _tearDownClassInternal(cls): - # Terminate the live server's thread. - cls.server_thread.terminate() - # Restore shared connections' non-shareability. - for conn in cls.server_thread.connections_override.values(): - conn.dec_thread_sharing() + # There may not be a 'server_thread' attribute if setUpClass() for some + # reasons has raised an exception. + if hasattr(cls, 'server_thread'): + # Terminate the live server's thread + cls.server_thread.terminate() - cls._live_server_modified_settings.disable() - super().tearDownClass() + # Restore sqlite in-memory database connections' non-shareability. + for conn in cls.server_thread.connections_override.values(): + conn.dec_thread_sharing() + + cls._live_server_modified_settings.disable() + super().tearDownClass() @classmethod def tearDownClass(cls): @@ -1838,20 +1585,19 @@ class SerializeMixin: Place it early in the MRO in order to isolate setUpClass()/tearDownClass(). """ - lockfile = None - def __init_subclass__(cls, /, **kwargs): - super().__init_subclass__(**kwargs) - if cls.lockfile is None: - raise ValueError( - "{}.lockfile isn't set. Set it to a unique value " - "in the base class.".format(cls.__name__) - ) - @classmethod def setUpClass(cls): + if cls.lockfile is None: + raise ValueError( + "{}.lockfile isn't set. Set it to a unique value " + "in the base class.".format(cls.__name__)) cls._lockfile = open(cls.lockfile) - cls.addClassCleanup(cls._lockfile.close) locks.lock(cls._lockfile, locks.LOCK_EX) super().setUpClass() + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls._lockfile.close() diff --git a/venv/Lib/site-packages/django/test/utils.py b/venv/Lib/site-packages/django/test/utils.py index b99f633..b91532e 100644 --- a/venv/Lib/site-packages/django/test/utils.py +++ b/venv/Lib/site-packages/django/test/utils.py @@ -25,7 +25,6 @@ from django.db.models.options import Options from django.template import Template from django.test.signals import setting_changed, template_rendered from django.urls import get_script_prefix, set_script_prefix -from django.utils.deprecation import RemovedInDjango50Warning from django.utils.translation import deactivate try: @@ -35,24 +34,15 @@ except ImportError: __all__ = ( - "Approximate", - "ContextList", - "isolate_lru_cache", - "get_runner", - "CaptureQueriesContext", - "ignore_warnings", - "isolate_apps", - "modify_settings", - "override_settings", - "override_system_checks", - "tag", - "requires_tz_support", - "setup_databases", - "setup_test_environment", - "teardown_test_environment", + 'Approximate', 'ContextList', 'isolate_lru_cache', 'get_runner', + 'CaptureQueriesContext', + 'ignore_warnings', 'isolate_apps', 'modify_settings', 'override_settings', + 'override_system_checks', 'tag', + 'requires_tz_support', + 'setup_databases', 'setup_test_environment', 'teardown_test_environment', ) -TZ_SUPPORT = hasattr(time, "tzset") +TZ_SUPPORT = hasattr(time, 'tzset') class Approximate: @@ -72,7 +62,6 @@ class ContextList(list): A wrapper that provides direct key access to context items contained in a list of context objects. """ - def __getitem__(self, key): if isinstance(key, str): for subcontext in self: @@ -120,7 +109,7 @@ def setup_test_environment(debug=None): Perform global pre-test setup, such as installing the instrumented template renderer and setting the email backend to the locmem email backend. """ - if hasattr(_TestState, "saved_data"): + if hasattr(_TestState, 'saved_data'): # Executing this function twice would overwrite the saved values. raise RuntimeError( "setup_test_environment() was already called and can't be called " @@ -135,13 +124,13 @@ def setup_test_environment(debug=None): saved_data.allowed_hosts = settings.ALLOWED_HOSTS # Add the default host of the test client. - settings.ALLOWED_HOSTS = [*settings.ALLOWED_HOSTS, "testserver"] + settings.ALLOWED_HOSTS = [*settings.ALLOWED_HOSTS, 'testserver'] saved_data.debug = settings.DEBUG settings.DEBUG = debug saved_data.email_backend = settings.EMAIL_BACKEND - settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" + settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' saved_data.template_render = Template._render Template._render = instrumented_test_render @@ -167,18 +156,8 @@ def teardown_test_environment(): del mail.outbox -def setup_databases( - verbosity, - interactive, - *, - time_keeper=None, - keepdb=False, - debug_sql=False, - parallel=0, - aliases=None, - serialized_aliases=None, - **kwargs, -): +def setup_databases(verbosity, interactive, *, time_keeper=None, keepdb=False, debug_sql=False, parallel=0, + aliases=None, **kwargs): """Create the test databases.""" if time_keeper is None: time_keeper = NullTimeKeeper() @@ -197,31 +176,11 @@ def setup_databases( if first_alias is None: first_alias = alias with time_keeper.timed(" Creating '%s'" % alias): - # RemovedInDjango50Warning: when the deprecation ends, - # replace with: - # serialize_alias = ( - # serialized_aliases is None - # or alias in serialized_aliases - # ) - try: - serialize_alias = connection.settings_dict["TEST"]["SERIALIZE"] - except KeyError: - serialize_alias = ( - serialized_aliases is None or alias in serialized_aliases - ) - else: - warnings.warn( - "The SERIALIZE test database setting is " - "deprecated as it can be inferred from the " - "TestCase/TransactionTestCase.databases that " - "enable the serialized_rollback feature.", - category=RemovedInDjango50Warning, - ) connection.creation.create_test_db( verbosity=verbosity, autoclobber=not interactive, keepdb=keepdb, - serialize=serialize_alias, + serialize=connection.settings_dict['TEST'].get('SERIALIZE', True), ) if parallel > 1: for index in range(parallel): @@ -233,15 +192,12 @@ def setup_databases( ) # Configure all other connections as mirrors of the first one else: - connections[alias].creation.set_as_test_mirror( - connections[first_alias].settings_dict - ) + connections[alias].creation.set_as_test_mirror(connections[first_alias].settings_dict) # Configure the test mirrors. for alias, mirror_alias in mirrored_aliases.items(): connections[alias].creation.set_as_test_mirror( - connections[mirror_alias].settings_dict - ) + connections[mirror_alias].settings_dict) if debug_sql: for alias in connections: @@ -250,27 +206,6 @@ def setup_databases( return old_names -def iter_test_cases(tests): - """ - Return an iterator over a test suite's unittest.TestCase objects. - - The tests argument can also be an iterable of TestCase objects. - """ - for test in tests: - if isinstance(test, str): - # Prevent an unfriendly RecursionError that can happen with - # strings. - raise TypeError( - f"Test {test!r} must be a test case or test suite not string " - f"(was found in {tests!r})." - ) - if isinstance(test, TestCase): - yield test - else: - # Otherwise, assume it is a test suite. - yield from iter_test_cases(test) - - def dependency_ordered(test_databases, dependencies): """ Reorder test_databases into an order that honors the dependencies @@ -334,18 +269,18 @@ def get_unique_databases_and_mirrors(aliases=None): for alias in connections: connection = connections[alias] - test_settings = connection.settings_dict["TEST"] + test_settings = connection.settings_dict['TEST'] - if test_settings["MIRROR"]: + if test_settings['MIRROR']: # If the database is marked as a test mirror, save the alias. - mirrored_aliases[alias] = test_settings["MIRROR"] + mirrored_aliases[alias] = test_settings['MIRROR'] elif alias in aliases: # Store a tuple with DB parameters that uniquely identify it. # If we have two aliases with the same values for that tuple, # we only need to create the test database once. item = test_databases.setdefault( connection.creation.test_db_signature(), - (connection.settings_dict["NAME"], []), + (connection.settings_dict['NAME'], []), ) # The default database must be the first because data migrations # use the default alias by default. @@ -354,16 +289,11 @@ def get_unique_databases_and_mirrors(aliases=None): else: item[1].append(alias) - if "DEPENDENCIES" in test_settings: - dependencies[alias] = test_settings["DEPENDENCIES"] + if 'DEPENDENCIES' in test_settings: + dependencies[alias] = test_settings['DEPENDENCIES'] else: - if ( - alias != DEFAULT_DB_ALIAS - and connection.creation.test_db_signature() != default_sig - ): - dependencies[alias] = test_settings.get( - "DEPENDENCIES", [DEFAULT_DB_ALIAS] - ) + if alias != DEFAULT_DB_ALIAS and connection.creation.test_db_signature() != default_sig: + dependencies[alias] = test_settings.get('DEPENDENCIES', [DEFAULT_DB_ALIAS]) test_databases = dict(dependency_ordered(test_databases.items(), dependencies)) return test_databases, mirrored_aliases @@ -385,12 +315,12 @@ def teardown_databases(old_config, verbosity, parallel=0, keepdb=False): def get_runner(settings, test_runner_class=None): test_runner_class = test_runner_class or settings.TEST_RUNNER - test_path = test_runner_class.split(".") + test_path = test_runner_class.split('.') # Allow for relative paths if len(test_path) > 1: - test_module_name = ".".join(test_path[:-1]) + test_module_name = '.'.join(test_path[:-1]) else: - test_module_name = "." + test_module_name = '.' test_module = __import__(test_module_name, {}, {}, test_path[-1]) return getattr(test_module, test_path[-1]) @@ -407,7 +337,6 @@ class TestContextDecorator: `kwarg_name`: keyword argument passing the return value of enable() if used as a function decorator. """ - def __init__(self, attr_name=None, kwarg_name=None): self.attr_name = attr_name self.kwarg_name = kwarg_name @@ -437,7 +366,7 @@ class TestContextDecorator: cls.setUp = setUp return cls - raise TypeError("Can only decorate subclasses of unittest.TestCase") + raise TypeError('Can only decorate subclasses of unittest.TestCase') def decorate_callable(self, func): if asyncio.iscoroutinefunction(func): @@ -449,16 +378,13 @@ class TestContextDecorator: if self.kwarg_name: kwargs[self.kwarg_name] = context return await func(*args, **kwargs) - else: - @wraps(func) def inner(*args, **kwargs): with self as context: if self.kwarg_name: kwargs[self.kwarg_name] = context return func(*args, **kwargs) - return inner def __call__(self, decorated): @@ -466,7 +392,7 @@ class TestContextDecorator: return self.decorate_class(decorated) elif callable(decorated): return self.decorate_callable(decorated) - raise TypeError("Cannot decorate object of type %s" % type(decorated)) + raise TypeError('Cannot decorate object of type %s' % type(decorated)) class override_settings(TestContextDecorator): @@ -476,7 +402,6 @@ class override_settings(TestContextDecorator): with the ``with`` statement. In either event, entering/exiting are called before and after, respectively, the function/block is executed. """ - enable_exception = None def __init__(self, **kwargs): @@ -486,9 +411,9 @@ class override_settings(TestContextDecorator): def enable(self): # Keep this code at the beginning to leave the settings unchanged # in case it raises an exception because INSTALLED_APPS is invalid. - if "INSTALLED_APPS" in self.options: + if 'INSTALLED_APPS' in self.options: try: - apps.set_installed_apps(self.options["INSTALLED_APPS"]) + apps.set_installed_apps(self.options['INSTALLED_APPS']) except Exception: apps.unset_installed_apps() raise @@ -501,16 +426,14 @@ class override_settings(TestContextDecorator): try: setting_changed.send( sender=settings._wrapped.__class__, - setting=key, - value=new_value, - enter=True, + setting=key, value=new_value, enter=True, ) except Exception as exc: self.enable_exception = exc self.disable() def disable(self): - if "INSTALLED_APPS" in self.options: + if 'INSTALLED_APPS' in self.options: apps.unset_installed_apps() settings._wrapped = self.wrapped del self.wrapped @@ -519,9 +442,7 @@ class override_settings(TestContextDecorator): new_value = getattr(settings, key, None) responses_for_setting = setting_changed.send_robust( sender=settings._wrapped.__class__, - setting=key, - value=new_value, - enter=False, + setting=key, value=new_value, enter=False, ) responses.extend(responses_for_setting) if self.enable_exception is not None: @@ -544,12 +465,10 @@ class override_settings(TestContextDecorator): def decorate_class(self, cls): from django.test import SimpleTestCase - if not issubclass(cls, SimpleTestCase): raise ValueError( "Only subclasses of Django SimpleTestCase can be decorated " - "with override_settings" - ) + "with override_settings") self.save_options(cls) return cls @@ -559,7 +478,6 @@ class modify_settings(override_settings): Like override_settings, but makes it possible to append, prepend, or remove items instead of redefining the entire list. """ - def __init__(self, *args, **kwargs): if args: # Hack used when instantiating from SimpleTestCase.setUpClass. @@ -575,9 +493,8 @@ class modify_settings(override_settings): test_func._modified_settings = self.operations else: # Duplicate list to prevent subclasses from altering their parent. - test_func._modified_settings = ( - list(test_func._modified_settings) + self.operations - ) + test_func._modified_settings = list( + test_func._modified_settings) + self.operations def enable(self): self.options = {} @@ -592,11 +509,11 @@ class modify_settings(override_settings): # items my be a single value or an iterable. if isinstance(items, str): items = [items] - if action == "append": + if action == 'append': value = value + [item for item in items if item not in value] - elif action == "prepend": + elif action == 'prepend': value = [item for item in items if item not in value] + value - elif action == "remove": + elif action == 'remove': value = [item for item in value if item not in items] else: raise ValueError("Unsupported action: %s" % action) @@ -610,10 +527,8 @@ class override_system_checks(TestContextDecorator): Useful when you override `INSTALLED_APPS`, e.g. if you exclude `auth` app, you also need to exclude its system checks. """ - def __init__(self, new_checks, deployment_checks=None): from django.core.checks.registry import registry - self.registry = registry self.new_checks = new_checks self.deployment_checks = deployment_checks @@ -623,12 +538,12 @@ class override_system_checks(TestContextDecorator): self.old_checks = self.registry.registered_checks self.registry.registered_checks = set() for check in self.new_checks: - self.registry.register(check, *getattr(check, "tags", ())) + self.registry.register(check, *getattr(check, 'tags', ())) self.old_deployment_checks = self.registry.deployment_checks if self.deployment_checks is not None: self.registry.deployment_checks = set() for check in self.deployment_checks: - self.registry.register(check, *getattr(check, "tags", ()), deploy=True) + self.registry.register(check, *getattr(check, 'tags', ()), deploy=True) def disable(self): self.registry.registered_checks = self.old_checks @@ -644,18 +559,18 @@ def compare_xml(want, got): Based on https://github.com/lxml/lxml/blob/master/src/lxml/doctestcompare.py """ - _norm_whitespace_re = re.compile(r"[ \t\n][ \t\n]+") + _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+') def norm_whitespace(v): - return _norm_whitespace_re.sub(" ", v) + return _norm_whitespace_re.sub(' ', v) def child_text(element): - return "".join( - c.data for c in element.childNodes if c.nodeType == Node.TEXT_NODE - ) + return ''.join(c.data for c in element.childNodes + if c.nodeType == Node.TEXT_NODE) def children(element): - return [c for c in element.childNodes if c.nodeType == Node.ELEMENT_NODE] + return [c for c in element.childNodes + if c.nodeType == Node.ELEMENT_NODE] def norm_child_text(element): return norm_whitespace(child_text(element)) @@ -674,9 +589,7 @@ def compare_xml(want, got): got_children = children(got_element) if len(want_children) != len(got_children): return False - return all( - check_element(want, got) for want, got in zip(want_children, got_children) - ) + return all(check_element(want, got) for want, got in zip(want_children, got_children)) def first_node(document): for node in document.childNodes: @@ -687,13 +600,13 @@ def compare_xml(want, got): ): return node - want = want.strip().replace("\\n", "\n") - got = got.strip().replace("\\n", "\n") + want = want.strip().replace('\\n', '\n') + got = got.strip().replace('\\n', '\n') # If the string is not a complete xml document, we may need to add a # root element. This allow us to compare fragments, like "<foo/><bar/>" - if not want.startswith("<?xml"): - wrapper = "<root>%s</root>" + if not want.startswith('<?xml'): + wrapper = '<root>%s</root>' want = wrapper % want got = wrapper % got @@ -708,7 +621,6 @@ class CaptureQueriesContext: """ Context manager that captures queries executed by the specified connection. """ - def __init__(self, connection): self.connection = connection @@ -723,7 +635,7 @@ class CaptureQueriesContext: @property def captured_queries(self): - return self.connection.queries[self.initial_queries : self.final_queries] + return self.connection.queries[self.initial_queries:self.final_queries] def __enter__(self): self.force_debug_cursor = self.connection.force_debug_cursor @@ -747,7 +659,7 @@ class CaptureQueriesContext: class ignore_warnings(TestContextDecorator): def __init__(self, **kwargs): self.ignore_kwargs = kwargs - if "message" in self.ignore_kwargs or "module" in self.ignore_kwargs: + if 'message' in self.ignore_kwargs or 'module' in self.ignore_kwargs: self.filter_func = warnings.filterwarnings else: self.filter_func = warnings.simplefilter @@ -756,7 +668,7 @@ class ignore_warnings(TestContextDecorator): def enable(self): self.catch_warnings = warnings.catch_warnings() self.catch_warnings.__enter__() - self.filter_func("ignore", **self.ignore_kwargs) + self.filter_func('ignore', **self.ignore_kwargs) def disable(self): self.catch_warnings.__exit__(*sys.exc_info()) @@ -770,7 +682,7 @@ class ignore_warnings(TestContextDecorator): requires_tz_support = skipUnless( TZ_SUPPORT, "This test relies on the ability to run a program in an arbitrary " - "time zone, but your operating system isn't able to do that.", + "time zone, but your operating system isn't able to do that." ) @@ -813,9 +725,9 @@ def captured_output(stream_name): def captured_stdout(): """Capture the output of sys.stdout: - with captured_stdout() as stdout: - print("hello") - self.assertEqual(stdout.getvalue(), "hello\n") + with captured_stdout() as stdout: + print("hello") + self.assertEqual(stdout.getvalue(), "hello\n") """ return captured_output("stdout") @@ -823,9 +735,9 @@ def captured_stdout(): def captured_stderr(): """Capture the output of sys.stderr: - with captured_stderr() as stderr: - print("hello", file=sys.stderr) - self.assertEqual(stderr.getvalue(), "hello\n") + with captured_stderr() as stderr: + print("hello", file=sys.stderr) + self.assertEqual(stderr.getvalue(), "hello\n") """ return captured_output("stderr") @@ -833,12 +745,12 @@ def captured_stderr(): def captured_stdin(): """Capture the input to sys.stdin: - with captured_stdin() as stdin: - stdin.write('hello\n') - stdin.seek(0) - # call test code that consumes from sys.stdin - captured = input() - self.assertEqual(captured, "hello") + with captured_stdin() as stdin: + stdin.write('hello\n') + stdin.seek(0) + # call test code that consumes from sys.stdin + captured = input() + self.assertEqual(captured, "hello") """ return captured_output("stdin") @@ -866,24 +778,18 @@ def require_jinja2(test_func): Django template engine for a test or skip it if Jinja2 isn't available. """ test_func = skipIf(jinja2 is None, "this test requires jinja2")(test_func) - return override_settings( - TEMPLATES=[ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "APP_DIRS": True, - }, - { - "BACKEND": "django.template.backends.jinja2.Jinja2", - "APP_DIRS": True, - "OPTIONS": {"keep_trailing_newline": True}, - }, - ] - )(test_func) + return override_settings(TEMPLATES=[{ + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + }, { + 'BACKEND': 'django.template.backends.jinja2.Jinja2', + 'APP_DIRS': True, + 'OPTIONS': {'keep_trailing_newline': True}, + }])(test_func) class override_script_prefix(TestContextDecorator): """Decorator or context manager to temporary override the script prefix.""" - def __init__(self, prefix): self.prefix = prefix super().__init__() @@ -901,9 +807,8 @@ class LoggingCaptureMixin: Capture the output from the 'django' logger and store it on the class's logger_output attribute. """ - def setUp(self): - self.logger = logging.getLogger("django") + self.logger = logging.getLogger('django') self.old_stream = self.logger.handlers[0].stream self.logger_output = StringIO() self.logger.handlers[0].stream = self.logger_output @@ -928,7 +833,6 @@ class isolate_apps(TestContextDecorator): `kwarg_name`: keyword argument passing the isolated registry if used as a function decorator. """ - def __init__(self, *installed_apps, **kwargs): self.installed_apps = installed_apps super().__init__(**kwargs) @@ -936,11 +840,11 @@ class isolate_apps(TestContextDecorator): def enable(self): self.old_apps = Options.default_apps apps = Apps(self.installed_apps) - setattr(Options, "default_apps", apps) + setattr(Options, 'default_apps', apps) return apps def disable(self): - setattr(Options, "default_apps", self.old_apps) + setattr(Options, 'default_apps', self.old_apps) class TimeKeeper: @@ -960,7 +864,7 @@ class TimeKeeper: def print_results(self): for name, end_times in self.records.items(): for record_time in end_times: - record = "%s took %.3fs" % (name, record_time) + record = '%s took %.3fs' % (name, record_time) sys.stderr.write(record + os.linesep) @@ -975,14 +879,12 @@ class NullTimeKeeper: def tag(*tags): """Decorator to add tags to a test class or method.""" - def decorator(obj): - if hasattr(obj, "tags"): + if hasattr(obj, 'tags'): obj.tags = obj.tags.union(tags) else: - setattr(obj, "tags", set(tags)) + setattr(obj, 'tags', set(tags)) return obj - return decorator diff --git a/venv/Lib/site-packages/django/urls/__init__.py b/venv/Lib/site-packages/django/urls/__init__.py index 9aaf481..e9e32ac 100644 --- a/venv/Lib/site-packages/django/urls/__init__.py +++ b/venv/Lib/site-packages/django/urls/__init__.py @@ -1,53 +1,23 @@ from .base import ( - clear_script_prefix, - clear_url_caches, - get_script_prefix, - get_urlconf, - is_valid_path, - resolve, - reverse, - reverse_lazy, - set_script_prefix, - set_urlconf, - translate_url, + clear_script_prefix, clear_url_caches, get_script_prefix, get_urlconf, + is_valid_path, resolve, reverse, reverse_lazy, set_script_prefix, + set_urlconf, translate_url, ) from .conf import include, path, re_path from .converters import register_converter from .exceptions import NoReverseMatch, Resolver404 from .resolvers import ( - LocalePrefixPattern, - ResolverMatch, - URLPattern, - URLResolver, - get_ns_resolver, - get_resolver, + LocalePrefixPattern, ResolverMatch, URLPattern, URLResolver, + get_ns_resolver, get_resolver, ) from .utils import get_callable, get_mod_func __all__ = [ - "LocalePrefixPattern", - "NoReverseMatch", - "URLPattern", - "URLResolver", - "Resolver404", - "ResolverMatch", - "clear_script_prefix", - "clear_url_caches", - "get_callable", - "get_mod_func", - "get_ns_resolver", - "get_resolver", - "get_script_prefix", - "get_urlconf", - "include", - "is_valid_path", - "path", - "re_path", - "register_converter", - "resolve", - "reverse", - "reverse_lazy", - "set_script_prefix", - "set_urlconf", - "translate_url", + 'LocalePrefixPattern', 'NoReverseMatch', 'URLPattern', + 'URLResolver', 'Resolver404', 'ResolverMatch', 'clear_script_prefix', + 'clear_url_caches', 'get_callable', 'get_mod_func', 'get_ns_resolver', + 'get_resolver', 'get_script_prefix', 'get_urlconf', 'include', + 'is_valid_path', 'path', 're_path', 'register_converter', 'resolve', + 'reverse', 'reverse_lazy', 'set_script_prefix', 'set_urlconf', + 'translate_url', ] diff --git a/venv/Lib/site-packages/django/urls/base.py b/venv/Lib/site-packages/django/urls/base.py index 647ef3e..8c26a38 100644 --- a/venv/Lib/site-packages/django/urls/base.py +++ b/venv/Lib/site-packages/django/urls/base.py @@ -36,16 +36,16 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None): if not isinstance(viewname, str): view = viewname else: - *path, view = viewname.split(":") + *path, view = viewname.split(':') if current_app: - current_path = current_app.split(":") + current_path = current_app.split(':') current_path.reverse() else: current_path = None resolved_path = [] - ns_pattern = "" + ns_pattern = '' ns_converters = {} for ns in path: current_ns = current_path.pop() if current_path else None @@ -75,15 +75,13 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None): except KeyError as key: if resolved_path: raise NoReverseMatch( - "%s is not a registered namespace inside '%s'" - % (key, ":".join(resolved_path)) + "%s is not a registered namespace inside '%s'" % + (key, ':'.join(resolved_path)) ) else: raise NoReverseMatch("%s is not a registered namespace" % key) if ns_pattern: - resolver = get_ns_resolver( - ns_pattern, resolver, tuple(ns_converters.items()) - ) + resolver = get_ns_resolver(ns_pattern, resolver, tuple(ns_converters.items())) return resolver._reverse_with_prefix(view, prefix, *args, **kwargs) @@ -101,8 +99,8 @@ def set_script_prefix(prefix): """ Set the script prefix for the current thread. """ - if not prefix.endswith("/"): - prefix += "/" + if not prefix.endswith('/'): + prefix += '/' _prefixes.value = prefix @@ -112,7 +110,7 @@ def get_script_prefix(): wishes to construct their own URLs manually (although accessing the request instance is normally going to be a lot cleaner). """ - return getattr(_prefixes, "value", "/") + return getattr(_prefixes, "value", '/') def clear_script_prefix(): @@ -170,18 +168,12 @@ def translate_url(url, lang_code): except Resolver404: pass else: - to_be_reversed = ( - "%s:%s" % (match.namespace, match.url_name) - if match.namespace - else match.url_name - ) + to_be_reversed = "%s:%s" % (match.namespace, match.url_name) if match.namespace else match.url_name with override(lang_code): try: url = reverse(to_be_reversed, args=match.args, kwargs=match.kwargs) except NoReverseMatch: pass else: - url = urlunsplit( - (parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment) - ) + url = urlunsplit((parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment)) return url diff --git a/venv/Lib/site-packages/django/urls/conf.py b/venv/Lib/site-packages/django/urls/conf.py index a7ae349..119e95d 100644 --- a/venv/Lib/site-packages/django/urls/conf.py +++ b/venv/Lib/site-packages/django/urls/conf.py @@ -5,11 +5,7 @@ from importlib import import_module from django.core.exceptions import ImproperlyConfigured from .resolvers import ( - LocalePrefixPattern, - RegexPattern, - RoutePattern, - URLPattern, - URLResolver, + LocalePrefixPattern, RegexPattern, RoutePattern, URLPattern, URLResolver, ) @@ -22,13 +18,13 @@ def include(arg, namespace=None): except ValueError: if namespace: raise ImproperlyConfigured( - "Cannot override the namespace for a dynamic module that " - "provides a namespace." + 'Cannot override the namespace for a dynamic module that ' + 'provides a namespace.' ) raise ImproperlyConfigured( - "Passing a %d-tuple to include() is not supported. Pass a " - "2-tuple containing the list of patterns and app_name, and " - "provide the namespace argument to include() instead." % len(arg) + 'Passing a %d-tuple to include() is not supported. Pass a ' + '2-tuple containing the list of patterns and app_name, and ' + 'provide the namespace argument to include() instead.' % len(arg) ) else: # No namespace hint - use manually provided namespace. @@ -36,31 +32,29 @@ def include(arg, namespace=None): if isinstance(urlconf_module, str): urlconf_module = import_module(urlconf_module) - patterns = getattr(urlconf_module, "urlpatterns", urlconf_module) - app_name = getattr(urlconf_module, "app_name", app_name) + patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) + app_name = getattr(urlconf_module, 'app_name', app_name) if namespace and not app_name: raise ImproperlyConfigured( - "Specifying a namespace in include() without providing an app_name " - "is not supported. Set the app_name attribute in the included " - "module, or pass a 2-tuple containing the list of patterns and " - "app_name instead.", + 'Specifying a namespace in include() without providing an app_name ' + 'is not supported. Set the app_name attribute in the included ' + 'module, or pass a 2-tuple containing the list of patterns and ' + 'app_name instead.', ) namespace = namespace or app_name # Make sure the patterns can be iterated through (without this, some # testcases will break). if isinstance(patterns, (list, tuple)): for url_pattern in patterns: - pattern = getattr(url_pattern, "pattern", None) + pattern = getattr(url_pattern, 'pattern', None) if isinstance(pattern, LocalePrefixPattern): raise ImproperlyConfigured( - "Using i18n_patterns in an included URLconf is not allowed." + 'Using i18n_patterns in an included URLconf is not allowed.' ) return (urlconf_module, app_name, namespace) def _path(route, view, kwargs=None, name=None, Pattern=None): - from django.views import View - if isinstance(view, (list, tuple)): # For include(...) processing. pattern = Pattern(route, is_endpoint=False) @@ -75,16 +69,8 @@ def _path(route, view, kwargs=None, name=None, Pattern=None): elif callable(view): pattern = Pattern(route, name=name, is_endpoint=True) return URLPattern(pattern, view, kwargs, name) - elif isinstance(view, View): - view_cls_name = view.__class__.__name__ - raise TypeError( - f"view must be a callable, pass {view_cls_name}.as_view(), not " - f"{view_cls_name}()." - ) else: - raise TypeError( - "view must be a callable or a list/tuple in the case of include()." - ) + raise TypeError('view must be a callable or a list/tuple in the case of include().') path = partial(_path, Pattern=RoutePattern) diff --git a/venv/Lib/site-packages/django/urls/converters.py b/venv/Lib/site-packages/django/urls/converters.py index 8af3cba..bb8478e 100644 --- a/venv/Lib/site-packages/django/urls/converters.py +++ b/venv/Lib/site-packages/django/urls/converters.py @@ -3,7 +3,7 @@ from functools import lru_cache class IntConverter: - regex = "[0-9]+" + regex = '[0-9]+' def to_python(self, value): return int(value) @@ -13,7 +13,7 @@ class IntConverter: class StringConverter: - regex = "[^/]+" + regex = '[^/]+' def to_python(self, value): return value @@ -23,7 +23,7 @@ class StringConverter: class UUIDConverter: - regex = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + regex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' def to_python(self, value): return uuid.UUID(value) @@ -33,19 +33,19 @@ class UUIDConverter: class SlugConverter(StringConverter): - regex = "[-a-zA-Z0-9_]+" + regex = '[-a-zA-Z0-9_]+' class PathConverter(StringConverter): - regex = ".+" + regex = '.+' DEFAULT_CONVERTERS = { - "int": IntConverter(), - "path": PathConverter(), - "slug": SlugConverter(), - "str": StringConverter(), - "uuid": UUIDConverter(), + 'int': IntConverter(), + 'path': PathConverter(), + 'slug': SlugConverter(), + 'str': StringConverter(), + 'uuid': UUIDConverter(), } diff --git a/venv/Lib/site-packages/django/urls/resolvers.py b/venv/Lib/site-packages/django/urls/resolvers.py index 26c9884..9b00e24 100644 --- a/venv/Lib/site-packages/django/urls/resolvers.py +++ b/venv/Lib/site-packages/django/urls/resolvers.py @@ -10,7 +10,6 @@ import inspect import re import string from importlib import import_module -from pickle import PicklingError from urllib.parse import quote from asgiref.local import Local @@ -31,17 +30,7 @@ from .utils import get_callable class ResolverMatch: - def __init__( - self, - func, - args, - kwargs, - url_name=None, - app_names=None, - namespaces=None, - route=None, - tried=None, - ): + def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None, route=None, tried=None): self.func = func self.args = args self.kwargs = kwargs @@ -52,47 +41,29 @@ class ResolverMatch: # If a URLRegexResolver doesn't have a namespace or app_name, it passes # in an empty value. self.app_names = [x for x in app_names if x] if app_names else [] - self.app_name = ":".join(self.app_names) + self.app_name = ':'.join(self.app_names) self.namespaces = [x for x in namespaces if x] if namespaces else [] - self.namespace = ":".join(self.namespaces) + self.namespace = ':'.join(self.namespaces) - if hasattr(func, "view_class"): - func = func.view_class - if not hasattr(func, "__name__"): + if not hasattr(func, '__name__'): # A class-based view - self._func_path = func.__class__.__module__ + "." + func.__class__.__name__ + self._func_path = func.__class__.__module__ + '.' + func.__class__.__name__ else: # A function-based view - self._func_path = func.__module__ + "." + func.__name__ + self._func_path = func.__module__ + '.' + func.__name__ view_path = url_name or self._func_path - self.view_name = ":".join(self.namespaces + [view_path]) + self.view_name = ':'.join(self.namespaces + [view_path]) def __getitem__(self, index): return (self.func, self.args, self.kwargs)[index] def __repr__(self): - if isinstance(self.func, functools.partial): - func = repr(self.func) - else: - func = self._func_path - return ( - "ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, " - "app_names=%r, namespaces=%r, route=%r)" - % ( - func, - self.args, - self.kwargs, - self.url_name, - self.app_names, - self.namespaces, - self.route, - ) + return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s, route=%s)" % ( + self._func_path, self.args, self.kwargs, self.url_name, + self.app_names, self.namespaces, self.route, ) - def __reduce_ex__(self, protocol): - raise PicklingError(f"Cannot pickle {self.__class__.__qualname__}.") - def get_resolver(urlconf=None): if urlconf is None: @@ -102,7 +73,7 @@ def get_resolver(urlconf=None): @functools.lru_cache(maxsize=None) def _get_cached_resolver(urlconf=None): - return URLResolver(RegexPattern(r"^/"), urlconf) + return URLResolver(RegexPattern(r'^/'), urlconf) @functools.lru_cache(maxsize=None) @@ -113,7 +84,7 @@ def get_ns_resolver(ns_pattern, resolver, converters): pattern = RegexPattern(ns_pattern) pattern.converters = dict(converters) ns_resolver = URLResolver(pattern, resolver.url_patterns) - return URLResolver(RegexPattern(r"^/"), [ns_resolver]) + return URLResolver(RegexPattern(r'^/'), [ns_resolver]) class LocaleRegexDescriptor: @@ -131,8 +102,8 @@ class LocaleRegexDescriptor: # avoid per-language compilation. pattern = getattr(instance, self.attr) if isinstance(pattern, str): - instance.__dict__["regex"] = instance._compile(pattern) - return instance.__dict__["regex"] + instance.__dict__['regex'] = instance._compile(pattern) + return instance.__dict__['regex'] language_code = get_language() if language_code not in instance._regex_dict: instance._regex_dict[language_code] = instance._compile(str(pattern)) @@ -158,9 +129,7 @@ class CheckURLMixin: # Skip check as it can be useful to start a URL pattern with a slash # when APPEND_SLASH=False. return [] - if regex_pattern.startswith(("/", "^/", "^\\/")) and not regex_pattern.endswith( - "/" - ): + if regex_pattern.startswith(('/', '^/', '^\\/')) and not regex_pattern.endswith('/'): warning = Warning( "Your URL pattern {} has a route beginning with a '/'. Remove this " "slash as it is unnecessary. If this pattern is targeted in an " @@ -175,7 +144,7 @@ class CheckURLMixin: class RegexPattern(CheckURLMixin): - regex = LocaleRegexDescriptor("_regex") + regex = LocaleRegexDescriptor('_regex') def __init__(self, regex, name=None, is_endpoint=False): self._regex = regex @@ -185,11 +154,7 @@ class RegexPattern(CheckURLMixin): self.converters = {} def match(self, path): - match = ( - self.regex.fullmatch(path) - if self._is_endpoint and self.regex.pattern.endswith("$") - else self.regex.search(path) - ) + match = self.regex.search(path) if match: # If there are any named groups, use those as kwargs, ignoring # non-named groups. Otherwise, pass all non-named arguments as @@ -197,7 +162,7 @@ class RegexPattern(CheckURLMixin): kwargs = match.groupdict() args = () if kwargs else match.groups() kwargs = {k: v for k, v in kwargs.items() if v is not None} - return path[match.end() :], args, kwargs + return path[match.end():], args, kwargs return None def check(self): @@ -209,15 +174,13 @@ class RegexPattern(CheckURLMixin): def _check_include_trailing_dollar(self): regex_pattern = self.regex.pattern - if regex_pattern.endswith("$") and not regex_pattern.endswith(r"\$"): - return [ - Warning( - "Your URL pattern {} uses include with a route ending with a '$'. " - "Remove the dollar from the route to avoid problems including " - "URLs.".format(self.describe()), - id="urls.W001", - ) - ] + if regex_pattern.endswith('$') and not regex_pattern.endswith(r'\$'): + return [Warning( + "Your URL pattern {} uses include with a route ending with a '$'. " + "Remove the dollar from the route to avoid problems including " + "URLs.".format(self.describe()), + id='urls.W001', + )] else: return [] @@ -235,7 +198,7 @@ class RegexPattern(CheckURLMixin): _PATH_PARAMETER_COMPONENT_RE = _lazy_re_compile( - r"<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>" + r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>' ) @@ -247,7 +210,7 @@ def _route_to_regex(route, is_endpoint=False): and {'pk': <django.urls.converters.IntConverter>}. """ original_route = route - parts = ["^"] + parts = ['^'] converters = {} while True: match = _PATH_PARAMETER_COMPONENT_RE.search(route) @@ -259,34 +222,34 @@ def _route_to_regex(route, is_endpoint=False): "URL route '%s' cannot contain whitespace in angle brackets " "<…>." % original_route ) - parts.append(re.escape(route[: match.start()])) - route = route[match.end() :] - parameter = match["parameter"] + parts.append(re.escape(route[:match.start()])) + route = route[match.end():] + parameter = match['parameter'] if not parameter.isidentifier(): raise ImproperlyConfigured( "URL route '%s' uses parameter name %r which isn't a valid " "Python identifier." % (original_route, parameter) ) - raw_converter = match["converter"] + raw_converter = match['converter'] if raw_converter is None: # If a converter isn't specified, the default is `str`. - raw_converter = "str" + raw_converter = 'str' try: converter = get_converter(raw_converter) except KeyError as e: raise ImproperlyConfigured( - "URL route %r uses invalid converter %r." + 'URL route %r uses invalid converter %r.' % (original_route, raw_converter) ) from e converters[parameter] = converter - parts.append("(?P<" + parameter + ">" + converter.regex + ")") + parts.append('(?P<' + parameter + '>' + converter.regex + ')') if is_endpoint: - parts.append(r"\Z") - return "".join(parts), converters + parts.append('$') + return ''.join(parts), converters class RoutePattern(CheckURLMixin): - regex = LocaleRegexDescriptor("_route") + regex = LocaleRegexDescriptor('_route') def __init__(self, route, name=None, is_endpoint=False): self._route = route @@ -306,21 +269,19 @@ class RoutePattern(CheckURLMixin): kwargs[key] = converter.to_python(value) except ValueError: return None - return path[match.end() :], (), kwargs + return path[match.end():], (), kwargs return None def check(self): warnings = self._check_pattern_startswith_slash() route = self._route - if "(?P<" in route or route.startswith("^") or route.endswith("$"): - warnings.append( - Warning( - "Your URL pattern {} has a route that contains '(?P<', begins " - "with a '^', or ends with a '$'. This was likely an oversight " - "when migrating to django.urls.path().".format(self.describe()), - id="2_0.W001", - ) - ) + if '(?P<' in route or route.startswith('^') or route.endswith('$'): + warnings.append(Warning( + "Your URL pattern {} has a route that contains '(?P<', begins " + "with a '^', or ends with a '$'. This was likely an oversight " + "when migrating to django.urls.path().".format(self.describe()), + id='2_0.W001', + )) return warnings def _compile(self, route): @@ -344,14 +305,14 @@ class LocalePrefixPattern: def language_prefix(self): language_code = get_language() or settings.LANGUAGE_CODE if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language: - return "" + return '' else: - return "%s/" % language_code + return '%s/' % language_code def match(self, path): language_prefix = self.language_prefix if path.startswith(language_prefix): - return path[len(language_prefix) :], (), {} + return path[len(language_prefix):], (), {} return None def check(self): @@ -372,12 +333,11 @@ class URLPattern: self.name = name def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.pattern.describe()) + return '<%s %s>' % (self.__class__.__name__, self.pattern.describe()) def check(self): warnings = self._check_pattern_name() warnings.extend(self.pattern.check()) - warnings.extend(self._check_callback()) return warnings def _check_pattern_name(self): @@ -394,34 +354,13 @@ class URLPattern: else: return [] - def _check_callback(self): - from django.views import View - - view = self.callback - if inspect.isclass(view) and issubclass(view, View): - return [ - Error( - "Your URL pattern %s has an invalid view, pass %s.as_view() " - "instead of %s." - % ( - self.pattern.describe(), - view.__name__, - view.__name__, - ), - id="urls.E009", - ) - ] - return [] - def resolve(self, path): match = self.pattern.match(path) if match: new_path, args, kwargs = match # Pass any extra_kwargs as **kwargs. kwargs.update(self.default_args) - return ResolverMatch( - self.callback, args, kwargs, self.pattern.name, route=str(self.pattern) - ) + return ResolverMatch(self.callback, args, kwargs, self.pattern.name, route=str(self.pattern)) @cached_property def lookup_str(self): @@ -432,17 +371,13 @@ class URLPattern: callback = self.callback if isinstance(callback, functools.partial): callback = callback.func - if hasattr(callback, "view_class"): - callback = callback.view_class - elif not hasattr(callback, "__name__"): + if not hasattr(callback, '__name__'): return callback.__module__ + "." + callback.__class__.__name__ return callback.__module__ + "." + callback.__qualname__ class URLResolver: - def __init__( - self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None - ): + def __init__(self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None): self.pattern = pattern # urlconf_name is the dotted Python path to the module defining # urlpatterns. It may also be an object with an urlpatterns attribute @@ -464,15 +399,12 @@ class URLResolver: def __repr__(self): if isinstance(self.urlconf_name, list) and self.urlconf_name: # Don't bother to output the whole list, it can be huge - urlconf_repr = "<%s list>" % self.urlconf_name[0].__class__.__name__ + urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__ else: urlconf_repr = repr(self.urlconf_name) - return "<%s %s (%s:%s) %s>" % ( - self.__class__.__name__, - urlconf_repr, - self.app_name, - self.namespace, - self.pattern.describe(), + return '<%s %s (%s:%s) %s>' % ( + self.__class__.__name__, urlconf_repr, self.app_name, + self.namespace, self.pattern.describe(), ) def check(self): @@ -490,12 +422,11 @@ class URLResolver: try: handler = self.resolve_error_handler(status_code) except (ImportError, ViewDoesNotExist) as e: - path = getattr(self.urlconf_module, "handler%s" % status_code) + path = getattr(self.urlconf_module, 'handler%s' % status_code) msg = ( - "The custom handler{status_code} view '{path}' could not be " - "imported." + "The custom handler{status_code} view '{path}' could not be imported." ).format(status_code=status_code, path=path) - messages.append(Error(msg, hint=str(e), id="urls.E008")) + messages.append(Error(msg, hint=str(e), id='urls.E008')) continue signature = inspect.signature(handler) args = [None] * num_parameters @@ -507,10 +438,10 @@ class URLResolver: "take the correct number of arguments ({args})." ).format( status_code=status_code, - path=handler.__module__ + "." + handler.__qualname__, - args="request, exception" if num_parameters == 2 else "request", + path=handler.__module__ + '.' + handler.__qualname__, + args='request, exception' if num_parameters == 2 else 'request', ) - messages.append(Error(msg, id="urls.E007")) + messages.append(Error(msg, id='urls.E007')) return messages def _populate(self): @@ -518,7 +449,7 @@ class URLResolver: # infinite recursion. Concurrent threads may call this at the same # time and will need to continue, so set 'populating' on a # thread-local variable. - if getattr(self._local, "populating", False): + if getattr(self._local, 'populating', False): return try: self._local.populating = True @@ -528,45 +459,28 @@ class URLResolver: language_code = get_language() for url_pattern in reversed(self.url_patterns): p_pattern = url_pattern.pattern.regex.pattern - if p_pattern.startswith("^"): + if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(url_pattern, URLPattern): self._callback_strs.add(url_pattern.lookup_str) bits = normalize(url_pattern.pattern.regex.pattern) lookups.appendlist( url_pattern.callback, - ( - bits, - p_pattern, - url_pattern.default_args, - url_pattern.pattern.converters, - ), + (bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters) ) if url_pattern.name is not None: lookups.appendlist( url_pattern.name, - ( - bits, - p_pattern, - url_pattern.default_args, - url_pattern.pattern.converters, - ), + (bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters) ) else: # url_pattern is a URLResolver. url_pattern._populate() if url_pattern.app_name: - apps.setdefault(url_pattern.app_name, []).append( - url_pattern.namespace - ) + apps.setdefault(url_pattern.app_name, []).append(url_pattern.namespace) namespaces[url_pattern.namespace] = (p_pattern, url_pattern) else: for name in url_pattern.reverse_dict: - for ( - matches, - pat, - defaults, - converters, - ) in url_pattern.reverse_dict.getlist(name): + for matches, pat, defaults, converters in url_pattern.reverse_dict.getlist(name): new_matches = normalize(p_pattern + pat) lookups.appendlist( name, @@ -574,17 +488,10 @@ class URLResolver: new_matches, p_pattern + pat, {**defaults, **url_pattern.default_kwargs}, - { - **self.pattern.converters, - **url_pattern.pattern.converters, - **converters, - }, - ), + {**self.pattern.converters, **url_pattern.pattern.converters, **converters} + ) ) - for namespace, ( - prefix, - sub_pattern, - ) in url_pattern.namespace_dict.items(): + for namespace, (prefix, sub_pattern) in url_pattern.namespace_dict.items(): current_converters = url_pattern.pattern.converters sub_pattern.pattern.converters.update(current_converters) namespaces[namespace] = (p_pattern + prefix, sub_pattern) @@ -631,7 +538,7 @@ class URLResolver: """Join two routes, without the starting ^ in the second route.""" if not route1: return route2 - if route2.startswith("^"): + if route2.startswith('^'): route2 = route2[1:] return route1 + route2 @@ -650,7 +557,7 @@ class URLResolver: try: sub_match = pattern.resolve(new_path) except Resolver404 as e: - self._extend_tried(tried, pattern, e.args[0].get("tried")) + self._extend_tried(tried, pattern, e.args[0].get('tried')) else: if sub_match: # Merge captured arguments in match with submatch @@ -658,16 +565,11 @@ class URLResolver: # Update the sub_match_dict with the kwargs from the sub_match. sub_match_dict.update(sub_match.kwargs) # If there are *any* named groups, ignore all non-named groups. - # Otherwise, pass all non-named arguments as positional - # arguments. + # Otherwise, pass all non-named arguments as positional arguments. sub_match_args = sub_match.args if not sub_match_dict: sub_match_args = args + sub_match.args - current_route = ( - "" - if isinstance(pattern, URLPattern) - else str(pattern.pattern) - ) + current_route = '' if isinstance(pattern, URLPattern) else str(pattern.pattern) self._extend_tried(tried, pattern, sub_match.tried) return ResolverMatch( sub_match.func, @@ -680,8 +582,8 @@ class URLResolver: tried, ) tried.append([pattern]) - raise Resolver404({"tried": tried, "path": new_path}) - raise Resolver404({"path": path}) + raise Resolver404({'tried': tried, 'path': new_path}) + raise Resolver404({'path': path}) @cached_property def urlconf_module(self): @@ -698,26 +600,24 @@ class URLResolver: iter(patterns) except TypeError as e: msg = ( - "The included URLconf '{name}' does not appear to have " - "any patterns in it. If you see the 'urlpatterns' variable " - "with valid patterns in the file then the issue is probably " - "caused by a circular import." + "The included URLconf '{name}' does not appear to have any " + "patterns in it. If you see valid patterns in the file then " + "the issue is probably caused by a circular import." ) raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e return patterns def resolve_error_handler(self, view_type): - callback = getattr(self.urlconf_module, "handler%s" % view_type, None) + callback = getattr(self.urlconf_module, 'handler%s' % view_type, None) if not callback: # No handler specified in file; use lazy import, since # django.conf.urls imports this file. from django.conf import urls - - callback = getattr(urls, "handler%s" % view_type) + callback = getattr(urls, 'handler%s' % view_type) return get_callable(callback) def reverse(self, lookup_view, *args, **kwargs): - return self._reverse_with_prefix(lookup_view, "", *args, **kwargs) + return self._reverse_with_prefix(lookup_view, '', *args, **kwargs) def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: @@ -759,22 +659,16 @@ class URLResolver: # without quoting to build a decoded URL and look for a match. # Then, if we have a match, redo the substitution with quoted # arguments in order to return a properly encoded URL. - candidate_pat = _prefix.replace("%", "%%") + result - if re.search( - "^%s%s" % (re.escape(_prefix), pattern), - candidate_pat % text_candidate_subs, - ): + candidate_pat = _prefix.replace('%', '%%') + result + if re.search('^%s%s' % (re.escape(_prefix), pattern), candidate_pat % text_candidate_subs): # safe characters from `pchar` definition of RFC 3986 - url = quote( - candidate_pat % text_candidate_subs, - safe=RFC3986_SUBDELIMS + "/~:@", - ) + url = quote(candidate_pat % text_candidate_subs, safe=RFC3986_SUBDELIMS + '/~:@') # Don't allow construction of scheme relative urls. return escape_leading_slashes(url) # lookup_view can be URL name or callable, but callables are not # friendly in error messages. - m = getattr(lookup_view, "__module__", None) - n = getattr(lookup_view, "__name__", None) + m = getattr(lookup_view, '__module__', None) + n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: @@ -788,15 +682,13 @@ class URLResolver: arg_msg = "keyword arguments '%s'" % kwargs else: arg_msg = "no arguments" - msg = "Reverse for '%s' with %s not found. %d pattern(s) tried: %s" % ( - lookup_view_s, - arg_msg, - len(patterns), - patterns, + msg = ( + "Reverse for '%s' with %s not found. %d pattern(s) tried: %s" % + (lookup_view_s, arg_msg, len(patterns), patterns) ) else: msg = ( "Reverse for '%(view)s' not found. '%(view)s' is not " - "a valid view function or pattern name." % {"view": lookup_view_s} + "a valid view function or pattern name." % {'view': lookup_view_s} ) raise NoReverseMatch(msg) diff --git a/venv/Lib/site-packages/django/urls/utils.py b/venv/Lib/site-packages/django/urls/utils.py index 60b46d9..e59ab9f 100644 --- a/venv/Lib/site-packages/django/urls/utils.py +++ b/venv/Lib/site-packages/django/urls/utils.py @@ -18,15 +18,11 @@ def get_callable(lookup_view): return lookup_view if not isinstance(lookup_view, str): - raise ViewDoesNotExist( - "'%s' is not a callable or a dot-notation path" % lookup_view - ) + raise ViewDoesNotExist("'%s' is not a callable or a dot-notation path" % lookup_view) mod_name, func_name = get_mod_func(lookup_view) if not func_name: # No '.' in lookup_view - raise ImportError( - "Could not import '%s'. The path must be fully qualified." % lookup_view - ) + raise ImportError("Could not import '%s'. The path must be fully qualified." % lookup_view) try: mod = import_module(mod_name) @@ -34,8 +30,8 @@ def get_callable(lookup_view): parentmod, submod = get_mod_func(mod_name) if submod and not module_has_submodule(import_module(parentmod), submod): raise ViewDoesNotExist( - "Could not import '%s'. Parent module %s does not exist." - % (lookup_view, mod_name) + "Could not import '%s'. Parent module %s does not exist." % + (lookup_view, mod_name) ) else: raise @@ -44,14 +40,14 @@ def get_callable(lookup_view): view_func = getattr(mod, func_name) except AttributeError: raise ViewDoesNotExist( - "Could not import '%s'. View does not exist in module %s." - % (lookup_view, mod_name) + "Could not import '%s'. View does not exist in module %s." % + (lookup_view, mod_name) ) else: if not callable(view_func): raise ViewDoesNotExist( - "Could not import '%s.%s'. View is not callable." - % (mod_name, func_name) + "Could not import '%s.%s'. View is not callable." % + (mod_name, func_name) ) return view_func @@ -60,7 +56,7 @@ def get_mod_func(callback): # Convert 'django.views.news.stories.story_detail' to # ['django.views.news.stories', 'story_detail'] try: - dot = callback.rindex(".") + dot = callback.rindex('.') except ValueError: - return callback, "" - return callback[:dot], callback[dot + 1 :] + return callback, '' + return callback[:dot], callback[dot + 1:] diff --git a/venv/Lib/site-packages/django/utils/_os.py b/venv/Lib/site-packages/django/utils/_os.py index f85fe09..b2a2a9d 100644 --- a/venv/Lib/site-packages/django/utils/_os.py +++ b/venv/Lib/site-packages/django/utils/_os.py @@ -23,15 +23,12 @@ def safe_join(base, *paths): # safe_join("/dir", "/../d")) # b) The final path must be the same as the base path. # c) The base path must be the most root path (meaning either "/" or "C:\\") - if ( - not normcase(final_path).startswith(normcase(base_path + sep)) - and normcase(final_path) != normcase(base_path) - and dirname(normcase(base_path)) != normcase(base_path) - ): + if (not normcase(final_path).startswith(normcase(base_path + sep)) and + normcase(final_path) != normcase(base_path) and + dirname(normcase(base_path)) != normcase(base_path)): raise SuspiciousFileOperation( - "The joined path ({}) is located outside of the base path " - "component ({})".format(final_path, base_path) - ) + 'The joined path ({}) is located outside of the base path ' + 'component ({})'.format(final_path, base_path)) return final_path @@ -42,8 +39,8 @@ def symlinks_supported(): permissions). """ with tempfile.TemporaryDirectory() as temp_dir: - original_path = os.path.join(temp_dir, "original") - symlink_path = os.path.join(temp_dir, "symlink") + original_path = os.path.join(temp_dir, 'original') + symlink_path = os.path.join(temp_dir, 'symlink') os.makedirs(original_path) try: os.symlink(original_path, symlink_path) @@ -58,5 +55,5 @@ def to_path(value): if isinstance(value, Path): return value elif not isinstance(value, str): - raise TypeError("Invalid path type: %s" % type(value).__name__) + raise TypeError('Invalid path type: %s' % type(value).__name__) return Path(value) diff --git a/venv/Lib/site-packages/django/utils/archive.py b/venv/Lib/site-packages/django/utils/archive.py index 71ec2d0..d5a0cf0 100644 --- a/venv/Lib/site-packages/django/utils/archive.py +++ b/venv/Lib/site-packages/django/utils/archive.py @@ -55,7 +55,6 @@ class Archive: """ The external API class that encapsulates an archive implementation. """ - def __init__(self, file): self._archive = self._archive_cls(file)(file) @@ -69,8 +68,7 @@ class Archive: filename = file.name except AttributeError: raise UnrecognizedArchiveFormat( - "File object not a recognized archive format." - ) + "File object not a recognized archive format.") base, tail_ext = os.path.splitext(filename.lower()) cls = extension_map.get(tail_ext) if not cls: @@ -78,8 +76,7 @@ class Archive: cls = extension_map.get(ext) if not cls: raise UnrecognizedArchiveFormat( - "Path not a recognized archive format: %s" % filename - ) + "Path not a recognized archive format: %s" % filename) return cls def __enter__(self): @@ -102,7 +99,6 @@ class BaseArchive: """ Base Archive class. Implementations should inherit this class. """ - @staticmethod def _copy_permissions(mode, filename): """ @@ -115,15 +111,13 @@ class BaseArchive: def split_leading_dir(self, path): path = str(path) - path = path.lstrip("/").lstrip("\\") - if "/" in path and ( - ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path - ): - return path.split("/", 1) - elif "\\" in path: - return path.split("\\", 1) + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) else: - return path, "" + return path, '' def has_leading_dir(self, paths): """ @@ -149,17 +143,14 @@ class BaseArchive: return filename def extract(self): - raise NotImplementedError( - "subclasses of BaseArchive must provide an extract() method" - ) + raise NotImplementedError('subclasses of BaseArchive must provide an extract() method') def list(self): - raise NotImplementedError( - "subclasses of BaseArchive must provide a list() method" - ) + raise NotImplementedError('subclasses of BaseArchive must provide a list() method') class TarArchive(BaseArchive): + def __init__(self, file): self._archive = tarfile.open(file) @@ -183,15 +174,13 @@ class TarArchive(BaseArchive): except (KeyError, AttributeError) as exc: # Some corrupt tar files seem to produce this # (specifically bad symlinks) - print( - "In the tar file %s the member %s is invalid: %s" - % (name, member.name, exc) - ) + print("In the tar file %s the member %s is invalid: %s" % + (name, member.name, exc)) else: dirname = os.path.dirname(filename) if dirname: os.makedirs(dirname, exist_ok=True) - with open(filename, "wb") as outfile: + with open(filename, 'wb') as outfile: shutil.copyfileobj(extracted, outfile) self._copy_permissions(member.mode, filename) finally: @@ -203,6 +192,7 @@ class TarArchive(BaseArchive): class ZipArchive(BaseArchive): + def __init__(self, file): self._archive = zipfile.ZipFile(file) @@ -220,14 +210,14 @@ class ZipArchive(BaseArchive): if not name: continue filename = self.target_filename(to_path, name) - if name.endswith(("/", "\\")): + if name.endswith(('/', '\\')): # A directory os.makedirs(filename, exist_ok=True) else: dirname = os.path.dirname(filename) if dirname: os.makedirs(dirname, exist_ok=True) - with open(filename, "wb") as outfile: + with open(filename, 'wb') as outfile: outfile.write(data) # Convert ZipInfo.external_attr to mode mode = info.external_attr >> 16 @@ -237,21 +227,11 @@ class ZipArchive(BaseArchive): self._archive.close() -extension_map = dict.fromkeys( - ( - ".tar", - ".tar.bz2", - ".tbz2", - ".tbz", - ".tz2", - ".tar.gz", - ".tgz", - ".taz", - ".tar.lzma", - ".tlz", - ".tar.xz", - ".txz", - ), - TarArchive, -) -extension_map[".zip"] = ZipArchive +extension_map = dict.fromkeys(( + '.tar', + '.tar.bz2', '.tbz2', '.tbz', '.tz2', + '.tar.gz', '.tgz', '.taz', + '.tar.lzma', '.tlz', + '.tar.xz', '.txz', +), TarArchive) +extension_map['.zip'] = ZipArchive diff --git a/venv/Lib/site-packages/django/utils/asyncio.py b/venv/Lib/site-packages/django/utils/asyncio.py index e7d6c6c..2405e34 100644 --- a/venv/Lib/site-packages/django/utils/asyncio.py +++ b/venv/Lib/site-packages/django/utils/asyncio.py @@ -10,30 +10,25 @@ def async_unsafe(message): Decorator to mark functions as async-unsafe. Someone trying to access the function while in an async context will get an error message. """ - def decorator(func): @functools.wraps(func) def inner(*args, **kwargs): - if not os.environ.get("DJANGO_ALLOW_ASYNC_UNSAFE"): + if not os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'): # Detect a running event loop in this thread. try: - asyncio.get_running_loop() + event_loop = asyncio.get_event_loop() except RuntimeError: pass else: - raise SynchronousOnlyOperation(message) - # Pass onward. + if event_loop.is_running(): + raise SynchronousOnlyOperation(message) + # Pass onwards. return func(*args, **kwargs) - return inner - # If the message is actually a function, then be a no-arguments decorator. if callable(message): func = message - message = ( - "You cannot call this from an async context - use a thread or " - "sync_to_async." - ) + message = 'You cannot call this from an async context - use a thread or sync_to_async.' return decorator(func) else: return decorator diff --git a/venv/Lib/site-packages/django/utils/autoreload.py b/venv/Lib/site-packages/django/utils/autoreload.py index 8873897..9d29654 100644 --- a/venv/Lib/site-packages/django/utils/autoreload.py +++ b/venv/Lib/site-packages/django/utils/autoreload.py @@ -24,9 +24,9 @@ from django.utils.version import get_version_tuple autoreload_started = Signal() file_changed = Signal() -DJANGO_AUTORELOAD_ENV = "RUN_MAIN" +DJANGO_AUTORELOAD_ENV = 'RUN_MAIN' -logger = logging.getLogger("django.utils.autoreload") +logger = logging.getLogger('django.utils.autoreload') # If an error is raised while importing a file, it's not placed in sys.modules. # This means that any future modifications aren't caught. Keep a list of these @@ -48,7 +48,7 @@ except ImportError: def is_django_module(module): """Return True if the given module is nested under Django.""" - return module.__name__.startswith("django.") + return module.__name__.startswith('django.') def is_django_path(path): @@ -67,7 +67,7 @@ def check_errors(fn): et, ev, tb = _exception - if getattr(ev, "filename", None) is None: + if getattr(ev, 'filename', None) is None: # get the filename from the last item in the stack filename = traceback.extract_tb(tb)[-1][0] else: @@ -97,7 +97,7 @@ def ensure_echo_on(): attr_list = termios.tcgetattr(sys.stdin) if not attr_list[3] & termios.ECHO: attr_list[3] |= termios.ECHO - if hasattr(signal, "SIGTTOU"): + if hasattr(signal, 'SIGTTOU'): old_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN) else: old_handler = None @@ -112,11 +112,7 @@ def iter_all_python_module_files(): # This ensures cached results are returned in the usual case that modules # aren't loaded on the fly. keys = sorted(sys.modules) - modules = tuple( - m - for m in map(sys.modules.__getitem__, keys) - if not isinstance(m, weakref.ProxyTypes) - ) + modules = tuple(m for m in map(sys.modules.__getitem__, keys) if not isinstance(m, weakref.ProxyTypes)) return iter_modules_and_files(modules, frozenset(_error_files)) @@ -130,25 +126,21 @@ def iter_modules_and_files(modules, extra_files): # cause issues here. if not isinstance(module, ModuleType): continue - if module.__name__ == "__main__": + if module.__name__ == '__main__': # __main__ (usually manage.py) doesn't always have a __spec__ set. # Handle this by falling back to using __file__, resolved below. # See https://docs.python.org/reference/import.html#main-spec # __file__ may not exists, e.g. when running ipdb debugger. - if hasattr(module, "__file__"): + if hasattr(module, '__file__'): sys_file_paths.append(module.__file__) continue - if getattr(module, "__spec__", None) is None: + if getattr(module, '__spec__', None) is None: continue spec = module.__spec__ # Modules could be loaded from places without a concrete location. If # this is the case, skip them. if spec.has_location: - origin = ( - spec.loader.archive - if isinstance(spec.loader, zipimporter) - else spec.origin - ) + origin = spec.loader.archive if isinstance(spec.loader, zipimporter) else spec.origin sys_file_paths.append(origin) results = set() @@ -225,50 +217,43 @@ def get_child_arguments(): on reloading. """ import __main__ - py_script = Path(sys.argv[0]) - args = [sys.executable] + ["-W%s" % o for o in sys.warnoptions] - if sys.implementation.name == "cpython": - args.extend( - f"-X{key}" if value is True else f"-X{key}={value}" - for key, value in sys._xoptions.items() - ) + args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] # __spec__ is set when the server was started with the `-m` option, # see https://docs.python.org/3/reference/import.html#main-spec # __spec__ may not exist, e.g. when running in a Conda env. - if getattr(__main__, "__spec__", None) is not None: - spec = __main__.__spec__ - if (spec.name == "__main__" or spec.name.endswith(".__main__")) and spec.parent: - name = spec.parent - else: - name = spec.name - args += ["-m", name] + if getattr(__main__, '__spec__', None) is not None and __main__.__spec__.parent: + args += ['-m', __main__.__spec__.parent] args += sys.argv[1:] elif not py_script.exists(): # sys.argv[0] may not exist for several reasons on Windows. # It may exist with a .exe extension or have a -script.py suffix. - exe_entrypoint = py_script.with_suffix(".exe") + exe_entrypoint = py_script.with_suffix('.exe') if exe_entrypoint.exists(): # Should be executed directly, ignoring sys.executable. - return [exe_entrypoint, *sys.argv[1:]] - script_entrypoint = py_script.with_name("%s-script.py" % py_script.name) + # TODO: Remove str() when dropping support for PY37. + # args parameter accepts path-like on Windows from Python 3.8. + return [str(exe_entrypoint), *sys.argv[1:]] + script_entrypoint = py_script.with_name('%s-script.py' % py_script.name) if script_entrypoint.exists(): # Should be executed as usual. - return [*args, script_entrypoint, *sys.argv[1:]] - raise RuntimeError("Script %s does not exist." % py_script) + # TODO: Remove str() when dropping support for PY37. + # args parameter accepts path-like on Windows from Python 3.8. + return [*args, str(script_entrypoint), *sys.argv[1:]] + raise RuntimeError('Script %s does not exist.' % py_script) else: args += sys.argv return args def trigger_reload(filename): - logger.info("%s changed, reloading.", filename) + logger.info('%s changed, reloading.', filename) sys.exit(3) def restart_with_reloader(): - new_environ = {**os.environ, DJANGO_AUTORELOAD_ENV: "true"} + new_environ = {**os.environ, DJANGO_AUTORELOAD_ENV: 'true'} args = get_child_arguments() while True: p = subprocess.run(args, env=new_environ, close_fds=False) @@ -288,12 +273,12 @@ class BaseReloader: path = path.absolute() except FileNotFoundError: logger.debug( - "Unable to watch directory %s as it cannot be resolved.", + 'Unable to watch directory %s as it cannot be resolved.', path, exc_info=True, ) return - logger.debug("Watching dir %s with glob %s.", path, glob) + logger.debug('Watching dir %s with glob %s.', path, glob) self.directory_globs[path].add(glob) def watched_files(self, include_globs=True): @@ -323,11 +308,11 @@ class BaseReloader: if app_reg.ready_event.wait(timeout=0.1): return True else: - logger.debug("Main Django thread has terminated before apps are ready.") + logger.debug('Main Django thread has terminated before apps are ready.') return False def run(self, django_main_thread): - logger.debug("Waiting for apps ready_event.") + logger.debug('Waiting for apps ready_event.') self.wait_for_apps_ready(apps, django_main_thread) from django.urls import get_resolver @@ -339,7 +324,7 @@ class BaseReloader: # Loading the urlconf can result in errors during development. # If this occurs then swallow the error and continue. pass - logger.debug("Apps ready_event triggered. Sending autoreload_started signal.") + logger.debug('Apps ready_event triggered. Sending autoreload_started signal.') autoreload_started.send(sender=self) self.run_loop() @@ -360,15 +345,15 @@ class BaseReloader: testability of the reloader implementations by decoupling the work they do from the loop. """ - raise NotImplementedError("subclasses must implement tick().") + raise NotImplementedError('subclasses must implement tick().') @classmethod def check_availability(cls): - raise NotImplementedError("subclasses must implement check_availability().") + raise NotImplementedError('subclasses must implement check_availability().') def notify_file_changed(self, path): results = file_changed.send(sender=self, file_path=path) - logger.debug("%s notified as changed. Signal results: %s.", path, results) + logger.debug('%s notified as changed. Signal results: %s.', path, results) if not any(res[1] for res in results): trigger_reload(path) @@ -391,15 +376,10 @@ class StatReloader(BaseReloader): old_time = mtimes.get(filepath) mtimes[filepath] = mtime if old_time is None: - logger.debug("File %s first seen with mtime %s", filepath, mtime) + logger.debug('File %s first seen with mtime %s', filepath, mtime) continue elif mtime > old_time: - logger.debug( - "File %s previous mtime: %s, current mtime: %s", - filepath, - old_time, - mtime, - ) + logger.debug('File %s previous mtime: %s, current mtime: %s', filepath, old_time, mtime) self.notify_file_changed(filepath) time.sleep(self.SLEEP_TIME) @@ -432,7 +412,7 @@ class WatchmanReloader(BaseReloader): def __init__(self): self.roots = defaultdict(set) self.processed_request = threading.Event() - self.client_timeout = int(os.environ.get("DJANGO_WATCHMAN_TIMEOUT", 5)) + self.client_timeout = int(os.environ.get('DJANGO_WATCHMAN_TIMEOUT', 5)) super().__init__() @cached_property @@ -451,63 +431,52 @@ class WatchmanReloader(BaseReloader): # now, watching its parent, if possible, is sufficient. if not root.exists(): if not root.parent.exists(): - logger.warning( - "Unable to watch root dir %s as neither it or its parent exist.", - root, - ) + logger.warning('Unable to watch root dir %s as neither it or its parent exist.', root) return root = root.parent - result = self.client.query("watch-project", str(root.absolute())) - if "warning" in result: - logger.warning("Watchman warning: %s", result["warning"]) - logger.debug("Watchman watch-project result: %s", result) - return result["watch"], result.get("relative_path") + result = self.client.query('watch-project', str(root.absolute())) + if 'warning' in result: + logger.warning('Watchman warning: %s', result['warning']) + logger.debug('Watchman watch-project result: %s', result) + return result['watch'], result.get('relative_path') @functools.lru_cache() def _get_clock(self, root): - return self.client.query("clock", root)["clock"] + return self.client.query('clock', root)['clock'] def _subscribe(self, directory, name, expression): root, rel_path = self._watch_root(directory) # Only receive notifications of files changing, filtering out other types # like special files: https://facebook.github.io/watchman/docs/type only_files_expression = [ - "allof", - ["anyof", ["type", "f"], ["type", "l"]], - expression, + 'allof', + ['anyof', ['type', 'f'], ['type', 'l']], + expression ] query = { - "expression": only_files_expression, - "fields": ["name"], - "since": self._get_clock(root), - "dedup_results": True, + 'expression': only_files_expression, + 'fields': ['name'], + 'since': self._get_clock(root), + 'dedup_results': True, } if rel_path: - query["relative_root"] = rel_path - logger.debug( - "Issuing watchman subscription %s, for root %s. Query: %s", - name, - root, - query, - ) - self.client.query("subscribe", root, name, query) + query['relative_root'] = rel_path + logger.debug('Issuing watchman subscription %s, for root %s. Query: %s', name, root, query) + self.client.query('subscribe', root, name, query) def _subscribe_dir(self, directory, filenames): if not directory.exists(): if not directory.parent.exists(): - logger.warning( - "Unable to watch directory %s as neither it or its parent exist.", - directory, - ) + logger.warning('Unable to watch directory %s as neither it or its parent exist.', directory) return - prefix = "files-parent-%s" % directory.name - filenames = ["%s/%s" % (directory.name, filename) for filename in filenames] + prefix = 'files-parent-%s' % directory.name + filenames = ['%s/%s' % (directory.name, filename) for filename in filenames] directory = directory.parent - expression = ["name", filenames, "wholename"] + expression = ['name', filenames, 'wholename'] else: - prefix = "files" - expression = ["name", filenames] - self._subscribe(directory, "%s:%s" % (prefix, directory), expression) + prefix = 'files' + expression = ['name', filenames] + self._subscribe(directory, '%s:%s' % (prefix, directory), expression) def _watch_glob(self, directory, patterns): """ @@ -518,22 +487,19 @@ class WatchmanReloader(BaseReloader): overwrite the named subscription, so it must include all possible glob expressions. """ - prefix = "glob" + prefix = 'glob' if not directory.exists(): if not directory.parent.exists(): - logger.warning( - "Unable to watch directory %s as neither it or its parent exist.", - directory, - ) + logger.warning('Unable to watch directory %s as neither it or its parent exist.', directory) return - prefix = "glob-parent-%s" % directory.name - patterns = ["%s/%s" % (directory.name, pattern) for pattern in patterns] + prefix = 'glob-parent-%s' % directory.name + patterns = ['%s/%s' % (directory.name, pattern) for pattern in patterns] directory = directory.parent - expression = ["anyof"] + expression = ['anyof'] for pattern in patterns: - expression.append(["match", pattern, "wholename"]) - self._subscribe(directory, "%s:%s" % (prefix, directory), expression) + expression.append(['match', pattern, 'wholename']) + self._subscribe(directory, '%s:%s' % (prefix, directory), expression) def watched_roots(self, watched_files): extra_directories = self.directory_globs.keys() @@ -544,8 +510,8 @@ class WatchmanReloader(BaseReloader): def _update_watches(self): watched_files = list(self.watched_files(include_globs=False)) found_roots = common_roots(self.watched_roots(watched_files)) - logger.debug("Watching %s files", len(watched_files)) - logger.debug("Found common roots: %s", found_roots) + logger.debug('Watching %s files', len(watched_files)) + logger.debug('Found common roots: %s', found_roots) # Setup initial roots for performance, shortest roots first. for root in sorted(found_roots): self._watch_root(root) @@ -555,9 +521,7 @@ class WatchmanReloader(BaseReloader): sorted_files = sorted(watched_files, key=lambda p: p.parent) for directory, group in itertools.groupby(sorted_files, key=lambda p: p.parent): # These paths need to be relative to the parent directory. - self._subscribe_dir( - directory, [str(p.relative_to(directory)) for p in group] - ) + self._subscribe_dir(directory, [str(p.relative_to(directory)) for p in group]) def update_watches(self): try: @@ -571,19 +535,19 @@ class WatchmanReloader(BaseReloader): subscription = self.client.getSubscription(sub) if not subscription: return - logger.debug("Watchman subscription %s has results.", sub) + logger.debug('Watchman subscription %s has results.', sub) for result in subscription: # When using watch-project, it's not simple to get the relative # directory without storing some specific state. Store the full # path to the directory in the subscription name, prefixed by its # type (glob, files). - root_directory = Path(result["subscription"].split(":", 1)[1]) - logger.debug("Found root directory %s", root_directory) - for file in result.get("files", []): + root_directory = Path(result['subscription'].split(':', 1)[1]) + logger.debug('Found root directory %s', root_directory) + for file in result.get('files', []): self.notify_file_changed(root_directory / file) def request_processed(self, **kwargs): - logger.debug("Request processed. Setting update_watches event.") + logger.debug('Request processed. Setting update_watches event.') self.processed_request.set() def tick(self): @@ -598,7 +562,7 @@ class WatchmanReloader(BaseReloader): except pywatchman.SocketTimeout: pass except pywatchman.WatchmanError as ex: - logger.debug("Watchman error: %s, checking server status.", ex) + logger.debug('Watchman error: %s, checking server status.', ex) self.check_server_status(ex) else: for sub in list(self.client.subs.keys()): @@ -614,7 +578,7 @@ class WatchmanReloader(BaseReloader): def check_server_status(self, inner_ex=None): """Return True if the server is available.""" try: - self.client.query("version") + self.client.query('version') except Exception: raise WatchmanUnavailable(str(inner_ex)) from inner_ex return True @@ -622,19 +586,19 @@ class WatchmanReloader(BaseReloader): @classmethod def check_availability(cls): if not pywatchman: - raise WatchmanUnavailable("pywatchman not installed.") + raise WatchmanUnavailable('pywatchman not installed.') client = pywatchman.client(timeout=0.1) try: result = client.capabilityCheck() except Exception: # The service is down? - raise WatchmanUnavailable("Cannot connect to the watchman service.") - version = get_version_tuple(result["version"]) + raise WatchmanUnavailable('Cannot connect to the watchman service.') + version = get_version_tuple(result['version']) # Watchman 4.9 includes multiple improvements to watching project # directories as well as case insensitive filesystems. - logger.debug("Watchman version %s", version) + logger.debug('Watchman version %s', version) if version < (4, 9): - raise WatchmanUnavailable("Watchman 4.9 or later is required.") + raise WatchmanUnavailable('Watchman 4.9 or later is required.') def get_reloader(): @@ -650,10 +614,8 @@ def start_django(reloader, main_func, *args, **kwargs): ensure_echo_on() main_func = check_errors(main_func) - django_main_thread = threading.Thread( - target=main_func, args=args, kwargs=kwargs, name="django-main-thread" - ) - django_main_thread.daemon = True + django_main_thread = threading.Thread(target=main_func, args=args, kwargs=kwargs, name='django-main-thread') + django_main_thread.setDaemon(True) django_main_thread.start() while not reloader.should_stop: @@ -663,20 +625,16 @@ def start_django(reloader, main_func, *args, **kwargs): # It's possible that the watchman service shuts down or otherwise # becomes unavailable. In that case, use the StatReloader. reloader = StatReloader() - logger.error("Error connecting to Watchman: %s", ex) - logger.info( - "Watching for file changes with %s", reloader.__class__.__name__ - ) + logger.error('Error connecting to Watchman: %s', ex) + logger.info('Watching for file changes with %s', reloader.__class__.__name__) def run_with_reloader(main_func, *args, **kwargs): signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) try: - if os.environ.get(DJANGO_AUTORELOAD_ENV) == "true": + if os.environ.get(DJANGO_AUTORELOAD_ENV) == 'true': reloader = get_reloader() - logger.info( - "Watching for file changes with %s", reloader.__class__.__name__ - ) + logger.info('Watching for file changes with %s', reloader.__class__.__name__) start_django(reloader, main_func, *args, **kwargs) else: exit_code = restart_with_reloader() diff --git a/venv/Lib/site-packages/django/utils/baseconv.py b/venv/Lib/site-packages/django/utils/baseconv.py index fcaab23..28099af 100644 --- a/venv/Lib/site-packages/django/utils/baseconv.py +++ b/venv/Lib/site-packages/django/utils/baseconv.py @@ -1,4 +1,3 @@ -# RemovedInDjango50Warning # Copyright (c) 2010 Guilherme Gondim. All rights reserved. # Copyright (c) 2009 Simon Willison. All rights reserved. # Copyright (c) 2002 Drew Perttula. All rights reserved. @@ -31,48 +30,35 @@ Sample usage:: >>> base20.decode('-31e') -1234 >>> base11 = BaseConverter('0123456789-', sign='$') - >>> base11.encode(-1234) + >>> base11.encode('$1234') '$-22' >>> base11.decode('$-22') - -1234 + '$1234' """ -import warnings -from django.utils.deprecation import RemovedInDjango50Warning - -warnings.warn( - "The django.utils.baseconv module is deprecated.", - category=RemovedInDjango50Warning, - stacklevel=2, -) - -BASE2_ALPHABET = "01" -BASE16_ALPHABET = "0123456789ABCDEF" -BASE56_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz" -BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz" -BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -BASE64_ALPHABET = BASE62_ALPHABET + "-_" +BASE2_ALPHABET = '01' +BASE16_ALPHABET = '0123456789ABCDEF' +BASE56_ALPHABET = '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz' +BASE36_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz' +BASE62_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +BASE64_ALPHABET = BASE62_ALPHABET + '-_' class BaseConverter: - decimal_digits = "0123456789" + decimal_digits = '0123456789' - def __init__(self, digits, sign="-"): + def __init__(self, digits, sign='-'): self.sign = sign self.digits = digits if sign in self.digits: - raise ValueError("Sign character found in converter base digits.") + raise ValueError('Sign character found in converter base digits.') def __repr__(self): - return "<%s: base%s (%s)>" % ( - self.__class__.__name__, - len(self.digits), - self.digits, - ) + return "<%s: base%s (%s)>" % (self.__class__.__name__, len(self.digits), self.digits) def encode(self, i): - neg, value = self.convert(i, self.decimal_digits, self.digits, "-") + neg, value = self.convert(i, self.decimal_digits, self.digits, '-') if neg: return self.sign + value return value @@ -80,7 +66,7 @@ class BaseConverter: def decode(self, s): neg, value = self.convert(s, self.digits, self.decimal_digits, self.sign) if neg: - value = "-" + value + value = '-' + value return int(value) def convert(self, number, from_digits, to_digits, sign): @@ -99,7 +85,7 @@ class BaseConverter: if x == 0: res = to_digits[0] else: - res = "" + res = '' while x > 0: digit = x % len(to_digits) res = to_digits[digit] + res @@ -112,4 +98,4 @@ base16 = BaseConverter(BASE16_ALPHABET) base36 = BaseConverter(BASE36_ALPHABET) base56 = BaseConverter(BASE56_ALPHABET) base62 = BaseConverter(BASE62_ALPHABET) -base64 = BaseConverter(BASE64_ALPHABET, sign="$") +base64 = BaseConverter(BASE64_ALPHABET, sign='$') diff --git a/venv/Lib/site-packages/django/utils/cache.py b/venv/Lib/site-packages/django/utils/cache.py index efea44f..bb756fe 100644 --- a/venv/Lib/site-packages/django/utils/cache.py +++ b/venv/Lib/site-packages/django/utils/cache.py @@ -23,13 +23,15 @@ from collections import defaultdict from django.conf import settings from django.core.cache import caches from django.http import HttpResponse, HttpResponseNotModified -from django.utils.http import http_date, parse_etags, parse_http_date_safe, quote_etag +from django.utils.http import ( + http_date, parse_etags, parse_http_date_safe, quote_etag, +) from django.utils.log import log_response from django.utils.regex_helper import _lazy_re_compile from django.utils.timezone import get_current_timezone_name from django.utils.translation import get_language -cc_delim_re = _lazy_re_compile(r"\s*,\s*") +cc_delim_re = _lazy_re_compile(r'\s*,\s*') def patch_cache_control(response, **kwargs): @@ -44,9 +46,8 @@ def patch_cache_control(response, **kwargs): * All other parameters are added with their value, after applying str() to it. """ - def dictitem(s): - t = s.split("=", 1) + t = s.split('=', 1) if len(t) > 1: return (t[0].lower(), t[1]) else: @@ -56,13 +57,13 @@ def patch_cache_control(response, **kwargs): if t[1] is True: return t[0] else: - return "%s=%s" % (t[0], t[1]) + return '%s=%s' % (t[0], t[1]) cc = defaultdict(set) - if response.get("Cache-Control"): - for field in cc_delim_re.split(response.headers["Cache-Control"]): + if response.get('Cache-Control'): + for field in cc_delim_re.split(response.headers['Cache-Control']): directive, value = dictitem(field) - if directive == "no-cache": + if directive == 'no-cache': # no-cache supports multiple field names. cc[directive].add(value) else: @@ -71,18 +72,18 @@ def patch_cache_control(response, **kwargs): # If there's already a max-age header but we're being asked to set a new # max-age, use the minimum of the two ages. In practice this happens when # a decorator and a piece of middleware both operate on a given view. - if "max-age" in cc and "max_age" in kwargs: - kwargs["max_age"] = min(int(cc["max-age"]), kwargs["max_age"]) + if 'max-age' in cc and 'max_age' in kwargs: + kwargs['max_age'] = min(int(cc['max-age']), kwargs['max_age']) # Allow overriding private caching and vice versa - if "private" in cc and "public" in kwargs: - del cc["private"] - elif "public" in cc and "private" in kwargs: - del cc["public"] + if 'private' in cc and 'public' in kwargs: + del cc['private'] + elif 'public' in cc and 'private' in kwargs: + del cc['public'] for (k, v) in kwargs.items(): - directive = k.replace("_", "-") - if directive == "no-cache": + directive = k.replace('_', '-') + if directive == 'no-cache': # no-cache supports multiple field names. cc[directive].add(v) else: @@ -97,8 +98,8 @@ def patch_cache_control(response, **kwargs): directives.extend([dictvalue(directive, value) for value in values]) else: directives.append(dictvalue(directive, values)) - cc = ", ".join(directives) - response.headers["Cache-Control"] = cc + cc = ', '.join(directives) + response.headers['Cache-Control'] = cc def get_max_age(response): @@ -106,28 +107,25 @@ def get_max_age(response): Return the max-age from the response Cache-Control header as an integer, or None if it wasn't found or wasn't an integer. """ - if not response.has_header("Cache-Control"): + if not response.has_header('Cache-Control'): return - cc = dict( - _to_tuple(el) for el in cc_delim_re.split(response.headers["Cache-Control"]) - ) + cc = dict(_to_tuple(el) for el in cc_delim_re.split(response.headers['Cache-Control'])) try: - return int(cc["max-age"]) + return int(cc['max-age']) except (ValueError, TypeError, KeyError): pass def set_response_etag(response): if not response.streaming and response.content: - response.headers["ETag"] = quote_etag(hashlib.md5(response.content).hexdigest()) + response.headers['ETag'] = quote_etag(hashlib.md5(response.content).hexdigest()) return response def _precondition_failed(request): response = HttpResponse(status=412) log_response( - "Precondition Failed: %s", - request.path, + 'Precondition Failed: %s', request.path, response=response, request=request, ) @@ -139,15 +137,7 @@ def _not_modified(request, response=None): if response: # Preserve the headers required by Section 4.1 of RFC 7232, as well as # Last-Modified. - for header in ( - "Cache-Control", - "Content-Location", - "Date", - "ETag", - "Expires", - "Last-Modified", - "Vary", - ): + for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'): if header in response: new_response.headers[header] = response.headers[header] @@ -166,13 +156,11 @@ def get_conditional_response(request, etag=None, last_modified=None, response=No return response # Get HTTP request headers. - if_match_etags = parse_etags(request.META.get("HTTP_IF_MATCH", "")) - if_unmodified_since = request.META.get("HTTP_IF_UNMODIFIED_SINCE") - if_unmodified_since = if_unmodified_since and parse_http_date_safe( - if_unmodified_since - ) - if_none_match_etags = parse_etags(request.META.get("HTTP_IF_NONE_MATCH", "")) - if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE") + if_match_etags = parse_etags(request.META.get('HTTP_IF_MATCH', '')) + if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE') + if_unmodified_since = if_unmodified_since and parse_http_date_safe(if_unmodified_since) + if_none_match_etags = parse_etags(request.META.get('HTTP_IF_NONE_MATCH', '')) + if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE') if_modified_since = if_modified_since and parse_http_date_safe(if_modified_since) # Step 1 of section 6 of RFC 7232: Test the If-Match precondition. @@ -180,26 +168,23 @@ def get_conditional_response(request, etag=None, last_modified=None, response=No return _precondition_failed(request) # Step 2: Test the If-Unmodified-Since precondition. - if ( - not if_match_etags - and if_unmodified_since - and not _if_unmodified_since_passes(last_modified, if_unmodified_since) - ): + if (not if_match_etags and if_unmodified_since and + not _if_unmodified_since_passes(last_modified, if_unmodified_since)): return _precondition_failed(request) # Step 3: Test the If-None-Match precondition. if if_none_match_etags and not _if_none_match_passes(etag, if_none_match_etags): - if request.method in ("GET", "HEAD"): + if request.method in ('GET', 'HEAD'): return _not_modified(request, response) else: return _precondition_failed(request) # Step 4: Test the If-Modified-Since precondition. if ( - not if_none_match_etags - and if_modified_since - and not _if_modified_since_passes(last_modified, if_modified_since) - and request.method in ("GET", "HEAD") + not if_none_match_etags and + if_modified_since and + not _if_modified_since_passes(last_modified, if_modified_since) and + request.method in ('GET', 'HEAD') ): return _not_modified(request, response) @@ -215,12 +200,12 @@ def _if_match_passes(target_etag, etags): if not target_etag: # If there isn't an ETag, then there can't be a match. return False - elif etags == ["*"]: + elif etags == ['*']: # The existence of an ETag means that there is "a current # representation for the target resource", even if the ETag is weak, # so there is a match to '*'. return True - elif target_etag.startswith("W/"): + elif target_etag.startswith('W/'): # A weak ETag can never strongly match another ETag. return False else: @@ -244,15 +229,15 @@ def _if_none_match_passes(target_etag, etags): if not target_etag: # If there isn't an ETag, then there isn't a match. return True - elif etags == ["*"]: + elif etags == ['*']: # The existence of an ETag means that there is "a current # representation for the target resource", so there is a match to '*'. return False else: # The comparison should be weak, so look for a match after stripping # off any weak indicators. - target_etag = target_etag.strip("W/") - etags = (etag.strip("W/") for etag in etags) + target_etag = target_etag.strip('W/') + etags = (etag.strip('W/') for etag in etags) return target_etag not in etags @@ -277,8 +262,8 @@ def patch_response_headers(response, cache_timeout=None): cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS if cache_timeout < 0: cache_timeout = 0 # Can't have max-age negative - if not response.has_header("Expires"): - response.headers["Expires"] = http_date(time.time() + cache_timeout) + if not response.has_header('Expires'): + response.headers['Expires'] = http_date(time.time() + cache_timeout) patch_cache_control(response, max_age=cache_timeout) @@ -287,9 +272,7 @@ def add_never_cache_headers(response): Add headers to a response to indicate that a page should never be cached. """ patch_response_headers(response, cache_timeout=-1) - patch_cache_control( - response, no_cache=True, no_store=True, must_revalidate=True, private=True - ) + patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True, private=True) def patch_vary_headers(response, newheaders): @@ -302,31 +285,28 @@ def patch_vary_headers(response, newheaders): # Note that we need to keep the original order intact, because cache # implementations may rely on the order of the Vary contents in, say, # computing an MD5 hash. - if response.has_header("Vary"): - vary_headers = cc_delim_re.split(response.headers["Vary"]) + if response.has_header('Vary'): + vary_headers = cc_delim_re.split(response.headers['Vary']) else: vary_headers = [] # Use .lower() here so we treat headers as case-insensitive. existing_headers = {header.lower() for header in vary_headers} - additional_headers = [ - newheader - for newheader in newheaders - if newheader.lower() not in existing_headers - ] + additional_headers = [newheader for newheader in newheaders + if newheader.lower() not in existing_headers] vary_headers += additional_headers - if "*" in vary_headers: - response.headers["Vary"] = "*" + if '*' in vary_headers: + response.headers['Vary'] = '*' else: - response.headers["Vary"] = ", ".join(vary_headers) + response.headers['Vary'] = ', '.join(vary_headers) def has_vary_header(response, header_query): """ Check to see if the response has a given header name in its Vary header. """ - if not response.has_header("Vary"): + if not response.has_header('Vary'): return False - vary_headers = cc_delim_re.split(response.headers["Vary"]) + vary_headers = cc_delim_re.split(response.headers['Vary']) existing_headers = {header.lower() for header in vary_headers} return header_query.lower() in existing_headers @@ -337,9 +317,9 @@ def _i18n_cache_key_suffix(request, cache_key): # first check if LocaleMiddleware or another middleware added # LANGUAGE_CODE to request, then fall back to the active language # which in turn can also fall back to settings.LANGUAGE_CODE - cache_key += ".%s" % getattr(request, "LANGUAGE_CODE", get_language()) + cache_key += '.%s' % getattr(request, 'LANGUAGE_CODE', get_language()) if settings.USE_TZ: - cache_key += ".%s" % get_current_timezone_name() + cache_key += '.%s' % get_current_timezone_name() return cache_key @@ -350,27 +330,21 @@ def _generate_cache_key(request, method, headerlist, key_prefix): value = request.META.get(header) if value is not None: ctx.update(value.encode()) - url = hashlib.md5(request.build_absolute_uri().encode("ascii")) - cache_key = "views.decorators.cache.cache_page.%s.%s.%s.%s" % ( - key_prefix, - method, - url.hexdigest(), - ctx.hexdigest(), - ) + url = hashlib.md5(request.build_absolute_uri().encode('ascii')) + cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % ( + key_prefix, method, url.hexdigest(), ctx.hexdigest()) return _i18n_cache_key_suffix(request, cache_key) def _generate_cache_header_key(key_prefix, request): """Return a cache key for the header cache.""" - url = hashlib.md5(request.build_absolute_uri().encode("ascii")) - cache_key = "views.decorators.cache.cache_header.%s.%s" % ( - key_prefix, - url.hexdigest(), - ) + url = hashlib.md5(request.build_absolute_uri().encode('ascii')) + cache_key = 'views.decorators.cache.cache_header.%s.%s' % ( + key_prefix, url.hexdigest()) return _i18n_cache_key_suffix(request, cache_key) -def get_cache_key(request, key_prefix=None, method="GET", cache=None): +def get_cache_key(request, key_prefix=None, method='GET', cache=None): """ Return a cache key based on the request URL and query. It can be used in the request phase because it pulls the list of headers to take into @@ -412,17 +386,17 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach cache_key = _generate_cache_header_key(key_prefix, request) if cache is None: cache = caches[settings.CACHE_MIDDLEWARE_ALIAS] - if response.has_header("Vary"): + if response.has_header('Vary'): is_accept_language_redundant = settings.USE_I18N # If i18n is used, the generated cache key will be suffixed with the # current locale. Adding the raw value of Accept-Language is redundant # in that case and would result in storing the same content under # multiple keys in the cache. See #18191 for details. headerlist = [] - for header in cc_delim_re.split(response.headers["Vary"]): - header = header.upper().replace("-", "_") - if header != "ACCEPT_LANGUAGE" or not is_accept_language_redundant: - headerlist.append("HTTP_" + header) + for header in cc_delim_re.split(response.headers['Vary']): + header = header.upper().replace('-', '_') + if header != 'ACCEPT_LANGUAGE' or not is_accept_language_redundant: + headerlist.append('HTTP_' + header) headerlist.sort() cache.set(cache_key, headerlist, cache_timeout) return _generate_cache_key(request, request.method, headerlist, key_prefix) @@ -434,7 +408,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach def _to_tuple(s): - t = s.split("=", 1) + t = s.split('=', 1) if len(t) == 2: return t[0].lower(), t[1] return t[0].lower(), True diff --git a/venv/Lib/site-packages/django/utils/connection.py b/venv/Lib/site-packages/django/utils/connection.py index 1b5895e..72a0214 100644 --- a/venv/Lib/site-packages/django/utils/connection.py +++ b/venv/Lib/site-packages/django/utils/connection.py @@ -8,8 +8,8 @@ class ConnectionProxy: """Proxy for accessing a connection object's attributes.""" def __init__(self, connections, alias): - self.__dict__["_connections"] = connections - self.__dict__["_alias"] = alias + self.__dict__['_connections'] = connections + self.__dict__['_alias'] = alias def __getattr__(self, item): return getattr(self._connections[self._alias], item) @@ -51,7 +51,7 @@ class BaseConnectionHandler: return settings def create_connection(self, alias): - raise NotImplementedError("Subclasses must implement create_connection().") + raise NotImplementedError('Subclasses must implement create_connection().') def __getitem__(self, alias): try: diff --git a/venv/Lib/site-packages/django/utils/crypto.py b/venv/Lib/site-packages/django/utils/crypto.py index 1c0e700..4fb3a9d 100644 --- a/venv/Lib/site-packages/django/utils/crypto.py +++ b/venv/Lib/site-packages/django/utils/crypto.py @@ -4,18 +4,19 @@ Django's standard crypto functions and utilities. import hashlib import hmac import secrets +import warnings from django.conf import settings +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.encoding import force_bytes class InvalidAlgorithm(ValueError): """Algorithm is not supported by hashlib.""" - pass -def salted_hmac(key_salt, value, secret=None, *, algorithm="sha1"): +def salted_hmac(key_salt, value, secret=None, *, algorithm='sha1'): """ Return the HMAC of 'value', using a key generated from key_salt and a secret (which defaults to settings.SECRET_KEY). Default algorithm is SHA1, @@ -32,7 +33,8 @@ def salted_hmac(key_salt, value, secret=None, *, algorithm="sha1"): hasher = getattr(hashlib, algorithm) except AttributeError as e: raise InvalidAlgorithm( - "%r is not an algorithm accepted by the hashlib module." % algorithm + '%r is not an algorithm accepted by the hashlib module.' + % algorithm ) from e # We need to generate a derived key from our base key. We can do this by # passing the key_salt and our base key through a pseudo-random function. @@ -44,10 +46,13 @@ def salted_hmac(key_salt, value, secret=None, *, algorithm="sha1"): return hmac.new(key, msg=force_bytes(value), digestmod=hasher) -RANDOM_STRING_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +NOT_PROVIDED = object() # RemovedInDjango40Warning. +RANDOM_STRING_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' -def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS): +# RemovedInDjango40Warning: when the deprecation ends, replace with: +# def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS): +def get_random_string(length=NOT_PROVIDED, allowed_chars=RANDOM_STRING_CHARS): """ Return a securely generated random string. @@ -58,7 +63,13 @@ def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS): * length: 12, bit length =~ 71 bits * length: 22, bit length =~ 131 bits """ - return "".join(secrets.choice(allowed_chars) for i in range(length)) + if length is NOT_PROVIDED: + warnings.warn( + 'Not providing a length argument is deprecated.', + RemovedInDjango40Warning, + ) + length = 12 + return ''.join(secrets.choice(allowed_chars) for i in range(length)) def constant_time_compare(val1, val2): diff --git a/venv/Lib/site-packages/django/utils/datastructures.py b/venv/Lib/site-packages/django/utils/datastructures.py index 54e4001..871b016 100644 --- a/venv/Lib/site-packages/django/utils/datastructures.py +++ b/venv/Lib/site-packages/django/utils/datastructures.py @@ -25,9 +25,6 @@ class OrderedSet: def __iter__(self): return iter(self.dict) - def __reversed__(self): - return reversed(self.dict) - def __contains__(self, item): return item in self.dict @@ -37,10 +34,6 @@ class OrderedSet: def __len__(self): return len(self.dict) - def __repr__(self): - data = repr(list(self.dict)) if self.dict else "" - return f"{self.__class__.__qualname__}({data})" - class MultiValueDictKeyError(KeyError): pass @@ -65,10 +58,9 @@ class MultiValueDict(dict): >>> d.setlist('lastname', ['Holovaty', 'Willison']) This class exists to solve the irritating problem raised by cgi.parse_qs, - which returns a list for every key, even though most web forms submit + which returns a list for every key, even though most Web forms submit single name-value pairs. """ - def __init__(self, key_to_list_mapping=()): super().__init__(key_to_list_mapping) @@ -93,22 +85,24 @@ class MultiValueDict(dict): super().__setitem__(key, [value]) def __copy__(self): - return self.__class__([(k, v[:]) for k, v in self.lists()]) + return self.__class__([ + (k, v[:]) + for k, v in self.lists() + ]) def __deepcopy__(self, memo): result = self.__class__() memo[id(self)] = result for key, value in dict.items(self): - dict.__setitem__( - result, copy.deepcopy(key, memo), copy.deepcopy(value, memo) - ) + dict.__setitem__(result, copy.deepcopy(key, memo), + copy.deepcopy(value, memo)) return result def __getstate__(self): - return {**self.__dict__, "_data": {k: self._getlist(k) for k in self}} + return {**self.__dict__, '_data': {k: self._getlist(k) for k in self}} def __setstate__(self, obj_dict): - data = obj_dict.pop("_data", {}) + data = obj_dict.pop('_data', {}) for k, v in data.items(): self.setlist(k, v) self.__dict__.update(obj_dict) @@ -230,7 +224,7 @@ class ImmutableList(tuple): AttributeError: You cannot mutate this. """ - def __new__(cls, *args, warning="ImmutableList object is immutable.", **kwargs): + def __new__(cls, *args, warning='ImmutableList object is immutable.', **kwargs): self = tuple.__new__(cls, *args, **kwargs) self.warning = warning return self @@ -263,7 +257,6 @@ class DictWrapper(dict): Used by the SQL construction code to ensure that values are correctly quoted before being used. """ - def __init__(self, data, func, prefix): super().__init__(data) self.func = func @@ -277,7 +270,7 @@ class DictWrapper(dict): """ use_func = key.startswith(self.prefix) if use_func: - key = key[len(self.prefix) :] + key = key[len(self.prefix):] value = super().__getitem__(key) if use_func: return self.func(value) @@ -288,13 +281,11 @@ def _destruct_iterable_mapping_values(data): for i, elem in enumerate(data): if len(elem) != 2: raise ValueError( - "dictionary update sequence element #{} has " - "length {}; 2 is required.".format(i, len(elem)) + 'dictionary update sequence element #{} has ' + 'length {}; 2 is required.'.format(i, len(elem)) ) if not isinstance(elem[0], str): - raise ValueError( - "Element key %r invalid, only strings are allowed" % elem[0] - ) + raise ValueError('Element key %r invalid, only strings are allowed' % elem[0]) yield tuple(elem) @@ -330,7 +321,9 @@ class CaseInsensitiveMapping(Mapping): def __eq__(self, other): return isinstance(other, Mapping) and { k.lower(): v for k, v in self.items() - } == {k.lower(): v for k, v in other.items()} + } == { + k.lower(): v for k, v in other.items() + } def __iter__(self): return (original_key for original_key, value in self._store.values()) diff --git a/venv/Lib/site-packages/django/utils/dateformat.py b/venv/Lib/site-packages/django/utils/dateformat.py index a125cca..38e89c4 100644 --- a/venv/Lib/site-packages/django/utils/dateformat.py +++ b/venv/Lib/site-packages/django/utils/dateformat.py @@ -12,27 +12,21 @@ Usage: """ import calendar import datetime +import time from email.utils import format_datetime as format_datetime_rfc5322 from django.utils.dates import ( - MONTHS, - MONTHS_3, - MONTHS_ALT, - MONTHS_AP, - WEEKDAYS, - WEEKDAYS_ABBR, + MONTHS, MONTHS_3, MONTHS_ALT, MONTHS_AP, WEEKDAYS, WEEKDAYS_ABBR, ) from django.utils.regex_helper import _lazy_re_compile from django.utils.timezone import ( - _datetime_ambiguous_or_imaginary, - get_default_timezone, - is_naive, + _datetime_ambiguous_or_imaginary, get_default_timezone, is_aware, is_naive, make_aware, ) from django.utils.translation import gettext as _ -re_formatchars = _lazy_re_compile(r"(?<!\\)([aAbcdDeEfFgGhHiIjlLmMnNoOPrsStTUuwWyYzZ])") -re_escaped = _lazy_re_compile(r"\\(.)") +re_formatchars = _lazy_re_compile(r'(?<!\\)([aAbcdDeEfFgGhHiIjlLmMnNoOPrsStTUuwWyYzZ])') +re_escaped = _lazy_re_compile(r'\\(.)') class Formatter: @@ -47,11 +41,12 @@ class Formatter: ) pieces.append(str(getattr(self, piece)())) elif piece: - pieces.append(re_escaped.sub(r"\1", piece)) - return "".join(pieces) + pieces.append(re_escaped.sub(r'\1', piece)) + return ''.join(pieces) class TimeFormat(Formatter): + def __init__(self, obj): self.data = obj self.timezone = None @@ -65,23 +60,17 @@ class TimeFormat(Formatter): else: self.timezone = obj.tzinfo - @property - def _no_timezone_or_datetime_is_ambiguous_or_imaginary(self): - return not self.timezone or _datetime_ambiguous_or_imaginary( - self.data, self.timezone - ) - def a(self): "'a.m.' or 'p.m.'" if self.data.hour > 11: - return _("p.m.") - return _("a.m.") + return _('p.m.') + return _('a.m.') def A(self): "'AM' or 'PM'" if self.data.hour > 11: - return _("PM") - return _("AM") + return _('PM') + return _('AM') def e(self): """ @@ -93,8 +82,8 @@ class TimeFormat(Formatter): return "" try: - if hasattr(self.data, "tzinfo") and self.data.tzinfo: - return self.data.tzname() or "" + if hasattr(self.data, 'tzinfo') and self.data.tzinfo: + return self.data.tzname() or '' except NotImplementedError: pass return "" @@ -106,9 +95,9 @@ class TimeFormat(Formatter): Examples: '1', '1:30', '2:05', '2' Proprietary extension. """ - hour = self.data.hour % 12 or 12 - minute = self.data.minute - return "%d:%02d" % (hour, minute) if minute else hour + if self.data.minute == 0: + return self.g() + return '%s:%s' % (self.g(), self.i()) def g(self): "Hour, 12-hour format without leading zeros; i.e. '1' to '12'" @@ -120,15 +109,15 @@ class TimeFormat(Formatter): def h(self): "Hour, 12-hour format; i.e. '01' to '12'" - return "%02d" % (self.data.hour % 12 or 12) + return '%02d' % self.g() def H(self): "Hour, 24-hour format; i.e. '00' to '23'" - return "%02d" % self.data.hour + return '%02d' % self.G() def i(self): "Minutes; i.e. '00' to '59'" - return "%02d" % self.data.minute + return '%02d' % self.data.minute def O(self): # NOQA: E743, E741 """ @@ -136,11 +125,13 @@ class TimeFormat(Formatter): If timezone information is not available, return an empty string. """ - if self._no_timezone_or_datetime_is_ambiguous_or_imaginary: + if not self.timezone: return "" seconds = self.Z() - sign = "-" if seconds < 0 else "+" + if seconds == "": + return "" + sign = '-' if seconds < 0 else '+' seconds = abs(seconds) return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60) @@ -152,14 +143,14 @@ class TimeFormat(Formatter): Proprietary extension. """ if self.data.minute == 0 and self.data.hour == 0: - return _("midnight") + return _('midnight') if self.data.minute == 0 and self.data.hour == 12: - return _("noon") - return "%s %s" % (self.f(), self.a()) + return _('noon') + return '%s %s' % (self.f(), self.a()) def s(self): "Seconds; i.e. '00' to '59'" - return "%02d" % self.data.second + return '%02d' % self.data.second def T(self): """ @@ -167,14 +158,18 @@ class TimeFormat(Formatter): If timezone information is not available, return an empty string. """ - if self._no_timezone_or_datetime_is_ambiguous_or_imaginary: + if not self.timezone: return "" - return str(self.timezone.tzname(self.data)) + if not _datetime_ambiguous_or_imaginary(self.data, self.timezone): + name = self.timezone.tzname(self.data) + else: + name = self.format('O') + return str(name) def u(self): "Microseconds; i.e. '000000' to '999999'" - return "%06d" % self.data.microsecond + return '%06d' % self.data.microsecond def Z(self): """ @@ -184,7 +179,10 @@ class TimeFormat(Formatter): If timezone information is not available, return an empty string. """ - if self._no_timezone_or_datetime_is_ambiguous_or_imaginary: + if ( + not self.timezone or + _datetime_ambiguous_or_imaginary(self.data, self.timezone) + ): return "" offset = self.timezone.utcoffset(self.data) @@ -210,7 +208,7 @@ class DateFormat(TimeFormat): def d(self): "Day of the month, 2 digits with leading zeros; i.e. '01' to '31'" - return "%02d" % self.data.day + return '%02d' % self.data.day def D(self): "Day of the week, textual, 3 letters; e.g. 'Fri'" @@ -225,10 +223,13 @@ class DateFormat(TimeFormat): return MONTHS[self.data.month] def I(self): # NOQA: E743, E741 - "'1' if daylight saving time, '0' otherwise." - if self._no_timezone_or_datetime_is_ambiguous_or_imaginary: - return "" - return "1" if self.timezone.dst(self.data) else "0" + "'1' if Daylight Savings Time, '0' otherwise." + if ( + not self.timezone or + _datetime_ambiguous_or_imaginary(self.data, self.timezone) + ): + return '' + return '1' if self.timezone.dst(self.data) else '0' def j(self): "Day of the month without leading zeros; i.e. '1' to '31'" @@ -244,7 +245,7 @@ class DateFormat(TimeFormat): def m(self): "Month; i.e. '01' to '12'" - return "%02d" % self.data.month + return '%02d' % self.data.month def M(self): "Month, textual, 3 letters; e.g. 'Jan'" @@ -276,31 +277,28 @@ class DateFormat(TimeFormat): return format_datetime_rfc5322(dt) def S(self): - """ - English ordinal suffix for the day of the month, 2 characters; i.e. - 'st', 'nd', 'rd' or 'th'. - """ + "English ordinal suffix for the day of the month, 2 characters; i.e. 'st', 'nd', 'rd' or 'th'" if self.data.day in (11, 12, 13): # Special case - return "th" + return 'th' last = self.data.day % 10 if last == 1: - return "st" + return 'st' if last == 2: - return "nd" + return 'nd' if last == 3: - return "rd" - return "th" + return 'rd' + return 'th' def t(self): "Number of days in the given month; i.e. '28' to '31'" - return "%02d" % calendar.monthrange(self.data.year, self.data.month)[1] + return '%02d' % calendar.monthrange(self.data.year, self.data.month)[1] def U(self): "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)" - value = self.data - if not isinstance(value, datetime.datetime): - value = datetime.datetime.combine(value, datetime.time.min) - return int(value.timestamp()) + if isinstance(self.data, datetime.datetime) and is_aware(self.data): + return int(calendar.timegm(self.data.utctimetuple())) + else: + return int(time.mktime(self.data.timetuple())) def w(self): "Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)" @@ -312,11 +310,11 @@ class DateFormat(TimeFormat): def y(self): """Year, 2 digits with leading zeros; e.g. '99'.""" - return "%02d" % (self.data.year % 100) + return '%02d' % (self.data.year % 100) def Y(self): - """Year, 4 digits with leading zeros; e.g. '1999'.""" - return "%04d" % self.data.year + "Year, 4 digits; e.g. '1999'" + return self.data.year def z(self): """Day of the year, i.e. 1 to 366.""" diff --git a/venv/Lib/site-packages/django/utils/dateparse.py b/venv/Lib/site-packages/django/utils/dateparse.py index 0ef902e..e069f5d 100644 --- a/venv/Lib/site-packages/django/utils/dateparse.py +++ b/venv/Lib/site-packages/django/utils/dateparse.py @@ -10,57 +10,59 @@ import datetime from django.utils.regex_helper import _lazy_re_compile from django.utils.timezone import get_fixed_timezone, utc -date_re = _lazy_re_compile(r"(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$") +date_re = _lazy_re_compile( + r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$' +) time_re = _lazy_re_compile( - r"(?P<hour>\d{1,2}):(?P<minute>\d{1,2})" - r"(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?$" + r'(?P<hour>\d{1,2}):(?P<minute>\d{1,2})' + r'(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?' ) datetime_re = _lazy_re_compile( - r"(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})" - r"[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})" - r"(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?" - r"\s*(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$" + r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})' + r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})' + r'(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?' + r'(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$' ) standard_duration_re = _lazy_re_compile( - r"^" - r"(?:(?P<days>-?\d+) (days?, )?)?" - r"(?P<sign>-?)" - r"((?:(?P<hours>\d+):)(?=\d+:\d+))?" - r"(?:(?P<minutes>\d+):)?" - r"(?P<seconds>\d+)" - r"(?:[\.,](?P<microseconds>\d{1,6})\d{0,6})?" - r"$" + r'^' + r'(?:(?P<days>-?\d+) (days?, )?)?' + r'(?P<sign>-?)' + r'((?:(?P<hours>\d+):)(?=\d+:\d+))?' + r'(?:(?P<minutes>\d+):)?' + r'(?P<seconds>\d+)' + r'(?:[\.,](?P<microseconds>\d{1,6})\d{0,6})?' + r'$' ) # Support the sections of ISO 8601 date representation that are accepted by # timedelta iso8601_duration_re = _lazy_re_compile( - r"^(?P<sign>[-+]?)" - r"P" - r"(?:(?P<days>\d+(.\d+)?)D)?" - r"(?:T" - r"(?:(?P<hours>\d+(.\d+)?)H)?" - r"(?:(?P<minutes>\d+(.\d+)?)M)?" - r"(?:(?P<seconds>\d+(.\d+)?)S)?" - r")?" - r"$" + r'^(?P<sign>[-+]?)' + r'P' + r'(?:(?P<days>\d+(.\d+)?)D)?' + r'(?:T' + r'(?:(?P<hours>\d+(.\d+)?)H)?' + r'(?:(?P<minutes>\d+(.\d+)?)M)?' + r'(?:(?P<seconds>\d+(.\d+)?)S)?' + r')?' + r'$' ) # Support PostgreSQL's day-time interval format, e.g. "3 days 04:05:06". The # year-month and mixed intervals cannot be converted to a timedelta and thus # aren't accepted. postgres_interval_re = _lazy_re_compile( - r"^" - r"(?:(?P<days>-?\d+) (days? ?))?" - r"(?:(?P<sign>[-+])?" - r"(?P<hours>\d+):" - r"(?P<minutes>\d\d):" - r"(?P<seconds>\d\d)" - r"(?:\.(?P<microseconds>\d{1,6}))?" - r")?$" + r'^' + r'(?:(?P<days>-?\d+) (days? ?))?' + r'(?:(?P<sign>[-+])?' + r'(?P<hours>\d+):' + r'(?P<minutes>\d\d):' + r'(?P<seconds>\d\d)' + r'(?:\.(?P<microseconds>\d{1,6}))?' + r')?$' ) @@ -70,12 +72,10 @@ def parse_date(value): Raise ValueError if the input is well formatted but not a valid date. Return None if the input isn't well formatted. """ - try: - return datetime.date.fromisoformat(value) - except ValueError: - if match := date_re.match(value): - kw = {k: int(v) for k, v in match.groupdict().items()} - return datetime.date(**kw) + match = date_re.match(value) + if match: + kw = {k: int(v) for k, v in match.groupdict().items()} + return datetime.date(**kw) def parse_time(value): @@ -87,18 +87,12 @@ def parse_time(value): Return None if the input isn't well formatted, in particular if it contains an offset. """ - try: - # The fromisoformat() method takes time zone info into account and - # returns a time with a tzinfo component, if possible. However, there - # are no circumstances where aware datetime.time objects make sense, so - # remove the time zone offset. - return datetime.time.fromisoformat(value).replace(tzinfo=None) - except ValueError: - if match := time_re.match(value): - kw = match.groupdict() - kw["microsecond"] = kw["microsecond"] and kw["microsecond"].ljust(6, "0") - kw = {k: int(v) for k, v in kw.items() if v is not None} - return datetime.time(**kw) + match = time_re.match(value) + if match: + kw = match.groupdict() + kw['microsecond'] = kw['microsecond'] and kw['microsecond'].ljust(6, '0') + kw = {k: int(v) for k, v in kw.items() if v is not None} + return datetime.time(**kw) def parse_datetime(value): @@ -110,23 +104,22 @@ def parse_datetime(value): Raise ValueError if the input is well formatted but not a valid datetime. Return None if the input isn't well formatted. """ - try: - return datetime.datetime.fromisoformat(value) - except ValueError: - if match := datetime_re.match(value): - kw = match.groupdict() - kw["microsecond"] = kw["microsecond"] and kw["microsecond"].ljust(6, "0") - tzinfo = kw.pop("tzinfo") - if tzinfo == "Z": - tzinfo = utc - elif tzinfo is not None: - offset_mins = int(tzinfo[-2:]) if len(tzinfo) > 3 else 0 - offset = 60 * int(tzinfo[1:3]) + offset_mins - if tzinfo[0] == "-": - offset = -offset - tzinfo = get_fixed_timezone(offset) - kw = {k: int(v) for k, v in kw.items() if v is not None} - return datetime.datetime(**kw, tzinfo=tzinfo) + match = datetime_re.match(value) + if match: + kw = match.groupdict() + kw['microsecond'] = kw['microsecond'] and kw['microsecond'].ljust(6, '0') + tzinfo = kw.pop('tzinfo') + if tzinfo == 'Z': + tzinfo = utc + elif tzinfo is not None: + offset_mins = int(tzinfo[-2:]) if len(tzinfo) > 3 else 0 + offset = 60 * int(tzinfo[1:3]) + offset_mins + if tzinfo[0] == '-': + offset = -offset + tzinfo = get_fixed_timezone(offset) + kw = {k: int(v) for k, v in kw.items() if v is not None} + kw['tzinfo'] = tzinfo + return datetime.datetime(**kw) def parse_duration(value): @@ -138,23 +131,19 @@ def parse_duration(value): format. """ match = ( - standard_duration_re.match(value) - or iso8601_duration_re.match(value) - or postgres_interval_re.match(value) + standard_duration_re.match(value) or + iso8601_duration_re.match(value) or + postgres_interval_re.match(value) ) if match: kw = match.groupdict() - sign = -1 if kw.pop("sign", "+") == "-" else 1 - if kw.get("microseconds"): - kw["microseconds"] = kw["microseconds"].ljust(6, "0") - if ( - kw.get("seconds") - and kw.get("microseconds") - and kw["seconds"].startswith("-") - ): - kw["microseconds"] = "-" + kw["microseconds"] - kw = {k: float(v.replace(",", ".")) for k, v in kw.items() if v is not None} - days = datetime.timedelta(kw.pop("days", 0.0) or 0.0) + sign = -1 if kw.pop('sign', '+') == '-' else 1 + if kw.get('microseconds'): + kw['microseconds'] = kw['microseconds'].ljust(6, '0') + if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'): + kw['microseconds'] = '-' + kw['microseconds'] + kw = {k: float(v.replace(',', '.')) for k, v in kw.items() if v is not None} + days = datetime.timedelta(kw.pop('days', .0) or .0) if match.re == iso8601_duration_re: days *= sign return days + sign * datetime.timedelta(**kw) diff --git a/venv/Lib/site-packages/django/utils/dates.py b/venv/Lib/site-packages/django/utils/dates.py index 05ac329..d2e769d 100644 --- a/venv/Lib/site-packages/django/utils/dates.py +++ b/venv/Lib/site-packages/django/utils/dates.py @@ -1,79 +1,49 @@ "Commonly-used date structures" -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import pgettext_lazy +from django.utils.translation import gettext_lazy as _, pgettext_lazy WEEKDAYS = { - 0: _("Monday"), - 1: _("Tuesday"), - 2: _("Wednesday"), - 3: _("Thursday"), - 4: _("Friday"), - 5: _("Saturday"), - 6: _("Sunday"), + 0: _('Monday'), 1: _('Tuesday'), 2: _('Wednesday'), 3: _('Thursday'), 4: _('Friday'), + 5: _('Saturday'), 6: _('Sunday') } WEEKDAYS_ABBR = { - 0: _("Mon"), - 1: _("Tue"), - 2: _("Wed"), - 3: _("Thu"), - 4: _("Fri"), - 5: _("Sat"), - 6: _("Sun"), + 0: _('Mon'), 1: _('Tue'), 2: _('Wed'), 3: _('Thu'), 4: _('Fri'), + 5: _('Sat'), 6: _('Sun') } MONTHS = { - 1: _("January"), - 2: _("February"), - 3: _("March"), - 4: _("April"), - 5: _("May"), - 6: _("June"), - 7: _("July"), - 8: _("August"), - 9: _("September"), - 10: _("October"), - 11: _("November"), - 12: _("December"), + 1: _('January'), 2: _('February'), 3: _('March'), 4: _('April'), 5: _('May'), 6: _('June'), + 7: _('July'), 8: _('August'), 9: _('September'), 10: _('October'), 11: _('November'), + 12: _('December') } MONTHS_3 = { - 1: _("jan"), - 2: _("feb"), - 3: _("mar"), - 4: _("apr"), - 5: _("may"), - 6: _("jun"), - 7: _("jul"), - 8: _("aug"), - 9: _("sep"), - 10: _("oct"), - 11: _("nov"), - 12: _("dec"), + 1: _('jan'), 2: _('feb'), 3: _('mar'), 4: _('apr'), 5: _('may'), 6: _('jun'), + 7: _('jul'), 8: _('aug'), 9: _('sep'), 10: _('oct'), 11: _('nov'), 12: _('dec') } MONTHS_AP = { # month names in Associated Press style - 1: pgettext_lazy("abbrev. month", "Jan."), - 2: pgettext_lazy("abbrev. month", "Feb."), - 3: pgettext_lazy("abbrev. month", "March"), - 4: pgettext_lazy("abbrev. month", "April"), - 5: pgettext_lazy("abbrev. month", "May"), - 6: pgettext_lazy("abbrev. month", "June"), - 7: pgettext_lazy("abbrev. month", "July"), - 8: pgettext_lazy("abbrev. month", "Aug."), - 9: pgettext_lazy("abbrev. month", "Sept."), - 10: pgettext_lazy("abbrev. month", "Oct."), - 11: pgettext_lazy("abbrev. month", "Nov."), - 12: pgettext_lazy("abbrev. month", "Dec."), + 1: pgettext_lazy('abbrev. month', 'Jan.'), + 2: pgettext_lazy('abbrev. month', 'Feb.'), + 3: pgettext_lazy('abbrev. month', 'March'), + 4: pgettext_lazy('abbrev. month', 'April'), + 5: pgettext_lazy('abbrev. month', 'May'), + 6: pgettext_lazy('abbrev. month', 'June'), + 7: pgettext_lazy('abbrev. month', 'July'), + 8: pgettext_lazy('abbrev. month', 'Aug.'), + 9: pgettext_lazy('abbrev. month', 'Sept.'), + 10: pgettext_lazy('abbrev. month', 'Oct.'), + 11: pgettext_lazy('abbrev. month', 'Nov.'), + 12: pgettext_lazy('abbrev. month', 'Dec.') } MONTHS_ALT = { # required for long date representation by some locales - 1: pgettext_lazy("alt. month", "January"), - 2: pgettext_lazy("alt. month", "February"), - 3: pgettext_lazy("alt. month", "March"), - 4: pgettext_lazy("alt. month", "April"), - 5: pgettext_lazy("alt. month", "May"), - 6: pgettext_lazy("alt. month", "June"), - 7: pgettext_lazy("alt. month", "July"), - 8: pgettext_lazy("alt. month", "August"), - 9: pgettext_lazy("alt. month", "September"), - 10: pgettext_lazy("alt. month", "October"), - 11: pgettext_lazy("alt. month", "November"), - 12: pgettext_lazy("alt. month", "December"), + 1: pgettext_lazy('alt. month', 'January'), + 2: pgettext_lazy('alt. month', 'February'), + 3: pgettext_lazy('alt. month', 'March'), + 4: pgettext_lazy('alt. month', 'April'), + 5: pgettext_lazy('alt. month', 'May'), + 6: pgettext_lazy('alt. month', 'June'), + 7: pgettext_lazy('alt. month', 'July'), + 8: pgettext_lazy('alt. month', 'August'), + 9: pgettext_lazy('alt. month', 'September'), + 10: pgettext_lazy('alt. month', 'October'), + 11: pgettext_lazy('alt. month', 'November'), + 12: pgettext_lazy('alt. month', 'December') } diff --git a/venv/Lib/site-packages/django/utils/datetime_safe.py b/venv/Lib/site-packages/django/utils/datetime_safe.py index 817ddcf..8c87c82 100644 --- a/venv/Lib/site-packages/django/utils/datetime_safe.py +++ b/venv/Lib/site-packages/django/utils/datetime_safe.py @@ -7,20 +7,13 @@ # >>> datetime_safe.date(10, 8, 2).strftime("%Y/%m/%d was a %A") # '0010/08/02 was a Monday' -import time -import warnings -from datetime import date as real_date -from datetime import datetime as real_datetime - -from django.utils.deprecation import RemovedInDjango50Warning -from django.utils.regex_helper import _lazy_re_compile - -warnings.warn( - "The django.utils.datetime_safe module is deprecated.", - category=RemovedInDjango50Warning, - stacklevel=2, +import time as ttime +from datetime import ( + date as real_date, datetime as real_datetime, time as real_time, ) +from django.utils.regex_helper import _lazy_re_compile + class date(real_date): def strftime(self, fmt): @@ -33,21 +26,18 @@ class datetime(real_datetime): @classmethod def combine(cls, date, time): - return cls( - date.year, - date.month, - date.day, - time.hour, - time.minute, - time.second, - time.microsecond, - time.tzinfo, - ) + return cls(date.year, date.month, date.day, + time.hour, time.minute, time.second, + time.microsecond, time.tzinfo) def date(self): return date(self.year, self.month, self.day) +class time(real_time): + pass + + def new_date(d): "Generate a safe date from a datetime.date object." return date(d.year, d.month, d.day) @@ -86,9 +76,7 @@ def strftime(dt, fmt): return super(type(dt), dt).strftime(fmt) illegal_formatting = _illegal_formatting.search(fmt) if illegal_formatting: - raise TypeError( - "strftime of dates before 1000 does not handle " + illegal_formatting[0] - ) + raise TypeError('strftime of dates before 1000 does not handle ' + illegal_formatting[0]) year = dt.year # For every non-leap year century, advance by @@ -100,10 +88,10 @@ def strftime(dt, fmt): # Move to around the year 2000 year = year + ((2000 - year) // 28) * 28 timetuple = dt.timetuple() - s1 = time.strftime(fmt, (year,) + timetuple[1:]) + s1 = ttime.strftime(fmt, (year,) + timetuple[1:]) sites1 = _findall(s1, str(year)) - s2 = time.strftime(fmt, (year + 28,) + timetuple[1:]) + s2 = ttime.strftime(fmt, (year + 28,) + timetuple[1:]) sites2 = _findall(s2, str(year + 28)) sites = [] @@ -114,5 +102,5 @@ def strftime(dt, fmt): s = s1 syear = "%04d" % dt.year for site in sites: - s = s[:site] + syear + s[site + 4 :] + s = s[:site] + syear + s[site + 4:] return s diff --git a/venv/Lib/site-packages/django/utils/deconstruct.py b/venv/Lib/site-packages/django/utils/deconstruct.py index feaea3a..aa952f6 100644 --- a/venv/Lib/site-packages/django/utils/deconstruct.py +++ b/venv/Lib/site-packages/django/utils/deconstruct.py @@ -10,7 +10,6 @@ def deconstructible(*args, path=None): The `path` kwarg specifies the import path. """ - def decorator(klass): def __new__(cls, *args, **kwargs): # We capture the arguments to make returning them trivial @@ -25,7 +24,7 @@ def deconstructible(*args, path=None): """ # Fallback version if path: - module_name, _, name = path.rpartition(".") + module_name, _, name = path.rpartition('.') else: module_name = obj.__module__ name = obj.__class__.__name__ @@ -38,11 +37,10 @@ def deconstructible(*args, path=None): "classes. Please move the object into the main module " "body to use migrations.\n" "For more information, see " - "https://docs.djangoproject.com/en/%s/topics/migrations/" - "#serializing-values" % (name, module_name, get_docs_version()) - ) + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version())) return ( - path or "%s.%s" % (obj.__class__.__module__, name), + path or '%s.%s' % (obj.__class__.__module__, name), obj._constructor_args[0], obj._constructor_args[1], ) diff --git a/venv/Lib/site-packages/django/utils/decorators.py b/venv/Lib/site-packages/django/utils/decorators.py index e412bb1..5c9a5d0 100644 --- a/venv/Lib/site-packages/django/utils/decorators.py +++ b/venv/Lib/site-packages/django/utils/decorators.py @@ -6,9 +6,7 @@ from functools import partial, update_wrapper, wraps class classonlymethod(classmethod): def __get__(self, instance, cls=None): if instance is not None: - raise AttributeError( - "This method is available only on the class, not on instances." - ) + raise AttributeError("This method is available only on the class, not on instances.") return super().__get__(instance, cls) @@ -18,7 +16,6 @@ def _update_method_wrapper(_wrapper, decorator): @decorator def dummy(*args, **kwargs): pass - update_wrapper(_wrapper, dummy) @@ -27,7 +24,7 @@ def _multi_decorate(decorators, method): Decorate `method` with one or more function decorators. `decorators` can be a single decorator or an iterable of decorators. """ - if hasattr(decorators, "__iter__"): + if hasattr(decorators, '__iter__'): # Apply a list/tuple of decorators if 'decorators' is one. Decorator # functions are applied so that the call order is the same as the # order in which they appear in the iterable. @@ -40,7 +37,7 @@ def _multi_decorate(decorators, method): # 'self' argument, but it's a closure over self so it can call # 'func'. Also, wrap method.__get__() in a function because new # attributes can't be set on bound method objects, only on functions. - bound_method = wraps(method)(partial(method.__get__(self, type(self)))) + bound_method = partial(method.__get__(self, type(self))) for dec in decorators: bound_method = dec(bound_method) return bound_method(*args, **kwargs) @@ -53,7 +50,7 @@ def _multi_decorate(decorators, method): return _wrapper -def method_decorator(decorator, name=""): +def method_decorator(decorator, name=''): """ Convert a function decorator into a method decorator """ @@ -81,11 +78,11 @@ def method_decorator(decorator, name=""): # Don't worry about making _dec look similar to a list/tuple as it's rather # meaningless. - if not hasattr(decorator, "__iter__"): + if not hasattr(decorator, '__iter__'): update_wrapper(_dec, decorator) # Change the name to aid debugging. - obj = decorator if hasattr(decorator, "__name__") else decorator.__class__ - _dec.__name__ = "method_decorator(%s)" % obj.__name__ + obj = decorator if hasattr(decorator, '__name__') else decorator.__class__ + _dec.__name__ = 'method_decorator(%s)' % obj.__name__ return _dec @@ -121,44 +118,37 @@ def make_middleware_decorator(middleware_class): @wraps(view_func) def _wrapped_view(request, *args, **kwargs): - if hasattr(middleware, "process_request"): + if hasattr(middleware, 'process_request'): result = middleware.process_request(request) if result is not None: return result - if hasattr(middleware, "process_view"): + if hasattr(middleware, 'process_view'): result = middleware.process_view(request, view_func, args, kwargs) if result is not None: return result try: response = view_func(request, *args, **kwargs) except Exception as e: - if hasattr(middleware, "process_exception"): + if hasattr(middleware, 'process_exception'): result = middleware.process_exception(request, e) if result is not None: return result raise - if hasattr(response, "render") and callable(response.render): - if hasattr(middleware, "process_template_response"): - response = middleware.process_template_response( - request, response - ) + if hasattr(response, 'render') and callable(response.render): + if hasattr(middleware, 'process_template_response'): + response = middleware.process_template_response(request, response) # Defer running of process_response until after the template # has been rendered: - if hasattr(middleware, "process_response"): - + if hasattr(middleware, 'process_response'): def callback(response): return middleware.process_response(request, response) - response.add_post_render_callback(callback) else: - if hasattr(middleware, "process_response"): + if hasattr(middleware, 'process_response'): return middleware.process_response(request, response) return response - return _wrapped_view - return _decorator - return _make_decorator diff --git a/venv/Lib/site-packages/django/utils/deprecation.py b/venv/Lib/site-packages/django/utils/deprecation.py index 13c11fc..b2c681b 100644 --- a/venv/Lib/site-packages/django/utils/deprecation.py +++ b/venv/Lib/site-packages/django/utils/deprecation.py @@ -5,21 +5,19 @@ import warnings from asgiref.sync import sync_to_async -class RemovedInDjango41Warning(DeprecationWarning): +class RemovedInDjango40Warning(DeprecationWarning): pass -class RemovedInDjango50Warning(PendingDeprecationWarning): +class RemovedInDjango41Warning(PendingDeprecationWarning): pass -RemovedInNextVersionWarning = RemovedInDjango41Warning +RemovedInNextVersionWarning = RemovedInDjango40Warning class warn_about_renamed_method: - def __init__( - self, class_name, old_method_name, new_method_name, deprecation_warning - ): + def __init__(self, class_name, old_method_name, new_method_name, deprecation_warning): self.class_name = class_name self.old_method_name = old_method_name self.new_method_name = new_method_name @@ -28,13 +26,10 @@ class warn_about_renamed_method: def __call__(self, f): def wrapped(*args, **kwargs): warnings.warn( - "`%s.%s` is deprecated, use `%s` instead." - % (self.class_name, self.old_method_name, self.new_method_name), - self.deprecation_warning, - 2, - ) + "`%s.%s` is deprecated, use `%s` instead." % + (self.class_name, self.old_method_name, self.new_method_name), + self.deprecation_warning, 2) return f(*args, **kwargs) - return wrapped @@ -68,11 +63,9 @@ class RenameMethodsBase(type): # Define the new method if missing and complain about it if not new_method and old_method: warnings.warn( - "`%s.%s` method should be renamed `%s`." - % (class_name, old_method_name, new_method_name), - deprecation_warning, - 2, - ) + "`%s.%s` method should be renamed `%s`." % + (class_name, old_method_name, new_method_name), + deprecation_warning, 2) setattr(base, new_method_name, old_method) setattr(base, old_method_name, wrapper(old_method)) @@ -87,8 +80,7 @@ class DeprecationInstanceCheck(type): def __instancecheck__(self, instance): warnings.warn( "`%s` is deprecated, use `%s` instead." % (self.__name__, self.alternative), - self.deprecation_warning, - 2, + self.deprecation_warning, 2 ) return super().__instancecheck__(instance) @@ -97,23 +89,14 @@ class MiddlewareMixin: sync_capable = True async_capable = True - def __init__(self, get_response): - if get_response is None: - raise ValueError("get_response must be provided.") + # RemovedInDjango40Warning: when the deprecation ends, replace with: + # def __init__(self, get_response): + def __init__(self, get_response=None): + self._get_response_none_deprecation(get_response) self.get_response = get_response self._async_check() super().__init__() - def __repr__(self): - return "<%s get_response=%s>" % ( - self.__class__.__qualname__, - getattr( - self.get_response, - "__qualname__", - self.get_response.__class__.__name__, - ), - ) - def _async_check(self): """ If get_response is a coroutine function, turns us into async mode so @@ -129,10 +112,10 @@ class MiddlewareMixin: if asyncio.iscoroutinefunction(self.get_response): return self.__acall__(request) response = None - if hasattr(self, "process_request"): + if hasattr(self, 'process_request'): response = self.process_request(request) response = response or self.get_response(request) - if hasattr(self, "process_response"): + if hasattr(self, 'process_response'): response = self.process_response(request, response) return response @@ -142,15 +125,23 @@ class MiddlewareMixin: is running. """ response = None - if hasattr(self, "process_request"): + if hasattr(self, 'process_request'): response = await sync_to_async( self.process_request, thread_sensitive=True, )(request) response = response or await self.get_response(request) - if hasattr(self, "process_response"): + if hasattr(self, 'process_response'): response = await sync_to_async( self.process_response, thread_sensitive=True, )(request, response) return response + + def _get_response_none_deprecation(self, get_response): + if get_response is None: + warnings.warn( + 'Passing None for the middleware get_response argument is ' + 'deprecated.', + RemovedInDjango40Warning, stacklevel=3, + ) diff --git a/venv/Lib/site-packages/django/utils/duration.py b/venv/Lib/site-packages/django/utils/duration.py index 8495af3..466603d 100644 --- a/venv/Lib/site-packages/django/utils/duration.py +++ b/venv/Lib/site-packages/django/utils/duration.py @@ -19,27 +19,25 @@ def duration_string(duration): """Version of str(timedelta) which is not English specific.""" days, hours, minutes, seconds, microseconds = _get_duration_components(duration) - string = "{:02d}:{:02d}:{:02d}".format(hours, minutes, seconds) + string = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds) if days: - string = "{} ".format(days) + string + string = '{} '.format(days) + string if microseconds: - string += ".{:06d}".format(microseconds) + string += '.{:06d}'.format(microseconds) return string def duration_iso_string(duration): if duration < datetime.timedelta(0): - sign = "-" + sign = '-' duration *= -1 else: - sign = "" + sign = '' days, hours, minutes, seconds, microseconds = _get_duration_components(duration) - ms = ".{:06d}".format(microseconds) if microseconds else "" - return "{}P{}DT{:02d}H{:02d}M{:02d}{}S".format( - sign, days, hours, minutes, seconds, ms - ) + ms = '.{:06d}'.format(microseconds) if microseconds else "" + return '{}P{}DT{:02d}H{:02d}M{:02d}{}S'.format(sign, days, hours, minutes, seconds, ms) def duration_microseconds(delta): diff --git a/venv/Lib/site-packages/django/utils/encoding.py b/venv/Lib/site-packages/django/utils/encoding.py index 89eac79..e1ebace 100644 --- a/venv/Lib/site-packages/django/utils/encoding.py +++ b/venv/Lib/site-packages/django/utils/encoding.py @@ -1,9 +1,11 @@ import codecs import datetime import locale +import warnings from decimal import Decimal from urllib.parse import quote +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import Promise @@ -13,14 +15,10 @@ class DjangoUnicodeDecodeError(UnicodeDecodeError): super().__init__(*args) def __str__(self): - return "%s. You passed in %r (%s)" % ( - super().__str__(), - self.obj, - type(self.obj), - ) + return '%s. You passed in %r (%s)' % (super().__str__(), self.obj, type(self.obj)) -def smart_str(s, encoding="utf-8", strings_only=False, errors="strict"): +def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): """ Return a string representing 's'. Treat bytestrings using the 'encoding' codec. @@ -34,13 +32,7 @@ def smart_str(s, encoding="utf-8", strings_only=False, errors="strict"): _PROTECTED_TYPES = ( - type(None), - int, - float, - Decimal, - datetime.datetime, - datetime.date, - datetime.time, + type(None), int, float, Decimal, datetime.datetime, datetime.date, datetime.time, ) @@ -53,7 +45,7 @@ def is_protected_type(obj): return isinstance(obj, _PROTECTED_TYPES) -def force_str(s, encoding="utf-8", strings_only=False, errors="strict"): +def force_str(s, encoding='utf-8', strings_only=False, errors='strict'): """ Similar to smart_str(), except that lazy instances are resolved to strings, rather than kept as lazy objects. @@ -75,7 +67,7 @@ def force_str(s, encoding="utf-8", strings_only=False, errors="strict"): return s -def smart_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): +def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Return a bytestring version of 's', encoded as specified in 'encoding'. @@ -87,7 +79,7 @@ def smart_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): return force_bytes(s, encoding, strings_only, errors) -def force_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): +def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Similar to smart_bytes, except that lazy instances are resolved to strings, rather than kept as lazy objects. @@ -96,10 +88,10 @@ def force_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): """ # Handle the common case first for performance reasons. if isinstance(s, bytes): - if encoding == "utf-8": + if encoding == 'utf-8': return s else: - return s.decode("utf-8", errors).encode(encoding, errors) + return s.decode('utf-8', errors).encode(encoding, errors) if strings_only and is_protected_type(s): return s if isinstance(s, memoryview): @@ -107,6 +99,22 @@ def force_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): return str(s).encode(encoding, errors) +def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'): + warnings.warn( + 'smart_text() is deprecated in favor of smart_str().', + RemovedInDjango40Warning, stacklevel=2, + ) + return smart_str(s, encoding, strings_only, errors) + + +def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): + warnings.warn( + 'force_text() is deprecated in favor of force_str().', + RemovedInDjango40Warning, stacklevel=2, + ) + return force_str(s, encoding, strings_only, errors) + + def iri_to_uri(iri): """ Convert an Internationalized Resource Identifier (IRI) portion to a URI @@ -146,14 +154,15 @@ _hextobyte = { (fmt % char).encode(): bytes((char,)) for ascii_range in _ascii_ranges for char in ascii_range - for fmt in ["%02x", "%02X"] + for fmt in ['%02x', '%02X'] } # And then everything above 128, because bytes ≥ 128 are part of multibyte # Unicode characters. -_hexdig = "0123456789ABCDEFabcdef" -_hextobyte.update( - {(a + b).encode(): bytes.fromhex(a + b) for a in _hexdig[8:] for b in _hexdig} -) +_hexdig = '0123456789ABCDEFabcdef' +_hextobyte.update({ + (a + b).encode(): bytes.fromhex(a + b) + for a in _hexdig[8:] for b in _hexdig +}) def uri_to_iri(uri): @@ -169,11 +178,11 @@ def uri_to_iri(uri): if uri is None: return uri uri = force_bytes(uri) - # Fast selective unquote: First, split on '%' and then starting with the + # Fast selective unqote: First, split on '%' and then starting with the # second block, decode the first 2 bytes if they represent a hex code to # decode. The rest of the block is the part after '%AB', not containing # any '%'. Add that to the output without further processing. - bits = uri.split(b"%") + bits = uri.split(b'%') if len(bits) == 1: iri = uri else: @@ -186,9 +195,9 @@ def uri_to_iri(uri): append(hextobyte[item[:2]]) append(item[2:]) else: - append(b"%") + append(b'%') append(item) - iri = b"".join(parts) + iri = b''.join(parts) return repercent_broken_unicode(iri).decode() @@ -211,7 +220,7 @@ def escape_uri_path(path): def punycode(domain): """Return the Punycode of the given domain if it's non-ASCII.""" - return domain.encode("idna").decode("ascii") + return domain.encode('idna').decode('ascii') def repercent_broken_unicode(path): @@ -226,8 +235,8 @@ def repercent_broken_unicode(path): except UnicodeDecodeError as e: # CVE-2019-14235: A recursion shouldn't be used since the exception # handling uses massive amounts of memory - repercent = quote(path[e.start : e.end], safe=b"/#%[]=:;$&()+,!?*@'~") - path = path[: e.start] + repercent.encode() + path[e.end :] + repercent = quote(path[e.start:e.end], safe=b"/#%[]=:;$&()+,!?*@'~") + path = path[:e.start] + repercent.encode() + path[e.end:] else: return path @@ -254,10 +263,10 @@ def get_system_encoding(): #10335 and #5846. """ try: - encoding = locale.getdefaultlocale()[1] or "ascii" + encoding = locale.getdefaultlocale()[1] or 'ascii' codecs.lookup(encoding) except Exception: - encoding = "ascii" + encoding = 'ascii' return encoding diff --git a/venv/Lib/site-packages/django/utils/feedgenerator.py b/venv/Lib/site-packages/django/utils/feedgenerator.py index 5a57db1..f08e89b 100644 --- a/venv/Lib/site-packages/django/utils/feedgenerator.py +++ b/venv/Lib/site-packages/django/utils/feedgenerator.py @@ -7,7 +7,7 @@ Sample usage: >>> feed = feedgenerator.Rss201rev2Feed( ... title="Poynter E-Media Tidbits", ... link="http://www.poynter.org/column.asp?id=31", -... description="A group blog by the sharpest minds in online journalism.", +... description="A group Weblog by the sharpest minds in online media/journalism/publishing.", ... language="en", ... ) >>> feed.add_item( @@ -40,114 +40,78 @@ def rfc2822_date(date): def rfc3339_date(date): if not isinstance(date, datetime.datetime): date = datetime.datetime.combine(date, datetime.time()) - return date.isoformat() + ("Z" if date.utcoffset() is None else "") + return date.isoformat() + ('Z' if date.utcoffset() is None else '') def get_tag_uri(url, date): """ Create a TagURI. - See - https://web.archive.org/web/20110514113830/http://diveintomark.org/archives/2004/05/28/howto-atom-id + See https://web.archive.org/web/20110514113830/http://diveintomark.org/archives/2004/05/28/howto-atom-id """ bits = urlparse(url) - d = "" + d = '' if date is not None: - d = ",%s" % date.strftime("%Y-%m-%d") - return "tag:%s%s:%s/%s" % (bits.hostname, d, bits.path, bits.fragment) + d = ',%s' % date.strftime('%Y-%m-%d') + return 'tag:%s%s:%s/%s' % (bits.hostname, d, bits.path, bits.fragment) class SyndicationFeed: "Base class for all syndication feeds. Subclasses should provide write()" - - def __init__( - self, - title, - link, - description, - language=None, - author_email=None, - author_name=None, - author_link=None, - subtitle=None, - categories=None, - feed_url=None, - feed_copyright=None, - feed_guid=None, - ttl=None, - **kwargs, - ): + def __init__(self, title, link, description, language=None, author_email=None, + author_name=None, author_link=None, subtitle=None, categories=None, + feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs): def to_str(s): return str(s) if s is not None else s - categories = categories and [str(c) for c in categories] self.feed = { - "title": to_str(title), - "link": iri_to_uri(link), - "description": to_str(description), - "language": to_str(language), - "author_email": to_str(author_email), - "author_name": to_str(author_name), - "author_link": iri_to_uri(author_link), - "subtitle": to_str(subtitle), - "categories": categories or (), - "feed_url": iri_to_uri(feed_url), - "feed_copyright": to_str(feed_copyright), - "id": feed_guid or link, - "ttl": to_str(ttl), + 'title': to_str(title), + 'link': iri_to_uri(link), + 'description': to_str(description), + 'language': to_str(language), + 'author_email': to_str(author_email), + 'author_name': to_str(author_name), + 'author_link': iri_to_uri(author_link), + 'subtitle': to_str(subtitle), + 'categories': categories or (), + 'feed_url': iri_to_uri(feed_url), + 'feed_copyright': to_str(feed_copyright), + 'id': feed_guid or link, + 'ttl': to_str(ttl), **kwargs, } self.items = [] - def add_item( - self, - title, - link, - description, - author_email=None, - author_name=None, - author_link=None, - pubdate=None, - comments=None, - unique_id=None, - unique_id_is_permalink=None, - categories=(), - item_copyright=None, - ttl=None, - updateddate=None, - enclosures=None, - **kwargs, - ): + def add_item(self, title, link, description, author_email=None, + author_name=None, author_link=None, pubdate=None, comments=None, + unique_id=None, unique_id_is_permalink=None, categories=(), + item_copyright=None, ttl=None, updateddate=None, enclosures=None, **kwargs): """ Add an item to the feed. All args are expected to be strings except pubdate and updateddate, which are datetime.datetime objects, and enclosures, which is an iterable of instances of the Enclosure class. """ - def to_str(s): return str(s) if s is not None else s - categories = categories and [to_str(c) for c in categories] - self.items.append( - { - "title": to_str(title), - "link": iri_to_uri(link), - "description": to_str(description), - "author_email": to_str(author_email), - "author_name": to_str(author_name), - "author_link": iri_to_uri(author_link), - "pubdate": pubdate, - "updateddate": updateddate, - "comments": to_str(comments), - "unique_id": to_str(unique_id), - "unique_id_is_permalink": unique_id_is_permalink, - "enclosures": enclosures or (), - "categories": categories or (), - "item_copyright": to_str(item_copyright), - "ttl": to_str(ttl), - **kwargs, - } - ) + self.items.append({ + 'title': to_str(title), + 'link': iri_to_uri(link), + 'description': to_str(description), + 'author_email': to_str(author_email), + 'author_name': to_str(author_name), + 'author_link': iri_to_uri(author_link), + 'pubdate': pubdate, + 'updateddate': updateddate, + 'comments': to_str(comments), + 'unique_id': to_str(unique_id), + 'unique_id_is_permalink': unique_id_is_permalink, + 'enclosures': enclosures or (), + 'categories': categories or (), + 'item_copyright': to_str(item_copyright), + 'ttl': to_str(ttl), + **kwargs, + }) def num_items(self): return len(self.items) @@ -183,9 +147,7 @@ class SyndicationFeed: Output the feed in the given encoding to outfile, which is a file-like object. Subclasses should override this. """ - raise NotImplementedError( - "subclasses of SyndicationFeed must provide a write() method" - ) + raise NotImplementedError('subclasses of SyndicationFeed must provide a write() method') def writeString(self, encoding): """ @@ -201,7 +163,7 @@ class SyndicationFeed: have either of these attributes this return the current UTC date/time. """ latest_date = None - date_keys = ("updateddate", "pubdate") + date_keys = ('updateddate', 'pubdate') for item in self.items: for date_key in date_keys: @@ -210,12 +172,12 @@ class SyndicationFeed: if latest_date is None or item_date > latest_date: latest_date = item_date - return latest_date or datetime.datetime.now(tz=utc) + # datetime.now(tz=utc) is slower, as documented in django.utils.timezone.now + return latest_date or datetime.datetime.utcnow().replace(tzinfo=utc) class Enclosure: """An RSS enclosure""" - def __init__(self, url, length, mime_type): "All args are expected to be strings" self.length, self.mime_type = length, mime_type @@ -223,10 +185,10 @@ class Enclosure: class RssFeed(SyndicationFeed): - content_type = "application/rss+xml; charset=utf-8" + content_type = 'application/rss+xml; charset=utf-8' def write(self, outfile, encoding): - handler = SimplerXMLGenerator(outfile, encoding, short_empty_elements=True) + handler = SimplerXMLGenerator(outfile, encoding) handler.startDocument() handler.startElement("rss", self.rss_attributes()) handler.startElement("channel", self.root_attributes()) @@ -237,33 +199,31 @@ class RssFeed(SyndicationFeed): def rss_attributes(self): return { - "version": self._version, - "xmlns:atom": "http://www.w3.org/2005/Atom", + 'version': self._version, + 'xmlns:atom': 'http://www.w3.org/2005/Atom', } def write_items(self, handler): for item in self.items: - handler.startElement("item", self.item_attributes(item)) + handler.startElement('item', self.item_attributes(item)) self.add_item_elements(handler, item) handler.endElement("item") def add_root_elements(self, handler): - handler.addQuickElement("title", self.feed["title"]) - handler.addQuickElement("link", self.feed["link"]) - handler.addQuickElement("description", self.feed["description"]) - if self.feed["feed_url"] is not None: - handler.addQuickElement( - "atom:link", None, {"rel": "self", "href": self.feed["feed_url"]} - ) - if self.feed["language"] is not None: - handler.addQuickElement("language", self.feed["language"]) - for cat in self.feed["categories"]: + handler.addQuickElement("title", self.feed['title']) + handler.addQuickElement("link", self.feed['link']) + handler.addQuickElement("description", self.feed['description']) + if self.feed['feed_url'] is not None: + handler.addQuickElement("atom:link", None, {"rel": "self", "href": self.feed['feed_url']}) + if self.feed['language'] is not None: + handler.addQuickElement("language", self.feed['language']) + for cat in self.feed['categories']: handler.addQuickElement("category", cat) - if self.feed["feed_copyright"] is not None: - handler.addQuickElement("copyright", self.feed["feed_copyright"]) + if self.feed['feed_copyright'] is not None: + handler.addQuickElement("copyright", self.feed['feed_copyright']) handler.addQuickElement("lastBuildDate", rfc2822_date(self.latest_post_date())) - if self.feed["ttl"] is not None: - handler.addQuickElement("ttl", self.feed["ttl"]) + if self.feed['ttl'] is not None: + handler.addQuickElement("ttl", self.feed['ttl']) def endChannelElement(self, handler): handler.endElement("channel") @@ -273,10 +233,10 @@ class RssUserland091Feed(RssFeed): _version = "0.91" def add_item_elements(self, handler, item): - handler.addQuickElement("title", item["title"]) - handler.addQuickElement("link", item["link"]) - if item["description"] is not None: - handler.addQuickElement("description", item["description"]) + handler.addQuickElement("title", item['title']) + handler.addQuickElement("link", item['link']) + if item['description'] is not None: + handler.addQuickElement("description", item['description']) class Rss201rev2Feed(RssFeed): @@ -284,105 +244,93 @@ class Rss201rev2Feed(RssFeed): _version = "2.0" def add_item_elements(self, handler, item): - handler.addQuickElement("title", item["title"]) - handler.addQuickElement("link", item["link"]) - if item["description"] is not None: - handler.addQuickElement("description", item["description"]) + handler.addQuickElement("title", item['title']) + handler.addQuickElement("link", item['link']) + if item['description'] is not None: + handler.addQuickElement("description", item['description']) # Author information. if item["author_name"] and item["author_email"]: - handler.addQuickElement( - "author", "%s (%s)" % (item["author_email"], item["author_name"]) - ) + handler.addQuickElement("author", "%s (%s)" % (item['author_email'], item['author_name'])) elif item["author_email"]: handler.addQuickElement("author", item["author_email"]) elif item["author_name"]: handler.addQuickElement( - "dc:creator", - item["author_name"], - {"xmlns:dc": "http://purl.org/dc/elements/1.1/"}, + "dc:creator", item["author_name"], {"xmlns:dc": "http://purl.org/dc/elements/1.1/"} ) - if item["pubdate"] is not None: - handler.addQuickElement("pubDate", rfc2822_date(item["pubdate"])) - if item["comments"] is not None: - handler.addQuickElement("comments", item["comments"]) - if item["unique_id"] is not None: + if item['pubdate'] is not None: + handler.addQuickElement("pubDate", rfc2822_date(item['pubdate'])) + if item['comments'] is not None: + handler.addQuickElement("comments", item['comments']) + if item['unique_id'] is not None: guid_attrs = {} - if isinstance(item.get("unique_id_is_permalink"), bool): - guid_attrs["isPermaLink"] = str(item["unique_id_is_permalink"]).lower() - handler.addQuickElement("guid", item["unique_id"], guid_attrs) - if item["ttl"] is not None: - handler.addQuickElement("ttl", item["ttl"]) + if isinstance(item.get('unique_id_is_permalink'), bool): + guid_attrs['isPermaLink'] = str(item['unique_id_is_permalink']).lower() + handler.addQuickElement("guid", item['unique_id'], guid_attrs) + if item['ttl'] is not None: + handler.addQuickElement("ttl", item['ttl']) # Enclosure. - if item["enclosures"]: - enclosures = list(item["enclosures"]) + if item['enclosures']: + enclosures = list(item['enclosures']) if len(enclosures) > 1: raise ValueError( "RSS feed items may only have one enclosure, see " "http://www.rssboard.org/rss-profile#element-channel-item-enclosure" ) enclosure = enclosures[0] - handler.addQuickElement( - "enclosure", - "", - { - "url": enclosure.url, - "length": enclosure.length, - "type": enclosure.mime_type, - }, - ) + handler.addQuickElement('enclosure', '', { + 'url': enclosure.url, + 'length': enclosure.length, + 'type': enclosure.mime_type, + }) # Categories. - for cat in item["categories"]: + for cat in item['categories']: handler.addQuickElement("category", cat) class Atom1Feed(SyndicationFeed): # Spec: https://tools.ietf.org/html/rfc4287 - content_type = "application/atom+xml; charset=utf-8" + content_type = 'application/atom+xml; charset=utf-8' ns = "http://www.w3.org/2005/Atom" def write(self, outfile, encoding): - handler = SimplerXMLGenerator(outfile, encoding, short_empty_elements=True) + handler = SimplerXMLGenerator(outfile, encoding) handler.startDocument() - handler.startElement("feed", self.root_attributes()) + handler.startElement('feed', self.root_attributes()) self.add_root_elements(handler) self.write_items(handler) handler.endElement("feed") def root_attributes(self): - if self.feed["language"] is not None: - return {"xmlns": self.ns, "xml:lang": self.feed["language"]} + if self.feed['language'] is not None: + return {"xmlns": self.ns, "xml:lang": self.feed['language']} else: return {"xmlns": self.ns} def add_root_elements(self, handler): - handler.addQuickElement("title", self.feed["title"]) - handler.addQuickElement( - "link", "", {"rel": "alternate", "href": self.feed["link"]} - ) - if self.feed["feed_url"] is not None: - handler.addQuickElement( - "link", "", {"rel": "self", "href": self.feed["feed_url"]} - ) - handler.addQuickElement("id", self.feed["id"]) + handler.addQuickElement("title", self.feed['title']) + handler.addQuickElement("link", "", {"rel": "alternate", "href": self.feed['link']}) + if self.feed['feed_url'] is not None: + handler.addQuickElement("link", "", {"rel": "self", "href": self.feed['feed_url']}) + handler.addQuickElement("id", self.feed['id']) handler.addQuickElement("updated", rfc3339_date(self.latest_post_date())) - if self.feed["author_name"] is not None: + if self.feed['author_name'] is not None: handler.startElement("author", {}) - handler.addQuickElement("name", self.feed["author_name"]) - if self.feed["author_email"] is not None: - handler.addQuickElement("email", self.feed["author_email"]) - if self.feed["author_link"] is not None: - handler.addQuickElement("uri", self.feed["author_link"]) + handler.addQuickElement("name", self.feed['author_name']) + if self.feed['author_email'] is not None: + handler.addQuickElement("email", self.feed['author_email']) + if self.feed['author_link'] is not None: + handler.addQuickElement("uri", self.feed['author_link']) handler.endElement("author") - if self.feed["subtitle"] is not None: - handler.addQuickElement("subtitle", self.feed["subtitle"]) - for cat in self.feed["categories"]: + if self.feed['subtitle'] is not None: + handler.addQuickElement("subtitle", self.feed['subtitle']) + for cat in self.feed['categories']: handler.addQuickElement("category", "", {"term": cat}) - if self.feed["feed_copyright"] is not None: - handler.addQuickElement("rights", self.feed["feed_copyright"]) + if self.feed['feed_copyright'] is not None: + handler.addQuickElement("rights", self.feed['feed_copyright']) def write_items(self, handler): for item in self.items: @@ -391,56 +339,52 @@ class Atom1Feed(SyndicationFeed): handler.endElement("entry") def add_item_elements(self, handler, item): - handler.addQuickElement("title", item["title"]) - handler.addQuickElement("link", "", {"href": item["link"], "rel": "alternate"}) + handler.addQuickElement("title", item['title']) + handler.addQuickElement("link", "", {"href": item['link'], "rel": "alternate"}) - if item["pubdate"] is not None: - handler.addQuickElement("published", rfc3339_date(item["pubdate"])) + if item['pubdate'] is not None: + handler.addQuickElement('published', rfc3339_date(item['pubdate'])) - if item["updateddate"] is not None: - handler.addQuickElement("updated", rfc3339_date(item["updateddate"])) + if item['updateddate'] is not None: + handler.addQuickElement('updated', rfc3339_date(item['updateddate'])) # Author information. - if item["author_name"] is not None: + if item['author_name'] is not None: handler.startElement("author", {}) - handler.addQuickElement("name", item["author_name"]) - if item["author_email"] is not None: - handler.addQuickElement("email", item["author_email"]) - if item["author_link"] is not None: - handler.addQuickElement("uri", item["author_link"]) + handler.addQuickElement("name", item['author_name']) + if item['author_email'] is not None: + handler.addQuickElement("email", item['author_email']) + if item['author_link'] is not None: + handler.addQuickElement("uri", item['author_link']) handler.endElement("author") # Unique ID. - if item["unique_id"] is not None: - unique_id = item["unique_id"] + if item['unique_id'] is not None: + unique_id = item['unique_id'] else: - unique_id = get_tag_uri(item["link"], item["pubdate"]) + unique_id = get_tag_uri(item['link'], item['pubdate']) handler.addQuickElement("id", unique_id) # Summary. - if item["description"] is not None: - handler.addQuickElement("summary", item["description"], {"type": "html"}) + if item['description'] is not None: + handler.addQuickElement("summary", item['description'], {"type": "html"}) # Enclosures. - for enclosure in item["enclosures"]: - handler.addQuickElement( - "link", - "", - { - "rel": "enclosure", - "href": enclosure.url, - "length": enclosure.length, - "type": enclosure.mime_type, - }, - ) + for enclosure in item['enclosures']: + handler.addQuickElement('link', '', { + 'rel': 'enclosure', + 'href': enclosure.url, + 'length': enclosure.length, + 'type': enclosure.mime_type, + }) # Categories. - for cat in item["categories"]: + for cat in item['categories']: handler.addQuickElement("category", "", {"term": cat}) # Rights. - if item["item_copyright"] is not None: - handler.addQuickElement("rights", item["item_copyright"]) + if item['item_copyright'] is not None: + handler.addQuickElement("rights", item['item_copyright']) # This isolates the decision of what the system default is, so calling code can diff --git a/venv/Lib/site-packages/django/utils/formats.py b/venv/Lib/site-packages/django/utils/formats.py index 420f4f6..028e114 100644 --- a/venv/Lib/site-packages/django/utils/formats.py +++ b/venv/Lib/site-packages/django/utils/formats.py @@ -1,14 +1,14 @@ import datetime import decimal -import functools -import re import unicodedata from importlib import import_module from django.conf import settings -from django.utils import dateformat, numberformat +from django.utils import dateformat, datetime_safe, numberformat from django.utils.functional import lazy -from django.utils.translation import check_for_language, get_language, to_locale +from django.utils.translation import ( + check_for_language, get_language, to_locale, +) # format_cache is a mapping from (format_type, lang) to the format string. # By using the cache, it is possible to avoid running get_format_modules @@ -17,35 +17,33 @@ _format_cache = {} _format_modules_cache = {} ISO_INPUT_FORMATS = { - "DATE_INPUT_FORMATS": ["%Y-%m-%d"], - "TIME_INPUT_FORMATS": ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"], - "DATETIME_INPUT_FORMATS": [ - "%Y-%m-%d %H:%M:%S", - "%Y-%m-%d %H:%M:%S.%f", - "%Y-%m-%d %H:%M", - "%Y-%m-%d", + 'DATE_INPUT_FORMATS': ['%Y-%m-%d'], + 'TIME_INPUT_FORMATS': ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'], + 'DATETIME_INPUT_FORMATS': [ + '%Y-%m-%d %H:%M:%S', + '%Y-%m-%d %H:%M:%S.%f', + '%Y-%m-%d %H:%M', + '%Y-%m-%d' ], } -FORMAT_SETTINGS = frozenset( - [ - "DECIMAL_SEPARATOR", - "THOUSAND_SEPARATOR", - "NUMBER_GROUPING", - "FIRST_DAY_OF_WEEK", - "MONTH_DAY_FORMAT", - "TIME_FORMAT", - "DATE_FORMAT", - "DATETIME_FORMAT", - "SHORT_DATE_FORMAT", - "SHORT_DATETIME_FORMAT", - "YEAR_MONTH_FORMAT", - "DATE_INPUT_FORMATS", - "TIME_INPUT_FORMATS", - "DATETIME_INPUT_FORMATS", - ] -) +FORMAT_SETTINGS = frozenset([ + 'DECIMAL_SEPARATOR', + 'THOUSAND_SEPARATOR', + 'NUMBER_GROUPING', + 'FIRST_DAY_OF_WEEK', + 'MONTH_DAY_FORMAT', + 'TIME_FORMAT', + 'DATE_FORMAT', + 'DATETIME_FORMAT', + 'SHORT_DATE_FORMAT', + 'SHORT_DATETIME_FORMAT', + 'YEAR_MONTH_FORMAT', + 'DATE_INPUT_FORMATS', + 'TIME_INPUT_FORMATS', + 'DATETIME_INPUT_FORMATS', +]) def reset_format_cache(): @@ -72,29 +70,30 @@ def iter_format_modules(lang, format_module_path=None): if isinstance(format_module_path, str): format_module_path = [format_module_path] for path in format_module_path: - format_locations.append(path + ".%s") - format_locations.append("django.conf.locale.%s") + format_locations.append(path + '.%s') + format_locations.append('django.conf.locale.%s') locale = to_locale(lang) locales = [locale] - if "_" in locale: - locales.append(locale.split("_")[0]) + if '_' in locale: + locales.append(locale.split('_')[0]) for location in format_locations: for loc in locales: try: - yield import_module("%s.formats" % (location % loc)) + yield import_module('%s.formats' % (location % loc)) except ImportError: pass -def get_format_modules(lang=None): +def get_format_modules(lang=None, reverse=False): """Return a list of the format modules found.""" if lang is None: lang = get_language() if lang not in _format_modules_cache: - _format_modules_cache[lang] = list( - iter_format_modules(lang, settings.FORMAT_MODULE_PATH) - ) - return _format_modules_cache[lang] + _format_modules_cache[lang] = list(iter_format_modules(lang, settings.FORMAT_MODULE_PATH)) + modules = _format_modules_cache[lang] + if reverse: + return list(reversed(modules)) + return modules def get_format(format_type, lang=None, use_l10n=None): @@ -106,14 +105,7 @@ def get_format(format_type, lang=None, use_l10n=None): If use_l10n is provided and is not None, it forces the value to be localized (or not), overriding the value of settings.USE_L10N. """ - use_l10n = use_l10n or ( - use_l10n is None - and ( - settings._USE_L10N_INTERNAL - if hasattr(settings, "_USE_L10N_INTERNAL") - else settings.USE_L10N - ) - ) + use_l10n = use_l10n or (use_l10n is None and settings.USE_L10N) if use_l10n and lang is None: lang = get_language() cache_key = (format_type, lang) @@ -157,9 +149,7 @@ def date_format(value, format=None, use_l10n=None): If use_l10n is provided and is not None, that will force the value to be localized (or not), overriding the value of settings.USE_L10N. """ - return dateformat.format( - value, get_format(format or "DATE_FORMAT", use_l10n=use_l10n) - ) + return dateformat.format(value, get_format(format or 'DATE_FORMAT', use_l10n=use_l10n)) def time_format(value, format=None, use_l10n=None): @@ -169,9 +159,7 @@ def time_format(value, format=None, use_l10n=None): If use_l10n is provided and is not None, it forces the value to be localized (or not), overriding the value of settings.USE_L10N. """ - return dateformat.time_format( - value, get_format(format or "TIME_FORMAT", use_l10n=use_l10n) - ) + return dateformat.time_format(value, get_format(format or 'TIME_FORMAT', use_l10n=use_l10n)) def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False): @@ -181,21 +169,16 @@ def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False): If use_l10n is provided and is not None, it forces the value to be localized (or not), overriding the value of settings.USE_L10N. """ - use_l10n = use_l10n or ( - use_l10n is None - and ( - settings._USE_L10N_INTERNAL - if hasattr(settings, "_USE_L10N_INTERNAL") - else settings.USE_L10N - ) - ) - lang = get_language() if use_l10n else None + if use_l10n or (use_l10n is None and settings.USE_L10N): + lang = get_language() + else: + lang = None return numberformat.format( value, - get_format("DECIMAL_SEPARATOR", lang, use_l10n=use_l10n), + get_format('DECIMAL_SEPARATOR', lang, use_l10n=use_l10n), decimal_pos, - get_format("NUMBER_GROUPING", lang, use_l10n=use_l10n), - get_format("THOUSAND_SEPARATOR", lang, use_l10n=use_l10n), + get_format('NUMBER_GROUPING', lang, use_l10n=use_l10n), + get_format('THOUSAND_SEPARATOR', lang, use_l10n=use_l10n), force_grouping=force_grouping, use_l10n=use_l10n, ) @@ -218,11 +201,11 @@ def localize(value, use_l10n=None): return str(value) return number_format(value, use_l10n=use_l10n) elif isinstance(value, datetime.datetime): - return date_format(value, "DATETIME_FORMAT", use_l10n=use_l10n) + return date_format(value, 'DATETIME_FORMAT', use_l10n=use_l10n) elif isinstance(value, datetime.date): return date_format(value, use_l10n=use_l10n) elif isinstance(value, datetime.time): - return time_format(value, "TIME_FORMAT", use_l10n=use_l10n) + return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n) return value @@ -238,52 +221,19 @@ def localize_input(value, default=None): elif isinstance(value, (decimal.Decimal, float, int)): return number_format(value) elif isinstance(value, datetime.datetime): - format = default or get_format("DATETIME_INPUT_FORMATS")[0] - format = sanitize_strftime_format(format) + value = datetime_safe.new_datetime(value) + format = default or get_format('DATETIME_INPUT_FORMATS')[0] return value.strftime(format) elif isinstance(value, datetime.date): - format = default or get_format("DATE_INPUT_FORMATS")[0] - format = sanitize_strftime_format(format) + value = datetime_safe.new_date(value) + format = default or get_format('DATE_INPUT_FORMATS')[0] return value.strftime(format) elif isinstance(value, datetime.time): - format = default or get_format("TIME_INPUT_FORMATS")[0] + format = default or get_format('TIME_INPUT_FORMATS')[0] return value.strftime(format) return value -@functools.lru_cache() -def sanitize_strftime_format(fmt): - """ - Ensure that certain specifiers are correctly padded with leading zeros. - - For years < 1000 specifiers %C, %F, %G, and %Y don't work as expected for - strftime provided by glibc on Linux as they don't pad the year or century - with leading zeros. Support for specifying the padding explicitly is - available, however, which can be used to fix this issue. - - FreeBSD, macOS, and Windows do not support explicitly specifying the - padding, but return four digit years (with leading zeros) as expected. - - This function checks whether the %Y produces a correctly padded string and, - if not, makes the following substitutions: - - - %C → %02C - - %F → %010F - - %G → %04G - - %Y → %04Y - - See https://bugs.python.org/issue13305 for more details. - """ - if datetime.date(1, 1, 1).strftime("%Y") == "0001": - return fmt - mapping = {"C": 2, "F": 10, "G": 4, "Y": 4} - return re.sub( - r"((?:^|[^%])(?:%%)*)%([CFGY])", - lambda m: r"%s%%0%s%s" % (m[1], mapping[m[2]], m[2]), - fmt, - ) - - def sanitize_separators(value): """ Sanitize a value according to the current decimal and @@ -291,26 +241,19 @@ def sanitize_separators(value): """ if isinstance(value, str): parts = [] - decimal_separator = get_format("DECIMAL_SEPARATOR") + decimal_separator = get_format('DECIMAL_SEPARATOR') if decimal_separator in value: value, decimals = value.split(decimal_separator, 1) parts.append(decimals) if settings.USE_THOUSAND_SEPARATOR: - thousand_sep = get_format("THOUSAND_SEPARATOR") - if ( - thousand_sep == "." - and value.count(".") == 1 - and len(value.split(".")[-1]) != 3 - ): - # Special case where we suspect a dot meant decimal separator - # (see #22171). + thousand_sep = get_format('THOUSAND_SEPARATOR') + if thousand_sep == '.' and value.count('.') == 1 and len(value.split('.')[-1]) != 3: + # Special case where we suspect a dot meant decimal separator (see #22171) pass else: for replacement in { - thousand_sep, - unicodedata.normalize("NFKD", thousand_sep), - }: - value = value.replace(replacement, "") + thousand_sep, unicodedata.normalize('NFKD', thousand_sep)}: + value = value.replace(replacement, '') parts.append(value) - value = ".".join(reversed(parts)) + value = '.'.join(reversed(parts)) return value diff --git a/venv/Lib/site-packages/django/utils/functional.py b/venv/Lib/site-packages/django/utils/functional.py index bb19e6f..5c8a0c2 100644 --- a/venv/Lib/site-packages/django/utils/functional.py +++ b/venv/Lib/site-packages/django/utils/functional.py @@ -14,19 +14,18 @@ class cached_property: The optional ``name`` argument is obsolete as of Python 3.6 and will be deprecated in Django 4.0 (#30127). """ - name = None @staticmethod def func(instance): raise TypeError( - "Cannot use cached_property instance without calling " - "__set_name__() on it." + 'Cannot use cached_property instance without calling ' + '__set_name__() on it.' ) def __init__(self, func, name=None): self.real_func = func - self.__doc__ = getattr(func, "__doc__") + self.__doc__ = getattr(func, '__doc__') def __set_name__(self, owner, name): if self.name is None: @@ -55,7 +54,6 @@ class classproperty: Decorator that converts a method with a single cls argument into a property that can be accessed directly from the class. """ - def __init__(self, method=None): self.fget = method @@ -72,7 +70,6 @@ class Promise: Base class for the proxy class created in the closure of the lazy function. It's used to recognize promises in code. """ - pass @@ -91,7 +88,6 @@ def lazy(func, *resultclasses): called on the result of that function. The function is not evaluated until one of the methods on the result is called. """ - __prepared = False def __init__(self, args, kw): @@ -104,7 +100,7 @@ def lazy(func, *resultclasses): def __reduce__(self): return ( _lazy_proxy_unpickle, - (func, self.__args, self.__kw) + resultclasses, + (func, self.__args, self.__kw) + resultclasses ) def __repr__(self): @@ -123,10 +119,8 @@ def lazy(func, *resultclasses): setattr(cls, method_name, meth) cls._delegate_bytes = bytes in resultclasses cls._delegate_text = str in resultclasses - if cls._delegate_bytes and cls._delegate_text: - raise ValueError( - "Cannot call lazy() with both bytes and text return types." - ) + assert not (cls._delegate_bytes and cls._delegate_text), ( + "Cannot call lazy() with both bytes and text return types.") if cls._delegate_text: cls.__str__ = cls.__text_cast elif cls._delegate_bytes: @@ -140,7 +134,6 @@ def lazy(func, *resultclasses): # applies the given magic method of the result type. res = func(*self.__args, **self.__kw) return getattr(res, method_name)(*args, **kw) - return __wrapper__ def __text_cast(self): @@ -230,15 +223,10 @@ def keep_lazy(*resultclasses): @wraps(func) def wrapper(*args, **kwargs): - if any( - isinstance(arg, Promise) - for arg in itertools.chain(args, kwargs.values()) - ): + if any(isinstance(arg, Promise) for arg in itertools.chain(args, kwargs.values())): return lazy_func(*args, **kwargs) return func(*args, **kwargs) - return wrapper - return decorator @@ -257,7 +245,6 @@ def new_method_proxy(func): if self._wrapped is empty: self._setup() return func(self._wrapped, *args) - return inner @@ -300,9 +287,7 @@ class LazyObject: """ Must be implemented by subclasses to initialize the wrapped object. """ - raise NotImplementedError( - "subclasses of LazyObject must provide a _setup() method" - ) + raise NotImplementedError('subclasses of LazyObject must provide a _setup() method') # Because we have messed with __class__ below, we confuse pickle as to what # class we are pickling. We're going to have to initialize the wrapped @@ -381,7 +366,6 @@ class SimpleLazyObject(LazyObject): Designed for compound objects of unknown type. For builtins or objects of known type, use django.utils.functional.lazy. """ - def __init__(self, func): """ Pass in a callable that returns the object to be wrapped. @@ -391,7 +375,7 @@ class SimpleLazyObject(LazyObject): callable can be safely run more than once and will return the same value. """ - self.__dict__["_setupfunc"] = func + self.__dict__['_setupfunc'] = func super().__init__() def _setup(self): @@ -404,7 +388,7 @@ class SimpleLazyObject(LazyObject): repr_attr = self._setupfunc else: repr_attr = self._wrapped - return "<%s: %r>" % (type(self).__name__, repr_attr) + return '<%s: %r>' % (type(self).__name__, repr_attr) def __copy__(self): if self._wrapped is empty: diff --git a/venv/Lib/site-packages/django/utils/hashable.py b/venv/Lib/site-packages/django/utils/hashable.py index 042e1a4..7d137cc 100644 --- a/venv/Lib/site-packages/django/utils/hashable.py +++ b/venv/Lib/site-packages/django/utils/hashable.py @@ -8,12 +8,10 @@ def make_hashable(value): The returned value should generate the same hash for equal values. """ if isinstance(value, dict): - return tuple( - [ - (key, make_hashable(nested_value)) - for key, nested_value in sorted(value.items()) - ] - ) + return tuple([ + (key, make_hashable(nested_value)) + for key, nested_value in sorted(value.items()) + ]) # Try hash to avoid converting a hashable iterable (e.g. string, frozenset) # to a tuple. try: diff --git a/venv/Lib/site-packages/django/utils/html.py b/venv/Lib/site-packages/django/utils/html.py index d3b659b..3bc02b8 100644 --- a/venv/Lib/site-packages/django/utils/html.py +++ b/venv/Lib/site-packages/django/utils/html.py @@ -4,7 +4,9 @@ import html import json import re from html.parser import HTMLParser -from urllib.parse import parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit +from urllib.parse import ( + parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit, +) from django.utils.encoding import punycode from django.utils.functional import Promise, keep_lazy, keep_lazy_text @@ -14,16 +16,17 @@ from django.utils.safestring import SafeData, SafeString, mark_safe from django.utils.text import normalize_newlines # Configuration for urlize() function. -TRAILING_PUNCTUATION_CHARS = ".,:;!" -WRAPPING_PUNCTUATION = [("(", ")"), ("[", "]")] +TRAILING_PUNCTUATION_CHARS = '.,:;!' +WRAPPING_PUNCTUATION = [('(', ')'), ('[', ']')] # List of possible strings used for bullets in bulleted lists. -DOTS = ["·", "*", "\u2022", "•", "•", "•"] +DOTS = ['·', '*', '\u2022', '•', '•', '•'] -word_split_re = _lazy_re_compile(r"""([\s<>"']+)""") -simple_url_re = _lazy_re_compile(r"^https?://\[?\w", re.IGNORECASE) +word_split_re = _lazy_re_compile(r'''([\s<>"']+)''') +simple_url_re = _lazy_re_compile(r'^https?://\[?\w', re.IGNORECASE) simple_url_2_re = _lazy_re_compile( - r"^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)($|/.*)$", re.IGNORECASE + r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)($|/.*)$', + re.IGNORECASE ) @@ -41,22 +44,22 @@ def escape(text): _js_escapes = { - ord("\\"): "\\u005C", - ord("'"): "\\u0027", - ord('"'): "\\u0022", - ord(">"): "\\u003E", - ord("<"): "\\u003C", - ord("&"): "\\u0026", - ord("="): "\\u003D", - ord("-"): "\\u002D", - ord(";"): "\\u003B", - ord("`"): "\\u0060", - ord("\u2028"): "\\u2028", - ord("\u2029"): "\\u2029", + ord('\\'): '\\u005C', + ord('\''): '\\u0027', + ord('"'): '\\u0022', + ord('>'): '\\u003E', + ord('<'): '\\u003C', + ord('&'): '\\u0026', + ord('='): '\\u003D', + ord('-'): '\\u002D', + ord(';'): '\\u003B', + ord('`'): '\\u0060', + ord('\u2028'): '\\u2028', + ord('\u2029'): '\\u2029' } # Escape every ASCII character with a value less than 32. -_js_escapes.update((ord("%c" % z), "\\u%04X" % z) for z in range(32)) +_js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32)) @keep_lazy(str, SafeString) @@ -66,9 +69,9 @@ def escapejs(value): _json_script_escapes = { - ord(">"): "\\u003E", - ord("<"): "\\u003C", - ord("&"): "\\u0026", + ord('>'): '\\u003E', + ord('<'): '\\u003C', + ord('&'): '\\u0026', } @@ -79,12 +82,10 @@ def json_script(value, element_id): the escaped JSON in a script tag. """ from django.core.serializers.json import DjangoJSONEncoder - json_str = json.dumps(value, cls=DjangoJSONEncoder).translate(_json_script_escapes) return format_html( '<script id="{}" type="application/json">{}</script>', - element_id, - mark_safe(json_str), + element_id, mark_safe(json_str) ) @@ -97,7 +98,7 @@ def conditional_escape(text): """ if isinstance(text, Promise): text = str(text) - if hasattr(text, "__html__"): + if hasattr(text, '__html__'): return text.__html__() else: return escape(text) @@ -128,23 +129,22 @@ def format_html_join(sep, format_string, args_generator): format_html_join('\n', "<li>{} {}</li>", ((u.first_name, u.last_name) for u in users)) """ - return mark_safe( - conditional_escape(sep).join( - format_html(format_string, *args) for args in args_generator - ) - ) + return mark_safe(conditional_escape(sep).join( + format_html(format_string, *args) + for args in args_generator + )) @keep_lazy_text def linebreaks(value, autoescape=False): """Convert newlines into <p> and <br>s.""" value = normalize_newlines(value) - paras = re.split("\n{2,}", str(value)) + paras = re.split('\n{2,}', str(value)) if autoescape: - paras = ["<p>%s</p>" % escape(p).replace("\n", "<br>") for p in paras] + paras = ['<p>%s</p>' % escape(p).replace('\n', '<br>') for p in paras] else: - paras = ["<p>%s</p>" % p.replace("\n", "<br>") for p in paras] - return "\n\n".join(paras) + paras = ['<p>%s</p>' % p.replace('\n', '<br>') for p in paras] + return '\n\n'.join(paras) class MLStripper(HTMLParser): @@ -157,13 +157,13 @@ class MLStripper(HTMLParser): self.fed.append(d) def handle_entityref(self, name): - self.fed.append("&%s;" % name) + self.fed.append('&%s;' % name) def handle_charref(self, name): - self.fed.append("&#%s;" % name) + self.fed.append('&#%s;' % name) def get_data(self): - return "".join(self.fed) + return ''.join(self.fed) def _strip_once(value): @@ -182,9 +182,9 @@ def strip_tags(value): # Note: in typical case this loop executes _strip_once once. Loop condition # is redundant, but helps to reduce number of executions of _strip_once. value = str(value) - while "<" in value and ">" in value: + while '<' in value and '>' in value: new_value = _strip_once(value) - if value.count("<") == new_value.count("<"): + if value.count('<') == new_value.count('<'): # _strip_once wasn't able to detect more tags. break value = new_value @@ -194,18 +194,17 @@ def strip_tags(value): @keep_lazy_text def strip_spaces_between_tags(value): """Return the given HTML with spaces between tags removed.""" - return re.sub(r">\s+<", "><", str(value)) + return re.sub(r'>\s+<', '><', str(value)) def smart_urlquote(url): """Quote a URL if it isn't already quoted.""" - def unquote_quote(segment): segment = unquote(segment) # Tilde is part of RFC3986 Unreserved Characters # https://tools.ietf.org/html/rfc3986#section-2.3 # See also https://bugs.python.org/issue16285 - return quote(segment, safe=RFC3986_SUBDELIMS + RFC3986_GENDELIMS + "~") + return quote(segment, safe=RFC3986_SUBDELIMS + RFC3986_GENDELIMS + '~') # Handle IDN before quoting. try: @@ -222,10 +221,8 @@ def smart_urlquote(url): if query: # Separately unquoting key/value, so as to not mix querystring separators # included in query values. See #22267. - query_parts = [ - (unquote(q[0]), unquote(q[1])) - for q in parse_qsl(query, keep_blank_values=True) - ] + query_parts = [(unquote(q[0]), unquote(q[1])) + for q in parse_qsl(query, keep_blank_values=True)] # urlencode will take care of quoting query = urlencode(query_parts) @@ -257,7 +254,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): def trim_url(x, limit=trim_url_limit): if limit is None or len(x) <= limit: return x - return "%s…" % x[: max(0, limit - 1)] + return '%s…' % x[:max(0, limit - 1)] def trim_punctuation(lead, middle, trail): """ @@ -271,15 +268,13 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): # Trim wrapping punctuation. for opening, closing in WRAPPING_PUNCTUATION: if middle.startswith(opening): - middle = middle[len(opening) :] + middle = middle[len(opening):] lead += opening trimmed_something = True # Keep parentheses at the end only if they're balanced. - if ( - middle.endswith(closing) - and middle.count(closing) == middle.count(opening) + 1 - ): - middle = middle[: -len(closing)] + if (middle.endswith(closing) and + middle.count(closing) == middle.count(opening) + 1): + middle = middle[:-len(closing)] trail = closing + trail trimmed_something = True # Trim trailing punctuation (after trimming wrapping punctuation, @@ -288,52 +283,51 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): middle_unescaped = html.unescape(middle) stripped = middle_unescaped.rstrip(TRAILING_PUNCTUATION_CHARS) if middle_unescaped != stripped: - punctuation_count = len(middle_unescaped) - len(stripped) - trail = middle[-punctuation_count:] + trail - middle = middle[:-punctuation_count] + trail = middle[len(stripped):] + trail + middle = middle[:len(stripped) - len(middle_unescaped)] trimmed_something = True return lead, middle, trail def is_email_simple(value): """Return True if value looks like an email address.""" # An @ must be in the middle of the value. - if "@" not in value or value.startswith("@") or value.endswith("@"): + if '@' not in value or value.startswith('@') or value.endswith('@'): return False try: - p1, p2 = value.split("@") + p1, p2 = value.split('@') except ValueError: # value contains more than one @. return False # Dot must be in p2 (e.g. example.com) - if "." not in p2 or p2.startswith("."): + if '.' not in p2 or p2.startswith('.'): return False return True words = word_split_re.split(str(text)) for i, word in enumerate(words): - if "." in word or "@" in word or ":" in word: + if '.' in word or '@' in word or ':' in word: # lead: Current punctuation trimmed from the beginning of the word. # middle: Current state of the word. # trail: Current punctuation trimmed from the end of the word. - lead, middle, trail = "", word, "" + lead, middle, trail = '', word, '' # Deal with punctuation. lead, middle, trail = trim_punctuation(lead, middle, trail) # Make URL we want to point to. url = None - nofollow_attr = ' rel="nofollow"' if nofollow else "" + nofollow_attr = ' rel="nofollow"' if nofollow else '' if simple_url_re.match(middle): url = smart_urlquote(html.unescape(middle)) elif simple_url_2_re.match(middle): - url = smart_urlquote("http://%s" % html.unescape(middle)) - elif ":" not in middle and is_email_simple(middle): - local, domain = middle.rsplit("@", 1) + url = smart_urlquote('http://%s' % html.unescape(middle)) + elif ':' not in middle and is_email_simple(middle): + local, domain = middle.rsplit('@', 1) try: domain = punycode(domain) except UnicodeError: continue - url = "mailto:%s@%s" % (local, domain) - nofollow_attr = "" + url = 'mailto:%s@%s' % (local, domain) + nofollow_attr = '' # Make link. if url: @@ -342,7 +336,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): lead, trail = escape(lead), escape(trail) trimmed = escape(trimmed) middle = '<a href="%s"%s>%s</a>' % (escape(url), nofollow_attr, trimmed) - words[i] = mark_safe("%s%s%s" % (lead, middle, trail)) + words[i] = mark_safe('%s%s%s' % (lead, middle, trail)) else: if safe_input: words[i] = mark_safe(word) @@ -352,7 +346,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): words[i] = mark_safe(word) elif autoescape: words[i] = escape(word) - return "".join(words) + return ''.join(words) def avoid_wrapping(value): @@ -368,12 +362,12 @@ def html_safe(klass): A decorator that defines the __html__ method. This helps non-Django templates to detect classes whose __str__ methods return SafeString. """ - if "__html__" in klass.__dict__: + if '__html__' in klass.__dict__: raise ValueError( "can't apply @html_safe to %s because it defines " "__html__()." % klass.__name__ ) - if "__str__" not in klass.__dict__: + if '__str__' not in klass.__dict__: raise ValueError( "can't apply @html_safe to %s because it doesn't " "define __str__()." % klass.__name__ diff --git a/venv/Lib/site-packages/django/utils/http.py b/venv/Lib/site-packages/django/utils/http.py index 4605cf4..61db5e6 100644 --- a/venv/Lib/site-packages/django/utils/http.py +++ b/venv/Lib/site-packages/django/utils/http.py @@ -1,51 +1,103 @@ import base64 +import calendar import datetime import re import unicodedata +import warnings from binascii import Error as BinasciiError from email.utils import formatdate from urllib.parse import ( - ParseResult, - SplitResult, - _coerce_args, - _splitnetloc, - _splitparams, - scheme_chars, + ParseResult, SplitResult, _coerce_args, _splitnetloc, _splitparams, quote, + quote_plus, scheme_chars, unquote, unquote_plus, + urlencode as original_urlencode, uses_params, ) -from urllib.parse import urlencode as original_urlencode -from urllib.parse import uses_params from django.utils.datastructures import MultiValueDict +from django.utils.deprecation import RemovedInDjango40Warning +from django.utils.functional import keep_lazy_text from django.utils.regex_helper import _lazy_re_compile # based on RFC 7232, Appendix C -ETAG_MATCH = _lazy_re_compile( - r""" +ETAG_MATCH = _lazy_re_compile(r''' \A( # start of string and capture group (?:W/)? # optional weak indicator " # opening quote [^"]* # any sequence of non-quote characters " # end quote )\Z # end of string and capture group -""", - re.X, -) +''', re.X) -MONTHS = "jan feb mar apr may jun jul aug sep oct nov dec".split() -__D = r"(?P<day>\d{2})" -__D2 = r"(?P<day>[ \d]\d)" -__M = r"(?P<mon>\w{3})" -__Y = r"(?P<year>\d{4})" -__Y2 = r"(?P<year>\d{2})" -__T = r"(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})" -RFC1123_DATE = _lazy_re_compile(r"^\w{3}, %s %s %s %s GMT$" % (__D, __M, __Y, __T)) -RFC850_DATE = _lazy_re_compile(r"^\w{6,9}, %s-%s-%s %s GMT$" % (__D, __M, __Y2, __T)) -ASCTIME_DATE = _lazy_re_compile(r"^\w{3} %s %s %s %s$" % (__M, __D2, __T, __Y)) +MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split() +__D = r'(?P<day>\d{2})' +__D2 = r'(?P<day>[ \d]\d)' +__M = r'(?P<mon>\w{3})' +__Y = r'(?P<year>\d{4})' +__Y2 = r'(?P<year>\d{2})' +__T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})' +RFC1123_DATE = _lazy_re_compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T)) +RFC850_DATE = _lazy_re_compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T)) +ASCTIME_DATE = _lazy_re_compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y)) RFC3986_GENDELIMS = ":/?#[]@" RFC3986_SUBDELIMS = "!$&'()*+,;=" +@keep_lazy_text +def urlquote(url, safe='/'): + """ + A legacy compatibility wrapper to Python's urllib.parse.quote() function. + (was used for unicode handling on Python 2) + """ + warnings.warn( + 'django.utils.http.urlquote() is deprecated in favor of ' + 'urllib.parse.quote().', + RemovedInDjango40Warning, stacklevel=2, + ) + return quote(url, safe) + + +@keep_lazy_text +def urlquote_plus(url, safe=''): + """ + A legacy compatibility wrapper to Python's urllib.parse.quote_plus() + function. (was used for unicode handling on Python 2) + """ + warnings.warn( + 'django.utils.http.urlquote_plus() is deprecated in favor of ' + 'urllib.parse.quote_plus(),', + RemovedInDjango40Warning, stacklevel=2, + ) + return quote_plus(url, safe) + + +@keep_lazy_text +def urlunquote(quoted_url): + """ + A legacy compatibility wrapper to Python's urllib.parse.unquote() function. + (was used for unicode handling on Python 2) + """ + warnings.warn( + 'django.utils.http.urlunquote() is deprecated in favor of ' + 'urllib.parse.unquote().', + RemovedInDjango40Warning, stacklevel=2, + ) + return unquote(quoted_url) + + +@keep_lazy_text +def urlunquote_plus(quoted_url): + """ + A legacy compatibility wrapper to Python's urllib.parse.unquote_plus() + function. (was used for unicode handling on Python 2) + """ + warnings.warn( + 'django.utils.http.urlunquote_plus() is deprecated in favor of ' + 'urllib.parse.unquote_plus().', + RemovedInDjango40Warning, stacklevel=2, + ) + return unquote_plus(quoted_url) + + def urlencode(query, doseq=False): """ A version of Python's urllib.parse.urlencode() function that can operate on @@ -53,7 +105,7 @@ def urlencode(query, doseq=False): """ if isinstance(query, MultiValueDict): query = query.lists() - elif hasattr(query, "items"): + elif hasattr(query, 'items'): query = query.items() query_params = [] for key, value in query: @@ -120,10 +172,9 @@ def parse_http_date(date): else: raise ValueError("%r is not in a valid HTTP date format" % date) try: - tz = datetime.timezone.utc - year = int(m["year"]) + year = int(m['year']) if year < 100: - current_year = datetime.datetime.now(tz=tz).year + current_year = datetime.datetime.utcnow().year current_century = current_year - (current_year % 100) if year - (current_year % 100) > 50: # year that appears to be more than 50 years in the future are @@ -131,13 +182,13 @@ def parse_http_date(date): year += current_century - 100 else: year += current_century - month = MONTHS.index(m["mon"].lower()) + 1 - day = int(m["day"]) - hour = int(m["hour"]) - min = int(m["min"]) - sec = int(m["sec"]) - result = datetime.datetime(year, month, day, hour, min, sec, tzinfo=tz) - return int(result.timestamp()) + month = MONTHS.index(m['mon'].lower()) + 1 + day = int(m['day']) + hour = int(m['hour']) + min = int(m['min']) + sec = int(m['sec']) + result = datetime.datetime(year, month, day, hour, min, sec) + return calendar.timegm(result.utctimetuple()) except Exception as exc: raise ValueError("%r is not a valid date" % date) from exc @@ -154,7 +205,6 @@ def parse_http_date_safe(date): # Base 36 functions: useful for generating compact URLs - def base36_to_int(s): """ Convert a base 36 string to an int. Raise ValueError if the input won't fit @@ -170,12 +220,12 @@ def base36_to_int(s): def int_to_base36(i): """Convert an integer to a base36 string.""" - char_set = "0123456789abcdefghijklmnopqrstuvwxyz" + char_set = '0123456789abcdefghijklmnopqrstuvwxyz' if i < 0: raise ValueError("Negative base36 conversion input.") if i < 36: return char_set[i] - b36 = "" + b36 = '' while i != 0: i, n = divmod(i, 36) b36 = char_set[n] + b36 @@ -187,7 +237,7 @@ def urlsafe_base64_encode(s): Encode a bytestring to a base64 string for use in URLs. Strip any trailing equal signs. """ - return base64.urlsafe_b64encode(s).rstrip(b"\n=").decode("ascii") + return base64.urlsafe_b64encode(s).rstrip(b'\n=').decode('ascii') def urlsafe_base64_decode(s): @@ -197,7 +247,7 @@ def urlsafe_base64_decode(s): """ s = s.encode() try: - return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, b"=")) + return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, b'=')) except (LookupError, BinasciiError) as e: raise ValueError(e) @@ -208,11 +258,11 @@ def parse_etags(etag_str): defined by RFC 7232. Return a list of quoted ETags, or ['*'] if all ETags should be matched. """ - if etag_str.strip() == "*": - return ["*"] + if etag_str.strip() == '*': + return ['*'] else: # Parse each ETag individually, and return any that are valid. - etag_matches = (ETAG_MATCH.match(etag.strip()) for etag in etag_str.split(",")) + etag_matches = (ETAG_MATCH.match(etag.strip()) for etag in etag_str.split(',')) return [match[1] for match in etag_matches if match] @@ -241,9 +291,8 @@ def is_same_domain(host, pattern): pattern = pattern.lower() return ( - pattern[0] == "." - and (host.endswith(pattern) or host == pattern[1:]) - or pattern == host + pattern[0] == '.' and (host.endswith(pattern) or host == pattern[1:]) or + pattern == host ) @@ -270,15 +319,23 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False): allowed_hosts = {allowed_hosts} # Chrome treats \ completely as / in paths but it could be part of some # basic auth credentials so we need to check both URLs. - return _url_has_allowed_host_and_scheme( - url, allowed_hosts, require_https=require_https - ) and _url_has_allowed_host_and_scheme( - url.replace("\\", "/"), allowed_hosts, require_https=require_https + return ( + _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=require_https) and + _url_has_allowed_host_and_scheme(url.replace('\\', '/'), allowed_hosts, require_https=require_https) ) +def is_safe_url(url, allowed_hosts, require_https=False): + warnings.warn( + 'django.utils.http.is_safe_url() is deprecated in favor of ' + 'url_has_allowed_host_and_scheme().', + RemovedInDjango40Warning, stacklevel=2, + ) + return url_has_allowed_host_and_scheme(url, allowed_hosts, require_https) + + # Copied from urllib.parse.urlparse() but uses fixed urlsplit() function. -def _urlparse(url, scheme="", allow_fragments=True): +def _urlparse(url, scheme='', allow_fragments=True): """Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<fragment> Return a 6-tuple: (scheme, netloc, path, params, query, fragment). @@ -287,42 +344,41 @@ def _urlparse(url, scheme="", allow_fragments=True): url, scheme, _coerce_result = _coerce_args(url, scheme) splitresult = _urlsplit(url, scheme, allow_fragments) scheme, netloc, url, query, fragment = splitresult - if scheme in uses_params and ";" in url: + if scheme in uses_params and ';' in url: url, params = _splitparams(url) else: - params = "" + params = '' result = ParseResult(scheme, netloc, url, params, query, fragment) return _coerce_result(result) # Copied from urllib.parse.urlsplit() with # https://github.com/python/cpython/pull/661 applied. -def _urlsplit(url, scheme="", allow_fragments=True): +def _urlsplit(url, scheme='', allow_fragments=True): """Parse a URL into 5 components: <scheme>://<netloc>/<path>?<query>#<fragment> Return a 5-tuple: (scheme, netloc, path, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escapes.""" url, scheme, _coerce_result = _coerce_args(url, scheme) - netloc = query = fragment = "" - i = url.find(":") + netloc = query = fragment = '' + i = url.find(':') if i > 0: for c in url[:i]: if c not in scheme_chars: break else: - scheme, url = url[:i].lower(), url[i + 1 :] + scheme, url = url[:i].lower(), url[i + 1:] - if url[:2] == "//": + if url[:2] == '//': netloc, url = _splitnetloc(url, 2) - if ("[" in netloc and "]" not in netloc) or ( - "]" in netloc and "[" not in netloc - ): + if (('[' in netloc and ']' not in netloc) or + (']' in netloc and '[' not in netloc)): raise ValueError("Invalid IPv6 URL") - if allow_fragments and "#" in url: - url, fragment = url.split("#", 1) - if "?" in url: - url, query = url.split("?", 1) + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: + url, query = url.split('?', 1) v = SplitResult(scheme, netloc, url, query, fragment) return _coerce_result(v) @@ -330,7 +386,7 @@ def _urlsplit(url, scheme="", allow_fragments=True): def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False): # Chrome considers any URL with more than two slashes to be absolute, but # urlparse is not so flexible. Treat any url with three slashes as unsafe. - if url.startswith("///"): + if url.startswith('///'): return False try: url_info = _urlparse(url) @@ -345,16 +401,93 @@ def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False): # Forbid URLs that start with control characters. Some browsers (like # Chrome) ignore quite a few control characters at the start of a # URL and might consider the URL as scheme relative. - if unicodedata.category(url[0])[0] == "C": + if unicodedata.category(url[0])[0] == 'C': return False scheme = url_info.scheme # Consider URLs without a scheme (e.g. //example.com/p) to be http. if not url_info.scheme and url_info.netloc: - scheme = "http" - valid_schemes = ["https"] if require_https else ["http", "https"] - return (not url_info.netloc or url_info.netloc in allowed_hosts) and ( - not scheme or scheme in valid_schemes - ) + scheme = 'http' + valid_schemes = ['https'] if require_https else ['http', 'https'] + return ((not url_info.netloc or url_info.netloc in allowed_hosts) and + (not scheme or scheme in valid_schemes)) + + +# TODO: Remove when dropping support for PY37. +def parse_qsl( + qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', + errors='replace', max_num_fields=None, separator='&', +): + """ + Return a list of key/value tuples parsed from query string. + + Backport of urllib.parse.parse_qsl() from Python 3.8.8. + Copyright (C) 2021 Python Software Foundation (see LICENSE.python). + + ---- + + Parse a query given as a string argument. + + Arguments: + + qs: percent-encoded query string to be parsed + + keep_blank_values: flag indicating whether blank values in + percent-encoded queries should be treated as blank strings. A + true value indicates that blanks should be retained as blank + strings. The default false value indicates that blank values + are to be ignored and treated as if they were not included. + + strict_parsing: flag indicating what to do with parsing errors. If false + (the default), errors are silently ignored. If true, errors raise a + ValueError exception. + + encoding and errors: specify how to decode percent-encoded sequences + into Unicode characters, as accepted by the bytes.decode() method. + + max_num_fields: int. If set, then throws a ValueError if there are more + than n fields read by parse_qsl(). + + separator: str. The symbol to use for separating the query arguments. + Defaults to &. + + Returns a list, as G-d intended. + """ + qs, _coerce_result = _coerce_args(qs) + + if not separator or not isinstance(separator, (str, bytes)): + raise ValueError('Separator must be of type string or bytes.') + + # If max_num_fields is defined then check that the number of fields is less + # than max_num_fields. This prevents a memory exhaustion DOS attack via + # post bodies with many fields. + if max_num_fields is not None: + num_fields = 1 + qs.count(separator) + if max_num_fields < num_fields: + raise ValueError('Max number of fields exceeded') + + pairs = [s1 for s1 in qs.split(separator)] + r = [] + for name_value in pairs: + if not name_value and not strict_parsing: + continue + nv = name_value.split('=', 1) + if len(nv) != 2: + if strict_parsing: + raise ValueError("bad query field: %r" % (name_value,)) + # Handle case of a control-name with no equal sign. + if keep_blank_values: + nv.append('') + else: + continue + if len(nv[1]) or keep_blank_values: + name = nv[0].replace('+', ' ') + name = unquote(name, encoding=encoding, errors=errors) + name = _coerce_result(name) + value = nv[1].replace('+', ' ') + value = unquote(value, encoding=encoding, errors=errors) + value = _coerce_result(value) + r.append((name, value)) + return r def escape_leading_slashes(url): @@ -363,6 +496,6 @@ def escape_leading_slashes(url): escaped to prevent browsers from handling the path as schemaless and redirecting to another host. """ - if url.startswith("//"): - url = "/%2F{}".format(url[2:]) + if url.startswith('//'): + url = '/%2F{}'.format(url[2:]) return url diff --git a/venv/Lib/site-packages/django/utils/inspect.py b/venv/Lib/site-packages/django/utils/inspect.py index 28418f7..7e06224 100644 --- a/venv/Lib/site-packages/django/utils/inspect.py +++ b/venv/Lib/site-packages/django/utils/inspect.py @@ -19,8 +19,7 @@ def _get_callable_parameters(meth_or_func): def get_func_args(func): params = _get_callable_parameters(func) return [ - param.name - for param in params + param.name for param in params if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD ] @@ -36,12 +35,12 @@ def get_func_full_args(func): for param in params: name = param.name # Ignore 'self' - if name == "self": + if name == 'self': continue if param.kind == inspect.Parameter.VAR_POSITIONAL: - name = "*" + name + name = '*' + name elif param.kind == inspect.Parameter.VAR_KEYWORD: - name = "**" + name + name = '**' + name if param.default != inspect.Parameter.empty: args.append((name, param.default)) else: @@ -51,21 +50,28 @@ def get_func_full_args(func): def func_accepts_kwargs(func): """Return True if function 'func' accepts keyword arguments **kwargs.""" - return any(p for p in _get_callable_parameters(func) if p.kind == p.VAR_KEYWORD) + return any( + p for p in _get_callable_parameters(func) + if p.kind == p.VAR_KEYWORD + ) def func_accepts_var_args(func): """ Return True if function 'func' accepts positional arguments *args. """ - return any(p for p in _get_callable_parameters(func) if p.kind == p.VAR_POSITIONAL) + return any( + p for p in _get_callable_parameters(func) + if p.kind == p.VAR_POSITIONAL + ) def method_has_no_args(meth): """Return True if a method only accepts 'self'.""" - count = len( - [p for p in _get_callable_parameters(meth) if p.kind == p.POSITIONAL_OR_KEYWORD] - ) + count = len([ + p for p in _get_callable_parameters(meth) + if p.kind == p.POSITIONAL_OR_KEYWORD + ]) return count == 0 if inspect.ismethod(meth) else count == 1 diff --git a/venv/Lib/site-packages/django/utils/ipv6.py b/venv/Lib/site-packages/django/utils/ipv6.py index 88dd6ec..ddb8c80 100644 --- a/venv/Lib/site-packages/django/utils/ipv6.py +++ b/venv/Lib/site-packages/django/utils/ipv6.py @@ -4,9 +4,8 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -def clean_ipv6_address( - ip_str, unpack_ipv4=False, error_message=_("This is not a valid IPv6 address.") -): +def clean_ipv6_address(ip_str, unpack_ipv4=False, + error_message=_("This is not a valid IPv6 address.")): """ Clean an IPv6 address string. @@ -26,12 +25,12 @@ def clean_ipv6_address( try: addr = ipaddress.IPv6Address(int(ipaddress.IPv6Address(ip_str))) except ValueError: - raise ValidationError(error_message, code="invalid") + raise ValidationError(error_message, code='invalid') if unpack_ipv4 and addr.ipv4_mapped: return str(addr.ipv4_mapped) elif addr.ipv4_mapped: - return "::ffff:%s" % str(addr.ipv4_mapped) + return '::ffff:%s' % str(addr.ipv4_mapped) return str(addr) diff --git a/venv/Lib/site-packages/django/utils/jslex.py b/venv/Lib/site-packages/django/utils/jslex.py index 47875c0..1e3a8eb 100644 --- a/venv/Lib/site-packages/django/utils/jslex.py +++ b/venv/Lib/site-packages/django/utils/jslex.py @@ -1,4 +1,4 @@ -"""JsLex: a lexer for JavaScript""" +"""JsLex: a lexer for Javascript""" # Originally from https://bitbucket.org/ned/jslex import re @@ -7,7 +7,6 @@ class Tok: """ A specification for a token class. """ - num = 0 def __init__(self, name, regex, next=None): @@ -76,23 +75,23 @@ class Lexer: class JsLexer(Lexer): """ - A JavaScript lexer + A Javascript lexer >>> lexer = JsLexer() >>> list(lexer.lex("a = 1")) [('id', 'a'), ('ws', ' '), ('punct', '='), ('ws', ' '), ('dnum', '1')] - This doesn't properly handle non-ASCII characters in the JavaScript source. + This doesn't properly handle non-ASCII characters in the Javascript source. """ # Because these tokens are matched as alternatives in a regex, longer # possibilities must appear in the list before shorter ones, for example, # '>>' before '>'. # - # Note that we don't have to detect malformed JavaScript, only properly - # lex correct JavaScript, so much of this is simplified. + # Note that we don't have to detect malformed Javascript, only properly + # lex correct Javascript, so much of this is simplified. - # Details of JavaScript lexical structure are taken from + # Details of Javascript lexical structure are taken from # http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf # A useful explanation of automatic semicolon insertion is at @@ -102,34 +101,21 @@ class JsLexer(Lexer): Tok("comment", r"/\*(.|\n)*?\*/"), Tok("linecomment", r"//.*?$"), Tok("ws", r"\s+"), - Tok( - "keyword", - literals( - """ + Tok("keyword", literals(""" break case catch class const continue debugger default delete do else enum export extends finally for function if import in instanceof new return super switch this throw try typeof var void while with - """, - suffix=r"\b", - ), - next="reg", - ), - Tok("reserved", literals("null true false", suffix=r"\b"), next="div"), - Tok( - "id", - r""" + """, suffix=r"\b"), next='reg'), + Tok("reserved", literals("null true false", suffix=r"\b"), next='div'), + Tok("id", r""" ([a-zA-Z_$ ]|\\u[0-9a-fA-Z]{4}) # first char ([a-zA-Z_$0-9]|\\u[0-9a-fA-F]{4})* # rest chars - """, - next="div", - ), - Tok("hnum", r"0[xX][0-9a-fA-F]+", next="div"), + """, next='div'), + Tok("hnum", r"0[xX][0-9a-fA-F]+", next='div'), Tok("onum", r"0[0-7]+"), - Tok( - "dnum", - r""" + Tok("dnum", r""" ( (0|[1-9][0-9]*) # DecimalIntegerLiteral \. # dot [0-9]* # DecimalDigits-opt @@ -142,23 +128,15 @@ class JsLexer(Lexer): (0|[1-9][0-9]*) # DecimalIntegerLiteral ([eE][-+]?[0-9]+)? # ExponentPart-opt ) - """, - next="div", - ), - Tok( - "punct", - literals( - """ + """, next='div'), + Tok("punct", literals(""" >>>= === !== >>> <<= >>= <= >= == != << >> && || += -= *= %= &= |= ^= - """ - ), - next="reg", - ), - Tok("punct", literals("++ -- ) ]"), next="div"), - Tok("punct", literals("{ } ( [ . ; , < > + - * % & | ^ ! ~ ? : ="), next="reg"), - Tok("string", r'"([^"\\]|(\\(.|\n)))*?"', next="div"), - Tok("string", r"'([^'\\]|(\\(.|\n)))*?'", next="div"), + """), next="reg"), + Tok("punct", literals("++ -- ) ]"), next='div'), + Tok("punct", literals("{ } ( [ . ; , < > + - * % & | ^ ! ~ ? : ="), next='reg'), + Tok("string", r'"([^"\\]|(\\(.|\n)))*?"', next='div'), + Tok("string", r"'([^'\\]|(\\(.|\n)))*?'", next='div'), ] both_after = [ @@ -167,16 +145,13 @@ class JsLexer(Lexer): states = { # slash will mean division - "div": both_before - + [ - Tok("punct", literals("/= /"), next="reg"), - ] - + both_after, + 'div': both_before + [ + Tok("punct", literals("/= /"), next='reg'), + ] + both_after, + # slash will mean regex - "reg": both_before - + [ - Tok( - "regex", + 'reg': both_before + [ + Tok("regex", r""" / # opening slash # First character is.. @@ -199,51 +174,47 @@ class JsLexer(Lexer): )* # many times / # closing slash [a-zA-Z0-9]* # trailing flags - """, - next="div", - ), - ] - + both_after, + """, next='div'), + ] + both_after, } def __init__(self): - super().__init__(self.states, "reg") + super().__init__(self.states, 'reg') def prepare_js_for_gettext(js): """ - Convert the JavaScript source `js` into something resembling C for + Convert the Javascript source `js` into something resembling C for xgettext. What actually happens is that all the regex literals are replaced with "REGEX". """ - def escape_quotes(m): """Used in a regex to properly escape double quotes.""" s = m[0] if s == '"': - return r"\"" + return r'\"' else: return s lexer = JsLexer() c = [] for name, tok in lexer.lex(js): - if name == "regex": + if name == 'regex': # C doesn't grok regexes, and they aren't needed for gettext, # so just output a string instead. tok = '"REGEX"' - elif name == "string": + elif name == 'string': # C doesn't have single-quoted strings, so make all strings # double-quoted. if tok.startswith("'"): guts = re.sub(r"\\.|.", escape_quotes, tok[1:-1]) tok = '"' + guts + '"' - elif name == "id": + elif name == 'id': # C can't deal with Unicode escapes in identifiers. We don't # need them for gettext anyway, so replace them with something # innocuous tok = tok.replace("\\", "U") c.append(tok) - return "".join(c) + return ''.join(c) diff --git a/venv/Lib/site-packages/django/utils/log.py b/venv/Lib/site-packages/django/utils/log.py index dae2610..3d3e870 100644 --- a/venv/Lib/site-packages/django/utils/log.py +++ b/venv/Lib/site-packages/django/utils/log.py @@ -8,59 +8,58 @@ from django.core.mail import get_connection from django.core.management.color import color_style from django.utils.module_loading import import_string -request_logger = logging.getLogger("django.request") +request_logger = logging.getLogger('django.request') # Default logging for Django. This sends an email to the site admins on every # HTTP 500 error. Depending on DEBUG, all other log records are either sent to # the console (DEBUG=True) or discarded (DEBUG=False) by means of the -# require_debug_true filter. This configuration is quoted in -# docs/ref/logging.txt; please amend it there if edited here. +# require_debug_true filter. DEFAULT_LOGGING = { - "version": 1, - "disable_existing_loggers": False, - "filters": { - "require_debug_false": { - "()": "django.utils.log.RequireDebugFalse", + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse', }, - "require_debug_true": { - "()": "django.utils.log.RequireDebugTrue", + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', }, }, - "formatters": { - "django.server": { - "()": "django.utils.log.ServerFormatter", - "format": "[{server_time}] {message}", - "style": "{", + 'formatters': { + 'django.server': { + '()': 'django.utils.log.ServerFormatter', + 'format': '[{server_time}] {message}', + 'style': '{', } }, - "handlers": { - "console": { - "level": "INFO", - "filters": ["require_debug_true"], - "class": "logging.StreamHandler", + 'handlers': { + 'console': { + 'level': 'INFO', + 'filters': ['require_debug_true'], + 'class': 'logging.StreamHandler', }, - "django.server": { - "level": "INFO", - "class": "logging.StreamHandler", - "formatter": "django.server", - }, - "mail_admins": { - "level": "ERROR", - "filters": ["require_debug_false"], - "class": "django.utils.log.AdminEmailHandler", + 'django.server': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'django.server', }, + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } }, - "loggers": { - "django": { - "handlers": ["console", "mail_admins"], - "level": "INFO", + 'loggers': { + 'django': { + 'handlers': ['console', 'mail_admins'], + 'level': 'INFO', }, - "django.server": { - "handlers": ["django.server"], - "level": "INFO", - "propagate": False, + 'django.server': { + 'handlers': ['django.server'], + 'level': 'INFO', + 'propagate': False, }, - }, + } } @@ -87,24 +86,22 @@ class AdminEmailHandler(logging.Handler): super().__init__() self.include_html = include_html self.email_backend = email_backend - self.reporter_class = import_string( - reporter_class or settings.DEFAULT_EXCEPTION_REPORTER - ) + self.reporter_class = import_string(reporter_class or settings.DEFAULT_EXCEPTION_REPORTER) def emit(self, record): try: request = record.request - subject = "%s (%s IP): %s" % ( + subject = '%s (%s IP): %s' % ( record.levelname, - ( - "internal" - if request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS - else "EXTERNAL" - ), - record.getMessage(), + ('internal' if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS + else 'EXTERNAL'), + record.getMessage() ) except Exception: - subject = "%s: %s" % (record.levelname, record.getMessage()) + subject = '%s: %s' % ( + record.levelname, + record.getMessage() + ) request = None subject = self.format_subject(subject) @@ -120,17 +117,12 @@ class AdminEmailHandler(logging.Handler): exc_info = (None, record.getMessage(), None) reporter = self.reporter_class(request, is_email=True, *exc_info) - message = "%s\n\n%s" % ( - self.format(no_exc_record), - reporter.get_traceback_text(), - ) + message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text()) html_message = reporter.get_traceback_html() if self.include_html else None self.send_mail(subject, message, fail_silently=True, html_message=html_message) def send_mail(self, subject, message, *args, **kwargs): - mail.mail_admins( - subject, message, *args, connection=self.connection(), **kwargs - ) + mail.mail_admins(subject, message, *args, connection=self.connection(), **kwargs) def connection(self): return get_connection(backend=self.email_backend, fail_silently=True) @@ -139,7 +131,7 @@ class AdminEmailHandler(logging.Handler): """ Escape CR and LF characters. """ - return subject.replace("\n", "\\n").replace("\r", "\\r") + return subject.replace('\n', '\\n').replace('\r', '\\r') class CallbackFilter(logging.Filter): @@ -148,7 +140,6 @@ class CallbackFilter(logging.Filter): takes the record-to-be-logged as its only parameter) to decide whether to log a record. """ - def __init__(self, callback): self.callback = callback @@ -169,7 +160,7 @@ class RequireDebugTrue(logging.Filter): class ServerFormatter(logging.Formatter): - default_time_format = "%d/%b/%Y %H:%M:%S" + default_time_format = '%d/%b/%Y %H:%M:%S' def __init__(self, *args, **kwargs): self.style = color_style() @@ -177,7 +168,7 @@ class ServerFormatter(logging.Formatter): def format(self, record): msg = record.msg - status_code = getattr(record, "status_code", None) + status_code = getattr(record, 'status_code', None) if status_code: if 200 <= status_code < 300: @@ -197,25 +188,17 @@ class ServerFormatter(logging.Formatter): # Any 5XX, or any other status code msg = self.style.HTTP_SERVER_ERROR(msg) - if self.uses_server_time() and not hasattr(record, "server_time"): + if self.uses_server_time() and not hasattr(record, 'server_time'): record.server_time = self.formatTime(record, self.datefmt) record.msg = msg return super().format(record) def uses_server_time(self): - return self._fmt.find("{server_time}") >= 0 + return self._fmt.find('{server_time}') >= 0 -def log_response( - message, - *args, - response=None, - request=None, - logger=request_logger, - level=None, - exc_info=None, -): +def log_response(message, *args, response=None, request=None, logger=request_logger, level=None, exc_info=None): """ Log errors based on HttpResponse status. @@ -227,23 +210,22 @@ def log_response( # the same response can be received in some cases, e.g., when the # response is the result of an exception and is logged at the time the # exception is caught so that the exc_info can be recorded. - if getattr(response, "_has_been_logged", False): + if getattr(response, '_has_been_logged', False): return if level is None: if response.status_code >= 500: - level = "error" + level = 'error' elif response.status_code >= 400: - level = "warning" + level = 'warning' else: - level = "info" + level = 'info' getattr(logger, level)( - message, - *args, + message, *args, extra={ - "status_code": response.status_code, - "request": request, + 'status_code': response.status_code, + 'request': request, }, exc_info=exc_info, ) diff --git a/venv/Lib/site-packages/django/utils/lorem_ipsum.py b/venv/Lib/site-packages/django/utils/lorem_ipsum.py index 5cbc4e5..cfa675d 100644 --- a/venv/Lib/site-packages/django/utils/lorem_ipsum.py +++ b/venv/Lib/site-packages/django/utils/lorem_ipsum.py @@ -5,220 +5,51 @@ Utility functions for generating "lorem ipsum" Latin text. import random COMMON_P = ( - "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " - "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " - "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " - "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate " - "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " - "occaecat cupidatat non proident, sunt in culpa qui officia deserunt " - "mollit anim id est laborum." + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod ' + 'tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim ' + 'veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ' + 'commodo consequat. Duis aute irure dolor in reprehenderit in voluptate ' + 'velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint ' + 'occaecat cupidatat non proident, sunt in culpa qui officia deserunt ' + 'mollit anim id est laborum.' ) WORDS = ( - "exercitationem", - "perferendis", - "perspiciatis", - "laborum", - "eveniet", - "sunt", - "iure", - "nam", - "nobis", - "eum", - "cum", - "officiis", - "excepturi", - "odio", - "consectetur", - "quasi", - "aut", - "quisquam", - "vel", - "eligendi", - "itaque", - "non", - "odit", - "tempore", - "quaerat", - "dignissimos", - "facilis", - "neque", - "nihil", - "expedita", - "vitae", - "vero", - "ipsum", - "nisi", - "animi", - "cumque", - "pariatur", - "velit", - "modi", - "natus", - "iusto", - "eaque", - "sequi", - "illo", - "sed", - "ex", - "et", - "voluptatibus", - "tempora", - "veritatis", - "ratione", - "assumenda", - "incidunt", - "nostrum", - "placeat", - "aliquid", - "fuga", - "provident", - "praesentium", - "rem", - "necessitatibus", - "suscipit", - "adipisci", - "quidem", - "possimus", - "voluptas", - "debitis", - "sint", - "accusantium", - "unde", - "sapiente", - "voluptate", - "qui", - "aspernatur", - "laudantium", - "soluta", - "amet", - "quo", - "aliquam", - "saepe", - "culpa", - "libero", - "ipsa", - "dicta", - "reiciendis", - "nesciunt", - "doloribus", - "autem", - "impedit", - "minima", - "maiores", - "repudiandae", - "ipsam", - "obcaecati", - "ullam", - "enim", - "totam", - "delectus", - "ducimus", - "quis", - "voluptates", - "dolores", - "molestiae", - "harum", - "dolorem", - "quia", - "voluptatem", - "molestias", - "magni", - "distinctio", - "omnis", - "illum", - "dolorum", - "voluptatum", - "ea", - "quas", - "quam", - "corporis", - "quae", - "blanditiis", - "atque", - "deserunt", - "laboriosam", - "earum", - "consequuntur", - "hic", - "cupiditate", - "quibusdam", - "accusamus", - "ut", - "rerum", - "error", - "minus", - "eius", - "ab", - "ad", - "nemo", - "fugit", - "officia", - "at", - "in", - "id", - "quos", - "reprehenderit", - "numquam", - "iste", - "fugiat", - "sit", - "inventore", - "beatae", - "repellendus", - "magnam", - "recusandae", - "quod", - "explicabo", - "doloremque", - "aperiam", - "consequatur", - "asperiores", - "commodi", - "optio", - "dolor", - "labore", - "temporibus", - "repellat", - "veniam", - "architecto", - "est", - "esse", - "mollitia", - "nulla", - "a", - "similique", - "eos", - "alias", - "dolore", - "tenetur", - "deleniti", - "porro", - "facere", - "maxime", - "corrupti", + 'exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet', + 'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi', + 'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi', + 'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos', + 'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum', + 'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus', + 'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus', + 'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum', + 'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem', + 'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus', + 'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente', + 'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet', + 'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta', + 'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima', + 'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim', + 'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores', + 'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias', + 'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea', + 'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt', + 'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate', + 'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius', + 'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos', + 'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore', + 'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo', + 'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi', + 'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam', + 'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique', + 'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere', + 'maxime', 'corrupti', ) COMMON_WORDS = ( - "lorem", - "ipsum", - "dolor", - "sit", - "amet", - "consectetur", - "adipisicing", - "elit", - "sed", - "do", - "eiusmod", - "tempor", - "incididunt", - "ut", - "labore", - "et", - "dolore", - "magna", - "aliqua", + 'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', + 'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', + 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua', ) @@ -231,13 +62,10 @@ def sentence(): """ # Determine the number of comma-separated sections and number of words in # each section for this sentence. - sections = [ - " ".join(random.sample(WORDS, random.randint(3, 12))) - for i in range(random.randint(1, 5)) - ] - s = ", ".join(sections) + sections = [' '.join(random.sample(WORDS, random.randint(3, 12))) for i in range(random.randint(1, 5))] + s = ', '.join(sections) # Convert to sentence case and add end punctuation. - return "%s%s%s" % (s[0].upper(), s[1:], random.choice("?.")) + return '%s%s%s' % (s[0].upper(), s[1:], random.choice('?.')) def paragraph(): @@ -246,7 +74,7 @@ def paragraph(): The paragraph consists of between 1 and 4 sentences, inclusive. """ - return " ".join(sentence() for i in range(random.randint(1, 4))) + return ' '.join(sentence() for i in range(random.randint(1, 4))) def paragraphs(count, common=True): @@ -283,4 +111,4 @@ def words(count, common=True): word_list += random.sample(WORDS, c) else: word_list = word_list[:count] - return " ".join(word_list) + return ' '.join(word_list) diff --git a/venv/Lib/site-packages/django/utils/module_loading.py b/venv/Lib/site-packages/django/utils/module_loading.py index 18155ac..df8e650 100644 --- a/venv/Lib/site-packages/django/utils/module_loading.py +++ b/venv/Lib/site-packages/django/utils/module_loading.py @@ -1,37 +1,26 @@ import copy import os -import sys from importlib import import_module from importlib.util import find_spec as importlib_find -def cached_import(module_path, class_name): - modules = sys.modules - if module_path not in modules or ( - # Module is not fully initialized. - getattr(modules[module_path], "__spec__", None) is not None - and getattr(modules[module_path].__spec__, "_initializing", False) is True - ): - import_module(module_path) - return getattr(modules[module_path], class_name) - - def import_string(dotted_path): """ Import a dotted module path and return the attribute/class designated by the last name in the path. Raise ImportError if the import failed. """ try: - module_path, class_name = dotted_path.rsplit(".", 1) + module_path, class_name = dotted_path.rsplit('.', 1) except ValueError as err: raise ImportError("%s doesn't look like a module path" % dotted_path) from err + module = import_module(module_path) + try: - return cached_import(module_path, class_name) + return getattr(module, class_name) except AttributeError as err: - raise ImportError( - 'Module "%s" does not define a "%s" attribute/class' - % (module_path, class_name) + raise ImportError('Module "%s" does not define a "%s" attribute/class' % ( + module_path, class_name) ) from err @@ -47,7 +36,7 @@ def autodiscover_modules(*args, **kwargs): """ from django.apps import apps - register_to = kwargs.get("register_to") + register_to = kwargs.get('register_to') for app_config in apps.get_app_configs(): for module_to_search in args: # Attempt to import the app's module. @@ -55,7 +44,7 @@ def autodiscover_modules(*args, **kwargs): if register_to: before_import_registry = copy.copy(register_to._registry) - import_module("%s.%s" % (app_config.name, module_to_search)) + import_module('%s.%s' % (app_config.name, module_to_search)) except Exception: # Reset the registry to the state before the last import # as this import will have to reoccur on the next request and @@ -80,12 +69,13 @@ def module_has_submodule(package, module_name): # package isn't a package. return False - full_module_name = package_name + "." + module_name + full_module_name = package_name + '.' + module_name try: return importlib_find(full_module_name, package_path) is not None - except ModuleNotFoundError: + except (ModuleNotFoundError, AttributeError): # When module_name is an invalid dotted path, Python raises - # ModuleNotFoundError. + # ModuleNotFoundError. AttributeError is raised on PY36 (fixed in PY37) + # if the penultimate part of the path is not a package. return False @@ -96,12 +86,12 @@ def module_dir(module): Raise ValueError otherwise, e.g. for namespace packages that are split over several directories. """ - # Convert to list because __path__ may not support indexing. - paths = list(getattr(module, "__path__", [])) + # Convert to list because _NamespacePath does not support indexing. + paths = list(getattr(module, '__path__', [])) if len(paths) == 1: return paths[0] else: - filename = getattr(module, "__file__", None) + filename = getattr(module, '__file__', None) if filename is not None: return os.path.dirname(filename) raise ValueError("Cannot determine directory containing %s" % module) diff --git a/venv/Lib/site-packages/django/utils/numberformat.py b/venv/Lib/site-packages/django/utils/numberformat.py index 488d6a7..3bfdb2e 100644 --- a/venv/Lib/site-packages/django/utils/numberformat.py +++ b/venv/Lib/site-packages/django/utils/numberformat.py @@ -4,15 +4,8 @@ from django.conf import settings from django.utils.safestring import mark_safe -def format( - number, - decimal_sep, - decimal_pos=None, - grouping=0, - thousand_sep="", - force_grouping=False, - use_l10n=None, -): +def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='', + force_grouping=False, use_l10n=None): """ Get a number (as a number or string), and return it as a string, using formats defined as arguments: @@ -25,61 +18,54 @@ def format( module in locale.localeconv() LC_NUMERIC grouping (e.g. (3, 2, 0)). * thousand_sep: Thousand separator symbol (for example ",") """ - use_grouping = ( - use_l10n or (use_l10n is None and settings.USE_L10N) - ) and settings.USE_THOUSAND_SEPARATOR + use_grouping = (use_l10n or (use_l10n is None and settings.USE_L10N)) and settings.USE_THOUSAND_SEPARATOR use_grouping = use_grouping or force_grouping use_grouping = use_grouping and grouping != 0 # Make the common case fast if isinstance(number, int) and not use_grouping and not decimal_pos: return mark_safe(number) # sign - sign = "" + sign = '' # Treat potentially very large/small floats as Decimals. - if isinstance(number, float) and "e" in str(number).lower(): + if isinstance(number, float) and 'e' in str(number).lower(): number = Decimal(str(number)) if isinstance(number, Decimal): if decimal_pos is not None: # If the provided number is too small to affect any of the visible # decimal places, consider it equal to '0'. - cutoff = Decimal("0." + "1".rjust(decimal_pos, "0")) + cutoff = Decimal('0.' + '1'.rjust(decimal_pos, '0')) if abs(number) < cutoff: - number = Decimal("0") + number = Decimal('0') # Format values with more than 200 digits (an arbitrary cutoff) using # scientific notation to avoid high memory usage in {:f}'.format(). _, digits, exponent = number.as_tuple() if abs(exponent) + len(digits) > 200: - number = "{:e}".format(number) - coefficient, exponent = number.split("e") + number = '{:e}'.format(number) + coefficient, exponent = number.split('e') # Format the coefficient. coefficient = format( - coefficient, - decimal_sep, - decimal_pos, - grouping, - thousand_sep, - force_grouping, - use_l10n, + coefficient, decimal_sep, decimal_pos, grouping, + thousand_sep, force_grouping, use_l10n, ) - return "{}e{}".format(coefficient, exponent) + return '{}e{}'.format(coefficient, exponent) else: - str_number = "{:f}".format(number) + str_number = '{:f}'.format(number) else: str_number = str(number) - if str_number[0] == "-": - sign = "-" + if str_number[0] == '-': + sign = '-' str_number = str_number[1:] # decimal part - if "." in str_number: - int_part, dec_part = str_number.split(".") + if '.' in str_number: + int_part, dec_part = str_number.split('.') if decimal_pos is not None: dec_part = dec_part[:decimal_pos] else: - int_part, dec_part = str_number, "" + int_part, dec_part = str_number, '' if decimal_pos is not None: - dec_part = dec_part + ("0" * (decimal_pos - len(dec_part))) + dec_part = dec_part + ('0' * (decimal_pos - len(dec_part))) dec_part = dec_part and decimal_sep + dec_part # grouping if use_grouping: @@ -90,7 +76,7 @@ def format( # grouping is a single value intervals = [grouping, 0] active_interval = intervals.pop(0) - int_part_gd = "" + int_part_gd = '' cnt = 0 for digit in int_part[::-1]: if cnt and cnt == active_interval: diff --git a/venv/Lib/site-packages/django/utils/regex_helper.py b/venv/Lib/site-packages/django/utils/regex_helper.py index 9ee82e1..8612475 100644 --- a/venv/Lib/site-packages/django/utils/regex_helper.py +++ b/venv/Lib/site-packages/django/utils/regex_helper.py @@ -73,23 +73,23 @@ def normalize(pattern): try: ch, escaped = next(pattern_iter) except StopIteration: - return [("", [])] + return [('', [])] try: while True: if escaped: result.append(ch) - elif ch == ".": + elif ch == '.': # Replace "any character" with an arbitrary representative. result.append(".") - elif ch == "|": + elif ch == '|': # FIXME: One day we'll should do this, but not in 1.0. - raise NotImplementedError("Awaiting Implementation") + raise NotImplementedError('Awaiting Implementation') elif ch == "^": pass - elif ch == "$": + elif ch == '$': break - elif ch == ")": + elif ch == ')': # This can only be the end of a non-capturing group, since all # other unescaped parentheses are handled by the grouping # section later (and the full group is handled there). @@ -99,17 +99,17 @@ def normalize(pattern): start = non_capturing_groups.pop() inner = NonCapture(result[start:]) result = result[:start] + [inner] - elif ch == "[": + elif ch == '[': # Replace ranges with the first character in the range. ch, escaped = next(pattern_iter) result.append(ch) ch, escaped = next(pattern_iter) - while escaped or ch != "]": + while escaped or ch != ']': ch, escaped = next(pattern_iter) - elif ch == "(": + elif ch == '(': # Some kind of group. ch, escaped = next(pattern_iter) - if ch != "?" or escaped: + if ch != '?' or escaped: # A positional group name = "_%d" % num_args num_args += 1 @@ -117,39 +117,37 @@ def normalize(pattern): walk_to_end(ch, pattern_iter) else: ch, escaped = next(pattern_iter) - if ch in "!=<": + if ch in '!=<': # All of these are ignorable. Walk to the end of the # group. walk_to_end(ch, pattern_iter) - elif ch == ":": + elif ch == ':': # Non-capturing group non_capturing_groups.append(len(result)) - elif ch != "P": + elif ch != 'P': # Anything else, other than a named group, is something # we cannot reverse. raise ValueError("Non-reversible reg-exp portion: '(?%s'" % ch) else: ch, escaped = next(pattern_iter) - if ch not in ("<", "="): - raise ValueError( - "Non-reversible reg-exp portion: '(?P%s'" % ch - ) + if ch not in ('<', '='): + raise ValueError("Non-reversible reg-exp portion: '(?P%s'" % ch) # We are in a named capturing group. Extra the name and # then skip to the end. - if ch == "<": - terminal_char = ">" + if ch == '<': + terminal_char = '>' # We are in a named backreference. else: - terminal_char = ")" + terminal_char = ')' name = [] ch, escaped = next(pattern_iter) while ch != terminal_char: name.append(ch) ch, escaped = next(pattern_iter) - param = "".join(name) + param = ''.join(name) # Named backreferences have already consumed the # parenthesis. - if terminal_char != ")": + if terminal_char != ')': result.append(Group((("%%(%s)s" % param), param))) walk_to_end(ch, pattern_iter) else: @@ -187,7 +185,7 @@ def normalize(pattern): pass except NotImplementedError: # A case of using the disjunctive form. No results for you! - return [("", [])] + return [('', [])] return list(zip(*flatten_result(result))) @@ -203,7 +201,7 @@ def next_char(input_iter): raw (unescaped) character or not. """ for ch in input_iter: - if ch != "\\": + if ch != '\\': yield ch, False continue ch = next(input_iter) @@ -219,16 +217,16 @@ def walk_to_end(ch, input_iter): this group, skipping over any nested groups and handling escaped parentheses correctly. """ - if ch == "(": + if ch == '(': nesting = 1 else: nesting = 0 for ch, escaped in input_iter: if escaped: continue - elif ch == "(": + elif ch == '(': nesting += 1 - elif ch == ")": + elif ch == ')': if not nesting: return nesting -= 1 @@ -243,30 +241,30 @@ def get_quantifier(ch, input_iter): either None or the next character from the input_iter if the next character is not part of the quantifier. """ - if ch in "*?+": + if ch in '*?+': try: ch2, escaped = next(input_iter) except StopIteration: ch2 = None - if ch2 == "?": + if ch2 == '?': ch2 = None - if ch == "+": + if ch == '+': return 1, ch2 return 0, ch2 quant = [] - while ch != "}": + while ch != '}': ch, escaped = next(input_iter) quant.append(ch) quant = quant[:-1] - values = "".join(quant).split(",") + values = ''.join(quant).split(',') # Consume the trailing '?', if necessary. try: ch, escaped = next(input_iter) except StopIteration: ch = None - if ch == "?": + if ch == '?': ch = None return int(values[0]), ch @@ -292,20 +290,20 @@ def flatten_result(source): Each of the two lists will be of the same length. """ if source is None: - return [""], [[]] + return [''], [[]] if isinstance(source, Group): if source[1] is None: params = [] else: params = [source[1]] return [source[0]], [params] - result = [""] + result = [''] result_args = [[]] pos = last = 0 for pos, elt in enumerate(source): if isinstance(elt, str): continue - piece = "".join(source[last:pos]) + piece = ''.join(source[last:pos]) if isinstance(elt, Group): piece += elt[0] param = elt[1] @@ -333,7 +331,7 @@ def flatten_result(source): result = new_result result_args = new_args if pos >= last: - piece = "".join(source[last:]) + piece = ''.join(source[last:]) for i in range(len(result)): result[i] += piece return result, result_args @@ -341,13 +339,13 @@ def flatten_result(source): def _lazy_re_compile(regex, flags=0): """Lazily compile a regex with flags.""" - def _compile(): # Compile the regex if it was not passed pre-compiled. if isinstance(regex, (str, bytes)): return re.compile(regex, flags) else: - assert not flags, "flags must be empty if regex is passed pre-compiled" + assert not flags, ( + 'flags must be empty if regex is passed pre-compiled' + ) return regex - return SimpleLazyObject(_compile) diff --git a/venv/Lib/site-packages/django/utils/safestring.py b/venv/Lib/site-packages/django/utils/safestring.py index 32f7eb6..a484f67 100644 --- a/venv/Lib/site-packages/django/utils/safestring.py +++ b/venv/Lib/site-packages/django/utils/safestring.py @@ -23,7 +23,6 @@ class SafeString(str, SafeData): A str subclass that has been specifically marked as "safe" for HTML output purposes. """ - def __add__(self, rhs): """ Concatenating a safe string with another safe bytestring or @@ -45,7 +44,6 @@ def _safety_decorator(safety_marker, func): @wraps(func) def wrapped(*args, **kwargs): return safety_marker(func(*args, **kwargs)) - return wrapped @@ -58,7 +56,7 @@ def mark_safe(s): Can be called multiple times on a single string. """ - if hasattr(s, "__html__"): + if hasattr(s, '__html__'): return s if callable(s): return _safety_decorator(mark_safe, s) diff --git a/venv/Lib/site-packages/django/utils/termcolors.py b/venv/Lib/site-packages/django/utils/termcolors.py index 3d1eee6..089ae63 100644 --- a/venv/Lib/site-packages/django/utils/termcolors.py +++ b/venv/Lib/site-packages/django/utils/termcolors.py @@ -2,21 +2,15 @@ termcolors.py """ -color_names = ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white") -foreground = {color_names[x]: "3%s" % x for x in range(8)} -background = {color_names[x]: "4%s" % x for x in range(8)} +color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white') +foreground = {color_names[x]: '3%s' % x for x in range(8)} +background = {color_names[x]: '4%s' % x for x in range(8)} -RESET = "0" -opt_dict = { - "bold": "1", - "underscore": "4", - "blink": "5", - "reverse": "7", - "conceal": "8", -} +RESET = '0' +opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'} -def colorize(text="", opts=(), **kwargs): +def colorize(text='', opts=(), **kwargs): """ Return your text, enclosed in ANSI graphics codes. @@ -46,19 +40,19 @@ def colorize(text="", opts=(), **kwargs): print('this should not be red') """ code_list = [] - if text == "" and len(opts) == 1 and opts[0] == "reset": - return "\x1b[%sm" % RESET + if text == '' and len(opts) == 1 and opts[0] == 'reset': + return '\x1b[%sm' % RESET for k, v in kwargs.items(): - if k == "fg": + if k == 'fg': code_list.append(foreground[v]) - elif k == "bg": + elif k == 'bg': code_list.append(background[v]) for o in opts: if o in opt_dict: code_list.append(opt_dict[o]) - if "noreset" not in opts: - text = "%s\x1b[%sm" % (text or "", RESET) - return "%s%s" % (("\x1b[%sm" % ";".join(code_list)), text or "") + if 'noreset' not in opts: + text = '%s\x1b[%sm' % (text or '', RESET) + return '%s%s' % (('\x1b[%sm' % ';'.join(code_list)), text or '') def make_style(opts=(), **kwargs): @@ -74,68 +68,68 @@ def make_style(opts=(), **kwargs): return lambda text: colorize(text, opts, **kwargs) -NOCOLOR_PALETTE = "nocolor" -DARK_PALETTE = "dark" -LIGHT_PALETTE = "light" +NOCOLOR_PALETTE = 'nocolor' +DARK_PALETTE = 'dark' +LIGHT_PALETTE = 'light' PALETTES = { NOCOLOR_PALETTE: { - "ERROR": {}, - "SUCCESS": {}, - "WARNING": {}, - "NOTICE": {}, - "SQL_FIELD": {}, - "SQL_COLTYPE": {}, - "SQL_KEYWORD": {}, - "SQL_TABLE": {}, - "HTTP_INFO": {}, - "HTTP_SUCCESS": {}, - "HTTP_REDIRECT": {}, - "HTTP_NOT_MODIFIED": {}, - "HTTP_BAD_REQUEST": {}, - "HTTP_NOT_FOUND": {}, - "HTTP_SERVER_ERROR": {}, - "MIGRATE_HEADING": {}, - "MIGRATE_LABEL": {}, + 'ERROR': {}, + 'SUCCESS': {}, + 'WARNING': {}, + 'NOTICE': {}, + 'SQL_FIELD': {}, + 'SQL_COLTYPE': {}, + 'SQL_KEYWORD': {}, + 'SQL_TABLE': {}, + 'HTTP_INFO': {}, + 'HTTP_SUCCESS': {}, + 'HTTP_REDIRECT': {}, + 'HTTP_NOT_MODIFIED': {}, + 'HTTP_BAD_REQUEST': {}, + 'HTTP_NOT_FOUND': {}, + 'HTTP_SERVER_ERROR': {}, + 'MIGRATE_HEADING': {}, + 'MIGRATE_LABEL': {}, }, DARK_PALETTE: { - "ERROR": {"fg": "red", "opts": ("bold",)}, - "SUCCESS": {"fg": "green", "opts": ("bold",)}, - "WARNING": {"fg": "yellow", "opts": ("bold",)}, - "NOTICE": {"fg": "red"}, - "SQL_FIELD": {"fg": "green", "opts": ("bold",)}, - "SQL_COLTYPE": {"fg": "green"}, - "SQL_KEYWORD": {"fg": "yellow"}, - "SQL_TABLE": {"opts": ("bold",)}, - "HTTP_INFO": {"opts": ("bold",)}, - "HTTP_SUCCESS": {}, - "HTTP_REDIRECT": {"fg": "green"}, - "HTTP_NOT_MODIFIED": {"fg": "cyan"}, - "HTTP_BAD_REQUEST": {"fg": "red", "opts": ("bold",)}, - "HTTP_NOT_FOUND": {"fg": "yellow"}, - "HTTP_SERVER_ERROR": {"fg": "magenta", "opts": ("bold",)}, - "MIGRATE_HEADING": {"fg": "cyan", "opts": ("bold",)}, - "MIGRATE_LABEL": {"opts": ("bold",)}, + 'ERROR': {'fg': 'red', 'opts': ('bold',)}, + 'SUCCESS': {'fg': 'green', 'opts': ('bold',)}, + 'WARNING': {'fg': 'yellow', 'opts': ('bold',)}, + 'NOTICE': {'fg': 'red'}, + 'SQL_FIELD': {'fg': 'green', 'opts': ('bold',)}, + 'SQL_COLTYPE': {'fg': 'green'}, + 'SQL_KEYWORD': {'fg': 'yellow'}, + 'SQL_TABLE': {'opts': ('bold',)}, + 'HTTP_INFO': {'opts': ('bold',)}, + 'HTTP_SUCCESS': {}, + 'HTTP_REDIRECT': {'fg': 'green'}, + 'HTTP_NOT_MODIFIED': {'fg': 'cyan'}, + 'HTTP_BAD_REQUEST': {'fg': 'red', 'opts': ('bold',)}, + 'HTTP_NOT_FOUND': {'fg': 'yellow'}, + 'HTTP_SERVER_ERROR': {'fg': 'magenta', 'opts': ('bold',)}, + 'MIGRATE_HEADING': {'fg': 'cyan', 'opts': ('bold',)}, + 'MIGRATE_LABEL': {'opts': ('bold',)}, }, LIGHT_PALETTE: { - "ERROR": {"fg": "red", "opts": ("bold",)}, - "SUCCESS": {"fg": "green", "opts": ("bold",)}, - "WARNING": {"fg": "yellow", "opts": ("bold",)}, - "NOTICE": {"fg": "red"}, - "SQL_FIELD": {"fg": "green", "opts": ("bold",)}, - "SQL_COLTYPE": {"fg": "green"}, - "SQL_KEYWORD": {"fg": "blue"}, - "SQL_TABLE": {"opts": ("bold",)}, - "HTTP_INFO": {"opts": ("bold",)}, - "HTTP_SUCCESS": {}, - "HTTP_REDIRECT": {"fg": "green", "opts": ("bold",)}, - "HTTP_NOT_MODIFIED": {"fg": "green"}, - "HTTP_BAD_REQUEST": {"fg": "red", "opts": ("bold",)}, - "HTTP_NOT_FOUND": {"fg": "red"}, - "HTTP_SERVER_ERROR": {"fg": "magenta", "opts": ("bold",)}, - "MIGRATE_HEADING": {"fg": "cyan", "opts": ("bold",)}, - "MIGRATE_LABEL": {"opts": ("bold",)}, - }, + 'ERROR': {'fg': 'red', 'opts': ('bold',)}, + 'SUCCESS': {'fg': 'green', 'opts': ('bold',)}, + 'WARNING': {'fg': 'yellow', 'opts': ('bold',)}, + 'NOTICE': {'fg': 'red'}, + 'SQL_FIELD': {'fg': 'green', 'opts': ('bold',)}, + 'SQL_COLTYPE': {'fg': 'green'}, + 'SQL_KEYWORD': {'fg': 'blue'}, + 'SQL_TABLE': {'opts': ('bold',)}, + 'HTTP_INFO': {'opts': ('bold',)}, + 'HTTP_SUCCESS': {}, + 'HTTP_REDIRECT': {'fg': 'green', 'opts': ('bold',)}, + 'HTTP_NOT_MODIFIED': {'fg': 'green'}, + 'HTTP_BAD_REQUEST': {'fg': 'red', 'opts': ('bold',)}, + 'HTTP_NOT_FOUND': {'fg': 'red'}, + 'HTTP_SERVER_ERROR': {'fg': 'magenta', 'opts': ('bold',)}, + 'MIGRATE_HEADING': {'fg': 'cyan', 'opts': ('bold',)}, + 'MIGRATE_LABEL': {'opts': ('bold',)}, + } } DEFAULT_PALETTE = DARK_PALETTE @@ -175,39 +169,39 @@ def parse_color_setting(config_string): return PALETTES[DEFAULT_PALETTE] # Split the color configuration into parts - parts = config_string.lower().split(";") + parts = config_string.lower().split(';') palette = PALETTES[NOCOLOR_PALETTE].copy() for part in parts: if part in PALETTES: # A default palette has been specified palette.update(PALETTES[part]) - elif "=" in part: + elif '=' in part: # Process a palette defining string definition = {} # Break the definition into the role, # plus the list of specific instructions. # The role must be in upper case - role, instructions = part.split("=") + role, instructions = part.split('=') role = role.upper() - styles = instructions.split(",") + styles = instructions.split(',') styles.reverse() # The first instruction can contain a slash # to break apart fg/bg. - colors = styles.pop().split("/") + colors = styles.pop().split('/') colors.reverse() fg = colors.pop() if fg in color_names: - definition["fg"] = fg + definition['fg'] = fg if colors and colors[-1] in color_names: - definition["bg"] = colors[-1] + definition['bg'] = colors[-1] # All remaining instructions are options opts = tuple(s for s in styles if s in opt_dict) if opts: - definition["opts"] = opts + definition['opts'] = opts # The nocolor palette has all available roles. # Use that palette as the basis for determining diff --git a/venv/Lib/site-packages/django/utils/text.py b/venv/Lib/site-packages/django/utils/text.py index 86849a2..baa44f2 100644 --- a/venv/Lib/site-packages/django/utils/text.py +++ b/venv/Lib/site-packages/django/utils/text.py @@ -1,33 +1,29 @@ import html.entities import re import unicodedata +import warnings from gzip import GzipFile -from gzip import compress as gzip_compress from io import BytesIO from django.core.exceptions import SuspiciousFileOperation +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import SimpleLazyObject, keep_lazy_text, lazy from django.utils.regex_helper import _lazy_re_compile -from django.utils.translation import gettext as _ -from django.utils.translation import gettext_lazy, pgettext +from django.utils.translation import gettext as _, gettext_lazy, pgettext @keep_lazy_text def capfirst(x): """Capitalize the first letter of a string.""" - if not x: - return x - if not isinstance(x, str): - x = str(x) - return x[0].upper() + x[1:] + return x and str(x)[0].upper() + str(x)[1:] # Set up regular expressions -re_words = _lazy_re_compile(r"<[^>]+?>|([^<>\s]+)", re.S) -re_chars = _lazy_re_compile(r"<[^>]+?>|(.)", re.S) -re_tag = _lazy_re_compile(r"<(/)?(\S+?)(?:(\s*/)|\s.*?)?>", re.S) -re_newlines = _lazy_re_compile(r"\r\n|\r") # Used in normalize_newlines -re_camel_case = _lazy_re_compile(r"(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))") +re_words = _lazy_re_compile(r'<[^>]+?>|([^<>\s]+)', re.S) +re_chars = _lazy_re_compile(r'<[^>]+?>|(.)', re.S) +re_tag = _lazy_re_compile(r'<(/)?(\S+?)(?:(\s*/)|\s.*?)?>', re.S) +re_newlines = _lazy_re_compile(r'\r\n|\r') # Used in normalize_newlines +re_camel_case = _lazy_re_compile(r'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))') @keep_lazy_text @@ -42,49 +38,46 @@ def wrap(text, width): Don't wrap long words, thus the output text may have lines longer than ``width``. """ - def _generator(): for line in text.splitlines(True): # True keeps trailing linebreaks - max_width = min((line.endswith("\n") and width + 1 or width), width) + max_width = min((line.endswith('\n') and width + 1 or width), width) while len(line) > max_width: - space = line[: max_width + 1].rfind(" ") + 1 + space = line[:max_width + 1].rfind(' ') + 1 if space == 0: - space = line.find(" ") + 1 + space = line.find(' ') + 1 if space == 0: yield line - line = "" + line = '' break - yield "%s\n" % line[: space - 1] + yield '%s\n' % line[:space - 1] line = line[space:] - max_width = min((line.endswith("\n") and width + 1 or width), width) + max_width = min((line.endswith('\n') and width + 1 or width), width) if line: yield line - - return "".join(_generator()) + return ''.join(_generator()) class Truncator(SimpleLazyObject): """ An object used to truncate text, either by characters or words. """ - def __init__(self, text): super().__init__(lambda: str(text)) def add_truncation_text(self, text, truncate=None): if truncate is None: truncate = pgettext( - "String to return when truncating text", "%(truncated_text)s…" - ) - if "%(truncated_text)s" in truncate: - return truncate % {"truncated_text": text} + 'String to return when truncating text', + '%(truncated_text)s…') + if '%(truncated_text)s' in truncate: + return truncate % {'truncated_text': text} # The truncation text didn't contain the %(truncated_text)s string # replacement argument so just append it to the text. if text.endswith(truncate): # But don't append the truncation text if the current text already # ends in this. return text - return "%s%s" % (text, truncate) + return '%s%s' % (text, truncate) def chars(self, num, truncate=None, html=False): """ @@ -96,11 +89,11 @@ class Truncator(SimpleLazyObject): """ self._setup() length = int(num) - text = unicodedata.normalize("NFC", self._wrapped) + text = unicodedata.normalize('NFC', self._wrapped) # Calculate the length to truncate to (max length - end_text length) truncate_len = length - for char in self.add_truncation_text("", truncate): + for char in self.add_truncation_text('', truncate): if not unicodedata.combining(char): truncate_len -= 1 if truncate_len == 0: @@ -123,7 +116,8 @@ class Truncator(SimpleLazyObject): end_index = i if s_len > length: # Return the truncated string - return self.add_truncation_text(text[: end_index or 0], truncate) + return self.add_truncation_text(text[:end_index or 0], + truncate) # Return the original string since no truncation was necessary return text @@ -149,8 +143,8 @@ class Truncator(SimpleLazyObject): words = self._wrapped.split() if len(words) > length: words = words[:length] - return self.add_truncation_text(" ".join(words), truncate) - return " ".join(words) + return self.add_truncation_text(' '.join(words), truncate) + return ' '.join(words) def _truncate_html(self, length, truncate, text, truncate_len, words): """ @@ -161,18 +155,11 @@ class Truncator(SimpleLazyObject): Preserve newlines in the HTML. """ if words and length <= 0: - return "" + return '' html4_singlets = ( - "br", - "col", - "link", - "base", - "img", - "param", - "area", - "hr", - "input", + 'br', 'col', 'link', 'base', 'img', + 'param', 'area', 'hr', 'input' ) # Count non-HTML chars/words and keep note of open tags @@ -214,7 +201,7 @@ class Truncator(SimpleLazyObject): else: # SGML: An end tag closes, back to the matching start tag, # all unclosed intervening start tags with omitted end tags - open_tags = open_tags[i + 1 :] + open_tags = open_tags[i + 1:] else: # Add it to the start of the open tags list open_tags.insert(0, tagname) @@ -222,12 +209,12 @@ class Truncator(SimpleLazyObject): if current_len <= length: return text out = text[:end_text_pos] - truncate_text = self.add_truncation_text("", truncate) + truncate_text = self.add_truncation_text('', truncate) if truncate_text: out += truncate_text # Close any tags still open for tag in open_tags: - out += "</%s>" % tag + out += '</%s>' % tag # Return string return out @@ -242,15 +229,15 @@ def get_valid_filename(name): >>> get_valid_filename("john's portrait in 2004.jpg") 'johns_portrait_in_2004.jpg' """ - s = str(name).strip().replace(" ", "_") - s = re.sub(r"(?u)[^-\w.]", "", s) - if s in {"", ".", ".."}: + s = str(name).strip().replace(' ', '_') + s = re.sub(r'(?u)[^-\w.]', '', s) + if s in {'', '.', '..'}: raise SuspiciousFileOperation("Could not derive file name from '%s'" % name) return s @keep_lazy_text -def get_text_list(list_, last_word=gettext_lazy("or")): +def get_text_list(list_, last_word=gettext_lazy('or')): """ >>> get_text_list(['a', 'b', 'c', 'd']) 'a, b, c or d' @@ -264,59 +251,40 @@ def get_text_list(list_, last_word=gettext_lazy("or")): '' """ if not list_: - return "" + return '' if len(list_) == 1: return str(list_[0]) - return "%s %s %s" % ( + return '%s %s %s' % ( # Translators: This string is used as a separator between list elements - _(", ").join(str(i) for i in list_[:-1]), - str(last_word), - str(list_[-1]), + _(', ').join(str(i) for i in list_[:-1]), str(last_word), str(list_[-1]) ) @keep_lazy_text def normalize_newlines(text): """Normalize CRLF and CR newlines to just LF.""" - return re_newlines.sub("\n", str(text)) + return re_newlines.sub('\n', str(text)) @keep_lazy_text def phone2numeric(phone): """Convert a phone number with letters into its numeric equivalent.""" char2number = { - "a": "2", - "b": "2", - "c": "2", - "d": "3", - "e": "3", - "f": "3", - "g": "4", - "h": "4", - "i": "4", - "j": "5", - "k": "5", - "l": "5", - "m": "6", - "n": "6", - "o": "6", - "p": "7", - "q": "7", - "r": "7", - "s": "7", - "t": "8", - "u": "8", - "v": "8", - "w": "9", - "x": "9", - "y": "9", - "z": "9", + 'a': '2', 'b': '2', 'c': '2', 'd': '3', 'e': '3', 'f': '3', 'g': '4', + 'h': '4', 'i': '4', 'j': '5', 'k': '5', 'l': '5', 'm': '6', 'n': '6', + 'o': '6', 'p': '7', 'q': '7', 'r': '7', 's': '7', 't': '8', 'u': '8', + 'v': '8', 'w': '9', 'x': '9', 'y': '9', 'z': '9', } - return "".join(char2number.get(c, c) for c in phone.lower()) + return ''.join(char2number.get(c, c) for c in phone.lower()) +# From http://www.xhaus.com/alan/python/httpcomp.html#gzip +# Used with permission. def compress_string(s): - return gzip_compress(s, compresslevel=6, mtime=0) + zbuf = BytesIO() + with GzipFile(mode='wb', compresslevel=6, fileobj=zbuf, mtime=0) as zfile: + zfile.write(s) + return zbuf.getvalue() class StreamingBuffer(BytesIO): @@ -330,7 +298,7 @@ class StreamingBuffer(BytesIO): # Like compress_string, but for iterators of strings. def compress_sequence(sequence): buf = StreamingBuffer() - with GzipFile(mode="wb", compresslevel=6, fileobj=buf, mtime=0) as zfile: + with GzipFile(mode='wb', compresslevel=6, fileobj=buf, mtime=0) as zfile: # Output headers... yield buf.read() for item in sequence: @@ -343,8 +311,7 @@ def compress_sequence(sequence): # Expression to match some_token and some_token="with spaces" (and similarly # for single-quoted strings). -smart_split_re = _lazy_re_compile( - r""" +smart_split_re = _lazy_re_compile(r""" ((?: [^\s'"]* (?: @@ -352,9 +319,7 @@ smart_split_re = _lazy_re_compile( [^\s'"]* )+ ) | \S+) -""", - re.VERBOSE, -) +""", re.VERBOSE) def smart_split(text): @@ -378,10 +343,10 @@ def smart_split(text): def _replace_entity(match): text = match[1] - if text[0] == "#": + if text[0] == '#': text = text[1:] try: - if text[0] in "xX": + if text[0] in 'xX': c = int(text[1:], 16) else: c = int(text) @@ -398,6 +363,16 @@ def _replace_entity(match): _entity_re = _lazy_re_compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));") +@keep_lazy_text +def unescape_entities(text): + warnings.warn( + 'django.utils.text.unescape_entities() is deprecated in favor of ' + 'html.unescape().', + RemovedInDjango40Warning, stacklevel=2, + ) + return _entity_re.sub(_replace_entity, str(text)) + + @keep_lazy_text def unescape_string_literal(s): r""" @@ -416,7 +391,7 @@ def unescape_string_literal(s): if s[0] not in "\"'" or s[-1] != s[0]: raise ValueError("Not a string literal: %r" % s) quote = s[0] - return s[1:-1].replace(r"\%s" % quote, quote).replace(r"\\", "\\") + return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\') @keep_lazy_text @@ -429,22 +404,18 @@ def slugify(value, allow_unicode=False): """ value = str(value) if allow_unicode: - value = unicodedata.normalize("NFKC", value) + value = unicodedata.normalize('NFKC', value) else: - value = ( - unicodedata.normalize("NFKD", value) - .encode("ascii", "ignore") - .decode("ascii") - ) - value = re.sub(r"[^\w\s-]", "", value.lower()) - return re.sub(r"[-\s]+", "-", value).strip("-_") + value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') + value = re.sub(r'[^\w\s-]', '', value.lower()) + return re.sub(r'[-\s]+', '-', value).strip('-_') def camel_case_to_spaces(value): """ Split CamelCase and convert to lowercase. Strip surrounding whitespace. """ - return re_camel_case.sub(r" \1", value).strip().lower() + return re_camel_case.sub(r' \1', value).strip().lower() def _format_lazy(format_string, *args, **kwargs): diff --git a/venv/Lib/site-packages/django/utils/timesince.py b/venv/Lib/site-packages/django/utils/timesince.py index 8a8ffb8..a9e6b61 100644 --- a/venv/Lib/site-packages/django/utils/timesince.py +++ b/venv/Lib/site-packages/django/utils/timesince.py @@ -6,21 +6,21 @@ from django.utils.timezone import is_aware, utc from django.utils.translation import gettext, ngettext_lazy TIME_STRINGS = { - "year": ngettext_lazy("%(num)d year", "%(num)d years", "num"), - "month": ngettext_lazy("%(num)d month", "%(num)d months", "num"), - "week": ngettext_lazy("%(num)d week", "%(num)d weeks", "num"), - "day": ngettext_lazy("%(num)d day", "%(num)d days", "num"), - "hour": ngettext_lazy("%(num)d hour", "%(num)d hours", "num"), - "minute": ngettext_lazy("%(num)d minute", "%(num)d minutes", "num"), + 'year': ngettext_lazy('%d year', '%d years'), + 'month': ngettext_lazy('%d month', '%d months'), + 'week': ngettext_lazy('%d week', '%d weeks'), + 'day': ngettext_lazy('%d day', '%d days'), + 'hour': ngettext_lazy('%d hour', '%d hours'), + 'minute': ngettext_lazy('%d minute', '%d minutes'), } TIMESINCE_CHUNKS = ( - (60 * 60 * 24 * 365, "year"), - (60 * 60 * 24 * 30, "month"), - (60 * 60 * 24 * 7, "week"), - (60 * 60 * 24, "day"), - (60 * 60, "hour"), - (60, "minute"), + (60 * 60 * 24 * 365, 'year'), + (60 * 60 * 24 * 30, 'month'), + (60 * 60 * 24 * 7, 'week'), + (60 * 60 * 24, 'day'), + (60 * 60, 'hour'), + (60, 'minute'), ) @@ -47,7 +47,7 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2): if time_strings is None: time_strings = TIME_STRINGS if depth <= 0: - raise ValueError("depth must be greater than 0.") + raise ValueError('depth must be greater than 0.') # Convert datetime.date to datetime.datetime for comparison. if not isinstance(d, datetime.datetime): d = datetime.datetime(d.year, d.month, d.day) @@ -73,13 +73,13 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2): since = delta.days * 24 * 60 * 60 + delta.seconds if since <= 0: # d is in the future compared to now, stop processing. - return avoid_wrapping(time_strings["minute"] % {"num": 0}) + return avoid_wrapping(time_strings['minute'] % 0) for i, (seconds, name) in enumerate(TIMESINCE_CHUNKS): count = since // seconds if count != 0: break else: - return avoid_wrapping(time_strings["minute"] % {"num": 0}) + return avoid_wrapping(time_strings['minute'] % 0) result = [] current_depth = 0 while i < len(TIMESINCE_CHUNKS) and current_depth < depth: @@ -87,11 +87,11 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2): count = since // seconds if count == 0: break - result.append(avoid_wrapping(time_strings[name] % {"num": count})) + result.append(avoid_wrapping(time_strings[name] % count)) since -= seconds * count current_depth += 1 i += 1 - return gettext(", ").join(result) + return gettext(', ').join(result) def timeuntil(d, now=None, time_strings=None, depth=2): diff --git a/venv/Lib/site-packages/django/utils/timezone.py b/venv/Lib/site-packages/django/utils/timezone.py index 8622094..cf22ec3 100644 --- a/venv/Lib/site-packages/django/utils/timezone.py +++ b/venv/Lib/site-packages/django/utils/timezone.py @@ -3,53 +3,39 @@ Timezone-related classes and functions. """ import functools -import sys -import warnings - -try: - import zoneinfo -except ImportError: - from backports import zoneinfo - from contextlib import ContextDecorator from datetime import datetime, timedelta, timezone, tzinfo +import pytz from asgiref.local import Local from django.conf import settings -from django.utils.deprecation import RemovedInDjango50Warning __all__ = [ - "utc", - "get_fixed_timezone", - "get_default_timezone", - "get_default_timezone_name", - "get_current_timezone", - "get_current_timezone_name", - "activate", - "deactivate", - "override", - "localtime", - "now", - "is_aware", - "is_naive", - "make_aware", - "make_naive", + 'utc', 'get_fixed_timezone', + 'get_default_timezone', 'get_default_timezone_name', + 'get_current_timezone', 'get_current_timezone_name', + 'activate', 'deactivate', 'override', + 'localtime', 'now', + 'is_aware', 'is_naive', 'make_aware', 'make_naive', ] -# RemovedInDjango50Warning: sentinel for deprecation of is_dst parameters. -NOT_PASSED = object() +# UTC time zone as a tzinfo instance. +utc = pytz.utc -utc = timezone.utc +_PYTZ_BASE_CLASSES = (pytz.tzinfo.BaseTzInfo, pytz._FixedOffset) +# In releases prior to 2018.4, pytz.UTC was not a subclass of BaseTzInfo +if not isinstance(pytz.UTC, pytz._FixedOffset): + _PYTZ_BASE_CLASSES = _PYTZ_BASE_CLASSES + (type(pytz.UTC),) def get_fixed_timezone(offset): """Return a tzinfo instance with a fixed offset from UTC.""" if isinstance(offset, timedelta): offset = offset.total_seconds() // 60 - sign = "-" if offset < 0 else "+" - hhmm = "%02d%02d" % divmod(abs(offset), 60) + sign = '-' if offset < 0 else '+' + hhmm = '%02d%02d' % divmod(abs(offset), 60) name = sign + hhmm return timezone(timedelta(minutes=offset), name) @@ -63,11 +49,7 @@ def get_default_timezone(): This is the time zone defined by settings.TIME_ZONE. """ - if settings.USE_DEPRECATED_PYTZ: - import pytz - - return pytz.timezone(settings.TIME_ZONE) - return zoneinfo.ZoneInfo(settings.TIME_ZONE) + return pytz.timezone(settings.TIME_ZONE) # This function exists for consistency with get_current_timezone_name @@ -90,12 +72,8 @@ def get_current_timezone_name(): def _get_timezone_name(timezone): - """ - Return the offset for fixed offset timezones, or the name of timezone if - not set. - """ - return timezone.tzname(None) or str(timezone) - + """Return the name of ``timezone``.""" + return str(timezone) # Timezone selection functions. @@ -113,12 +91,7 @@ def activate(timezone): if isinstance(timezone, tzinfo): _active.value = timezone elif isinstance(timezone, str): - if settings.USE_DEPRECATED_PYTZ: - import pytz - - _active.value = pytz.timezone(timezone) - else: - _active.value = zoneinfo.ZoneInfo(timezone) + _active.value = pytz.timezone(timezone) else: raise ValueError("Invalid timezone: %r" % timezone) @@ -145,12 +118,11 @@ class override(ContextDecorator): time zone name, or ``None``. If it is ``None``, Django enables the default time zone. """ - def __init__(self, timezone): self.timezone = timezone def __enter__(self): - self.old_timezone = getattr(_active, "value", None) + self.old_timezone = getattr(_active, 'value', None) if self.timezone is None: deactivate() else: @@ -165,7 +137,6 @@ class override(ContextDecorator): # Templates - def template_localtime(value, use_tz=None): """ Check if value is a datetime and converts it to local time if necessary. @@ -176,17 +147,16 @@ def template_localtime(value, use_tz=None): This function is designed for use by the template engine. """ should_convert = ( - isinstance(value, datetime) - and (settings.USE_TZ if use_tz is None else use_tz) - and not is_naive(value) - and getattr(value, "convert_to_local_time", True) + isinstance(value, datetime) and + (settings.USE_TZ if use_tz is None else use_tz) and + not is_naive(value) and + getattr(value, 'convert_to_local_time', True) ) return localtime(value) if should_convert else value # Utilities - def localtime(value=None, timezone=None): """ Convert an aware datetime.datetime to local time. @@ -224,13 +194,16 @@ def now(): """ Return an aware or naive datetime.datetime, depending on settings.USE_TZ. """ - return datetime.now(tz=utc if settings.USE_TZ else None) + if settings.USE_TZ: + # timeit shows that datetime.now(tz=utc) is 24% slower + return datetime.utcnow().replace(tzinfo=utc) + else: + return datetime.now() # By design, these four functions don't perform any checks on their arguments. # The caller should ensure that they don't receive an invalid value like None. - def is_aware(value): """ Determine if a given datetime.datetime is aware. @@ -257,17 +230,8 @@ def is_naive(value): return value.utcoffset() is None -def make_aware(value, timezone=None, is_dst=NOT_PASSED): +def make_aware(value, timezone=None, is_dst=None): """Make a naive datetime.datetime in a given time zone aware.""" - if is_dst is NOT_PASSED: - is_dst = None - else: - warnings.warn( - "The is_dst argument to make_aware(), used by the Trunc() " - "database functions and QuerySet.datetimes(), is deprecated as it " - "has no effect with zoneinfo time zones.", - RemovedInDjango50Warning, - ) if timezone is None: timezone = get_current_timezone() if _is_pytz_zone(timezone): @@ -276,7 +240,8 @@ def make_aware(value, timezone=None, is_dst=NOT_PASSED): else: # Check that we won't overwrite the timezone of an aware datetime. if is_aware(value): - raise ValueError("make_aware expects a naive datetime, got %s" % value) + raise ValueError( + "make_aware expects a naive datetime, got %s" % value) # This may be wrong around DST changes! return value.replace(tzinfo=timezone) @@ -291,46 +256,13 @@ def make_naive(value, timezone=None): return value.astimezone(timezone).replace(tzinfo=None) -_PYTZ_IMPORTED = False - - -def _pytz_imported(): - """ - Detects whether or not pytz has been imported without importing pytz. - - Copied from pytz_deprecation_shim with thanks to Paul Ganssle. - """ - global _PYTZ_IMPORTED - - if not _PYTZ_IMPORTED and "pytz" in sys.modules: - _PYTZ_IMPORTED = True - - return _PYTZ_IMPORTED - - def _is_pytz_zone(tz): """Checks if a zone is a pytz zone.""" - # See if pytz was already imported rather than checking - # settings.USE_DEPRECATED_PYTZ to *allow* manually passing a pytz timezone, - # which some of the test cases (at least) rely on. - if not _pytz_imported(): - return False - - # If tz could be pytz, then pytz is needed here. - import pytz - - _PYTZ_BASE_CLASSES = (pytz.tzinfo.BaseTzInfo, pytz._FixedOffset) - # In releases prior to 2018.4, pytz.UTC was not a subclass of BaseTzInfo - if not isinstance(pytz.UTC, pytz._FixedOffset): - _PYTZ_BASE_CLASSES = _PYTZ_BASE_CLASSES + (type(pytz.UTC),) - return isinstance(tz, _PYTZ_BASE_CLASSES) def _datetime_ambiguous_or_imaginary(dt, tz): if _is_pytz_zone(tz): - import pytz - try: tz.utcoffset(dt) except (pytz.AmbiguousTimeError, pytz.NonExistentTimeError): diff --git a/venv/Lib/site-packages/django/utils/topological_sort.py b/venv/Lib/site-packages/django/utils/topological_sort.py index 66b6866..f7ce0e0 100644 --- a/venv/Lib/site-packages/django/utils/topological_sort.py +++ b/venv/Lib/site-packages/django/utils/topological_sort.py @@ -17,20 +17,14 @@ def topological_sort_as_sets(dependency_graph): current = {node for node, deps in todo.items() if not deps} if not current: - raise CyclicDependencyError( - "Cyclic dependency in graph: {}".format( - ", ".join(repr(x) for x in todo.items()) - ) - ) + raise CyclicDependencyError('Cyclic dependency in graph: {}'.format( + ', '.join(repr(x) for x in todo.items()))) yield current # remove current from todo's nodes & dependencies - todo = { - node: (dependencies - current) - for node, dependencies in todo.items() - if node not in current - } + todo = {node: (dependencies - current) for node, dependencies in + todo.items() if node not in current} def stable_topological_sort(nodes, dependency_graph): diff --git a/venv/Lib/site-packages/django/utils/translation/__init__.py b/venv/Lib/site-packages/django/utils/translation/__init__.py index 6b8cc73..0dd2503 100644 --- a/venv/Lib/site-packages/django/utils/translation/__init__.py +++ b/venv/Lib/site-packages/django/utils/translation/__init__.py @@ -1,37 +1,31 @@ """ Internationalization support. """ +import warnings from contextlib import ContextDecorator from decimal import ROUND_UP, Decimal from django.utils.autoreload import autoreload_started, file_changed +from django.utils.deprecation import RemovedInDjango40Warning from django.utils.functional import lazy from django.utils.regex_helper import _lazy_re_compile __all__ = [ - "activate", - "deactivate", - "override", - "deactivate_all", - "get_language", - "get_language_from_request", - "get_language_info", - "get_language_bidi", - "check_for_language", - "to_language", - "to_locale", - "templatize", - "gettext", - "gettext_lazy", - "gettext_noop", - "ngettext", - "ngettext_lazy", - "pgettext", - "pgettext_lazy", - "npgettext", - "npgettext_lazy", + 'activate', 'deactivate', 'override', 'deactivate_all', + 'get_language', 'get_language_from_request', + 'get_language_info', 'get_language_bidi', + 'check_for_language', 'to_language', 'to_locale', 'templatize', + 'gettext', 'gettext_lazy', 'gettext_noop', + 'ugettext', 'ugettext_lazy', 'ugettext_noop', + 'ngettext', 'ngettext_lazy', + 'ungettext', 'ungettext_lazy', + 'pgettext', 'pgettext_lazy', + 'npgettext', 'npgettext_lazy', + 'LANGUAGE_SESSION_KEY', ] +LANGUAGE_SESSION_KEY = '_language' + class TranslatorCommentWarning(SyntaxWarning): pass @@ -45,7 +39,6 @@ class TranslatorCommentWarning(SyntaxWarning): # replace the functions with their real counterparts (once we do access the # settings). - class Trans: """ The purpose of this class is to store the actual translation function upon @@ -61,20 +54,13 @@ class Trans: def __getattr__(self, real_name): from django.conf import settings - if settings.USE_I18N: from django.utils.translation import trans_real as trans from django.utils.translation.reloader import ( - translation_file_changed, - watch_for_translation_changes, - ) - - autoreload_started.connect( - watch_for_translation_changes, dispatch_uid="translation_file_changed" - ) - file_changed.connect( - translation_file_changed, dispatch_uid="translation_file_changed" + translation_file_changed, watch_for_translation_changes, ) + autoreload_started.connect(watch_for_translation_changes, dispatch_uid='translation_file_changed') + file_changed.connect(translation_file_changed, dispatch_uid='translation_file_changed') else: from django.utils.translation import trans_null as trans setattr(self, real_name, getattr(trans, real_name)) @@ -91,14 +77,53 @@ def gettext_noop(message): return _trans.gettext_noop(message) +def ugettext_noop(message): + """ + A legacy compatibility wrapper for Unicode handling on Python 2. + Alias of gettext_noop() since Django 2.0. + """ + warnings.warn( + 'django.utils.translation.ugettext_noop() is deprecated in favor of ' + 'django.utils.translation.gettext_noop().', + RemovedInDjango40Warning, stacklevel=2, + ) + return gettext_noop(message) + + def gettext(message): return _trans.gettext(message) +def ugettext(message): + """ + A legacy compatibility wrapper for Unicode handling on Python 2. + Alias of gettext() since Django 2.0. + """ + warnings.warn( + 'django.utils.translation.ugettext() is deprecated in favor of ' + 'django.utils.translation.gettext().', + RemovedInDjango40Warning, stacklevel=2, + ) + return gettext(message) + + def ngettext(singular, plural, number): return _trans.ngettext(singular, plural, number) +def ungettext(singular, plural, number): + """ + A legacy compatibility wrapper for Unicode handling on Python 2. + Alias of ngettext() since Django 2.0. + """ + warnings.warn( + 'django.utils.translation.ungettext() is deprecated in favor of ' + 'django.utils.translation.ngettext().', + RemovedInDjango40Warning, stacklevel=2, + ) + return ngettext(singular, plural, number) + + def pgettext(context, message): return _trans.pgettext(context, message) @@ -111,35 +136,46 @@ gettext_lazy = lazy(gettext, str) pgettext_lazy = lazy(pgettext, str) +def ugettext_lazy(message): + """ + A legacy compatibility wrapper for Unicode handling on Python 2. Has been + Alias of gettext_lazy since Django 2.0. + """ + warnings.warn( + 'django.utils.translation.ugettext_lazy() is deprecated in favor of ' + 'django.utils.translation.gettext_lazy().', + RemovedInDjango40Warning, stacklevel=2, + ) + return gettext_lazy(message) + + def lazy_number(func, resultclass, number=None, **kwargs): if isinstance(number, int): - kwargs["number"] = number + kwargs['number'] = number proxy = lazy(func, resultclass)(**kwargs) else: original_kwargs = kwargs.copy() class NumberAwareString(resultclass): def __bool__(self): - return bool(kwargs["singular"]) + return bool(kwargs['singular']) def _get_number_value(self, values): try: return values[number] except KeyError: raise KeyError( - "Your dictionary lacks key '%s'. Please provide " + "Your dictionary lacks key '%s\'. Please provide " "it, because it is required to determine whether " "string is singular or plural." % number ) def _translate(self, number_value): - kwargs["number"] = number_value + kwargs['number'] = number_value return func(**kwargs) def format(self, *args, **kwargs): - number_value = ( - self._get_number_value(kwargs) if kwargs and number else args[0] - ) + number_value = self._get_number_value(kwargs) if kwargs and number else args[0] return self._translate(number_value).format(*args, **kwargs) def __mod__(self, rhs): @@ -156,10 +192,7 @@ def lazy_number(func, resultclass, number=None, **kwargs): return translated proxy = lazy(lambda **kwargs: NumberAwareString(), NumberAwareString)(**kwargs) - proxy.__reduce__ = lambda: ( - _lazy_number_unpickle, - (func, resultclass, number, original_kwargs), - ) + proxy.__reduce__ = lambda: (_lazy_number_unpickle, (func, resultclass, number, original_kwargs)) return proxy @@ -171,10 +204,21 @@ def ngettext_lazy(singular, plural, number=None): return lazy_number(ngettext, str, singular=singular, plural=plural, number=number) -def npgettext_lazy(context, singular, plural, number=None): - return lazy_number( - npgettext, str, context=context, singular=singular, plural=plural, number=number +def ungettext_lazy(singular, plural, number=None): + """ + A legacy compatibility wrapper for Unicode handling on Python 2. + An alias of ungettext_lazy() since Django 2.0. + """ + warnings.warn( + 'django.utils.translation.ungettext_lazy() is deprecated in favor of ' + 'django.utils.translation.ngettext_lazy().', + RemovedInDjango40Warning, stacklevel=2, ) + return ngettext_lazy(singular, plural, number) + + +def npgettext_lazy(context, singular, plural, number=None): + return lazy_number(npgettext, str, context=context, singular=singular, plural=plural, number=number) def activate(language): @@ -220,27 +264,27 @@ def check_for_language(lang_code): def to_language(locale): """Turn a locale name (en_US) into a language name (en-us).""" - p = locale.find("_") + p = locale.find('_') if p >= 0: - return locale[:p].lower() + "-" + locale[p + 1 :].lower() + return locale[:p].lower() + '-' + locale[p + 1:].lower() else: return locale.lower() def to_locale(language): """Turn a language name (en-us) into a locale name (en_US).""" - lang, _, country = language.lower().partition("-") + language, _, country = language.lower().partition('-') if not country: - return language[:3].lower() + language[3:] + return language # A language with > 2 characters after the dash only has its first # character after the dash capitalized; e.g. sr-latn becomes sr_Latn. # A language with 2 characters after the dash has both characters # capitalized; e.g. en-us becomes en_US. - country, _, tail = country.partition("-") + country, _, tail = country.partition('-') country = country.title() if len(country) > 2 else country.upper() if tail: - country += "-" + tail - return lang + "_" + country + country += '-' + tail + return language + '_' + country def get_language_from_request(request, check_path=False): @@ -257,7 +301,6 @@ def get_supported_language_variant(lang_code, *, strict=False): def templatize(src, **kwargs): from .template import templatize - return templatize(src, **kwargs) @@ -267,35 +310,32 @@ def deactivate_all(): def get_language_info(lang_code): from django.conf.locale import LANG_INFO - try: lang_info = LANG_INFO[lang_code] - if "fallback" in lang_info and "name" not in lang_info: - info = get_language_info(lang_info["fallback"][0]) + if 'fallback' in lang_info and 'name' not in lang_info: + info = get_language_info(lang_info['fallback'][0]) else: info = lang_info except KeyError: - if "-" not in lang_code: + if '-' not in lang_code: raise KeyError("Unknown language code %s." % lang_code) - generic_lang_code = lang_code.split("-")[0] + generic_lang_code = lang_code.split('-')[0] try: info = LANG_INFO[generic_lang_code] except KeyError: - raise KeyError( - "Unknown language code %s and %s." % (lang_code, generic_lang_code) - ) + raise KeyError("Unknown language code %s and %s." % (lang_code, generic_lang_code)) if info: - info["name_translated"] = gettext_lazy(info["name"]) + info['name_translated'] = gettext_lazy(info['name']) return info -trim_whitespace_re = _lazy_re_compile(r"\s*\n\s*") +trim_whitespace_re = _lazy_re_compile(r'\s*\n\s*') def trim_whitespace(s): - return trim_whitespace_re.sub(" ", s.strip()) + return trim_whitespace_re.sub(' ', s.strip()) def round_away_from_one(value): - return int(Decimal(value - 1).quantize(Decimal("0"), rounding=ROUND_UP)) + 1 + return int(Decimal(value - 1).quantize(Decimal('0'), rounding=ROUND_UP)) + 1 diff --git a/venv/Lib/site-packages/django/utils/translation/reloader.py b/venv/Lib/site-packages/django/utils/translation/reloader.py index be05ccc..d8afa89 100644 --- a/venv/Lib/site-packages/django/utils/translation/reloader.py +++ b/venv/Lib/site-packages/django/utils/translation/reloader.py @@ -11,24 +11,23 @@ def watch_for_translation_changes(sender, **kwargs): from django.conf import settings if settings.USE_I18N: - directories = [Path("locale")] + directories = [Path('locale')] directories.extend( - Path(config.path) / "locale" + Path(config.path) / 'locale' for config in apps.get_app_configs() if not is_django_module(config.module) ) directories.extend(Path(p) for p in settings.LOCALE_PATHS) for path in directories: - sender.watch_dir(path, "**/*.mo") + sender.watch_dir(path, '**/*.mo') def translation_file_changed(sender, file_path, **kwargs): """Clear the internal translations cache if a .mo file is modified.""" - if file_path.suffix == ".mo": + if file_path.suffix == '.mo': import gettext from django.utils.translation import trans_real - gettext._translations = {} trans_real._translations = {} trans_real._default = None diff --git a/venv/Lib/site-packages/django/utils/translation/template.py b/venv/Lib/site-packages/django/utils/translation/template.py index d7353a3..778faa7 100644 --- a/venv/Lib/site-packages/django/utils/translation/template.py +++ b/venv/Lib/site-packages/django/utils/translation/template.py @@ -1,14 +1,12 @@ import warnings from io import StringIO -from django.template.base import Lexer, TokenType +from django.template.base import TRANSLATOR_COMMENT_MARK, Lexer, TokenType from django.utils.regex_helper import _lazy_re_compile from . import TranslatorCommentWarning, trim_whitespace -TRANSLATOR_COMMENT_MARK = "Translators" - -dot_re = _lazy_re_compile(r"\S") +dot_re = _lazy_re_compile(r'\S') def blankout(src, char): @@ -28,9 +26,7 @@ inline_re = _lazy_re_compile( # Match the optional context part r"""(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?\s*""" ) -block_re = _lazy_re_compile( - r"""^\s*blocktrans(?:late)?(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""" -) +block_re = _lazy_re_compile(r"""^\s*blocktrans(?:late)?(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""") endblock_re = _lazy_re_compile(r"""^\s*endblocktrans(?:late)?$""") plural_re = _lazy_re_compile(r"""^\s*plural$""") constant_re = _lazy_re_compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") @@ -42,7 +38,7 @@ def templatize(src, origin=None): does so by translating the Django translation tags into standard gettext function invocations. """ - out = StringIO("") + out = StringIO('') message_context = None intrans = False inplural = False @@ -54,30 +50,27 @@ def templatize(src, origin=None): lineno_comment_map = {} comment_lineno_cache = None # Adding the u prefix allows gettext to recognize the string (#26093). - raw_prefix = "u" + raw_prefix = 'u' def join_tokens(tokens, trim=False): - message = "".join(tokens) + message = ''.join(tokens) if trim: message = trim_whitespace(message) return message for t in Lexer(src).tokenize(): if incomment: - if t.token_type == TokenType.BLOCK and t.contents == "endcomment": - content = "".join(comment) + if t.token_type == TokenType.BLOCK and t.contents == 'endcomment': + content = ''.join(comment) translators_comment_start = None for lineno, line in enumerate(content.splitlines(True)): if line.lstrip().startswith(TRANSLATOR_COMMENT_MARK): translators_comment_start = lineno for lineno, line in enumerate(content.splitlines(True)): - if ( - translators_comment_start is not None - and lineno >= translators_comment_start - ): - out.write(" # %s" % line) + if translators_comment_start is not None and lineno >= translators_comment_start: + out.write(' # %s' % line) else: - out.write(" #\n") + out.write(' #\n') incomment = False comment = [] else: @@ -89,44 +82,36 @@ def templatize(src, origin=None): if endbmatch: if inplural: if message_context: - out.write( - " npgettext({p}{!r}, {p}{!r}, {p}{!r},count) ".format( - message_context, - join_tokens(singular, trimmed), - join_tokens(plural, trimmed), - p=raw_prefix, - ) - ) + out.write(' npgettext({p}{!r}, {p}{!r}, {p}{!r},count) '.format( + message_context, + join_tokens(singular, trimmed), + join_tokens(plural, trimmed), + p=raw_prefix, + )) else: - out.write( - " ngettext({p}{!r}, {p}{!r}, count) ".format( - join_tokens(singular, trimmed), - join_tokens(plural, trimmed), - p=raw_prefix, - ) - ) + out.write(' ngettext({p}{!r}, {p}{!r}, count) '.format( + join_tokens(singular, trimmed), + join_tokens(plural, trimmed), + p=raw_prefix, + )) for part in singular: - out.write(blankout(part, "S")) + out.write(blankout(part, 'S')) for part in plural: - out.write(blankout(part, "P")) + out.write(blankout(part, 'P')) else: if message_context: - out.write( - " pgettext({p}{!r}, {p}{!r}) ".format( - message_context, - join_tokens(singular, trimmed), - p=raw_prefix, - ) - ) + out.write(' pgettext({p}{!r}, {p}{!r}) '.format( + message_context, + join_tokens(singular, trimmed), + p=raw_prefix, + )) else: - out.write( - " gettext({p}{!r}) ".format( - join_tokens(singular, trimmed), - p=raw_prefix, - ) - ) + out.write(' gettext({p}{!r}) '.format( + join_tokens(singular, trimmed), + p=raw_prefix, + )) for part in singular: - out.write(blankout(part, "S")) + out.write(blankout(part, 'S')) message_context = None intrans = False inplural = False @@ -135,20 +120,20 @@ def templatize(src, origin=None): elif pluralmatch: inplural = True else: - filemsg = "" + filemsg = '' if origin: - filemsg = "file %s, " % origin + filemsg = 'file %s, ' % origin raise SyntaxError( "Translation blocks must not include other block tags: " "%s (%sline %d)" % (t.contents, filemsg, t.lineno) ) elif t.token_type == TokenType.VAR: if inplural: - plural.append("%%(%s)s" % t.contents) + plural.append('%%(%s)s' % t.contents) else: - singular.append("%%(%s)s" % t.contents) + singular.append('%%(%s)s' % t.contents) elif t.token_type == TokenType.TEXT: - contents = t.contents.replace("%", "%%") + contents = t.contents.replace('%', '%%') if inplural: plural.append(contents) else: @@ -157,13 +142,13 @@ def templatize(src, origin=None): # Handle comment tokens (`{# ... #}`) plus other constructs on # the same line: if comment_lineno_cache is not None: - cur_lineno = t.lineno + t.contents.count("\n") + cur_lineno = t.lineno + t.contents.count('\n') if comment_lineno_cache == cur_lineno: if t.token_type != TokenType.COMMENT: for c in lineno_comment_map[comment_lineno_cache]: - filemsg = "" + filemsg = '' if origin: - filemsg = "file %s, " % origin + filemsg = 'file %s, ' % origin warn_msg = ( "The translator-targeted comment '%s' " "(%sline %d) was ignored, because it wasn't " @@ -172,9 +157,7 @@ def templatize(src, origin=None): warnings.warn(warn_msg, TranslatorCommentWarning) lineno_comment_map[comment_lineno_cache] = [] else: - out.write( - "# %s" % " | ".join(lineno_comment_map[comment_lineno_cache]) - ) + out.write('# %s' % ' | '.join(lineno_comment_map[comment_lineno_cache])) comment_lineno_cache = None if t.token_type == TokenType.BLOCK: @@ -187,7 +170,7 @@ def templatize(src, origin=None): g = g.strip('"') elif g[0] == "'": g = g.strip("'") - g = g.replace("%", "%%") + g = g.replace('%', '%%') if imatch[2]: # A context is provided context_match = context_re.match(imatch[2]) @@ -196,17 +179,15 @@ def templatize(src, origin=None): message_context = message_context.strip('"') elif message_context[0] == "'": message_context = message_context.strip("'") - out.write( - " pgettext({p}{!r}, {p}{!r}) ".format( - message_context, g, p=raw_prefix - ) - ) + out.write(' pgettext({p}{!r}, {p}{!r}) '.format( + message_context, g, p=raw_prefix + )) message_context = None else: - out.write(" gettext({p}{!r}) ".format(g, p=raw_prefix)) + out.write(' gettext({p}{!r}) '.format(g, p=raw_prefix)) elif bmatch: for fmatch in constant_re.findall(t.contents): - out.write(" _(%s) " % fmatch) + out.write(' _(%s) ' % fmatch) if bmatch[1]: # A context is provided context_match = context_re.match(bmatch[1]) @@ -217,30 +198,30 @@ def templatize(src, origin=None): message_context = message_context.strip("'") intrans = True inplural = False - trimmed = "trimmed" in t.split_contents() + trimmed = 'trimmed' in t.split_contents() singular = [] plural = [] elif cmatches: for cmatch in cmatches: - out.write(" _(%s) " % cmatch) - elif t.contents == "comment": + out.write(' _(%s) ' % cmatch) + elif t.contents == 'comment': incomment = True else: - out.write(blankout(t.contents, "B")) + out.write(blankout(t.contents, 'B')) elif t.token_type == TokenType.VAR: - parts = t.contents.split("|") + parts = t.contents.split('|') cmatch = constant_re.match(parts[0]) if cmatch: - out.write(" _(%s) " % cmatch[1]) + out.write(' _(%s) ' % cmatch[1]) for p in parts[1:]: - if p.find(":_(") >= 0: - out.write(" %s " % p.split(":", 1)[1]) + if p.find(':_(') >= 0: + out.write(' %s ' % p.split(':', 1)[1]) else: - out.write(blankout(p, "F")) + out.write(blankout(p, 'F')) elif t.token_type == TokenType.COMMENT: if t.contents.lstrip().startswith(TRANSLATOR_COMMENT_MARK): lineno_comment_map.setdefault(t.lineno, []).append(t.contents) comment_lineno_cache = t.lineno else: - out.write(blankout(t.contents, "X")) + out.write(blankout(t.contents, 'X')) return out.getvalue() diff --git a/venv/Lib/site-packages/django/utils/translation/trans_real.py b/venv/Lib/site-packages/django/utils/translation/trans_real.py index 517118a..8042f6f 100644 --- a/venv/Lib/site-packages/django/utils/translation/trans_real.py +++ b/venv/Lib/site-packages/django/utils/translation/trans_real.py @@ -32,23 +32,18 @@ CONTEXT_SEPARATOR = "\x04" # Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9 # and RFC 3066, section 2.1 -accept_language_re = _lazy_re_compile( - r""" - # "en", "en-au", "x-y-z", "es-419", "*" - ([A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*|\*) - # Optional "q=1.00", "q=0.8" - (?:\s*;\s*q=(0(?:\.\d{,3})?|1(?:\.0{,3})?))? - # Multiple accepts per header. - (?:\s*,\s*|$) - """, - re.VERBOSE, -) +accept_language_re = _lazy_re_compile(r''' + ([A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*|\*) # "en", "en-au", "x-y-z", "es-419", "*" + (?:\s*;\s*q=(0(?:\.\d{,3})?|1(?:\.0{,3})?))? # Optional "q=1.00", "q=0.8" + (?:\s*,\s*|$) # Multiple accepts per header. + ''', re.VERBOSE) language_code_re = _lazy_re_compile( - r"^[a-z]{1,8}(?:-[a-z0-9]{1,8})*(?:@[a-z0-9]{1,20})?$", re.IGNORECASE + r'^[a-z]{1,8}(?:-[a-z0-9]{1,8})*(?:@[a-z0-9]{1,20})?$', + re.IGNORECASE ) -language_code_prefix_re = _lazy_re_compile(r"^/(\w+([@-]\w+)?)(/|$)") +language_code_prefix_re = _lazy_re_compile(r'^/(\w+([@-]\w+)?)(/|$)') @receiver(setting_changed) @@ -57,7 +52,7 @@ def reset_cache(**kwargs): Reset global state when LANGUAGES setting has been changed, as some languages should no longer be accepted. """ - if kwargs["setting"] in ("LANGUAGES", "LANGUAGE_CODE"): + if kwargs['setting'] in ('LANGUAGES', 'LANGUAGE_CODE'): check_for_language.cache_clear() get_languages.cache_clear() get_supported_language_variant.cache_clear() @@ -68,7 +63,6 @@ class TranslationCatalog: Simulate a dict for DjangoTranslation._catalog so as multiple catalogs with different plural equations are kept separate. """ - def __init__(self, trans=None): self._catalogs = [trans._catalog.copy()] if trans else [{}] self._plurals = [trans.plural] if trans else [lambda n: int(n != 1)] @@ -130,8 +124,7 @@ class DjangoTranslation(gettext_module.GNUTranslations): requested language and add a fallback to the default language, if it's different from the requested language. """ - - domain = "django" + domain = 'django' def __init__(self, language, domain=None, localedirs=None): """Create a GNUTranslations() using many locale directories""" @@ -147,12 +140,10 @@ class DjangoTranslation(gettext_module.GNUTranslations): # pluralization: anything except one is pluralized. self.plural = lambda n: int(n != 1) - if self.domain == "django": + if self.domain == 'django': if localedirs is not None: # A module-level cache is used for caching 'django' translations - warnings.warn( - "localedirs is ignored when domain is 'django'.", RuntimeWarning - ) + warnings.warn("localedirs is ignored when domain is 'django'.", RuntimeWarning) localedirs = None self._init_translation_catalog() @@ -164,16 +155,9 @@ class DjangoTranslation(gettext_module.GNUTranslations): self._add_installed_apps_translations() self._add_local_translations() - if ( - self.__language == settings.LANGUAGE_CODE - and self.domain == "django" - and self._catalog is None - ): + if self.__language == settings.LANGUAGE_CODE and self.domain == 'django' and self._catalog is None: # default lang should have at least one translation file available. - raise OSError( - "No translation files found for default language %s." - % settings.LANGUAGE_CODE - ) + raise OSError('No translation files found for default language %s.' % settings.LANGUAGE_CODE) self._add_fallback(localedirs) if self._catalog is None: # No catalogs found for this language, set an empty catalog. @@ -200,7 +184,7 @@ class DjangoTranslation(gettext_module.GNUTranslations): def _init_translation_catalog(self): """Create a base catalog using global django translations.""" settingsfile = sys.modules[settings.__module__].__file__ - localedir = os.path.join(os.path.dirname(settingsfile), "locale") + localedir = os.path.join(os.path.dirname(settingsfile), 'locale') translation = self._new_gnu_trans(localedir) self.merge(translation) @@ -212,10 +196,9 @@ class DjangoTranslation(gettext_module.GNUTranslations): raise AppRegistryNotReady( "The translation infrastructure cannot be initialized before the " "apps registry is ready. Check that you don't make non-lazy " - "gettext calls at import time." - ) + "gettext calls at import time.") for app_config in app_configs: - localedir = os.path.join(app_config.path, "locale") + localedir = os.path.join(app_config.path, 'locale') if os.path.exists(localedir): translation = self._new_gnu_trans(localedir) self.merge(translation) @@ -230,11 +213,9 @@ class DjangoTranslation(gettext_module.GNUTranslations): """Set the GNUTranslations() fallback with the default language.""" # Don't set a fallback for the default language or any English variant # (as it's empty, so it'll ALWAYS fall back to the default language) - if self.__language == settings.LANGUAGE_CODE or self.__language.startswith( - "en" - ): + if self.__language == settings.LANGUAGE_CODE or self.__language.startswith('en'): return - if self.domain == "django": + if self.domain == 'django': # Get from cache default_translation = translation(settings.LANGUAGE_CODE) else: @@ -245,7 +226,7 @@ class DjangoTranslation(gettext_module.GNUTranslations): def merge(self, other): """Merge another translation into this catalog.""" - if not getattr(other, "_catalog", None): + if not getattr(other, '_catalog', None): return # NullTranslations() has no _catalog if self._catalog is None: # Take plural and _info from first catalog found (generally Django's). @@ -340,7 +321,7 @@ def get_language_bidi(): if lang is None: return False else: - base_lang = get_language().split("-")[0] + base_lang = get_language().split('-')[0] return base_lang in settings.LANGUAGES_BIDI @@ -368,7 +349,7 @@ def gettext(message): """ global _default - eol_message = message.replace("\r\n", "\n").replace("\r", "\n") + eol_message = message.replace('\r\n', '\n').replace('\r', '\n') if eol_message: _default = _default or translation(settings.LANGUAGE_CODE) @@ -378,7 +359,7 @@ def gettext(message): else: # Return an empty value of the corresponding type if an empty message # is given, instead of metadata, which is the default gettext behavior. - result = type(message)("") + result = type(message)('') if isinstance(message, SafeData): return mark_safe(result) @@ -423,15 +404,13 @@ def ngettext(singular, plural, number): Return a string of the translation of either the singular or plural, based on the number. """ - return do_ntranslate(singular, plural, number, "ngettext") + return do_ntranslate(singular, plural, number, 'ngettext') def npgettext(context, singular, plural, number): - msgs_with_ctxt = ( - "%s%s%s" % (context, CONTEXT_SEPARATOR, singular), - "%s%s%s" % (context, CONTEXT_SEPARATOR, plural), - number, - ) + msgs_with_ctxt = ("%s%s%s" % (context, CONTEXT_SEPARATOR, singular), + "%s%s%s" % (context, CONTEXT_SEPARATOR, plural), + number) result = ngettext(*msgs_with_ctxt) if CONTEXT_SEPARATOR in result: # Translation not found @@ -444,11 +423,10 @@ def all_locale_paths(): Return a list of paths to user-provides languages files. """ globalpath = os.path.join( - os.path.dirname(sys.modules[settings.__module__].__file__), "locale" - ) + os.path.dirname(sys.modules[settings.__module__].__file__), 'locale') app_paths = [] for app_config in apps.get_app_configs(): - locale_path = os.path.join(app_config.path, "locale") + locale_path = os.path.join(app_config.path, 'locale') if os.path.exists(locale_path): app_paths.append(locale_path) return [globalpath, *settings.LOCALE_PATHS, *app_paths] @@ -469,7 +447,7 @@ def check_for_language(lang_code): if lang_code is None or not language_code_re.search(lang_code): return False return any( - gettext_module.find("django", path, [to_locale(lang_code)]) is not None + gettext_module.find('django', path, [to_locale(lang_code)]) is not None for path in all_locale_paths() ) @@ -496,17 +474,14 @@ def get_supported_language_variant(lang_code, strict=False): <https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>. """ if lang_code: - # If 'zh-hant-tw' is not supported, try special fallback or subsequent - # language codes i.e. 'zh-hant' and 'zh'. + # If 'fr-ca' is not supported, try special fallback or language-only 'fr'. possible_lang_codes = [lang_code] try: - possible_lang_codes.extend(LANG_INFO[lang_code]["fallback"]) + possible_lang_codes.extend(LANG_INFO[lang_code]['fallback']) except KeyError: pass - i = None - while (i := lang_code.rfind("-", 0, i)) > -1: - possible_lang_codes.append(lang_code[:i]) - generic_lang_code = possible_lang_codes[-1] + generic_lang_code = lang_code.split('-')[0] + possible_lang_codes.append(generic_lang_code) supported_lang_codes = get_languages() for code in possible_lang_codes: @@ -515,7 +490,7 @@ def get_supported_language_variant(lang_code, strict=False): if not strict: # if fr-fr is not supported, try fr-ca. for supported_code in supported_lang_codes: - if supported_code.startswith(generic_lang_code + "-"): + if supported_code.startswith(generic_lang_code + '-'): return supported_code raise LookupError(lang_code) @@ -553,11 +528,7 @@ def get_language_from_request(request, check_path=False): return lang_code lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME) - if ( - lang_code is not None - and lang_code in get_languages() - and check_for_language(lang_code) - ): + if lang_code is not None and lang_code in get_languages() and check_for_language(lang_code): return lang_code try: @@ -565,9 +536,9 @@ def get_language_from_request(request, check_path=False): except LookupError: pass - accept = request.META.get("HTTP_ACCEPT_LANGUAGE", "") + accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '') for accept_lang, unused in parse_accept_lang_header(accept): - if accept_lang == "*": + if accept_lang == '*': break if not language_code_re.search(accept_lang): @@ -597,7 +568,7 @@ def parse_accept_lang_header(lang_string): if pieces[-1]: return () for i in range(0, len(pieces) - 1, 3): - first, lang, priority = pieces[i : i + 3] + first, lang, priority = pieces[i:i + 3] if first: return () if priority: diff --git a/venv/Lib/site-packages/django/utils/tree.py b/venv/Lib/site-packages/django/utils/tree.py index f67c90e..af17be9 100644 --- a/venv/Lib/site-packages/django/utils/tree.py +++ b/venv/Lib/site-packages/django/utils/tree.py @@ -14,10 +14,9 @@ class Node: connection (the root) with the children being either leaf nodes or other Node instances. """ - # Standard connector type. Clients usually won't use this at all and # subclasses will usually override the value. - default = "DEFAULT" + default = 'DEFAULT' def __init__(self, children=None, connector=None, negated=False): """Construct a new Node. If no connector is given, use the default.""" @@ -42,8 +41,8 @@ class Node: return obj def __str__(self): - template = "(NOT (%s: %s))" if self.negated else "(%s: %s)" - return template % (self.connector, ", ".join(str(c) for c in self.children)) + template = '(NOT (%s: %s))' if self.negated else '(%s: %s)' + return template % (self.connector, ', '.join(str(c) for c in self.children)) def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self) @@ -68,23 +67,15 @@ class Node: def __eq__(self, other): return ( - self.__class__ == other.__class__ - and self.connector == other.connector - and self.negated == other.negated - and self.children == other.children + self.__class__ == other.__class__ and + (self.connector, self.negated) == (other.connector, other.negated) and + self.children == other.children ) def __hash__(self): - return hash( - ( - self.__class__, - self.connector, - self.negated, - *make_hashable(self.children), - ) - ) + return hash((self.__class__, self.connector, self.negated, *make_hashable(self.children))) - def add(self, data, conn_type): + def add(self, data, conn_type, squash=True): """ Combine this tree and the data represented by data using the connector conn_type. The combine is done by squashing the node other @@ -95,29 +86,38 @@ class Node: Return a node which can be used in place of data regardless if the node other got squashed or not. + + If `squash` is False the data is prepared and added as a child to + this tree without further logic. """ - if self.connector != conn_type: - obj = self._new_instance(self.children, self.connector, self.negated) + if self.connector == conn_type and data in self.children: + return data + if not squash: + self.children.append(data) + return data + if self.connector == conn_type: + # We can reuse self.children to append or squash the node other. + if (isinstance(data, Node) and not data.negated and + (data.connector == conn_type or len(data) == 1)): + # We can squash the other node's children directly into this + # node. We are just doing (AB)(CD) == (ABCD) here, with the + # addition that if the length of the other node is 1 the + # connector doesn't matter. However, for the len(self) == 1 + # case we don't want to do the squashing, as it would alter + # self.connector. + self.children.extend(data.children) + return self + else: + # We could use perhaps additional logic here to see if some + # children could be used for pushdown here. + self.children.append(data) + return data + else: + obj = self._new_instance(self.children, self.connector, + self.negated) self.connector = conn_type self.children = [obj, data] return data - elif ( - isinstance(data, Node) - and not data.negated - and (data.connector == conn_type or len(data) == 1) - ): - # We can squash the other node's children directly into this node. - # We are just doing (AB)(CD) == (ABCD) here, with the addition that - # if the length of the other node is 1 the connector doesn't - # matter. However, for the len(self) == 1 case we don't want to do - # the squashing, as it would alter self.connector. - self.children.extend(data.children) - return self - else: - # We could use perhaps additional logic here to see if some - # children could be used for pushdown here. - self.children.append(data) - return data def negate(self): """Negate the sense of the root connector.""" diff --git a/venv/Lib/site-packages/django/utils/version.py b/venv/Lib/site-packages/django/utils/version.py index 77f1383..4b26586 100644 --- a/venv/Lib/site-packages/django/utils/version.py +++ b/venv/Lib/site-packages/django/utils/version.py @@ -3,8 +3,7 @@ import functools import os import subprocess import sys - -from django.utils.regex_helper import _lazy_re_compile +from distutils.version import LooseVersion # Private, stable API for detecting the Python version. PYXY means "Python X.Y # or later". So that third-party apps can use these values, each constant @@ -14,7 +13,6 @@ PY36 = sys.version_info >= (3, 6) PY37 = sys.version_info >= (3, 7) PY38 = sys.version_info >= (3, 8) PY39 = sys.version_info >= (3, 9) -PY310 = sys.version_info >= (3, 10) def get_version(version=None): @@ -28,14 +26,14 @@ def get_version(version=None): main = get_main_version(version) - sub = "" - if version[3] == "alpha" and version[4] == 0: + sub = '' + if version[3] == 'alpha' and version[4] == 0: git_changeset = get_git_changeset() if git_changeset: - sub = ".dev%s" % git_changeset + sub = '.dev%s' % git_changeset - elif version[3] != "final": - mapping = {"alpha": "a", "beta": "b", "rc": "rc"} + elif version[3] != 'final': + mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'rc'} sub = mapping[version[3]] + str(version[4]) return main + sub @@ -45,7 +43,7 @@ def get_main_version(version=None): """Return main version (X.Y[.Z]) from VERSION.""" version = get_complete_version(version) parts = 2 if version[2] == 0 else 3 - return ".".join(str(x) for x in version[:parts]) + return '.'.join(str(x) for x in version[:parts]) def get_complete_version(version=None): @@ -57,17 +55,17 @@ def get_complete_version(version=None): from django import VERSION as version else: assert len(version) == 5 - assert version[3] in ("alpha", "beta", "rc", "final") + assert version[3] in ('alpha', 'beta', 'rc', 'final') return version def get_docs_version(version=None): version = get_complete_version(version) - if version[3] != "final": - return "dev" + if version[3] != 'final': + return 'dev' else: - return "%d.%d" % version[:2] + return '%d.%d' % version[:2] @functools.lru_cache() @@ -78,29 +76,18 @@ def get_git_changeset(): This value isn't guaranteed to be unique, but collisions are very unlikely, so it's sufficient for generating the development version numbers. """ - # Repository may not be found if __file__ is undefined, e.g. in a frozen - # module. - if "__file__" not in globals(): - return None repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) git_log = subprocess.run( - "git log --pretty=format:%ct --quiet -1 HEAD", - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=True, - cwd=repo_dir, - universal_newlines=True, + ['git', 'log', '--pretty=format:%ct', '--quiet', '-1', 'HEAD'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=True, cwd=repo_dir, universal_newlines=True, ) timestamp = git_log.stdout - tz = datetime.timezone.utc try: - timestamp = datetime.datetime.fromtimestamp(int(timestamp), tz=tz) + timestamp = datetime.datetime.utcfromtimestamp(int(timestamp)) except ValueError: return None - return timestamp.strftime("%Y%m%d%H%M%S") - - -version_component_re = _lazy_re_compile(r"(\d+|[a-z]+|\.)") + return timestamp.strftime('%Y%m%d%H%M%S') def get_version_tuple(version): @@ -108,13 +95,10 @@ def get_version_tuple(version): Return a tuple of version numbers (e.g. (1, 2, 3)) from the version string (e.g. '1.2.3'). """ + loose_version = LooseVersion(version) version_numbers = [] - for item in version_component_re.split(version): - if item and item != ".": - try: - component = int(item) - except ValueError: - break - else: - version_numbers.append(component) + for item in loose_version.version: + if not isinstance(item, int): + break + version_numbers.append(item) return tuple(version_numbers) diff --git a/venv/Lib/site-packages/django/utils/xmlutils.py b/venv/Lib/site-packages/django/utils/xmlutils.py index c3eb3ba..e4607b9 100644 --- a/venv/Lib/site-packages/django/utils/xmlutils.py +++ b/venv/Lib/site-packages/django/utils/xmlutils.py @@ -21,12 +21,10 @@ class SimplerXMLGenerator(XMLGenerator): self.endElement(name) def characters(self, content): - if content and re.search(r"[\x00-\x08\x0B-\x0C\x0E-\x1F]", content): + if content and re.search(r'[\x00-\x08\x0B-\x0C\x0E-\x1F]', content): # Fail loudly when content has control chars (unsupported in XML 1.0) # See https://www.w3.org/International/questions/qa-controls - raise UnserializableContentError( - "Control characters are not supported in XML 1.0" - ) + raise UnserializableContentError("Control characters are not supported in XML 1.0") XMLGenerator.characters(self, content) def startElement(self, name, attrs): diff --git a/venv/Lib/site-packages/django/views/__init__.py b/venv/Lib/site-packages/django/views/__init__.py index 1440d43..95b0c6b 100644 --- a/venv/Lib/site-packages/django/views/__init__.py +++ b/venv/Lib/site-packages/django/views/__init__.py @@ -1,3 +1,3 @@ from django.views.generic.base import View -__all__ = ["View"] +__all__ = ['View'] diff --git a/venv/Lib/site-packages/django/views/csrf.py b/venv/Lib/site-packages/django/views/csrf.py index 2d96167..72ac8ba 100644 --- a/venv/Lib/site-packages/django/views/csrf.py +++ b/venv/Lib/site-packages/django/views/csrf.py @@ -97,7 +97,7 @@ CSRF_FAILURE_TEMPLATE = """ {% endif %} </body> </html> -""" # NOQA +""" CSRF_FAILURE_TEMPLATE_NAME = "403_csrf.html" @@ -106,46 +106,40 @@ def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME): Default view used when request fails CSRF protection """ from django.middleware.csrf import REASON_NO_CSRF_COOKIE, REASON_NO_REFERER - c = { - "title": _("Forbidden"), - "main": _("CSRF verification failed. Request aborted."), - "reason": reason, - "no_referer": reason == REASON_NO_REFERER, - "no_referer1": _( - "You are seeing this message because this HTTPS site requires a " - "“Referer header” to be sent by your web browser, but none was " - "sent. This header is required for security reasons, to ensure " - "that your browser is not being hijacked by third parties." - ), - "no_referer2": _( - "If you have configured your browser to disable “Referer” headers, " - "please re-enable them, at least for this site, or for HTTPS " - "connections, or for “same-origin” requests." - ), - "no_referer3": _( + 'title': _("Forbidden"), + 'main': _("CSRF verification failed. Request aborted."), + 'reason': reason, + 'no_referer': reason == REASON_NO_REFERER, + 'no_referer1': _( + 'You are seeing this message because this HTTPS site requires a ' + '“Referer header” to be sent by your Web browser, but none was ' + 'sent. This header is required for security reasons, to ensure ' + 'that your browser is not being hijacked by third parties.'), + 'no_referer2': _( + 'If you have configured your browser to disable “Referer” headers, ' + 'please re-enable them, at least for this site, or for HTTPS ' + 'connections, or for “same-origin” requests.'), + 'no_referer3': _( 'If you are using the <meta name="referrer" ' - 'content="no-referrer"> tag or including the “Referrer-Policy: ' - "no-referrer” header, please remove them. The CSRF protection " - "requires the “Referer” header to do strict referer checking. If " - "you’re concerned about privacy, use alternatives like " - '<a rel="noreferrer" …> for links to third-party sites.' - ), - "no_cookie": reason == REASON_NO_CSRF_COOKIE, - "no_cookie1": _( + 'content=\"no-referrer\"> tag or including the “Referrer-Policy: ' + 'no-referrer” header, please remove them. The CSRF protection ' + 'requires the “Referer” header to do strict referer checking. If ' + 'you’re concerned about privacy, use alternatives like ' + '<a rel=\"noreferrer\" …> for links to third-party sites.'), + 'no_cookie': reason == REASON_NO_CSRF_COOKIE, + 'no_cookie1': _( "You are seeing this message because this site requires a CSRF " "cookie when submitting forms. This cookie is required for " "security reasons, to ensure that your browser is not being " - "hijacked by third parties." - ), - "no_cookie2": _( - "If you have configured your browser to disable cookies, please " - "re-enable them, at least for this site, or for “same-origin” " - "requests." - ), - "DEBUG": settings.DEBUG, - "docs_version": get_docs_version(), - "more": _("More information is available with DEBUG=True."), + "hijacked by third parties."), + 'no_cookie2': _( + 'If you have configured your browser to disable cookies, please ' + 're-enable them, at least for this site, or for “same-origin” ' + 'requests.'), + 'DEBUG': settings.DEBUG, + 'docs_version': get_docs_version(), + 'more': _("More information is available with DEBUG=True."), } try: t = loader.get_template(template_name) @@ -157,4 +151,4 @@ def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME): else: # Raise if a developer-specified template doesn't exist. raise - return HttpResponseForbidden(t.render(c), content_type="text/html") + return HttpResponseForbidden(t.render(c), content_type='text/html') diff --git a/venv/Lib/site-packages/django/views/debug.py b/venv/Lib/site-packages/django/views/debug.py index 157c997..a24ffa6 100644 --- a/venv/Lib/site-packages/django/views/debug.py +++ b/venv/Lib/site-packages/django/views/debug.py @@ -23,18 +23,10 @@ from django.utils.version import get_docs_version # works even if the template loader is broken. DEBUG_ENGINE = Engine( debug=True, - libraries={"i18n": "django.templatetags.i18n"}, + libraries={'i18n': 'django.templatetags.i18n'}, ) - -def builtin_template_path(name): - """ - Return a path to a builtin template. - - Avoid calling this function at the module level or in a class-definition - because __file__ may not exist, e.g. in frozen environments. - """ - return Path(__file__).parent / "templates" / name +CURRENT_DIR = Path(__file__).parent class ExceptionCycleWarning(UserWarning): @@ -48,7 +40,6 @@ class CallableSettingWrapper: * Not to break the debug page if the callable forbidding to set attributes (#23070). """ - def __init__(self, callable_setting): self._wrapped = callable_setting @@ -62,14 +53,12 @@ def technical_500_response(request, exc_type, exc_value, tb, status_code=500): the values returned from sys.exc_info() and friends. """ reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb) - if request.accepts("text/html"): + if request.accepts('text/html'): html = reporter.get_traceback_html() - return HttpResponse(html, status=status_code, content_type="text/html") + return HttpResponse(html, status=status_code, content_type='text/html') else: text = reporter.get_traceback_text() - return HttpResponse( - text, status=status_code, content_type="text/plain; charset=utf-8" - ) + return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8') @functools.lru_cache() @@ -80,16 +69,12 @@ def get_default_exception_reporter_filter(): def get_exception_reporter_filter(request): default_filter = get_default_exception_reporter_filter() - return getattr(request, "exception_reporter_filter", default_filter) + return getattr(request, 'exception_reporter_filter', default_filter) def get_exception_reporter_class(request): - default_exception_reporter_class = import_string( - settings.DEFAULT_EXCEPTION_REPORTER - ) - return getattr( - request, "exception_reporter_class", default_exception_reporter_class - ) + default_exception_reporter_class = import_string(settings.DEFAULT_EXCEPTION_REPORTER) + return getattr(request, 'exception_reporter_class', default_exception_reporter_class) class SafeExceptionReporterFilter: @@ -97,11 +82,8 @@ class SafeExceptionReporterFilter: Use annotations made by the sensitive_post_parameters and sensitive_variables decorators to filter out sensitive information. """ - - cleansed_substitute = "********************" - hidden_settings = _lazy_re_compile( - "API|TOKEN|KEY|SECRET|PASS|SIGNATURE", flags=re.I - ) + cleansed_substitute = '********************' + hidden_settings = _lazy_re_compile('API|TOKEN|KEY|SECRET|PASS|SIGNATURE', flags=re.I) def cleanse_setting(self, key, value): """ @@ -118,9 +100,9 @@ class SafeExceptionReporterFilter: elif isinstance(value, dict): cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()} elif isinstance(value, list): - cleansed = [self.cleanse_setting("", v) for v in value] + cleansed = [self.cleanse_setting('', v) for v in value] elif isinstance(value, tuple): - cleansed = tuple([self.cleanse_setting("", v) for v in value]) + cleansed = tuple([self.cleanse_setting('', v) for v in value]) else: cleansed = value @@ -144,7 +126,7 @@ class SafeExceptionReporterFilter: """ Return a dictionary of request.META with sensitive values redacted. """ - if not hasattr(request, "META"): + if not hasattr(request, 'META'): return {} return {k: self.cleanse_setting(k, v) for k, v in request.META.items()} @@ -163,7 +145,7 @@ class SafeExceptionReporterFilter: This mitigates leaking sensitive POST parameters if something like request.POST['nonexistent_key'] throws an exception (#21098). """ - sensitive_post_parameters = getattr(request, "sensitive_post_parameters", []) + sensitive_post_parameters = getattr(request, 'sensitive_post_parameters', []) if self.is_active(request) and sensitive_post_parameters: multivaluedict = multivaluedict.copy() for param in sensitive_post_parameters: @@ -179,12 +161,10 @@ class SafeExceptionReporterFilter: if request is None: return {} else: - sensitive_post_parameters = getattr( - request, "sensitive_post_parameters", [] - ) + sensitive_post_parameters = getattr(request, 'sensitive_post_parameters', []) if self.is_active(request) and sensitive_post_parameters: cleansed = request.POST.copy() - if sensitive_post_parameters == "__ALL__": + if sensitive_post_parameters == '__ALL__': # Cleanse all parameters. for k in cleansed: cleansed[k] = self.cleansed_substitute @@ -205,7 +185,7 @@ class SafeExceptionReporterFilter: # MultiValueDicts will have a return value. is_multivalue_dict = isinstance(value, MultiValueDict) except Exception as e: - return "{!r} while evaluating {!r}".format(e, value) + return '{!r} while evaluating {!r}'.format(e, value) if is_multivalue_dict: # Cleanse MultiValueDicts (request.POST is the one we usually care about) @@ -222,20 +202,18 @@ class SafeExceptionReporterFilter: current_frame = tb_frame.f_back sensitive_variables = None while current_frame is not None: - if ( - current_frame.f_code.co_name == "sensitive_variables_wrapper" - and "sensitive_variables_wrapper" in current_frame.f_locals - ): + if (current_frame.f_code.co_name == 'sensitive_variables_wrapper' and + 'sensitive_variables_wrapper' in current_frame.f_locals): # The sensitive_variables decorator was used, so we take note # of the sensitive variables' names. - wrapper = current_frame.f_locals["sensitive_variables_wrapper"] - sensitive_variables = getattr(wrapper, "sensitive_variables", None) + wrapper = current_frame.f_locals['sensitive_variables_wrapper'] + sensitive_variables = getattr(wrapper, 'sensitive_variables', None) break current_frame = current_frame.f_back cleansed = {} if self.is_active(request) and sensitive_variables: - if sensitive_variables == "__ALL__": + if sensitive_variables == '__ALL__': # Cleanse all variables for name in tb_frame.f_locals: cleansed[name] = self.cleansed_substitute @@ -253,16 +231,14 @@ class SafeExceptionReporterFilter: for name, value in tb_frame.f_locals.items(): cleansed[name] = self.cleanse_special_types(request, value) - if ( - tb_frame.f_code.co_name == "sensitive_variables_wrapper" - and "sensitive_variables_wrapper" in tb_frame.f_locals - ): + if (tb_frame.f_code.co_name == 'sensitive_variables_wrapper' and + 'sensitive_variables_wrapper' in tb_frame.f_locals): # For good measure, obfuscate the decorated function's arguments in # the sensitive_variables decorator's frame, in case the variables # associated with those arguments were meant to be obfuscated from # the decorated function's frame. - cleansed["func_args"] = self.cleansed_substitute - cleansed["func_kwargs"] = self.cleansed_substitute + cleansed['func_args'] = self.cleansed_substitute + cleansed['func_kwargs'] = self.cleansed_substitute return cleansed.items() @@ -272,11 +248,11 @@ class ExceptionReporter: @property def html_template_path(self): - return builtin_template_path("technical_500.html") + return CURRENT_DIR / 'templates' / 'technical_500.html' @property def text_template_path(self): - return builtin_template_path("technical_500.txt") + return CURRENT_DIR / 'templates' / 'technical_500.txt' def __init__(self, request, exc_type, exc_value, tb, is_email=False): self.request = request @@ -286,21 +262,10 @@ class ExceptionReporter: self.tb = tb self.is_email = is_email - self.template_info = getattr(self.exc_value, "template_debug", None) + self.template_info = getattr(self.exc_value, 'template_debug', None) self.template_does_not_exist = False self.postmortem = None - def _get_raw_insecure_uri(self): - """ - Return an absolute URI from variables available in this request. Skip - allowed hosts protection, so may return insecure URI. - """ - return "{scheme}://{host}{path}".format( - scheme=self.request.scheme, - host=self.request._get_raw_host(), - path=self.request.get_full_path(), - ) - def get_traceback_data(self): """Return a dictionary containing traceback information.""" if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist): @@ -309,27 +274,26 @@ class ExceptionReporter: frames = self.get_traceback_frames() for i, frame in enumerate(frames): - if "vars" in frame: + if 'vars' in frame: frame_vars = [] - for k, v in frame["vars"]: + for k, v in frame['vars']: v = pprint(v) # Trim large blobs of data if len(v) > 4096: - v = "%s… <trimmed %d bytes string>" % (v[0:4096], len(v)) + v = '%s… <trimmed %d bytes string>' % (v[0:4096], len(v)) frame_vars.append((k, v)) - frame["vars"] = frame_vars + frame['vars'] = frame_vars frames[i] = frame - unicode_hint = "" + unicode_hint = '' if self.exc_type and issubclass(self.exc_type, UnicodeError): - start = getattr(self.exc_value, "start", None) - end = getattr(self.exc_value, "end", None) + start = getattr(self.exc_value, 'start', None) + end = getattr(self.exc_value, 'end', None) if start is not None and end is not None: unicode_str = self.exc_value.args[1] unicode_hint = force_str( - unicode_str[max(start - 5, 0) : min(end + 5, len(unicode_str))], - "ascii", - errors="replace", + unicode_str[max(start - 5, 0):min(end + 5, len(unicode_str))], + 'ascii', errors='replace' ) from django import get_version @@ -341,60 +305,56 @@ class ExceptionReporter: except Exception: # request.user may raise OperationalError if the database is # unavailable, for example. - user_str = "[unable to retrieve the current user]" + user_str = '[unable to retrieve the current user]' c = { - "is_email": self.is_email, - "unicode_hint": unicode_hint, - "frames": frames, - "request": self.request, - "request_meta": self.filter.get_safe_request_meta(self.request), - "user_str": user_str, - "filtered_POST_items": list( - self.filter.get_post_parameters(self.request).items() - ), - "settings": self.filter.get_safe_settings(), - "sys_executable": sys.executable, - "sys_version_info": "%d.%d.%d" % sys.version_info[0:3], - "server_time": timezone.now(), - "django_version_info": get_version(), - "sys_path": sys.path, - "template_info": self.template_info, - "template_does_not_exist": self.template_does_not_exist, - "postmortem": self.postmortem, + 'is_email': self.is_email, + 'unicode_hint': unicode_hint, + 'frames': frames, + 'request': self.request, + 'request_meta': self.filter.get_safe_request_meta(self.request), + 'user_str': user_str, + 'filtered_POST_items': list(self.filter.get_post_parameters(self.request).items()), + 'settings': self.filter.get_safe_settings(), + 'sys_executable': sys.executable, + 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], + 'server_time': timezone.now(), + 'django_version_info': get_version(), + 'sys_path': sys.path, + 'template_info': self.template_info, + 'template_does_not_exist': self.template_does_not_exist, + 'postmortem': self.postmortem, } if self.request is not None: - c["request_GET_items"] = self.request.GET.items() - c["request_FILES_items"] = self.request.FILES.items() - c["request_COOKIES_items"] = self.request.COOKIES.items() - c["request_insecure_uri"] = self._get_raw_insecure_uri() - + c['request_GET_items'] = self.request.GET.items() + c['request_FILES_items'] = self.request.FILES.items() + c['request_COOKIES_items'] = self.request.COOKIES.items() # Check whether exception info is available if self.exc_type: - c["exception_type"] = self.exc_type.__name__ + c['exception_type'] = self.exc_type.__name__ if self.exc_value: - c["exception_value"] = str(self.exc_value) + c['exception_value'] = str(self.exc_value) if frames: - c["lastframe"] = frames[-1] + c['lastframe'] = frames[-1] return c def get_traceback_html(self): """Return HTML version of debug 500 HTTP error page.""" - with self.html_template_path.open(encoding="utf-8") as fh: + with self.html_template_path.open(encoding='utf-8') as fh: t = DEBUG_ENGINE.from_string(fh.read()) c = Context(self.get_traceback_data(), use_l10n=False) return t.render(c) def get_traceback_text(self): """Return plain text version of debug 500 HTTP error page.""" - with self.text_template_path.open(encoding="utf-8") as fh: + with self.text_template_path.open(encoding='utf-8') as fh: t = DEBUG_ENGINE.from_string(fh.read()) c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False) return t.render(c) def _get_source(self, filename, loader, module_name): source = None - if hasattr(loader, "get_source"): + if hasattr(loader, 'get_source'): try: source = loader.get_source(module_name) except ImportError: @@ -403,15 +363,13 @@ class ExceptionReporter: source = source.splitlines() if source is None: try: - with open(filename, "rb") as fp: + with open(filename, 'rb') as fp: source = fp.read().splitlines() except OSError: pass return source - def _get_lines_from_file( - self, filename, lineno, context_lines, loader=None, module_name=None - ): + def _get_lines_from_file(self, filename, lineno, context_lines, loader=None, module_name=None): """ Return context_lines before and after lineno from file. Return (pre_context_lineno, pre_context, context_line, post_context). @@ -424,15 +382,15 @@ class ExceptionReporter: # apply tokenize.detect_encoding to decode the source into a # string, then we should do that ourselves. if isinstance(source[0], bytes): - encoding = "ascii" + encoding = 'ascii' for line in source[:2]: # File coding may be specified. Match pattern from PEP-263 # (https://www.python.org/dev/peps/pep-0263/) - match = re.search(rb"coding[:=]\s*([-\w.]+)", line) + match = re.search(br'coding[:=]\s*([-\w.]+)', line) if match: - encoding = match[1].decode("ascii") + encoding = match[1].decode('ascii') break - source = [str(sline, encoding, "replace") for sline in source] + source = [str(sline, encoding, 'replace') for sline in source] lower_bound = max(0, lineno - context_lines) upper_bound = lineno + context_lines @@ -440,15 +398,15 @@ class ExceptionReporter: try: pre_context = source[lower_bound:lineno] context_line = source[lineno] - post_context = source[lineno + 1 : upper_bound] + post_context = source[lineno + 1:upper_bound] except IndexError: return None, [], None, [] return lower_bound, pre_context, context_line, post_context def _get_explicit_or_implicit_cause(self, exc_value): - explicit = getattr(exc_value, "__cause__", None) - suppress_context = getattr(exc_value, "__suppress_context__", None) - implicit = getattr(exc_value, "__context__", None) + explicit = getattr(exc_value, '__cause__', None) + suppress_context = getattr(exc_value, '__suppress_context__', None) + implicit = getattr(exc_value, '__context__', None) return explicit or (None if suppress_context else implicit) def get_traceback_frames(self): @@ -486,58 +444,47 @@ class ExceptionReporter: def get_exception_traceback_frames(self, exc_value, tb): exc_cause = self._get_explicit_or_implicit_cause(exc_value) - exc_cause_explicit = getattr(exc_value, "__cause__", True) + exc_cause_explicit = getattr(exc_value, '__cause__', True) if tb is None: yield { - "exc_cause": exc_cause, - "exc_cause_explicit": exc_cause_explicit, - "tb": None, - "type": "user", + 'exc_cause': exc_cause, + 'exc_cause_explicit': exc_cause_explicit, + 'tb': None, + 'type': 'user', } while tb is not None: # Support for __traceback_hide__ which is used by a few libraries # to hide internal frames. - if tb.tb_frame.f_locals.get("__traceback_hide__"): + if tb.tb_frame.f_locals.get('__traceback_hide__'): tb = tb.tb_next continue filename = tb.tb_frame.f_code.co_filename function = tb.tb_frame.f_code.co_name lineno = tb.tb_lineno - 1 - loader = tb.tb_frame.f_globals.get("__loader__") - module_name = tb.tb_frame.f_globals.get("__name__") or "" - ( - pre_context_lineno, - pre_context, - context_line, - post_context, - ) = self._get_lines_from_file( - filename, - lineno, - 7, - loader, - module_name, + loader = tb.tb_frame.f_globals.get('__loader__') + module_name = tb.tb_frame.f_globals.get('__name__') or '' + pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file( + filename, lineno, 7, loader, module_name, ) if pre_context_lineno is None: pre_context_lineno = lineno pre_context = [] - context_line = "<source code not available>" + context_line = '<source code not available>' post_context = [] yield { - "exc_cause": exc_cause, - "exc_cause_explicit": exc_cause_explicit, - "tb": tb, - "type": "django" if module_name.startswith("django.") else "user", - "filename": filename, - "function": function, - "lineno": lineno + 1, - "vars": self.filter.get_traceback_frame_variables( - self.request, tb.tb_frame - ), - "id": id(tb), - "pre_context": pre_context, - "context_line": context_line, - "post_context": post_context, - "pre_context_lineno": pre_context_lineno + 1, + 'exc_cause': exc_cause, + 'exc_cause_explicit': exc_cause_explicit, + 'tb': tb, + 'type': 'django' if module_name.startswith('django.') else 'user', + 'filename': filename, + 'function': function, + 'lineno': lineno + 1, + 'vars': self.filter.get_traceback_frame_variables(self.request, tb.tb_frame), + 'id': id(tb), + 'pre_context': pre_context, + 'context_line': context_line, + 'post_context': post_context, + 'pre_context_lineno': pre_context_lineno + 1, } tb = tb.tb_next @@ -545,32 +492,30 @@ class ExceptionReporter: def technical_404_response(request, exception): """Create a technical 404 error response. `exception` is the Http404.""" try: - error_url = exception.args[0]["path"] + error_url = exception.args[0]['path'] except (IndexError, TypeError, KeyError): error_url = request.path_info[1:] # Trim leading slash try: - tried = exception.args[0]["tried"] + tried = exception.args[0]['tried'] except (IndexError, TypeError, KeyError): resolved = True tried = request.resolver_match.tried if request.resolver_match else None else: resolved = False - if not tried or ( # empty URLconf - request.path == "/" - and len(tried) == 1 - and len(tried[0]) == 1 # default URLconf - and getattr(tried[0][0], "app_name", "") - == getattr(tried[0][0], "namespace", "") - == "admin" - ): + if (not tried or ( # empty URLconf + request.path == '/' and + len(tried) == 1 and # default URLconf + len(tried[0]) == 1 and + getattr(tried[0][0], 'app_name', '') == getattr(tried[0][0], 'namespace', '') == 'admin' + )): return default_urlconf(request) - urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) + urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) if isinstance(urlconf, types.ModuleType): urlconf = urlconf.__name__ - caller = "" + caller = '' try: resolver_match = resolve(request.path) except Http404: @@ -578,45 +523,38 @@ def technical_404_response(request, exception): else: obj = resolver_match.func - if hasattr(obj, "view_class"): - obj = obj.view_class - - if hasattr(obj, "__name__"): + if hasattr(obj, '__name__'): caller = obj.__name__ - elif hasattr(obj, "__class__") and hasattr(obj.__class__, "__name__"): + elif hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'): caller = obj.__class__.__name__ - if hasattr(obj, "__module__"): + if hasattr(obj, '__module__'): module = obj.__module__ - caller = "%s.%s" % (module, caller) + caller = '%s.%s' % (module, caller) - with builtin_template_path("technical_404.html").open(encoding="utf-8") as fh: + with Path(CURRENT_DIR, 'templates', 'technical_404.html').open(encoding='utf-8') as fh: t = DEBUG_ENGINE.from_string(fh.read()) reporter_filter = get_default_exception_reporter_filter() - c = Context( - { - "urlconf": urlconf, - "root_urlconf": settings.ROOT_URLCONF, - "request_path": error_url, - "urlpatterns": tried, - "resolved": resolved, - "reason": str(exception), - "request": request, - "settings": reporter_filter.get_safe_settings(), - "raising_view_name": caller, - } - ) - return HttpResponseNotFound(t.render(c), content_type="text/html") + c = Context({ + 'urlconf': urlconf, + 'root_urlconf': settings.ROOT_URLCONF, + 'request_path': error_url, + 'urlpatterns': tried, + 'resolved': resolved, + 'reason': str(exception), + 'request': request, + 'settings': reporter_filter.get_safe_settings(), + 'raising_view_name': caller, + }) + return HttpResponseNotFound(t.render(c), content_type='text/html') def default_urlconf(request): """Create an empty URLconf 404 error response.""" - with builtin_template_path("default_urlconf.html").open(encoding="utf-8") as fh: + with Path(CURRENT_DIR, 'templates', 'default_urlconf.html').open(encoding='utf-8') as fh: t = DEBUG_ENGINE.from_string(fh.read()) - c = Context( - { - "version": get_docs_version(), - } - ) + c = Context({ + 'version': get_docs_version(), + }) - return HttpResponse(t.render(c), content_type="text/html") + return HttpResponse(t.render(c), content_type='text/html') diff --git a/venv/Lib/site-packages/django/views/decorators/cache.py b/venv/Lib/site-packages/django/views/decorators/cache.py index de61b6f..773cf0c 100644 --- a/venv/Lib/site-packages/django/views/decorators/cache.py +++ b/venv/Lib/site-packages/django/views/decorators/cache.py @@ -20,9 +20,7 @@ def cache_page(timeout, *, cache=None, key_prefix=None): into account on caching -- just like the middleware does. """ return decorator_from_middleware_with_args(CacheMiddleware)( - page_timeout=timeout, - cache_alias=cache, - key_prefix=key_prefix, + page_timeout=timeout, cache_alias=cache, key_prefix=key_prefix, ) @@ -30,19 +28,10 @@ def cache_control(**kwargs): def _cache_controller(viewfunc): @wraps(viewfunc) def _cache_controlled(request, *args, **kw): - # Ensure argument looks like a request. - if not hasattr(request, "META"): - raise TypeError( - "cache_control didn't receive an HttpRequest. If you are " - "decorating a classmethod, be sure to use " - "@method_decorator." - ) response = viewfunc(request, *args, **kw) patch_cache_control(response, **kwargs) return response - return _cache_controlled - return _cache_controller @@ -50,17 +39,9 @@ def never_cache(view_func): """ Decorator that adds headers to a response so that it will never be cached. """ - @wraps(view_func) def _wrapped_view_func(request, *args, **kwargs): - # Ensure argument looks like a request. - if not hasattr(request, "META"): - raise TypeError( - "never_cache didn't receive an HttpRequest. If you are " - "decorating a classmethod, be sure to use @method_decorator." - ) response = view_func(request, *args, **kwargs) add_never_cache_headers(response) return response - return _wrapped_view_func diff --git a/venv/Lib/site-packages/django/views/decorators/clickjacking.py b/venv/Lib/site-packages/django/views/decorators/clickjacking.py index c59f4f2..f8fc2d2 100644 --- a/venv/Lib/site-packages/django/views/decorators/clickjacking.py +++ b/venv/Lib/site-packages/django/views/decorators/clickjacking.py @@ -11,13 +11,11 @@ def xframe_options_deny(view_func): def some_view(request): ... """ - def wrapped_view(*args, **kwargs): resp = view_func(*args, **kwargs) - if resp.get("X-Frame-Options") is None: - resp["X-Frame-Options"] = "DENY" + if resp.get('X-Frame-Options') is None: + resp['X-Frame-Options'] = 'DENY' return resp - return wraps(view_func)(wrapped_view) @@ -31,13 +29,11 @@ def xframe_options_sameorigin(view_func): def some_view(request): ... """ - def wrapped_view(*args, **kwargs): resp = view_func(*args, **kwargs) - if resp.get("X-Frame-Options") is None: - resp["X-Frame-Options"] = "SAMEORIGIN" + if resp.get('X-Frame-Options') is None: + resp['X-Frame-Options'] = 'SAMEORIGIN' return resp - return wraps(view_func)(wrapped_view) @@ -50,10 +46,8 @@ def xframe_options_exempt(view_func): def some_view(request): ... """ - def wrapped_view(*args, **kwargs): resp = view_func(*args, **kwargs) resp.xframe_options_exempt = True return resp - return wraps(view_func)(wrapped_view) diff --git a/venv/Lib/site-packages/django/views/decorators/common.py b/venv/Lib/site-packages/django/views/decorators/common.py index 8c84688..34b0e5a 100644 --- a/venv/Lib/site-packages/django/views/decorators/common.py +++ b/venv/Lib/site-packages/django/views/decorators/common.py @@ -10,6 +10,5 @@ def no_append_slash(view_func): # nicer if they don't have side effects, so return a new function. def wrapped_view(*args, **kwargs): return view_func(*args, **kwargs) - wrapped_view.should_append_slash = False return wraps(view_func)(wrapped_view) diff --git a/venv/Lib/site-packages/django/views/decorators/csrf.py b/venv/Lib/site-packages/django/views/decorators/csrf.py index 4841089..19d439a 100644 --- a/venv/Lib/site-packages/django/views/decorators/csrf.py +++ b/venv/Lib/site-packages/django/views/decorators/csrf.py @@ -19,7 +19,7 @@ class _EnsureCsrfToken(CsrfViewMiddleware): requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken) -requires_csrf_token.__name__ = "requires_csrf_token" +requires_csrf_token.__name__ = 'requires_csrf_token' requires_csrf_token.__doc__ = """ Use this decorator on views that need a correct csrf_token available to RequestContext, but without the CSRF protection that csrf_protect @@ -39,7 +39,7 @@ class _EnsureCsrfCookie(CsrfViewMiddleware): ensure_csrf_cookie = decorator_from_middleware(_EnsureCsrfCookie) -ensure_csrf_cookie.__name__ = "ensure_csrf_cookie" +ensure_csrf_cookie.__name__ = 'ensure_csrf_cookie' ensure_csrf_cookie.__doc__ = """ Use this decorator to ensure that a view sets a CSRF cookie, whether or not it uses the csrf_token template tag, or the CsrfViewMiddleware is used. @@ -52,6 +52,5 @@ def csrf_exempt(view_func): # if they don't have side effects, so return a new function. def wrapped_view(*args, **kwargs): return view_func(*args, **kwargs) - wrapped_view.csrf_exempt = True return wraps(view_func)(wrapped_view) diff --git a/venv/Lib/site-packages/django/views/decorators/debug.py b/venv/Lib/site-packages/django/views/decorators/debug.py index 5cf8db8..18900ff 100644 --- a/venv/Lib/site-packages/django/views/decorators/debug.py +++ b/venv/Lib/site-packages/django/views/decorators/debug.py @@ -28,8 +28,8 @@ def sensitive_variables(*variables): """ if len(variables) == 1 and callable(variables[0]): raise TypeError( - "sensitive_variables() must be called to use it as a decorator, " - "e.g., use @sensitive_variables(), not @sensitive_variables." + 'sensitive_variables() must be called to use it as a decorator, ' + 'e.g., use @sensitive_variables(), not @sensitive_variables.' ) def decorator(func): @@ -38,11 +38,9 @@ def sensitive_variables(*variables): if variables: sensitive_variables_wrapper.sensitive_variables = variables else: - sensitive_variables_wrapper.sensitive_variables = "__ALL__" + sensitive_variables_wrapper.sensitive_variables = '__ALL__' return func(*func_args, **func_kwargs) - return sensitive_variables_wrapper - return decorator @@ -71,26 +69,23 @@ def sensitive_post_parameters(*parameters): """ if len(parameters) == 1 and callable(parameters[0]): raise TypeError( - "sensitive_post_parameters() must be called to use it as a " - "decorator, e.g., use @sensitive_post_parameters(), not " - "@sensitive_post_parameters." + 'sensitive_post_parameters() must be called to use it as a ' + 'decorator, e.g., use @sensitive_post_parameters(), not ' + '@sensitive_post_parameters.' ) def decorator(view): @functools.wraps(view) def sensitive_post_parameters_wrapper(request, *args, **kwargs): - if not isinstance(request, HttpRequest): - raise TypeError( - "sensitive_post_parameters didn't receive an HttpRequest " - "object. If you are decorating a classmethod, make sure " - "to use @method_decorator." - ) + assert isinstance(request, HttpRequest), ( + "sensitive_post_parameters didn't receive an HttpRequest. " + "If you are decorating a classmethod, be sure to use " + "@method_decorator." + ) if parameters: request.sensitive_post_parameters = parameters else: - request.sensitive_post_parameters = "__ALL__" + request.sensitive_post_parameters = '__ALL__' return view(request, *args, **kwargs) - return sensitive_post_parameters_wrapper - return decorator diff --git a/venv/Lib/site-packages/django/views/decorators/http.py b/venv/Lib/site-packages/django/views/decorators/http.py index 6f7578e..5caf13e 100644 --- a/venv/Lib/site-packages/django/views/decorators/http.py +++ b/venv/Lib/site-packages/django/views/decorators/http.py @@ -2,11 +2,11 @@ Decorators for views based on HTTP headers. """ +from calendar import timegm from functools import wraps from django.http import HttpResponseNotAllowed from django.middleware.http import ConditionalGetMiddleware -from django.utils import timezone from django.utils.cache import get_conditional_response from django.utils.decorators import decorator_from_middleware from django.utils.http import http_date, quote_etag @@ -26,24 +26,19 @@ def require_http_methods(request_method_list): Note that request methods should be in uppercase. """ - def decorator(func): @wraps(func) def inner(request, *args, **kwargs): if request.method not in request_method_list: response = HttpResponseNotAllowed(request_method_list) log_response( - "Method Not Allowed (%s): %s", - request.method, - request.path, + 'Method Not Allowed (%s): %s', request.method, request.path, response=response, request=request, ) return response return func(request, *args, **kwargs) - return inner - return decorator @@ -54,9 +49,7 @@ require_POST = require_http_methods(["POST"]) require_POST.__doc__ = "Decorator to require that a view only accepts the POST method." require_safe = require_http_methods(["GET", "HEAD"]) -require_safe.__doc__ = ( - "Decorator to require that a view only accepts safe methods: GET and HEAD." -) +require_safe.__doc__ = "Decorator to require that a view only accepts safe methods: GET and HEAD." def condition(etag_func=None, last_modified_func=None): @@ -81,7 +74,6 @@ def condition(etag_func=None, last_modified_func=None): will add the generated ETag and Last-Modified headers to the response if the headers aren't already set and if the request's method is safe. """ - def decorator(func): @wraps(func) def inner(request, *args, **kwargs): @@ -90,9 +82,7 @@ def condition(etag_func=None, last_modified_func=None): if last_modified_func: dt = last_modified_func(request, *args, **kwargs) if dt: - if not timezone.is_aware(dt): - dt = timezone.make_aware(dt, timezone.utc) - return int(dt.timestamp()) + return timegm(dt.utctimetuple()) # The value from etag_func() could be quoted or unquoted. res_etag = etag_func(request, *args, **kwargs) if etag_func else None @@ -110,16 +100,15 @@ def condition(etag_func=None, last_modified_func=None): # Set relevant headers on the response if they don't already exist # and if the request method is safe. - if request.method in ("GET", "HEAD"): - if res_last_modified and not response.has_header("Last-Modified"): - response.headers["Last-Modified"] = http_date(res_last_modified) + if request.method in ('GET', 'HEAD'): + if res_last_modified and not response.has_header('Last-Modified'): + response.headers['Last-Modified'] = http_date(res_last_modified) if res_etag: - response.headers.setdefault("ETag", res_etag) + response.headers.setdefault('ETag', res_etag) return response return inner - return decorator diff --git a/venv/Lib/site-packages/django/views/decorators/vary.py b/venv/Lib/site-packages/django/views/decorators/vary.py index 6098a0f..68b783e 100644 --- a/venv/Lib/site-packages/django/views/decorators/vary.py +++ b/venv/Lib/site-packages/django/views/decorators/vary.py @@ -14,16 +14,13 @@ def vary_on_headers(*headers): Note that the header names are not case-sensitive. """ - def decorator(func): @wraps(func) def inner_func(*args, **kwargs): response = func(*args, **kwargs) patch_vary_headers(response, headers) return response - return inner_func - return decorator @@ -36,11 +33,9 @@ def vary_on_cookie(func): def index(request): ... """ - @wraps(func) def inner_func(*args, **kwargs): response = func(*args, **kwargs) - patch_vary_headers(response, ("Cookie",)) + patch_vary_headers(response, ('Cookie',)) return response - return inner_func diff --git a/venv/Lib/site-packages/django/views/defaults.py b/venv/Lib/site-packages/django/views/defaults.py index d5f6708..2fb0d0b 100644 --- a/venv/Lib/site-packages/django/views/defaults.py +++ b/venv/Lib/site-packages/django/views/defaults.py @@ -1,18 +1,16 @@ from urllib.parse import quote from django.http import ( - HttpResponseBadRequest, - HttpResponseForbidden, - HttpResponseNotFound, + HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, HttpResponseServerError, ) from django.template import Context, Engine, TemplateDoesNotExist, loader from django.views.decorators.csrf import requires_csrf_token -ERROR_404_TEMPLATE_NAME = "404.html" -ERROR_403_TEMPLATE_NAME = "403.html" -ERROR_400_TEMPLATE_NAME = "400.html" -ERROR_500_TEMPLATE_NAME = "500.html" +ERROR_404_TEMPLATE_NAME = '404.html' +ERROR_403_TEMPLATE_NAME = '403.html' +ERROR_400_TEMPLATE_NAME = '400.html' +ERROR_500_TEMPLATE_NAME = '500.html' ERROR_PAGE_TEMPLATE = """ <!doctype html> <html lang="en"> @@ -26,11 +24,9 @@ ERROR_PAGE_TEMPLATE = """ """ -# These views can be called when CsrfViewMiddleware.process_view() not run, +# This can be called when CsrfViewMiddleware.process_view has not run, # therefore need @requires_csrf_token in case the template needs # {% csrf_token %}. - - @requires_csrf_token def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME): """ @@ -56,13 +52,13 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME): if isinstance(message, str): exception_repr = message context = { - "request_path": quote(request.path), - "exception": exception_repr, + 'request_path': quote(request.path), + 'exception': exception_repr, } try: template = loader.get_template(template_name) body = template.render(context, request) - content_type = None # Django will use 'text/html'. + content_type = None # Django will use 'text/html'. except TemplateDoesNotExist: if template_name != ERROR_404_TEMPLATE_NAME: # Reraise if it's a missing custom template. @@ -70,14 +66,13 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME): # Render template (even though there are no substitutions) to allow # inspecting the context in tests. template = Engine().from_string( - ERROR_PAGE_TEMPLATE - % { - "title": "Not Found", - "details": "The requested resource was not found on this server.", + ERROR_PAGE_TEMPLATE % { + 'title': 'Not Found', + 'details': 'The requested resource was not found on this server.', }, ) body = template.render(Context(context)) - content_type = "text/html" + content_type = 'text/html' return HttpResponseNotFound(body, content_type=content_type) @@ -96,8 +91,8 @@ def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): # Reraise if it's a missing custom template. raise return HttpResponseServerError( - ERROR_PAGE_TEMPLATE % {"title": "Server Error (500)", "details": ""}, - content_type="text/html", + ERROR_PAGE_TEMPLATE % {'title': 'Server Error (500)', 'details': ''}, + content_type='text/html', ) return HttpResponseServerError(template.render()) @@ -117,24 +112,23 @@ def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME): # Reraise if it's a missing custom template. raise return HttpResponseBadRequest( - ERROR_PAGE_TEMPLATE % {"title": "Bad Request (400)", "details": ""}, - content_type="text/html", + ERROR_PAGE_TEMPLATE % {'title': 'Bad Request (400)', 'details': ''}, + content_type='text/html', ) - # No exception content is passed to the template, to not disclose any - # sensitive information. + # No exception content is passed to the template, to not disclose any sensitive information. return HttpResponseBadRequest(template.render()) +# This can be called when CsrfViewMiddleware.process_view has not run, +# therefore need @requires_csrf_token in case the template needs +# {% csrf_token %}. @requires_csrf_token def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME): """ Permission denied (403) handler. Templates: :template:`403.html` - Context: - exception - The message from the exception which triggered the 403 (if one was - supplied). + Context: None If the template does not exist, an Http403 response containing the text "403 Forbidden" (as per RFC 7231) will be returned. @@ -146,9 +140,9 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME) # Reraise if it's a missing custom template. raise return HttpResponseForbidden( - ERROR_PAGE_TEMPLATE % {"title": "403 Forbidden", "details": ""}, - content_type="text/html", + ERROR_PAGE_TEMPLATE % {'title': '403 Forbidden', 'details': ''}, + content_type='text/html', ) return HttpResponseForbidden( - template.render(request=request, context={"exception": str(exception)}) + template.render(request=request, context={'exception': str(exception)}) ) diff --git a/venv/Lib/site-packages/django/views/generic/__init__.py b/venv/Lib/site-packages/django/views/generic/__init__.py index 8514bae..bc32403 100644 --- a/venv/Lib/site-packages/django/views/generic/__init__.py +++ b/venv/Lib/site-packages/django/views/generic/__init__.py @@ -1,39 +1,22 @@ from django.views.generic.base import RedirectView, TemplateView, View from django.views.generic.dates import ( - ArchiveIndexView, - DateDetailView, - DayArchiveView, - MonthArchiveView, - TodayArchiveView, - WeekArchiveView, - YearArchiveView, + ArchiveIndexView, DateDetailView, DayArchiveView, MonthArchiveView, + TodayArchiveView, WeekArchiveView, YearArchiveView, ) from django.views.generic.detail import DetailView -from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView +from django.views.generic.edit import ( + CreateView, DeleteView, FormView, UpdateView, +) from django.views.generic.list import ListView __all__ = [ - "View", - "TemplateView", - "RedirectView", - "ArchiveIndexView", - "YearArchiveView", - "MonthArchiveView", - "WeekArchiveView", - "DayArchiveView", - "TodayArchiveView", - "DateDetailView", - "DetailView", - "FormView", - "CreateView", - "UpdateView", - "DeleteView", - "ListView", - "GenericViewError", + 'View', 'TemplateView', 'RedirectView', 'ArchiveIndexView', + 'YearArchiveView', 'MonthArchiveView', 'WeekArchiveView', 'DayArchiveView', + 'TodayArchiveView', 'DateDetailView', 'DetailView', 'FormView', + 'CreateView', 'UpdateView', 'DeleteView', 'ListView', 'GenericViewError', ] class GenericViewError(Exception): """A problem in a generic view.""" - pass diff --git a/venv/Lib/site-packages/django/views/generic/base.py b/venv/Lib/site-packages/django/views/generic/base.py index d45b176..ab800eb 100644 --- a/venv/Lib/site-packages/django/views/generic/base.py +++ b/venv/Lib/site-packages/django/views/generic/base.py @@ -1,18 +1,16 @@ import logging +from functools import update_wrapper from django.core.exceptions import ImproperlyConfigured from django.http import ( - HttpResponse, - HttpResponseGone, - HttpResponseNotAllowed, - HttpResponsePermanentRedirect, - HttpResponseRedirect, + HttpResponse, HttpResponseGone, HttpResponseNotAllowed, + HttpResponsePermanentRedirect, HttpResponseRedirect, ) from django.template.response import TemplateResponse from django.urls import reverse from django.utils.decorators import classonlymethod -logger = logging.getLogger("django.request") +logger = logging.getLogger('django.request') class ContextMixin: @@ -20,11 +18,10 @@ class ContextMixin: A default context mixin that passes the keyword arguments received by get_context_data() as the template context. """ - extra_context = None def get_context_data(self, **kwargs): - kwargs.setdefault("view", self) + kwargs.setdefault('view', self) if self.extra_context is not None: kwargs.update(self.extra_context) return kwargs @@ -36,16 +33,7 @@ class View: dispatch-by-method and simple sanity checking. """ - http_method_names = [ - "get", - "post", - "put", - "patch", - "delete", - "head", - "options", - "trace", - ] + http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): """ @@ -63,44 +51,37 @@ class View: for key in initkwargs: if key in cls.http_method_names: raise TypeError( - "The method name %s is not accepted as a keyword argument " - "to %s()." % (key, cls.__name__) + 'The method name %s is not accepted as a keyword argument ' + 'to %s().' % (key, cls.__name__) ) if not hasattr(cls, key): - raise TypeError( - "%s() received an invalid keyword %r. as_view " - "only accepts arguments that are already " - "attributes of the class." % (cls.__name__, key) - ) + raise TypeError("%s() received an invalid keyword %r. as_view " + "only accepts arguments that are already " + "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) self.setup(request, *args, **kwargs) - if not hasattr(self, "request"): + if not hasattr(self, 'request'): raise AttributeError( "%s instance has no 'request' attribute. Did you override " "setup() and forget to call super()?" % cls.__name__ ) return self.dispatch(request, *args, **kwargs) - view.view_class = cls view.view_initkwargs = initkwargs - # __name__ and __qualname__ are intentionally left unchanged as - # view_class should be used to robustly determine the name of the view - # instead. - view.__doc__ = cls.__doc__ - view.__module__ = cls.__module__ - view.__annotations__ = cls.dispatch.__annotations__ - # Copy possible attributes set by decorators, e.g. @csrf_exempt, from - # the dispatch method. - view.__dict__.update(cls.dispatch.__dict__) + # take name and docstring from class + update_wrapper(view, cls, updated=()) + # and possible attributes set by decorators + # like csrf_exempt from dispatch + update_wrapper(view, cls.dispatch, assigned=()) return view def setup(self, request, *args, **kwargs): """Initialize attributes shared by all view methods.""" - if hasattr(self, "get") and not hasattr(self, "head"): + if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args @@ -111,27 +92,23 @@ class View: # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: - handler = getattr( - self, request.method.lower(), self.http_method_not_allowed - ) + handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) def http_method_not_allowed(self, request, *args, **kwargs): logger.warning( - "Method Not Allowed (%s): %s", - request.method, - request.path, - extra={"status_code": 405, "request": request}, + 'Method Not Allowed (%s): %s', request.method, request.path, + extra={'status_code': 405, 'request': request} ) return HttpResponseNotAllowed(self._allowed_methods()) def options(self, request, *args, **kwargs): """Handle responding to requests for the OPTIONS HTTP verb.""" response = HttpResponse() - response.headers["Allow"] = ", ".join(self._allowed_methods()) - response.headers["Content-Length"] = "0" + response.headers['Allow'] = ', '.join(self._allowed_methods()) + response.headers['Content-Length'] = '0' return response def _allowed_methods(self): @@ -140,7 +117,6 @@ class View: class TemplateResponseMixin: """A mixin that can be used to render a template.""" - template_name = None template_engine = None response_class = TemplateResponse @@ -153,13 +129,13 @@ class TemplateResponseMixin: Pass response_kwargs to the constructor of the response class. """ - response_kwargs.setdefault("content_type", self.content_type) + response_kwargs.setdefault('content_type', self.content_type) return self.response_class( request=self.request, template=self.get_template_names(), context=context, using=self.template_engine, - **response_kwargs, + **response_kwargs ) def get_template_names(self): @@ -170,8 +146,7 @@ class TemplateResponseMixin: if self.template_name is None: raise ImproperlyConfigured( "TemplateResponseMixin requires either a definition of " - "'template_name' or an implementation of 'get_template_names()'" - ) + "'template_name' or an implementation of 'get_template_names()'") else: return [self.template_name] @@ -180,7 +155,6 @@ class TemplateView(TemplateResponseMixin, ContextMixin, View): """ Render a template. Pass keyword arguments from the URLconf to the context. """ - def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) return self.render_to_response(context) @@ -188,7 +162,6 @@ class TemplateView(TemplateResponseMixin, ContextMixin, View): class RedirectView(View): """Provide a redirect on any GET request.""" - permanent = False url = None pattern_name = None @@ -207,7 +180,7 @@ class RedirectView(View): else: return None - args = self.request.META.get("QUERY_STRING", "") + args = self.request.META.get('QUERY_STRING', '') if args and self.query_string: url = "%s?%s" % (url, args) return url @@ -221,7 +194,8 @@ class RedirectView(View): return HttpResponseRedirect(url) else: logger.warning( - "Gone: %s", request.path, extra={"status_code": 410, "request": request} + 'Gone: %s', request.path, + extra={'status_code': 410, 'request': request} ) return HttpResponseGone() diff --git a/venv/Lib/site-packages/django/views/generic/dates.py b/venv/Lib/site-packages/django/views/generic/dates.py index d2b776c..63151dd 100644 --- a/venv/Lib/site-packages/django/views/generic/dates.py +++ b/venv/Lib/site-packages/django/views/generic/dates.py @@ -9,19 +9,16 @@ from django.utils.functional import cached_property from django.utils.translation import gettext as _ from django.views.generic.base import View from django.views.generic.detail import ( - BaseDetailView, - SingleObjectTemplateResponseMixin, + BaseDetailView, SingleObjectTemplateResponseMixin, ) from django.views.generic.list import ( - MultipleObjectMixin, - MultipleObjectTemplateResponseMixin, + MultipleObjectMixin, MultipleObjectTemplateResponseMixin, ) class YearMixin: """Mixin for views manipulating year-based data.""" - - year_format = "%Y" + year_format = '%Y' year = None def get_year_format(self): @@ -36,21 +33,21 @@ class YearMixin: year = self.year if year is None: try: - year = self.kwargs["year"] + year = self.kwargs['year'] except KeyError: try: - year = self.request.GET["year"] + year = self.request.GET['year'] except KeyError: raise Http404(_("No year specified")) return year def get_next_year(self, date): """Get the next valid year.""" - return _get_next_prev(self, date, is_previous=False, period="year") + return _get_next_prev(self, date, is_previous=False, period='year') def get_previous_year(self, date): """Get the previous valid year.""" - return _get_next_prev(self, date, is_previous=True, period="year") + return _get_next_prev(self, date, is_previous=True, period='year') def _get_next_year(self, date): """ @@ -70,8 +67,7 @@ class YearMixin: class MonthMixin: """Mixin for views manipulating month-based data.""" - - month_format = "%b" + month_format = '%b' month = None def get_month_format(self): @@ -86,21 +82,21 @@ class MonthMixin: month = self.month if month is None: try: - month = self.kwargs["month"] + month = self.kwargs['month'] except KeyError: try: - month = self.request.GET["month"] + month = self.request.GET['month'] except KeyError: raise Http404(_("No month specified")) return month def get_next_month(self, date): """Get the next valid month.""" - return _get_next_prev(self, date, is_previous=False, period="month") + return _get_next_prev(self, date, is_previous=False, period='month') def get_previous_month(self, date): """Get the previous valid month.""" - return _get_next_prev(self, date, is_previous=True, period="month") + return _get_next_prev(self, date, is_previous=True, period='month') def _get_next_month(self, date): """ @@ -123,8 +119,7 @@ class MonthMixin: class DayMixin: """Mixin for views manipulating day-based data.""" - - day_format = "%d" + day_format = '%d' day = None def get_day_format(self): @@ -139,21 +134,21 @@ class DayMixin: day = self.day if day is None: try: - day = self.kwargs["day"] + day = self.kwargs['day'] except KeyError: try: - day = self.request.GET["day"] + day = self.request.GET['day'] except KeyError: raise Http404(_("No day specified")) return day def get_next_day(self, date): """Get the next valid day.""" - return _get_next_prev(self, date, is_previous=False, period="day") + return _get_next_prev(self, date, is_previous=False, period='day') def get_previous_day(self, date): """Get the previous valid day.""" - return _get_next_prev(self, date, is_previous=True, period="day") + return _get_next_prev(self, date, is_previous=True, period='day') def _get_next_day(self, date): """ @@ -170,8 +165,7 @@ class DayMixin: class WeekMixin: """Mixin for views manipulating week-based data.""" - - week_format = "%U" + week_format = '%U' week = None def get_week_format(self): @@ -186,21 +180,21 @@ class WeekMixin: week = self.week if week is None: try: - week = self.kwargs["week"] + week = self.kwargs['week'] except KeyError: try: - week = self.request.GET["week"] + week = self.request.GET['week'] except KeyError: raise Http404(_("No week specified")) return week def get_next_week(self, date): """Get the next valid week.""" - return _get_next_prev(self, date, is_previous=False, period="week") + return _get_next_prev(self, date, is_previous=False, period='week') def get_previous_week(self, date): """Get the previous valid week.""" - return _get_next_prev(self, date, is_previous=True, period="week") + return _get_next_prev(self, date, is_previous=True, period='week') def _get_next_week(self, date): """ @@ -224,9 +218,9 @@ class WeekMixin: The first day according to the week format is 0 and the last day is 6. """ week_format = self.get_week_format() - if week_format in {"%W", "%V"}: # week starts on Monday + if week_format in {'%W', '%V'}: # week starts on Monday return date.weekday() - elif week_format == "%U": # week starts on Sunday + elif week_format == '%U': # week starts on Sunday return (date.weekday() + 1) % 7 else: raise ValueError("unknown week format: %s" % week_format) @@ -234,16 +228,13 @@ class WeekMixin: class DateMixin: """Mixin class for views manipulating date-based data.""" - date_field = None allow_future = False def get_date_field(self): """Get the name of the date field to be used to filter by.""" if self.date_field is None: - raise ImproperlyConfigured( - "%s.date_field is required." % self.__class__.__name__ - ) + raise ImproperlyConfigured("%s.date_field is required." % self.__class__.__name__) return self.date_field def get_allow_future(self): @@ -291,8 +282,8 @@ class DateMixin: since = self._make_date_lookup_arg(date) until = self._make_date_lookup_arg(date + datetime.timedelta(days=1)) return { - "%s__gte" % date_field: since, - "%s__lt" % date_field: until, + '%s__gte' % date_field: since, + '%s__lt' % date_field: until, } else: # Skip self._make_date_lookup_arg, it's a no-op in this branch. @@ -301,29 +292,28 @@ class DateMixin: class BaseDateListView(MultipleObjectMixin, DateMixin, View): """Abstract base class for date-based views displaying a list of objects.""" - allow_empty = False - date_list_period = "year" + date_list_period = 'year' def get(self, request, *args, **kwargs): self.date_list, self.object_list, extra_context = self.get_dated_items() context = self.get_context_data( - object_list=self.object_list, date_list=self.date_list, **extra_context + object_list=self.object_list, + date_list=self.date_list, + **extra_context ) return self.render_to_response(context) def get_dated_items(self): """Obtain the list of dates and items.""" - raise NotImplementedError( - "A DateView must provide an implementation of get_dated_items()" - ) + raise NotImplementedError('A DateView must provide an implementation of get_dated_items()') def get_ordering(self): """ Return the field or fields to use for ordering the queryset; use the date field by default. """ - return "-%s" % self.get_date_field() if self.ordering is None else self.ordering + return '-%s' % self.get_date_field() if self.ordering is None else self.ordering def get_dated_queryset(self, **lookup): """ @@ -338,19 +328,16 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View): if not allow_future: now = timezone.now() if self.uses_datetime_field else timezone_today() - qs = qs.filter(**{"%s__lte" % date_field: now}) + qs = qs.filter(**{'%s__lte' % date_field: now}) if not allow_empty: # When pagination is enabled, it's better to do a cheap query # than to load the unpaginated queryset in memory. is_empty = not qs if paginate_by is None else not qs.exists() if is_empty: - raise Http404( - _("No %(verbose_name_plural)s available") - % { - "verbose_name_plural": qs.model._meta.verbose_name_plural, - } - ) + raise Http404(_("No %(verbose_name_plural)s available") % { + 'verbose_name_plural': qs.model._meta.verbose_name_plural, + }) return qs @@ -361,7 +348,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View): """ return self.date_list_period - def get_date_list(self, queryset, date_type=None, ordering="ASC"): + def get_date_list(self, queryset, date_type=None, ordering='ASC'): """ Get a date list by calling `queryset.dates/datetimes()`, checking along the way for empty lists that aren't allowed. @@ -377,9 +364,8 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View): date_list = queryset.dates(date_field, date_type, ordering) if date_list is not None and not date_list and not allow_empty: raise Http404( - _("No %(verbose_name_plural)s available") - % { - "verbose_name_plural": queryset.model._meta.verbose_name_plural, + _("No %(verbose_name_plural)s available") % { + 'verbose_name_plural': queryset.model._meta.verbose_name_plural, } ) @@ -390,13 +376,12 @@ class BaseArchiveIndexView(BaseDateListView): """ Base class for archives of date-based items. Requires a response mixin. """ - - context_object_name = "latest" + context_object_name = 'latest' def get_dated_items(self): """Return (date_list, items, extra_context) for this request.""" qs = self.get_dated_queryset() - date_list = self.get_date_list(qs, ordering="DESC") + date_list = self.get_date_list(qs, ordering='DESC') if not date_list: qs = qs.none() @@ -406,14 +391,12 @@ class BaseArchiveIndexView(BaseDateListView): class ArchiveIndexView(MultipleObjectTemplateResponseMixin, BaseArchiveIndexView): """Top-level archive of date-based items.""" - - template_name_suffix = "_archive" + template_name_suffix = '_archive' class BaseYearArchiveView(YearMixin, BaseDateListView): """List of objects published in a given year.""" - - date_list_period = "month" + date_list_period = 'month' make_object_list = False def get_dated_items(self): @@ -426,8 +409,8 @@ class BaseYearArchiveView(YearMixin, BaseDateListView): since = self._make_date_lookup_arg(date) until = self._make_date_lookup_arg(self._get_next_year(date)) lookup_kwargs = { - "%s__gte" % date_field: since, - "%s__lt" % date_field: until, + '%s__gte' % date_field: since, + '%s__lt' % date_field: until, } qs = self.get_dated_queryset(**lookup_kwargs) @@ -438,15 +421,11 @@ class BaseYearArchiveView(YearMixin, BaseDateListView): # to find information about the model. qs = qs.none() - return ( - date_list, - qs, - { - "year": date, - "next_year": self.get_next_year(date), - "previous_year": self.get_previous_year(date), - }, - ) + return (date_list, qs, { + 'year': date, + 'next_year': self.get_next_year(date), + 'previous_year': self.get_previous_year(date), + }) def get_make_object_list(self): """ @@ -458,14 +437,12 @@ class BaseYearArchiveView(YearMixin, BaseDateListView): class YearArchiveView(MultipleObjectTemplateResponseMixin, BaseYearArchiveView): """List of objects published in a given year.""" - - template_name_suffix = "_archive_year" + template_name_suffix = '_archive_year' class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): """List of objects published in a given month.""" - - date_list_period = "day" + date_list_period = 'day' def get_dated_items(self): """Return (date_list, items, extra_context) for this request.""" @@ -473,35 +450,29 @@ class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): month = self.get_month() date_field = self.get_date_field() - date = _date_from_string( - year, self.get_year_format(), month, self.get_month_format() - ) + date = _date_from_string(year, self.get_year_format(), + month, self.get_month_format()) since = self._make_date_lookup_arg(date) until = self._make_date_lookup_arg(self._get_next_month(date)) lookup_kwargs = { - "%s__gte" % date_field: since, - "%s__lt" % date_field: until, + '%s__gte' % date_field: since, + '%s__lt' % date_field: until, } qs = self.get_dated_queryset(**lookup_kwargs) date_list = self.get_date_list(qs) - return ( - date_list, - qs, - { - "month": date, - "next_month": self.get_next_month(date), - "previous_month": self.get_previous_month(date), - }, - ) + return (date_list, qs, { + 'month': date, + 'next_month': self.get_next_month(date), + 'previous_month': self.get_previous_month(date), + }) class MonthArchiveView(MultipleObjectTemplateResponseMixin, BaseMonthArchiveView): """List of objects published in a given month.""" - - template_name_suffix = "_archive_month" + template_name_suffix = '_archive_month' class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): @@ -514,71 +485,55 @@ class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): date_field = self.get_date_field() week_format = self.get_week_format() - week_choices = {"%W": "1", "%U": "0", "%V": "1"} + week_choices = {'%W': '1', '%U': '0', '%V': '1'} try: week_start = week_choices[week_format] except KeyError: - raise ValueError( - "Unknown week format %r. Choices are: %s" - % ( - week_format, - ", ".join(sorted(week_choices)), - ) - ) + raise ValueError('Unknown week format %r. Choices are: %s' % ( + week_format, + ', '.join(sorted(week_choices)), + )) year_format = self.get_year_format() - if week_format == "%V" and year_format != "%G": + if week_format == '%V' and year_format != '%G': raise ValueError( "ISO week directive '%s' is incompatible with the year " - "directive '%s'. Use the ISO year '%%G' instead." - % ( - week_format, - year_format, + "directive '%s'. Use the ISO year '%%G' instead." % ( + week_format, year_format, ) ) - date = _date_from_string(year, year_format, week_start, "%w", week, week_format) + date = _date_from_string(year, year_format, week_start, '%w', week, week_format) since = self._make_date_lookup_arg(date) until = self._make_date_lookup_arg(self._get_next_week(date)) lookup_kwargs = { - "%s__gte" % date_field: since, - "%s__lt" % date_field: until, + '%s__gte' % date_field: since, + '%s__lt' % date_field: until, } qs = self.get_dated_queryset(**lookup_kwargs) - return ( - None, - qs, - { - "week": date, - "next_week": self.get_next_week(date), - "previous_week": self.get_previous_week(date), - }, - ) + return (None, qs, { + 'week': date, + 'next_week': self.get_next_week(date), + 'previous_week': self.get_previous_week(date), + }) class WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView): """List of objects published in a given week.""" - - template_name_suffix = "_archive_week" + template_name_suffix = '_archive_week' class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): """List of objects published on a given day.""" - def get_dated_items(self): """Return (date_list, items, extra_context) for this request.""" year = self.get_year() month = self.get_month() day = self.get_day() - date = _date_from_string( - year, - self.get_year_format(), - month, - self.get_month_format(), - day, - self.get_day_format(), - ) + date = _date_from_string(year, self.get_year_format(), + month, self.get_month_format(), + day, self.get_day_format()) return self._get_dated_items(date) @@ -590,22 +545,17 @@ class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): lookup_kwargs = self._make_single_date_lookup(date) qs = self.get_dated_queryset(**lookup_kwargs) - return ( - None, - qs, - { - "day": date, - "previous_day": self.get_previous_day(date), - "next_day": self.get_next_day(date), - "previous_month": self.get_previous_month(date), - "next_month": self.get_next_month(date), - }, - ) + return (None, qs, { + 'day': date, + 'previous_day': self.get_previous_day(date), + 'next_day': self.get_next_day(date), + 'previous_month': self.get_previous_month(date), + 'next_month': self.get_next_month(date) + }) class DayArchiveView(MultipleObjectTemplateResponseMixin, BaseDayArchiveView): """List of objects published on a given day.""" - template_name_suffix = "_archive_day" @@ -619,7 +569,6 @@ class BaseTodayArchiveView(BaseDayArchiveView): class TodayArchiveView(MultipleObjectTemplateResponseMixin, BaseTodayArchiveView): """List of objects published today.""" - template_name_suffix = "_archive_day" @@ -628,35 +577,26 @@ class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailV Detail view of a single object on a single date; this differs from the standard DetailView by accepting a year/month/day in the URL. """ - def get_object(self, queryset=None): """Get the object this request displays.""" year = self.get_year() month = self.get_month() day = self.get_day() - date = _date_from_string( - year, - self.get_year_format(), - month, - self.get_month_format(), - day, - self.get_day_format(), - ) + date = _date_from_string(year, self.get_year_format(), + month, self.get_month_format(), + day, self.get_day_format()) # Use a custom queryset if provided qs = self.get_queryset() if queryset is None else queryset if not self.get_allow_future() and date > datetime.date.today(): - raise Http404( - _( - "Future %(verbose_name_plural)s not available because " - "%(class_name)s.allow_future is False." - ) - % { - "verbose_name_plural": qs.model._meta.verbose_name_plural, - "class_name": self.__class__.__name__, - } - ) + raise Http404(_( + "Future %(verbose_name_plural)s not available because " + "%(class_name)s.allow_future is False." + ) % { + 'verbose_name_plural': qs.model._meta.verbose_name_plural, + 'class_name': self.__class__.__name__, + }) # Filter down a queryset from self.queryset using the date from the # URL. This'll get passed as the queryset to DetailView.get_object, @@ -672,13 +612,10 @@ class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView): Detail view of a single object on a single date; this differs from the standard DetailView by accepting a year/month/day in the URL. """ - - template_name_suffix = "_detail" + template_name_suffix = '_detail' -def _date_from_string( - year, year_format, month="", month_format="", day="", day_format="", delim="__" -): +def _date_from_string(year, year_format, month='', month_format='', day='', day_format='', delim='__'): """ Get a datetime.date object given a format string and a year, month, and day (only year is mandatory). Raise a 404 for an invalid date. @@ -688,13 +625,10 @@ def _date_from_string( try: return datetime.datetime.strptime(datestr, format).date() except ValueError: - raise Http404( - _("Invalid date string “%(datestr)s” given format “%(format)s”") - % { - "datestr": datestr, - "format": format, - } - ) + raise Http404(_('Invalid date string “%(datestr)s” given format “%(format)s”') % { + 'datestr': datestr, + 'format': format, + }) def _get_next_prev(generic_view, date, is_previous, period): @@ -727,8 +661,8 @@ def _get_next_prev(generic_view, date, is_previous, period): allow_empty = generic_view.get_allow_empty() allow_future = generic_view.get_allow_future() - get_current = getattr(generic_view, "_get_current_%s" % period) - get_next = getattr(generic_view, "_get_next_%s" % period) + get_current = getattr(generic_view, '_get_current_%s' % period) + get_next = getattr(generic_view, '_get_next_%s' % period) # Bounds of the current interval start, end = get_current(date), get_next(date) @@ -752,10 +686,10 @@ def _get_next_prev(generic_view, date, is_previous, period): # Construct a lookup and an ordering depending on whether we're doing # a previous date or a next date lookup. if is_previous: - lookup = {"%s__lt" % date_field: generic_view._make_date_lookup_arg(start)} - ordering = "-%s" % date_field + lookup = {'%s__lt' % date_field: generic_view._make_date_lookup_arg(start)} + ordering = '-%s' % date_field else: - lookup = {"%s__gte" % date_field: generic_view._make_date_lookup_arg(end)} + lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(end)} ordering = date_field # Filter out objects in the future if appropriate. @@ -766,7 +700,7 @@ def _get_next_prev(generic_view, date, is_previous, period): now = timezone.now() else: now = timezone_today() - lookup["%s__lte" % date_field] = now + lookup['%s__lte' % date_field] = now qs = generic_view.get_queryset().filter(**lookup).order_by(ordering) diff --git a/venv/Lib/site-packages/django/views/generic/detail.py b/venv/Lib/site-packages/django/views/generic/detail.py index e4428c8..1922114 100644 --- a/venv/Lib/site-packages/django/views/generic/detail.py +++ b/venv/Lib/site-packages/django/views/generic/detail.py @@ -9,13 +9,12 @@ class SingleObjectMixin(ContextMixin): """ Provide the ability to retrieve a single object for further manipulation. """ - model = None queryset = None - slug_field = "slug" + slug_field = 'slug' context_object_name = None - slug_url_kwarg = "slug" - pk_url_kwarg = "pk" + slug_url_kwarg = 'slug' + pk_url_kwarg = 'pk' query_pk_and_slug = False def get_object(self, queryset=None): @@ -52,10 +51,8 @@ class SingleObjectMixin(ContextMixin): # Get the single item from the filtered queryset obj = queryset.get() except queryset.model.DoesNotExist: - raise Http404( - _("No %(verbose_name)s found matching the query") - % {"verbose_name": queryset.model._meta.verbose_name} - ) + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) return obj def get_queryset(self): @@ -72,7 +69,9 @@ class SingleObjectMixin(ContextMixin): raise ImproperlyConfigured( "%(cls)s is missing a QuerySet. Define " "%(cls)s.model, %(cls)s.queryset, or override " - "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__} + "%(cls)s.get_queryset()." % { + 'cls': self.__class__.__name__ + } ) return self.queryset.all() @@ -93,7 +92,7 @@ class SingleObjectMixin(ContextMixin): """Insert the single object into the context dict.""" context = {} if self.object: - context["object"] = self.object + context['object'] = self.object context_object_name = self.get_context_object_name(self.object) if context_object_name: context[context_object_name] = self.object @@ -103,7 +102,6 @@ class SingleObjectMixin(ContextMixin): class BaseDetailView(SingleObjectMixin, View): """A base view for displaying a single object.""" - def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) @@ -112,7 +110,7 @@ class BaseDetailView(SingleObjectMixin, View): class SingleObjectTemplateResponseMixin(TemplateResponseMixin): template_name_field = None - template_name_suffix = "_detail" + template_name_suffix = '_detail' def get_template_names(self): """ @@ -143,25 +141,17 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin): # only use this if the object in question is a model. if isinstance(self.object, models.Model): object_meta = self.object._meta - names.append( - "%s/%s%s.html" - % ( - object_meta.app_label, - object_meta.model_name, - self.template_name_suffix, - ) - ) - elif getattr(self, "model", None) is not None and issubclass( - self.model, models.Model - ): - names.append( - "%s/%s%s.html" - % ( - self.model._meta.app_label, - self.model._meta.model_name, - self.template_name_suffix, - ) - ) + names.append("%s/%s%s.html" % ( + object_meta.app_label, + object_meta.model_name, + self.template_name_suffix + )) + elif getattr(self, 'model', None) is not None and issubclass(self.model, models.Model): + names.append("%s/%s%s.html" % ( + self.model._meta.app_label, + self.model._meta.model_name, + self.template_name_suffix + )) # If we still haven't managed to find any template names, we should # re-raise the ImproperlyConfigured to alert the user. diff --git a/venv/Lib/site-packages/django/views/generic/edit.py b/venv/Lib/site-packages/django/views/generic/edit.py index e1f37bc..ccfef9c 100644 --- a/venv/Lib/site-packages/django/views/generic/edit.py +++ b/venv/Lib/site-packages/django/views/generic/edit.py @@ -1,20 +1,14 @@ -import warnings - from django.core.exceptions import ImproperlyConfigured -from django.forms import Form from django.forms import models as model_forms from django.http import HttpResponseRedirect from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.detail import ( - BaseDetailView, - SingleObjectMixin, - SingleObjectTemplateResponseMixin, + BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin, ) class FormMixin(ContextMixin): """Provide a way to show and handle a form in a request.""" - initial = {} form_class = None success_url = None @@ -41,17 +35,15 @@ class FormMixin(ContextMixin): def get_form_kwargs(self): """Return the keyword arguments for instantiating the form.""" kwargs = { - "initial": self.get_initial(), - "prefix": self.get_prefix(), + 'initial': self.get_initial(), + 'prefix': self.get_prefix(), } - if self.request.method in ("POST", "PUT"): - kwargs.update( - { - "data": self.request.POST, - "files": self.request.FILES, - } - ) + if self.request.method in ('POST', 'PUT'): + kwargs.update({ + 'data': self.request.POST, + 'files': self.request.FILES, + }) return kwargs def get_success_url(self): @@ -70,14 +62,13 @@ class FormMixin(ContextMixin): def get_context_data(self, **kwargs): """Insert the form into the context dict.""" - if "form" not in kwargs: - kwargs["form"] = self.get_form() + if 'form' not in kwargs: + kwargs['form'] = self.get_form() return super().get_context_data(**kwargs) class ModelFormMixin(FormMixin, SingleObjectMixin): """Provide a way to show and handle a ModelForm in a request.""" - fields = None def get_form_class(self): @@ -92,7 +83,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): if self.model is not None: # If a model has been explicitly provided, use it model = self.model - elif getattr(self, "object", None) is not None: + elif getattr(self, 'object', None) is not None: # If this view is operating on a single object, use # the class of that object model = self.object.__class__ @@ -112,8 +103,8 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): def get_form_kwargs(self): """Return the keyword arguments for instantiating the form.""" kwargs = super().get_form_kwargs() - if hasattr(self, "object"): - kwargs.update({"instance": self.object}) + if hasattr(self, 'object'): + kwargs.update({'instance': self.object}) return kwargs def get_success_url(self): @@ -126,8 +117,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): except AttributeError: raise ImproperlyConfigured( "No URL to redirect to. Either provide a url or define" - " a get_absolute_url method on the Model." - ) + " a get_absolute_url method on the Model.") return url def form_valid(self, form): @@ -138,7 +128,6 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): class ProcessFormView(View): """Render a form on GET and processes it on POST.""" - def get(self, request, *args, **kwargs): """Handle GET requests: instantiate a blank version of the form.""" return self.render_to_response(self.get_context_data()) @@ -174,7 +163,6 @@ class BaseCreateView(ModelFormMixin, ProcessFormView): Using this base class requires subclassing to provide a response mixin. """ - def get(self, request, *args, **kwargs): self.object = None return super().get(request, *args, **kwargs) @@ -188,8 +176,7 @@ class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): """ View for creating a new object, with a response rendered by a template. """ - - template_name_suffix = "_form" + template_name_suffix = '_form' class BaseUpdateView(ModelFormMixin, ProcessFormView): @@ -198,7 +185,6 @@ class BaseUpdateView(ModelFormMixin, ProcessFormView): Using this base class requires subclassing to provide a response mixin. """ - def get(self, request, *args, **kwargs): self.object = self.get_object() return super().get(request, *args, **kwargs) @@ -210,13 +196,11 @@ class BaseUpdateView(ModelFormMixin, ProcessFormView): class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView): """View for updating an object, with a response rendered by a template.""" - - template_name_suffix = "_form" + template_name_suffix = '_form' class DeletionMixin: """Provide the ability to delete objects.""" - success_url = None def delete(self, request, *args, **kwargs): @@ -237,58 +221,21 @@ class DeletionMixin: if self.success_url: return self.success_url.format(**self.object.__dict__) else: - raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") + raise ImproperlyConfigured( + "No URL to redirect to. Provide a success_url.") -# RemovedInDjango50Warning. -class DeleteViewCustomDeleteWarning(Warning): - pass - - -class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView): +class BaseDeleteView(DeletionMixin, BaseDetailView): """ Base view for deleting an object. Using this base class requires subclassing to provide a response mixin. """ - form_class = Form - - def __init__(self, *args, **kwargs): - # RemovedInDjango50Warning. - if self.__class__.delete is not DeletionMixin.delete: - warnings.warn( - f"DeleteView uses FormMixin to handle POST requests. As a " - f"consequence, any custom deletion logic in " - f"{self.__class__.__name__}.delete() handler should be moved " - f"to form_valid().", - DeleteViewCustomDeleteWarning, - stacklevel=2, - ) - super().__init__(*args, **kwargs) - - def post(self, request, *args, **kwargs): - # Set self.object before the usual form processing flow. - # Inlined because having DeletionMixin as the first base, for - # get_success_url(), makes leveraging super() with ProcessFormView - # overly complex. - self.object = self.get_object() - form = self.get_form() - if form.is_valid(): - return self.form_valid(form) - else: - return self.form_invalid(form) - - def form_valid(self, form): - success_url = self.get_success_url() - self.object.delete() - return HttpResponseRedirect(success_url) - class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView): """ View for deleting an object retrieved with self.get_object(), with a response rendered by a template. """ - - template_name_suffix = "_confirm_delete" + template_name_suffix = '_confirm_delete' diff --git a/venv/Lib/site-packages/django/views/generic/list.py b/venv/Lib/site-packages/django/views/generic/list.py index 830a8df..65bae2b 100644 --- a/venv/Lib/site-packages/django/views/generic/list.py +++ b/venv/Lib/site-packages/django/views/generic/list.py @@ -8,7 +8,6 @@ from django.views.generic.base import ContextMixin, TemplateResponseMixin, View class MultipleObjectMixin(ContextMixin): """A mixin for views manipulating multiple objects.""" - allow_empty = True queryset = None model = None @@ -16,7 +15,7 @@ class MultipleObjectMixin(ContextMixin): paginate_orphans = 0 context_object_name = None paginator_class = Paginator - page_kwarg = "page" + page_kwarg = 'page' ordering = None def get_queryset(self): @@ -36,7 +35,9 @@ class MultipleObjectMixin(ContextMixin): raise ImproperlyConfigured( "%(cls)s is missing a QuerySet. Define " "%(cls)s.model, %(cls)s.queryset, or override " - "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__} + "%(cls)s.get_queryset()." % { + 'cls': self.__class__.__name__ + } ) ordering = self.get_ordering() if ordering: @@ -53,30 +54,25 @@ class MultipleObjectMixin(ContextMixin): def paginate_queryset(self, queryset, page_size): """Paginate the queryset, if needed.""" paginator = self.get_paginator( - queryset, - page_size, - orphans=self.get_paginate_orphans(), - allow_empty_first_page=self.get_allow_empty(), - ) + queryset, page_size, orphans=self.get_paginate_orphans(), + allow_empty_first_page=self.get_allow_empty()) page_kwarg = self.page_kwarg page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1 try: page_number = int(page) except ValueError: - if page == "last": + if page == 'last': page_number = paginator.num_pages else: - raise Http404( - _("Page is not “last”, nor can it be converted to an int.") - ) + raise Http404(_('Page is not “last”, nor can it be converted to an int.')) try: page = paginator.page(page_number) return (paginator, page, page.object_list, page.has_other_pages()) except InvalidPage as e: - raise Http404( - _("Invalid page (%(page_number)s): %(message)s") - % {"page_number": page_number, "message": str(e)} - ) + raise Http404(_('Invalid page (%(page_number)s): %(message)s') % { + 'page_number': page_number, + 'message': str(e) + }) def get_paginate_by(self, queryset): """ @@ -84,17 +80,12 @@ class MultipleObjectMixin(ContextMixin): """ return self.paginate_by - def get_paginator( - self, queryset, per_page, orphans=0, allow_empty_first_page=True, **kwargs - ): + def get_paginator(self, queryset, per_page, orphans=0, + allow_empty_first_page=True, **kwargs): """Return an instance of the paginator for this view.""" return self.paginator_class( - queryset, - per_page, - orphans=orphans, - allow_empty_first_page=allow_empty_first_page, - **kwargs, - ) + queryset, per_page, orphans=orphans, + allow_empty_first_page=allow_empty_first_page, **kwargs) def get_paginate_orphans(self): """ @@ -114,8 +105,8 @@ class MultipleObjectMixin(ContextMixin): """Get the name of the item to be used in the context.""" if self.context_object_name: return self.context_object_name - elif hasattr(object_list, "model"): - return "%s_list" % object_list.model._meta.model_name + elif hasattr(object_list, 'model'): + return '%s_list' % object_list.model._meta.model_name else: return None @@ -125,21 +116,19 @@ class MultipleObjectMixin(ContextMixin): page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: - paginator, page, queryset, is_paginated = self.paginate_queryset( - queryset, page_size - ) + paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size) context = { - "paginator": paginator, - "page_obj": page, - "is_paginated": is_paginated, - "object_list": queryset, + 'paginator': paginator, + 'page_obj': page, + 'is_paginated': is_paginated, + 'object_list': queryset } else: context = { - "paginator": None, - "page_obj": None, - "is_paginated": False, - "object_list": queryset, + 'paginator': None, + 'page_obj': None, + 'is_paginated': False, + 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset @@ -149,7 +138,6 @@ class MultipleObjectMixin(ContextMixin): class BaseListView(MultipleObjectMixin, View): """A base view for displaying a list of objects.""" - def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() @@ -158,27 +146,21 @@ class BaseListView(MultipleObjectMixin, View): # When pagination is enabled and object_list is a queryset, # it's better to do a cheap query than to load the unpaginated # queryset in memory. - if self.get_paginate_by(self.object_list) is not None and hasattr( - self.object_list, "exists" - ): + if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'): is_empty = not self.object_list.exists() else: is_empty = not self.object_list if is_empty: - raise Http404( - _("Empty list and “%(class_name)s.allow_empty” is False.") - % { - "class_name": self.__class__.__name__, - } - ) + raise Http404(_('Empty list and “%(class_name)s.allow_empty” is False.') % { + 'class_name': self.__class__.__name__, + }) context = self.get_context_data() return self.render_to_response(context) class MultipleObjectTemplateResponseMixin(TemplateResponseMixin): """Mixin for responding with a template and list of objects.""" - - template_name_suffix = "_list" + template_name_suffix = '_list' def get_template_names(self): """ @@ -196,18 +178,14 @@ class MultipleObjectTemplateResponseMixin(TemplateResponseMixin): # app and model name. This name gets put at the end of the template # name list so that user-supplied names override the automatically- # generated ones. - if hasattr(self.object_list, "model"): + if hasattr(self.object_list, 'model'): opts = self.object_list.model._meta - names.append( - "%s/%s%s.html" - % (opts.app_label, opts.model_name, self.template_name_suffix) - ) + names.append("%s/%s%s.html" % (opts.app_label, opts.model_name, self.template_name_suffix)) elif not names: raise ImproperlyConfigured( "%(cls)s requires either a 'template_name' attribute " - "or a get_queryset() method that returns a QuerySet." - % { - "cls": self.__class__.__name__, + "or a get_queryset() method that returns a QuerySet." % { + 'cls': self.__class__.__name__, } ) return names diff --git a/venv/Lib/site-packages/django/views/i18n.py b/venv/Lib/site-packages/django/views/i18n.py index 2cf24d3..c1a5417 100644 --- a/venv/Lib/site-packages/django/views/i18n.py +++ b/venv/Lib/site-packages/django/views/i18n.py @@ -10,11 +10,13 @@ from django.template import Context, Engine from django.urls import translate_url from django.utils.formats import get_format from django.utils.http import url_has_allowed_host_and_scheme -from django.utils.translation import check_for_language, get_language +from django.utils.translation import ( + LANGUAGE_SESSION_KEY, check_for_language, get_language, +) from django.utils.translation.trans_real import DjangoTranslation from django.views.generic import View -LANGUAGE_QUERY_PARAMETER = "language" +LANGUAGE_QUERY_PARAMETER = 'language' def set_language(request): @@ -28,32 +30,36 @@ def set_language(request): redirect to the page in the request (the 'next' parameter) without changing any state. """ - next_url = request.POST.get("next", request.GET.get("next")) + next_url = request.POST.get('next', request.GET.get('next')) if ( - next_url or request.accepts("text/html") - ) and not url_has_allowed_host_and_scheme( - url=next_url, - allowed_hosts={request.get_host()}, - require_https=request.is_secure(), + (next_url or request.accepts('text/html')) and + not url_has_allowed_host_and_scheme( + url=next_url, + allowed_hosts={request.get_host()}, + require_https=request.is_secure(), + ) ): - next_url = request.META.get("HTTP_REFERER") + next_url = request.META.get('HTTP_REFERER') if not url_has_allowed_host_and_scheme( url=next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ): - next_url = "/" + next_url = '/' response = HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204) - if request.method == "POST": + if request.method == 'POST': lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER) if lang_code and check_for_language(lang_code): if next_url: next_trans = translate_url(next_url, lang_code) if next_trans != next_url: response = HttpResponseRedirect(next_trans) + if hasattr(request, 'session'): + # Storing the language in the session is deprecated. + # (RemovedInDjango40Warning) + request.session[LANGUAGE_SESSION_KEY] = lang_code response.set_cookie( - settings.LANGUAGE_COOKIE_NAME, - lang_code, + settings.LANGUAGE_COOKIE_NAME, lang_code, max_age=settings.LANGUAGE_COOKIE_AGE, path=settings.LANGUAGE_COOKIE_PATH, domain=settings.LANGUAGE_COOKIE_DOMAIN, @@ -67,20 +73,11 @@ def set_language(request): def get_formats(): """Return all formats strings required for i18n to work.""" FORMAT_SETTINGS = ( - "DATE_FORMAT", - "DATETIME_FORMAT", - "TIME_FORMAT", - "YEAR_MONTH_FORMAT", - "MONTH_DAY_FORMAT", - "SHORT_DATE_FORMAT", - "SHORT_DATETIME_FORMAT", - "FIRST_DAY_OF_WEEK", - "DECIMAL_SEPARATOR", - "THOUSAND_SEPARATOR", - "NUMBER_GROUPING", - "DATE_INPUT_FORMATS", - "TIME_INPUT_FORMATS", - "DATETIME_INPUT_FORMATS", + 'DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT', + 'YEAR_MONTH_FORMAT', 'MONTH_DAY_FORMAT', 'SHORT_DATE_FORMAT', + 'SHORT_DATETIME_FORMAT', 'FIRST_DAY_OF_WEEK', 'DECIMAL_SEPARATOR', + 'THOUSAND_SEPARATOR', 'NUMBER_GROUPING', + 'DATE_INPUT_FORMATS', 'TIME_INPUT_FORMATS', 'DATETIME_INPUT_FORMATS' ) return {attr: get_format(attr) for attr in FORMAT_SETTINGS} @@ -188,7 +185,7 @@ js_catalog_template = r""" } }; {% endautoescape %} -""" # NOQA +""" class JavaScriptCatalog(View): @@ -203,37 +200,31 @@ class JavaScriptCatalog(View): want to do that as JavaScript messages go to the djangojs domain. This might be needed if you deliver your JavaScript source from Django templates. """ - - domain = "djangojs" + domain = 'djangojs' packages = None def get(self, request, *args, **kwargs): locale = get_language() - domain = kwargs.get("domain", self.domain) + domain = kwargs.get('domain', self.domain) # If packages are not provided, default to all installed packages, as # DjangoTranslation without localedirs harvests them all. - packages = kwargs.get("packages", "") - packages = packages.split("+") if packages else self.packages + packages = kwargs.get('packages', '') + packages = packages.split('+') if packages else self.packages paths = self.get_paths(packages) if packages else None self.translation = DjangoTranslation(locale, domain=domain, localedirs=paths) context = self.get_context_data(**kwargs) return self.render_to_response(context) def get_paths(self, packages): - allowable_packages = { - app_config.name: app_config for app_config in apps.get_app_configs() - } - app_configs = [ - allowable_packages[p] for p in packages if p in allowable_packages - ] + allowable_packages = {app_config.name: app_config for app_config in apps.get_app_configs()} + app_configs = [allowable_packages[p] for p in packages if p in allowable_packages] if len(app_configs) < len(packages): excluded = [p for p in packages if p not in allowable_packages] raise ValueError( - "Invalid package(s) provided to JavaScriptCatalog: %s" - % ",".join(excluded) + 'Invalid package(s) provided to JavaScriptCatalog: %s' % ','.join(excluded) ) # paths of requested packages - return [os.path.join(app.path, "locale") for app in app_configs] + return [os.path.join(app.path, 'locale') for app in app_configs] @property def _num_plurals(self): @@ -241,7 +232,7 @@ class JavaScriptCatalog(View): Return the number of plurals for this catalog language, or 2 if no plural string is available. """ - match = re.search(r"nplurals=\s*(\d+)", self._plural_string or "") + match = re.search(r'nplurals=\s*(\d+)', self._plural_string or '') if match: return int(match[1]) return 2 @@ -252,10 +243,10 @@ class JavaScriptCatalog(View): Return the plural string (including nplurals) for this catalog language, or None if no plural string is available. """ - if "" in self.translation._catalog: - for line in self.translation._catalog[""].split("\n"): - if line.startswith("Plural-Forms:"): - return line.split(":", 1)[1].strip() + if '' in self.translation._catalog: + for line in self.translation._catalog[''].split('\n'): + if line.startswith('Plural-Forms:'): + return line.split(':', 1)[1].strip() return None def get_plural(self): @@ -264,11 +255,7 @@ class JavaScriptCatalog(View): # This should be a compiled function of a typical plural-form: # Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : # n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; - plural = [ - el.strip() - for el in plural.split(";") - if el.strip().startswith("plural=") - ][0].split("=", 1)[1] + plural = [el.strip() for el in plural.split(';') if el.strip().startswith('plural=')][0].split('=', 1)[1] return plural def get_catalog(self): @@ -276,14 +263,10 @@ class JavaScriptCatalog(View): num_plurals = self._num_plurals catalog = {} trans_cat = self.translation._catalog - trans_fallback_cat = ( - self.translation._fallback._catalog if self.translation._fallback else {} - ) + trans_fallback_cat = self.translation._fallback._catalog if self.translation._fallback else {} seen_keys = set() - for key, value in itertools.chain( - trans_cat.items(), trans_fallback_cat.items() - ): - if key == "" or key in seen_keys: + for key, value in itertools.chain(trans_cat.items(), trans_fallback_cat.items()): + if key == '' or key in seen_keys: continue if isinstance(key, str): catalog[key] = value @@ -294,33 +277,27 @@ class JavaScriptCatalog(View): raise TypeError(key) seen_keys.add(key) for k, v in pdict.items(): - catalog[k] = [v.get(i, "") for i in range(num_plurals)] + catalog[k] = [v.get(i, '') for i in range(num_plurals)] return catalog def get_context_data(self, **kwargs): return { - "catalog": self.get_catalog(), - "formats": get_formats(), - "plural": self.get_plural(), + 'catalog': self.get_catalog(), + 'formats': get_formats(), + 'plural': self.get_plural(), } def render_to_response(self, context, **response_kwargs): def indent(s): - return s.replace("\n", "\n ") + return s.replace('\n', '\n ') template = Engine().from_string(js_catalog_template) - context["catalog_str"] = ( - indent(json.dumps(context["catalog"], sort_keys=True, indent=2)) - if context["catalog"] - else None - ) - context["formats_str"] = indent( - json.dumps(context["formats"], sort_keys=True, indent=2) - ) + context['catalog_str'] = indent( + json.dumps(context['catalog'], sort_keys=True, indent=2) + ) if context['catalog'] else None + context['formats_str'] = indent(json.dumps(context['formats'], sort_keys=True, indent=2)) - return HttpResponse( - template.render(Context(context)), 'text/javascript; charset="utf-8"' - ) + return HttpResponse(template.render(Context(context)), 'text/javascript; charset="utf-8"') class JSONCatalog(JavaScriptCatalog): @@ -340,6 +317,5 @@ class JSONCatalog(JavaScriptCatalog): "plural": '...' # Expression for plural forms, or null. } """ - def render_to_response(self, context, **response_kwargs): return JsonResponse(context) diff --git a/venv/Lib/site-packages/django/views/static.py b/venv/Lib/site-packages/django/views/static.py index 1c558a5..1d4900b 100644 --- a/venv/Lib/site-packages/django/views/static.py +++ b/venv/Lib/site-packages/django/views/static.py @@ -7,12 +7,13 @@ import posixpath import re from pathlib import Path -from django.http import FileResponse, Http404, HttpResponse, HttpResponseNotModified +from django.http import ( + FileResponse, Http404, HttpResponse, HttpResponseNotModified, +) from django.template import Context, Engine, TemplateDoesNotExist, loader from django.utils._os import safe_join from django.utils.http import http_date, parse_http_date -from django.utils.translation import gettext as _ -from django.utils.translation import gettext_lazy +from django.utils.translation import gettext as _, gettext_lazy def serve(request, path, document_root=None, show_indexes=False): @@ -31,23 +32,22 @@ def serve(request, path, document_root=None, show_indexes=False): but if you'd like to override it, you can create a template called ``static/directory_index.html``. """ - path = posixpath.normpath(path).lstrip("/") + path = posixpath.normpath(path).lstrip('/') fullpath = Path(safe_join(document_root, path)) if fullpath.is_dir(): if show_indexes: return directory_index(path, fullpath) raise Http404(_("Directory indexes are not allowed here.")) if not fullpath.exists(): - raise Http404(_("“%(path)s” does not exist") % {"path": fullpath}) + raise Http404(_('“%(path)s” does not exist') % {'path': fullpath}) # Respect the If-Modified-Since header. statobj = fullpath.stat() - if not was_modified_since( - request.META.get("HTTP_IF_MODIFIED_SINCE"), statobj.st_mtime, statobj.st_size - ): + if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), + statobj.st_mtime, statobj.st_size): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(str(fullpath)) - content_type = content_type or "application/octet-stream" - response = FileResponse(fullpath.open("rb"), content_type=content_type) + content_type = content_type or 'application/octet-stream' + response = FileResponse(fullpath.open('rb'), content_type=content_type) response.headers["Last-Modified"] = http_date(statobj.st_mtime) if encoding: response.headers["Content-Encoding"] = encoding @@ -82,32 +82,26 @@ template_translatable = gettext_lazy("Index of %(directory)s") def directory_index(path, fullpath): try: - t = loader.select_template( - [ - "static/directory_index.html", - "static/directory_index", - ] - ) + t = loader.select_template([ + 'static/directory_index.html', + 'static/directory_index', + ]) except TemplateDoesNotExist: - t = Engine(libraries={"i18n": "django.templatetags.i18n"}).from_string( - DEFAULT_DIRECTORY_INDEX_TEMPLATE - ) + t = Engine(libraries={'i18n': 'django.templatetags.i18n'}).from_string(DEFAULT_DIRECTORY_INDEX_TEMPLATE) c = Context() else: c = {} files = [] for f in fullpath.iterdir(): - if not f.name.startswith("."): + if not f.name.startswith('.'): url = str(f.relative_to(fullpath)) if f.is_dir(): - url += "/" + url += '/' files.append(url) - c.update( - { - "directory": path + "/", - "file_list": files, - } - ) + c.update({ + 'directory': path + '/', + 'file_list': files, + }) return HttpResponse(t.render(c)) @@ -128,7 +122,8 @@ def was_modified_since(header=None, mtime=0, size=0): try: if header is None: raise ValueError - matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header, re.IGNORECASE) + matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header, + re.IGNORECASE) header_mtime = parse_http_date(matches[1]) header_len = matches[3] if header_len and int(header_len) != size: diff --git a/venv/Lib/site-packages/django/views/templates/technical_500.html b/venv/Lib/site-packages/django/views/templates/technical_500.html index 5ace2a1..0ed70d5 100644 --- a/venv/Lib/site-packages/django/views/templates/technical_500.html +++ b/venv/Lib/site-packages/django/views/templates/technical_500.html @@ -16,7 +16,6 @@ h3 { margin:1em 0 .5em 0; } h4 { margin:0 0 .5em 0; font-weight: normal; } code, pre { font-size: 100%; white-space: pre-wrap; } - summary { cursor: pointer; } table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; } tbody td, tbody th { vertical-align:top; padding:2px 3px; } thead th { @@ -41,8 +40,8 @@ div.context ol.context-line li span { position:absolute; right:32px; } .user div.context ol.context-line li { background-color:#bbb; color:#000; } .user div.context ol li { color:#666; } - div.commands, summary.commands { margin-left: 40px; } - div.commands a, summary.commands { color:#555; text-decoration:none; } + div.commands { margin-left: 40px; } + div.commands a { color:#555; text-decoration:none; } .user div.commands a { color: black; } #summary { background: #ffc; } #summary h2 { font-weight: normal; color: #666; } @@ -72,6 +71,7 @@ } } window.onload = function() { + hideAll(document.querySelectorAll('table.vars')); hideAll(document.querySelectorAll('ol.pre-context')); hideAll(document.querySelectorAll('ol.post-context')); hideAll(document.querySelectorAll('div.pastebin')); @@ -85,6 +85,14 @@ } return false; } + function varToggle(link, id) { + toggle('v' + id); + var s = link.getElementsByTagName('span')[0]; + var uarr = String.fromCharCode(0x25b6); + var darr = String.fromCharCode(0x25bc); + s.textContent = s.textContent == uarr ? darr : uarr; + return false; + } function switchPastebinFriendly(link) { s1 = "Switch to copy-and-paste view"; s2 = "Switch back to interactive view"; @@ -108,7 +116,7 @@ </tr> <tr> <th>Request URL:</th> - <td>{{ request_insecure_uri }}</td> + <td>{{ request.get_raw_uri }}</td> </tr> {% endif %} <tr> @@ -246,14 +254,13 @@ {% endif %} {% if frame.vars %} - {% if is_email %} - <div class="commands"> - <h2>Local Vars</h2> - </div> - {% else %} - <details> - <summary class="commands">Local vars</summary> - {% endif %} + <div class="commands"> + {% if is_email %} + <h2>Local Vars</h2> + {% else %} + <a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a> + {% endif %} + </div> <table class="vars" id="v{{ frame.id }}"> <thead> <tr> @@ -270,14 +277,13 @@ {% endfor %} </tbody> </table> - {% if not is_email %}</details>{% endif %} {% endif %} </li> {% endfor %} </ul> </div> -{% if not is_email %} <form action="https://dpaste.com/" name="pasteform" id="pasteform" method="post"> +{% if not is_email %} <div id="pastebinTraceback" class="pastebin"> <input type="hidden" name="language" value="PythonConsole"> <input type="hidden" name="title" @@ -289,7 +295,7 @@ Environment: {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} -Request URL: {{ request_insecure_uri }} +Request URL: {{ request.get_raw_uri }} {% endif %} Django Version: {{ django_version_info }} Python Version: {{ sys_version_info }} @@ -327,9 +333,9 @@ Exception Value: {{ exception_value|force_escape }} <input type="submit" value="Share this traceback on a public website"> </div> </form> -{% endif %} </div> {% endif %} +{% endif %} <div id="requestinfo"> <h2>Request information</h2> diff --git a/venv/Lib/site-packages/django/views/templates/technical_500.txt b/venv/Lib/site-packages/django/views/templates/technical_500.txt index 5c86a31..551413a 100644 --- a/venv/Lib/site-packages/django/views/templates/technical_500.txt +++ b/venv/Lib/site-packages/django/views/templates/technical_500.txt @@ -2,7 +2,7 @@ {% firstof exception_value 'No exception message supplied' %} {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} -Request URL: {{ request_insecure_uri }}{% endif %} +Request URL: {{ request.get_raw_uri }}{% endif %} Django Version: {{ django_version_info }} Python Executable: {{ sys_executable }} Python Version: {{ sys_version_info }} diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/INSTALLER b/venv/Lib/site-packages/django_filter-21.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/LICENSE b/venv/Lib/site-packages/django_filter-21.1.dist-info/LICENSE new file mode 100644 index 0000000..4b73093 --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) Alex Gaynor and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The names of its contributors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/METADATA b/venv/Lib/site-packages/django_filter-21.1.dist-info/METADATA new file mode 100644 index 0000000..8f1b555 --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/METADATA @@ -0,0 +1,156 @@ +Metadata-Version: 2.1 +Name: django-filter +Version: 21.1 +Summary: Django-filter is a reusable Django application for allowing users to filter querysets dynamically. +Home-page: https://github.com/carltongibson/django-filter/tree/main +Author: Alex Gaynor +Author-email: alex.gaynor@gmail.com +Maintainer: Carlton Gibson +Maintainer-email: carlton.gibson@noumenal.es +License: BSD +Project-URL: Documentation, https://django-filter.readthedocs.io/en/main/ +Project-URL: Changelog, https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst +Project-URL: Bug Tracker, https://github.com/carltongibson/django-filter/issues +Project-URL: Source Code, https://github.com/carltongibson/django-filter +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Framework :: Django +Classifier: Framework :: Django :: 2.2 +Classifier: Framework :: Django :: 3.1 +Classifier: Framework :: Django :: 3.2 +Classifier: Framework :: Django :: 4.0 +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.6 +License-File: LICENSE +Requires-Dist: Django (>=2.2) + +Django Filter +============= + +Django-filter is a reusable Django application allowing users to declaratively +add dynamic ``QuerySet`` filtering from URL parameters. + +Full documentation on `read the docs`_. + +.. image:: https://codecov.io/gh/carltongibson/django-filter/branch/develop/graph/badge.svg + :target: https://codecov.io/gh/carltongibson/django-filter + +.. image:: https://badge.fury.io/py/django-filter.svg + :target: http://badge.fury.io/py/django-filter + + +Versioning and stability policy +------------------------------- + +Django-Filter is a mature and stable package. It uses a two-part CalVer +versioning scheme, such as ``21.1``. The first number is the year. The second +is the release number within that year. + +On an on-going basis, Django-Filter aims to support all current Django +versions, the matching current Python versions, and the latest version of +Django REST Framework. + +Please see: + +* `Status of supported Python branches <https://devguide.python.org/#status-of-python-branches>`_ +* `List of supported Django versions <https://www.djangoproject.com/download/#support-versions>`_ + +Support for Python and Django versions will be dropped when they reach +end-of-life. Support for Python versions will dropped when they reach +end-of-life, even when still supported by a current version of Django. + +Other breaking changes are rare. Where required, every effort will be made to +apply a "Year plus two" deprecation period. For example, a change initially +introduced in ``23.x`` would offer a fallback where feasible and finally be +removed in ``25.1``. Where fallbacks are not feasible, breaking changes without +deprecation will be called out in the release notes. + + +Installation +------------ + +Install using pip: + +.. code-block:: sh + + pip install django-filter + +Then add ``'django_filters'`` to your ``INSTALLED_APPS``. + +.. code-block:: python + + INSTALLED_APPS = [ + ... + 'django_filters', + ] + + +Usage +----- + +Django-filter can be used for generating interfaces similar to the Django +admin's ``list_filter`` interface. It has an API very similar to Django's +``ModelForms``. For example, if you had a Product model you could have a +filterset for it with the code: + +.. code-block:: python + + import django_filters + + class ProductFilter(django_filters.FilterSet): + class Meta: + model = Product + fields = ['name', 'price', 'manufacturer'] + + +And then in your view you could do: + +.. code-block:: python + + def product_list(request): + filter = ProductFilter(request.GET, queryset=Product.objects.all()) + return render(request, 'my_app/template.html', {'filter': filter}) + + +Usage with Django REST Framework +-------------------------------- + +Django-filter provides a custom ``FilterSet`` and filter backend for use with +Django REST Framework. + +To use this adjust your import to use +``django_filters.rest_framework.FilterSet``. + +.. code-block:: python + + from django_filters import rest_framework as filters + + class ProductFilter(filters.FilterSet): + class Meta: + model = Product + fields = ('category', 'in_stock') + + +For more details see the `DRF integration docs`_. + + +Support +------- + +If you need help you can start a `discussion`_. For commercial support, please +`contact Carlton Gibson via his website <https://noumenal.es/>`_. + +.. _`discussion`: https://github.com/carltongibson/django-filter/discussions +.. _`read the docs`: https://django-filter.readthedocs.io/en/main/ +.. _`DRF integration docs`: https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html + + diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/RECORD b/venv/Lib/site-packages/django_filter-21.1.dist-info/RECORD new file mode 100644 index 0000000..f738f3e --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/RECORD @@ -0,0 +1,74 @@ +django_filter-21.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +django_filter-21.1.dist-info/LICENSE,sha256=4UQ8qx2nFmTo4lASXOByK3RcVWDurx7_w9HozSy9mAI,1487 +django_filter-21.1.dist-info/METADATA,sha256=D5IHYcqiWsyxrQggFmRvz3-ncMtjEMdMWfhF1pXOq94,5097 +django_filter-21.1.dist-info/RECORD,, +django_filter-21.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +django_filter-21.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +django_filter-21.1.dist-info/top_level.txt,sha256=JVS5v6IKT4Q2Sqv3pRcYCkKZoSdRlH2KalrhTuAenas,15 +django_filters/__init__.py,sha256=x17jQnKLjLiVc0KXqMzaMOiONzH-Krz3WLSPDXA356g,643 +django_filters/__pycache__/__init__.cpython-39.pyc,, +django_filters/__pycache__/compat.cpython-39.pyc,, +django_filters/__pycache__/conf.cpython-39.pyc,, +django_filters/__pycache__/constants.cpython-39.pyc,, +django_filters/__pycache__/exceptions.cpython-39.pyc,, +django_filters/__pycache__/fields.cpython-39.pyc,, +django_filters/__pycache__/filters.cpython-39.pyc,, +django_filters/__pycache__/filterset.cpython-39.pyc,, +django_filters/__pycache__/utils.cpython-39.pyc,, +django_filters/__pycache__/views.cpython-39.pyc,, +django_filters/__pycache__/widgets.cpython-39.pyc,, +django_filters/compat.py,sha256=5GeZj6vzL-B56z_KGF4MJq9GkxmNHX_xxGLuPjmqwIQ,545 +django_filters/conf.py,sha256=hfmpXUSr-SSO9kIl4d2KOCyyn7XtooCXmTR3-JqCtK4,3056 +django_filters/constants.py,sha256=LbxdUwNFU2QD8N2XT2Hqcog418gEJk5Ike_Pm08ieOw,64 +django_filters/exceptions.py,sha256=NHK-xlz1XmC_78yNI5-lH1kjwVMb-zrkSegU2Ga8-BY,254 +django_filters/fields.py,sha256=WdnyaoYs6_JmM0sOYnn89xTYLHGgbp_i4A5R_qLIKtQ,9892 +django_filters/filters.py,sha256=uODlEmDv571EqXUHh8Tr_thcus_rAzWbiQlgKrY8F_U,24875 +django_filters/filterset.py,sha256=FBj9WubrBMhYmlsUOtTqJoZLBm69FSOa7URGIzeoNHA,16523 +django_filters/locale/ar/LC_MESSAGES/django.mo,sha256=utzbP4BsdW91KwGgFwyvXVY1uNZ8otdcUDoZZpIZ9Pg,2568 +django_filters/locale/ar/LC_MESSAGES/django.po,sha256=UaO-74kymS4qUcCkGn4VgcoQOsFXoyat40_xT99mT2g,3621 +django_filters/locale/be/LC_MESSAGES/django.mo,sha256=lbp-b9nTHDvBb8ozSkyHWGlmi4X3WyKaObT9GB2fe9E,2819 +django_filters/locale/be/LC_MESSAGES/django.po,sha256=f6SF5hE7bSWaPNBkydB0T55APL4TmlEY7vDlk9bAVL8,3651 +django_filters/locale/bg/LC_MESSAGES/django.mo,sha256=ZPmu82dqvj3yd3-J0KLK-hxfwETzqKmq0c-Anozn5Go,2711 +django_filters/locale/bg/LC_MESSAGES/django.po,sha256=zUPOML-VeE0V-bnW4BNmhL9pewlwkLKfh1MXr8lH2J8,3736 +django_filters/locale/cs/LC_MESSAGES/django.mo,sha256=vZuyiklIF_I3qs9pdhb3OTT2d63aIttgtcHY1b9Gsps,2368 +django_filters/locale/cs/LC_MESSAGES/django.po,sha256=D_4W8R8y5s5x7Hd2xz-Y39zvlN2fr7Df2uCCJ_W-sUw,3144 +django_filters/locale/da/LC_MESSAGES/django.mo,sha256=gPy5CaNJWYbCPqeqb6XPr1uynW9FEn8zV_-85RMJaZc,2166 +django_filters/locale/da/LC_MESSAGES/django.po,sha256=5aCGYuKedqszhcntOw1OUF0_cUtGoXzZB9E1nOoOAnI,3037 +django_filters/locale/de/LC_MESSAGES/django.mo,sha256=IvgqQ0BQ7AiJSmdcGpKWheuLrzrXqs-lbp4Bac2jOdI,2277 +django_filters/locale/de/LC_MESSAGES/django.po,sha256=rbAvo_wdbA494qusvkRGESF4E77jizPafnZhgo9s2aw,3293 +django_filters/locale/el/LC_MESSAGES/django.mo,sha256=2--juTiXF9v6u95krY9VwZCv2cXoJai6CXi4RWpi39w,2836 +django_filters/locale/el/LC_MESSAGES/django.po,sha256=i8h_UnGeVEYAZNXDr6KMqalpq8g3hj6kbtEMcFoqgWE,3966 +django_filters/locale/es/LC_MESSAGES/django.mo,sha256=5KCl_uUwge5RuGStcyMSsVPD6AOunjNvjuE-32PqWis,2279 +django_filters/locale/es/LC_MESSAGES/django.po,sha256=tP1Zuzgz0v84c74PZRy6DuiFyRkN7NOTlMQigEhXL94,3293 +django_filters/locale/es_AR/LC_MESSAGES/django.mo,sha256=OCKAVbT3ct5gf2_t5XsKryjlkIQDYZjC67Oz0j-YE6s,703 +django_filters/locale/es_AR/LC_MESSAGES/django.po,sha256=jI7WMhsSWbTZ7mnLSzB4lsloohr-TtxxPdkNComOZHc,1015 +django_filters/locale/fr/LC_MESSAGES/django.mo,sha256=Bb39Mt6ocOXlcYvT3Om7xMLGNtHEKpzNPQzw7hXqsBE,727 +django_filters/locale/fr/LC_MESSAGES/django.po,sha256=JT34o_10l5Gxyqe5nqDWy8m1TKBsatTTuDE6AqlOhdw,1067 +django_filters/locale/it/LC_MESSAGES/django.mo,sha256=TKIdnZSuYtyCpnl8X9jDyKFuIX6G69CmCvVaWpcuPXM,2268 +django_filters/locale/it/LC_MESSAGES/django.po,sha256=Yo0WtfBI56qq8XWe0QGjYPOw7RexbyJtVRARROYUoYg,3209 +django_filters/locale/pl/LC_MESSAGES/django.mo,sha256=-9taafe4N3mKLdZ4fEXkrj-azO-L4F0fGoxnDgTBuwU,1859 +django_filters/locale/pl/LC_MESSAGES/django.po,sha256=SUzt2qncjYnCmkbgac1BxBz6ZrXhQh2MADSwioCYwCE,3607 +django_filters/locale/pt_BR/LC_MESSAGES/django.mo,sha256=GLakV-03XUsCNKaofuG2fGCBIRGVYEMJiC-kD1UX4D0,2263 +django_filters/locale/pt_BR/LC_MESSAGES/django.po,sha256=R955ohil0dtXI0HU54PhdCalMwb5x4iM2f33a4IHRyg,3217 +django_filters/locale/ru/LC_MESSAGES/django.mo,sha256=1KrtkfLhq0BiDskKFffF5i53pM7Tp-bwsbPDe9F4Co0,2796 +django_filters/locale/ru/LC_MESSAGES/django.po,sha256=ut8nJ7xfrVekgvgHvgTJwQu1CbcVjTHl8gQSuy5yhH8,3818 +django_filters/locale/sk/LC_MESSAGES/django.mo,sha256=em13cqJIPA3JLTp6JXPXuNNeDqJ7uaEuxxqtOvl9PLk,2394 +django_filters/locale/sk/LC_MESSAGES/django.po,sha256=1yaXj0PaV8Ik_SJAA0Wm95GpkYAruZGbIh8rNCkFItw,3386 +django_filters/locale/uk/LC_MESSAGES/django.mo,sha256=zgC01vyDPPS81GiD3C4WeQxtCt4_ift_pU-j_2l_LrU,2912 +django_filters/locale/uk/LC_MESSAGES/django.po,sha256=eX_FYkXmRcVZKYtBoNlZ12fHg8U1FhJ0lAelfI7PcsA,3694 +django_filters/locale/zh_CN/LC_MESSAGES/django.mo,sha256=2aSG7Whwpj7iRY_7QcTV-ReuCm8JKsV-ktlRaAbYC0U,852 +django_filters/locale/zh_CN/LC_MESSAGES/django.po,sha256=9Kj2VQ9TuPgAeQdauY5zR0wOZpkCL1L-GMUpm8fnxT4,1305 +django_filters/rest_framework/__init__.py,sha256=HpNAGIdsBRJSkyM1QmqyOTb7I9VVwoMTbexbD21X6vE,113 +django_filters/rest_framework/__pycache__/__init__.cpython-39.pyc,, +django_filters/rest_framework/__pycache__/backends.cpython-39.pyc,, +django_filters/rest_framework/__pycache__/filters.cpython-39.pyc,, +django_filters/rest_framework/__pycache__/filterset.cpython-39.pyc,, +django_filters/rest_framework/backends.py,sha256=3XIwJkguiioY4BWdjzcZJIHgN-HUzbNGS-sUP9-lqDo,6182 +django_filters/rest_framework/filters.py,sha256=DXDAE1--_os5SvoFic5VxVfIHvAzidm1Del9A0NCSSA,312 +django_filters/rest_framework/filterset.py,sha256=SHr213z6vpLybwpc9cN8dROBzeGU3Lc2RMhB2Gn--Gs,1200 +django_filters/templates/django_filters/rest_framework/crispy_form.html,sha256=_Mg40d_4sWAuy7_Mzf1HRACbRgeheu0pGXy2UKpzd3s,108 +django_filters/templates/django_filters/rest_framework/form.html,sha256=KoVGtezI-pWnC18jpCKy3vufR23QLpXXooCgmEFXjAA,211 +django_filters/templates/django_filters/widgets/multiwidget.html,sha256=W0RT7BL9-sF-hCA_Ut4MfWaDwE8Z32syJs3anyurceg,118 +django_filters/utils.py,sha256=Gql2Bq2Q7Os6xiASVe_FVizv0PhoaGqYhs7wWGkrQIw,10508 +django_filters/views.py,sha256=xMs37as1DHqM4l279kDs7IBJJKegw3vaddqqGcIjTDo,4181 +django_filters/widgets.py,sha256=wQv7b03h3rAHZFmAMxSy349paRB1WbUu5Vjcv6K1aEI,9126 diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/REQUESTED b/venv/Lib/site-packages/django_filter-21.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/WHEEL b/venv/Lib/site-packages/django_filter-21.1.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/django_filter-21.1.dist-info/top_level.txt b/venv/Lib/site-packages/django_filter-21.1.dist-info/top_level.txt new file mode 100644 index 0000000..d34ce38 --- /dev/null +++ b/venv/Lib/site-packages/django_filter-21.1.dist-info/top_level.txt @@ -0,0 +1 @@ +django_filters diff --git a/venv/Lib/site-packages/django_filters/__init__.py b/venv/Lib/site-packages/django_filters/__init__.py new file mode 100644 index 0000000..1a9aead --- /dev/null +++ b/venv/Lib/site-packages/django_filters/__init__.py @@ -0,0 +1,30 @@ +# flake8: noqa +import pkgutil + +from .filters import * +from .filterset import FilterSet + +# We make the `rest_framework` module available without an additional import. +# If DRF is not installed, no-op. +if pkgutil.find_loader('rest_framework') is not None: + from . import rest_framework +del pkgutil + +__version__ = '21.1' + + +def parse_version(version): + ''' + '0.1.2.dev1' -> (0, 1, 2, 'dev1') + '0.1.2' -> (0, 1, 2) + ''' + v = version.split('.') + ret = [] + for p in v: + if p.isdigit(): + ret.append(int(p)) + else: + ret.append(p) + return tuple(ret) + +VERSION = parse_version(__version__) diff --git a/venv/Lib/site-packages/django_filters/compat.py b/venv/Lib/site-packages/django_filters/compat.py new file mode 100644 index 0000000..fcaa9e9 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/compat.py @@ -0,0 +1,25 @@ +from django.conf import settings + +# django-crispy-forms is optional +try: + import crispy_forms +except ImportError: + crispy_forms = None + + +def is_crispy(): + return 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms + + +# coreapi is optional (Note that uritemplate is a dependency of coreapi) +# Fixes #525 - cannot simply import from rest_framework.compat, due to +# import issues w/ django-guardian. +try: + import coreapi +except ImportError: + coreapi = None + +try: + import coreschema +except ImportError: + coreschema = None diff --git a/venv/Lib/site-packages/django_filters/conf.py b/venv/Lib/site-packages/django_filters/conf.py new file mode 100644 index 0000000..6ca1767 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/conf.py @@ -0,0 +1,109 @@ +from django.conf import settings as dj_settings +from django.core.signals import setting_changed +from django.utils.translation import gettext_lazy as _ + +from .utils import deprecate + +DEFAULTS = { + 'DISABLE_HELP_TEXT': False, + + 'DEFAULT_LOOKUP_EXPR': 'exact', + + # empty/null choices + 'EMPTY_CHOICE_LABEL': '---------', + 'NULL_CHOICE_LABEL': None, + 'NULL_CHOICE_VALUE': 'null', + + 'VERBOSE_LOOKUPS': { + # transforms don't need to be verbose, since their expressions are chained + 'date': _('date'), + 'year': _('year'), + 'month': _('month'), + 'day': _('day'), + 'week_day': _('week day'), + 'hour': _('hour'), + 'minute': _('minute'), + 'second': _('second'), + + # standard lookups + 'exact': '', + 'iexact': '', + 'contains': _('contains'), + 'icontains': _('contains'), + 'in': _('is in'), + 'gt': _('is greater than'), + 'gte': _('is greater than or equal to'), + 'lt': _('is less than'), + 'lte': _('is less than or equal to'), + 'startswith': _('starts with'), + 'istartswith': _('starts with'), + 'endswith': _('ends with'), + 'iendswith': _('ends with'), + 'range': _('is in range'), + 'isnull': _('is null'), + 'regex': _('matches regex'), + 'iregex': _('matches regex'), + 'search': _('search'), + + # postgres lookups + 'contained_by': _('is contained by'), + 'overlap': _('overlaps'), + 'has_key': _('has key'), + 'has_keys': _('has keys'), + 'has_any_keys': _('has any keys'), + 'trigram_similar': _('search'), + }, +} + + +DEPRECATED_SETTINGS = [ +] + + +def is_callable(value): + # check for callables, except types + return callable(value) and not isinstance(value, type) + + +class Settings: + + def __getattr__(self, name): + if name not in DEFAULTS: + msg = "'%s' object has no attribute '%s'" + raise AttributeError(msg % (self.__class__.__name__, name)) + + value = self.get_setting(name) + + if is_callable(value): + value = value() + + # Cache the result + setattr(self, name, value) + return value + + def get_setting(self, setting): + django_setting = 'FILTERS_%s' % setting + + if setting in DEPRECATED_SETTINGS and hasattr(dj_settings, django_setting): + deprecate("The '%s' setting has been deprecated." % django_setting) + + return getattr(dj_settings, django_setting, DEFAULTS[setting]) + + def change_setting(self, setting, value, enter, **kwargs): + if not setting.startswith('FILTERS_'): + return + setting = setting[8:] # strip 'FILTERS_' + + # ensure a valid app setting is being overridden + if setting not in DEFAULTS: + return + + # if exiting, delete value to repopulate + if enter: + setattr(self, setting, value) + else: + delattr(self, setting) + + +settings = Settings() +setting_changed.connect(settings.change_setting) diff --git a/venv/Lib/site-packages/django_filters/constants.py b/venv/Lib/site-packages/django_filters/constants.py new file mode 100644 index 0000000..795d6cc --- /dev/null +++ b/venv/Lib/site-packages/django_filters/constants.py @@ -0,0 +1,5 @@ + +ALL_FIELDS = '__all__' + + +EMPTY_VALUES = ([], (), {}, '', None) diff --git a/venv/Lib/site-packages/django_filters/exceptions.py b/venv/Lib/site-packages/django_filters/exceptions.py new file mode 100644 index 0000000..1d79e4d --- /dev/null +++ b/venv/Lib/site-packages/django_filters/exceptions.py @@ -0,0 +1,9 @@ + +from django.core.exceptions import FieldError + + +class FieldLookupError(FieldError): + def __init__(self, model_field, lookup_expr): + super().__init__( + "Unsupported lookup '%s' for field '%s'." % (lookup_expr, model_field) + ) diff --git a/venv/Lib/site-packages/django_filters/fields.py b/venv/Lib/site-packages/django_filters/fields.py new file mode 100644 index 0000000..13c9df2 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/fields.py @@ -0,0 +1,312 @@ +from collections import namedtuple +from datetime import datetime, time + +from django import forms +from django.utils.dateparse import parse_datetime +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ + +from .conf import settings +from .constants import EMPTY_VALUES +from .utils import handle_timezone +from .widgets import ( + BaseCSVWidget, + CSVWidget, + DateRangeWidget, + LookupChoiceWidget, + RangeWidget +) + + +class RangeField(forms.MultiValueField): + widget = RangeWidget + + def __init__(self, fields=None, *args, **kwargs): + if fields is None: + fields = ( + forms.DecimalField(), + forms.DecimalField()) + super().__init__(fields, *args, **kwargs) + + def compress(self, data_list): + if data_list: + return slice(*data_list) + return None + + +class DateRangeField(RangeField): + widget = DateRangeWidget + + def __init__(self, *args, **kwargs): + fields = ( + forms.DateField(), + forms.DateField()) + super().__init__(fields, *args, **kwargs) + + def compress(self, data_list): + if data_list: + start_date, stop_date = data_list + if start_date: + start_date = handle_timezone( + datetime.combine(start_date, time.min), + False + ) + if stop_date: + stop_date = handle_timezone( + datetime.combine(stop_date, time.max), + False + ) + return slice(start_date, stop_date) + return None + + +class DateTimeRangeField(RangeField): + widget = DateRangeWidget + + def __init__(self, *args, **kwargs): + fields = ( + forms.DateTimeField(), + forms.DateTimeField()) + super().__init__(fields, *args, **kwargs) + + +class IsoDateTimeRangeField(RangeField): + widget = DateRangeWidget + + def __init__(self, *args, **kwargs): + fields = ( + IsoDateTimeField(), + IsoDateTimeField()) + super().__init__(fields, *args, **kwargs) + + +class TimeRangeField(RangeField): + widget = DateRangeWidget + + def __init__(self, *args, **kwargs): + fields = ( + forms.TimeField(), + forms.TimeField()) + super().__init__(fields, *args, **kwargs) + + +class Lookup(namedtuple('Lookup', ('value', 'lookup_expr'))): + def __new__(cls, value, lookup_expr): + if value in EMPTY_VALUES or lookup_expr in EMPTY_VALUES: + raise ValueError( + "Empty values ([], (), {}, '', None) are not " + "valid Lookup arguments. Return None instead." + ) + + return super().__new__(cls, value, lookup_expr) + + +class LookupChoiceField(forms.MultiValueField): + default_error_messages = { + 'lookup_required': _('Select a lookup.'), + } + + def __init__(self, field, lookup_choices, *args, **kwargs): + empty_label = kwargs.pop('empty_label', settings.EMPTY_CHOICE_LABEL) + fields = (field, ChoiceField(choices=lookup_choices, empty_label=empty_label)) + widget = LookupChoiceWidget(widgets=[f.widget for f in fields]) + kwargs['widget'] = widget + kwargs['help_text'] = field.help_text + super().__init__(fields, *args, **kwargs) + + def compress(self, data_list): + if len(data_list) == 2: + value, lookup_expr = data_list + if value not in EMPTY_VALUES: + if lookup_expr not in EMPTY_VALUES: + return Lookup(value=value, lookup_expr=lookup_expr) + else: + raise forms.ValidationError( + self.error_messages['lookup_required'], + code='lookup_required') + return None + + +class IsoDateTimeField(forms.DateTimeField): + """ + Supports 'iso-8601' date format too which is out the scope of + the ``datetime.strptime`` standard library + + # ISO 8601: ``http://www.w3.org/TR/NOTE-datetime`` + + Based on Gist example by David Medina https://gist.github.com/copitux/5773821 + """ + ISO_8601 = 'iso-8601' + input_formats = [ISO_8601] + + def strptime(self, value, format): + value = force_str(value) + + if format == self.ISO_8601: + parsed = parse_datetime(value) + if parsed is None: # Continue with other formats if doesn't match + raise ValueError + return handle_timezone(parsed) + return super().strptime(value, format) + + +class BaseCSVField(forms.Field): + """ + Base field for validating CSV types. Value validation is performed by + secondary base classes. + + ex:: + class IntegerCSVField(BaseCSVField, filters.IntegerField): + pass + + """ + base_widget_class = BaseCSVWidget + + def __init__(self, *args, **kwargs): + widget = kwargs.get('widget') or self.widget + kwargs['widget'] = self._get_widget_class(widget) + + super().__init__(*args, **kwargs) + + def _get_widget_class(self, widget): + # passthrough, allows for override + if isinstance(widget, BaseCSVWidget) or ( + isinstance(widget, type) and + issubclass(widget, BaseCSVWidget)): + return widget + + # complain since we are unable to reconstruct widget instances + assert isinstance(widget, type), \ + "'%s.widget' must be a widget class, not %s." \ + % (self.__class__.__name__, repr(widget)) + + bases = (self.base_widget_class, widget, ) + return type(str('CSV%s' % widget.__name__), bases, {}) + + def clean(self, value): + if value in self.empty_values and self.required: + raise forms.ValidationError(self.error_messages['required'], code='required') + + if value is None: + return None + return [super(BaseCSVField, self).clean(v) for v in value] + + +class BaseRangeField(BaseCSVField): + # Force use of text input, as range must always have two inputs. A date + # input would only allow a user to input one value and would always fail. + widget = CSVWidget + + default_error_messages = { + 'invalid_values': _('Range query expects two values.') + } + + def clean(self, value): + value = super().clean(value) + + assert value is None or isinstance(value, list) + + if value and len(value) != 2: + raise forms.ValidationError( + self.error_messages['invalid_values'], + code='invalid_values') + + return value + + +class ChoiceIterator: + # Emulates the behavior of ModelChoiceIterator, but instead wraps + # the field's _choices iterable. + + def __init__(self, field, choices): + self.field = field + self.choices = choices + + def __iter__(self): + if self.field.empty_label is not None: + yield ("", self.field.empty_label) + if self.field.null_label is not None: + yield (self.field.null_value, self.field.null_label) + yield from self.choices + + def __len__(self): + add = 1 if self.field.empty_label is not None else 0 + add += 1 if self.field.null_label is not None else 0 + return len(self.choices) + add + + +class ModelChoiceIterator(forms.models.ModelChoiceIterator): + # Extends the base ModelChoiceIterator to add in 'null' choice handling. + # This is a bit verbose since we have to insert the null choice after the + # empty choice, but before the remainder of the choices. + + def __iter__(self): + iterable = super().__iter__() + + if self.field.empty_label is not None: + yield next(iterable) + if self.field.null_label is not None: + yield (self.field.null_value, self.field.null_label) + yield from iterable + + def __len__(self): + add = 1 if self.field.null_label is not None else 0 + return super().__len__() + add + + +class ChoiceIteratorMixin: + def __init__(self, *args, **kwargs): + self.null_label = kwargs.pop('null_label', settings.NULL_CHOICE_LABEL) + self.null_value = kwargs.pop('null_value', settings.NULL_CHOICE_VALUE) + + super().__init__(*args, **kwargs) + + def _get_choices(self): + return super()._get_choices() + + def _set_choices(self, value): + super()._set_choices(value) + value = self.iterator(self, self._choices) + + self._choices = self.widget.choices = value + choices = property(_get_choices, _set_choices) + + +# Unlike their Model* counterparts, forms.ChoiceField and forms.MultipleChoiceField do not set empty_label +class ChoiceField(ChoiceIteratorMixin, forms.ChoiceField): + iterator = ChoiceIterator + + def __init__(self, *args, **kwargs): + self.empty_label = kwargs.pop('empty_label', settings.EMPTY_CHOICE_LABEL) + super().__init__(*args, **kwargs) + + +class MultipleChoiceField(ChoiceIteratorMixin, forms.MultipleChoiceField): + iterator = ChoiceIterator + + def __init__(self, *args, **kwargs): + self.empty_label = None + super().__init__(*args, **kwargs) + + +class ModelChoiceField(ChoiceIteratorMixin, forms.ModelChoiceField): + iterator = ModelChoiceIterator + + def to_python(self, value): + # bypass the queryset value check + if self.null_label is not None and value == self.null_value: + return value + return super().to_python(value) + + +class ModelMultipleChoiceField(ChoiceIteratorMixin, forms.ModelMultipleChoiceField): + iterator = ModelChoiceIterator + + def _check_values(self, value): + null = self.null_label is not None and value and self.null_value in value + if null: # remove the null value and any potential duplicates + value = [v for v in value if v != self.null_value] + + result = list(super()._check_values(value)) + result += [self.null_value] if null else [] + return result diff --git a/venv/Lib/site-packages/django_filters/filters.py b/venv/Lib/site-packages/django_filters/filters.py new file mode 100644 index 0000000..4f6ebe3 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/filters.py @@ -0,0 +1,804 @@ +from collections import OrderedDict +from datetime import timedelta + +from django import forms +from django.core.validators import MaxValueValidator +from django.db.models import Q +from django.db.models.constants import LOOKUP_SEP +from django.forms.utils import pretty_name +from django.utils.itercompat import is_iterable +from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ + +from .conf import settings +from .constants import EMPTY_VALUES +from .fields import ( + BaseCSVField, + BaseRangeField, + ChoiceField, + DateRangeField, + DateTimeRangeField, + IsoDateTimeField, + IsoDateTimeRangeField, + LookupChoiceField, + ModelChoiceField, + ModelMultipleChoiceField, + MultipleChoiceField, + RangeField, + TimeRangeField +) +from .utils import get_model_field, label_for_filter + +__all__ = [ + 'AllValuesFilter', + 'AllValuesMultipleFilter', + 'BaseCSVFilter', + 'BaseInFilter', + 'BaseRangeFilter', + 'BooleanFilter', + 'CharFilter', + 'ChoiceFilter', + 'DateFilter', + 'DateFromToRangeFilter', + 'DateRangeFilter', + 'DateTimeFilter', + 'DateTimeFromToRangeFilter', + 'DurationFilter', + 'Filter', + 'IsoDateTimeFilter', + 'IsoDateTimeFromToRangeFilter', + 'LookupChoiceFilter', + 'ModelChoiceFilter', + 'ModelMultipleChoiceFilter', + 'MultipleChoiceFilter', + 'NumberFilter', + 'NumericRangeFilter', + 'OrderingFilter', + 'RangeFilter', + 'TimeFilter', + 'TimeRangeFilter', + 'TypedChoiceFilter', + 'TypedMultipleChoiceFilter', + 'UUIDFilter', +] + + +class Filter: + creation_counter = 0 + field_class = forms.Field + + def __init__(self, field_name=None, lookup_expr=None, *, label=None, + method=None, distinct=False, exclude=False, **kwargs): + if lookup_expr is None: + lookup_expr = settings.DEFAULT_LOOKUP_EXPR + self.field_name = field_name + self.lookup_expr = lookup_expr + self.label = label + self.method = method + self.distinct = distinct + self.exclude = exclude + + self.extra = kwargs + self.extra.setdefault('required', False) + + self.creation_counter = Filter.creation_counter + Filter.creation_counter += 1 + + def get_method(self, qs): + """Return filter method based on whether we're excluding + or simply filtering. + """ + return qs.exclude if self.exclude else qs.filter + + def method(): + """ + Filter method needs to be lazily resolved, as it may be dependent on + the 'parent' FilterSet. + """ + def fget(self): + return self._method + + def fset(self, value): + self._method = value + + # clear existing FilterMethod + if isinstance(self.filter, FilterMethod): + del self.filter + + # override filter w/ FilterMethod. + if value is not None: + self.filter = FilterMethod(self) + + return locals() + method = property(**method()) + + def label(): + def fget(self): + if self._label is None and hasattr(self, 'model'): + self._label = label_for_filter( + self.model, self.field_name, self.lookup_expr, self.exclude + ) + return self._label + + def fset(self, value): + self._label = value + + return locals() + label = property(**label()) + + @property + def field(self): + if not hasattr(self, '_field'): + field_kwargs = self.extra.copy() + + if settings.DISABLE_HELP_TEXT: + field_kwargs.pop('help_text', None) + + self._field = self.field_class(label=self.label, **field_kwargs) + return self._field + + def filter(self, qs, value): + if value in EMPTY_VALUES: + return qs + if self.distinct: + qs = qs.distinct() + lookup = '%s__%s' % (self.field_name, self.lookup_expr) + qs = self.get_method(qs)(**{lookup: value}) + return qs + + +class CharFilter(Filter): + field_class = forms.CharField + + +class BooleanFilter(Filter): + field_class = forms.NullBooleanField + + +class ChoiceFilter(Filter): + field_class = ChoiceField + + def __init__(self, *args, **kwargs): + self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE) + super().__init__(*args, **kwargs) + + def filter(self, qs, value): + if value != self.null_value: + return super().filter(qs, value) + + qs = self.get_method(qs)(**{'%s__%s' % (self.field_name, self.lookup_expr): None}) + return qs.distinct() if self.distinct else qs + + +class TypedChoiceFilter(Filter): + field_class = forms.TypedChoiceField + + +class UUIDFilter(Filter): + field_class = forms.UUIDField + + +class MultipleChoiceFilter(Filter): + """ + This filter performs OR(by default) or AND(using conjoined=True) query + on the selected options. + + Advanced usage + -------------- + Depending on your application logic, when all or no choices are selected, + filtering may be a no-operation. In this case you may wish to avoid the + filtering overhead, particularly if using a `distinct` call. + + You can override `get_filter_predicate` to use a custom filter. + By default it will use the filter's name for the key, and the value will + be the model object - or in case of passing in `to_field_name` the + value of that attribute on the model. + + Set `always_filter` to `False` after instantiation to enable the default + `is_noop` test. You can override `is_noop` if you need a different test + for your application. + + `distinct` defaults to `True` as to-many relationships will generally + require this. + """ + field_class = MultipleChoiceField + + always_filter = True + + def __init__(self, *args, **kwargs): + kwargs.setdefault('distinct', True) + self.conjoined = kwargs.pop('conjoined', False) + self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE) + super().__init__(*args, **kwargs) + + def is_noop(self, qs, value): + """ + Return `True` to short-circuit unnecessary and potentially slow + filtering. + """ + if self.always_filter: + return False + + # A reasonable default for being a noop... + if self.extra.get('required') and len(value) == len(self.field.choices): + return True + + return False + + def filter(self, qs, value): + if not value: + # Even though not a noop, no point filtering if empty. + return qs + + if self.is_noop(qs, value): + return qs + + if not self.conjoined: + q = Q() + for v in set(value): + if v == self.null_value: + v = None + predicate = self.get_filter_predicate(v) + if self.conjoined: + qs = self.get_method(qs)(**predicate) + else: + q |= Q(**predicate) + + if not self.conjoined: + qs = self.get_method(qs)(q) + + return qs.distinct() if self.distinct else qs + + def get_filter_predicate(self, v): + name = self.field_name + if name and self.lookup_expr != settings.DEFAULT_LOOKUP_EXPR: + name = LOOKUP_SEP.join([name, self.lookup_expr]) + try: + return {name: getattr(v, self.field.to_field_name)} + except (AttributeError, TypeError): + return {name: v} + + +class TypedMultipleChoiceFilter(MultipleChoiceFilter): + field_class = forms.TypedMultipleChoiceField + + +class DateFilter(Filter): + field_class = forms.DateField + + +class DateTimeFilter(Filter): + field_class = forms.DateTimeField + + +class IsoDateTimeFilter(DateTimeFilter): + """ + Uses IsoDateTimeField to support filtering on ISO 8601 formatted datetimes. + + For context see: + + * https://code.djangoproject.com/ticket/23448 + * https://github.com/encode/django-rest-framework/issues/1338 + * https://github.com/carltongibson/django-filter/pull/264 + """ + field_class = IsoDateTimeField + + +class TimeFilter(Filter): + field_class = forms.TimeField + + +class DurationFilter(Filter): + field_class = forms.DurationField + + +class QuerySetRequestMixin: + """ + Add callable functionality to filters that support the ``queryset`` + argument. If the ``queryset`` is callable, then it **must** accept the + ``request`` object as a single argument. + + This is useful for filtering querysets by properties on the ``request`` + object, such as the user. + + Example:: + + def departments(request): + company = request.user.company + return company.department_set.all() + + class EmployeeFilter(filters.FilterSet): + department = filters.ModelChoiceFilter(queryset=departments) + ... + + The above example restricts the set of departments to those in the logged-in + user's associated company. + + """ + def __init__(self, *args, **kwargs): + self.queryset = kwargs.get('queryset') + super().__init__(*args, **kwargs) + + def get_request(self): + try: + return self.parent.request + except AttributeError: + return None + + def get_queryset(self, request): + queryset = self.queryset + + if callable(queryset): + return queryset(request) + return queryset + + @property + def field(self): + request = self.get_request() + queryset = self.get_queryset(request) + + if queryset is not None: + self.extra['queryset'] = queryset + + return super().field + + +class ModelChoiceFilter(QuerySetRequestMixin, ChoiceFilter): + field_class = ModelChoiceField + + def __init__(self, *args, **kwargs): + kwargs.setdefault('empty_label', settings.EMPTY_CHOICE_LABEL) + super().__init__(*args, **kwargs) + + +class ModelMultipleChoiceFilter(QuerySetRequestMixin, MultipleChoiceFilter): + field_class = ModelMultipleChoiceField + + +class NumberFilter(Filter): + field_class = forms.DecimalField + + def get_max_validator(self): + """ + Return a MaxValueValidator for the field, or None to disable. + """ + return MaxValueValidator(1e50) + + @property + def field(self): + if not hasattr(self, '_field'): + field = super().field + max_validator = self.get_max_validator() + if max_validator: + field.validators.append(max_validator) + + self._field = field + return self._field + + +class NumericRangeFilter(Filter): + field_class = RangeField + + def filter(self, qs, value): + if value: + if value.start is not None and value.stop is not None: + value = (value.start, value.stop) + elif value.start is not None: + self.lookup_expr = 'startswith' + value = value.start + elif value.stop is not None: + self.lookup_expr = 'endswith' + value = value.stop + + return super().filter(qs, value) + + +class RangeFilter(Filter): + field_class = RangeField + + def filter(self, qs, value): + if value: + if value.start is not None and value.stop is not None: + self.lookup_expr = 'range' + value = (value.start, value.stop) + elif value.start is not None: + self.lookup_expr = 'gte' + value = value.start + elif value.stop is not None: + self.lookup_expr = 'lte' + value = value.stop + + return super().filter(qs, value) + + +def _truncate(dt): + return dt.date() + + +class DateRangeFilter(ChoiceFilter): + choices = [ + ('today', _('Today')), + ('yesterday', _('Yesterday')), + ('week', _('Past 7 days')), + ('month', _('This month')), + ('year', _('This year')), + ] + + filters = { + 'today': lambda qs, name: qs.filter(**{ + '%s__year' % name: now().year, + '%s__month' % name: now().month, + '%s__day' % name: now().day + }), + 'yesterday': lambda qs, name: qs.filter(**{ + '%s__year' % name: (now() - timedelta(days=1)).year, + '%s__month' % name: (now() - timedelta(days=1)).month, + '%s__day' % name: (now() - timedelta(days=1)).day, + }), + 'week': lambda qs, name: qs.filter(**{ + '%s__gte' % name: _truncate(now() - timedelta(days=7)), + '%s__lt' % name: _truncate(now() + timedelta(days=1)), + }), + 'month': lambda qs, name: qs.filter(**{ + '%s__year' % name: now().year, + '%s__month' % name: now().month + }), + 'year': lambda qs, name: qs.filter(**{ + '%s__year' % name: now().year, + }), + } + + def __init__(self, choices=None, filters=None, *args, **kwargs): + if choices is not None: + self.choices = choices + if filters is not None: + self.filters = filters + + unique = set([x[0] for x in self.choices]) ^ set(self.filters) + assert not unique, \ + "Keys must be present in both 'choices' and 'filters'. Missing keys: " \ + "'%s'" % ', '.join(sorted(unique)) + + # TODO: remove assertion in 2.1 + assert not hasattr(self, 'options'), \ + "The 'options' attribute has been replaced by 'choices' and 'filters'. " \ + "See: https://django-filter.readthedocs.io/en/main/guide/migration.html" + + # null choice not relevant + kwargs.setdefault('null_label', None) + super().__init__(choices=self.choices, *args, **kwargs) + + def filter(self, qs, value): + if not value: + return qs + + assert value in self.filters + + qs = self.filters[value](qs, self.field_name) + return qs.distinct() if self.distinct else qs + + +class DateFromToRangeFilter(RangeFilter): + field_class = DateRangeField + + +class DateTimeFromToRangeFilter(RangeFilter): + field_class = DateTimeRangeField + + +class IsoDateTimeFromToRangeFilter(RangeFilter): + field_class = IsoDateTimeRangeField + + +class TimeRangeFilter(RangeFilter): + field_class = TimeRangeField + + +class AllValuesFilter(ChoiceFilter): + @property + def field(self): + qs = self.model._default_manager.distinct() + qs = qs.order_by(self.field_name).values_list(self.field_name, flat=True) + self.extra['choices'] = [(o, o) for o in qs] + return super().field + + +class AllValuesMultipleFilter(MultipleChoiceFilter): + @property + def field(self): + qs = self.model._default_manager.distinct() + qs = qs.order_by(self.field_name).values_list(self.field_name, flat=True) + self.extra['choices'] = [(o, o) for o in qs] + return super().field + + +class BaseCSVFilter(Filter): + """ + Base class for CSV type filters, such as IN and RANGE. + """ + base_field_class = BaseCSVField + + def __init__(self, *args, **kwargs): + kwargs.setdefault('help_text', _('Multiple values may be separated by commas.')) + super().__init__(*args, **kwargs) + + class ConcreteCSVField(self.base_field_class, self.field_class): + pass + ConcreteCSVField.__name__ = self._field_class_name( + self.field_class, self.lookup_expr + ) + + self.field_class = ConcreteCSVField + + @classmethod + def _field_class_name(cls, field_class, lookup_expr): + """ + Generate a suitable class name for the concrete field class. This is not + completely reliable, as not all field class names are of the format + <Type>Field. + + ex:: + + BaseCSVFilter._field_class_name(DateTimeField, 'year__in') + + returns 'DateTimeYearInField' + + """ + # DateTimeField => DateTime + type_name = field_class.__name__ + if type_name.endswith('Field'): + type_name = type_name[:-5] + + # year__in => YearIn + parts = lookup_expr.split(LOOKUP_SEP) + expression_name = ''.join(p.capitalize() for p in parts) + + # DateTimeYearInField + return str('%s%sField' % (type_name, expression_name)) + + +class BaseInFilter(BaseCSVFilter): + + def __init__(self, *args, **kwargs): + kwargs.setdefault('lookup_expr', 'in') + super().__init__(*args, **kwargs) + + +class BaseRangeFilter(BaseCSVFilter): + base_field_class = BaseRangeField + + def __init__(self, *args, **kwargs): + kwargs.setdefault('lookup_expr', 'range') + super().__init__(*args, **kwargs) + + +class LookupChoiceFilter(Filter): + """ + A combined filter that allows users to select the lookup expression from a dropdown. + + * ``lookup_choices`` is an optional argument that accepts multiple input + formats, and is ultimately normlized as the choices used in the lookup + dropdown. See ``.get_lookup_choices()`` for more information. + + * ``field_class`` is an optional argument that allows you to set the inner + form field class used to validate the value. Default: ``forms.CharField`` + + ex:: + + price = django_filters.LookupChoiceFilter( + field_class=forms.DecimalField, + lookup_choices=[ + ('exact', 'Equals'), + ('gt', 'Greater than'), + ('lt', 'Less than'), + ] + ) + + """ + field_class = forms.CharField + outer_class = LookupChoiceField + + def __init__(self, field_name=None, lookup_choices=None, field_class=None, **kwargs): + self.empty_label = kwargs.pop('empty_label', settings.EMPTY_CHOICE_LABEL) + + super(LookupChoiceFilter, self).__init__(field_name=field_name, **kwargs) + + self.lookup_choices = lookup_choices + if field_class is not None: + self.field_class = field_class + + @classmethod + def normalize_lookup(cls, lookup): + """ + Normalize the lookup into a tuple of ``(lookup expression, display value)`` + + If the ``lookup`` is already a tuple, the tuple is not altered. + If the ``lookup`` is a string, a tuple is returned with the lookup + expression used as the basis for the display value. + + ex:: + + >>> LookupChoiceFilter.normalize_lookup(('exact', 'Equals')) + ('exact', 'Equals') + + >>> LookupChoiceFilter.normalize_lookup('has_key') + ('has_key', 'Has key') + + """ + if isinstance(lookup, str): + return (lookup, pretty_name(lookup)) + return (lookup[0], lookup[1]) + + def get_lookup_choices(self): + """ + Get the lookup choices in a format suitable for ``django.forms.ChoiceField``. + If the filter is initialized with ``lookup_choices``, this value is normalized + and passed to the underlying ``LookupChoiceField``. If no choices are provided, + they are generated from the corresponding model field's registered lookups. + """ + lookups = self.lookup_choices + if lookups is None: + field = get_model_field(self.model, self.field_name) + lookups = field.get_lookups() + + return [self.normalize_lookup(lookup) for lookup in lookups] + + @property + def field(self): + if not hasattr(self, '_field'): + inner_field = super().field + lookups = self.get_lookup_choices() + + self._field = self.outer_class( + inner_field, lookups, + label=self.label, + empty_label=self.empty_label, + required=self.extra['required'], + ) + + return self._field + + def filter(self, qs, lookup): + if not lookup: + return super().filter(qs, None) + + self.lookup_expr = lookup.lookup_expr + return super().filter(qs, lookup.value) + + +class OrderingFilter(BaseCSVFilter, ChoiceFilter): + """ + Enable queryset ordering. As an extension of ``ChoiceFilter`` it accepts + two additional arguments that are used to build the ordering choices. + + * ``fields`` is a mapping of {model field name: parameter name}. The + parameter names are exposed in the choices and mask/alias the field + names used in the ``order_by()`` call. Similar to field ``choices``, + ``fields`` accepts the 'list of two-tuples' syntax that retains order. + ``fields`` may also just be an iterable of strings. In this case, the + field names simply double as the exposed parameter names. + + * ``field_labels`` is an optional argument that allows you to customize + the display label for the corresponding parameter. It accepts a mapping + of {field name: human readable label}. Keep in mind that the key is the + field name, and not the exposed parameter name. + + Additionally, you can just provide your own ``choices`` if you require + explicit control over the exposed options. For example, when you might + want to disable descending sort options. + + This filter is also CSV-based, and accepts multiple ordering params. The + default select widget does not enable the use of this, but it is useful + for APIs. + + """ + descending_fmt = _('%s (descending)') + + def __init__(self, *args, **kwargs): + """ + ``fields`` may be either a mapping or an iterable. + ``field_labels`` must be a map of field names to display labels + """ + fields = kwargs.pop('fields', {}) + fields = self.normalize_fields(fields) + field_labels = kwargs.pop('field_labels', {}) + + self.param_map = {v: k for k, v in fields.items()} + + if 'choices' not in kwargs: + kwargs['choices'] = self.build_choices(fields, field_labels) + + kwargs.setdefault('label', _('Ordering')) + kwargs.setdefault('help_text', '') + kwargs.setdefault('null_label', None) + super().__init__(*args, **kwargs) + + def get_ordering_value(self, param): + descending = param.startswith('-') + param = param[1:] if descending else param + field_name = self.param_map.get(param, param) + + return "-%s" % field_name if descending else field_name + + def filter(self, qs, value): + if value in EMPTY_VALUES: + return qs + + ordering = [self.get_ordering_value(param) for param in value] + return qs.order_by(*ordering) + + @classmethod + def normalize_fields(cls, fields): + """ + Normalize the fields into an ordered map of {field name: param name} + """ + # fields is a mapping, copy into new OrderedDict + if isinstance(fields, dict): + return OrderedDict(fields) + + # convert iterable of values => iterable of pairs (field name, param name) + assert is_iterable(fields), \ + "'fields' must be an iterable (e.g., a list, tuple, or mapping)." + + # fields is an iterable of field names + assert all(isinstance(field, str) or + is_iterable(field) and len(field) == 2 # may need to be wrapped in parens + for field in fields), \ + "'fields' must contain strings or (field name, param name) pairs." + + return OrderedDict([ + (f, f) if isinstance(f, str) else f for f in fields + ]) + + def build_choices(self, fields, labels): + ascending = [ + (param, labels.get(field, _(pretty_name(param)))) + for field, param in fields.items() + ] + descending = [ + ('-%s' % param, labels.get('-%s' % param, self.descending_fmt % label)) + for param, label in ascending + ] + + # interleave the ascending and descending choices + return [val for pair in zip(ascending, descending) for val in pair] + + +class FilterMethod: + """ + This helper is used to override Filter.filter() when a 'method' argument + is passed. It proxies the call to the actual method on the filter's parent. + """ + def __init__(self, filter_instance): + self.f = filter_instance + + def __call__(self, qs, value): + if value in EMPTY_VALUES: + return qs + + return self.method(qs, self.f.field_name, value) + + @property + def method(self): + """ + Resolve the method on the parent filterset. + """ + instance = self.f + + # noop if 'method' is a function + if callable(instance.method): + return instance.method + + # otherwise, method is the name of a method on the parent FilterSet. + assert hasattr(instance, 'parent'), \ + "Filter '%s' must have a parent FilterSet to find '.%s()'" % \ + (instance.field_name, instance.method) + + parent = instance.parent + method = getattr(parent, instance.method, None) + + assert callable(method), \ + "Expected parent FilterSet '%s.%s' to have a '.%s()' method." % \ + (parent.__class__.__module__, parent.__class__.__name__, instance.method) + + return method diff --git a/venv/Lib/site-packages/django_filters/filterset.py b/venv/Lib/site-packages/django_filters/filterset.py new file mode 100644 index 0000000..9c2a4f7 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/filterset.py @@ -0,0 +1,470 @@ +import copy +from collections import OrderedDict + +from django import forms +from django.db import models +from django.db.models.constants import LOOKUP_SEP +from django.db.models.fields.related import ( + ManyToManyRel, + ManyToOneRel, + OneToOneRel +) + +from .conf import settings +from .constants import ALL_FIELDS +from .filters import ( + BaseInFilter, + BaseRangeFilter, + BooleanFilter, + CharFilter, + ChoiceFilter, + DateFilter, + DateTimeFilter, + DurationFilter, + Filter, + ModelChoiceFilter, + ModelMultipleChoiceFilter, + NumberFilter, + TimeFilter, + UUIDFilter +) +from .utils import ( + get_all_model_fields, + get_model_field, + resolve_field, + try_dbfield +) + + +def remote_queryset(field): + """ + Get the queryset for the other side of a relationship. This works + for both `RelatedField`s and `ForeignObjectRel`s. + """ + model = field.related_model + + # Reverse relationships do not have choice limits + if not hasattr(field, 'get_limit_choices_to'): + return model._default_manager.all() + + limit_choices_to = field.get_limit_choices_to() + return model._default_manager.complex_filter(limit_choices_to) + + +class FilterSetOptions: + def __init__(self, options=None): + self.model = getattr(options, 'model', None) + self.fields = getattr(options, 'fields', None) + self.exclude = getattr(options, 'exclude', None) + + self.filter_overrides = getattr(options, 'filter_overrides', {}) + + self.form = getattr(options, 'form', forms.Form) + + +class FilterSetMetaclass(type): + def __new__(cls, name, bases, attrs): + attrs['declared_filters'] = cls.get_declared_filters(bases, attrs) + + new_class = super().__new__(cls, name, bases, attrs) + new_class._meta = FilterSetOptions(getattr(new_class, 'Meta', None)) + new_class.base_filters = new_class.get_filters() + + # TODO: remove assertion in 2.1 + assert not hasattr(new_class, 'filter_for_reverse_field'), ( + "`%(cls)s.filter_for_reverse_field` has been removed. " + "`%(cls)s.filter_for_field` now generates filters for reverse fields. " + "See: https://django-filter.readthedocs.io/en/main/guide/migration.html" + % {'cls': new_class.__name__} + ) + + return new_class + + @classmethod + def get_declared_filters(cls, bases, attrs): + filters = [ + (filter_name, attrs.pop(filter_name)) + for filter_name, obj in list(attrs.items()) + if isinstance(obj, Filter) + ] + + # Default the `filter.field_name` to the attribute name on the filterset + for filter_name, f in filters: + if getattr(f, 'field_name', None) is None: + f.field_name = filter_name + + filters.sort(key=lambda x: x[1].creation_counter) + + # Ensures a base class field doesn't override cls attrs, and maintains + # field precedence when inheriting multiple parents. e.g. if there is a + # class C(A, B), and A and B both define 'field', use 'field' from A. + known = set(attrs) + + def visit(name): + known.add(name) + return name + + base_filters = [ + (visit(name), f) + for base in bases if hasattr(base, 'declared_filters') + for name, f in base.declared_filters.items() if name not in known + ] + + return OrderedDict(base_filters + filters) + + +FILTER_FOR_DBFIELD_DEFAULTS = { + models.AutoField: {'filter_class': NumberFilter}, + models.CharField: {'filter_class': CharFilter}, + models.TextField: {'filter_class': CharFilter}, + models.BooleanField: {'filter_class': BooleanFilter}, + models.DateField: {'filter_class': DateFilter}, + models.DateTimeField: {'filter_class': DateTimeFilter}, + models.TimeField: {'filter_class': TimeFilter}, + models.DurationField: {'filter_class': DurationFilter}, + models.DecimalField: {'filter_class': NumberFilter}, + models.SmallIntegerField: {'filter_class': NumberFilter}, + models.IntegerField: {'filter_class': NumberFilter}, + models.PositiveIntegerField: {'filter_class': NumberFilter}, + models.PositiveSmallIntegerField: {'filter_class': NumberFilter}, + models.FloatField: {'filter_class': NumberFilter}, + models.NullBooleanField: {'filter_class': BooleanFilter}, + models.SlugField: {'filter_class': CharFilter}, + models.EmailField: {'filter_class': CharFilter}, + models.FilePathField: {'filter_class': CharFilter}, + models.URLField: {'filter_class': CharFilter}, + models.GenericIPAddressField: {'filter_class': CharFilter}, + models.CommaSeparatedIntegerField: {'filter_class': CharFilter}, + models.UUIDField: {'filter_class': UUIDFilter}, + + # Forward relationships + models.OneToOneField: { + 'filter_class': ModelChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + 'to_field_name': f.remote_field.field_name, + 'null_label': settings.NULL_CHOICE_LABEL if f.null else None, + } + }, + models.ForeignKey: { + 'filter_class': ModelChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + 'to_field_name': f.remote_field.field_name, + 'null_label': settings.NULL_CHOICE_LABEL if f.null else None, + } + }, + models.ManyToManyField: { + 'filter_class': ModelMultipleChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + } + }, + + # Reverse relationships + OneToOneRel: { + 'filter_class': ModelChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + 'null_label': settings.NULL_CHOICE_LABEL if f.null else None, + } + }, + ManyToOneRel: { + 'filter_class': ModelMultipleChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + } + }, + ManyToManyRel: { + 'filter_class': ModelMultipleChoiceFilter, + 'extra': lambda f: { + 'queryset': remote_queryset(f), + } + }, +} + + +class BaseFilterSet: + FILTER_DEFAULTS = FILTER_FOR_DBFIELD_DEFAULTS + + def __init__(self, data=None, queryset=None, *, request=None, prefix=None): + if queryset is None: + queryset = self._meta.model._default_manager.all() + model = queryset.model + + self.is_bound = data is not None + self.data = data or {} + self.queryset = queryset + self.request = request + self.form_prefix = prefix + + self.filters = copy.deepcopy(self.base_filters) + + # propagate the model and filterset to the filters + for filter_ in self.filters.values(): + filter_.model = model + filter_.parent = self + + def is_valid(self): + """ + Return True if the underlying form has no errors, or False otherwise. + """ + return self.is_bound and self.form.is_valid() + + @property + def errors(self): + """ + Return an ErrorDict for the data provided for the underlying form. + """ + return self.form.errors + + def filter_queryset(self, queryset): + """ + Filter the queryset with the underlying form's `cleaned_data`. You must + call `is_valid()` or `errors` before calling this method. + + This method should be overridden if additional filtering needs to be + applied to the queryset before it is cached. + """ + for name, value in self.form.cleaned_data.items(): + queryset = self.filters[name].filter(queryset, value) + assert isinstance(queryset, models.QuerySet), \ + "Expected '%s.%s' to return a QuerySet, but got a %s instead." \ + % (type(self).__name__, name, type(queryset).__name__) + return queryset + + @property + def qs(self): + if not hasattr(self, '_qs'): + qs = self.queryset.all() + if self.is_bound: + # ensure form validation before filtering + self.errors + qs = self.filter_queryset(qs) + self._qs = qs + return self._qs + + def get_form_class(self): + """ + Returns a django Form suitable of validating the filterset data. + + This method should be overridden if the form class needs to be + customized relative to the filterset instance. + """ + fields = OrderedDict([ + (name, filter_.field) + for name, filter_ in self.filters.items()]) + + return type(str('%sForm' % self.__class__.__name__), + (self._meta.form,), fields) + + @property + def form(self): + if not hasattr(self, '_form'): + Form = self.get_form_class() + if self.is_bound: + self._form = Form(self.data, prefix=self.form_prefix) + else: + self._form = Form(prefix=self.form_prefix) + return self._form + + @classmethod + def get_fields(cls): + """ + Resolve the 'fields' argument that should be used for generating filters on the + filterset. This is 'Meta.fields' sans the fields in 'Meta.exclude'. + """ + model = cls._meta.model + fields = cls._meta.fields + exclude = cls._meta.exclude + + assert not (fields is None and exclude is None), \ + "Setting 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' " \ + "has been deprecated since 0.15.0 and is now disallowed. Add an explicit " \ + "'Meta.fields' or 'Meta.exclude' to the %s class." % cls.__name__ + + # Setting exclude with no fields implies all other fields. + if exclude is not None and fields is None: + fields = ALL_FIELDS + + # Resolve ALL_FIELDS into all fields for the filterset's model. + if fields == ALL_FIELDS: + fields = get_all_model_fields(model) + + # Remove excluded fields + exclude = exclude or [] + if not isinstance(fields, dict): + fields = [(f, [settings.DEFAULT_LOOKUP_EXPR]) for f in fields if f not in exclude] + else: + fields = [(f, lookups) for f, lookups in fields.items() if f not in exclude] + + return OrderedDict(fields) + + @classmethod + def get_filter_name(cls, field_name, lookup_expr): + """ + Combine a field name and lookup expression into a usable filter name. + Exact lookups are the implicit default, so "exact" is stripped from the + end of the filter name. + """ + filter_name = LOOKUP_SEP.join([field_name, lookup_expr]) + + # This also works with transformed exact lookups, such as 'date__exact' + _default_expr = LOOKUP_SEP + settings.DEFAULT_LOOKUP_EXPR + if filter_name.endswith(_default_expr): + filter_name = filter_name[:-len(_default_expr)] + + return filter_name + + @classmethod + def get_filters(cls): + """ + Get all filters for the filterset. This is the combination of declared and + generated filters. + """ + + # No model specified - skip filter generation + if not cls._meta.model: + return cls.declared_filters.copy() + + # Determine the filters that should be included on the filterset. + filters = OrderedDict() + fields = cls.get_fields() + undefined = [] + + for field_name, lookups in fields.items(): + field = get_model_field(cls._meta.model, field_name) + + # warn if the field doesn't exist. + if field is None: + undefined.append(field_name) + + for lookup_expr in lookups: + filter_name = cls.get_filter_name(field_name, lookup_expr) + + # If the filter is explicitly declared on the class, skip generation + if filter_name in cls.declared_filters: + filters[filter_name] = cls.declared_filters[filter_name] + continue + + if field is not None: + filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr) + + # Allow Meta.fields to contain declared filters *only* when a list/tuple + if isinstance(cls._meta.fields, (list, tuple)): + undefined = [f for f in undefined if f not in cls.declared_filters] + + if undefined: + raise TypeError( + "'Meta.fields' must not contain non-model field names: %s" + % ', '.join(undefined) + ) + + # Add in declared filters. This is necessary since we don't enforce adding + # declared filters to the 'Meta.fields' option + filters.update(cls.declared_filters) + return filters + + @classmethod + def filter_for_field(cls, field, field_name, lookup_expr=None): + if lookup_expr is None: + lookup_expr = settings.DEFAULT_LOOKUP_EXPR + field, lookup_type = resolve_field(field, lookup_expr) + + default = { + 'field_name': field_name, + 'lookup_expr': lookup_expr, + } + + filter_class, params = cls.filter_for_lookup(field, lookup_type) + default.update(params) + + assert filter_class is not None, ( + "%s resolved field '%s' with '%s' lookup to an unrecognized field " + "type %s. Try adding an override to 'Meta.filter_overrides'. See: " + "https://django-filter.readthedocs.io/en/main/ref/filterset.html" + "#customise-filter-generation-with-filter-overrides" + ) % (cls.__name__, field_name, lookup_expr, field.__class__.__name__) + + return filter_class(**default) + + @classmethod + def filter_for_lookup(cls, field, lookup_type): + DEFAULTS = dict(cls.FILTER_DEFAULTS) + if hasattr(cls, '_meta'): + DEFAULTS.update(cls._meta.filter_overrides) + + data = try_dbfield(DEFAULTS.get, field.__class__) or {} + filter_class = data.get('filter_class') + params = data.get('extra', lambda field: {})(field) + + # if there is no filter class, exit early + if not filter_class: + return None, {} + + # perform lookup specific checks + if lookup_type == 'exact' and getattr(field, 'choices', None): + return ChoiceFilter, {'choices': field.choices} + + if lookup_type == 'isnull': + data = try_dbfield(DEFAULTS.get, models.BooleanField) + + filter_class = data.get('filter_class') + params = data.get('extra', lambda field: {})(field) + return filter_class, params + + if lookup_type == 'in': + class ConcreteInFilter(BaseInFilter, filter_class): + pass + ConcreteInFilter.__name__ = cls._csv_filter_class_name( + filter_class, lookup_type + ) + + return ConcreteInFilter, params + + if lookup_type == 'range': + class ConcreteRangeFilter(BaseRangeFilter, filter_class): + pass + ConcreteRangeFilter.__name__ = cls._csv_filter_class_name( + filter_class, lookup_type + ) + + return ConcreteRangeFilter, params + + return filter_class, params + + @classmethod + def _csv_filter_class_name(cls, filter_class, lookup_type): + """ + Generate a suitable class name for a concrete filter class. This is not + completely reliable, as not all filter class names are of the format + <Type>Filter. + + ex:: + + FilterSet._csv_filter_class_name(DateTimeFilter, 'in') + + returns 'DateTimeInFilter' + + """ + # DateTimeFilter => DateTime + type_name = filter_class.__name__ + if type_name.endswith('Filter'): + type_name = type_name[:-6] + + # in => In + lookup_name = lookup_type.capitalize() + + # DateTimeInFilter + return str('%s%sFilter' % (type_name, lookup_name)) + + +class FilterSet(BaseFilterSet, metaclass=FilterSetMetaclass): + pass + + +def filterset_factory(model, fields=ALL_FIELDS): + meta = type(str('Meta'), (object,), {'model': model, 'fields': fields}) + filterset = type(str('%sFilterSet' % model._meta.object_name), + (FilterSet,), {'Meta': meta}) + return filterset diff --git a/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000..b1b876f Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..d35fe14 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,189 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# FULL NAME <EMAIL@ADDRESS>, 2020. +# +#: conf.py:29 conf.py:30 conf.py:43 +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-03-24 00:19+0100\n" +"PO-Revision-Date: 2020-03-24 00:48+0100\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +"X-Generator: Gtranslator 2.91.7\n" + +#: conf.py:19 +msgid "date" +msgstr "تاريخ" + +#: conf.py:20 +msgid "year" +msgstr "سنة" + +#: conf.py:21 +msgid "month" +msgstr "شهر" + +#: conf.py:22 +msgid "day" +msgstr "يوم" + +#: conf.py:23 +msgid "week day" +msgstr "يوم الأسبوع" + +#: conf.py:24 +msgid "hour" +msgstr "ساعة" + +#: conf.py:25 +msgid "minute" +msgstr "دقيقة" + +#: conf.py:26 +msgid "second" +msgstr "ثانية" + +#: conf.py:31 conf.py:32 +msgid "contains" +msgstr "يحتوي على" + +#: conf.py:33 +msgid "is in" +msgstr "في داخل" + +#: conf.py:34 +msgid "is greater than" +msgstr "أكبر من" + +#: conf.py:35 +msgid "is greater than or equal to" +msgstr "أكبر من أو يساوي" + +#: conf.py:36 +msgid "is less than" +msgstr "أصغر من" + +#: conf.py:37 +msgid "is less than or equal to" +msgstr "أصغر من أو يساوي" + +#: conf.py:38 conf.py:39 +msgid "starts with" +msgstr "يبدأ ب" + +#: conf.py:40 conf.py:41 +msgid "ends with" +msgstr "ينتهي ب" + +#: conf.py:42 +msgid "is in range" +msgstr "في النطاق" + +#: conf.py:44 conf.py:45 +msgid "matches regex" +msgstr "يطابق التعبير العادي" + +#: conf.py:46 conf.py:54 +msgid "search" +msgstr "بحث" + +#: conf.py:49 +msgid "is contained by" +msgstr "موجود في" + +#: conf.py:50 +msgid "overlaps" +msgstr "يتداخل" + +#: conf.py:51 +msgid "has key" +msgstr "لديه مفتاح" + +#: conf.py:52 +msgid "has keys" +msgstr "لديه مفاتيح" + +#: conf.py:53 +msgid "has any keys" +msgstr "لديه أي مفاتيح" + +#: fields.py:106 +msgid "Select a lookup." +msgstr "حدد بحث" + +#: fields.py:198 +msgid "Range query expects two values." +msgstr "إستعلام النطاق يتوقع قيمتين" + +#: filters.py:402 +msgid "Today" +msgstr "اليوم" + +#: filters.py:403 +msgid "Yesterday" +msgstr "أمس" + +#: filters.py:404 +msgid "Past 7 days" +msgstr "الأيام السبعة الماضية" + +#: filters.py:405 +msgid "This month" +msgstr "هذا الشهر" + +#: filters.py:406 +msgid "This year" +msgstr "هذه السنة" + +#: filters.py:504 +msgid "Multiple values may be separated by commas." +msgstr "يمكن فصل القيم المتعددة بفواصل." + +#: filters.py:677 +#, python-format +msgid "%s (descending)" +msgstr "%s (تنازلي)" + +#: filters.py:693 +msgid "Ordering" +msgstr "الترتيب" + +#: rest_framework/filterset.py:31 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "إرسال" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "مرشحات الحقل" + +#: utils.py:294 +msgid "exclude" +msgstr "استبعاد" + +#: widgets.py:58 +msgid "All" +msgstr "كل" + +#: widgets.py:160 +msgid "Unknown" +msgstr "مجهول" + +#: widgets.py:161 +msgid "Yes" +msgstr "نعم" + +#: widgets.py:162 +msgid "No" +msgstr "لا" diff --git a/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.mo new file mode 100644 index 0000000..595dad9 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.po new file mode 100644 index 0000000..ebd0221 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/be/LC_MESSAGES/django.po @@ -0,0 +1,185 @@ +# +#: conf.py:27 conf.py:28 conf.py:41 +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-24 18:51+0500\n" +"PO-Revision-Date: 2016-09-29 11:47+0300\n" +"Last-Translator: Eugena Mikhaylikova <eugena.mihailikova@gmail.com>\n" +"Language-Team: TextTempearture\n" +"Language: be\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" +"X-Generator: Poedit 1.8.9\n" + +#: conf.py:17 +msgid "date" +msgstr "дата" + +#: conf.py:18 +msgid "year" +msgstr "год" + +#: conf.py:19 +msgid "month" +msgstr "месяц" + +#: conf.py:20 +msgid "day" +msgstr "дзень" + +#: conf.py:21 +msgid "week day" +msgstr "дзень тыдня" + +#: conf.py:22 +msgid "hour" +msgstr "гадзіну" + +#: conf.py:23 +msgid "minute" +msgstr "хвіліна" + +#: conf.py:24 +msgid "second" +msgstr "секунда" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "змяшчае" + +#: conf.py:31 +msgid "is in" +msgstr "у" + +#: conf.py:32 +msgid "is greater than" +msgstr "больш чым" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "больш або роўна" + +#: conf.py:34 +msgid "is less than" +msgstr "менш чым" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "менш або роўна" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "пачынаецца" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "заканчваецца" + +#: conf.py:40 +msgid "is in range" +msgstr "у дыяпазоне" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "адпавядае рэгулярнаму выразу" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "пошук" + +#: conf.py:47 +msgid "is contained by" +msgstr "змяшчаецца ў" + +#: conf.py:48 +msgid "overlaps" +msgstr "перакрываецца" + +#: conf.py:49 +msgid "has key" +msgstr "мае ключ" + +#: conf.py:50 +msgid "has keys" +msgstr "мае ключы" + +#: conf.py:51 +msgid "has any keys" +msgstr "мае любыя ключы" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Запыт дыяпазону чакае два значэння." + +#: filters.py:429 +msgid "Any date" +msgstr "Любая дата" + +#: filters.py:430 +msgid "Today" +msgstr "Сёння" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Мінулыя 7 дзён" + +#: filters.py:439 +msgid "This month" +msgstr "За гэты месяц" + +#: filters.py:443 +msgid "This year" +msgstr "У гэтым годзе" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Учора" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Некалькі значэнняў могуць быць падзеленыя коскамі." + +#: filters.py:591 +#, python-format +msgid "%s (descending)" +msgstr "%s (па змяншэнні)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Парадак" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Адправіць" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Фільтры па палях" + +#: utils.py:224 +msgid "exclude" +msgstr "выключаючы" + +#: widgets.py:57 +msgid "All" +msgstr "Усе" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Не было прапанавана" + +#: widgets.py:160 +msgid "Yes" +msgstr "Ды" + +#: widgets.py:161 +msgid "No" +msgstr "Няма" diff --git a/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.mo new file mode 100644 index 0000000..122fe4d Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.po new file mode 100644 index 0000000..9149eaa --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/bg/LC_MESSAGES/django.po @@ -0,0 +1,187 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Hristo Gatsinski <gatsinski@gmail.com>, 2019. +# +#: conf.py:27 conf.py:28 conf.py:41 +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-12-22 19:45+0200\n" +"PO-Revision-Date: 2019-12-21 19:36+0200\n" +"Last-Translator: Hristo Gatsinski <gatsinski@gmail.com>\n" +"Language-Team: \n" +"Language: bg\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.8.9\n" + +#: conf.py:17 +msgid "date" +msgstr "дата" + +#: conf.py:18 +msgid "year" +msgstr "година" + +#: conf.py:19 +msgid "month" +msgstr "месец" + +#: conf.py:20 +msgid "day" +msgstr "ден" + +#: conf.py:21 +msgid "week day" +msgstr "ден от седмицата" + +#: conf.py:22 +msgid "hour" +msgstr "час" + +#: conf.py:23 +msgid "minute" +msgstr "минута" + +#: conf.py:24 +msgid "second" +msgstr "секунда" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "съдържа" + +#: conf.py:31 +msgid "is in" +msgstr "в" + +#: conf.py:32 +msgid "is greater than" +msgstr "е по-голям от" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "е по-голям или равен на" + +#: conf.py:34 +msgid "is less than" +msgstr "е по-малък от" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "е по-малък или равен на" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "започва с" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "завършва с" + +#: conf.py:40 +msgid "is in range" +msgstr "е в диапазона" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "съвпада с регуларен израз" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "търсене" + +#: conf.py:47 +msgid "is contained by" +msgstr "се съдържа от" + +#: conf.py:48 +msgid "overlaps" +msgstr "припокрива" + +#: conf.py:49 +msgid "has key" +msgstr "има ключ" + +#: conf.py:50 +msgid "has keys" +msgstr "има ключове" + +#: conf.py:51 +msgid "has any keys" +msgstr "има който и да е ключ" + +#: fields.py:106 +msgid "Select a lookup." +msgstr "Изберете справка" + +#: fields.py:198 +msgid "Range query expects two values." +msgstr "Търсенето по диапазон изисква две стойности" + +#: filters.py:406 +msgid "Today" +msgstr "Днес" + +#: filters.py:407 +msgid "Yesterday" +msgstr "Вчера" + +#: filters.py:408 +msgid "Past 7 days" +msgstr "Последните 7 дни" + +#: filters.py:409 +msgid "This month" +msgstr "Този месец" + +#: filters.py:410 +msgid "This year" +msgstr "Тази година" + +#: filters.py:508 +msgid "Multiple values may be separated by commas." +msgstr "Множество стойности може да се разделят със запетая" + +#: filters.py:681 +#, python-format +msgid "%s (descending)" +msgstr "%s (намалавящ)" + +#: filters.py:697 +msgid "Ordering" +msgstr "Подредба" + +#: rest_framework/filterset.py:31 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Изпращане" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Филтри на полетата" + +#: utils.py:298 +msgid "exclude" +msgstr "изключва" + +#: widgets.py:57 +msgid "All" +msgstr "Всичко" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Неизвестен" + +#: widgets.py:160 +msgid "Yes" +msgstr "Да" + +#: widgets.py:161 +msgid "No" +msgstr "Не" diff --git a/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.mo new file mode 100644 index 0000000..54a8915 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.po new file mode 100644 index 0000000..40930a6 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/cs/LC_MESSAGES/django.po @@ -0,0 +1,182 @@ +# +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-24 11:03+0500\n" +"PO-Revision-Date: 2016-09-29 11:47+0300\n" +"Last-Translator: Eugena Mikhaylikova <eugena.mihailikova@gmail.com>\n" +"Language-Team: TextTempearture\n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " +"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" +"X-Generator: Poedit 1.8.9\n" + +#: conf.py:17 +msgid "date" +msgstr "datum" + +#: conf.py:18 +msgid "year" +msgstr "rok" + +#: conf.py:19 +msgid "month" +msgstr "měsíc" + +#: conf.py:20 +msgid "day" +msgstr "den" + +#: conf.py:21 +msgid "week day" +msgstr "den v týdnu" + +#: conf.py:22 +msgid "hour" +msgstr "hodinu" + +#: conf.py:23 +msgid "minute" +msgstr "minutu" + +#: conf.py:24 +msgid "second" +msgstr "vteřina" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "obsahuje" + +#: conf.py:31 +msgid "is in" +msgstr "v" + +#: conf.py:32 +msgid "is greater than" +msgstr "více než" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "větší nebo roven" + +#: conf.py:34 +msgid "is less than" +msgstr "méně než" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "menší nebo rovné" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "začíná" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "končí" + +#: conf.py:40 +msgid "is in range" +msgstr "v rozsahu" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "odpovídá normálnímu výrazu" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "vyhledávání" + +#: conf.py:47 +msgid "is contained by" +msgstr "je obsažen v" + +#: conf.py:48 +msgid "overlaps" +msgstr "překrývají" + +#: conf.py:49 +msgid "has key" +msgstr "má klíč" + +#: conf.py:50 +msgid "has keys" +msgstr "má klíče" + +#: conf.py:51 +msgid "has any keys" +msgstr "má nějaké klíče" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Rozsah dotazu očekává dvě hodnoty." + +#: filters.py:429 +msgid "Any date" +msgstr "Jakékoliv datum" + +#: filters.py:430 +msgid "Today" +msgstr "Dnes" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Posledních 7 dní" + +#: filters.py:439 +msgid "This month" +msgstr "Tento měsíc" + +#: filters.py:443 +msgid "This year" +msgstr "Tento rok" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Včera" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Více hodnot lze oddělit čárkami." + +#: filters.py:591 +msgid "%s (descending)" +msgstr "%s (sestupně)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Řád z" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Odeslat" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Filtry na polích" + +#: utils.py:224 +msgid "exclude" +msgstr "s výjimkou" + +#: widgets.py:57 +msgid "All" +msgstr "Všechno" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Není nastaveno" + +#: widgets.py:160 +msgid "Yes" +msgstr "Ano" + +#: widgets.py:161 +msgid "No" +msgstr "Ne" diff --git a/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.mo new file mode 100644 index 0000000..84f7e94 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.po new file mode 100644 index 0000000..6260b3f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/da/LC_MESSAGES/django.po @@ -0,0 +1,181 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.0.1\n" +"Project-Id-Version: django-filter\n" +"Language: da\n" +"Last-Translator: Danni Randeris <danni@danniranderis.dk>\n" +"Language-Team: Danni Randeris <danni@danniranderis.dk>\n" +"POT-Creation-Date: 2017-10-28\n" +"PO-Revision-Date: 2017-10-28\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: conf.py:17 +msgid "date" +msgstr "dato" + +#: conf.py:18 +msgid "year" +msgstr "år" + +#: conf.py:19 +msgid "month" +msgstr "måned" + +#: conf.py:20 +msgid "day" +msgstr "dag" + +#: conf.py:21 +msgid "week day" +msgstr "ugedag" + +#: conf.py:22 +msgid "hour" +msgstr "time" + +#: conf.py:23 +msgid "minute" +msgstr "minut" + +#: conf.py:24 +msgid "second" +msgstr "sekund" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "indeholder" + +#: conf.py:31 +msgid "is in" +msgstr "er i" + +#: conf.py:32 +msgid "is greater than" +msgstr "er større end" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "er større end eller lig med" + +#: conf.py:34 +msgid "is less than" +msgstr "er mindre end" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "er mindre end eller lig med" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "starter med" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "slutter med" + +#: conf.py:40 +msgid "is in range" +msgstr "er i intervallet" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "matcher regex" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "søg" + +#: conf.py:47 +msgid "is contained by" +msgstr "er indeholdt af" + +#: conf.py:48 +msgid "overlaps" +msgstr "overlapper" + +#: conf.py:49 +msgid "has key" +msgstr "har string" + +#: conf.py:50 +msgid "has keys" +msgstr "har stringe" + +#: conf.py:51 +msgid "has any keys" +msgstr "har hvilken som helst string" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Interval forespørgslen forventer to værdier." + +#: filters.py:429 +msgid "Any date" +msgstr "Hvilken som helst dag" + +#: filters.py:430 +msgid "Today" +msgstr "I dag" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Sidste 7 dage" + +#: filters.py:439 +msgid "This month" +msgstr "Denne måned" + +#: filters.py:443 +msgid "This year" +msgstr "Dette år" + +#: filters.py:446 +msgid "Yesterday" +msgstr "I går" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Flere værdier kan adskilles via komma." + +#: filters.py:591 +msgid "%s (descending)" +msgstr "%s (aftagende)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Sortering" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +#, fuzzy +msgid "Submit" +msgstr "Indsend" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +#, fuzzy +msgid "Field filters" +msgstr "Felt filtre" + +#: utils.py:224 +msgid "exclude" +msgstr "udelad" + +#: widgets.py:57 +msgid "All" +msgstr "Alle" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Ukendt" + +#: widgets.py:160 +msgid "Yes" +msgstr "Ja" + +#: widgets.py:161 +msgid "No" +msgstr "Nej" diff --git a/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000..9a48a0a Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..428a2ff --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,187 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#: conf.py:27 conf.py:28 conf.py:41 +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-24 11:03+0500\n" +"PO-Revision-Date: 2013-08-10 12:29+0100\n" +"Last-Translator: Florian Apolloner <florian@apolloner.eu>\n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.5.4\n" + +#: conf.py:17 +msgid "date" +msgstr "Datum" + +#: conf.py:18 +msgid "year" +msgstr "Jahr" + +#: conf.py:19 +msgid "month" +msgstr "Monat" + +#: conf.py:20 +msgid "day" +msgstr "Tag" + +#: conf.py:21 +msgid "week day" +msgstr "Wochentag" + +#: conf.py:22 +msgid "hour" +msgstr "Stunde" + +#: conf.py:23 +msgid "minute" +msgstr "Minute" + +#: conf.py:24 +msgid "second" +msgstr "Sekunde" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "enthält" + +#: conf.py:31 +msgid "is in" +msgstr "ist in" + +#: conf.py:32 +msgid "is greater than" +msgstr "ist größer als" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "ist größer oder gleich" + +#: conf.py:34 +msgid "is less than" +msgstr "ist kleiner als" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "ist kleiner oder gleich" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "beginnt mit" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "endet mit" + +#: conf.py:40 +msgid "is in range" +msgstr "ist im Bereich" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "passt auf Regex" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "Suche" + +#: conf.py:47 +msgid "is contained by" +msgstr "ist enthalten in" + +#: conf.py:48 +msgid "overlaps" +msgstr "überlappen" + +#: conf.py:49 +msgid "has key" +msgstr "hat Schlüssel" + +#: conf.py:50 +msgid "has keys" +msgstr "hat Schlüssel" + +#: conf.py:51 +msgid "has any keys" +msgstr "hat beliebige Schlüssel" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Die Bereichsabfrage erwartet zwei Werte." + +#: filters.py:429 +msgid "Any date" +msgstr "Alle Daten" + +#: filters.py:430 +msgid "Today" +msgstr "Heute" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Letzte 7 Tage" + +#: filters.py:439 +msgid "This month" +msgstr "Diesen Monat" + +#: filters.py:443 +msgid "This year" +msgstr "Dieses Jahr" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Gestern" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Mehrere Werte können durch Kommas getrennt sein." + +#: filters.py:591 +#, python-format +msgid "%s (descending)" +msgstr "%s (absteigend)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Sortierung" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Absenden" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Feldfilter" + +#: utils.py:224 +msgid "exclude" +msgstr "ausschließen" + +#: widgets.py:57 +msgid "All" +msgstr "Alle" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Unbekannte" + +#: widgets.py:160 +msgid "Yes" +msgstr "Ja" + +#: widgets.py:161 +msgid "No" +msgstr "Nein" diff --git a/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4e2258b Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.po new file mode 100644 index 0000000..0018a2c --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/el/LC_MESSAGES/django.po @@ -0,0 +1,187 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Serafeim Papastefanos <spapas@gmail.com>, 2017. +# +#: .\conf.py:27 .\conf.py:28 .\conf.py:41 +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-16 10:06+0200\n" +"PO-Revision-Date: 2017-11-16 10:04+0200\n" +"Last-Translator: Serafeim Papastefanos <spapas@gmail.com>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.6.5\n" + +#: .\conf.py:17 +msgid "date" +msgstr "ημερομηνία" + +#: .\conf.py:18 +msgid "year" +msgstr "έτος" + +#: .\conf.py:19 +msgid "month" +msgstr "μήνας" + +#: .\conf.py:20 +msgid "day" +msgstr "ημέρα" + +#: .\conf.py:21 +msgid "week day" +msgstr "ημέρα της εβδομάδας" + +#: .\conf.py:22 +msgid "hour" +msgstr "ώρα" + +#: .\conf.py:23 +msgid "minute" +msgstr "λεπτό" + +#: .\conf.py:24 +msgid "second" +msgstr "δευτερόλεπτο" + +#: .\conf.py:29 .\conf.py:30 +msgid "contains" +msgstr "περιέχει" + +#: .\conf.py:31 +msgid "is in" +msgstr "είναι εντός των" + +#: .\conf.py:32 +msgid "is greater than" +msgstr "είναι μεγαλύτερο από" + +#: .\conf.py:33 +msgid "is greater than or equal to" +msgstr "είναι μεγαλύτερο ή ίσο του" + +#: .\conf.py:34 +msgid "is less than" +msgstr "είναι μικρότερο από" + +#: .\conf.py:35 +msgid "is less than or equal to" +msgstr "είναι μικρότερο ή ίσο του" + +#: .\conf.py:36 .\conf.py:37 +msgid "starts with" +msgstr "ξεκινά με" + +#: .\conf.py:38 .\conf.py:39 +msgid "ends with" +msgstr "τελειώνει με" + +#: .\conf.py:40 +msgid "is in range" +msgstr "είναι εντος του εύρους" + +#: .\conf.py:42 .\conf.py:43 +msgid "matches regex" +msgstr "περιέχει regex" + +#: .\conf.py:44 .\conf.py:52 +msgid "search" +msgstr "αναζήτηση" + +#: .\conf.py:47 +msgid "is contained by" +msgstr "περιέχεται σε" + +#: .\conf.py:48 +msgid "overlaps" +msgstr "επικαλύπτεται" + +#: .\conf.py:49 +msgid "has key" +msgstr "έχει το κλειδί" + +#: .\conf.py:50 +msgid "has keys" +msgstr "έχει τα κλειδιά" + +#: .\conf.py:51 +msgid "has any keys" +msgstr "έχει οποιαδήποτε κλειδιά" + +#: .\fields.py:178 +msgid "Range query expects two values." +msgstr "Το ερώτημα εύρους απαιτεί δύο τιμές," + +#: .\filters.py:429 +msgid "Any date" +msgstr "Οποιαδήποτε ημερομηνία" + +#: .\filters.py:430 +msgid "Today" +msgstr "Σήμερα" + +#: .\filters.py:435 +msgid "Past 7 days" +msgstr "Τις προηγούμενες 7 ημέρες" + +#: .\filters.py:439 +msgid "This month" +msgstr "Αυτό το μήνα" + +#: .\filters.py:443 +msgid "This year" +msgstr "Αυτό το έτος" + +#: .\filters.py:446 +msgid "Yesterday" +msgstr "Χτες" + +#: .\filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Οι πολλαπλές τιμές πρέπει να διαχωρίζονται με κόμμα." + +#: .\filters.py:591 +#, python-format +msgid "%s (descending)" +msgstr "%s (φθίνουσα" + +#: .\filters.py:607 +msgid "Ordering" +msgstr "Ταξινόμηση" + +#: .\rest_framework\filterset.py:30 +#: .\templates\django_filters\rest_framework\form.html:5 +msgid "Submit" +msgstr "Υποβολή" + +#: .\templates\django_filters\rest_framework\crispy_form.html:4 +#: .\templates\django_filters\rest_framework\form.html:2 +msgid "Field filters" +msgstr "Φίλτρα πεδίων" + +#: .\utils.py:224 +msgid "exclude" +msgstr "απέκλεισε" + +#: .\widgets.py:57 +msgid "All" +msgstr "Όλα" + +#: .\widgets.py:159 +msgid "Unknown" +msgstr "Άγνωστο" + +#: .\widgets.py:160 +msgid "Yes" +msgstr "Ναι" + +#: .\widgets.py:161 +msgid "No" +msgstr "Όχι" diff --git a/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000..3338ff7 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..5c4647f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,190 @@ +# Django Filter translation. +# Copyright (C) 2013 +# This file is distributed under the same license as the django_filter package. +# Carlos Goce, 2017. +# Nicolás Stuardo, 2020 +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-12-12 17:58-0300\n" +"PO-Revision-Date: 2020-12-12 17:57-0300\n" +"Last-Translator: Nicolás Stuardo\n" +"Language-Team: Spanish (España)\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.4.2\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: conf.py:19 +msgid "date" +msgstr "fecha" + +#: conf.py:20 +msgid "year" +msgstr "año" + +#: conf.py:21 +msgid "month" +msgstr "mes" + +#: conf.py:22 +msgid "day" +msgstr "día" + +#: conf.py:23 +msgid "week day" +msgstr "día de la semana" + +#: conf.py:24 +msgid "hour" +msgstr "hora" + +#: conf.py:25 +msgid "minute" +msgstr "minuto" + +#: conf.py:26 +msgid "second" +msgstr "segundo" + +#: conf.py:31 conf.py:32 +msgid "contains" +msgstr "contiene" + +#: conf.py:33 +msgid "is in" +msgstr "presente en" + +#: conf.py:34 +msgid "is greater than" +msgstr "mayor que" + +#: conf.py:35 +msgid "is greater than or equal to" +msgstr "mayor o igual que" + +#: conf.py:36 +msgid "is less than" +msgstr "menor que" + +#: conf.py:37 +msgid "is less than or equal to" +msgstr "menor o igual que" + +#: conf.py:38 conf.py:39 +msgid "starts with" +msgstr "comienza por" + +#: conf.py:40 conf.py:41 +msgid "ends with" +msgstr "termina por" + +#: conf.py:42 +msgid "is in range" +msgstr "en el rango" + +#: conf.py:44 conf.py:45 +msgid "matches regex" +msgstr "coincide con la expresión regular" + +#: conf.py:46 conf.py:54 +msgid "search" +msgstr "buscar" + +#: conf.py:49 +msgid "is contained by" +msgstr "contenido en" + +#: conf.py:50 +msgid "overlaps" +msgstr "solapado" + +#: conf.py:51 +msgid "has key" +msgstr "contiene la clave" + +#: conf.py:52 +msgid "has keys" +msgstr "contiene las claves" + +#: conf.py:53 +msgid "has any keys" +msgstr "contiene alguna de las claves" + +#: fields.py:106 +msgid "Select a lookup." +msgstr "Seleccione un operador de consulta." + +#: fields.py:198 +msgid "Range query expects two values." +msgstr "Consultar un rango requiere dos valores." + +#: filters.py:420 +msgid "Today" +msgstr "Hoy" + +#: filters.py:421 +msgid "Yesterday" +msgstr "Ayer" + +#: filters.py:422 +msgid "Past 7 days" +msgstr "Últimos 7 días" + +#: filters.py:423 +msgid "This month" +msgstr "Este mes" + +#: filters.py:424 +msgid "This year" +msgstr "Este año" + +#: filters.py:522 +msgid "Multiple values may be separated by commas." +msgstr "Múltiples valores separados por comas." + +#: filters.py:695 +#, python-format +msgid "%s (descending)" +msgstr "%s (descendente)" + +#: filters.py:711 +msgid "Ordering" +msgstr "Ordenado" + +#: rest_framework/filterset.py:31 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Enviar" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Filtros de campo" + +#: utils.py:294 +msgid "exclude" +msgstr "excluye" + +#: widgets.py:58 +msgid "All" +msgstr "Todo" + +#: widgets.py:160 +msgid "Unknown" +msgstr "Desconocido" + +#: widgets.py:161 +msgid "Yes" +msgstr "Sí" + +#: widgets.py:162 +msgid "No" +msgstr "No" + +#~ msgid "Any date" +#~ msgstr "Cualquier fecha" diff --git a/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..7f4778a Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.po new file mode 100644 index 0000000..751dc8f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/es_AR/LC_MESSAGES/django.po @@ -0,0 +1,47 @@ +# Django Filter translation. +# Copyright (C) 2013 +# This file is distributed under the same license as the django_filter package. +# Gonzalo Bustos, 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-05 19:24+0200\n" +"PO-Revision-Date: 2015-10-11 20:53-0300\n" +"Last-Translator: Gonzalo Bustos\n" +"Language-Team: Spanish (Argentina)\n" +"Language: es_AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.6.10\n" + +#: filters.py:51 +msgid "This is an exclusion filter" +msgstr "Este es un filtro de exclusión" + +#: filters.py:158 +msgid "Any date" +msgstr "Cualquier fecha" + +#: filters.py:159 +msgid "Today" +msgstr "Hoy" + +#: filters.py:164 +msgid "Past 7 days" +msgstr "Últimos 7 días" + +#: filters.py:168 +msgid "This month" +msgstr "Este mes" + +#: filters.py:172 +msgid "This year" +msgstr "Este año" + +#: widgets.py:63 +msgid "All" +msgstr "Todos" diff --git a/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000..1b16915 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..8926219 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,47 @@ +# Django Filter translation. +# Copyright (C) 2013 +# This file is distributed under the same license as the django_filter package. +# Axel Haustant <noirbizarre@gmail.com>, 2013. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-05 19:24+0200\n" +"PO-Revision-Date: 2013-07-05 19:24+0200\n" +"Last-Translator: Axel Haustant <noirbizarre@gmail.com>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: filters.py:51 +msgid "This is an exclusion filter" +msgstr "Ceci est un filtre d'exclusion" + +#: filters.py:158 +msgid "Any date" +msgstr "Toutes les dates" + +#: filters.py:159 +msgid "Today" +msgstr "Aujourd'hui" + +#: filters.py:164 +msgid "Past 7 days" +msgstr "7 derniers jours" + +#: filters.py:168 +msgid "This month" +msgstr "Ce mois-ci" + +#: filters.py:172 +msgid "This year" +msgstr "Cette année" + +#: widgets.py:63 +msgid "All" +msgstr "Tous" diff --git a/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000..f53c4ea Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000..eb16bf2 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,186 @@ +# Django Filter translation. +# Copyright (C) 2013 +# This file is distributed under the same license as the django_filter package. +# Carlos Goce, 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-26 20:32+0100\n" +"PO-Revision-Date: 2017-01-26 20:52+0100\n" +"Last-Translator: Carlos Goce\n" +"Language-Team: Spanish (España)\n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.11\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: conf.py:26 +msgid "date" +msgstr "data" + +#: conf.py:27 +msgid "year" +msgstr "anno" + +#: conf.py:28 +msgid "month" +msgstr "mese" + +#: conf.py:29 +msgid "day" +msgstr "giorno" + +#: conf.py:30 +msgid "week day" +msgstr "giorno della settimana" + +#: conf.py:31 +msgid "hour" +msgstr "ora" + +#: conf.py:32 +msgid "minute" +msgstr "minuto" + +#: conf.py:33 +msgid "second" +msgstr "secondo" + +#: conf.py:38 conf.py:39 +msgid "contains" +msgstr "contiene" + +#: conf.py:40 +msgid "is in" +msgstr "presente in" + +#: conf.py:41 +msgid "is greater than" +msgstr "maggiore di" + +#: conf.py:42 +msgid "is greater than or equal to" +msgstr "maggiore o uguale di" + +#: conf.py:43 +msgid "is less than" +msgstr "minore di" + +#: conf.py:44 +msgid "is less than or equal to" +msgstr "minore o uguale di" + +#: conf.py:45 conf.py:46 +msgid "starts with" +msgstr "comincia per" + +#: conf.py:47 conf.py:48 +msgid "ends with" +msgstr "termina per" + +#: conf.py:49 +msgid "is in range" +msgstr "nell'intervallo" + +#: conf.py:51 conf.py:52 +msgid "matches regex" +msgstr "coincide con la espressione regolare" + +#: conf.py:53 conf.py:61 +msgid "search" +msgstr "cerca" + +#: conf.py:56 +msgid "is contained by" +msgstr "contenuto in" + +#: conf.py:57 +msgid "overlaps" +msgstr "sovrapposto" + +#: conf.py:58 +msgid "has key" +msgstr "contiene la chiave" + +#: conf.py:59 +msgid "has keys" +msgstr "contiene le chiavi" + +#: conf.py:60 +msgid "has any keys" +msgstr "contiene qualsiasi chiave" + +#: fields.py:167 +msgid "Range query expects two values." +msgstr "La query di intervallo richiede due valori" + +#: filters.py:443 +msgid "Any date" +msgstr "Qualsiasi data" + +#: filters.py:444 +msgid "Today" +msgstr "Oggi" + +#: filters.py:449 +msgid "Past 7 days" +msgstr "Ultimi 7 giorni" + +#: filters.py:453 +msgid "This month" +msgstr "Questo mese" + +#: filters.py:457 +msgid "This year" +msgstr "Questo anno" + +#: filters.py:460 +msgid "Yesterday" +msgstr "Ieri" + +#: filters.py:526 +msgid "Multiple values may be separated by commas." +msgstr "Più valori separati da virgole." + +#: filters.py:605 +#, python-format +msgid "%s (descending)" +msgstr "%s (decrescente)" + +#: filters.py:621 +msgid "Ordering" +msgstr "Ordinamento" + +#: utils.py:220 +msgid "exclude" +msgstr "escludi" + +#: widgets.py:71 +msgid "All" +msgstr "Tutti" + +#: widgets.py:119 +msgid "Unknown" +msgstr "Sconosciuto" + +#: widgets.py:120 +msgid "Yes" +msgstr "Sí" + +#: widgets.py:121 +msgid "No" +msgstr "No" + +#: rest_framework/filterset.py:31 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Invia" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Filtri del campo" diff --git a/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..fdbf09d Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000..11abbad --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,202 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#: conf.py:35 conf.py:36 conf.py:49 +msgid "" +msgstr "" +"Project-Id-Version: django_filters 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-09-01 17:21+0000\n" +"PO-Revision-Date: 2015-07-25 01:27+0100\n" +"Last-Translator: Adam Dobrawy <naczelnik@jawnosc.tk>\n" +"Language-Team: Adam Dobrawy <naczelnik@jawnosc.tk>\n" +"Language: pl_PL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n" +"%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n" +"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" +"X-Generator: Poedit 1.5.4\n" + +#: conf.py:25 +#, fuzzy +#| msgid "Any date" +msgid "date" +msgstr "Dowolna data" + +#: conf.py:26 +#, fuzzy +#| msgid "This year" +msgid "year" +msgstr "Ten rok" + +#: conf.py:27 +#, fuzzy +#| msgid "This month" +msgid "month" +msgstr "Ten miesiąc" + +#: conf.py:28 +#, fuzzy +#| msgid "Today" +msgid "day" +msgstr "Dziś" + +#: conf.py:29 +msgid "week day" +msgstr "dzień tygodnia" + +#: conf.py:30 +msgid "hour" +msgstr "godzina" + +#: conf.py:31 +msgid "minute" +msgstr "minuta" + +#: conf.py:32 +msgid "second" +msgstr "" + +#: conf.py:37 conf.py:38 +msgid "contains" +msgstr "zawiera" + +#: conf.py:39 +msgid "is in" +msgstr "zawiera się w" + +#: conf.py:40 +msgid "is greater than" +msgstr "powyżej" + +#: conf.py:41 +msgid "is greater than or equal to" +msgstr "powyżej lub równe" + +#: conf.py:42 +msgid "is less than" +msgstr "poniżej" + +#: conf.py:43 +msgid "is less than or equal to" +msgstr "poniżej lub równe" + +#: conf.py:44 conf.py:45 +msgid "starts with" +msgstr "zaczyna się od" + +#: conf.py:46 conf.py:47 +msgid "ends with" +msgstr "kończy się na" + +#: conf.py:48 +msgid "is in range" +msgstr "zawiera się w zakresie" + +#: conf.py:50 conf.py:51 +msgid "matches regex" +msgstr "pasuje do wyrażenia regularnego" + +#: conf.py:52 conf.py:60 +msgid "search" +msgstr "szukaj" + +#: conf.py:55 +msgid "is contained by" +msgstr "zawiera się w" + +#: conf.py:56 +msgid "overlaps" +msgstr "" + +#: conf.py:57 +msgid "has key" +msgstr "" + +#: conf.py:58 +msgid "has keys" +msgstr "" + +#: conf.py:59 +msgid "has any keys" +msgstr "" + +#: fields.py:172 +msgid "Range query expects two values." +msgstr "" + +#: filters.py:452 +msgid "Any date" +msgstr "Dowolna data" + +#: filters.py:453 +msgid "Today" +msgstr "Dziś" + +#: filters.py:458 +msgid "Past 7 days" +msgstr "Ostatnie 7 dni" + +#: filters.py:462 +msgid "This month" +msgstr "Ten miesiąc" + +#: filters.py:466 +msgid "This year" +msgstr "Ten rok" + +#: filters.py:469 +msgid "Yesterday" +msgstr "Wczoraj" + +#: filters.py:535 +msgid "Multiple values may be separated by commas." +msgstr "Wiele wartości można rozdzielić przecinkami" + +#: filters.py:614 +#, python-format +msgid "%s (descending)" +msgstr "%s (malejąco)" + +#: filters.py:630 +msgid "Ordering" +msgstr "Sortowanie" + +#: rest_framework/filterset.py:34 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +#, fuzzy +#| msgid "Filter" +msgid "Field filters" +msgstr "Filter" + +#: utils.py:225 +msgid "exclude" +msgstr "" + +#: widgets.py:66 +msgid "All" +msgstr "Wszystko" + +#: widgets.py:173 +msgid "Unknown" +msgstr "" + +#: widgets.py:174 +msgid "Yes" +msgstr "Tak" + +#: widgets.py:175 +msgid "No" +msgstr "Nie" + +#~ msgid "This is an exclusion filter" +#~ msgstr "Jest to filtr wykluczający" diff --git a/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..e9cc1a8 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000..6f8eb0f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,186 @@ +# Django Filter translation. +# Copyright (C) 2017 +# This file is distributed under the same license as the django_filter package. +# Anderson Scouto da Silva, 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-12-11 22:04+0100\n" +"PO-Revision-Date: 2017-12-11 22:07-0200\n" +"Last-Translator: Anderson Scouto da Silva\n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.13\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: conf.py:26 +msgid "date" +msgstr "data" + +#: conf.py:27 +msgid "year" +msgstr "ano" + +#: conf.py:28 +msgid "month" +msgstr "mês" + +#: conf.py:29 +msgid "day" +msgstr "dia" + +#: conf.py:30 +msgid "week day" +msgstr "dia da semana" + +#: conf.py:31 +msgid "hour" +msgstr "hora" + +#: conf.py:32 +msgid "minute" +msgstr "minuto" + +#: conf.py:33 +msgid "second" +msgstr "segundo" + +#: conf.py:38 conf.py:39 +msgid "contains" +msgstr "contém" + +#: conf.py:40 +msgid "is in" +msgstr "presente em" + +#: conf.py:41 +msgid "is greater than" +msgstr "é maior que" + +#: conf.py:42 +msgid "is greater than or equal to" +msgstr "é maior ou igual que" + +#: conf.py:43 +msgid "is less than" +msgstr "é menor que" + +#: conf.py:44 +msgid "is less than or equal to" +msgstr "é menor ou igual que" + +#: conf.py:45 conf.py:46 +msgid "starts with" +msgstr "começa com" + +#: conf.py:47 conf.py:48 +msgid "ends with" +msgstr "termina com" + +#: conf.py:49 +msgid "is in range" +msgstr "está no range" + +#: conf.py:51 conf.py:52 +msgid "matches regex" +msgstr "coincide com a expressão regular" + +#: conf.py:53 conf.py:61 +msgid "search" +msgstr "buscar" + +#: conf.py:56 +msgid "is contained by" +msgstr "está contido por" + +#: conf.py:57 +msgid "overlaps" +msgstr "sobrepõe" + +#: conf.py:58 +msgid "has key" +msgstr "contém a chave" + +#: conf.py:59 +msgid "has keys" +msgstr "contém as chaves" + +#: conf.py:60 +msgid "has any keys" +msgstr "contém uma das chaves" + +#: fields.py:167 +msgid "Range query expects two values." +msgstr "Consulta por range requer dois valores." + +#: filters.py:443 +msgid "Any date" +msgstr "Qualquer data" + +#: filters.py:444 +msgid "Today" +msgstr "Hoje" + +#: filters.py:449 +msgid "Past 7 days" +msgstr "Últimos 7 dias" + +#: filters.py:453 +msgid "This month" +msgstr "Este mês" + +#: filters.py:457 +msgid "This year" +msgstr "Este ano" + +#: filters.py:460 +msgid "Yesterday" +msgstr "Ontem" + +#: filters.py:526 +msgid "Multiple values may be separated by commas." +msgstr "Valores múltiplos podem ser separados por vírgulas." + +#: filters.py:605 +#, python-format +msgid "%s (descending)" +msgstr "%s (decrescente)" + +#: filters.py:621 +msgid "Ordering" +msgstr "Ordenado" + +#: utils.py:220 +msgid "exclude" +msgstr "excluir" + +#: widgets.py:71 +msgid "All" +msgstr "Tudo" + +#: widgets.py:119 +msgid "Unknown" +msgstr "Desconhecido" + +#: widgets.py:120 +msgid "Yes" +msgstr "Sim" + +#: widgets.py:121 +msgid "No" +msgstr "Não" + +#: rest_framework/filterset.py:31 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Enviar" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Filtros de campo" diff --git a/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000..a4ff1ce Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..4b54cb5 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,189 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#: conf.py:27 conf.py:28 conf.py:41 +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-24 11:03+0500\n" +"PO-Revision-Date: 2016-09-29 11:47+0300\n" +"Last-Translator: Mikhail Mitrofanov <mm@elec.ru>\n" +"Language-Team: \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" +"X-Generator: Poedit 1.8.9\n" + +#: conf.py:17 +msgid "date" +msgstr "дата" + +#: conf.py:18 +msgid "year" +msgstr "год" + +#: conf.py:19 +msgid "month" +msgstr "месяц" + +#: conf.py:20 +msgid "day" +msgstr "день" + +#: conf.py:21 +msgid "week day" +msgstr "день недели" + +#: conf.py:22 +msgid "hour" +msgstr "час" + +#: conf.py:23 +msgid "minute" +msgstr "минута" + +#: conf.py:24 +msgid "second" +msgstr "секунда" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "содержит" + +#: conf.py:31 +msgid "is in" +msgstr "в" + +#: conf.py:32 +msgid "is greater than" +msgstr "больше чем" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "больше или равно" + +#: conf.py:34 +msgid "is less than" +msgstr "меньше чем" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "меньше или равно" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "начинается" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "заканчивается" + +#: conf.py:40 +msgid "is in range" +msgstr "в диапазоне" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "соответствует регулярному выражению" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "поиск" + +#: conf.py:47 +msgid "is contained by" +msgstr "содержится в" + +#: conf.py:48 +msgid "overlaps" +msgstr "перекрывается" + +#: conf.py:49 +msgid "has key" +msgstr "имеет ключ" + +#: conf.py:50 +msgid "has keys" +msgstr "имеет ключи" + +#: conf.py:51 +msgid "has any keys" +msgstr "имеет любые ключи" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Запрос диапазона ожидает два значения." + +#: filters.py:429 +msgid "Any date" +msgstr "Любая дата" + +#: filters.py:430 +msgid "Today" +msgstr "Сегодня" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Прошедшие 7 дней" + +#: filters.py:439 +msgid "This month" +msgstr "За этот месяц" + +#: filters.py:443 +msgid "This year" +msgstr "В этом году" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Вчера" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Несколько значений могут быть разделены запятыми." + +#: filters.py:591 +#, python-format +msgid "%s (descending)" +msgstr "%s (по убыванию)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Порядок" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Отправить" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Фильтры по полям" + +#: utils.py:224 +msgid "exclude" +msgstr "исключая" + +#: widgets.py:57 +msgid "All" +msgstr "Все" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Не задано" + +#: widgets.py:160 +msgid "Yes" +msgstr "Да" + +#: widgets.py:161 +msgid "No" +msgstr "Нет" diff --git a/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.mo new file mode 100644 index 0000000..d9c67d8 Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.po new file mode 100644 index 0000000..db19f3c --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/sk/LC_MESSAGES/django.po @@ -0,0 +1,188 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-25 19:11+0200\n" +"PO-Revision-Date: 2018-03-25 19:18+0058\n" +"Last-Translator: b'Erik Telepovsky <erik@pragmaticmates.com>'\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " +">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" +"X-Translated-Using: django-rosetta 0.8.1\n" + +#: conf.py:17 +msgid "date" +msgstr "dátum" + +#: conf.py:18 +msgid "year" +msgstr "rok" + +#: conf.py:19 +msgid "month" +msgstr "mesiac" + +#: conf.py:20 +msgid "day" +msgstr "deň" + +#: conf.py:21 +msgid "week day" +msgstr "deň týždňa" + +#: conf.py:22 +msgid "hour" +msgstr "hodina" + +#: conf.py:23 +msgid "minute" +msgstr "minúta" + +#: conf.py:24 +msgid "second" +msgstr "sekunda" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "obsahuje" + +#: conf.py:31 +msgid "is in" +msgstr "je v" + +#: conf.py:32 +msgid "is greater than" +msgstr "je vačší než" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "je vačší alebo rovný ako" + +#: conf.py:34 +msgid "is less than" +msgstr "je menší než" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "je menší alebo rovný ako" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "začína s" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "končí s" + +#: conf.py:40 +msgid "is in range" +msgstr "je v rozsahu" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "spĺňa regex" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "hľadať" + +#: conf.py:47 +msgid "is contained by" +msgstr "je obsiahnutý" + +#: conf.py:48 +msgid "overlaps" +msgstr "presahuje" + +#: conf.py:49 +msgid "has key" +msgstr "má kľúč" + +#: conf.py:50 +msgid "has keys" +msgstr "má kľúče" + +#: conf.py:51 +msgid "has any keys" +msgstr "má akékoľvek kľúče" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Rozsah očakáva dve hodnoty." + +#: filters.py:429 +msgid "Any date" +msgstr "Akýkoľvek dátum" + +#: filters.py:430 +msgid "Today" +msgstr "Dnes" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Posledných 7 dní" + +#: filters.py:439 +msgid "This month" +msgstr "Tento mesiac" + +#: filters.py:443 +msgid "This year" +msgstr "Tento rok" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Včera" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Viacero hodnôt môže byť oddelených čiarkami." + +#: filters.py:591 +#, python-format +msgid "%s (descending)" +msgstr "%s (klesajúco)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Zoradenie" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Potvrdiť" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Filtre poľa" + +#: utils.py:224 +msgid "exclude" +msgstr "neobsahuje" + +#: widgets.py:57 +msgid "All" +msgstr "Všetky" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Neznáme" + +#: widgets.py:160 +msgid "Yes" +msgstr "Áno" + +#: widgets.py:161 +msgid "No" +msgstr "Nie" diff --git a/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.mo new file mode 100644 index 0000000..e16148d Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.po new file mode 100644 index 0000000..0314b7f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/uk/LC_MESSAGES/django.po @@ -0,0 +1,184 @@ +# +msgid "" +msgstr "" +"Project-Id-Version: django-filter\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-24 11:03+0500\n" +"PO-Revision-Date: 2016-09-29 11:47+0300\n" +"Last-Translator: Eugena Mikhaylikova <eugena.mihailikova@gmail.com>\n" +"Language-Team: TextTempearture\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " +"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " +"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " +"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" +"X-Generator: Poedit 1.8.9\n" + +#: conf.py:17 +msgid "date" +msgstr "дата" + +#: conf.py:18 +msgid "year" +msgstr "рік" + +#: conf.py:19 +msgid "month" +msgstr "місяць" + +#: conf.py:20 +msgid "day" +msgstr "день" + +#: conf.py:21 +msgid "week day" +msgstr "день тижня" + +#: conf.py:22 +msgid "hour" +msgstr "година" + +#: conf.py:23 +msgid "minute" +msgstr "хвилина" + +#: conf.py:24 +msgid "second" +msgstr "секунда" + +#: conf.py:29 conf.py:30 +msgid "contains" +msgstr "містить" + +#: conf.py:31 +msgid "is in" +msgstr "в" + +#: conf.py:32 +msgid "is greater than" +msgstr "більше ніж" + +#: conf.py:33 +msgid "is greater than or equal to" +msgstr "більше або дорівнює" + +#: conf.py:34 +msgid "is less than" +msgstr "менше ніж" + +#: conf.py:35 +msgid "is less than or equal to" +msgstr "менше або дорівнює" + +#: conf.py:36 conf.py:37 +msgid "starts with" +msgstr "починається" + +#: conf.py:38 conf.py:39 +msgid "ends with" +msgstr "закінчується" + +#: conf.py:40 +msgid "is in range" +msgstr "в діапазоні" + +#: conf.py:42 conf.py:43 +msgid "matches regex" +msgstr "відповідає регулярному виразу" + +#: conf.py:44 conf.py:52 +msgid "search" +msgstr "пошук" + +#: conf.py:47 +msgid "is contained by" +msgstr "міститься в" + +#: conf.py:48 +msgid "overlaps" +msgstr "перекривається" + +#: conf.py:49 +msgid "has key" +msgstr "має ключ" + +#: conf.py:50 +msgid "has keys" +msgstr "має ключі" + +#: conf.py:51 +msgid "has any keys" +msgstr "має будь-які ключі" + +#: fields.py:178 +msgid "Range query expects two values." +msgstr "Запит діапазону очікує два значення." + +#: filters.py:429 +msgid "Any date" +msgstr "Будь-яка дата" + +#: filters.py:430 +msgid "Today" +msgstr "Сьогодні" + +#: filters.py:435 +msgid "Past 7 days" +msgstr "Минулі 7 днів" + +#: filters.py:439 +msgid "This month" +msgstr "За цей місяць" + +#: filters.py:443 +msgid "This year" +msgstr "В цьому році" + +#: filters.py:446 +msgid "Yesterday" +msgstr "Вчора" + +#: filters.py:512 +msgid "Multiple values may be separated by commas." +msgstr "Кілька значень можуть бути розділені комами." + +#: filters.py:591 +msgid "%s (descending)" +msgstr "%s (по спадаючій)" + +#: filters.py:607 +msgid "Ordering" +msgstr "Порядок" + +#: rest_framework/filterset.py:30 +#: templates/django_filters/rest_framework/form.html:5 +msgid "Submit" +msgstr "Відправити" + +#: templates/django_filters/rest_framework/crispy_form.html:4 +#: templates/django_filters/rest_framework/form.html:2 +msgid "Field filters" +msgstr "Фільтри по полях" + +#: utils.py:224 +msgid "exclude" +msgstr "виключаючи" + +#: widgets.py:57 +msgid "All" +msgstr "Усе" + +#: widgets.py:159 +msgid "Unknown" +msgstr "Не задано" + +#: widgets.py:160 +msgid "Yes" +msgstr "Так" + +#: widgets.py:161 +msgid "No" +msgstr "Немає" diff --git a/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.mo b/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.mo new file mode 100644 index 0000000..7047a5e Binary files /dev/null and b/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.po b/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000..de067b9 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,64 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Kane Blueriver <kxxoling@gmail.com>, 2016. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-01-30 17:39+0800\n" +"PO-Revision-Date: 2016-01-30 17:50+0800\n" +"Last-Translator: Kane Blueriver <kxxoling@gmail.com>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: filters.py:62 +msgid "This is an exclusion filter" +msgstr "未启用该过滤器" + +#: filters.py:62 +msgid "Filter" +msgstr "过滤器" + +#: filters.py:264 +msgid "Any date" +msgstr "任何时刻" + +#: filters.py:265 +msgid "Today" +msgstr "今日" + +#: filters.py:270 +msgid "Past 7 days" +msgstr "过去 7 日" + +#: filters.py:274 +msgid "This month" +msgstr "本月" + +#: filters.py:278 +msgid "This year" +msgstr "今年" + +#: filters.py:281 +msgid "Yesterday" +msgstr "昨日" + +#: filterset.py:398 filterset.py:409 +#, python-format +msgid "%s (descending)" +msgstr "%s(降序)" + +#: filterset.py:411 +msgid "Ordering" +msgstr "排序" + +#: widgets.py:60 +msgid "All" +msgstr "全部" diff --git a/venv/Lib/site-packages/django_filters/rest_framework/__init__.py b/venv/Lib/site-packages/django_filters/rest_framework/__init__.py new file mode 100644 index 0000000..4ffc408 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/rest_framework/__init__.py @@ -0,0 +1,4 @@ +# flake8: noqa +from .backends import DjangoFilterBackend +from .filters import * +from .filterset import FilterSet diff --git a/venv/Lib/site-packages/django_filters/rest_framework/backends.py b/venv/Lib/site-packages/django_filters/rest_framework/backends.py new file mode 100644 index 0000000..835a67f --- /dev/null +++ b/venv/Lib/site-packages/django_filters/rest_framework/backends.py @@ -0,0 +1,170 @@ +import warnings + +from django.template import loader +from django.utils.deprecation import RenameMethodsBase + +from .. import compat, utils +from . import filters, filterset + + +# TODO: remove metaclass in 2.1 +class RenameAttributes(utils.RenameAttributesBase, RenameMethodsBase): + renamed_attributes = ( + ('default_filter_set', 'filterset_base', utils.MigrationNotice), + ) + renamed_methods = ( + ('get_filter_class', 'get_filterset_class', utils.MigrationNotice), + ) + + +class DjangoFilterBackend(metaclass=RenameAttributes): + filterset_base = filterset.FilterSet + raise_exception = True + + @property + def template(self): + if compat.is_crispy(): + return 'django_filters/rest_framework/crispy_form.html' + return 'django_filters/rest_framework/form.html' + + def get_filterset(self, request, queryset, view): + filterset_class = self.get_filterset_class(view, queryset) + if filterset_class is None: + return None + + kwargs = self.get_filterset_kwargs(request, queryset, view) + return filterset_class(**kwargs) + + def get_filterset_class(self, view, queryset=None): + """ + Return the `FilterSet` class used to filter the queryset. + """ + filterset_class = getattr(view, 'filterset_class', None) + filterset_fields = getattr(view, 'filterset_fields', None) + + # TODO: remove assertion in 2.1 + if filterset_class is None and hasattr(view, 'filter_class'): + utils.deprecate( + "`%s.filter_class` attribute should be renamed `filterset_class`." + % view.__class__.__name__) + filterset_class = getattr(view, 'filter_class', None) + + # TODO: remove assertion in 2.1 + if filterset_fields is None and hasattr(view, 'filter_fields'): + utils.deprecate( + "`%s.filter_fields` attribute should be renamed `filterset_fields`." + % view.__class__.__name__) + filterset_fields = getattr(view, 'filter_fields', None) + + if filterset_class: + filterset_model = filterset_class._meta.model + + # FilterSets do not need to specify a Meta class + if filterset_model and queryset is not None: + assert issubclass(queryset.model, filterset_model), \ + 'FilterSet model %s does not match queryset model %s' % \ + (filterset_model, queryset.model) + + return filterset_class + + if filterset_fields and queryset is not None: + MetaBase = getattr(self.filterset_base, 'Meta', object) + + class AutoFilterSet(self.filterset_base): + class Meta(MetaBase): + model = queryset.model + fields = filterset_fields + + return AutoFilterSet + + return None + + def get_filterset_kwargs(self, request, queryset, view): + return { + 'data': request.query_params, + 'queryset': queryset, + 'request': request, + } + + def filter_queryset(self, request, queryset, view): + filterset = self.get_filterset(request, queryset, view) + if filterset is None: + return queryset + + if not filterset.is_valid() and self.raise_exception: + raise utils.translate_validation(filterset.errors) + return filterset.qs + + def to_html(self, request, queryset, view): + filterset = self.get_filterset(request, queryset, view) + if filterset is None: + return None + + template = loader.get_template(self.template) + context = {'filter': filterset} + return template.render(context, request) + + def get_coreschema_field(self, field): + if isinstance(field, filters.NumberFilter): + field_cls = compat.coreschema.Number + else: + field_cls = compat.coreschema.String + return field_cls( + description=str(field.extra.get('help_text', '')) + ) + + def get_schema_fields(self, view): + # This is not compatible with widgets where the query param differs from the + # filter's attribute name. Notably, this includes `MultiWidget`, where query + # params will be of the format `<name>_0`, `<name>_1`, etc... + assert compat.coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert compat.coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + + try: + queryset = view.get_queryset() + except Exception: + queryset = None + warnings.warn( + "{} is not compatible with schema generation".format(view.__class__) + ) + + filterset_class = self.get_filterset_class(view, queryset) + + return [] if not filterset_class else [ + compat.coreapi.Field( + name=field_name, + required=field.extra['required'], + location='query', + schema=self.get_coreschema_field(field) + ) for field_name, field in filterset_class.base_filters.items() + ] + + def get_schema_operation_parameters(self, view): + try: + queryset = view.get_queryset() + except Exception: + queryset = None + warnings.warn( + "{} is not compatible with schema generation".format(view.__class__) + ) + + filterset_class = self.get_filterset_class(view, queryset) + + if not filterset_class: + return [] + + parameters = [] + for field_name, field in filterset_class.base_filters.items(): + parameter = { + 'name': field_name, + 'required': field.extra['required'], + 'in': 'query', + 'description': field.label if field.label is not None else field_name, + 'schema': { + 'type': 'string', + }, + } + if field.extra and 'choices' in field.extra: + parameter['schema']['enum'] = [c[0] for c in field.extra['choices']] + parameters.append(parameter) + return parameters diff --git a/venv/Lib/site-packages/django_filters/rest_framework/filters.py b/venv/Lib/site-packages/django_filters/rest_framework/filters.py new file mode 100644 index 0000000..4c5755e --- /dev/null +++ b/venv/Lib/site-packages/django_filters/rest_framework/filters.py @@ -0,0 +1,13 @@ +from django_filters import filters + +from ..filters import * # noqa +from ..widgets import BooleanWidget + +__all__ = filters.__all__ + + +class BooleanFilter(filters.BooleanFilter): + def __init__(self, *args, **kwargs): + kwargs.setdefault('widget', BooleanWidget) + + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/django_filters/rest_framework/filterset.py b/venv/Lib/site-packages/django_filters/rest_framework/filterset.py new file mode 100644 index 0000000..8c42304 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/rest_framework/filterset.py @@ -0,0 +1,40 @@ +from copy import deepcopy + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from django_filters import filterset + +from .. import compat +from .filters import BooleanFilter, IsoDateTimeFilter + +FILTER_FOR_DBFIELD_DEFAULTS = deepcopy(filterset.FILTER_FOR_DBFIELD_DEFAULTS) +FILTER_FOR_DBFIELD_DEFAULTS.update({ + models.DateTimeField: {'filter_class': IsoDateTimeFilter}, + models.BooleanField: {'filter_class': BooleanFilter}, + models.NullBooleanField: {'filter_class': BooleanFilter}, +}) + + +class FilterSet(filterset.FilterSet): + FILTER_DEFAULTS = FILTER_FOR_DBFIELD_DEFAULTS + + @property + def form(self): + form = super().form + + if compat.is_crispy(): + from crispy_forms.helper import FormHelper + from crispy_forms.layout import Layout, Submit + + layout_components = list(form.fields.keys()) + [ + Submit('', _('Submit'), css_class='btn-default'), + ] + helper = FormHelper() + helper.form_method = 'GET' + helper.template_pack = 'bootstrap3' + helper.layout = Layout(*layout_components) + + form.helper = helper + + return form diff --git a/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/crispy_form.html b/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/crispy_form.html new file mode 100644 index 0000000..171767c --- /dev/null +++ b/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/crispy_form.html @@ -0,0 +1,5 @@ +{% load crispy_forms_tags %} +{% load i18n %} + +<h2>{% trans "Field filters" %}</h2> +{% crispy filter.form %} diff --git a/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/form.html b/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/form.html new file mode 100644 index 0000000..b116e35 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/templates/django_filters/rest_framework/form.html @@ -0,0 +1,6 @@ +{% load i18n %} +<h2>{% trans "Field filters" %}</h2> +<form class="form" action="" method="get"> + {{ filter.form.as_p }} + <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button> +</form> diff --git a/venv/Lib/site-packages/django_filters/templates/django_filters/widgets/multiwidget.html b/venv/Lib/site-packages/django_filters/templates/django_filters/widgets/multiwidget.html new file mode 100644 index 0000000..089ddb2 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/templates/django_filters/widgets/multiwidget.html @@ -0,0 +1 @@ +{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %} diff --git a/venv/Lib/site-packages/django_filters/utils.py b/venv/Lib/site-packages/django_filters/utils.py new file mode 100644 index 0000000..67a071a --- /dev/null +++ b/venv/Lib/site-packages/django_filters/utils.py @@ -0,0 +1,328 @@ +import warnings +from collections import OrderedDict + +from django.conf import settings +from django.core.exceptions import FieldDoesNotExist, FieldError +from django.db import models +from django.db.models.constants import LOOKUP_SEP +from django.db.models.expressions import Expression +from django.db.models.fields.related import ForeignObjectRel, RelatedField +from django.utils import timezone +from django.utils.encoding import force_str +from django.utils.text import capfirst +from django.utils.translation import gettext as _ + +from .exceptions import FieldLookupError + + +def deprecate(msg, level_modifier=0): + warnings.warn(msg, MigrationNotice, stacklevel=3 + level_modifier) + + +class MigrationNotice(DeprecationWarning): + url = 'https://django-filter.readthedocs.io/en/main/guide/migration.html' + + def __init__(self, message): + super().__init__('%s See: %s' % (message, self.url)) + + +class RenameAttributesBase(type): + """ + Handles the deprecation paths when renaming an attribute. + + It does the following: + - Defines accessors that redirect to the renamed attributes. + - Complain whenever an old attribute is accessed. + + This is conceptually based on `django.utils.deprecation.RenameMethodsBase`. + """ + renamed_attributes = () + + def __new__(metacls, name, bases, attrs): + # remove old attributes before creating class + old_names = [r[0] for r in metacls.renamed_attributes] + old_names = [name for name in old_names if name in attrs] + old_attrs = {name: attrs.pop(name) for name in old_names} + + # get a handle to any accessors defined on the class + cls_getattr = attrs.pop('__getattr__', None) + cls_setattr = attrs.pop('__setattr__', None) + + new_class = super().__new__(metacls, name, bases, attrs) + + def __getattr__(self, name): + name = type(self).get_name(name) + if cls_getattr is not None: + return cls_getattr(self, name) + elif hasattr(super(new_class, self), '__getattr__'): + return super(new_class, self).__getattr__(name) + return self.__getattribute__(name) + + def __setattr__(self, name, value): + name = type(self).get_name(name) + if cls_setattr is not None: + return cls_setattr(self, name, value) + return super(new_class, self).__setattr__(name, value) + + new_class.__getattr__ = __getattr__ + new_class.__setattr__ = __setattr__ + + # set renamed attributes + for name, value in old_attrs.items(): + setattr(new_class, name, value) + + return new_class + + def get_name(metacls, name): + """ + Get the real attribute name. If the attribute has been renamed, + the new name will be returned and a deprecation warning issued. + """ + for renamed_attribute in metacls.renamed_attributes: + old_name, new_name, deprecation_warning = renamed_attribute + + if old_name == name: + warnings.warn("`%s.%s` attribute should be renamed `%s`." + % (metacls.__name__, old_name, new_name), + deprecation_warning, 3) + return new_name + + return name + + def __getattr__(metacls, name): + return super().__getattribute__(metacls.get_name(name)) + + def __setattr__(metacls, name, value): + return super().__setattr__(metacls.get_name(name), value) + + +def try_dbfield(fn, field_class): + """ + Try ``fn`` with the DB ``field_class`` by walking its + MRO until a result is found. + + ex:: + _try_dbfield(field_dict.get, models.CharField) + + """ + # walk the mro, as field_class could be a derived model field. + for cls in field_class.mro(): + # skip if cls is models.Field + if cls is models.Field: + continue + + data = fn(cls) + if data: + return data + + +def get_all_model_fields(model): + opts = model._meta + + return [ + f.name for f in sorted(opts.fields + opts.many_to_many) + if not isinstance(f, models.AutoField) and + not (getattr(f.remote_field, 'parent_link', False)) + ] + + +def get_model_field(model, field_name): + """ + Get a ``model`` field, traversing relationships + in the ``field_name``. + + ex:: + + f = get_model_field(Book, 'author__first_name') + + """ + fields = get_field_parts(model, field_name) + return fields[-1] if fields else None + + +def get_field_parts(model, field_name): + """ + Get the field parts that represent the traversable relationships from the + base ``model`` to the final field, described by ``field_name``. + + ex:: + + >>> parts = get_field_parts(Book, 'author__first_name') + >>> [p.verbose_name for p in parts] + ['author', 'first name'] + + """ + parts = field_name.split(LOOKUP_SEP) + opts = model._meta + fields = [] + + # walk relationships + for name in parts: + try: + field = opts.get_field(name) + except FieldDoesNotExist: + return None + + fields.append(field) + try: + if isinstance(field, RelatedField): + opts = field.remote_field.model._meta + elif isinstance(field, ForeignObjectRel): + opts = field.related_model._meta + except AttributeError: + # Lazy relationships are not resolved until registry is populated. + raise RuntimeError( + "Unable to resolve relationship `%s` for `%s`. Django is most " + "likely not initialized, and its apps registry not populated. " + "Ensure Django has finished setup before loading `FilterSet`s." + % (field_name, model._meta.label)) + + return fields + + +def resolve_field(model_field, lookup_expr): + """ + Resolves a ``lookup_expr`` into its final output field, given + the initial ``model_field``. The lookup expression should only contain + transforms and lookups, not intermediary model field parts. + + Note: + This method is based on django.db.models.sql.query.Query.build_lookup + + For more info on the lookup API: + https://docs.djangoproject.com/en/stable/ref/models/lookups/ + + """ + query = model_field.model._default_manager.all().query + lhs = Expression(model_field) + lookups = lookup_expr.split(LOOKUP_SEP) + + assert len(lookups) > 0 + + try: + while lookups: + name = lookups[0] + args = (lhs, name) + # If there is just one part left, try first get_lookup() so + # that if the lhs supports both transform and lookup for the + # name, then lookup will be picked. + if len(lookups) == 1: + final_lookup = lhs.get_lookup(name) + if not final_lookup: + # We didn't find a lookup. We are going to interpret + # the name as transform, and do an Exact lookup against + # it. + lhs = query.try_transform(*args) + final_lookup = lhs.get_lookup('exact') + return lhs.output_field, final_lookup.lookup_name + lhs = query.try_transform(*args) + lookups = lookups[1:] + except FieldError as e: + raise FieldLookupError(model_field, lookup_expr) from e + + +def handle_timezone(value, is_dst=None): + if settings.USE_TZ and timezone.is_naive(value): + return timezone.make_aware(value, timezone.get_current_timezone(), is_dst) + elif not settings.USE_TZ and timezone.is_aware(value): + return timezone.make_naive(value, timezone.utc) + return value + + +def verbose_field_name(model, field_name): + """ + Get the verbose name for a given ``field_name``. The ``field_name`` + will be traversed across relationships. Returns '[invalid name]' for + any field name that cannot be traversed. + + ex:: + + >>> verbose_field_name(Article, 'author__name') + 'author name' + + """ + if field_name is None: + return '[invalid name]' + + parts = get_field_parts(model, field_name) + if not parts: + return '[invalid name]' + + names = [] + for part in parts: + if isinstance(part, ForeignObjectRel): + if part.related_name: + names.append(part.related_name.replace('_', ' ')) + else: + return '[invalid name]' + else: + names.append(force_str(part.verbose_name)) + + return ' '.join(names) + + +def verbose_lookup_expr(lookup_expr): + """ + Get a verbose, more humanized expression for a given ``lookup_expr``. + Each part in the expression is looked up in the ``FILTERS_VERBOSE_LOOKUPS`` + dictionary. Missing keys will simply default to itself. + + ex:: + + >>> verbose_lookup_expr('year__lt') + 'year is less than' + + # with `FILTERS_VERBOSE_LOOKUPS = {}` + >>> verbose_lookup_expr('year__lt') + 'year lt' + + """ + from .conf import settings as app_settings + + VERBOSE_LOOKUPS = app_settings.VERBOSE_LOOKUPS or {} + lookups = [ + force_str(VERBOSE_LOOKUPS.get(lookup, _(lookup))) + for lookup in lookup_expr.split(LOOKUP_SEP) + ] + + return ' '.join(lookups) + + +def label_for_filter(model, field_name, lookup_expr, exclude=False): + """ + Create a generic label suitable for a filter. + + ex:: + + >>> label_for_filter(Article, 'author__name', 'in') + 'auther name is in' + + """ + name = verbose_field_name(model, field_name) + verbose_expression = [_('exclude'), name] if exclude else [name] + + # iterable lookups indicate a LookupTypeField, which should not be verbose + if isinstance(lookup_expr, str): + verbose_expression += [verbose_lookup_expr(lookup_expr)] + + verbose_expression = [force_str(part) for part in verbose_expression if part] + verbose_expression = capfirst(' '.join(verbose_expression)) + + return verbose_expression + + +def translate_validation(error_dict): + """ + Translate a Django ErrorDict into its DRF ValidationError. + """ + # it's necessary to lazily import the exception, as it can otherwise create + # an import loop when importing django_filters inside the project settings. + from rest_framework.exceptions import ErrorDetail, ValidationError + + exc = OrderedDict( + (key, [ErrorDetail(e.message % (e.params or ()), code=e.code) + for e in error_list]) + for key, error_list in error_dict.as_data().items() + ) + + return ValidationError(exc) diff --git a/venv/Lib/site-packages/django_filters/views.py b/venv/Lib/site-packages/django_filters/views.py new file mode 100644 index 0000000..e9160ad --- /dev/null +++ b/venv/Lib/site-packages/django_filters/views.py @@ -0,0 +1,116 @@ +from django.core.exceptions import ImproperlyConfigured +from django.views.generic import View +from django.views.generic.list import ( + MultipleObjectMixin, + MultipleObjectTemplateResponseMixin +) + +from .constants import ALL_FIELDS +from .filterset import filterset_factory +from .utils import MigrationNotice, RenameAttributesBase + + +# TODO: remove metaclass in 2.1 +class FilterMixinRenames(RenameAttributesBase): + renamed_attributes = ( + ('filter_fields', 'filterset_fields', MigrationNotice), + ) + + +class FilterMixin(metaclass=FilterMixinRenames): + """ + A mixin that provides a way to show and handle a FilterSet in a request. + """ + filterset_class = None + filterset_fields = ALL_FIELDS + strict = True + + def get_filterset_class(self): + """ + Returns the filterset class to use in this view + """ + if self.filterset_class: + return self.filterset_class + elif self.model: + return filterset_factory(model=self.model, fields=self.filterset_fields) + else: + msg = "'%s' must define 'filterset_class' or 'model'" + raise ImproperlyConfigured(msg % self.__class__.__name__) + + def get_filterset(self, filterset_class): + """ + Returns an instance of the filterset to be used in this view. + """ + kwargs = self.get_filterset_kwargs(filterset_class) + return filterset_class(**kwargs) + + def get_filterset_kwargs(self, filterset_class): + """ + Returns the keyword arguments for instantiating the filterset. + """ + kwargs = { + 'data': self.request.GET or None, + 'request': self.request, + } + try: + kwargs.update({ + 'queryset': self.get_queryset(), + }) + except ImproperlyConfigured: + # ignore the error here if the filterset has a model defined + # to acquire a queryset from + if filterset_class._meta.model is None: + msg = ("'%s' does not define a 'model' and the view '%s' does " + "not return a valid queryset from 'get_queryset'. You " + "must fix one of them.") + args = (filterset_class.__name__, self.__class__.__name__) + raise ImproperlyConfigured(msg % args) + return kwargs + + def get_strict(self): + return self.strict + + +class BaseFilterView(FilterMixin, MultipleObjectMixin, View): + + def get(self, request, *args, **kwargs): + filterset_class = self.get_filterset_class() + self.filterset = self.get_filterset(filterset_class) + + if not self.filterset.is_bound or self.filterset.is_valid() or not self.get_strict(): + self.object_list = self.filterset.qs + else: + self.object_list = self.filterset.queryset.none() + + context = self.get_context_data(filter=self.filterset, + object_list=self.object_list) + return self.render_to_response(context) + + +class FilterView(MultipleObjectTemplateResponseMixin, BaseFilterView): + """ + Render some list of objects with filter, set by `self.model` or + `self.queryset`. + `self.queryset` can actually be any iterable of items, not just a queryset. + """ + template_name_suffix = '_filter' + + +def object_filter(request, model=None, queryset=None, template_name=None, + extra_context=None, context_processors=None, + filter_class=None): + class ECFilterView(FilterView): + """Handle the extra_context from the functional object_filter view""" + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + extra_context = self.kwargs.get('extra_context') or {} + for k, v in extra_context.items(): + if callable(v): + v = v() + context[k] = v + return context + + kwargs = dict(model=model, queryset=queryset, template_name=template_name, + filterset_class=filter_class) + view = ECFilterView.as_view(**kwargs) + return view(request, extra_context=extra_context) diff --git a/venv/Lib/site-packages/django_filters/widgets.py b/venv/Lib/site-packages/django_filters/widgets.py new file mode 100644 index 0000000..889fd81 --- /dev/null +++ b/venv/Lib/site-packages/django_filters/widgets.py @@ -0,0 +1,270 @@ +from collections.abc import Iterable +from copy import deepcopy +from itertools import chain +from re import search, sub + +from django import forms +from django.db.models.fields import BLANK_CHOICE_DASH +from django.forms.utils import flatatt +from django.utils.datastructures import MultiValueDict +from django.utils.encoding import force_str +from django.utils.http import urlencode +from django.utils.safestring import mark_safe +from django.utils.translation import gettext as _ + + +class LinkWidget(forms.Widget): + def __init__(self, attrs=None, choices=()): + super().__init__(attrs) + + self.choices = choices + + def value_from_datadict(self, data, files, name): + value = super().value_from_datadict(data, files, name) + self.data = data + return value + + def render(self, name, value, attrs=None, choices=(), renderer=None): + if not hasattr(self, 'data'): + self.data = {} + if value is None: + value = '' + final_attrs = self.build_attrs(self.attrs, extra_attrs=attrs) + output = ['<ul%s>' % flatatt(final_attrs)] + options = self.render_options(choices, [value], name) + if options: + output.append(options) + output.append('</ul>') + return mark_safe('\n'.join(output)) + + def render_options(self, choices, selected_choices, name): + selected_choices = set(force_str(v) for v in selected_choices) + output = [] + for option_value, option_label in chain(self.choices, choices): + if isinstance(option_label, (list, tuple)): + for option in option_label: + output.append( + self.render_option(name, selected_choices, *option)) + else: + output.append( + self.render_option(name, selected_choices, + option_value, option_label)) + return '\n'.join(output) + + def render_option(self, name, selected_choices, + option_value, option_label): + option_value = force_str(option_value) + if option_label == BLANK_CHOICE_DASH[0][1]: + option_label = _("All") + data = self.data.copy() + data[name] = option_value + selected = data == self.data or option_value in selected_choices + try: + url = data.urlencode() + except AttributeError: + url = urlencode(data) + return self.option_string() % { + 'attrs': selected and ' class="selected"' or '', + 'query_string': url, + 'label': force_str(option_label) + } + + def option_string(self): + return '<li><a%(attrs)s href="?%(query_string)s">%(label)s</a></li>' + + +class SuffixedMultiWidget(forms.MultiWidget): + """ + A MultiWidget that allows users to provide custom suffixes instead of indexes. + + - Suffixes must be unique. + - There must be the same number of suffixes as fields. + """ + suffixes = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + assert len(self.widgets) == len(self.suffixes) + assert len(self.suffixes) == len(set(self.suffixes)) + + def suffixed(self, name, suffix): + return '_'.join([name, suffix]) if suffix else name + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + for subcontext, suffix in zip(context['widget']['subwidgets'], self.suffixes): + subcontext['name'] = self.suffixed(name, suffix) + + return context + + def value_from_datadict(self, data, files, name): + return [ + widget.value_from_datadict(data, files, self.suffixed(name, suffix)) + for widget, suffix in zip(self.widgets, self.suffixes) + ] + + def value_omitted_from_data(self, data, files, name): + return all( + widget.value_omitted_from_data(data, files, self.suffixed(name, suffix)) + for widget, suffix in zip(self.widgets, self.suffixes) + ) + + def replace_name(self, output, index): + result = search(r'name="(?P<name>.*)_%d"' % index, output) + name = result.group('name') + name = self.suffixed(name, self.suffixes[index]) + name = 'name="%s"' % name + + return sub(r'name=".*_%d"' % index, name, output) + + def decompress(self, value): + if value is None: + return [None, None] + return value + + +class RangeWidget(SuffixedMultiWidget): + template_name = 'django_filters/widgets/multiwidget.html' + suffixes = ['min', 'max'] + + def __init__(self, attrs=None): + widgets = (forms.TextInput, forms.TextInput) + super().__init__(widgets, attrs) + + def decompress(self, value): + if value: + return [value.start, value.stop] + return [None, None] + + +class DateRangeWidget(RangeWidget): + suffixes = ['after', 'before'] + + +class LookupChoiceWidget(SuffixedMultiWidget): + suffixes = [None, 'lookup'] + + def decompress(self, value): + if value is None: + return [None, None] + return value + + +class BooleanWidget(forms.Select): + """Convert true/false values into the internal Python True/False. + This can be used for AJAX queries that pass true/false from JavaScript's + internal types through. + """ + def __init__(self, attrs=None): + choices = (('', _('Unknown')), + ('true', _('Yes')), + ('false', _('No'))) + super().__init__(attrs, choices) + + def render(self, name, value, attrs=None, renderer=None): + try: + value = { + True: 'true', + False: 'false', + '1': 'true', + '0': 'false' + }[value] + except KeyError: + value = '' + return super().render(name, value, attrs, renderer=renderer) + + def value_from_datadict(self, data, files, name): + value = data.get(name, None) + if isinstance(value, str): + value = value.lower() + + return { + '1': True, + '0': False, + 'true': True, + 'false': False, + True: True, + False: False, + }.get(value, None) + + +class BaseCSVWidget(forms.Widget): + # Surrogate widget for rendering multiple values + surrogate = forms.TextInput + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if isinstance(self.surrogate, type): + self.surrogate = self.surrogate() + else: + self.surrogate = deepcopy(self.surrogate) + + def _isiterable(self, value): + return isinstance(value, Iterable) and not isinstance(value, str) + + def value_from_datadict(self, data, files, name): + value = super().value_from_datadict(data, files, name) + + if value is not None: + if value == '': # empty value should parse as an empty list + return [] + return value.split(',') + return None + + def render(self, name, value, attrs=None, renderer=None): + if not self._isiterable(value): + value = [value] + + if len(value) <= 1: + # delegate to main widget (Select, etc...) if not multiple values + value = value[0] if value else '' + return super().render(name, value, attrs, renderer=renderer) + + # if we have multiple values, we need to force render as a text input + # (otherwise, the additional values are lost) + value = [force_str(self.surrogate.format_value(v)) for v in value] + value = ','.join(list(value)) + + return self.surrogate.render(name, value, attrs, renderer=renderer) + + +class CSVWidget(BaseCSVWidget, forms.TextInput): + def __init__(self, *args, attrs=None, **kwargs): + super().__init__(*args, attrs, **kwargs) + + if attrs is not None: + self.surrogate.attrs.update(attrs) + + +class QueryArrayWidget(BaseCSVWidget, forms.TextInput): + """ + Enables request query array notation that might be consumed by MultipleChoiceFilter + + 1. Values can be provided as csv string: ?foo=bar,baz + 2. Values can be provided as query array: ?foo[]=bar&foo[]=baz + 3. Values can be provided as query array: ?foo=bar&foo=baz + + Note: Duplicate and empty values are skipped from results + """ + + def value_from_datadict(self, data, files, name): + if not isinstance(data, MultiValueDict): + for key, value in data.items(): + # treat value as csv string: ?foo=1,2 + if isinstance(value, str): + data[key] = [x.strip() for x in value.rstrip(',').split(',') if x] + data = MultiValueDict(data) + + values_list = data.getlist(name, data.getlist('%s[]' % name)) or [] + + # apparently its an array, so no need to process it's values as csv + # ?foo=1&foo=2 -> data.getlist(foo) -> foo = [1, 2] + # ?foo[]=1&foo[]=2 -> data.getlist(foo[]) -> foo = [1, 2] + if len(values_list) > 0: + ret = [x for x in values_list if x] + else: + ret = [] + + return list(set(ret)) diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/INSTALLER b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/LICENSE.md b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/LICENSE.md new file mode 100644 index 0000000..3dea39c --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/LICENSE.md @@ -0,0 +1,29 @@ +# License + +Copyright © 2011-present, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/METADATA b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/METADATA new file mode 100644 index 0000000..7c1f515 --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/METADATA @@ -0,0 +1,264 @@ +Metadata-Version: 2.1 +Name: djangorestframework +Version: 3.13.1 +Summary: Web APIs for Django, made easy. +Home-page: https://www.django-rest-framework.org/ +Author: Tom Christie +Author-email: tom@tomchristie.com +License: BSD +Project-URL: Funding, https://fund.django-rest-framework.org/topics/funding/ +Project-URL: Source, https://github.com/encode/django-rest-framework +Project-URL: Changelog, https://www.django-rest-framework.org/community/release-notes/ +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Django +Classifier: Framework :: Django :: 2.2 +Classifier: Framework :: Django :: 3.0 +Classifier: Framework :: Django :: 3.1 +Classifier: Framework :: Django :: 3.2 +Classifier: Framework :: Django :: 4.0 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +Requires-Dist: django (>=2.2) +Requires-Dist: pytz + +# [Django REST framework][docs] + +[![build-status-image]][build-status] +[![coverage-status-image]][codecov] +[![pypi-version]][pypi] + +**Awesome web-browsable Web APIs.** + +Full documentation for the project is available at [https://www.django-rest-framework.org/][docs]. + +--- + +# Funding + +REST framework is a *collaboratively funded project*. If you use +REST framework commercially we strongly encourage you to invest in its +continued development by [signing up for a paid plan][funding]. + +The initial aim is to provide a single full-time position on REST framework. +*Every single sign-up makes a significant impact towards making that possible.* + +[![][sentry-img]][sentry-url] +[![][stream-img]][stream-url] +[![][rollbar-img]][rollbar-url] +[![][esg-img]][esg-url] +[![][retool-img]][retool-url] +[![][bitio-img]][bitio-url] +[![][posthog-img]][posthog-url] +[![][cryptapi-img]][cryptapi-url] + +Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Rollbar][rollbar-url], [ESG][esg-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], and [CryptAPI][cryptapi-url]. + +--- + +# Overview + +Django REST framework is a powerful and flexible toolkit for building Web APIs. + +Some reasons you might want to use REST framework: + +* The [Web browsable API][sandbox] is a huge usability win for your developers. +* [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section]. +* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. +* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. +* [Extensive documentation][docs], and [great community support][group]. + +There is a live example API for testing purposes, [available here][sandbox]. + +**Below**: *Screenshot from the browsable API* + +![Screenshot][image] + +---- + +# Requirements + +* Python (3.6, 3.7, 3.8, 3.9, 3.10) +* Django (2.2, 3.0, 3.1, 3.2, 4.0) + +We **highly recommend** and only officially support the latest patch release of +each Python and Django series. + +# Installation + +Install using `pip`... + + pip install djangorestframework + +Add `'rest_framework'` to your `INSTALLED_APPS` setting. + + INSTALLED_APPS = [ + ... + 'rest_framework', + ] + +# Example + +Let's take a look at a quick example of using REST framework to build a simple model-backed API for accessing users and groups. + +Startup up a new project like so... + + pip install django + pip install djangorestframework + django-admin startproject example . + ./manage.py migrate + ./manage.py createsuperuser + + +Now edit the `example/urls.py` module in your project: + +```python +from django.urls import path, include +from django.contrib.auth.models import User +from rest_framework import serializers, viewsets, routers + +# Serializers define the API representation. +class UserSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = User + fields = ['url', 'username', 'email', 'is_staff'] + + +# ViewSets define the view behavior. +class UserViewSet(viewsets.ModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + + +# Routers provide a way of automatically determining the URL conf. +router = routers.DefaultRouter() +router.register(r'users', UserViewSet) + + +# Wire up our API using automatic URL routing. +# Additionally, we include login URLs for the browsable API. +urlpatterns = [ + path('', include(router.urls)), + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), +] +``` + +We'd also like to configure a couple of settings for our API. + +Add the following to your `settings.py` module: + +```python +INSTALLED_APPS = [ + ... # Make sure to include the default installed apps here. + 'rest_framework', +] + +REST_FRAMEWORK = { + # Use Django's standard `django.contrib.auth` permissions, + # or allow read-only access for unauthenticated users. + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', + ] +} +``` + +That's it, we're done! + + ./manage.py runserver + +You can now open the API in your browser at `http://127.0.0.1:8000/`, and view your new 'users' API. If you use the `Login` control in the top right corner you'll also be able to add, create and delete users from the system. + +You can also interact with the API using command line tools such as [`curl`](https://curl.haxx.se/). For example, to list the users endpoint: + + $ curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/ + [ + { + "url": "http://127.0.0.1:8000/users/1/", + "username": "admin", + "email": "admin@example.com", + "is_staff": true, + } + ] + +Or to create a new user: + + $ curl -X POST -d username=new -d email=new@example.com -d is_staff=false -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/ + { + "url": "http://127.0.0.1:8000/users/2/", + "username": "new", + "email": "new@example.com", + "is_staff": false, + } + +# Documentation & Support + +Full documentation for the project is available at [https://www.django-rest-framework.org/][docs]. + +For questions and support, use the [REST framework discussion group][group], or `#restframework` on libera.chat IRC. + +You may also want to [follow the author on Twitter][twitter]. + +# Security + +Please see the [security policy][security-policy]. + +[build-status-image]: https://github.com/encode/django-rest-framework/actions/workflows/main.yml/badge.svg +[build-status]: https://github.com/encode/django-rest-framework/actions/workflows/main.yml +[coverage-status-image]: https://img.shields.io/codecov/c/github/encode/django-rest-framework/master.svg +[codecov]: https://codecov.io/github/encode/django-rest-framework?branch=master +[pypi-version]: https://img.shields.io/pypi/v/djangorestframework.svg +[pypi]: https://pypi.org/project/djangorestframework/ +[twitter]: https://twitter.com/_tomchristie +[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework +[sandbox]: https://restframework.herokuapp.com/ + +[funding]: https://fund.django-rest-framework.org/topics/funding/ +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors + +[sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png +[stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png +[rollbar-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rollbar-readme.png +[esg-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/esg-readme.png +[retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/retool-readme.png +[bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/bitio-readme.png +[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/posthog-readme.png +[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/cryptapi-readme.png + +[sentry-url]: https://getsentry.com/welcome/ +[stream-url]: https://getstream.io/?utm_source=drf&utm_medium=sponsorship&utm_content=developer +[rollbar-url]: https://rollbar.com/?utm_source=django&utm_medium=sponsorship&utm_campaign=freetrial +[esg-url]: https://software.esg-usa.com/ +[retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship +[bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship +[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship +[cryptapi-url]: https://cryptapi.io + +[oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth +[oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit +[serializer-section]: https://www.django-rest-framework.org/api-guide/serializers/#serializers +[modelserializer-section]: https://www.django-rest-framework.org/api-guide/serializers/#modelserializer +[functionview-section]: https://www.django-rest-framework.org/api-guide/views/#function-based-views +[generic-views]: https://www.django-rest-framework.org/api-guide/generic-views/ +[viewsets]: https://www.django-rest-framework.org/api-guide/viewsets/ +[routers]: https://www.django-rest-framework.org/api-guide/routers/ +[serializers]: https://www.django-rest-framework.org/api-guide/serializers/ +[authentication]: https://www.django-rest-framework.org/api-guide/authentication/ +[image]: https://www.django-rest-framework.org/img/quickstart.png + +[docs]: https://www.django-rest-framework.org/ +[security-policy]: https://github.com/encode/django-rest-framework/security/policy + + diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/RECORD b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/RECORD new file mode 100644 index 0000000..100262e --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/RECORD @@ -0,0 +1,311 @@ +djangorestframework-3.13.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +djangorestframework-3.13.1.dist-info/LICENSE.md,sha256=zBKwuFNolyF36_QiCJQZuf4-6lcp_ssREFxkD27qzNQ,1537 +djangorestframework-3.13.1.dist-info/METADATA,sha256=tJEmgKzJIvq0e6bEdA96pIVL8d5-VEMaCsqNomYLgdA,10646 +djangorestframework-3.13.1.dist-info/RECORD,, +djangorestframework-3.13.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +djangorestframework-3.13.1.dist-info/WHEEL,sha256=EVRjI69F5qVjm_YgqcTXPnTAv3BfSUr0WVAHuSP3Xoo,92 +djangorestframework-3.13.1.dist-info/top_level.txt,sha256=_sDOIN5T7esgAN5zlnfLHLo7AG7TWqBYVTyFKVRdXv4,15 +rest_framework/__init__.py,sha256=nLtEqiwzIrMvXt6Mc7sbkKD8Cv--5ZDgxJJ72pqSRKs,1028 +rest_framework/__pycache__/__init__.cpython-39.pyc,, +rest_framework/__pycache__/apps.cpython-39.pyc,, +rest_framework/__pycache__/authentication.cpython-39.pyc,, +rest_framework/__pycache__/checks.cpython-39.pyc,, +rest_framework/__pycache__/compat.cpython-39.pyc,, +rest_framework/__pycache__/decorators.cpython-39.pyc,, +rest_framework/__pycache__/documentation.cpython-39.pyc,, +rest_framework/__pycache__/exceptions.cpython-39.pyc,, +rest_framework/__pycache__/fields.cpython-39.pyc,, +rest_framework/__pycache__/filters.cpython-39.pyc,, +rest_framework/__pycache__/generics.cpython-39.pyc,, +rest_framework/__pycache__/metadata.cpython-39.pyc,, +rest_framework/__pycache__/mixins.cpython-39.pyc,, +rest_framework/__pycache__/negotiation.cpython-39.pyc,, +rest_framework/__pycache__/pagination.cpython-39.pyc,, +rest_framework/__pycache__/parsers.cpython-39.pyc,, +rest_framework/__pycache__/permissions.cpython-39.pyc,, +rest_framework/__pycache__/relations.cpython-39.pyc,, +rest_framework/__pycache__/renderers.cpython-39.pyc,, +rest_framework/__pycache__/request.cpython-39.pyc,, +rest_framework/__pycache__/response.cpython-39.pyc,, +rest_framework/__pycache__/reverse.cpython-39.pyc,, +rest_framework/__pycache__/routers.cpython-39.pyc,, +rest_framework/__pycache__/serializers.cpython-39.pyc,, +rest_framework/__pycache__/settings.cpython-39.pyc,, +rest_framework/__pycache__/status.cpython-39.pyc,, +rest_framework/__pycache__/test.cpython-39.pyc,, +rest_framework/__pycache__/throttling.cpython-39.pyc,, +rest_framework/__pycache__/urlpatterns.cpython-39.pyc,, +rest_framework/__pycache__/urls.cpython-39.pyc,, +rest_framework/__pycache__/validators.cpython-39.pyc,, +rest_framework/__pycache__/versioning.cpython-39.pyc,, +rest_framework/__pycache__/views.cpython-39.pyc,, +rest_framework/__pycache__/viewsets.cpython-39.pyc,, +rest_framework/apps.py,sha256=e-soDnr6WzO5YU4VKliGBgP_vneqoR85SiftQ7H7Ge4,255 +rest_framework/authentication.py,sha256=lhMzDRr6MZ9eMCWnEFSK02LYGqpYDS-VRxcmBPADOHk,7739 +rest_framework/authtoken/__init__.py,sha256=Nl2fsQY6cD3321dXmDe0YRhQhef0C4bPothmsyLbavg,116 +rest_framework/authtoken/__pycache__/__init__.cpython-39.pyc,, +rest_framework/authtoken/__pycache__/admin.cpython-39.pyc,, +rest_framework/authtoken/__pycache__/apps.cpython-39.pyc,, +rest_framework/authtoken/__pycache__/models.cpython-39.pyc,, +rest_framework/authtoken/__pycache__/serializers.cpython-39.pyc,, +rest_framework/authtoken/__pycache__/views.cpython-39.pyc,, +rest_framework/authtoken/admin.py,sha256=ajzPY3jEAm2WxHVo7KZpl8pH6DzPAuEhVfFlvaXpVzo,1760 +rest_framework/authtoken/apps.py,sha256=O5R_48w8g0cThVJ0k2TH3x5t7D1KTdN3zsKwPXZDYSQ,198 +rest_framework/authtoken/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/authtoken/management/__pycache__/__init__.cpython-39.pyc,, +rest_framework/authtoken/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/authtoken/management/commands/__pycache__/__init__.cpython-39.pyc,, +rest_framework/authtoken/management/commands/__pycache__/drf_create_token.cpython-39.pyc,, +rest_framework/authtoken/management/commands/drf_create_token.py,sha256=CcGkuS62daT8YXIMt64mbc0Qu1zA5sInKSI0K1Vsx9U,1380 +rest_framework/authtoken/migrations/0001_initial.py,sha256=8hmactx2pKGeJV95raW4F7klyXNlcNQBvmahvnLj2xU,706 +rest_framework/authtoken/migrations/0002_auto_20160226_1747.py,sha256=f2C8kJ1D4A2uTte1H2UCc2-p_gxPWzT4_96oagJ56nk,994 +rest_framework/authtoken/migrations/0003_tokenproxy.py,sha256=bsFvzO_i8iMRaHvebIZU55HKNr08kg2D6LtduZQDXGw,552 +rest_framework/authtoken/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/authtoken/migrations/__pycache__/0001_initial.cpython-39.pyc,, +rest_framework/authtoken/migrations/__pycache__/0002_auto_20160226_1747.cpython-39.pyc,, +rest_framework/authtoken/migrations/__pycache__/0003_tokenproxy.cpython-39.pyc,, +rest_framework/authtoken/migrations/__pycache__/__init__.cpython-39.pyc,, +rest_framework/authtoken/models.py,sha256=XKks3WuKMCyj4lbC__OXl1pzqYYHeXcSYrwV2iNk51U,1563 +rest_framework/authtoken/serializers.py,sha256=gZFJ3qhW17dOPigqY0_Qx9xVQeRD1V8fy1j07QXKmUc,1384 +rest_framework/authtoken/views.py,sha256=IXX6PNUwguRq7ZGebrG-vguAMFkSOmq4qPJI7CTcyAY,2216 +rest_framework/checks.py,sha256=WO57Y9Ks_MQKq0FsFVXoP7odQMQyVIr-LbPt6fcx2LU,970 +rest_framework/compat.py,sha256=52aw9WqrDOa6cKaYOfzDKQnGpm-m4D48Qgcm2Irr450,4387 +rest_framework/decorators.py,sha256=k0R6o5o6h7-Hwr-ixf7oWsPwoxt27ap8n6Cn5PjZ3sg,7787 +rest_framework/documentation.py,sha256=EKZGzMUzXq8H_bri0dRJ89npwT6JwKQOENNPHaYQ5-k,3054 +rest_framework/exceptions.py,sha256=RhsXKwTShr5lMETxWNhCjM72rrfPSU1fP2u5LFiIGJ8,8043 +rest_framework/fields.py,sha256=TEQ1CZeQSJzVaBv6zwdbxX8Fk5yGWcrJ151Ga2X8n1U,69462 +rest_framework/filters.py,sha256=ORaKCx92DjSFX1HXkX6vGnhdXOC8bIx1AJD_F1coWIg,12488 +rest_framework/generics.py,sha256=riKBm8m9yxSny6adlBiePeCqWmu4RJPvVVkxmiuidIw,10040 +rest_framework/locale/ach/LC_MESSAGES/django.mo,sha256=3LyV_OzlARDfeuhvZp6OCdt6xH4esewLh5fU8a4eXxg,472 +rest_framework/locale/ar/LC_MESSAGES/django.mo,sha256=th7efxoto1P_iFZKFezHyVuQYOyspbvusm-fAnk5j84,12150 +rest_framework/locale/az/LC_MESSAGES/django.mo,sha256=B3IIoIYmYPkM9iOsm0I10Zb__w7rVVZ9HhWMhbvv5bA,10428 +rest_framework/locale/be/LC_MESSAGES/django.mo,sha256=TvqPpcP1PWvOx6RwBM1vO5fUaqXMRddmwRb05TuqTaQ,614 +rest_framework/locale/bg/LC_MESSAGES/django.mo,sha256=j8UXyFO7YdRsZ5OwtI15fUmrN6QnSJonpX_eIo74WCQ,13083 +rest_framework/locale/ca/LC_MESSAGES/django.mo,sha256=5h1KYV14JnFIX02ABU8WeFlqJGKCBKtgbRMcR2lzjpE,9300 +rest_framework/locale/ca_ES/LC_MESSAGES/django.mo,sha256=Toqlh8opAxtm46W5UGvLZW_nsXjkjUoAMQjc5dF-WiQ,487 +rest_framework/locale/cs/LC_MESSAGES/django.mo,sha256=KMLhUh-qAPlAvKWaGZYgFJKCMaLH0N5SdgOY2d0gSig,10519 +rest_framework/locale/da/LC_MESSAGES/django.mo,sha256=_RNWqDszm5SPR6m6LJSkpkLsOlxX6FCm7mV2Ln8JDZw,9955 +rest_framework/locale/de/LC_MESSAGES/django.mo,sha256=8v8nY8HmMIOteF2s24sTFK7lOKTShTxvMWhbm0qMm3A,10490 +rest_framework/locale/el/LC_MESSAGES/django.mo,sha256=RbxSQ08hrFB173iNcfVTvoAU5teTkl9SKsEFLuOK2BI,12933 +rest_framework/locale/el_GR/LC_MESSAGES/django.mo,sha256=_oUBBH7uSAlTcQ3Km2U2kwFRMnpG3Fz0X1CuAQbU_9I,486 +rest_framework/locale/en/LC_MESSAGES/django.mo,sha256=NTQOG7G0S5cILPfjiwr5jGLFS6-BLSkJWz-IZ34WnqU,12285 +rest_framework/locale/en_AU/LC_MESSAGES/django.mo,sha256=6yNdQp3uMV-f63P7xuvX6fzRyb-iG3upwHmz2cJHNlU,491 +rest_framework/locale/en_CA/LC_MESSAGES/django.mo,sha256=JT4wh-kQWfeO4T-be9lwbDaLz0QVu5P0SnK4BDJfOSQ,488 +rest_framework/locale/en_US/LC_MESSAGES/django.mo,sha256=UXCQbz2AxBvh-IQ7bGgjoBnijo8h9DfE9107A-2Mgkk,337 +rest_framework/locale/es/LC_MESSAGES/django.mo,sha256=2NqCq4Rk6k0sZUOk1OO55VWSOgHRnuUuvgcc3wB7MUY,10627 +rest_framework/locale/et/LC_MESSAGES/django.mo,sha256=-ZMrAfUiqMzPnxJD6qNCY3RqRl7m0jY8LjwFz68dxM4,10096 +rest_framework/locale/fa/LC_MESSAGES/django.mo,sha256=a1B7CgBlwt9D_cHqB6VJbj-PUiSvG1QNsij6cPVom_s,11976 +rest_framework/locale/fa_IR/LC_MESSAGES/django.mo,sha256=29zq4NzdtVBc0Q_LaoHROnNYXtwb4kSdrMMKs-yyCyE,11989 +rest_framework/locale/fi/LC_MESSAGES/django.mo,sha256=-TPYxiKlLMCOfjPRM9rvvO5KTuj5XnrJhABw9Db8dSs,10197 +rest_framework/locale/fr/LC_MESSAGES/django.mo,sha256=56a48bfyLGVnGrTiKqGNe6fb4fYDkoJSarLeP3dbmc0,10662 +rest_framework/locale/fr_CA/LC_MESSAGES/django.mo,sha256=AIkTPlyS_J4u1-DtjroxlYZ4_xK8s_dBR8ps593XVTE,486 +rest_framework/locale/gl/LC_MESSAGES/django.mo,sha256=-dhiLFcnF6nZSm2F1jLGOu9JYHAKz468w2hD48Hf7qU,474 +rest_framework/locale/gl_ES/LC_MESSAGES/django.mo,sha256=IQjsBgbgFHZ48iWHMeHzfbnlhReQNoeFq3pCYMSb8gA,628 +rest_framework/locale/he_IL/LC_MESSAGES/django.mo,sha256=JmkZjjtdpkm_51gHbWDa6LnAXK8dHBdxWvkZLVchTPU,487 +rest_framework/locale/hu/LC_MESSAGES/django.mo,sha256=DsEmG9PSPMP_thCE2j_Z4MUIxVb9JY0IPxwxSjit9DI,10844 +rest_framework/locale/hy/LC_MESSAGES/django.mo,sha256=r5Gjh2x7RTGjPt05NzsxzeS6nQxqNClR9J1UYpR1W30,12885 +rest_framework/locale/id/LC_MESSAGES/django.mo,sha256=X8mmBaEYQ0U1PBKpmVzEv07jqdrZAQmWaINMjnFEIGo,5188 +rest_framework/locale/it/LC_MESSAGES/django.mo,sha256=Seuvke2Kb3XZyBgsLndK4kc4ABOHFb1ok2SNWZLBk3o,10480 +rest_framework/locale/ja/LC_MESSAGES/django.mo,sha256=D19OXy2vt0t54yPYCbDe9BGwnrBemcH3zkwXD2YphcA,11759 +rest_framework/locale/ko_KR/LC_MESSAGES/django.mo,sha256=M09dPfSrFqcX8Te7xXGgnhgIk52LhxksiLrooZ3sFvE,11698 +rest_framework/locale/lt/LC_MESSAGES/django.mo,sha256=-6W2uJJ3gYEYkd1H1CF4MfRykn75_HJ2V4vgm0rlu48,5056 +rest_framework/locale/lv/LC_MESSAGES/django.mo,sha256=VcPsDP3hVZt8YMlq1RdgYm_WUIo5QWt4SBTe9KyIn_k,10423 +rest_framework/locale/mk/LC_MESSAGES/django.mo,sha256=HA51S3Es40nX29BcU4Qh1BNbLWLXjpa51p_7c_u-Drk,12121 +rest_framework/locale/nb/LC_MESSAGES/django.mo,sha256=U0jYbFS-Kxj08Thb7fF8oMrab5S2nu1lhlQhsUCpi-A,9928 +rest_framework/locale/ne_NP/LC_MESSAGES/django.mo,sha256=7ohVBTeeKYoPiTFwYA14fSxkqRgx7GBZAgyjDk15OTA,15636 +rest_framework/locale/nl/LC_MESSAGES/django.mo,sha256=ad5I7tegcNW8FOqn9E1O_cqrtHdvlkEoKvHEokjRqQo,10163 +rest_framework/locale/nn/LC_MESSAGES/django.mo,sha256=3oQQnH3UF9nHmTsj3pqv5R2iakv-wdjy0k_EQvbo4ds,483 +rest_framework/locale/no/LC_MESSAGES/django.mo,sha256=ZpgR0-XMg7oQXeS9Q6Pdhk1Y8G6hXnQVx1x3iv8yAA4,475 +rest_framework/locale/pl/LC_MESSAGES/django.mo,sha256=0mc9ltZnrL60-T2yOLavGH8DwRnEN9wu8a8_zB5RkgE,10673 +rest_framework/locale/pt/LC_MESSAGES/django.mo,sha256=j1Y5sfsqtQYVEKWxbrO6AJHw_hfh5adocwYdaVyG9CY,10382 +rest_framework/locale/pt_BR/LC_MESSAGES/django.mo,sha256=mn1IClfteWM9J3Y6uvy5arKR7AdzEwhvqtw1Xvdpuew,10397 +rest_framework/locale/pt_PT/LC_MESSAGES/django.mo,sha256=sCkzHosWkIhNbRljJWvK4x_UlbSK3SNickNyp17cOpM,493 +rest_framework/locale/ro/LC_MESSAGES/django.mo,sha256=CEab6ZJC7xeFrN2v6UKkDWNegXkbGpFBZSWeno0qN0w,10701 +rest_framework/locale/ru/LC_MESSAGES/django.mo,sha256=IxiOtRY_UAVUT6Is4Gh09pGj7kLyqBHaUJh7S3uiRcI,13160 +rest_framework/locale/ru_RU/LC_MESSAGES/django.mo,sha256=AmZPL7_x-1BT9COfEDAitvyhXvkWeYINhXj1htyhq0g,5208 +rest_framework/locale/sk/LC_MESSAGES/django.mo,sha256=hPXX50ijI61arUGVjMhEhz_S-zZqA1WmUNjTEJUgI8I,9164 +rest_framework/locale/sl/LC_MESSAGES/django.mo,sha256=_6IhTyJmeOb73eMhSmeVBVoKztxBQ1ldnwET_N-rT6g,9985 +rest_framework/locale/sv/LC_MESSAGES/django.mo,sha256=OV3cEUiafwnLwesNOlHr3h0fur29GJVMFHAUYUdIHBc,10204 +rest_framework/locale/th/LC_MESSAGES/django.mo,sha256=FHJKADSeYWVdvsQ285jz_865vzUelHIqkxSa4QSLlOw,8880 +rest_framework/locale/tr/LC_MESSAGES/django.mo,sha256=UJEBznyWnRnVKmRn56MwAGc7s0FAcToGXmd-O-k-58g,10073 +rest_framework/locale/tr_TR/LC_MESSAGES/django.mo,sha256=Nm3vmt38r3DxKdl2Astxny_E4IEkJlhDkzYpwiio7NQ,10292 +rest_framework/locale/uk/LC_MESSAGES/django.mo,sha256=fWmrOfNUsagv9jsIfQzP4QYQRKUugCaTBitRfvG-7gg,13245 +rest_framework/locale/vi/LC_MESSAGES/django.mo,sha256=_TekVPiXtQP0HsHrqt0S5fa669Auow5EaDsvoimUzPY,2179 +rest_framework/locale/zh_CN/LC_MESSAGES/django.mo,sha256=K9xrJnn5hyO5dM7sveYajph49Vveoo9kBLxrs9nyXfQ,9915 +rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=GVmnW3r-jCqyllmF87w5sxGPLA3p49BaS0n0ufRqsI8,9938 +rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo,sha256=fZErAq1tsNswSyVXhMm96AUqQM3IwIZarbdLTWDhu64,4809 +rest_framework/locale/zh_TW/LC_MESSAGES/django.mo,sha256=x_Ba_GQLy-sIMYRRex0kGn9zZKzY5L8y69SSY2HasZU,481 +rest_framework/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/management/__pycache__/__init__.cpython-39.pyc,, +rest_framework/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/management/commands/__pycache__/__init__.cpython-39.pyc,, +rest_framework/management/commands/__pycache__/generateschema.cpython-39.pyc,, +rest_framework/management/commands/generateschema.py,sha256=M0J_eqPs-8WgM9qc7E8k_c_9kJtB5iZUOssExz8TaTo,2800 +rest_framework/metadata.py,sha256=ZFJdT5qzjiU2FPVgxotHcEbjo01QRGqKr8k31KV4rA4,5916 +rest_framework/mixins.py,sha256=bqdlz6p0JlhRNIA3cxhjPkY24dPTWMcMsvWUfx-Mews,2937 +rest_framework/negotiation.py,sha256=m6nEv7R_3AojWX6ry22hULSrvLyRscwgYlbNsW37iho,4033 +rest_framework/pagination.py,sha256=-O2IZPlPQZE_O-8KcVcC4ezHeywoBCkKo3LgX_6AHwM,35676 +rest_framework/parsers.py,sha256=lIsl5b8o7lGNsmcSRTsHBcVnQJeWOgzciXoq3bLBjGo,8337 +rest_framework/permissions.py,sha256=BkZxN1_VpiJyCixfv4jO6gY_xkpjukEj1rvj-pz7n_E,9112 +rest_framework/relations.py,sha256=nyH-hEmyrl-oX-Eo7cJMs9YRmnIWs4d1mjbhRvzMTCI,20205 +rest_framework/renderers.py,sha256=7KYFnAy3b6FEN8UUCBpNHqK6U3P26lVqLB94gTV1yiw,40314 +rest_framework/request.py,sha256=8hhuR_prYZso71m0v8OEPq8Z_kifV5s0EBNRAh_Z3MQ,15299 +rest_framework/response.py,sha256=1svv8WGiOOgTiXBef3jfsYu3rN_lrAO6j2_IHKdmn50,3433 +rest_framework/reverse.py,sha256=veLMPqo0v81NP0X7J_I6rCqtirJmCnJ4-Mm3-lOgBbY,2144 +rest_framework/routers.py,sha256=OhqWTDr8ogyvrHu__-FBnt5Z6CxCNjs2Os8ajdZFCZE,12211 +rest_framework/schemas/__init__.py,sha256=1CkgqzGW08pYGEPFqrUO4qjbUohDfcDf2boFe7-TVSw,1781 +rest_framework/schemas/__pycache__/__init__.cpython-39.pyc,, +rest_framework/schemas/__pycache__/coreapi.cpython-39.pyc,, +rest_framework/schemas/__pycache__/generators.cpython-39.pyc,, +rest_framework/schemas/__pycache__/inspectors.cpython-39.pyc,, +rest_framework/schemas/__pycache__/openapi.cpython-39.pyc,, +rest_framework/schemas/__pycache__/utils.cpython-39.pyc,, +rest_framework/schemas/__pycache__/views.cpython-39.pyc,, +rest_framework/schemas/coreapi.py,sha256=xLT-BHA2anDRI7tQa-LAl8B8sFpQo0JefobhBBDAFZw,20640 +rest_framework/schemas/generators.py,sha256=HJ71FwiGFEj8EjDd0f7VzWI2msTTGHPTFO0xFynahvU,7995 +rest_framework/schemas/inspectors.py,sha256=gN2tQUF8QMmcs_djzDbqvDJtbZYMF0JdzFAnjvXwbEI,4196 +rest_framework/schemas/openapi.py,sha256=S_4_0PApt6P-fpLnfn_qGnwgaq3NguZHs4zKxBRHkEw,30368 +rest_framework/schemas/utils.py,sha256=iGGJfS7S-MwTvr2gPystxrEO3TaRb-x0fpyUjDWkkW8,1195 +rest_framework/schemas/views.py,sha256=epv16dSdBIUpmqOMKGvS61wtSCJhlzT1JZH84w4QNHw,1836 +rest_framework/serializers.py,sha256=-uVUqgjaubx-YWu-fAKSSOfXsv5R06XchuOSAQYi_wU,65942 +rest_framework/settings.py,sha256=rAJEzjdthuFSXBzDyWXOp_THgQWa87wPSKBjPZGbe3g,7808 +rest_framework/static/rest_framework/css/bootstrap-theme.min.css,sha256=vaEzattT7-Q3c4L2iLoQj39wxwDIYKV5oupPN6pXDQk,23411 +rest_framework/static/rest_framework/css/bootstrap-tweaks.css,sha256=wXtAjvAHjAcfJg_6Gi_KgcWAe5cuM1_r79nrm9P8PgU,3385 +rest_framework/static/rest_framework/css/bootstrap.min.css,sha256=H0KfTigpUV-0_5tn2HXC0CPwhhDhWgSawJdnFd0CGCo,121457 +rest_framework/static/rest_framework/css/default.css,sha256=EWV35tstD5m0GezGG0U5Fa8O_4IGsL8_noeJdxnBs3s,1152 +rest_framework/static/rest_framework/css/font-awesome-4.0.3.css,sha256=MIPo07Id3D8ObWXsNYCqbt-q3KXZc32cqifmojPhzPM,21658 +rest_framework/static/rest_framework/css/prettify.css,sha256=-ZMq8eZ6blEFtxcVudM1hzv4gFwBwqlgPjHpbMSpWBk,817 +rest_framework/static/rest_framework/docs/css/base.css,sha256=fZ9Os4iAF3DBq3Aww8qbbRypx6fkGV_-PjxQ8YqssM4,6156 +rest_framework/static/rest_framework/docs/css/highlight.css,sha256=h-4d4bDFtOId4PkL4xBXl-XtRfav47B8cPUBoYWlc3M,1682 +rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css,sha256=w4_hEoz19Kk_fC7uLct1FzPE3gEIvNulKydv75PFyew,1307 +rest_framework/static/rest_framework/docs/img/favicon.ico,sha256=Ww2bNjGXyE2y9FeO6sW7YksqF2CQ89pdTrksiFbP4n4,5430 +rest_framework/static/rest_framework/docs/img/grid.png,sha256=bipYUDSUpwgQWsZG069cCMjIkDJbt4GiV9EPkf-Wipw,1458 +rest_framework/static/rest_framework/docs/js/api.js,sha256=YpTnPzrsK4dDGVmcRUpMrF4tE2Dk22nfA60DN2hdBlo,10539 +rest_framework/static/rest_framework/docs/js/highlight.pack.js,sha256=TpVs16YPyRxjTs122mIsboTVOpoTUb1AmzlBnOHjU4A,300764 +rest_framework/static/rest_framework/docs/js/jquery.json-view.min.js,sha256=xUY7pJMePDtQPh8KrWWP8GcwFpNVJn5AhLbXraVWvZY,2700 +rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot,sha256=OeI3wHQD5i8AvW3fC1nTNJx704aSUKqtw4lBnbaqQO8,38205 +rest_framework/static/rest_framework/fonts/fontawesome-webfont.svg,sha256=m_HPYZuOy2MUAJCBqEjDemn70HtKKnzeM59jQrjrom4,202148 +rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf,sha256=a0k0itU4htCc5MMvoUbomcgg3j-FqN03BKBiTrO_f6E,80652 +rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff,sha256=D9KP7Onr1ga4sHFGDr0_wu17x6Zu-RyINPEd-sq0qEk,44432 +rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot,sha256=E2NNqH2eI_jD7ZEIzhck0YOjmtBy5z4bPYy_ZG0tBAc,20127 +rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.svg,sha256=BogzMSnL5KzIuuo7NfgBUocHjzA1ufVnaDid2c-KDZE,108738 +rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf,sha256=45UEQJN1fYKvyxOJV9BqHqk2G9zwtELQahioBRr1dFY,45404 +rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff,sha256=omOU9-3hAMoRjv8u2ghZYnWpg5uVnCJuFUOVV6WoB0I,23424 +rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2,sha256=_hhdEaSWdokNR7t4MxKgzaWkTEA5IUCU55V7TAQO8Rw,18028 +rest_framework/static/rest_framework/img/glyphicons-halflings-white.png,sha256=8ODZWpyKvN-r9GNI4tQoWCm7BJH19q8OBa9Sv_tjJMQ,8777 +rest_framework/static/rest_framework/img/glyphicons-halflings.png,sha256=G9UbUyeER8HbM_AMR3PnEdsh5Vfs3SbZua6Wypk_BeI,12762 +rest_framework/static/rest_framework/img/grid.png,sha256=bipYUDSUpwgQWsZG069cCMjIkDJbt4GiV9EPkf-Wipw,1458 +rest_framework/static/rest_framework/js/ajax-form.js,sha256=pF_s2ECmU-o6iU6vKYyu3VMzqn0esRDgDpR7taSMUHY,3597 +rest_framework/static/rest_framework/js/bootstrap.min.js,sha256=nuL8_2cJ5NDSSwnKD8VqreErSWHtnEP9E7AySL-1ev4,39680 +rest_framework/static/rest_framework/js/coreapi-0.1.1.js,sha256=_gzyPn-2cx0Wgfjvj_tlUhPf4laKnzYbgkcr_uNGW6E,157600 +rest_framework/static/rest_framework/js/csrf.js,sha256=eS0bS4fMRRLgXQYqgmsaDLs6R2Yklj5PkZLIUCk2vdg,1719 +rest_framework/static/rest_framework/js/default.js,sha256=mJOP3JMDyQnRSX60X_T4WgtYzOBNDYpqc1ZhOUC85iM,1268 +rest_framework/static/rest_framework/js/jquery-3.5.1.min.js,sha256=9_aliU8dGd2tb6OSsuzixeV4y_faTqgFtohetphbbj0,89476 +rest_framework/static/rest_framework/js/prettify-min.js,sha256=4uV247xgfNF5_1EZRwEPZF00QaNTE67A29BsRDf4O3c,13632 +rest_framework/status.py,sha256=B85dLJEh3qU84eBleBPIF7rpUpiHLyjHsnzTWj73XtQ,2434 +rest_framework/templates/rest_framework/admin.html,sha256=gf7_-d0EgFYquGMpoYAiYjILGsuw8iwgDqqEIop-ZQw,10904 +rest_framework/templates/rest_framework/admin/detail.html,sha256=_ONyhbRK32d7IsUxmUVKQCWjxzZqwT-zK2u1CgB4lvk,306 +rest_framework/templates/rest_framework/admin/dict_value.html,sha256=KidLEu38kqvPGElnFDjiQMh72xsjT5Sq3L4gchrqMps,242 +rest_framework/templates/rest_framework/admin/list.html,sha256=sFoqx8GeL2gFZ0Yx35dq6DVXEgQIIAFYlA8h-65FEQM,819 +rest_framework/templates/rest_framework/admin/list_value.html,sha256=Z9VmS1f3mwk5LypgA8HAvogVPbDa09hABa389AYxQCo,241 +rest_framework/templates/rest_framework/admin/simple_list_value.html,sha256=d9cVgqyigNa0K0W7CYPNGcvRAqy5M-ZJAzkgA-Al70Q,121 +rest_framework/templates/rest_framework/api.html,sha256=6GkppzK1R50yjAVZJoqFnR_FdGlZBpfftD2m2WNPTVM,116 +rest_framework/templates/rest_framework/base.html,sha256=yI6-j4XBelGxqAdGr69zxEVnqu3rsGdqVccvk8YsyEg,13945 +rest_framework/templates/rest_framework/docs/auth/basic.html,sha256=GsLRwieUOwxvQRSI4nRz2PYVq72e7NLEXiprmoTbzsI,1250 +rest_framework/templates/rest_framework/docs/auth/session.html,sha256=jhtMeLg6Lq5yPb2noOabUj61UAOQe--NQGEYYwWZY_E,1154 +rest_framework/templates/rest_framework/docs/auth/token.html,sha256=0KcVSQ8w2ZvOO8A2oNlTwqmYyJ2O-12eWZL-NCBm1Rc,1618 +rest_framework/templates/rest_framework/docs/document.html,sha256=r7bWwBnt2NGaP3XbABVg-yxCiRkQuB3_-nh1cK1Wr5M,906 +rest_framework/templates/rest_framework/docs/error.html,sha256=AtDy1ZbSUG9B_mm470T8CTY1w9PxzLSJkBYNoJAuXos,1850 +rest_framework/templates/rest_framework/docs/index.html,sha256=dBZQhcy9bzPmtbkBrWJJpshbgWSyCTUsZblk5jGHk-c,2519 +rest_framework/templates/rest_framework/docs/interact.html,sha256=_8yaSo9DUW8eRVH-KEH5zJJ8MBIlI5QM_ajWPb4yrI8,1831 +rest_framework/templates/rest_framework/docs/langs/javascript-intro.html,sha256=TOLjcCnyA0MYI5w99Jtg2mN7w1wjXz0lfSM8b0wabZ4,330 +rest_framework/templates/rest_framework/docs/langs/javascript.html,sha256=vy1N2rMRSTKZjEo5mZ_rIp2g57TSa9PuuHXzeghpksk,714 +rest_framework/templates/rest_framework/docs/langs/python-intro.html,sha256=LBpDVN9qkOO5Itej4M6gl4WynB4Q2CpJ_ZEti1fAY8I,189 +rest_framework/templates/rest_framework/docs/langs/python.html,sha256=y24kdg1AdRDAWl2nu6UksrXMjbMTQcdTXuRpTOXSIJo,676 +rest_framework/templates/rest_framework/docs/langs/shell-intro.html,sha256=TQo-Y2ksj2T9kzsmENH7MEB1Mt940wW941AGGbIRjVE,189 +rest_framework/templates/rest_framework/docs/langs/shell.html,sha256=jcRLGn3Eu0Xc-lr7lGFUj-1z0e9wdMwfbFIi3Cs8mV4,441 +rest_framework/templates/rest_framework/docs/link.html,sha256=ITaS3W3sqL7gOHwVvtMi2fcuETv1eVhQedlkOsT7pTg,4624 +rest_framework/templates/rest_framework/docs/sidebar.html,sha256=S_D8Tbxo08fbmVT9FmEc8No1ESS1JMJID4q5SZRz5ZY,2581 +rest_framework/templates/rest_framework/filters/base.html,sha256=OjBIbd29onA2R-iUmkwrlXw0N5CJIsU1FOpsEJ-ZJRg,610 +rest_framework/templates/rest_framework/filters/ordering.html,sha256=wiAOyvmjypM4DJGMMWbdD4Il26Z8P7J_HcIurjv3xhA,556 +rest_framework/templates/rest_framework/filters/search.html,sha256=rO3_-Z2QVIom9WVErxMprYn-sJEe1inXHH4oAG-juWk,467 +rest_framework/templates/rest_framework/horizontal/checkbox.html,sha256=t8EJW3CIcuDb2NSa7VElCzj49b0S1RcZcS7mdkOEXMw,658 +rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html,sha256=iUVqbdxQ37iejUtfT0x6UeF8RcA4t71B61dhiQsqFgk,1184 +rest_framework/templates/rest_framework/horizontal/dict_field.html,sha256=VgZ3iSqrPK02VU3gDoDuK0szJbu_PGex1HnYIpYx6ao,324 +rest_framework/templates/rest_framework/horizontal/fieldset.html,sha256=bXy7Q_zBGMpE8a7K-2SxJfVDMsHZ24xniLoCw8Xmzyg,480 +rest_framework/templates/rest_framework/horizontal/form.html,sha256=yo9PS2__lzBuYSue812MwGmi3HT6kIAzHvAQBaqJAGY,149 +rest_framework/templates/rest_framework/horizontal/input.html,sha256=pOLeWdni3DNqZCN1Dbvj5xA0D-jdM0QEok_1XpIHfBE,889 +rest_framework/templates/rest_framework/horizontal/list_field.html,sha256=_BlSeMbpE8vpnyqHp6jmou2f441hrtYBvbdBjYxJeew,317 +rest_framework/templates/rest_framework/horizontal/list_fieldset.html,sha256=5W-9tZ-GvTHR4JlHwVdDJo-1FlH6-cmUu33x2RKsnkM,384 +rest_framework/templates/rest_framework/horizontal/radio.html,sha256=xpgDdvdW3FojnB3x-OqWzIiSsdcw9sd1z-g8vYz7ofg,1796 +rest_framework/templates/rest_framework/horizontal/select.html,sha256=_Eow62zUMM9VWJ03P1HlfTNB4vyv9GfqB9WjYRhfTBY,1222 +rest_framework/templates/rest_framework/horizontal/select_multiple.html,sha256=C_98qhJwLTMNfbFBn6nhD3Qjdp6tcHwq82d8S3pFwSw,1229 +rest_framework/templates/rest_framework/horizontal/textarea.html,sha256=8myshnZKHjohQtuVVPN2gmcYN0RTMNGkSh95CEIobx4,782 +rest_framework/templates/rest_framework/inline/checkbox.html,sha256=zpUnpKg-oMZ843vtmW7ec8lzxQttwGpFE7F2vmHxZFw,294 +rest_framework/templates/rest_framework/inline/checkbox_multiple.html,sha256=vGndr0cjo-Q-sj6rn0JdH9LTn2BO27xvcKBY7YBbJ3c,487 +rest_framework/templates/rest_framework/inline/dict_field.html,sha256=4EWRFxidLMSc_2kbaP3VcXSaQFVAjfmPi00gb3pHiKg,228 +rest_framework/templates/rest_framework/inline/fieldset.html,sha256=acBYZDeivx33bLNIYNKlXG_11YDBtIVUihbFeHGmy8M,171 +rest_framework/templates/rest_framework/inline/form.html,sha256=yo9PS2__lzBuYSue812MwGmi3HT6kIAzHvAQBaqJAGY,149 +rest_framework/templates/rest_framework/inline/input.html,sha256=mMWTdifDKSnma20ga7gYoHGuGbHAJuxinBQIrBqjs1A,530 +rest_framework/templates/rest_framework/inline/list_field.html,sha256=bafZBHQw3YqlJYGVaxFJgtj_pon9BiuEuCU7WU6a-LM,221 +rest_framework/templates/rest_framework/inline/list_fieldset.html,sha256=gPL69kBW9RBq_qIRvFrc26a0bvFqrCaWkOnarN5uJsw,62 +rest_framework/templates/rest_framework/inline/radio.html,sha256=nqjfBZcfpFjZ-ELyaLkio-wlw5fkPAG1OAlEbAsxk2Q,793 +rest_framework/templates/rest_framework/inline/select.html,sha256=rIDuGfGFZuc-5CUVMIRne4x0vBbAg-z_MWz139hVJvw,879 +rest_framework/templates/rest_framework/inline/select_multiple.html,sha256=3zoyMlBWFNDVT7yI-zgN3lMVPGmWOBRKB6-Qyzlu174,917 +rest_framework/templates/rest_framework/inline/textarea.html,sha256=0ZpewR8LDx9xLoMgs59r12AQsZVnbolNf4P7EsnIrEc,376 +rest_framework/templates/rest_framework/login.html,sha256=mZOh88AfLsiRWGPjJlvvOkvuvmMQwxTyjGZrzcXkxlQ,122 +rest_framework/templates/rest_framework/login_base.html,sha256=ZsWKtNWD0Em0cxHn-bjkeEFTecTOzsL4Qu_pgAO48L0,2857 +rest_framework/templates/rest_framework/pagination/numbers.html,sha256=6cnFpCm_xLvgUCkyGPUtV2zMgBz_mvBJhu98aYWbOoM,1183 +rest_framework/templates/rest_framework/pagination/previous_and_next.html,sha256=tvVdDzQeAX_mqre0ei8PO-bviFlTmEWVEGpfPQ9tmuc,456 +rest_framework/templates/rest_framework/raw_data_form.html,sha256=-96O3lHTPA0zP3zq19xofYvzT8oPbmghHY5DvP-laIM,335 +rest_framework/templates/rest_framework/schema.js,sha256=UFL1wg4ArGD2gAyDfqVNCTm1V9_5dxv7kYGyR5oBaGc,136 +rest_framework/templates/rest_framework/vertical/checkbox.html,sha256=5ZMJNFq6ERwAI06iGHL5m3XmMIgTmJG7vaw6WczjTfg,543 +rest_framework/templates/rest_framework/vertical/checkbox_multiple.html,sha256=MzJT5dw-vV2zNaJYgmL6W9lTlDx_CDGV-vA8AEEW6Fs,1157 +rest_framework/templates/rest_framework/vertical/dict_field.html,sha256=Pqk22yWIWaXUJYhqYCHmGwOCiKbhvUPjl-IWsC8uSbs,252 +rest_framework/templates/rest_framework/vertical/fieldset.html,sha256=TQsf1yWktrPAiPZCavwjyX3p5p9IjFk1bQOAXynS73E,346 +rest_framework/templates/rest_framework/vertical/form.html,sha256=yo9PS2__lzBuYSue812MwGmi3HT6kIAzHvAQBaqJAGY,149 +rest_framework/templates/rest_framework/vertical/input.html,sha256=lLcDs6xixer6eQZqjI5x-5pThUDNcil9lWNYTmNOIkk,801 +rest_framework/templates/rest_framework/vertical/list_field.html,sha256=JySAf36X8WXjDtNObb1kpYb6_lJXsLqB6Z99-c2_MnE,245 +rest_framework/templates/rest_framework/vertical/list_fieldset.html,sha256=TvSzps4U1WQTvbxWRrubRvjfiD8KdUym4wthTRxsumg,222 +rest_framework/templates/rest_framework/vertical/radio.html,sha256=xOYpOkf0SkCnNOwLSXZ9nJkjIc-KY3XnRU1_JJwcd8g,1809 +rest_framework/templates/rest_framework/vertical/select.html,sha256=qZcO0qN2XZIcGw1Rrsdkm8dfVqWw4nKMVaUettX-W3k,1162 +rest_framework/templates/rest_framework/vertical/select_multiple.html,sha256=97Rtt11FQHTX0yk-ksj3dchMXO7BBC2U4_6WZqqpegI,1184 +rest_framework/templates/rest_framework/vertical/textarea.html,sha256=xaGbiWJDGIWHgWLSCBpZXjf8FVDBo0LvCqHlSs-h5EY,694 +rest_framework/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/templatetags/__pycache__/__init__.cpython-39.pyc,, +rest_framework/templatetags/__pycache__/rest_framework.cpython-39.pyc,, +rest_framework/templatetags/rest_framework.py,sha256=IyEQi0rsZfbepCiM6tWDQOl5See3tAamz56HRY1IyLw,9845 +rest_framework/test.py,sha256=BmwbQVMGvKg7_bAH8xoP5ZWeBsBXqSMKpeKqVIZjmqE,14791 +rest_framework/throttling.py,sha256=5SmePnTCS74W8tH6VAYHeVMUcoEVTKUaA2VnQqovtGY,8016 +rest_framework/urlpatterns.py,sha256=j2SytOi_09uhyvZsVQLhkvnlc8slxE2_4jWUxf0pRb8,4235 +rest_framework/urls.py,sha256=JiEpSQau4E-m8xjNj-4mdcP3oGdf5MnsvFdnTFk7y0Y,615 +rest_framework/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +rest_framework/utils/__pycache__/__init__.cpython-39.pyc,, +rest_framework/utils/__pycache__/breadcrumbs.cpython-39.pyc,, +rest_framework/utils/__pycache__/encoders.cpython-39.pyc,, +rest_framework/utils/__pycache__/field_mapping.cpython-39.pyc,, +rest_framework/utils/__pycache__/formatting.cpython-39.pyc,, +rest_framework/utils/__pycache__/html.cpython-39.pyc,, +rest_framework/utils/__pycache__/humanize_datetime.cpython-39.pyc,, +rest_framework/utils/__pycache__/json.cpython-39.pyc,, +rest_framework/utils/__pycache__/mediatypes.cpython-39.pyc,, +rest_framework/utils/__pycache__/model_meta.cpython-39.pyc,, +rest_framework/utils/__pycache__/representation.cpython-39.pyc,, +rest_framework/utils/__pycache__/serializer_helpers.cpython-39.pyc,, +rest_framework/utils/__pycache__/urls.cpython-39.pyc,, +rest_framework/utils/breadcrumbs.py,sha256=IwjiRRwqTjXkdFR4sIt76EV9ucvzQA-ORiF1yI1Tj2I,2039 +rest_framework/utils/encoders.py,sha256=jKzfbZmqXXqeHAZUWClq2Y4TRrrEjHhEfUkCiCoimMA,2523 +rest_framework/utils/field_mapping.py,sha256=-EvQKsX_yZk4hG3kBYrue7Hzy7KROQps9sXJ3AJKqVY,11366 +rest_framework/utils/formatting.py,sha256=z292GYwr3WpcgRECnlouhQZgQk-m7LhsoQXRz1O0p-M,3015 +rest_framework/utils/html.py,sha256=2Bas2KS7dst6mdGUVSROPi9RUyD38Z1SPza_0WbV8Ts,2294 +rest_framework/utils/humanize_datetime.py,sha256=CeC4QWwfFKdn2RXHGYRaqVcOwp0J80J_AJsnx2ClVJ0,1281 +rest_framework/utils/json.py,sha256=1qVOOt_DuaQqO4ePub-jT29lZG-9sNmGLDEqW42BAZ0,1027 +rest_framework/utils/mediatypes.py,sha256=LDsDDnWICYOXL-4WEeK1xDUvJuQfDT-RvsNW_cePHtY,2572 +rest_framework/utils/model_meta.py,sha256=XAyM0os1O3m-lKIH6Qod3_k_7b1njSSgs_lCog5KQcE,5245 +rest_framework/utils/representation.py,sha256=Twi111DBDw-wWZz28tyW5bL0UxBfVCY80AAgh3Z_9FI,2976 +rest_framework/utils/serializer_helpers.py,sha256=1b1ICM1RwXaHfGiyacTNy1jru9UcwmTs0ZWkegLDJXo,5325 +rest_framework/utils/urls.py,sha256=5hagmITUV4AvYmhFRHccOvXx3Yn-OL6yEA1eYT4_bk8,1052 +rest_framework/validators.py,sha256=6lCy8AnQBipJSqDwF1_c5CiS6YW0zoWkId4X7GzFUG8,10338 +rest_framework/versioning.py,sha256=Uqnd1FKUJw0xQqHiIKWMyxhUfhtauVDn-39LYOAfayc,6791 +rest_framework/views.py,sha256=R7Q0LkGui_aSpHDj0Uc1Pbk00LU0eyKY-ge5QgrsGsM,18777 +rest_framework/viewsets.py,sha256=sxe4L3RmaMPS2zleIagUJtHfFAS04GAVLuDylfUSxv4,8889 diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/REQUESTED b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/WHEEL b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/WHEEL new file mode 100644 index 0000000..83ff02e --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/top_level.txt b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/top_level.txt new file mode 100644 index 0000000..6adea4d --- /dev/null +++ b/venv/Lib/site-packages/djangorestframework-3.13.1.dist-info/top_level.txt @@ -0,0 +1 @@ +rest_framework diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/INSTALLER b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/LICENSE b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/LICENSE new file mode 100644 index 0000000..be7e092 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017-2019 Jason R. Coombs, Barry Warsaw + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/METADATA b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/METADATA new file mode 100644 index 0000000..d4c3474 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/METADATA @@ -0,0 +1,118 @@ +Metadata-Version: 2.1 +Name: importlib-metadata +Version: 4.11.3 +Summary: Read metadata from Python packages +Home-page: https://github.com/python/importlib_metadata +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +License: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: zipp (>=0.5) +Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8" +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Provides-Extra: perf +Requires-Dist: ipython ; extra == 'perf' +Provides-Extra: testing +Requires-Dist: pytest (>=6) ; extra == 'testing' +Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' +Requires-Dist: pytest-flake8 ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing' +Requires-Dist: packaging ; extra == 'testing' +Requires-Dist: pyfakefs ; extra == 'testing' +Requires-Dist: flufl.flake8 ; extra == 'testing' +Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing' +Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/importlib_metadata + +.. image:: https://github.com/python/importlib_metadata/workflows/tests/badge.svg + :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest + :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2022-informational + :target: https://blog.jaraco.com/skeleton + + +Library to access the metadata for a Python package. + +This package supplies third-party access to the functionality of +`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_ +including improvements added to subsequent Python versions. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - importlib_metadata + - stdlib + * - 4.8 + - 3.11 + * - 4.4 + - 3.10 + * - 1.4 + - 3.8 + + +Usage +===== + +See the `online documentation <https://importlib_metadata.readthedocs.io/>`_ +for usage details. + +`Finder authors +<https://docs.python.org/3/reference/import.html#finders-and-loaders>`_ can +also add support for custom package installers. See the above documentation +for details. + + +Caveats +======= + +This project primarily supports third-party packages installed by PyPA +tools (or other conforming packages). It does not support: + +- Packages in the stdlib. +- Packages installed without metadata. + +Project details +=============== + + * Project home: https://github.com/python/importlib_metadata + * Report bugs at: https://github.com/python/importlib_metadata/issues + * Code hosting: https://github.com/python/importlib_metadata + * Documentation: https://importlib_metadata.readthedocs.io/ + + diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/RECORD b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/RECORD new file mode 100644 index 0000000..a335d30 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/RECORD @@ -0,0 +1,23 @@ +importlib_metadata-4.11.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +importlib_metadata-4.11.3.dist-info/LICENSE,sha256=wNe6dAchmJ1VvVB8D9oTc-gHHadCuaSBAev36sYEM6U,571 +importlib_metadata-4.11.3.dist-info/METADATA,sha256=QDN8bGG98uILiLVIoBDBL7qf1y40Vb_Pvp1fxPJtmS0,3997 +importlib_metadata-4.11.3.dist-info/RECORD,, +importlib_metadata-4.11.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +importlib_metadata-4.11.3.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19 +importlib_metadata/__init__.py,sha256=7VjJ9nthPlNrL_TriHRiT5Ta-ZBsBnXrZq65pRfGNIs,30889 +importlib_metadata/__pycache__/__init__.cpython-39.pyc,, +importlib_metadata/__pycache__/_adapters.cpython-39.pyc,, +importlib_metadata/__pycache__/_collections.cpython-39.pyc,, +importlib_metadata/__pycache__/_compat.cpython-39.pyc,, +importlib_metadata/__pycache__/_functools.cpython-39.pyc,, +importlib_metadata/__pycache__/_itertools.cpython-39.pyc,, +importlib_metadata/__pycache__/_meta.cpython-39.pyc,, +importlib_metadata/__pycache__/_text.cpython-39.pyc,, +importlib_metadata/_adapters.py,sha256=B6fCi5-8mLVDFUZj3krI5nAo-mKp1dH_qIavyIyFrJs,1862 +importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743 +importlib_metadata/_compat.py,sha256=EU2XCFBPFByuI0Of6XkAuBYbzqSyjwwwwqmsK4ccna0,1826 +importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895 +importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068 +importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO23qEygbVdF9r-6qh-hjE,1154 +importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166 +importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/WHEEL b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/top_level.txt b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/top_level.txt new file mode 100644 index 0000000..bbb0754 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata-4.11.3.dist-info/top_level.txt @@ -0,0 +1 @@ +importlib_metadata diff --git a/venv/Lib/site-packages/importlib_metadata/__init__.py b/venv/Lib/site-packages/importlib_metadata/__init__.py new file mode 100644 index 0000000..5ac8be2 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/__init__.py @@ -0,0 +1,1075 @@ +import os +import re +import abc +import csv +import sys +import zipp +import email +import pathlib +import operator +import textwrap +import warnings +import functools +import itertools +import posixpath +import collections + +from . import _adapters, _meta +from ._collections import FreezableDefaultDict, Pair +from ._compat import ( + NullFinder, + install, + pypy_partial, +) +from ._functools import method_cache, pass_none +from ._itertools import always_iterable, unique_everseen +from ._meta import PackageMetadata, SimplePath + +from contextlib import suppress +from importlib import import_module +from importlib.abc import MetaPathFinder +from itertools import starmap +from typing import List, Mapping, Optional, Union + + +__all__ = [ + 'Distribution', + 'DistributionFinder', + 'PackageMetadata', + 'PackageNotFoundError', + 'distribution', + 'distributions', + 'entry_points', + 'files', + 'metadata', + 'packages_distributions', + 'requires', + 'version', +] + + +class PackageNotFoundError(ModuleNotFoundError): + """The package was not found.""" + + def __str__(self): + return f"No package metadata was found for {self.name}" + + @property + def name(self): + (name,) = self.args + return name + + +class Sectioned: + """ + A simple entry point config parser for performance + + >>> for item in Sectioned.read(Sectioned._sample): + ... print(item) + Pair(name='sec1', value='# comments ignored') + Pair(name='sec1', value='a = 1') + Pair(name='sec1', value='b = 2') + Pair(name='sec2', value='a = 2') + + >>> res = Sectioned.section_pairs(Sectioned._sample) + >>> item = next(res) + >>> item.name + 'sec1' + >>> item.value + Pair(name='a', value='1') + >>> item = next(res) + >>> item.value + Pair(name='b', value='2') + >>> item = next(res) + >>> item.name + 'sec2' + >>> item.value + Pair(name='a', value='2') + >>> list(res) + [] + """ + + _sample = textwrap.dedent( + """ + [sec1] + # comments ignored + a = 1 + b = 2 + + [sec2] + a = 2 + """ + ).lstrip() + + @classmethod + def section_pairs(cls, text): + return ( + section._replace(value=Pair.parse(section.value)) + for section in cls.read(text, filter_=cls.valid) + if section.name is not None + ) + + @staticmethod + def read(text, filter_=None): + lines = filter(filter_, map(str.strip, text.splitlines())) + name = None + for value in lines: + section_match = value.startswith('[') and value.endswith(']') + if section_match: + name = value.strip('[]') + continue + yield Pair(name, value) + + @staticmethod + def valid(line): + return line and not line.startswith('#') + + +class DeprecatedTuple: + """ + Provide subscript item access for backward compatibility. + + >>> recwarn = getfixture('recwarn') + >>> ep = EntryPoint(name='name', value='value', group='group') + >>> ep[:] + ('name', 'value', 'group') + >>> ep[0] + 'name' + >>> len(recwarn) + 1 + """ + + _warn = functools.partial( + warnings.warn, + "EntryPoint tuple interface is deprecated. Access members by name.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, item): + self._warn() + return self._key()[item] + + +class EntryPoint(DeprecatedTuple): + """An entry point as defined by Python packaging conventions. + + See `the packaging docs on entry points + <https://packaging.python.org/specifications/entry-points/>`_ + for more information. + + >>> ep = EntryPoint( + ... name=None, group=None, value='package.module:attr [extra1, extra2]') + >>> ep.module + 'package.module' + >>> ep.attr + 'attr' + >>> ep.extras + ['extra1', 'extra2'] + """ + + pattern = re.compile( + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+)\s*)?' + r'((?P<extras>\[.*\])\s*)?$' + ) + """ + A regular expression describing the syntax for an entry point, + which might look like: + + - module + - package.module + - package.module:attribute + - package.module:object.attribute + - package.module:attr [extra1, extra2] + + Other combinations are possible as well. + + The expression is lenient about whitespace around the ':', + following the attr, and following any extras. + """ + + dist: Optional['Distribution'] = None + + def __init__(self, name, value, group): + vars(self).update(name=name, value=value, group=group) + + def load(self): + """Load the entry point from its definition. If only a module + is indicated by the value, return that module. Otherwise, + return the named object. + """ + match = self.pattern.match(self.value) + module = import_module(match.group('module')) + attrs = filter(None, (match.group('attr') or '').split('.')) + return functools.reduce(getattr, attrs, module) + + @property + def module(self): + match = self.pattern.match(self.value) + return match.group('module') + + @property + def attr(self): + match = self.pattern.match(self.value) + return match.group('attr') + + @property + def extras(self): + match = self.pattern.match(self.value) + return re.findall(r'\w+', match.group('extras') or '') + + def _for(self, dist): + vars(self).update(dist=dist) + return self + + def __iter__(self): + """ + Supply iter so one may construct dicts of EntryPoints by name. + """ + msg = ( + "Construction of dict of EntryPoints is deprecated in " + "favor of EntryPoints." + ) + warnings.warn(msg, DeprecationWarning) + return iter((self.name, self)) + + def matches(self, **params): + """ + EntryPoint matches the given parameters. + + >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') + >>> ep.matches(group='foo') + True + >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') + True + >>> ep.matches(group='foo', name='other') + False + >>> ep.matches() + True + >>> ep.matches(extras=['extra1', 'extra2']) + True + >>> ep.matches(module='bing') + True + >>> ep.matches(attr='bong') + True + """ + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + + def _key(self): + return self.name, self.value, self.group + + def __lt__(self, other): + return self._key() < other._key() + + def __eq__(self, other): + return self._key() == other._key() + + def __setattr__(self, name, value): + raise AttributeError("EntryPoint objects are immutable.") + + def __repr__(self): + return ( + f'EntryPoint(name={self.name!r}, value={self.value!r}, ' + f'group={self.group!r})' + ) + + def __hash__(self): + return hash(self._key()) + + +class DeprecatedList(list): + """ + Allow an otherwise immutable object to implement mutability + for compatibility. + + >>> recwarn = getfixture('recwarn') + >>> dl = DeprecatedList(range(3)) + >>> dl[0] = 1 + >>> dl.append(3) + >>> del dl[3] + >>> dl.reverse() + >>> dl.sort() + >>> dl.extend([4]) + >>> dl.pop(-1) + 4 + >>> dl.remove(1) + >>> dl += [5] + >>> dl + [6] + [1, 2, 5, 6] + >>> dl + (6,) + [1, 2, 5, 6] + >>> dl.insert(0, 0) + >>> dl + [0, 1, 2, 5] + >>> dl == [0, 1, 2, 5] + True + >>> dl == (0, 1, 2, 5) + True + >>> len(recwarn) + 1 + """ + + __slots__ = () + + _warn = functools.partial( + warnings.warn, + "EntryPoints list interface is deprecated. Cast to list if needed.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def _wrap_deprecated_method(method_name: str): # type: ignore + def wrapped(self, *args, **kwargs): + self._warn() + return getattr(super(), method_name)(*args, **kwargs) + + return method_name, wrapped + + locals().update( + map( + _wrap_deprecated_method, + '__setitem__ __delitem__ append reverse extend pop remove ' + '__iadd__ insert sort'.split(), + ) + ) + + def __add__(self, other): + if not isinstance(other, tuple): + self._warn() + other = tuple(other) + return self.__class__(tuple(self) + other) + + def __eq__(self, other): + if not isinstance(other, tuple): + self._warn() + other = tuple(other) + + return tuple(self).__eq__(other) + + +class EntryPoints(DeprecatedList): + """ + An immutable collection of selectable EntryPoint objects. + """ + + __slots__ = () + + def __getitem__(self, name): # -> EntryPoint: + """ + Get the EntryPoint in self matching name. + """ + if isinstance(name, int): + warnings.warn( + "Accessing entry points by index is deprecated. " + "Cast to tuple if needed.", + DeprecationWarning, + stacklevel=2, + ) + return super().__getitem__(name) + try: + return next(iter(self.select(name=name))) + except StopIteration: + raise KeyError(name) + + def select(self, **params): + """ + Select entry points from self that match the + given parameters (typically group and/or name). + """ + return EntryPoints(ep for ep in self if ep.matches(**params)) + + @property + def names(self): + """ + Return the set of all names of all entry points. + """ + return {ep.name for ep in self} + + @property + def groups(self): + """ + Return the set of all groups of all entry points. + + For coverage while SelectableGroups is present. + >>> EntryPoints().groups + set() + """ + return {ep.group for ep in self} + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in cls._from_text(text)) + + @staticmethod + def _from_text(text): + return ( + EntryPoint(name=item.value.name, value=item.value.value, group=item.name) + for item in Sectioned.section_pairs(text or '') + ) + + +class Deprecated: + """ + Compatibility add-in for mapping to indicate that + mapping behavior is deprecated. + + >>> recwarn = getfixture('recwarn') + >>> class DeprecatedDict(Deprecated, dict): pass + >>> dd = DeprecatedDict(foo='bar') + >>> dd.get('baz', None) + >>> dd['foo'] + 'bar' + >>> list(dd) + ['foo'] + >>> list(dd.keys()) + ['foo'] + >>> 'foo' in dd + True + >>> list(dd.values()) + ['bar'] + >>> len(recwarn) + 1 + """ + + _warn = functools.partial( + warnings.warn, + "SelectableGroups dict interface is deprecated. Use select.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, name): + self._warn() + return super().__getitem__(name) + + def get(self, name, default=None): + self._warn() + return super().get(name, default) + + def __iter__(self): + self._warn() + return super().__iter__() + + def __contains__(self, *args): + self._warn() + return super().__contains__(*args) + + def keys(self): + self._warn() + return super().keys() + + def values(self): + self._warn() + return super().values() + + +class SelectableGroups(Deprecated, dict): + """ + A backward- and forward-compatible result from + entry_points that fully implements the dict interface. + """ + + @classmethod + def load(cls, eps): + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return cls((group, EntryPoints(eps)) for group, eps in grouped) + + @property + def _all(self): + """ + Reconstruct a list of all entrypoints from the groups. + """ + groups = super(Deprecated, self).values() + return EntryPoints(itertools.chain.from_iterable(groups)) + + @property + def groups(self): + return self._all.groups + + @property + def names(self): + """ + for coverage: + >>> SelectableGroups().names + set() + """ + return self._all.names + + def select(self, **params): + if not params: + return self + return self._all.select(**params) + + +class PackagePath(pathlib.PurePosixPath): + """A reference to a path in a package""" + + def read_text(self, encoding='utf-8'): + with self.locate().open(encoding=encoding) as stream: + return stream.read() + + def read_binary(self): + with self.locate().open('rb') as stream: + return stream.read() + + def locate(self): + """Return a path-like object for this path""" + return self.dist.locate_file(self) + + +class FileHash: + def __init__(self, spec): + self.mode, _, self.value = spec.partition('=') + + def __repr__(self): + return f'<FileHash mode: {self.mode} value: {self.value}>' + + +class Distribution: + """A Python distribution package.""" + + @abc.abstractmethod + def read_text(self, filename): + """Attempt to load metadata file given by the name. + + :param filename: The name of the file in the distribution info. + :return: The text if found, otherwise None. + """ + + @abc.abstractmethod + def locate_file(self, path): + """ + Given a path to a file in this distribution, return a path + to it. + """ + + @classmethod + def from_name(cls, name): + """Return the Distribution for the given package name. + + :param name: The name of the distribution package to search for. + :return: The Distribution instance (or subclass thereof) for the named + package, if found. + :raises PackageNotFoundError: When the named package's distribution + metadata cannot be found. + """ + for resolver in cls._discover_resolvers(): + dists = resolver(DistributionFinder.Context(name=name)) + dist = next(iter(dists), None) + if dist is not None: + return dist + else: + raise PackageNotFoundError(name) + + @classmethod + def discover(cls, **kwargs): + """Return an iterable of Distribution objects for all packages. + + Pass a ``context`` or pass keyword arguments for constructing + a context. + + :context: A ``DistributionFinder.Context`` object. + :return: Iterable of Distribution objects for all packages. + """ + context = kwargs.pop('context', None) + if context and kwargs: + raise ValueError("cannot accept context and kwargs") + context = context or DistributionFinder.Context(**kwargs) + return itertools.chain.from_iterable( + resolver(context) for resolver in cls._discover_resolvers() + ) + + @staticmethod + def at(path): + """Return a Distribution for the indicated metadata path + + :param path: a string or path-like object + :return: a concrete Distribution instance for the path + """ + return PathDistribution(pathlib.Path(path)) + + @staticmethod + def _discover_resolvers(): + """Search the meta_path for resolvers.""" + declared = ( + getattr(finder, 'find_distributions', None) for finder in sys.meta_path + ) + return filter(None, declared) + + @property + def metadata(self) -> _meta.PackageMetadata: + """Return the parsed metadata for this Distribution. + + The returned object will have keys that name the various bits of + metadata. See PEP 566 for details. + """ + text = ( + self.read_text('METADATA') + or self.read_text('PKG-INFO') + # This last clause is here to support old egg-info files. Its + # effect is to just end up using the PathDistribution's self._path + # (which points to the egg-info file) attribute unchanged. + or self.read_text('') + ) + return _adapters.Message(email.message_from_string(text)) + + @property + def name(self): + """Return the 'Name' metadata for the distribution package.""" + return self.metadata['Name'] + + @property + def _normalized_name(self): + """Return a normalized version of the name.""" + return Prepared.normalize(self.name) + + @property + def version(self): + """Return the 'Version' metadata for the distribution package.""" + return self.metadata['Version'] + + @property + def entry_points(self): + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) + + @property + def files(self): + """Files in this distribution. + + :return: List of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ + + def make_file(name, hash=None, size_str=None): + result = PackagePath(name) + result.hash = FileHash(hash) if hash else None + result.size = int(size_str) if size_str else None + result.dist = self + return result + + @pass_none + def make_files(lines): + return list(starmap(make_file, csv.reader(lines))) + + return make_files(self._read_files_distinfo() or self._read_files_egginfo()) + + def _read_files_distinfo(self): + """ + Read the lines of RECORD + """ + text = self.read_text('RECORD') + return text and text.splitlines() + + def _read_files_egginfo(self): + """ + SOURCES.txt might contain literal commas, so wrap each line + in quotes. + """ + text = self.read_text('SOURCES.txt') + return text and map('"{}"'.format, text.splitlines()) + + @property + def requires(self): + """Generated requirements specified for this Distribution""" + reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() + return reqs and list(reqs) + + def _read_dist_info_reqs(self): + return self.metadata.get_all('Requires-Dist') + + def _read_egg_info_reqs(self): + source = self.read_text('requires.txt') + return pass_none(self._deps_from_requires_text)(source) + + @classmethod + def _deps_from_requires_text(cls, source): + return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source)) + + @staticmethod + def _convert_egg_info_reqs_to_simple_reqs(sections): + """ + Historically, setuptools would solicit and store 'extra' + requirements, including those with environment markers, + in separate sections. More modern tools expect each + dependency to be defined separately, with any relevant + extras and environment markers attached directly to that + requirement. This method converts the former to the + latter. See _test_deps_from_requires_text for an example. + """ + + def make_condition(name): + return name and f'extra == "{name}"' + + def quoted_marker(section): + section = section or '' + extra, sep, markers = section.partition(':') + if extra and markers: + markers = f'({markers})' + conditions = list(filter(None, [markers, make_condition(extra)])) + return '; ' + ' and '.join(conditions) if conditions else '' + + def url_req_space(req): + """ + PEP 508 requires a space between the url_spec and the quoted_marker. + Ref python/importlib_metadata#357. + """ + # '@' is uniquely indicative of a url_req. + return ' ' * ('@' in req) + + for section in sections: + space = url_req_space(section.value) + yield section.value + space + quoted_marker(section.name) + + +class DistributionFinder(MetaPathFinder): + """ + A MetaPathFinder capable of discovering installed distributions. + """ + + class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ + + name = None + """ + Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. + """ + + def __init__(self, **kwargs): + vars(self).update(kwargs) + + @property + def path(self): + """ + The sequence of directory path that a distribution finder + should search. + + Typically refers to Python installed package paths such as + "site-packages" directories and defaults to ``sys.path``. + """ + return vars(self).get('path', sys.path) + + @abc.abstractmethod + def find_distributions(self, context=Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the ``context``, + a DistributionFinder.Context instance. + """ + + +class FastPath: + """ + Micro-optimized class for searching a path for + children. + + >>> FastPath('').children() + ['...'] + """ + + @functools.lru_cache() # type: ignore + def __new__(cls, root): + return super().__new__(cls) + + def __init__(self, root): + self.root = root + + def joinpath(self, child): + return pathlib.Path(self.root, child) + + def children(self): + with suppress(Exception): + return os.listdir(self.root or '.') + with suppress(Exception): + return self.zip_children() + return [] + + def zip_children(self): + zip_path = zipp.Path(self.root) + names = zip_path.root.namelist() + self.joinpath = zip_path.joinpath + + return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) + + def search(self, name): + return self.lookup(self.mtime).search(name) + + @property + def mtime(self): + with suppress(OSError): + return os.stat(self.root).st_mtime + self.lookup.cache_clear() + + @method_cache + def lookup(self, mtime): + return Lookup(self) + + +class Lookup: + def __init__(self, path: FastPath): + base = os.path.basename(path.root).lower() + base_is_egg = base.endswith(".egg") + self.infos = FreezableDefaultDict(list) + self.eggs = FreezableDefaultDict(list) + + for child in path.children(): + low = child.lower() + if low.endswith((".dist-info", ".egg-info")): + # rpartition is faster than splitext and suitable for this purpose. + name = low.rpartition(".")[0].partition("-")[0] + normalized = Prepared.normalize(name) + self.infos[normalized].append(path.joinpath(child)) + elif base_is_egg and low == "egg-info": + name = base.rpartition(".")[0].partition("-")[0] + legacy_normalized = Prepared.legacy_normalize(name) + self.eggs[legacy_normalized].append(path.joinpath(child)) + + self.infos.freeze() + self.eggs.freeze() + + def search(self, prepared): + infos = ( + self.infos[prepared.normalized] + if prepared + else itertools.chain.from_iterable(self.infos.values()) + ) + eggs = ( + self.eggs[prepared.legacy_normalized] + if prepared + else itertools.chain.from_iterable(self.eggs.values()) + ) + return itertools.chain(infos, eggs) + + +class Prepared: + """ + A prepared search for metadata on a possibly-named package. + """ + + normalized = None + legacy_normalized = None + + def __init__(self, name): + self.name = name + if name is None: + return + self.normalized = self.normalize(name) + self.legacy_normalized = self.legacy_normalize(name) + + @staticmethod + def normalize(name): + """ + PEP 503 normalization plus dashes as underscores. + """ + return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + + @staticmethod + def legacy_normalize(name): + """ + Normalize the package name as found in the convention in + older packaging tools versions and specs. + """ + return name.lower().replace('-', '_') + + def __bool__(self): + return bool(self.name) + + +@install +class MetadataPathFinder(NullFinder, DistributionFinder): + """A degenerate finder for distribution packages on the file system. + + This finder supplies only a find_distributions() method for versions + of Python that do not have a PathFinder find_distributions(). + """ + + def find_distributions(self, context=DistributionFinder.Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching ``context.name`` + (or all names if ``None`` indicated) along the paths in the list + of directories ``context.path``. + """ + found = self._search_paths(context.name, context.path) + return map(PathDistribution, found) + + @classmethod + def _search_paths(cls, name, paths): + """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) + return itertools.chain.from_iterable( + path.search(prepared) for path in map(FastPath, paths) + ) + + def invalidate_caches(cls): + FastPath.__new__.cache_clear() + + +class PathDistribution(Distribution): + def __init__(self, path: SimplePath): + """Construct a distribution. + + :param path: SimplePath indicating the metadata directory. + """ + self._path = path + + def read_text(self, filename): + with suppress( + FileNotFoundError, + IsADirectoryError, + KeyError, + NotADirectoryError, + PermissionError, + ): + return self._path.joinpath(filename).read_text(encoding='utf-8') + + read_text.__doc__ = Distribution.read_text.__doc__ + + def locate_file(self, path): + return self._path.parent / path + + @property + def _normalized_name(self): + """ + Performance optimization: where possible, resolve the + normalized name from the file system path. + """ + stem = os.path.basename(str(self._path)) + return self._name_from_stem(stem) or super()._normalized_name + + def _name_from_stem(self, stem): + name, ext = os.path.splitext(stem) + if ext not in ('.dist-info', '.egg-info'): + return + name, sep, rest = stem.partition('-') + return name + + +def distribution(distribution_name): + """Get the ``Distribution`` instance for the named package. + + :param distribution_name: The name of the distribution package as a string. + :return: A ``Distribution`` instance (or subclass thereof). + """ + return Distribution.from_name(distribution_name) + + +def distributions(**kwargs): + """Get all ``Distribution`` instances in the current environment. + + :return: An iterable of ``Distribution`` instances. + """ + return Distribution.discover(**kwargs) + + +def metadata(distribution_name) -> _meta.PackageMetadata: + """Get the metadata for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: A PackageMetadata containing the parsed metadata. + """ + return Distribution.from_name(distribution_name).metadata + + +def version(distribution_name): + """Get the version string for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: The version string for the package as defined in the package's + "Version" metadata key. + """ + return distribution(distribution_name).version + + +def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: + """Return EntryPoint objects for all installed packages. + + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + For compatibility, returns ``SelectableGroups`` object unless + selection parameters are supplied. In the future, this function + will return ``EntryPoints`` instead of ``SelectableGroups`` + even when no selection parameters are supplied. + + For maximum future compatibility, pass selection parameters + or invoke ``.select`` with parameters on the result. + + :return: EntryPoints or SelectableGroups for all installed packages. + """ + norm_name = operator.attrgetter('_normalized_name') + unique = functools.partial(unique_everseen, key=norm_name) + eps = itertools.chain.from_iterable( + dist.entry_points for dist in unique(distributions()) + ) + return SelectableGroups.load(eps).select(**params) + + +def files(distribution_name): + """Return a list of files for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: List of files composing the distribution. + """ + return distribution(distribution_name).files + + +def requires(distribution_name): + """ + Return a list of requirements for the named package. + + :return: An iterator of requirements, suitable for + packaging.requirement.Requirement. + """ + return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> import collections.abc + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in _top_level_declared(dist) or _top_level_inferred(dist): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) + + +def _top_level_declared(dist): + return (dist.read_text('top_level.txt') or '').split() + + +def _top_level_inferred(dist): + return { + f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name + for f in always_iterable(dist.files) + if f.suffix == ".py" + } diff --git a/venv/Lib/site-packages/importlib_metadata/_adapters.py b/venv/Lib/site-packages/importlib_metadata/_adapters.py new file mode 100644 index 0000000..aa460d3 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_adapters.py @@ -0,0 +1,68 @@ +import re +import textwrap +import email.message + +from ._text import FoldedCase + + +class Message(email.message.Message): + multiple_use_keys = set( + map( + FoldedCase, + [ + 'Classifier', + 'Obsoletes-Dist', + 'Platform', + 'Project-URL', + 'Provides-Dist', + 'Provides-Extra', + 'Requires-Dist', + 'Requires-External', + 'Supported-Platform', + 'Dynamic', + ], + ) + ) + """ + Keys that may be indicated multiple times per PEP 566. + """ + + def __new__(cls, orig: email.message.Message): + res = super().__new__(cls) + vars(res).update(vars(orig)) + return res + + def __init__(self, *args, **kwargs): + self._headers = self._repair_headers() + + # suppress spurious error from mypy + def __iter__(self): + return super().__iter__() + + def _repair_headers(self): + def redent(value): + "Correct for RFC822 indentation" + if not value or '\n' not in value: + return value + return textwrap.dedent(' ' * 8 + value) + + headers = [(key, redent(value)) for key, value in vars(self)['_headers']] + if self._payload: + headers.append(('Description', self.get_payload())) + return headers + + @property + def json(self): + """ + Convert PackageMetadata to a JSON-compatible format + per PEP 0566. + """ + + def transform(key): + value = self.get_all(key) if key in self.multiple_use_keys else self[key] + if key == 'Keywords': + value = re.split(r'\s+', value) + tk = key.lower().replace('-', '_') + return tk, value + + return dict(map(transform, map(FoldedCase, self))) diff --git a/venv/Lib/site-packages/importlib_metadata/_collections.py b/venv/Lib/site-packages/importlib_metadata/_collections.py new file mode 100644 index 0000000..cf0954e --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_collections.py @@ -0,0 +1,30 @@ +import collections + + +# from jaraco.collections 3.3 +class FreezableDefaultDict(collections.defaultdict): + """ + Often it is desirable to prevent the mutation of + a default dict after its initial construction, such + as to prevent mutation during iteration. + + >>> dd = FreezableDefaultDict(list) + >>> dd[0].append('1') + >>> dd.freeze() + >>> dd[1] + [] + >>> len(dd) + 1 + """ + + def __missing__(self, key): + return getattr(self, '_frozen', super().__missing__)(key) + + def freeze(self): + self._frozen = lambda key: self.default_factory() + + +class Pair(collections.namedtuple('Pair', 'name value')): + @classmethod + def parse(cls, text): + return cls(*map(str.strip, text.split("=", 1))) diff --git a/venv/Lib/site-packages/importlib_metadata/_compat.py b/venv/Lib/site-packages/importlib_metadata/_compat.py new file mode 100644 index 0000000..8fe4e4e --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_compat.py @@ -0,0 +1,71 @@ +import sys +import platform + + +__all__ = ['install', 'NullFinder', 'Protocol'] + + +try: + from typing import Protocol +except ImportError: # pragma: no cover + from typing_extensions import Protocol # type: ignore + + +def install(cls): + """ + Class decorator for installation on sys.meta_path. + + Adds the backport DistributionFinder to sys.meta_path and + attempts to disable the finder functionality of the stdlib + DistributionFinder. + """ + sys.meta_path.append(cls()) + disable_stdlib_finder() + return cls + + +def disable_stdlib_finder(): + """ + Give the backport primacy for discovering path-based distributions + by monkey-patching the stdlib O_O. + + See #91 for more background for rationale on this sketchy + behavior. + """ + + def matches(finder): + return getattr( + finder, '__module__', None + ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') + + for finder in filter(matches, sys.meta_path): # pragma: nocover + del finder.find_distributions + + +class NullFinder: + """ + A "Finder" (aka "MetaClassFinder") that never finds any modules, + but may find distributions. + """ + + @staticmethod + def find_spec(*args, **kwargs): + return None + + # In Python 2, the import system requires finders + # to have a find_module() method, but this usage + # is deprecated in Python 3 in favor of find_spec(). + # For the purposes of this finder (i.e. being present + # on sys.meta_path but having no other import + # system functionality), the two methods are identical. + find_module = find_spec + + +def pypy_partial(val): + """ + Adjust for variable stacklevel on partial under PyPy. + + Workaround for #327. + """ + is_pypy = platform.python_implementation() == 'PyPy' + return val + is_pypy diff --git a/venv/Lib/site-packages/importlib_metadata/_functools.py b/venv/Lib/site-packages/importlib_metadata/_functools.py new file mode 100644 index 0000000..71f66bd --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_functools.py @@ -0,0 +1,104 @@ +import types +import functools + + +# from jaraco.functools 3.3 +def method_cache(method, cache_wrapper=None): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + cache_wrapper = cache_wrapper or functools.lru_cache() + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return wrapper + + +# From jaraco.functools 3.3 +def pass_none(func): + """ + Wrap func so it's not called if its first param is None + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + + return wrapper diff --git a/venv/Lib/site-packages/importlib_metadata/_itertools.py b/venv/Lib/site-packages/importlib_metadata/_itertools.py new file mode 100644 index 0000000..d4ca9b9 --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_itertools.py @@ -0,0 +1,73 @@ +from itertools import filterfalse + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + + +# copied from more_itertools 8.8 +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) diff --git a/venv/Lib/site-packages/importlib_metadata/_meta.py b/venv/Lib/site-packages/importlib_metadata/_meta.py new file mode 100644 index 0000000..37ee43e --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_meta.py @@ -0,0 +1,48 @@ +from ._compat import Protocol +from typing import Any, Dict, Iterator, List, TypeVar, Union + + +_T = TypeVar("_T") + + +class PackageMetadata(Protocol): + def __len__(self) -> int: + ... # pragma: no cover + + def __contains__(self, item: str) -> bool: + ... # pragma: no cover + + def __getitem__(self, key: str) -> str: + ... # pragma: no cover + + def __iter__(self) -> Iterator[str]: + ... # pragma: no cover + + def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: + """ + Return all values associated with a possibly multi-valued key. + """ + + @property + def json(self) -> Dict[str, Union[str, List[str]]]: + """ + A JSON-compatible form of the metadata. + """ + + +class SimplePath(Protocol): + """ + A minimal subset of pathlib.Path required by PathDistribution. + """ + + def joinpath(self) -> 'SimplePath': + ... # pragma: no cover + + def __truediv__(self) -> 'SimplePath': + ... # pragma: no cover + + def parent(self) -> 'SimplePath': + ... # pragma: no cover + + def read_text(self) -> str: + ... # pragma: no cover diff --git a/venv/Lib/site-packages/importlib_metadata/_text.py b/venv/Lib/site-packages/importlib_metadata/_text.py new file mode 100644 index 0000000..c88cfbb --- /dev/null +++ b/venv/Lib/site-packages/importlib_metadata/_text.py @@ -0,0 +1,99 @@ +import re + +from ._functools import method_cache + + +# from jaraco.text 3.5 +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use in_: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) diff --git a/venv/Lib/site-packages/importlib_metadata/py.typed b/venv/Lib/site-packages/importlib_metadata/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/markdown/__init__.py b/venv/Lib/site-packages/markdown/__init__.py new file mode 100644 index 0000000..e05af10 --- /dev/null +++ b/venv/Lib/site-packages/markdown/__init__.py @@ -0,0 +1,61 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import sys + +# TODO: Remove this check at some point in the future. +# (also remove flake8's 'ignore E402' comments below) +if sys.version_info[0] < 3: # pragma: no cover + raise ImportError('A recent version of Python 3 is required.') + +from .core import Markdown, markdown, markdownFromFile # noqa: E402 +from .util import PY37 # noqa: E402 +from .pep562 import Pep562 # noqa: E402 +from .__meta__ import __version__, __version_info__ # noqa: E402 +import warnings # noqa: E402 + +# For backward compatibility as some extensions expect it... +from .extensions import Extension # noqa + +__all__ = ['Markdown', 'markdown', 'markdownFromFile'] + +__deprecated__ = { + "version": ("__version__", __version__), + "version_info": ("__version_info__", __version_info__) +} + + +def __getattr__(name): + """Get attribute.""" + + deprecated = __deprecated__.get(name) + if deprecated: + warnings.warn( + "'{}' is deprecated. Use '{}' instead.".format(name, deprecated[0]), + category=DeprecationWarning, + stacklevel=(3 if PY37 else 4) + ) + return deprecated[1] + raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name)) + + +if not PY37: + Pep562(__name__) diff --git a/venv/Lib/site-packages/markdown/__main__.py b/venv/Lib/site-packages/markdown/__main__.py new file mode 100644 index 0000000..0184008 --- /dev/null +++ b/venv/Lib/site-packages/markdown/__main__.py @@ -0,0 +1,151 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import sys +import optparse +import codecs +import warnings +import markdown +try: + # We use `unsafe_load` because users may need to pass in actual Python + # objects. As this is only available from the CLI, the user has much + # worse problems if an attacker can use this as an attach vector. + from yaml import unsafe_load as yaml_load +except ImportError: # pragma: no cover + try: + # Fall back to PyYAML <5.1 + from yaml import load as yaml_load + except ImportError: + # Fall back to JSON + from json import load as yaml_load + +import logging +from logging import DEBUG, WARNING, CRITICAL + +logger = logging.getLogger('MARKDOWN') + + +def parse_options(args=None, values=None): + """ + Define and parse `optparse` options for command-line usage. + """ + usage = """%prog [options] [INPUTFILE] + (STDIN is assumed if no INPUTFILE is given)""" + desc = "A Python implementation of John Gruber's Markdown. " \ + "https://Python-Markdown.github.io/" + ver = "%%prog %s" % markdown.__version__ + + parser = optparse.OptionParser(usage=usage, description=desc, version=ver) + parser.add_option("-f", "--file", dest="filename", default=None, + help="Write output to OUTPUT_FILE. Defaults to STDOUT.", + metavar="OUTPUT_FILE") + parser.add_option("-e", "--encoding", dest="encoding", + help="Encoding for input and output files.",) + parser.add_option("-o", "--output_format", dest="output_format", + default='xhtml', metavar="OUTPUT_FORMAT", + help="Use output format 'xhtml' (default) or 'html'.") + parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol", + action='store_false', default=True, + help="Observe number of first item of ordered lists.") + parser.add_option("-x", "--extension", action="append", dest="extensions", + help="Load extension EXTENSION.", metavar="EXTENSION") + parser.add_option("-c", "--extension_configs", + dest="configfile", default=None, + help="Read extension configurations from CONFIG_FILE. " + "CONFIG_FILE must be of JSON or YAML format. YAML " + "format requires that a python YAML library be " + "installed. The parsed JSON or YAML must result in a " + "python dictionary which would be accepted by the " + "'extension_configs' keyword on the markdown.Markdown " + "class. The extensions must also be loaded with the " + "`--extension` option.", + metavar="CONFIG_FILE") + parser.add_option("-q", "--quiet", default=CRITICAL, + action="store_const", const=CRITICAL+10, dest="verbose", + help="Suppress all warnings.") + parser.add_option("-v", "--verbose", + action="store_const", const=WARNING, dest="verbose", + help="Print all warnings.") + parser.add_option("--noisy", + action="store_const", const=DEBUG, dest="verbose", + help="Print debug messages.") + + (options, args) = parser.parse_args(args, values) + + if len(args) == 0: + input_file = None + else: + input_file = args[0] + + if not options.extensions: + options.extensions = [] + + extension_configs = {} + if options.configfile: + with codecs.open( + options.configfile, mode="r", encoding=options.encoding + ) as fp: + try: + extension_configs = yaml_load(fp) + except Exception as e: + message = "Failed parsing extension config file: %s" % \ + options.configfile + e.args = (message,) + e.args[1:] + raise + + opts = { + 'input': input_file, + 'output': options.filename, + 'extensions': options.extensions, + 'extension_configs': extension_configs, + 'encoding': options.encoding, + 'output_format': options.output_format, + 'lazy_ol': options.lazy_ol + } + + return opts, options.verbose + + +def run(): # pragma: no cover + """Run Markdown from the command line.""" + + # Parse options and adjust logging level if necessary + options, logging_level = parse_options() + if not options: + sys.exit(2) + logger.setLevel(logging_level) + console_handler = logging.StreamHandler() + logger.addHandler(console_handler) + if logging_level <= WARNING: + # Ensure deprecation warnings get displayed + warnings.filterwarnings('default') + logging.captureWarnings(True) + warn_logger = logging.getLogger('py.warnings') + warn_logger.addHandler(console_handler) + + # Run + markdown.markdownFromFile(**options) + + +if __name__ == '__main__': # pragma: no cover + # Support running module as a commandline command. + # `python -m markdown [options] [args]`. + run() diff --git a/venv/Lib/site-packages/markdown/__meta__.py b/venv/Lib/site-packages/markdown/__meta__.py new file mode 100644 index 0000000..37ef099 --- /dev/null +++ b/venv/Lib/site-packages/markdown/__meta__.py @@ -0,0 +1,49 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +# __version_info__ format: +# (major, minor, patch, dev/alpha/beta/rc/final, #) +# (1, 1, 2, 'dev', 0) => "1.1.2.dev0" +# (1, 1, 2, 'alpha', 1) => "1.1.2a1" +# (1, 2, 0, 'beta', 2) => "1.2b2" +# (1, 2, 0, 'rc', 4) => "1.2rc4" +# (1, 2, 0, 'final', 0) => "1.2" +__version_info__ = (3, 3, 6, 'final', 0) + + +def _get_version(version_info): + " Returns a PEP 440-compliant version number from version_info. " + assert len(version_info) == 5 + assert version_info[3] in ('dev', 'alpha', 'beta', 'rc', 'final') + + parts = 2 if version_info[2] == 0 else 3 + v = '.'.join(map(str, version_info[:parts])) + + if version_info[3] == 'dev': + v += '.dev' + str(version_info[4]) + elif version_info[3] != 'final': + mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'rc'} + v += mapping[version_info[3]] + str(version_info[4]) + + return v + + +__version__ = _get_version(__version_info__) diff --git a/venv/Lib/site-packages/markdown/blockparser.py b/venv/Lib/site-packages/markdown/blockparser.py new file mode 100644 index 0000000..39219fd --- /dev/null +++ b/venv/Lib/site-packages/markdown/blockparser.py @@ -0,0 +1,125 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import xml.etree.ElementTree as etree +from . import util + + +class State(list): + """ Track the current and nested state of the parser. + + This utility class is used to track the state of the BlockParser and + support multiple levels if nesting. It's just a simple API wrapped around + a list. Each time a state is set, that state is appended to the end of the + list. Each time a state is reset, that state is removed from the end of + the list. + + Therefore, each time a state is set for a nested block, that state must be + reset when we back out of that level of nesting or the state could be + corrupted. + + While all the methods of a list object are available, only the three + defined below need be used. + + """ + + def set(self, state): + """ Set a new state. """ + self.append(state) + + def reset(self): + """ Step back one step in nested state. """ + self.pop() + + def isstate(self, state): + """ Test that top (current) level is of given state. """ + if len(self): + return self[-1] == state + else: + return False + + +class BlockParser: + """ Parse Markdown blocks into an ElementTree object. + + A wrapper class that stitches the various BlockProcessors together, + looping through them and creating an ElementTree object. + """ + + def __init__(self, md): + self.blockprocessors = util.Registry() + self.state = State() + self.md = md + + @property + @util.deprecated("Use 'md' instead.") + def markdown(self): + # TODO: remove this later + return self.md + + def parseDocument(self, lines): + """ Parse a markdown document into an ElementTree. + + Given a list of lines, an ElementTree object (not just a parent + Element) is created and the root element is passed to the parser + as the parent. The ElementTree object is returned. + + This should only be called on an entire document, not pieces. + + """ + # Create a ElementTree from the lines + self.root = etree.Element(self.md.doc_tag) + self.parseChunk(self.root, '\n'.join(lines)) + return etree.ElementTree(self.root) + + def parseChunk(self, parent, text): + """ Parse a chunk of markdown text and attach to given etree node. + + While the ``text`` argument is generally assumed to contain multiple + blocks which will be split on blank lines, it could contain only one + block. Generally, this method would be called by extensions when + block parsing is required. + + The ``parent`` etree Element passed in is altered in place. + Nothing is returned. + + """ + self.parseBlocks(parent, text.split('\n\n')) + + def parseBlocks(self, parent, blocks): + """ Process blocks of markdown text and attach to given etree node. + + Given a list of ``blocks``, each blockprocessor is stepped through + until there are no blocks left. While an extension could potentially + call this method directly, it's generally expected to be used + internally. + + This is a public method as an extension may need to add/alter + additional BlockProcessors which call this method to recursively + parse a nested block. + + """ + while blocks: + for processor in self.blockprocessors: + if processor.test(parent, blocks[0]): + if processor.run(parent, blocks) is not False: + # run returns True or None + break diff --git a/venv/Lib/site-packages/markdown/blockprocessors.py b/venv/Lib/site-packages/markdown/blockprocessors.py new file mode 100644 index 0000000..dac3f08 --- /dev/null +++ b/venv/Lib/site-packages/markdown/blockprocessors.py @@ -0,0 +1,623 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). + +CORE MARKDOWN BLOCKPARSER +=========================================================================== + +This parser handles basic parsing of Markdown blocks. It doesn't concern +itself with inline elements such as **bold** or *italics*, but rather just +catches blocks, lists, quotes, etc. + +The BlockParser is made up of a bunch of BlockProcessors, each handling a +different type of block. Extensions may add/replace/remove BlockProcessors +as they need to alter how markdown blocks are parsed. +""" + +import logging +import re +import xml.etree.ElementTree as etree +from . import util +from .blockparser import BlockParser + +logger = logging.getLogger('MARKDOWN') + + +def build_block_parser(md, **kwargs): + """ Build the default block parser used by Markdown. """ + parser = BlockParser(md) + parser.blockprocessors.register(EmptyBlockProcessor(parser), 'empty', 100) + parser.blockprocessors.register(ListIndentProcessor(parser), 'indent', 90) + parser.blockprocessors.register(CodeBlockProcessor(parser), 'code', 80) + parser.blockprocessors.register(HashHeaderProcessor(parser), 'hashheader', 70) + parser.blockprocessors.register(SetextHeaderProcessor(parser), 'setextheader', 60) + parser.blockprocessors.register(HRProcessor(parser), 'hr', 50) + parser.blockprocessors.register(OListProcessor(parser), 'olist', 40) + parser.blockprocessors.register(UListProcessor(parser), 'ulist', 30) + parser.blockprocessors.register(BlockQuoteProcessor(parser), 'quote', 20) + parser.blockprocessors.register(ReferenceProcessor(parser), 'reference', 15) + parser.blockprocessors.register(ParagraphProcessor(parser), 'paragraph', 10) + return parser + + +class BlockProcessor: + """ Base class for block processors. + + Each subclass will provide the methods below to work with the source and + tree. Each processor will need to define it's own ``test`` and ``run`` + methods. The ``test`` method should return True or False, to indicate + whether the current block should be processed by this processor. If the + test passes, the parser will call the processors ``run`` method. + + """ + + def __init__(self, parser): + self.parser = parser + self.tab_length = parser.md.tab_length + + def lastChild(self, parent): + """ Return the last child of an etree element. """ + if len(parent): + return parent[-1] + else: + return None + + def detab(self, text, length=None): + """ Remove a tab from the front of each line of the given text. """ + if length is None: + length = self.tab_length + newtext = [] + lines = text.split('\n') + for line in lines: + if line.startswith(' ' * length): + newtext.append(line[length:]) + elif not line.strip(): + newtext.append('') + else: + break + return '\n'.join(newtext), '\n'.join(lines[len(newtext):]) + + def looseDetab(self, text, level=1): + """ Remove a tab from front of lines but allowing dedented lines. """ + lines = text.split('\n') + for i in range(len(lines)): + if lines[i].startswith(' '*self.tab_length*level): + lines[i] = lines[i][self.tab_length*level:] + return '\n'.join(lines) + + def test(self, parent, block): + """ Test for block type. Must be overridden by subclasses. + + As the parser loops through processors, it will call the ``test`` + method on each to determine if the given block of text is of that + type. This method must return a boolean ``True`` or ``False``. The + actual method of testing is left to the needs of that particular + block type. It could be as simple as ``block.startswith(some_string)`` + or a complex regular expression. As the block type may be different + depending on the parent of the block (i.e. inside a list), the parent + etree element is also provided and may be used as part of the test. + + Keywords: + + * ``parent``: A etree element which will be the parent of the block. + * ``block``: A block of text from the source which has been split at + blank lines. + """ + pass # pragma: no cover + + def run(self, parent, blocks): + """ Run processor. Must be overridden by subclasses. + + When the parser determines the appropriate type of a block, the parser + will call the corresponding processor's ``run`` method. This method + should parse the individual lines of the block and append them to + the etree. + + Note that both the ``parent`` and ``etree`` keywords are pointers + to instances of the objects which should be edited in place. Each + processor must make changes to the existing objects as there is no + mechanism to return new/different objects to replace them. + + This means that this method should be adding SubElements or adding text + to the parent, and should remove (``pop``) or add (``insert``) items to + the list of blocks. + + Keywords: + + * ``parent``: A etree element which is the parent of the current block. + * ``blocks``: A list of all remaining blocks of the document. + """ + pass # pragma: no cover + + +class ListIndentProcessor(BlockProcessor): + """ Process children of list items. + + Example: + * a list item + process this part + + or this part + + """ + + ITEM_TYPES = ['li'] + LIST_TYPES = ['ul', 'ol'] + + def __init__(self, *args): + super().__init__(*args) + self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length) + + def test(self, parent, block): + return block.startswith(' '*self.tab_length) and \ + not self.parser.state.isstate('detabbed') and \ + (parent.tag in self.ITEM_TYPES or + (len(parent) and parent[-1] is not None and + (parent[-1].tag in self.LIST_TYPES))) + + def run(self, parent, blocks): + block = blocks.pop(0) + level, sibling = self.get_level(parent, block) + block = self.looseDetab(block, level) + + self.parser.state.set('detabbed') + if parent.tag in self.ITEM_TYPES: + # It's possible that this parent has a 'ul' or 'ol' child list + # with a member. If that is the case, then that should be the + # parent. This is intended to catch the edge case of an indented + # list whose first member was parsed previous to this point + # see OListProcessor + if len(parent) and parent[-1].tag in self.LIST_TYPES: + self.parser.parseBlocks(parent[-1], [block]) + else: + # The parent is already a li. Just parse the child block. + self.parser.parseBlocks(parent, [block]) + elif sibling.tag in self.ITEM_TYPES: + # The sibling is a li. Use it as parent. + self.parser.parseBlocks(sibling, [block]) + elif len(sibling) and sibling[-1].tag in self.ITEM_TYPES: + # The parent is a list (``ol`` or ``ul``) which has children. + # Assume the last child li is the parent of this block. + if sibling[-1].text: + # If the parent li has text, that text needs to be moved to a p + # The p must be 'inserted' at beginning of list in the event + # that other children already exist i.e.; a nested sublist. + p = etree.Element('p') + p.text = sibling[-1].text + sibling[-1].text = '' + sibling[-1].insert(0, p) + self.parser.parseChunk(sibling[-1], block) + else: + self.create_item(sibling, block) + self.parser.state.reset() + + def create_item(self, parent, block): + """ Create a new li and parse the block with it as the parent. """ + li = etree.SubElement(parent, 'li') + self.parser.parseBlocks(li, [block]) + + def get_level(self, parent, block): + """ Get level of indent based on list level. """ + # Get indent level + m = self.INDENT_RE.match(block) + if m: + indent_level = len(m.group(1))/self.tab_length + else: + indent_level = 0 + if self.parser.state.isstate('list'): + # We're in a tightlist - so we already are at correct parent. + level = 1 + else: + # We're in a looselist - so we need to find parent. + level = 0 + # Step through children of tree to find matching indent level. + while indent_level > level: + child = self.lastChild(parent) + if (child is not None and + (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES)): + if child.tag in self.LIST_TYPES: + level += 1 + parent = child + else: + # No more child levels. If we're short of indent_level, + # we have a code block. So we stop here. + break + return level, parent + + +class CodeBlockProcessor(BlockProcessor): + """ Process code blocks. """ + + def test(self, parent, block): + return block.startswith(' '*self.tab_length) + + def run(self, parent, blocks): + sibling = self.lastChild(parent) + block = blocks.pop(0) + theRest = '' + if (sibling is not None and sibling.tag == "pre" and + len(sibling) and sibling[0].tag == "code"): + # The previous block was a code block. As blank lines do not start + # new code blocks, append this block to the previous, adding back + # linebreaks removed from the split into a list. + code = sibling[0] + block, theRest = self.detab(block) + code.text = util.AtomicString( + '{}\n{}\n'.format(code.text, util.code_escape(block.rstrip())) + ) + else: + # This is a new codeblock. Create the elements and insert text. + pre = etree.SubElement(parent, 'pre') + code = etree.SubElement(pre, 'code') + block, theRest = self.detab(block) + code.text = util.AtomicString('%s\n' % util.code_escape(block.rstrip())) + if theRest: + # This block contained unindented line(s) after the first indented + # line. Insert these lines as the first block of the master blocks + # list for future processing. + blocks.insert(0, theRest) + + +class BlockQuoteProcessor(BlockProcessor): + + RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)') + + def test(self, parent, block): + return bool(self.RE.search(block)) and not util.nearing_recursion_limit() + + def run(self, parent, blocks): + block = blocks.pop(0) + m = self.RE.search(block) + if m: + before = block[:m.start()] # Lines before blockquote + # Pass lines before blockquote in recursively for parsing forst. + self.parser.parseBlocks(parent, [before]) + # Remove ``> `` from beginning of each line. + block = '\n'.join( + [self.clean(line) for line in block[m.start():].split('\n')] + ) + sibling = self.lastChild(parent) + if sibling is not None and sibling.tag == "blockquote": + # Previous block was a blockquote so set that as this blocks parent + quote = sibling + else: + # This is a new blockquote. Create a new parent element. + quote = etree.SubElement(parent, 'blockquote') + # Recursively parse block with blockquote as parent. + # change parser state so blockquotes embedded in lists use p tags + self.parser.state.set('blockquote') + self.parser.parseChunk(quote, block) + self.parser.state.reset() + + def clean(self, line): + """ Remove ``>`` from beginning of a line. """ + m = self.RE.match(line) + if line.strip() == ">": + return "" + elif m: + return m.group(2) + else: + return line + + +class OListProcessor(BlockProcessor): + """ Process ordered list blocks. """ + + TAG = 'ol' + # The integer (python string) with which the lists starts (default=1) + # Eg: If list is intialized as) + # 3. Item + # The ol tag will get starts="3" attribute + STARTSWITH = '1' + # Lazy ol - ignore startswith + LAZY_OL = True + # List of allowed sibling tags. + SIBLING_TAGS = ['ol', 'ul'] + + def __init__(self, parser): + super().__init__(parser) + # Detect an item (``1. item``). ``group(1)`` contains contents of item. + self.RE = re.compile(r'^[ ]{0,%d}\d+\.[ ]+(.*)' % (self.tab_length - 1)) + # Detect items on secondary lines. they can be of either list type. + self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.)|[*+-])[ ]+(.*)' % + (self.tab_length - 1)) + # Detect indented (nested) items of either type + self.INDENT_RE = re.compile(r'^[ ]{%d,%d}((\d+\.)|[*+-])[ ]+.*' % + (self.tab_length, self.tab_length * 2 - 1)) + + def test(self, parent, block): + return bool(self.RE.match(block)) + + def run(self, parent, blocks): + # Check fr multiple items in one block. + items = self.get_items(blocks.pop(0)) + sibling = self.lastChild(parent) + + if sibling is not None and sibling.tag in self.SIBLING_TAGS: + # Previous block was a list item, so set that as parent + lst = sibling + # make sure previous item is in a p- if the item has text, + # then it isn't in a p + if lst[-1].text: + # since it's possible there are other children for this + # sibling, we can't just SubElement the p, we need to + # insert it as the first item. + p = etree.Element('p') + p.text = lst[-1].text + lst[-1].text = '' + lst[-1].insert(0, p) + # if the last item has a tail, then the tail needs to be put in a p + # likely only when a header is not followed by a blank line + lch = self.lastChild(lst[-1]) + if lch is not None and lch.tail: + p = etree.SubElement(lst[-1], 'p') + p.text = lch.tail.lstrip() + lch.tail = '' + + # parse first block differently as it gets wrapped in a p. + li = etree.SubElement(lst, 'li') + self.parser.state.set('looselist') + firstitem = items.pop(0) + self.parser.parseBlocks(li, [firstitem]) + self.parser.state.reset() + elif parent.tag in ['ol', 'ul']: + # this catches the edge case of a multi-item indented list whose + # first item is in a blank parent-list item: + # * * subitem1 + # * subitem2 + # see also ListIndentProcessor + lst = parent + else: + # This is a new list so create parent with appropriate tag. + lst = etree.SubElement(parent, self.TAG) + # Check if a custom start integer is set + if not self.LAZY_OL and self.STARTSWITH != '1': + lst.attrib['start'] = self.STARTSWITH + + self.parser.state.set('list') + # Loop through items in block, recursively parsing each with the + # appropriate parent. + for item in items: + if item.startswith(' '*self.tab_length): + # Item is indented. Parse with last item as parent + self.parser.parseBlocks(lst[-1], [item]) + else: + # New item. Create li and parse with it as parent + li = etree.SubElement(lst, 'li') + self.parser.parseBlocks(li, [item]) + self.parser.state.reset() + + def get_items(self, block): + """ Break a block into list items. """ + items = [] + for line in block.split('\n'): + m = self.CHILD_RE.match(line) + if m: + # This is a new list item + # Check first item for the start index + if not items and self.TAG == 'ol': + # Detect the integer value of first list item + INTEGER_RE = re.compile(r'(\d+)') + self.STARTSWITH = INTEGER_RE.match(m.group(1)).group() + # Append to the list + items.append(m.group(3)) + elif self.INDENT_RE.match(line): + # This is an indented (possibly nested) item. + if items[-1].startswith(' '*self.tab_length): + # Previous item was indented. Append to that item. + items[-1] = '{}\n{}'.format(items[-1], line) + else: + items.append(line) + else: + # This is another line of previous item. Append to that item. + items[-1] = '{}\n{}'.format(items[-1], line) + return items + + +class UListProcessor(OListProcessor): + """ Process unordered list blocks. """ + + TAG = 'ul' + + def __init__(self, parser): + super().__init__(parser) + # Detect an item (``1. item``). ``group(1)`` contains contents of item. + self.RE = re.compile(r'^[ ]{0,%d}[*+-][ ]+(.*)' % (self.tab_length - 1)) + + +class HashHeaderProcessor(BlockProcessor): + """ Process Hash Headers. """ + + # Detect a header at start of any line in block + RE = re.compile(r'(?:^|\n)(?P<level>#{1,6})(?P<header>(?:\\.|[^\\])*?)#*(?:\n|$)') + + def test(self, parent, block): + return bool(self.RE.search(block)) + + def run(self, parent, blocks): + block = blocks.pop(0) + m = self.RE.search(block) + if m: + before = block[:m.start()] # All lines before header + after = block[m.end():] # All lines after header + if before: + # As the header was not the first line of the block and the + # lines before the header must be parsed first, + # recursively parse this lines as a block. + self.parser.parseBlocks(parent, [before]) + # Create header using named groups from RE + h = etree.SubElement(parent, 'h%d' % len(m.group('level'))) + h.text = m.group('header').strip() + if after: + # Insert remaining lines as first block for future parsing. + blocks.insert(0, after) + else: # pragma: no cover + # This should never happen, but just in case... + logger.warn("We've got a problem header: %r" % block) + + +class SetextHeaderProcessor(BlockProcessor): + """ Process Setext-style Headers. """ + + # Detect Setext-style header. Must be first 2 lines of block. + RE = re.compile(r'^.*?\n[=-]+[ ]*(\n|$)', re.MULTILINE) + + def test(self, parent, block): + return bool(self.RE.match(block)) + + def run(self, parent, blocks): + lines = blocks.pop(0).split('\n') + # Determine level. ``=`` is 1 and ``-`` is 2. + if lines[1].startswith('='): + level = 1 + else: + level = 2 + h = etree.SubElement(parent, 'h%d' % level) + h.text = lines[0].strip() + if len(lines) > 2: + # Block contains additional lines. Add to master blocks for later. + blocks.insert(0, '\n'.join(lines[2:])) + + +class HRProcessor(BlockProcessor): + """ Process Horizontal Rules. """ + + # Python's re module doesn't officially support atomic grouping. However you can fake it. + # See https://stackoverflow.com/a/13577411/866026 + RE = r'^[ ]{0,3}(?=(?P<atomicgroup>(-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,}))(?P=atomicgroup)[ ]*$' + # Detect hr on any line of a block. + SEARCH_RE = re.compile(RE, re.MULTILINE) + + def test(self, parent, block): + m = self.SEARCH_RE.search(block) + if m: + # Save match object on class instance so we can use it later. + self.match = m + return True + return False + + def run(self, parent, blocks): + block = blocks.pop(0) + match = self.match + # Check for lines in block before hr. + prelines = block[:match.start()].rstrip('\n') + if prelines: + # Recursively parse lines before hr so they get parsed first. + self.parser.parseBlocks(parent, [prelines]) + # create hr + etree.SubElement(parent, 'hr') + # check for lines in block after hr. + postlines = block[match.end():].lstrip('\n') + if postlines: + # Add lines after hr to master blocks for later parsing. + blocks.insert(0, postlines) + + +class EmptyBlockProcessor(BlockProcessor): + """ Process blocks that are empty or start with an empty line. """ + + def test(self, parent, block): + return not block or block.startswith('\n') + + def run(self, parent, blocks): + block = blocks.pop(0) + filler = '\n\n' + if block: + # Starts with empty line + # Only replace a single line. + filler = '\n' + # Save the rest for later. + theRest = block[1:] + if theRest: + # Add remaining lines to master blocks for later. + blocks.insert(0, theRest) + sibling = self.lastChild(parent) + if (sibling is not None and sibling.tag == 'pre' and + len(sibling) and sibling[0].tag == 'code'): + # Last block is a codeblock. Append to preserve whitespace. + sibling[0].text = util.AtomicString( + '{}{}'.format(sibling[0].text, filler) + ) + + +class ReferenceProcessor(BlockProcessor): + """ Process link references. """ + RE = re.compile( + r'^[ ]{0,3}\[([^\]]*)\]:[ ]*\n?[ ]*([^\s]+)[ ]*(?:\n[ ]*)?((["\'])(.*)\4[ ]*|\((.*)\)[ ]*)?$', re.MULTILINE + ) + + def test(self, parent, block): + return True + + def run(self, parent, blocks): + block = blocks.pop(0) + m = self.RE.search(block) + if m: + id = m.group(1).strip().lower() + link = m.group(2).lstrip('<').rstrip('>') + title = m.group(5) or m.group(6) + self.parser.md.references[id] = (link, title) + if block[m.end():].strip(): + # Add any content after match back to blocks as separate block + blocks.insert(0, block[m.end():].lstrip('\n')) + if block[:m.start()].strip(): + # Add any content before match back to blocks as separate block + blocks.insert(0, block[:m.start()].rstrip('\n')) + return True + # No match. Restore block. + blocks.insert(0, block) + return False + + +class ParagraphProcessor(BlockProcessor): + """ Process Paragraph blocks. """ + + def test(self, parent, block): + return True + + def run(self, parent, blocks): + block = blocks.pop(0) + if block.strip(): + # Not a blank block. Add to parent, otherwise throw it away. + if self.parser.state.isstate('list'): + # The parent is a tight-list. + # + # Check for any children. This will likely only happen in a + # tight-list when a header isn't followed by a blank line. + # For example: + # + # * # Header + # Line 2 of list item - not part of header. + sibling = self.lastChild(parent) + if sibling is not None: + # Insetrt after sibling. + if sibling.tail: + sibling.tail = '{}\n{}'.format(sibling.tail, block) + else: + sibling.tail = '\n%s' % block + else: + # Append to parent.text + if parent.text: + parent.text = '{}\n{}'.format(parent.text, block) + else: + parent.text = block.lstrip() + else: + # Create a regular paragraph + p = etree.SubElement(parent, 'p') + p.text = block.lstrip() diff --git a/venv/Lib/site-packages/markdown/core.py b/venv/Lib/site-packages/markdown/core.py new file mode 100644 index 0000000..d8c8196 --- /dev/null +++ b/venv/Lib/site-packages/markdown/core.py @@ -0,0 +1,407 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import codecs +import sys +import logging +import importlib +from . import util +from .preprocessors import build_preprocessors +from .blockprocessors import build_block_parser +from .treeprocessors import build_treeprocessors +from .inlinepatterns import build_inlinepatterns +from .postprocessors import build_postprocessors +from .extensions import Extension +from .serializers import to_html_string, to_xhtml_string + +__all__ = ['Markdown', 'markdown', 'markdownFromFile'] + + +logger = logging.getLogger('MARKDOWN') + + +class Markdown: + """Convert Markdown to HTML.""" + + doc_tag = "div" # Element used to wrap document - later removed + + output_formats = { + 'html': to_html_string, + 'xhtml': to_xhtml_string, + } + + def __init__(self, **kwargs): + """ + Creates a new Markdown instance. + + Keyword arguments: + + * extensions: A list of extensions. + If an item is an instance of a subclass of `markdown.extension.Extension`, the instance will be used + as-is. If an item is of type string, first an entry point will be loaded. If that fails, the string is + assumed to use Python dot notation (`path.to.module:ClassName`) to load a markdown.Extension subclass. If + no class is specified, then a `makeExtension` function is called within the specified module. + * extension_configs: Configuration settings for extensions. + * output_format: Format of output. Supported formats are: + * "xhtml": Outputs XHTML style tags. Default. + * "html": Outputs HTML style tags. + * tab_length: Length of tabs in the source. Default: 4 + + """ + + self.tab_length = kwargs.get('tab_length', 4) + + self.ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']', + '(', ')', '>', '#', '+', '-', '.', '!'] + + self.block_level_elements = [ + # Elements which are invalid to wrap in a `<p>` tag. + # See https://w3c.github.io/html/grouping-content.html#the-p-element + 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', + 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', + 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul', + # Other elements which Markdown should not be mucking up the contents of. + 'canvas', 'colgroup', 'dd', 'body', 'dt', 'group', 'iframe', 'li', 'legend', + 'math', 'map', 'noscript', 'output', 'object', 'option', 'progress', 'script', + 'style', 'summary', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'video' + ] + + self.registeredExtensions = [] + self.docType = "" + self.stripTopLevelTags = True + + self.build_parser() + + self.references = {} + self.htmlStash = util.HtmlStash() + self.registerExtensions(extensions=kwargs.get('extensions', []), + configs=kwargs.get('extension_configs', {})) + self.set_output_format(kwargs.get('output_format', 'xhtml')) + self.reset() + + def build_parser(self): + """ Build the parser from the various parts. """ + self.preprocessors = build_preprocessors(self) + self.parser = build_block_parser(self) + self.inlinePatterns = build_inlinepatterns(self) + self.treeprocessors = build_treeprocessors(self) + self.postprocessors = build_postprocessors(self) + return self + + def registerExtensions(self, extensions, configs): + """ + Register extensions with this instance of Markdown. + + Keyword arguments: + + * extensions: A list of extensions, which can either + be strings or objects. + * configs: A dictionary mapping extension names to config options. + + """ + for ext in extensions: + if isinstance(ext, str): + ext = self.build_extension(ext, configs.get(ext, {})) + if isinstance(ext, Extension): + ext._extendMarkdown(self) + logger.debug( + 'Successfully loaded extension "%s.%s".' + % (ext.__class__.__module__, ext.__class__.__name__) + ) + elif ext is not None: + raise TypeError( + 'Extension "{}.{}" must be of type: "{}.{}"'.format( + ext.__class__.__module__, ext.__class__.__name__, + Extension.__module__, Extension.__name__ + ) + ) + return self + + def build_extension(self, ext_name, configs): + """ + Build extension from a string name, then return an instance. + + First attempt to load an entry point. The string name must be registered as an entry point in the + `markdown.extensions` group which points to a subclass of the `markdown.extensions.Extension` class. + If multiple distributions have registered the same name, the first one found is returned. + + If no entry point is found, assume dot notation (`path.to.module:ClassName`). Load the specified class and + return an instance. If no class is specified, import the module and call a `makeExtension` function and return + the Extension instance returned by that function. + """ + configs = dict(configs) + + entry_points = [ep for ep in util.INSTALLED_EXTENSIONS if ep.name == ext_name] + if entry_points: + ext = entry_points[0].load() + return ext(**configs) + + # Get class name (if provided): `path.to.module:ClassName` + ext_name, class_name = ext_name.split(':', 1) if ':' in ext_name else (ext_name, '') + + try: + module = importlib.import_module(ext_name) + logger.debug( + 'Successfully imported extension module "%s".' % ext_name + ) + except ImportError as e: + message = 'Failed loading extension "%s".' % ext_name + e.args = (message,) + e.args[1:] + raise + + if class_name: + # Load given class name from module. + return getattr(module, class_name)(**configs) + else: + # Expect makeExtension() function to return a class. + try: + return module.makeExtension(**configs) + except AttributeError as e: + message = e.args[0] + message = "Failed to initiate extension " \ + "'%s': %s" % (ext_name, message) + e.args = (message,) + e.args[1:] + raise + + def registerExtension(self, extension): + """ This gets called by the extension """ + self.registeredExtensions.append(extension) + return self + + def reset(self): + """ + Resets all state variables so that we can start with a new text. + """ + self.htmlStash.reset() + self.references.clear() + + for extension in self.registeredExtensions: + if hasattr(extension, 'reset'): + extension.reset() + + return self + + def set_output_format(self, format): + """ Set the output format for the class instance. """ + self.output_format = format.lower().rstrip('145') # ignore num + try: + self.serializer = self.output_formats[self.output_format] + except KeyError as e: + valid_formats = list(self.output_formats.keys()) + valid_formats.sort() + message = 'Invalid Output Format: "%s". Use one of %s.' \ + % (self.output_format, + '"' + '", "'.join(valid_formats) + '"') + e.args = (message,) + e.args[1:] + raise + return self + + def is_block_level(self, tag): + """Check if the tag is a block level HTML tag.""" + if isinstance(tag, str): + return tag.lower().rstrip('/') in self.block_level_elements + # Some ElementTree tags are not strings, so return False. + return False + + def convert(self, source): + """ + Convert markdown to serialized XHTML or HTML. + + Keyword arguments: + + * source: Source text as a Unicode string. + + Markdown processing takes place in five steps: + + 1. A bunch of "preprocessors" munge the input text. + 2. BlockParser() parses the high-level structural elements of the + pre-processed text into an ElementTree. + 3. A bunch of "treeprocessors" are run against the ElementTree. One + such treeprocessor runs InlinePatterns against the ElementTree, + detecting inline markup. + 4. Some post-processors are run against the text after the ElementTree + has been serialized into text. + 5. The output is written to a string. + + """ + + # Fixup the source text + if not source.strip(): + return '' # a blank unicode string + + try: + source = str(source) + except UnicodeDecodeError as e: # pragma: no cover + # Customise error message while maintaining original trackback + e.reason += '. -- Note: Markdown only accepts unicode input!' + raise + + # Split into lines and run the line preprocessors. + self.lines = source.split("\n") + for prep in self.preprocessors: + self.lines = prep.run(self.lines) + + # Parse the high-level elements. + root = self.parser.parseDocument(self.lines).getroot() + + # Run the tree-processors + for treeprocessor in self.treeprocessors: + newRoot = treeprocessor.run(root) + if newRoot is not None: + root = newRoot + + # Serialize _properly_. Strip top-level tags. + output = self.serializer(root) + if self.stripTopLevelTags: + try: + start = output.index( + '<%s>' % self.doc_tag) + len(self.doc_tag) + 2 + end = output.rindex('</%s>' % self.doc_tag) + output = output[start:end].strip() + except ValueError as e: # pragma: no cover + if output.strip().endswith('<%s />' % self.doc_tag): + # We have an empty document + output = '' + else: + # We have a serious problem + raise ValueError('Markdown failed to strip top-level ' + 'tags. Document=%r' % output.strip()) from e + + # Run the text post-processors + for pp in self.postprocessors: + output = pp.run(output) + + return output.strip() + + def convertFile(self, input=None, output=None, encoding=None): + """Converts a markdown file and returns the HTML as a unicode string. + + Decodes the file using the provided encoding (defaults to utf-8), + passes the file content to markdown, and outputs the html to either + the provided stream or the file with provided name, using the same + encoding as the source file. The 'xmlcharrefreplace' error handler is + used when encoding the output. + + **Note:** This is the only place that decoding and encoding of unicode + takes place in Python-Markdown. (All other code is unicode-in / + unicode-out.) + + Keyword arguments: + + * input: File object or path. Reads from stdin if `None`. + * output: File object or path. Writes to stdout if `None`. + * encoding: Encoding of input and output files. Defaults to utf-8. + + """ + + encoding = encoding or "utf-8" + + # Read the source + if input: + if isinstance(input, str): + input_file = codecs.open(input, mode="r", encoding=encoding) + else: + input_file = codecs.getreader(encoding)(input) + text = input_file.read() + input_file.close() + else: + text = sys.stdin.read() + if not isinstance(text, str): # pragma: no cover + text = text.decode(encoding) + + text = text.lstrip('\ufeff') # remove the byte-order mark + + # Convert + html = self.convert(text) + + # Write to file or stdout + if output: + if isinstance(output, str): + output_file = codecs.open(output, "w", + encoding=encoding, + errors="xmlcharrefreplace") + output_file.write(html) + output_file.close() + else: + writer = codecs.getwriter(encoding) + output_file = writer(output, errors="xmlcharrefreplace") + output_file.write(html) + # Don't close here. User may want to write more. + else: + # Encode manually and write bytes to stdout. + html = html.encode(encoding, "xmlcharrefreplace") + try: + # Write bytes directly to buffer (Python 3). + sys.stdout.buffer.write(html) + except AttributeError: # pragma: no cover + # Probably Python 2, which works with bytes by default. + sys.stdout.write(html) + + return self + + +""" +EXPORTED FUNCTIONS +============================================================================= + +Those are the two functions we really mean to export: markdown() and +markdownFromFile(). +""" + + +def markdown(text, **kwargs): + """Convert a markdown string to HTML and return HTML as a unicode string. + + This is a shortcut function for `Markdown` class to cover the most + basic use case. It initializes an instance of Markdown, loads the + necessary extensions and runs the parser on the given text. + + Keyword arguments: + + * text: Markdown formatted text as Unicode or ASCII string. + * Any arguments accepted by the Markdown class. + + Returns: An HTML document as a string. + + """ + md = Markdown(**kwargs) + return md.convert(text) + + +def markdownFromFile(**kwargs): + """Read markdown code from a file and write it to a file or a stream. + + This is a shortcut function which initializes an instance of Markdown, + and calls the convertFile method rather than convert. + + Keyword arguments: + + * input: a file name or readable object. + * output: a file name or writable object. + * encoding: Encoding of input and output. + * Any arguments accepted by the Markdown class. + + """ + md = Markdown(**kwargs) + md.convertFile(kwargs.get('input', None), + kwargs.get('output', None), + kwargs.get('encoding', None)) diff --git a/venv/Lib/site-packages/markdown/extensions/__init__.py b/venv/Lib/site-packages/markdown/extensions/__init__.py new file mode 100644 index 0000000..4bc8e5f --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/__init__.py @@ -0,0 +1,107 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import warnings +from ..util import parseBoolValue + + +class Extension: + """ Base class for extensions to subclass. """ + + # Default config -- to be overriden by a subclass + # Must be of the following format: + # { + # 'key': ['value', 'description'] + # } + # Note that Extension.setConfig will raise a KeyError + # if a default is not set here. + config = {} + + def __init__(self, **kwargs): + """ Initiate Extension and set up configs. """ + self.setConfigs(kwargs) + + def getConfig(self, key, default=''): + """ Return a setting for the given key or an empty string. """ + if key in self.config: + return self.config[key][0] + else: + return default + + def getConfigs(self): + """ Return all configs settings as a dict. """ + return {key: self.getConfig(key) for key in self.config.keys()} + + def getConfigInfo(self): + """ Return all config descriptions as a list of tuples. """ + return [(key, self.config[key][1]) for key in self.config.keys()] + + def setConfig(self, key, value): + """ Set a config setting for `key` with the given `value`. """ + if isinstance(self.config[key][0], bool): + value = parseBoolValue(value) + if self.config[key][0] is None: + value = parseBoolValue(value, preserve_none=True) + self.config[key][0] = value + + def setConfigs(self, items): + """ Set multiple config settings given a dict or list of tuples. """ + if hasattr(items, 'items'): + # it's a dict + items = items.items() + for key, value in items: + self.setConfig(key, value) + + def _extendMarkdown(self, *args): + """ Private wrapper around extendMarkdown. """ + md = args[0] + try: + self.extendMarkdown(md) + except TypeError as e: + if "missing 1 required positional argument" in str(e): + # Must be a 2.x extension. Pass in a dumby md_globals. + self.extendMarkdown(md, {}) + warnings.warn( + "The 'md_globals' parameter of '{}.{}.extendMarkdown' is " + "deprecated.".format(self.__class__.__module__, self.__class__.__name__), + category=DeprecationWarning, + stacklevel=2 + ) + else: + raise + + def extendMarkdown(self, md): + """ + Add the various proccesors and patterns to the Markdown Instance. + + This method must be overriden by every extension. + + Keyword arguments: + + * md: The Markdown instance. + + * md_globals: Global variables in the markdown module namespace. + + """ + raise NotImplementedError( + 'Extension "%s.%s" must define an "extendMarkdown"' + 'method.' % (self.__class__.__module__, self.__class__.__name__) + ) diff --git a/venv/Lib/site-packages/markdown/extensions/abbr.py b/venv/Lib/site-packages/markdown/extensions/abbr.py new file mode 100644 index 0000000..9879314 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/abbr.py @@ -0,0 +1,99 @@ +''' +Abbreviation Extension for Python-Markdown +========================================== + +This extension adds abbreviation handling to Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/abbreviations> +for documentation. + +Oringinal code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/) and + [Seemant Kulleen](http://www.kulleen.org/) + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +''' + +from . import Extension +from ..blockprocessors import BlockProcessor +from ..inlinepatterns import InlineProcessor +from ..util import AtomicString +import re +import xml.etree.ElementTree as etree + + +class AbbrExtension(Extension): + """ Abbreviation Extension for Python-Markdown. """ + + def extendMarkdown(self, md): + """ Insert AbbrPreprocessor before ReferencePreprocessor. """ + md.parser.blockprocessors.register(AbbrPreprocessor(md.parser), 'abbr', 16) + + +class AbbrPreprocessor(BlockProcessor): + """ Abbreviation Preprocessor - parse text for abbr references. """ + + RE = re.compile(r'^[*]\[(?P<abbr>[^\]]*)\][ ]?:[ ]*\n?[ ]*(?P<title>.*)$', re.MULTILINE) + + def test(self, parent, block): + return True + + def run(self, parent, blocks): + ''' + Find and remove all Abbreviation references from the text. + Each reference is set as a new AbbrPattern in the markdown instance. + + ''' + block = blocks.pop(0) + m = self.RE.search(block) + if m: + abbr = m.group('abbr').strip() + title = m.group('title').strip() + self.parser.md.inlinePatterns.register( + AbbrInlineProcessor(self._generate_pattern(abbr), title), 'abbr-%s' % abbr, 2 + ) + if block[m.end():].strip(): + # Add any content after match back to blocks as separate block + blocks.insert(0, block[m.end():].lstrip('\n')) + if block[:m.start()].strip(): + # Add any content before match back to blocks as separate block + blocks.insert(0, block[:m.start()].rstrip('\n')) + return True + # No match. Restore block. + blocks.insert(0, block) + return False + + def _generate_pattern(self, text): + ''' + Given a string, returns an regex pattern to match that string. + + 'HTML' -> r'(?P<abbr>[H][T][M][L])' + + Note: we force each char as a literal match (in brackets) as we don't + know what they will be beforehand. + + ''' + chars = list(text) + for i in range(len(chars)): + chars[i] = r'[%s]' % chars[i] + return r'(?P<abbr>\b%s\b)' % (r''.join(chars)) + + +class AbbrInlineProcessor(InlineProcessor): + """ Abbreviation inline pattern. """ + + def __init__(self, pattern, title): + super().__init__(pattern) + self.title = title + + def handleMatch(self, m, data): + abbr = etree.Element('abbr') + abbr.text = AtomicString(m.group('abbr')) + abbr.set('title', self.title) + return abbr, m.start(0), m.end(0) + + +def makeExtension(**kwargs): # pragma: no cover + return AbbrExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/admonition.py b/venv/Lib/site-packages/markdown/extensions/admonition.py new file mode 100644 index 0000000..cb8d901 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/admonition.py @@ -0,0 +1,170 @@ +""" +Admonition extension for Python-Markdown +======================================== + +Adds rST-style admonitions. Inspired by [rST][] feature with the same name. + +[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions # noqa + +See <https://Python-Markdown.github.io/extensions/admonition> +for documentation. + +Original code Copyright [Tiago Serafim](https://www.tiagoserafim.com/). + +All changes Copyright The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import BlockProcessor +import xml.etree.ElementTree as etree +import re + + +class AdmonitionExtension(Extension): + """ Admonition extension for Python-Markdown. """ + + def extendMarkdown(self, md): + """ Add Admonition to Markdown instance. """ + md.registerExtension(self) + + md.parser.blockprocessors.register(AdmonitionProcessor(md.parser), 'admonition', 105) + + +class AdmonitionProcessor(BlockProcessor): + + CLASSNAME = 'admonition' + CLASSNAME_TITLE = 'admonition-title' + RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)') + RE_SPACES = re.compile(' +') + + def __init__(self, parser): + """Initialization.""" + + super().__init__(parser) + + self.current_sibling = None + self.content_indention = 0 + + def parse_content(self, parent, block): + """Get sibling admonition. + + Retrieve the appropriate sibling element. This can get tricky when + dealing with lists. + + """ + + old_block = block + the_rest = '' + + # We already acquired the block via test + if self.current_sibling is not None: + sibling = self.current_sibling + block, the_rest = self.detab(block, self.content_indent) + self.current_sibling = None + self.content_indent = 0 + return sibling, block, the_rest + + sibling = self.lastChild(parent) + + if sibling is None or sibling.get('class', '').find(self.CLASSNAME) == -1: + sibling = None + else: + # If the last child is a list and the content is sufficiently indented + # to be under it, then the content's sibling is in the list. + last_child = self.lastChild(sibling) + indent = 0 + while last_child: + if ( + sibling and block.startswith(' ' * self.tab_length * 2) and + last_child and last_child.tag in ('ul', 'ol', 'dl') + ): + + # The expectation is that we'll find an <li> or <dt>. + # We should get its last child as well. + sibling = self.lastChild(last_child) + last_child = self.lastChild(sibling) if sibling else None + + # Context has been lost at this point, so we must adjust the + # text's indentation level so it will be evaluated correctly + # under the list. + block = block[self.tab_length:] + indent += self.tab_length + else: + last_child = None + + if not block.startswith(' ' * self.tab_length): + sibling = None + + if sibling is not None: + indent += self.tab_length + block, the_rest = self.detab(old_block, indent) + self.current_sibling = sibling + self.content_indent = indent + + return sibling, block, the_rest + + def test(self, parent, block): + + if self.RE.search(block): + return True + else: + return self.parse_content(parent, block)[0] is not None + + def run(self, parent, blocks): + block = blocks.pop(0) + m = self.RE.search(block) + + if m: + if m.start() > 0: + self.parser.parseBlocks(parent, [block[:m.start()]]) + block = block[m.end():] # removes the first line + block, theRest = self.detab(block) + else: + sibling, block, theRest = self.parse_content(parent, block) + + if m: + klass, title = self.get_class_and_title(m) + div = etree.SubElement(parent, 'div') + div.set('class', '{} {}'.format(self.CLASSNAME, klass)) + if title: + p = etree.SubElement(div, 'p') + p.text = title + p.set('class', self.CLASSNAME_TITLE) + else: + # Sibling is a list item, but we need to wrap it's content should be wrapped in <p> + if sibling.tag in ('li', 'dd') and sibling.text: + text = sibling.text + sibling.text = '' + p = etree.SubElement(sibling, 'p') + p.text = text + + div = sibling + + self.parser.parseChunk(div, block) + + if theRest: + # This block contained unindented line(s) after the first indented + # line. Insert these lines as the first block of the master blocks + # list for future processing. + blocks.insert(0, theRest) + + def get_class_and_title(self, match): + klass, title = match.group(1).lower(), match.group(2) + klass = self.RE_SPACES.sub(' ', klass) + if title is None: + # no title was provided, use the capitalized classname as title + # e.g.: `!!! note` will render + # `<p class="admonition-title">Note</p>` + title = klass.split(' ', 1)[0].capitalize() + elif title == '': + # an explicit blank title should not be rendered + # e.g.: `!!! warning ""` will *not* render `p` with a title + title = None + return klass, title + + +def makeExtension(**kwargs): # pragma: no cover + return AdmonitionExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/attr_list.py b/venv/Lib/site-packages/markdown/extensions/attr_list.py new file mode 100644 index 0000000..9a67551 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/attr_list.py @@ -0,0 +1,166 @@ +""" +Attribute List Extension for Python-Markdown +============================================ + +Adds attribute list syntax. Inspired by +[maruku](http://maruku.rubyforge.org/proposal.html#attribute_lists)'s +feature of the same name. + +See <https://Python-Markdown.github.io/extensions/attr_list> +for documentation. + +Original code Copyright 2011 [Waylan Limberg](http://achinghead.com/). + +All changes Copyright 2011-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..treeprocessors import Treeprocessor +import re + + +def _handle_double_quote(s, t): + k, v = t.split('=', 1) + return k, v.strip('"') + + +def _handle_single_quote(s, t): + k, v = t.split('=', 1) + return k, v.strip("'") + + +def _handle_key_value(s, t): + return t.split('=', 1) + + +def _handle_word(s, t): + if t.startswith('.'): + return '.', t[1:] + if t.startswith('#'): + return 'id', t[1:] + return t, t + + +_scanner = re.Scanner([ + (r'[^ =]+=".*?"', _handle_double_quote), + (r"[^ =]+='.*?'", _handle_single_quote), + (r'[^ =]+=[^ =]+', _handle_key_value), + (r'[^ =]+', _handle_word), + (r' ', None) +]) + + +def get_attrs(str): + """ Parse attribute list and return a list of attribute tuples. """ + return _scanner.scan(str)[0] + + +def isheader(elem): + return elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] + + +class AttrListTreeprocessor(Treeprocessor): + + BASE_RE = r'\{\:?[ ]*([^\}\n ][^\}\n]*)[ ]*\}' + HEADER_RE = re.compile(r'[ ]+{}[ ]*$'.format(BASE_RE)) + BLOCK_RE = re.compile(r'\n[ ]*{}[ ]*$'.format(BASE_RE)) + INLINE_RE = re.compile(r'^{}'.format(BASE_RE)) + NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff' + r'\u0370-\u037d\u037f-\u1fff\u200c-\u200d' + r'\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff' + r'\uf900-\ufdcf\ufdf0-\ufffd' + r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+') + + def run(self, doc): + for elem in doc.iter(): + if self.md.is_block_level(elem.tag): + # Block level: check for attrs on last line of text + RE = self.BLOCK_RE + if isheader(elem) or elem.tag in ['dt', 'td', 'th']: + # header, def-term, or table cell: check for attrs at end of element + RE = self.HEADER_RE + if len(elem) and elem.tag == 'li': + # special case list items. children may include a ul or ol. + pos = None + # find the ul or ol position + for i, child in enumerate(elem): + if child.tag in ['ul', 'ol']: + pos = i + break + if pos is None and elem[-1].tail: + # use tail of last child. no ul or ol. + m = RE.search(elem[-1].tail) + if m: + self.assign_attrs(elem, m.group(1)) + elem[-1].tail = elem[-1].tail[:m.start()] + elif pos is not None and pos > 0 and elem[pos-1].tail: + # use tail of last child before ul or ol + m = RE.search(elem[pos-1].tail) + if m: + self.assign_attrs(elem, m.group(1)) + elem[pos-1].tail = elem[pos-1].tail[:m.start()] + elif elem.text: + # use text. ul is first child. + m = RE.search(elem.text) + if m: + self.assign_attrs(elem, m.group(1)) + elem.text = elem.text[:m.start()] + elif len(elem) and elem[-1].tail: + # has children. Get from tail of last child + m = RE.search(elem[-1].tail) + if m: + self.assign_attrs(elem, m.group(1)) + elem[-1].tail = elem[-1].tail[:m.start()] + if isheader(elem): + # clean up trailing #s + elem[-1].tail = elem[-1].tail.rstrip('#').rstrip() + elif elem.text: + # no children. Get from text. + m = RE.search(elem.text) + if m: + self.assign_attrs(elem, m.group(1)) + elem.text = elem.text[:m.start()] + if isheader(elem): + # clean up trailing #s + elem.text = elem.text.rstrip('#').rstrip() + else: + # inline: check for attrs at start of tail + if elem.tail: + m = self.INLINE_RE.match(elem.tail) + if m: + self.assign_attrs(elem, m.group(1)) + elem.tail = elem.tail[m.end():] + + def assign_attrs(self, elem, attrs): + """ Assign attrs to element. """ + for k, v in get_attrs(attrs): + if k == '.': + # add to class + cls = elem.get('class') + if cls: + elem.set('class', '{} {}'.format(cls, v)) + else: + elem.set('class', v) + else: + # assign attr k with v + elem.set(self.sanitize_name(k), v) + + def sanitize_name(self, name): + """ + Sanitize name as 'an XML Name, minus the ":"'. + See https://www.w3.org/TR/REC-xml-names/#NT-NCName + """ + return self.NAME_RE.sub('_', name) + + +class AttrListExtension(Extension): + def extendMarkdown(self, md): + md.treeprocessors.register(AttrListTreeprocessor(md), 'attr_list', 8) + md.registerExtension(self) + + +def makeExtension(**kwargs): # pragma: no cover + return AttrListExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/codehilite.py b/venv/Lib/site-packages/markdown/extensions/codehilite.py new file mode 100644 index 0000000..e1c2218 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/codehilite.py @@ -0,0 +1,307 @@ +""" +CodeHilite Extension for Python-Markdown +======================================== + +Adds code/syntax highlighting to standard Python-Markdown code blocks. + +See <https://Python-Markdown.github.io/extensions/code_hilite> +for documentation. + +Original code Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/). + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..treeprocessors import Treeprocessor +from ..util import parseBoolValue + +try: # pragma: no cover + from pygments import highlight + from pygments.lexers import get_lexer_by_name, guess_lexer + from pygments.formatters import get_formatter_by_name + pygments = True +except ImportError: # pragma: no cover + pygments = False + + +def parse_hl_lines(expr): + """Support our syntax for emphasizing certain lines of code. + + expr should be like '1 2' to emphasize lines 1 and 2 of a code block. + Returns a list of ints, the line numbers to emphasize. + """ + if not expr: + return [] + + try: + return list(map(int, expr.split())) + except ValueError: # pragma: no cover + return [] + + +# ------------------ The Main CodeHilite Class ---------------------- +class CodeHilite: + """ + Determine language of source code, and pass it on to the Pygments highlighter. + + Usage: + code = CodeHilite(src=some_code, lang='python') + html = code.hilite() + + Arguments: + * src: Source string or any object with a .readline attribute. + + * lang: String name of Pygments lexer to use for highlighting. Default: `None`. + + * guess_lang: Auto-detect which lexer to use. Ignored if `lang` is set to a valid + value. Default: `True`. + + * use_pygments: Pass code to pygments for code highlighting. If `False`, the code is + instead wrapped for highlighting by a JavaScript library. Default: `True`. + + * linenums: An alias to Pygments `linenos` formatter option. Default: `None`. + + * css_class: An alias to Pygments `cssclass` formatter option. Default: 'codehilite'. + + * lang_prefix: Prefix prepended to the language when `use_pygments` is `False`. + Default: "language-". + + Other Options: + Any other options are accepted and passed on to the lexer and formatter. Therefore, + valid options include any options which are accepted by the `html` formatter or + whichever lexer the code's language uses. Note that most lexers do not have any + options. However, a few have very useful options, such as PHP's `startinline` option. + Any invalid options are ignored without error. + + Formatter options: https://pygments.org/docs/formatters/#HtmlFormatter + Lexer Options: https://pygments.org/docs/lexers/ + + Advanced Usage: + code = CodeHilite( + src = some_code, + lang = 'php', + startinline = True, # Lexer option. Snippet does not start with `<?php`. + linenostart = 42, # Formatter option. Snippet starts on line 42. + hl_lines = [45, 49, 50], # Formatter option. Highlight lines 45, 49, and 50. + linenos = 'inline' # Formatter option. Avoid alignment problems. + ) + html = code.hilite() + + """ + + def __init__(self, src, **options): + self.src = src + self.lang = options.pop('lang', None) + self.guess_lang = options.pop('guess_lang', True) + self.use_pygments = options.pop('use_pygments', True) + self.lang_prefix = options.pop('lang_prefix', 'language-') + + if 'linenos' not in options: + options['linenos'] = options.pop('linenums', None) + if 'cssclass' not in options: + options['cssclass'] = options.pop('css_class', 'codehilite') + if 'wrapcode' not in options: + # Override pygments default + options['wrapcode'] = True + # Disallow use of `full` option + options['full'] = False + + self.options = options + + def hilite(self, shebang=True): + """ + Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with + optional line numbers. The output should then be styled with css to + your liking. No styles are applied by default - only styling hooks + (i.e.: <span class="k">). + + returns : A string of html. + + """ + + self.src = self.src.strip('\n') + + if self.lang is None and shebang: + self._parseHeader() + + if pygments and self.use_pygments: + try: + lexer = get_lexer_by_name(self.lang, **self.options) + except ValueError: + try: + if self.guess_lang: + lexer = guess_lexer(self.src, **self.options) + else: + lexer = get_lexer_by_name('text', **self.options) + except ValueError: # pragma: no cover + lexer = get_lexer_by_name('text', **self.options) + formatter = get_formatter_by_name('html', **self.options) + return highlight(self.src, lexer, formatter) + else: + # just escape and build markup usable by JS highlighting libs + txt = self.src.replace('&', '&') + txt = txt.replace('<', '<') + txt = txt.replace('>', '>') + txt = txt.replace('"', '"') + classes = [] + if self.lang: + classes.append('{}{}'.format(self.lang_prefix, self.lang)) + if self.options['linenos']: + classes.append('linenums') + class_str = '' + if classes: + class_str = ' class="{}"'.format(' '.join(classes)) + return '<pre class="{}"><code{}>{}\n</code></pre>\n'.format( + self.options['cssclass'], + class_str, + txt + ) + + def _parseHeader(self): + """ + Determines language of a code block from shebang line and whether the + said line should be removed or left in place. If the sheband line + contains a path (even a single /) then it is assumed to be a real + shebang line and left alone. However, if no path is given + (e.i.: #!python or :::python) then it is assumed to be a mock shebang + for language identification of a code fragment and removed from the + code block prior to processing for code highlighting. When a mock + shebang (e.i: #!python) is found, line numbering is turned on. When + colons are found in place of a shebang (e.i.: :::python), line + numbering is left in the current state - off by default. + + Also parses optional list of highlight lines, like: + + :::python hl_lines="1 3" + """ + + import re + + # split text into lines + lines = self.src.split("\n") + # pull first line to examine + fl = lines.pop(0) + + c = re.compile(r''' + (?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons + (?P<path>(?:/\w+)*[/ ])? # Zero or 1 path + (?P<lang>[\w#.+-]*) # The language + \s* # Arbitrary whitespace + # Optional highlight lines, single- or double-quote-delimited + (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))? + ''', re.VERBOSE) + # search first line for shebang + m = c.search(fl) + if m: + # we have a match + try: + self.lang = m.group('lang').lower() + except IndexError: # pragma: no cover + self.lang = None + if m.group('path'): + # path exists - restore first line + lines.insert(0, fl) + if self.options['linenos'] is None and m.group('shebang'): + # Overridable and Shebang exists - use line numbers + self.options['linenos'] = True + + self.options['hl_lines'] = parse_hl_lines(m.group('hl_lines')) + else: + # No match + lines.insert(0, fl) + + self.src = "\n".join(lines).strip("\n") + + +# ------------------ The Markdown Extension ------------------------------- + + +class HiliteTreeprocessor(Treeprocessor): + """ Hilight source code in code blocks. """ + + def code_unescape(self, text): + """Unescape code.""" + text = text.replace("<", "<") + text = text.replace(">", ">") + # Escaped '&' should be replaced at the end to avoid + # conflicting with < and >. + text = text.replace("&", "&") + return text + + def run(self, root): + """ Find code blocks and store in htmlStash. """ + blocks = root.iter('pre') + for block in blocks: + if len(block) == 1 and block[0].tag == 'code': + code = CodeHilite( + self.code_unescape(block[0].text), + tab_length=self.md.tab_length, + style=self.config.pop('pygments_style', 'default'), + **self.config + ) + placeholder = self.md.htmlStash.store(code.hilite()) + # Clear codeblock in etree instance + block.clear() + # Change to p element which will later + # be removed when inserting raw html + block.tag = 'p' + block.text = placeholder + + +class CodeHiliteExtension(Extension): + """ Add source code hilighting to markdown codeblocks. """ + + def __init__(self, **kwargs): + # define default configs + self.config = { + 'linenums': [None, + "Use lines numbers. True|table|inline=yes, False=no, None=auto"], + 'guess_lang': [True, + "Automatic language detection - Default: True"], + 'css_class': ["codehilite", + "Set class name for wrapper <div> - " + "Default: codehilite"], + 'pygments_style': ['default', + 'Pygments HTML Formatter Style ' + '(Colorscheme) - Default: default'], + 'noclasses': [False, + 'Use inline styles instead of CSS classes - ' + 'Default false'], + 'use_pygments': [True, + 'Use Pygments to Highlight code blocks. ' + 'Disable if using a JavaScript library. ' + 'Default: True'], + 'lang_prefix': [ + 'language-', + 'Prefix prepended to the language when use_pygments is false. Default: "language-"' + ] + } + + for key, value in kwargs.items(): + if key in self.config: + self.setConfig(key, value) + else: + # manually set unknown keywords. + if isinstance(value, str): + try: + # Attempt to parse str as a bool value + value = parseBoolValue(value, preserve_none=True) + except ValueError: + pass # Assume it's not a bool value. Use as-is. + self.config[key] = [value, ''] + + def extendMarkdown(self, md): + """ Add HilitePostprocessor to Markdown instance. """ + hiliter = HiliteTreeprocessor(md) + hiliter.config = self.getConfigs() + md.treeprocessors.register(hiliter, 'hilite', 30) + + md.registerExtension(self) + + +def makeExtension(**kwargs): # pragma: no cover + return CodeHiliteExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/def_list.py b/venv/Lib/site-packages/markdown/extensions/def_list.py new file mode 100644 index 0000000..0e8e452 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/def_list.py @@ -0,0 +1,111 @@ +""" +Definition List Extension for Python-Markdown +============================================= + +Adds parsing of Definition Lists to Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/definition_lists> +for documentation. + +Original code Copyright 2008 [Waylan Limberg](http://achinghead.com) + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import BlockProcessor, ListIndentProcessor +import xml.etree.ElementTree as etree +import re + + +class DefListProcessor(BlockProcessor): + """ Process Definition Lists. """ + + RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') + NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]') + + def test(self, parent, block): + return bool(self.RE.search(block)) + + def run(self, parent, blocks): + + raw_block = blocks.pop(0) + m = self.RE.search(raw_block) + terms = [term.strip() for term in + raw_block[:m.start()].split('\n') if term.strip()] + block = raw_block[m.end():] + no_indent = self.NO_INDENT_RE.match(block) + if no_indent: + d, theRest = (block, None) + else: + d, theRest = self.detab(block) + if d: + d = '{}\n{}'.format(m.group(2), d) + else: + d = m.group(2) + sibling = self.lastChild(parent) + if not terms and sibling is None: + # This is not a definition item. Most likely a paragraph that + # starts with a colon at the beginning of a document or list. + blocks.insert(0, raw_block) + return False + if not terms and sibling.tag == 'p': + # The previous paragraph contains the terms + state = 'looselist' + terms = sibling.text.split('\n') + parent.remove(sibling) + # Acquire new sibling + sibling = self.lastChild(parent) + else: + state = 'list' + + if sibling is not None and sibling.tag == 'dl': + # This is another item on an existing list + dl = sibling + if not terms and len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): + state = 'looselist' + else: + # This is a new list + dl = etree.SubElement(parent, 'dl') + # Add terms + for term in terms: + dt = etree.SubElement(dl, 'dt') + dt.text = term + # Add definition + self.parser.state.set(state) + dd = etree.SubElement(dl, 'dd') + self.parser.parseBlocks(dd, [d]) + self.parser.state.reset() + + if theRest: + blocks.insert(0, theRest) + + +class DefListIndentProcessor(ListIndentProcessor): + """ Process indented children of definition list items. """ + + # Defintion lists need to be aware of all list types + ITEM_TYPES = ['dd', 'li'] + LIST_TYPES = ['dl', 'ol', 'ul'] + + def create_item(self, parent, block): + """ Create a new dd or li (depending on parent) and parse the block with it as the parent. """ + + dd = etree.SubElement(parent, 'dd') + self.parser.parseBlocks(dd, [block]) + + +class DefListExtension(Extension): + """ Add definition lists to Markdown. """ + + def extendMarkdown(self, md): + """ Add an instance of DefListProcessor to BlockParser. """ + md.parser.blockprocessors.register(DefListIndentProcessor(md.parser), 'defindent', 85) + md.parser.blockprocessors.register(DefListProcessor(md.parser), 'deflist', 25) + + +def makeExtension(**kwargs): # pragma: no cover + return DefListExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/extra.py b/venv/Lib/site-packages/markdown/extensions/extra.py new file mode 100644 index 0000000..ebd168c --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/extra.py @@ -0,0 +1,58 @@ +""" +Python-Markdown Extra Extension +=============================== + +A compilation of various Python-Markdown extensions that imitates +[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/). + +Note that each of the individual extensions still need to be available +on your PYTHONPATH. This extension simply wraps them all up as a +convenience so that only one extension needs to be listed when +initiating Markdown. See the documentation for each individual +extension for specifics about that extension. + +There may be additional extensions that are distributed with +Python-Markdown that are not included here in Extra. Those extensions +are not part of PHP Markdown Extra, and therefore, not part of +Python-Markdown Extra. If you really would like Extra to include +additional extensions, we suggest creating your own clone of Extra +under a differant name. You could also edit the `extensions` global +variable defined below, but be aware that such changes may be lost +when you upgrade to any future version of Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/extra> +for documentation. + +Copyright The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension + +extensions = [ + 'fenced_code', + 'footnotes', + 'attr_list', + 'def_list', + 'tables', + 'abbr', + 'md_in_html' +] + + +class ExtraExtension(Extension): + """ Add various extensions to Markdown class.""" + + def __init__(self, **kwargs): + """ config is a dumb holder which gets passed to actual ext later. """ + self.config = kwargs + + def extendMarkdown(self, md): + """ Register extension instances. """ + md.registerExtensions(extensions, self.config) + + +def makeExtension(**kwargs): # pragma: no cover + return ExtraExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/fenced_code.py b/venv/Lib/site-packages/markdown/extensions/fenced_code.py new file mode 100644 index 0000000..9be0ca0 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/fenced_code.py @@ -0,0 +1,179 @@ +""" +Fenced Code Extension for Python Markdown +========================================= + +This extension adds Fenced Code Blocks to Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/fenced_code_blocks> +for documentation. + +Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). + + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) +""" + + +from textwrap import dedent +from . import Extension +from ..preprocessors import Preprocessor +from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines +from .attr_list import get_attrs, AttrListExtension +from ..util import parseBoolValue +import re + + +class FencedCodeExtension(Extension): + def __init__(self, **kwargs): + self.config = { + 'lang_prefix': ['language-', 'Prefix prepended to the language. Default: "language-"'] + } + super().__init__(**kwargs) + + def extendMarkdown(self, md): + """ Add FencedBlockPreprocessor to the Markdown instance. """ + md.registerExtension(self) + + md.preprocessors.register(FencedBlockPreprocessor(md, self.getConfigs()), 'fenced_code_block', 25) + + +class FencedBlockPreprocessor(Preprocessor): + FENCED_BLOCK_RE = re.compile( + dedent(r''' + (?P<fence>^(?:~{3,}|`{3,}))[ ]* # opening fence + ((\{(?P<attrs>[^\}\n]*)\})| # (optional {attrs} or + (\.?(?P<lang>[\w#.+-]*)[ ]*)? # optional (.)lang + (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot)[ ]*)?) # optional hl_lines) + \n # newline (end of opening fence) + (?P<code>.*?)(?<=\n) # the code block + (?P=fence)[ ]*$ # closing fence + '''), + re.MULTILINE | re.DOTALL | re.VERBOSE + ) + + def __init__(self, md, config): + super().__init__(md) + self.config = config + self.checked_for_deps = False + self.codehilite_conf = {} + self.use_attr_list = False + # List of options to convert to bool values + self.bool_options = [ + 'linenums', + 'guess_lang', + 'noclasses', + 'use_pygments' + ] + + def run(self, lines): + """ Match and store Fenced Code Blocks in the HtmlStash. """ + + # Check for dependent extensions + if not self.checked_for_deps: + for ext in self.md.registeredExtensions: + if isinstance(ext, CodeHiliteExtension): + self.codehilite_conf = ext.getConfigs() + if isinstance(ext, AttrListExtension): + self.use_attr_list = True + + self.checked_for_deps = True + + text = "\n".join(lines) + while 1: + m = self.FENCED_BLOCK_RE.search(text) + if m: + lang, id, classes, config = None, '', [], {} + if m.group('attrs'): + id, classes, config = self.handle_attrs(get_attrs(m.group('attrs'))) + if len(classes): + lang = classes.pop(0) + else: + if m.group('lang'): + lang = m.group('lang') + if m.group('hl_lines'): + # Support hl_lines outside of attrs for backward-compatibility + config['hl_lines'] = parse_hl_lines(m.group('hl_lines')) + + # If config is not empty, then the codehighlite extension + # is enabled, so we call it to highlight the code + if self.codehilite_conf and self.codehilite_conf['use_pygments'] and config.get('use_pygments', True): + local_config = self.codehilite_conf.copy() + local_config.update(config) + # Combine classes with cssclass. Ensure cssclass is at end + # as pygments appends a suffix under certain circumstances. + # Ignore ID as Pygments does not offer an option to set it. + if classes: + local_config['css_class'] = '{} {}'.format( + ' '.join(classes), + local_config['css_class'] + ) + highliter = CodeHilite( + m.group('code'), + lang=lang, + style=local_config.pop('pygments_style', 'default'), + **local_config + ) + + code = highliter.hilite(shebang=False) + else: + id_attr = lang_attr = class_attr = kv_pairs = '' + if lang: + lang_attr = ' class="{}{}"'.format(self.config.get('lang_prefix', 'language-'), lang) + if classes: + class_attr = ' class="{}"'.format(' '.join(classes)) + if id: + id_attr = ' id="{}"'.format(id) + if self.use_attr_list and config and not config.get('use_pygments', False): + # Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs + # were defined on the code block, and the `use_pygments` key was not set to True. The + # `use_pygments` key could be either set to False or not defined. It is omitted from output. + kv_pairs = ' ' + ' '.join( + '{k}="{v}"'.format(k=k, v=v) for k, v in config.items() if k != 'use_pygments' + ) + code = '<pre{id}{cls}><code{lang}{kv}>{code}</code></pre>'.format( + id=id_attr, + cls=class_attr, + lang=lang_attr, + kv=kv_pairs, + code=self._escape(m.group('code')) + ) + + placeholder = self.md.htmlStash.store(code) + text = '{}\n{}\n{}'.format(text[:m.start()], + placeholder, + text[m.end():]) + else: + break + return text.split("\n") + + def handle_attrs(self, attrs): + """ Return tuple: (id, [list, of, classes], {configs}) """ + id = '' + classes = [] + configs = {} + for k, v in attrs: + if k == 'id': + id = v + elif k == '.': + classes.append(v) + elif k == 'hl_lines': + configs[k] = parse_hl_lines(v) + elif k in self.bool_options: + configs[k] = parseBoolValue(v, fail_on_errors=False, preserve_none=True) + else: + configs[k] = v + return id, classes, configs + + def _escape(self, txt): + """ basic html escaping """ + txt = txt.replace('&', '&') + txt = txt.replace('<', '<') + txt = txt.replace('>', '>') + txt = txt.replace('"', '"') + return txt + + +def makeExtension(**kwargs): # pragma: no cover + return FencedCodeExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/footnotes.py b/venv/Lib/site-packages/markdown/extensions/footnotes.py new file mode 100644 index 0000000..f6f4c85 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/footnotes.py @@ -0,0 +1,402 @@ +""" +Footnotes Extension for Python-Markdown +======================================= + +Adds footnote handling to Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/footnotes> +for documentation. + +Copyright The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import BlockProcessor +from ..inlinepatterns import InlineProcessor +from ..treeprocessors import Treeprocessor +from ..postprocessors import Postprocessor +from .. import util +from collections import OrderedDict +import re +import copy +import xml.etree.ElementTree as etree + +FN_BACKLINK_TEXT = util.STX + "zz1337820767766393qq" + util.ETX +NBSP_PLACEHOLDER = util.STX + "qq3936677670287331zz" + util.ETX +RE_REF_ID = re.compile(r'(fnref)(\d+)') + + +class FootnoteExtension(Extension): + """ Footnote Extension. """ + + def __init__(self, **kwargs): + """ Setup configs. """ + + self.config = { + 'PLACE_MARKER': + ["///Footnotes Go Here///", + "The text string that marks where the footnotes go"], + 'UNIQUE_IDS': + [False, + "Avoid name collisions across " + "multiple calls to reset()."], + "BACKLINK_TEXT": + ["↩", + "The text string that links from the footnote " + "to the reader's place."], + "BACKLINK_TITLE": + ["Jump back to footnote %d in the text", + "The text string used for the title HTML attribute " + "of the backlink. %d will be replaced by the " + "footnote number."], + "SEPARATOR": + [":", + "Footnote separator."] + } + super().__init__(**kwargs) + + # In multiple invocations, emit links that don't get tangled. + self.unique_prefix = 0 + self.found_refs = {} + self.used_refs = set() + + self.reset() + + def extendMarkdown(self, md): + """ Add pieces to Markdown. """ + md.registerExtension(self) + self.parser = md.parser + self.md = md + # Insert a blockprocessor before ReferencePreprocessor + md.parser.blockprocessors.register(FootnoteBlockProcessor(self), 'footnote', 17) + + # Insert an inline pattern before ImageReferencePattern + FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah + md.inlinePatterns.register(FootnoteInlineProcessor(FOOTNOTE_RE, self), 'footnote', 175) + # Insert a tree-processor that would actually add the footnote div + # This must be before all other treeprocessors (i.e., inline and + # codehilite) so they can run on the the contents of the div. + md.treeprocessors.register(FootnoteTreeprocessor(self), 'footnote', 50) + + # Insert a tree-processor that will run after inline is done. + # In this tree-processor we want to check our duplicate footnote tracker + # And add additional backrefs to the footnote pointing back to the + # duplicated references. + md.treeprocessors.register(FootnotePostTreeprocessor(self), 'footnote-duplicate', 15) + + # Insert a postprocessor after amp_substitute processor + md.postprocessors.register(FootnotePostprocessor(self), 'footnote', 25) + + def reset(self): + """ Clear footnotes on reset, and prepare for distinct document. """ + self.footnotes = OrderedDict() + self.unique_prefix += 1 + self.found_refs = {} + self.used_refs = set() + + def unique_ref(self, reference, found=False): + """ Get a unique reference if there are duplicates. """ + if not found: + return reference + + original_ref = reference + while reference in self.used_refs: + ref, rest = reference.split(self.get_separator(), 1) + m = RE_REF_ID.match(ref) + if m: + reference = '%s%d%s%s' % (m.group(1), int(m.group(2))+1, self.get_separator(), rest) + else: + reference = '%s%d%s%s' % (ref, 2, self.get_separator(), rest) + + self.used_refs.add(reference) + if original_ref in self.found_refs: + self.found_refs[original_ref] += 1 + else: + self.found_refs[original_ref] = 1 + return reference + + def findFootnotesPlaceholder(self, root): + """ Return ElementTree Element that contains Footnote placeholder. """ + def finder(element): + for child in element: + if child.text: + if child.text.find(self.getConfig("PLACE_MARKER")) > -1: + return child, element, True + if child.tail: + if child.tail.find(self.getConfig("PLACE_MARKER")) > -1: + return child, element, False + child_res = finder(child) + if child_res is not None: + return child_res + return None + + res = finder(root) + return res + + def setFootnote(self, id, text): + """ Store a footnote for later retrieval. """ + self.footnotes[id] = text + + def get_separator(self): + """ Get the footnote separator. """ + return self.getConfig("SEPARATOR") + + def makeFootnoteId(self, id): + """ Return footnote link id. """ + if self.getConfig("UNIQUE_IDS"): + return 'fn%s%d-%s' % (self.get_separator(), self.unique_prefix, id) + else: + return 'fn{}{}'.format(self.get_separator(), id) + + def makeFootnoteRefId(self, id, found=False): + """ Return footnote back-link id. """ + if self.getConfig("UNIQUE_IDS"): + return self.unique_ref('fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id), found) + else: + return self.unique_ref('fnref{}{}'.format(self.get_separator(), id), found) + + def makeFootnotesDiv(self, root): + """ Return div of footnotes as et Element. """ + + if not list(self.footnotes.keys()): + return None + + div = etree.Element("div") + div.set('class', 'footnote') + etree.SubElement(div, "hr") + ol = etree.SubElement(div, "ol") + surrogate_parent = etree.Element("div") + + for index, id in enumerate(self.footnotes.keys(), start=1): + li = etree.SubElement(ol, "li") + li.set("id", self.makeFootnoteId(id)) + # Parse footnote with surrogate parent as li cannot be used. + # List block handlers have special logic to deal with li. + # When we are done parsing, we will copy everything over to li. + self.parser.parseChunk(surrogate_parent, self.footnotes[id]) + for el in list(surrogate_parent): + li.append(el) + surrogate_parent.remove(el) + backlink = etree.Element("a") + backlink.set("href", "#" + self.makeFootnoteRefId(id)) + backlink.set("class", "footnote-backref") + backlink.set( + "title", + self.getConfig("BACKLINK_TITLE") % (index) + ) + backlink.text = FN_BACKLINK_TEXT + + if len(li): + node = li[-1] + if node.tag == "p": + node.text = node.text + NBSP_PLACEHOLDER + node.append(backlink) + else: + p = etree.SubElement(li, "p") + p.append(backlink) + return div + + +class FootnoteBlockProcessor(BlockProcessor): + """ Find all footnote references and store for later use. """ + + RE = re.compile(r'^[ ]{0,3}\[\^([^\]]*)\]:[ ]*(.*)$', re.MULTILINE) + + def __init__(self, footnotes): + super().__init__(footnotes.parser) + self.footnotes = footnotes + + def test(self, parent, block): + return True + + def run(self, parent, blocks): + """ Find, set, and remove footnote definitions. """ + block = blocks.pop(0) + m = self.RE.search(block) + if m: + id = m.group(1) + fn_blocks = [m.group(2)] + + # Handle rest of block + therest = block[m.end():].lstrip('\n') + m2 = self.RE.search(therest) + if m2: + # Another footnote exists in the rest of this block. + # Any content before match is continuation of this footnote, which may be lazily indented. + before = therest[:m2.start()].rstrip('\n') + fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(before)]).lstrip('\n') + # Add back to blocks everything from begining of match forward for next iteration. + blocks.insert(0, therest[m2.start():]) + else: + # All remaining lines of block are continuation of this footnote, which may be lazily indented. + fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(therest)]).strip('\n') + + # Check for child elements in remaining blocks. + fn_blocks.extend(self.detectTabbed(blocks)) + + footnote = "\n\n".join(fn_blocks) + self.footnotes.setFootnote(id, footnote.rstrip()) + + if block[:m.start()].strip(): + # Add any content before match back to blocks as separate block + blocks.insert(0, block[:m.start()].rstrip('\n')) + return True + # No match. Restore block. + blocks.insert(0, block) + return False + + def detectTabbed(self, blocks): + """ Find indented text and remove indent before further proccesing. + + Returns: a list of blocks with indentation removed. + """ + fn_blocks = [] + while blocks: + if blocks[0].startswith(' '*4): + block = blocks.pop(0) + # Check for new footnotes within this block and split at new footnote. + m = self.RE.search(block) + if m: + # Another footnote exists in this block. + # Any content before match is continuation of this footnote, which may be lazily indented. + before = block[:m.start()].rstrip('\n') + fn_blocks.append(self.detab(before)) + # Add back to blocks everything from begining of match forward for next iteration. + blocks.insert(0, block[m.start():]) + # End of this footnote. + break + else: + # Entire block is part of this footnote. + fn_blocks.append(self.detab(block)) + else: + # End of this footnote. + break + return fn_blocks + + def detab(self, block): + """ Remove one level of indent from a block. + + Preserve lazily indented blocks by only removing indent from indented lines. + """ + lines = block.split('\n') + for i, line in enumerate(lines): + if line.startswith(' '*4): + lines[i] = line[4:] + return '\n'.join(lines) + + +class FootnoteInlineProcessor(InlineProcessor): + """ InlinePattern for footnote markers in a document's body text. """ + + def __init__(self, pattern, footnotes): + super().__init__(pattern) + self.footnotes = footnotes + + def handleMatch(self, m, data): + id = m.group(1) + if id in self.footnotes.footnotes.keys(): + sup = etree.Element("sup") + a = etree.SubElement(sup, "a") + sup.set('id', self.footnotes.makeFootnoteRefId(id, found=True)) + a.set('href', '#' + self.footnotes.makeFootnoteId(id)) + a.set('class', 'footnote-ref') + a.text = str(list(self.footnotes.footnotes.keys()).index(id) + 1) + return sup, m.start(0), m.end(0) + else: + return None, None, None + + +class FootnotePostTreeprocessor(Treeprocessor): + """ Amend footnote div with duplicates. """ + + def __init__(self, footnotes): + self.footnotes = footnotes + + def add_duplicates(self, li, duplicates): + """ Adjust current li and add the duplicates: fnref2, fnref3, etc. """ + for link in li.iter('a'): + # Find the link that needs to be duplicated. + if link.attrib.get('class', '') == 'footnote-backref': + ref, rest = link.attrib['href'].split(self.footnotes.get_separator(), 1) + # Duplicate link the number of times we need to + # and point the to the appropriate references. + links = [] + for index in range(2, duplicates + 1): + sib_link = copy.deepcopy(link) + sib_link.attrib['href'] = '%s%d%s%s' % (ref, index, self.footnotes.get_separator(), rest) + links.append(sib_link) + self.offset += 1 + # Add all the new duplicate links. + el = list(li)[-1] + for link in links: + el.append(link) + break + + def get_num_duplicates(self, li): + """ Get the number of duplicate refs of the footnote. """ + fn, rest = li.attrib.get('id', '').split(self.footnotes.get_separator(), 1) + link_id = '{}ref{}{}'.format(fn, self.footnotes.get_separator(), rest) + return self.footnotes.found_refs.get(link_id, 0) + + def handle_duplicates(self, parent): + """ Find duplicate footnotes and format and add the duplicates. """ + for li in list(parent): + # Check number of duplicates footnotes and insert + # additional links if needed. + count = self.get_num_duplicates(li) + if count > 1: + self.add_duplicates(li, count) + + def run(self, root): + """ Crawl the footnote div and add missing duplicate footnotes. """ + self.offset = 0 + for div in root.iter('div'): + if div.attrib.get('class', '') == 'footnote': + # Footnotes shoul be under the first orderd list under + # the footnote div. So once we find it, quit. + for ol in div.iter('ol'): + self.handle_duplicates(ol) + break + + +class FootnoteTreeprocessor(Treeprocessor): + """ Build and append footnote div to end of document. """ + + def __init__(self, footnotes): + self.footnotes = footnotes + + def run(self, root): + footnotesDiv = self.footnotes.makeFootnotesDiv(root) + if footnotesDiv is not None: + result = self.footnotes.findFootnotesPlaceholder(root) + if result: + child, parent, isText = result + ind = list(parent).index(child) + if isText: + parent.remove(child) + parent.insert(ind, footnotesDiv) + else: + parent.insert(ind + 1, footnotesDiv) + child.tail = None + else: + root.append(footnotesDiv) + + +class FootnotePostprocessor(Postprocessor): + """ Replace placeholders with html entities. """ + def __init__(self, footnotes): + self.footnotes = footnotes + + def run(self, text): + text = text.replace( + FN_BACKLINK_TEXT, self.footnotes.getConfig("BACKLINK_TEXT") + ) + return text.replace(NBSP_PLACEHOLDER, " ") + + +def makeExtension(**kwargs): # pragma: no cover + """ Return an instance of the FootnoteExtension """ + return FootnoteExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/legacy_attrs.py b/venv/Lib/site-packages/markdown/extensions/legacy_attrs.py new file mode 100644 index 0000000..b51d778 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/legacy_attrs.py @@ -0,0 +1,67 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). + +Legacy Attributes Extension +=========================== + +An extension to Python Markdown which implements legacy attributes. + +Prior to Python-Markdown version 3.0, the Markdown class had an `enable_attributes` +keyword which was on by default and provided for attributes to be defined for elements +using the format `{@key=value}`. This extension is provided as a replacement for +backward compatability. New documents should be authored using attr_lists. However, +numerious documents exist which have been using the old attribute format for many +years. This extension can be used to continue to render those documents correctly. +""" + +import re +from markdown.treeprocessors import Treeprocessor, isString +from markdown.extensions import Extension + + +ATTR_RE = re.compile(r'\{@([^\}]*)=([^\}]*)}') # {@id=123} + + +class LegacyAttrs(Treeprocessor): + def run(self, doc): + """Find and set values of attributes ({@key=value}). """ + for el in doc.iter(): + alt = el.get('alt', None) + if alt is not None: + el.set('alt', self.handleAttributes(el, alt)) + if el.text and isString(el.text): + el.text = self.handleAttributes(el, el.text) + if el.tail and isString(el.tail): + el.tail = self.handleAttributes(el, el.tail) + + def handleAttributes(self, el, txt): + """ Set attributes and return text without definitions. """ + def attributeCallback(match): + el.set(match.group(1), match.group(2).replace('\n', ' ')) + return ATTR_RE.sub(attributeCallback, txt) + + +class LegacyAttrExtension(Extension): + def extendMarkdown(self, md): + md.treeprocessors.register(LegacyAttrs(md), 'legacyattrs', 15) + + +def makeExtension(**kwargs): # pragma: no cover + return LegacyAttrExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/legacy_em.py b/venv/Lib/site-packages/markdown/extensions/legacy_em.py new file mode 100644 index 0000000..7fddb77 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/legacy_em.py @@ -0,0 +1,49 @@ +''' +Legacy Em Extension for Python-Markdown +======================================= + +This extention provides legacy behavior for _connected_words_. + +Copyright 2015-2018 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +''' + +from . import Extension +from ..inlinepatterns import UnderscoreProcessor, EmStrongItem, EM_STRONG2_RE, STRONG_EM2_RE +import re + +# _emphasis_ +EMPHASIS_RE = r'(_)([^_]+)\1' + +# __strong__ +STRONG_RE = r'(_{2})(.+?)\1' + +# __strong_em___ +STRONG_EM_RE = r'(_)\1(?!\1)([^_]+?)\1(?!\1)(.+?)\1{3}' + + +class LegacyUnderscoreProcessor(UnderscoreProcessor): + """Emphasis processor for handling strong and em matches inside underscores.""" + + PATTERNS = [ + EmStrongItem(re.compile(EM_STRONG2_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), + EmStrongItem(re.compile(STRONG_EM2_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), + EmStrongItem(re.compile(STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), + EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), + EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') + ] + + +class LegacyEmExtension(Extension): + """ Add legacy_em extension to Markdown class.""" + + def extendMarkdown(self, md): + """ Modify inline patterns. """ + md.inlinePatterns.register(LegacyUnderscoreProcessor(r'_'), 'em_strong2', 50) + + +def makeExtension(**kwargs): # pragma: no cover + """ Return an instance of the LegacyEmExtension """ + return LegacyEmExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/md_in_html.py b/venv/Lib/site-packages/markdown/extensions/md_in_html.py new file mode 100644 index 0000000..81cc15c --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/md_in_html.py @@ -0,0 +1,364 @@ +""" +Python-Markdown Markdown in HTML Extension +=============================== + +An implementation of [PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/)'s +parsing of Markdown syntax in raw HTML. + +See <https://Python-Markdown.github.io/extensions/raw_html> +for documentation. + +Copyright The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import BlockProcessor +from ..preprocessors import Preprocessor +from ..postprocessors import RawHtmlPostprocessor +from .. import util +from ..htmlparser import HTMLExtractor, blank_line_re +import xml.etree.ElementTree as etree + + +class HTMLExtractorExtra(HTMLExtractor): + """ + Override HTMLExtractor and create etree Elements for any elements which should have content parsed as Markdown. + """ + + def __init__(self, md, *args, **kwargs): + # All block-level tags. + self.block_level_tags = set(md.block_level_elements.copy()) + # Block-level tags in which the content only gets span level parsing + self.span_tags = set( + ['address', 'dd', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'li', 'p', 'summary', 'td', 'th'] + ) + # Block-level tags which never get their content parsed. + self.raw_tags = set(['canvas', 'math', 'option', 'pre', 'script', 'style', 'textarea']) + + super().__init__(md, *args, **kwargs) + + # Block-level tags in which the content gets parsed as blocks + self.block_tags = set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags) + self.span_and_blocks_tags = self.block_tags | self.span_tags + + def reset(self): + """Reset this instance. Loses all unprocessed data.""" + self.mdstack = [] # When markdown=1, stack contains a list of tags + self.treebuilder = etree.TreeBuilder() + self.mdstate = [] # one of 'block', 'span', 'off', or None + super().reset() + + def close(self): + """Handle any buffered data.""" + super().close() + # Handle any unclosed tags. + if self.mdstack: + # Close the outermost parent. handle_endtag will close all unclosed children. + self.handle_endtag(self.mdstack[0]) + + def get_element(self): + """ Return element from treebuilder and reset treebuilder for later use. """ + element = self.treebuilder.close() + self.treebuilder = etree.TreeBuilder() + return element + + def get_state(self, tag, attrs): + """ Return state from tag and `markdown` attr. One of 'block', 'span', or 'off'. """ + md_attr = attrs.get('markdown', '0') + if md_attr == 'markdown': + # `<tag markdown>` is the same as `<tag markdown='1'>`. + md_attr = '1' + parent_state = self.mdstate[-1] if self.mdstate else None + if parent_state == 'off' or (parent_state == 'span' and md_attr != '0'): + # Only use the parent state if it is more restrictive than the markdown attribute. + md_attr = parent_state + if ((md_attr == '1' and tag in self.block_tags) or + (md_attr == 'block' and tag in self.span_and_blocks_tags)): + return 'block' + elif ((md_attr == '1' and tag in self.span_tags) or + (md_attr == 'span' and tag in self.span_and_blocks_tags)): + return 'span' + elif tag in self.block_level_tags: + return 'off' + else: # pragma: no cover + return None + + def handle_starttag(self, tag, attrs): + # Handle tags that should always be empty and do not specify a closing tag + if tag in self.empty_tags and (self.at_line_start() or self.intail): + attrs = {key: value if value is not None else key for key, value in attrs} + if "markdown" in attrs: + attrs.pop('markdown') + element = etree.Element(tag, attrs) + data = etree.tostring(element, encoding='unicode', method='html') + else: + data = self.get_starttag_text() + self.handle_empty_tag(data, True) + return + + if tag in self.block_level_tags and (self.at_line_start() or self.intail): + # Valueless attr (ex: `<tag checked>`) results in `[('checked', None)]`. + # Convert to `{'checked': 'checked'}`. + attrs = {key: value if value is not None else key for key, value in attrs} + state = self.get_state(tag, attrs) + if self.inraw or (state in [None, 'off'] and not self.mdstack): + # fall back to default behavior + attrs.pop('markdown', None) + super().handle_starttag(tag, attrs) + else: + if 'p' in self.mdstack and tag in self.block_level_tags: + # Close unclosed 'p' tag + self.handle_endtag('p') + self.mdstate.append(state) + self.mdstack.append(tag) + attrs['markdown'] = state + self.treebuilder.start(tag, attrs) + else: + # Span level tag + if self.inraw: + super().handle_starttag(tag, attrs) + else: + text = self.get_starttag_text() + if self.mdstate and self.mdstate[-1] == "off": + self.handle_data(self.md.htmlStash.store(text)) + else: + self.handle_data(text) + if tag in self.CDATA_CONTENT_ELEMENTS: + # This is presumably a standalone tag in a code span (see #1036). + self.clear_cdata_mode() + + def handle_endtag(self, tag): + if tag in self.block_level_tags: + if self.inraw: + super().handle_endtag(tag) + elif tag in self.mdstack: + # Close element and any unclosed children + while self.mdstack: + item = self.mdstack.pop() + self.mdstate.pop() + self.treebuilder.end(item) + if item == tag: + break + if not self.mdstack: + # Last item in stack is closed. Stash it + element = self.get_element() + # Get last entry to see if it ends in newlines + # If it is an element, assume there is no newlines + item = self.cleandoc[-1] if self.cleandoc else '' + # If we only have one newline before block element, add another + if not item.endswith('\n\n') and item.endswith('\n'): + self.cleandoc.append('\n') + self.cleandoc.append(self.md.htmlStash.store(element)) + self.cleandoc.append('\n\n') + self.state = [] + # Check if element has a tail + if not blank_line_re.match( + self.rawdata[self.line_offset + self.offset + len(self.get_endtag_text(tag)):]): + # More content exists after endtag. + self.intail = True + else: + # Treat orphan closing tag as a span level tag. + text = self.get_endtag_text(tag) + if self.mdstate and self.mdstate[-1] == "off": + self.handle_data(self.md.htmlStash.store(text)) + else: + self.handle_data(text) + else: + # Span level tag + if self.inraw: + super().handle_endtag(tag) + else: + text = self.get_endtag_text(tag) + if self.mdstate and self.mdstate[-1] == "off": + self.handle_data(self.md.htmlStash.store(text)) + else: + self.handle_data(text) + + def handle_startendtag(self, tag, attrs): + if tag in self.empty_tags: + attrs = {key: value if value is not None else key for key, value in attrs} + if "markdown" in attrs: + attrs.pop('markdown') + element = etree.Element(tag, attrs) + data = etree.tostring(element, encoding='unicode', method='html') + else: + data = self.get_starttag_text() + else: + data = self.get_starttag_text() + self.handle_empty_tag(data, is_block=self.md.is_block_level(tag)) + + def handle_data(self, data): + if self.intail and '\n' in data: + self.intail = False + if self.inraw or not self.mdstack: + super().handle_data(data) + else: + self.treebuilder.data(data) + + def handle_empty_tag(self, data, is_block): + if self.inraw or not self.mdstack: + super().handle_empty_tag(data, is_block) + else: + if self.at_line_start() and is_block: + self.handle_data('\n' + self.md.htmlStash.store(data) + '\n\n') + else: + self.handle_data(self.md.htmlStash.store(data)) + + def parse_pi(self, i): + if self.at_line_start() or self.intail or self.mdstack: + # The same override exists in HTMLExtractor without the check + # for mdstack. Therefore, use HTMLExtractor's parent instead. + return super(HTMLExtractor, self).parse_pi(i) + # This is not the beginning of a raw block so treat as plain data + # and avoid consuming any tags which may follow (see #1066). + self.handle_data('<?') + return i + 2 + + def parse_html_declaration(self, i): + if self.at_line_start() or self.intail or self.mdstack: + # The same override exists in HTMLExtractor without the check + # for mdstack. Therefore, use HTMLExtractor's parent instead. + return super(HTMLExtractor, self).parse_html_declaration(i) + # This is not the beginning of a raw block so treat as plain data + # and avoid consuming any tags which may follow (see #1066). + self.handle_data('<!') + return i + 2 + + +class HtmlBlockPreprocessor(Preprocessor): + """Remove html blocks from the text and store them for later retrieval.""" + + def run(self, lines): + source = '\n'.join(lines) + parser = HTMLExtractorExtra(self.md) + parser.feed(source) + parser.close() + return ''.join(parser.cleandoc).split('\n') + + +class MarkdownInHtmlProcessor(BlockProcessor): + """Process Markdown Inside HTML Blocks which have been stored in the HtmlStash.""" + + def test(self, parent, block): + # ALways return True. `run` will return `False` it not a valid match. + return True + + def parse_element_content(self, element): + """ + Resursively parse the text content of an etree Element as Markdown. + + Any block level elements generated from the Markdown will be inserted as children of the element in place + of the text content. All `markdown` attributes are removed. For any elements in which Markdown parsing has + been dissabled, the text content of it and its chidlren are wrapped in an `AtomicString`. + """ + + md_attr = element.attrib.pop('markdown', 'off') + + if md_attr == 'block': + # Parse content as block level + # The order in which the different parts are parsed (text, children, tails) is important here as the + # order of elements needs to be preserved. We can't be inserting items at a later point in the current + # iteration as we don't want to do raw processing on elements created from parsing Markdown text (for + # example). Therefore, the order of operations is children, tails, text. + + # Recursively parse existing children from raw HTML + for child in list(element): + self.parse_element_content(child) + + # Parse Markdown text in tail of children. Do this seperate to avoid raw HTML parsing. + # Save the position of each item to be inserted later in reverse. + tails = [] + for pos, child in enumerate(element): + if child.tail: + block = child.tail.rstrip('\n') + child.tail = '' + # Use a dummy placeholder element. + dummy = etree.Element('div') + self.parser.parseBlocks(dummy, block.split('\n\n')) + children = list(dummy) + children.reverse() + tails.append((pos + 1, children)) + + # Insert the elements created from the tails in reverse. + tails.reverse() + for pos, tail in tails: + for item in tail: + element.insert(pos, item) + + # Parse Markdown text content. Do this last to avoid raw HTML parsing. + if element.text: + block = element.text.rstrip('\n') + element.text = '' + # Use a dummy placeholder element as the content needs to get inserted before existing children. + dummy = etree.Element('div') + self.parser.parseBlocks(dummy, block.split('\n\n')) + children = list(dummy) + children.reverse() + for child in children: + element.insert(0, child) + + elif md_attr == 'span': + # Span level parsing will be handled by inlineprocessors. + # Walk children here to remove any `markdown` attributes. + for child in list(element): + self.parse_element_content(child) + + else: + # Disable inline parsing for everything else + if element.text is None: + element.text = '' + element.text = util.AtomicString(element.text) + for child in list(element): + self.parse_element_content(child) + if child.tail: + child.tail = util.AtomicString(child.tail) + + def run(self, parent, blocks): + m = util.HTML_PLACEHOLDER_RE.match(blocks[0]) + if m: + index = int(m.group(1)) + element = self.parser.md.htmlStash.rawHtmlBlocks[index] + if isinstance(element, etree.Element): + # We have a matched element. Process it. + blocks.pop(0) + self.parse_element_content(element) + parent.append(element) + # Cleanup stash. Replace element with empty string to avoid confusing postprocessor. + self.parser.md.htmlStash.rawHtmlBlocks.pop(index) + self.parser.md.htmlStash.rawHtmlBlocks.insert(index, '') + # Comfirm the match to the blockparser. + return True + # No match found. + return False + + +class MarkdownInHTMLPostprocessor(RawHtmlPostprocessor): + def stash_to_string(self, text): + """ Override default to handle any etree elements still in the stash. """ + if isinstance(text, etree.Element): + return self.md.serializer(text) + else: + return str(text) + + +class MarkdownInHtmlExtension(Extension): + """Add Markdown parsing in HTML to Markdown class.""" + + def extendMarkdown(self, md): + """ Register extension instances. """ + + # Replace raw HTML preprocessor + md.preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20) + # Add blockprocessor which handles the placeholders for etree elements + md.parser.blockprocessors.register( + MarkdownInHtmlProcessor(md.parser), 'markdown_block', 105 + ) + # Replace raw HTML postprocessor + md.postprocessors.register(MarkdownInHTMLPostprocessor(md), 'raw_html', 30) + + +def makeExtension(**kwargs): # pragma: no cover + return MarkdownInHtmlExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/meta.py b/venv/Lib/site-packages/markdown/extensions/meta.py new file mode 100644 index 0000000..10dee11 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/meta.py @@ -0,0 +1,79 @@ +""" +Meta Data Extension for Python-Markdown +======================================= + +This extension adds Meta Data handling to markdown. + +See <https://Python-Markdown.github.io/extensions/meta_data> +for documentation. + +Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com). + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..preprocessors import Preprocessor +import re +import logging + +log = logging.getLogger('MARKDOWN') + +# Global Vars +META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)') +META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)') +BEGIN_RE = re.compile(r'^-{3}(\s.*)?') +END_RE = re.compile(r'^(-{3}|\.{3})(\s.*)?') + + +class MetaExtension (Extension): + """ Meta-Data extension for Python-Markdown. """ + + def extendMarkdown(self, md): + """ Add MetaPreprocessor to Markdown instance. """ + md.registerExtension(self) + self.md = md + md.preprocessors.register(MetaPreprocessor(md), 'meta', 27) + + def reset(self): + self.md.Meta = {} + + +class MetaPreprocessor(Preprocessor): + """ Get Meta-Data. """ + + def run(self, lines): + """ Parse Meta-Data and store in Markdown.Meta. """ + meta = {} + key = None + if lines and BEGIN_RE.match(lines[0]): + lines.pop(0) + while lines: + line = lines.pop(0) + m1 = META_RE.match(line) + if line.strip() == '' or END_RE.match(line): + break # blank line or end of YAML header - done + if m1: + key = m1.group('key').lower().strip() + value = m1.group('value').strip() + try: + meta[key].append(value) + except KeyError: + meta[key] = [value] + else: + m2 = META_MORE_RE.match(line) + if m2 and key: + # Add another line to existing key + meta[key].append(m2.group('value').strip()) + else: + lines.insert(0, line) + break # no meta data - done + self.md.Meta = meta + return lines + + +def makeExtension(**kwargs): # pragma: no cover + return MetaExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/nl2br.py b/venv/Lib/site-packages/markdown/extensions/nl2br.py new file mode 100644 index 0000000..6c7491b --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/nl2br.py @@ -0,0 +1,33 @@ +""" +NL2BR Extension +=============== + +A Python-Markdown extension to treat newlines as hard breaks; like +GitHub-flavored Markdown does. + +See <https://Python-Markdown.github.io/extensions/nl2br> +for documentation. + +Oringinal code Copyright 2011 [Brian Neal](https://deathofagremmie.com/) + +All changes Copyright 2011-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..inlinepatterns import SubstituteTagInlineProcessor + +BR_RE = r'\n' + + +class Nl2BrExtension(Extension): + + def extendMarkdown(self, md): + br_tag = SubstituteTagInlineProcessor(BR_RE, 'br') + md.inlinePatterns.register(br_tag, 'nl', 5) + + +def makeExtension(**kwargs): # pragma: no cover + return Nl2BrExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/sane_lists.py b/venv/Lib/site-packages/markdown/extensions/sane_lists.py new file mode 100644 index 0000000..e27eb18 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/sane_lists.py @@ -0,0 +1,54 @@ +""" +Sane List Extension for Python-Markdown +======================================= + +Modify the behavior of Lists in Python-Markdown to act in a sane manor. + +See <https://Python-Markdown.github.io/extensions/sane_lists> +for documentation. + +Original code Copyright 2011 [Waylan Limberg](http://achinghead.com) + +All changes Copyright 2011-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import OListProcessor, UListProcessor +import re + + +class SaneOListProcessor(OListProcessor): + + SIBLING_TAGS = ['ol'] + LAZY_OL = False + + def __init__(self, parser): + super().__init__(parser) + self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.))[ ]+(.*)' % + (self.tab_length - 1)) + + +class SaneUListProcessor(UListProcessor): + + SIBLING_TAGS = ['ul'] + + def __init__(self, parser): + super().__init__(parser) + self.CHILD_RE = re.compile(r'^[ ]{0,%d}(([*+-]))[ ]+(.*)' % + (self.tab_length - 1)) + + +class SaneListExtension(Extension): + """ Add sane lists to Markdown. """ + + def extendMarkdown(self, md): + """ Override existing Processors. """ + md.parser.blockprocessors.register(SaneOListProcessor(md.parser), 'olist', 40) + md.parser.blockprocessors.register(SaneUListProcessor(md.parser), 'ulist', 30) + + +def makeExtension(**kwargs): # pragma: no cover + return SaneListExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/smarty.py b/venv/Lib/site-packages/markdown/extensions/smarty.py new file mode 100644 index 0000000..894805f --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/smarty.py @@ -0,0 +1,263 @@ +''' +Smarty extension for Python-Markdown +==================================== + +Adds conversion of ASCII dashes, quotes and ellipses to their HTML +entity equivalents. + +See <https://Python-Markdown.github.io/extensions/smarty> +for documentation. + +Author: 2013, Dmitry Shachnev <mitya57@gmail.com> + +All changes Copyright 2013-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +SmartyPants license: + + Copyright (c) 2003 John Gruber <https://daringfireball.net/> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name "SmartyPants" nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + This software is provided by the copyright holders and contributors "as + is" and any express or implied warranties, including, but not limited + to, the implied warranties of merchantability and fitness for a + particular purpose are disclaimed. In no event shall the copyright + owner or contributors be liable for any direct, indirect, incidental, + special, exemplary, or consequential damages (including, but not + limited to, procurement of substitute goods or services; loss of use, + data, or profits; or business interruption) however caused and on any + theory of liability, whether in contract, strict liability, or tort + (including negligence or otherwise) arising in any way out of the use + of this software, even if advised of the possibility of such damage. + + +smartypants.py license: + + smartypants.py is a derivative work of SmartyPants. + Copyright (c) 2004, 2007 Chad Miller <http://web.chad.org/> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + This software is provided by the copyright holders and contributors "as + is" and any express or implied warranties, including, but not limited + to, the implied warranties of merchantability and fitness for a + particular purpose are disclaimed. In no event shall the copyright + owner or contributors be liable for any direct, indirect, incidental, + special, exemplary, or consequential damages (including, but not + limited to, procurement of substitute goods or services; loss of use, + data, or profits; or business interruption) however caused and on any + theory of liability, whether in contract, strict liability, or tort + (including negligence or otherwise) arising in any way out of the use + of this software, even if advised of the possibility of such damage. + +''' + + +from . import Extension +from ..inlinepatterns import HtmlInlineProcessor, HTML_RE +from ..treeprocessors import InlineProcessor +from ..util import Registry, deprecated + + +# Constants for quote education. +punctClass = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" +endOfWordClass = r"[\s.,;:!?)]" +closeClass = r"[^\ \t\r\n\[\{\(\-\u0002\u0003]" + +openingQuotesBase = ( + r'(\s' # a whitespace char + r'| ' # or a non-breaking space entity + r'|--' # or dashes + r'|–|—' # or unicode + r'|&[mn]dash;' # or named dash entities + r'|–|—' # or decimal entities + r')' +) + +substitutions = { + 'mdash': '—', + 'ndash': '–', + 'ellipsis': '…', + 'left-angle-quote': '«', + 'right-angle-quote': '»', + 'left-single-quote': '‘', + 'right-single-quote': '’', + 'left-double-quote': '“', + 'right-double-quote': '”', +} + + +# Special case if the very first character is a quote +# followed by punctuation at a non-word-break. Close the quotes by brute force: +singleQuoteStartRe = r"^'(?=%s\B)" % punctClass +doubleQuoteStartRe = r'^"(?=%s\B)' % punctClass + +# Special case for double sets of quotes, e.g.: +# <p>He said, "'Quoted' words in a larger quote."</p> +doubleQuoteSetsRe = r""""'(?=\w)""" +singleQuoteSetsRe = r"""'"(?=\w)""" + +# Special case for decade abbreviations (the '80s): +decadeAbbrRe = r"(?<!\w)'(?=\d{2}s)" + +# Get most opening double quotes: +openingDoubleQuotesRegex = r'%s"(?=\w)' % openingQuotesBase + +# Double closing quotes: +closingDoubleQuotesRegex = r'"(?=\s)' +closingDoubleQuotesRegex2 = '(?<=%s)"' % closeClass + +# Get most opening single quotes: +openingSingleQuotesRegex = r"%s'(?=\w)" % openingQuotesBase + +# Single closing quotes: +closingSingleQuotesRegex = r"(?<=%s)'(?!\s|s\b|\d)" % closeClass +closingSingleQuotesRegex2 = r"(?<=%s)'(\s|s\b)" % closeClass + +# All remaining quotes should be opening ones +remainingSingleQuotesRegex = r"'" +remainingDoubleQuotesRegex = r'"' + +HTML_STRICT_RE = HTML_RE + r'(?!\>)' + + +class SubstituteTextPattern(HtmlInlineProcessor): + def __init__(self, pattern, replace, md): + """ Replaces matches with some text. """ + HtmlInlineProcessor.__init__(self, pattern) + self.replace = replace + self.md = md + + @property + @deprecated("Use 'md' instead.") + def markdown(self): + # TODO: remove this later + return self.md + + def handleMatch(self, m, data): + result = '' + for part in self.replace: + if isinstance(part, int): + result += m.group(part) + else: + result += self.md.htmlStash.store(part) + return result, m.start(0), m.end(0) + + +class SmartyExtension(Extension): + def __init__(self, **kwargs): + self.config = { + 'smart_quotes': [True, 'Educate quotes'], + 'smart_angled_quotes': [False, 'Educate angled quotes'], + 'smart_dashes': [True, 'Educate dashes'], + 'smart_ellipses': [True, 'Educate ellipses'], + 'substitutions': [{}, 'Overwrite default substitutions'], + } + super().__init__(**kwargs) + self.substitutions = dict(substitutions) + self.substitutions.update(self.getConfig('substitutions', default={})) + + def _addPatterns(self, md, patterns, serie, priority): + for ind, pattern in enumerate(patterns): + pattern += (md,) + pattern = SubstituteTextPattern(*pattern) + name = 'smarty-%s-%d' % (serie, ind) + self.inlinePatterns.register(pattern, name, priority-ind) + + def educateDashes(self, md): + emDashesPattern = SubstituteTextPattern( + r'(?<!-)---(?!-)', (self.substitutions['mdash'],), md + ) + enDashesPattern = SubstituteTextPattern( + r'(?<!-)--(?!-)', (self.substitutions['ndash'],), md + ) + self.inlinePatterns.register(emDashesPattern, 'smarty-em-dashes', 50) + self.inlinePatterns.register(enDashesPattern, 'smarty-en-dashes', 45) + + def educateEllipses(self, md): + ellipsesPattern = SubstituteTextPattern( + r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md + ) + self.inlinePatterns.register(ellipsesPattern, 'smarty-ellipses', 10) + + def educateAngledQuotes(self, md): + leftAngledQuotePattern = SubstituteTextPattern( + r'\<\<', (self.substitutions['left-angle-quote'],), md + ) + rightAngledQuotePattern = SubstituteTextPattern( + r'\>\>', (self.substitutions['right-angle-quote'],), md + ) + self.inlinePatterns.register(leftAngledQuotePattern, 'smarty-left-angle-quotes', 40) + self.inlinePatterns.register(rightAngledQuotePattern, 'smarty-right-angle-quotes', 35) + + def educateQuotes(self, md): + lsquo = self.substitutions['left-single-quote'] + rsquo = self.substitutions['right-single-quote'] + ldquo = self.substitutions['left-double-quote'] + rdquo = self.substitutions['right-double-quote'] + patterns = ( + (singleQuoteStartRe, (rsquo,)), + (doubleQuoteStartRe, (rdquo,)), + (doubleQuoteSetsRe, (ldquo + lsquo,)), + (singleQuoteSetsRe, (lsquo + ldquo,)), + (decadeAbbrRe, (rsquo,)), + (openingSingleQuotesRegex, (1, lsquo)), + (closingSingleQuotesRegex, (rsquo,)), + (closingSingleQuotesRegex2, (rsquo, 1)), + (remainingSingleQuotesRegex, (lsquo,)), + (openingDoubleQuotesRegex, (1, ldquo)), + (closingDoubleQuotesRegex, (rdquo,)), + (closingDoubleQuotesRegex2, (rdquo,)), + (remainingDoubleQuotesRegex, (ldquo,)) + ) + self._addPatterns(md, patterns, 'quotes', 30) + + def extendMarkdown(self, md): + configs = self.getConfigs() + self.inlinePatterns = Registry() + if configs['smart_ellipses']: + self.educateEllipses(md) + if configs['smart_quotes']: + self.educateQuotes(md) + if configs['smart_angled_quotes']: + self.educateAngledQuotes(md) + # Override HTML_RE from inlinepatterns.py so that it does not + # process tags with duplicate closing quotes. + md.inlinePatterns.register(HtmlInlineProcessor(HTML_STRICT_RE, md), 'html', 90) + if configs['smart_dashes']: + self.educateDashes(md) + inlineProcessor = InlineProcessor(md) + inlineProcessor.inlinePatterns = self.inlinePatterns + md.treeprocessors.register(inlineProcessor, 'smarty', 2) + md.ESCAPED_CHARS.extend(['"', "'"]) + + +def makeExtension(**kwargs): # pragma: no cover + return SmartyExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/tables.py b/venv/Lib/site-packages/markdown/extensions/tables.py new file mode 100644 index 0000000..4b027bb --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/tables.py @@ -0,0 +1,223 @@ +""" +Tables Extension for Python-Markdown +==================================== + +Added parsing of tables to Python-Markdown. + +See <https://Python-Markdown.github.io/extensions/tables> +for documentation. + +Original code Copyright 2009 [Waylan Limberg](http://achinghead.com) + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..blockprocessors import BlockProcessor +import xml.etree.ElementTree as etree +import re +PIPE_NONE = 0 +PIPE_LEFT = 1 +PIPE_RIGHT = 2 + + +class TableProcessor(BlockProcessor): + """ Process Tables. """ + + RE_CODE_PIPES = re.compile(r'(?:(\\\\)|(\\`+)|(`+)|(\\\|)|(\|))') + RE_END_BORDER = re.compile(r'(?<!\\)(?:\\\\)*\|$') + + def __init__(self, parser): + self.border = False + self.separator = '' + super().__init__(parser) + + def test(self, parent, block): + """ + Ensure first two rows (column header and separator row) are valid table rows. + + Keep border check and separator row do avoid repeating the work. + """ + is_table = False + rows = [row.strip(' ') for row in block.split('\n')] + if len(rows) > 1: + header0 = rows[0] + self.border = PIPE_NONE + if header0.startswith('|'): + self.border |= PIPE_LEFT + if self.RE_END_BORDER.search(header0) is not None: + self.border |= PIPE_RIGHT + row = self._split_row(header0) + row0_len = len(row) + is_table = row0_len > 1 + + # Each row in a single column table needs at least one pipe. + if not is_table and row0_len == 1 and self.border: + for index in range(1, len(rows)): + is_table = rows[index].startswith('|') + if not is_table: + is_table = self.RE_END_BORDER.search(rows[index]) is not None + if not is_table: + break + + if is_table: + row = self._split_row(rows[1]) + is_table = (len(row) == row0_len) and set(''.join(row)) <= set('|:- ') + if is_table: + self.separator = row + + return is_table + + def run(self, parent, blocks): + """ Parse a table block and build table. """ + block = blocks.pop(0).split('\n') + header = block[0].strip(' ') + rows = [] if len(block) < 3 else block[2:] + + # Get alignment of columns + align = [] + for c in self.separator: + c = c.strip(' ') + if c.startswith(':') and c.endswith(':'): + align.append('center') + elif c.startswith(':'): + align.append('left') + elif c.endswith(':'): + align.append('right') + else: + align.append(None) + + # Build table + table = etree.SubElement(parent, 'table') + thead = etree.SubElement(table, 'thead') + self._build_row(header, thead, align) + tbody = etree.SubElement(table, 'tbody') + if len(rows) == 0: + # Handle empty table + self._build_empty_row(tbody, align) + else: + for row in rows: + self._build_row(row.strip(' '), tbody, align) + + def _build_empty_row(self, parent, align): + """Build an empty row.""" + tr = etree.SubElement(parent, 'tr') + count = len(align) + while count: + etree.SubElement(tr, 'td') + count -= 1 + + def _build_row(self, row, parent, align): + """ Given a row of text, build table cells. """ + tr = etree.SubElement(parent, 'tr') + tag = 'td' + if parent.tag == 'thead': + tag = 'th' + cells = self._split_row(row) + # We use align here rather than cells to ensure every row + # contains the same number of columns. + for i, a in enumerate(align): + c = etree.SubElement(tr, tag) + try: + c.text = cells[i].strip(' ') + except IndexError: # pragma: no cover + c.text = "" + if a: + c.set('align', a) + + def _split_row(self, row): + """ split a row of text into list of cells. """ + if self.border: + if row.startswith('|'): + row = row[1:] + row = self.RE_END_BORDER.sub('', row) + return self._split(row) + + def _split(self, row): + """ split a row of text with some code into a list of cells. """ + elements = [] + pipes = [] + tics = [] + tic_points = [] + tic_region = [] + good_pipes = [] + + # Parse row + # Throw out \\, and \| + for m in self.RE_CODE_PIPES.finditer(row): + # Store ` data (len, start_pos, end_pos) + if m.group(2): + # \`+ + # Store length of each tic group: subtract \ + tics.append(len(m.group(2)) - 1) + # Store start of group, end of group, and escape length + tic_points.append((m.start(2), m.end(2) - 1, 1)) + elif m.group(3): + # `+ + # Store length of each tic group + tics.append(len(m.group(3))) + # Store start of group, end of group, and escape length + tic_points.append((m.start(3), m.end(3) - 1, 0)) + # Store pipe location + elif m.group(5): + pipes.append(m.start(5)) + + # Pair up tics according to size if possible + # Subtract the escape length *only* from the opening. + # Walk through tic list and see if tic has a close. + # Store the tic region (start of region, end of region). + pos = 0 + tic_len = len(tics) + while pos < tic_len: + try: + tic_size = tics[pos] - tic_points[pos][2] + if tic_size == 0: + raise ValueError + index = tics[pos + 1:].index(tic_size) + 1 + tic_region.append((tic_points[pos][0], tic_points[pos + index][1])) + pos += index + 1 + except ValueError: + pos += 1 + + # Resolve pipes. Check if they are within a tic pair region. + # Walk through pipes comparing them to each region. + # - If pipe position is less that a region, it isn't in a region + # - If it is within a region, we don't want it, so throw it out + # - If we didn't throw it out, it must be a table pipe + for pipe in pipes: + throw_out = False + for region in tic_region: + if pipe < region[0]: + # Pipe is not in a region + break + elif region[0] <= pipe <= region[1]: + # Pipe is within a code region. Throw it out. + throw_out = True + break + if not throw_out: + good_pipes.append(pipe) + + # Split row according to table delimeters. + pos = 0 + for pipe in good_pipes: + elements.append(row[pos:pipe]) + pos = pipe + 1 + elements.append(row[pos:]) + return elements + + +class TableExtension(Extension): + """ Add tables to Markdown. """ + + def extendMarkdown(self, md): + """ Add an instance of TableProcessor to BlockParser. """ + if '|' not in md.ESCAPED_CHARS: + md.ESCAPED_CHARS.append('|') + md.parser.blockprocessors.register(TableProcessor(md.parser), 'table', 75) + + +def makeExtension(**kwargs): # pragma: no cover + return TableExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/toc.py b/venv/Lib/site-packages/markdown/extensions/toc.py new file mode 100644 index 0000000..e4dc378 --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/toc.py @@ -0,0 +1,380 @@ +""" +Table of Contents Extension for Python-Markdown +=============================================== + +See <https://Python-Markdown.github.io/extensions/toc> +for documentation. + +Oringinal code Copyright 2008 [Jack Miller](https://codezen.org/) + +All changes Copyright 2008-2014 The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +""" + +from . import Extension +from ..treeprocessors import Treeprocessor +from ..util import code_escape, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE, AtomicString +from ..postprocessors import UnescapePostprocessor +import re +import html +import unicodedata +import xml.etree.ElementTree as etree + + +def slugify(value, separator, unicode=False): + """ Slugify a string, to make it URL friendly. """ + if not unicode: + # Replace Extended Latin characters with ASCII, i.e. žlutý → zluty + value = unicodedata.normalize('NFKD', value) + value = value.encode('ascii', 'ignore').decode('ascii') + value = re.sub(r'[^\w\s-]', '', value).strip().lower() + return re.sub(r'[{}\s]+'.format(separator), separator, value) + + +def slugify_unicode(value, separator): + """ Slugify a string, to make it URL friendly while preserving Unicode characters. """ + return slugify(value, separator, unicode=True) + + +IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$') + + +def unique(id, ids): + """ Ensure id is unique in set of ids. Append '_1', '_2'... if not """ + while id in ids or not id: + m = IDCOUNT_RE.match(id) + if m: + id = '%s_%d' % (m.group(1), int(m.group(2))+1) + else: + id = '%s_%d' % (id, 1) + ids.add(id) + return id + + +def get_name(el): + """Get title name.""" + + text = [] + for c in el.itertext(): + if isinstance(c, AtomicString): + text.append(html.unescape(c)) + else: + text.append(c) + return ''.join(text).strip() + + +def stashedHTML2text(text, md, strip_entities=True): + """ Extract raw HTML from stash, reduce to plain text and swap with placeholder. """ + def _html_sub(m): + """ Substitute raw html with plain text. """ + try: + raw = md.htmlStash.rawHtmlBlocks[int(m.group(1))] + except (IndexError, TypeError): # pragma: no cover + return m.group(0) + # Strip out tags and/or entities - leaving text + res = re.sub(r'(<[^>]+>)', '', raw) + if strip_entities: + res = re.sub(r'(&[\#a-zA-Z0-9]+;)', '', res) + return res + + return HTML_PLACEHOLDER_RE.sub(_html_sub, text) + + +def unescape(text): + """ Unescape escaped text. """ + c = UnescapePostprocessor() + return c.run(text) + + +def nest_toc_tokens(toc_list): + """Given an unsorted list with errors and skips, return a nested one. + [{'level': 1}, {'level': 2}] + => + [{'level': 1, 'children': [{'level': 2, 'children': []}]}] + + A wrong list is also converted: + [{'level': 2}, {'level': 1}] + => + [{'level': 2, 'children': []}, {'level': 1, 'children': []}] + """ + + ordered_list = [] + if len(toc_list): + # Initialize everything by processing the first entry + last = toc_list.pop(0) + last['children'] = [] + levels = [last['level']] + ordered_list.append(last) + parents = [] + + # Walk the rest nesting the entries properly + while toc_list: + t = toc_list.pop(0) + current_level = t['level'] + t['children'] = [] + + # Reduce depth if current level < last item's level + if current_level < levels[-1]: + # Pop last level since we know we are less than it + levels.pop() + + # Pop parents and levels we are less than or equal to + to_pop = 0 + for p in reversed(parents): + if current_level <= p['level']: + to_pop += 1 + else: # pragma: no cover + break + if to_pop: + levels = levels[:-to_pop] + parents = parents[:-to_pop] + + # Note current level as last + levels.append(current_level) + + # Level is the same, so append to + # the current parent (if available) + if current_level == levels[-1]: + (parents[-1]['children'] if parents + else ordered_list).append(t) + + # Current level is > last item's level, + # So make last item a parent and append current as child + else: + last['children'].append(t) + parents.append(last) + levels.append(current_level) + last = t + + return ordered_list + + +class TocTreeprocessor(Treeprocessor): + def __init__(self, md, config): + super().__init__(md) + + self.marker = config["marker"] + self.title = config["title"] + self.base_level = int(config["baselevel"]) - 1 + self.slugify = config["slugify"] + self.sep = config["separator"] + self.use_anchors = parseBoolValue(config["anchorlink"]) + self.anchorlink_class = config["anchorlink_class"] + self.use_permalinks = parseBoolValue(config["permalink"], False) + if self.use_permalinks is None: + self.use_permalinks = config["permalink"] + self.permalink_class = config["permalink_class"] + self.permalink_title = config["permalink_title"] + self.header_rgx = re.compile("[Hh][123456]") + if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: + self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] + else: + self.toc_top = 1 + self.toc_bottom = int(config["toc_depth"]) + + def iterparent(self, node): + ''' Iterator wrapper to get allowed parent and child all at once. ''' + + # We do not allow the marker inside a header as that + # would causes an enless loop of placing a new TOC + # inside previously generated TOC. + for child in node: + if not self.header_rgx.match(child.tag) and child.tag not in ['pre', 'code']: + yield node, child + yield from self.iterparent(child) + + def replace_marker(self, root, elem): + ''' Replace marker with elem. ''' + for (p, c) in self.iterparent(root): + text = ''.join(c.itertext()).strip() + if not text: + continue + + # To keep the output from screwing up the + # validation by putting a <div> inside of a <p> + # we actually replace the <p> in its entirety. + + # The <p> element may contain more than a single text content + # (nl2br can introduce a <br>). In this situation, c.text returns + # the very first content, ignore children contents or tail content. + # len(c) == 0 is here to ensure there is only text in the <p>. + if c.text and c.text.strip() == self.marker and len(c) == 0: + for i in range(len(p)): + if p[i] == c: + p[i] = elem + break + + def set_level(self, elem): + ''' Adjust header level according to base level. ''' + level = int(elem.tag[-1]) + self.base_level + if level > 6: + level = 6 + elem.tag = 'h%d' % level + + def add_anchor(self, c, elem_id): # @ReservedAssignment + anchor = etree.Element("a") + anchor.text = c.text + anchor.attrib["href"] = "#" + elem_id + anchor.attrib["class"] = self.anchorlink_class + c.text = "" + for elem in c: + anchor.append(elem) + while len(c): + c.remove(c[0]) + c.append(anchor) + + def add_permalink(self, c, elem_id): + permalink = etree.Element("a") + permalink.text = ("%spara;" % AMP_SUBSTITUTE + if self.use_permalinks is True + else self.use_permalinks) + permalink.attrib["href"] = "#" + elem_id + permalink.attrib["class"] = self.permalink_class + if self.permalink_title: + permalink.attrib["title"] = self.permalink_title + c.append(permalink) + + def build_toc_div(self, toc_list): + """ Return a string div given a toc list. """ + div = etree.Element("div") + div.attrib["class"] = "toc" + + # Add title to the div + if self.title: + header = etree.SubElement(div, "span") + header.attrib["class"] = "toctitle" + header.text = self.title + + def build_etree_ul(toc_list, parent): + ul = etree.SubElement(parent, "ul") + for item in toc_list: + # List item link, to be inserted into the toc div + li = etree.SubElement(ul, "li") + link = etree.SubElement(li, "a") + link.text = item.get('name', '') + link.attrib["href"] = '#' + item.get('id', '') + if item['children']: + build_etree_ul(item['children'], li) + return ul + + build_etree_ul(toc_list, div) + + if 'prettify' in self.md.treeprocessors: + self.md.treeprocessors['prettify'].run(div) + + return div + + def run(self, doc): + # Get a list of id attributes + used_ids = set() + for el in doc.iter(): + if "id" in el.attrib: + used_ids.add(el.attrib["id"]) + + toc_tokens = [] + for el in doc.iter(): + if isinstance(el.tag, str) and self.header_rgx.match(el.tag): + self.set_level(el) + text = get_name(el) + + # Do not override pre-existing ids + if "id" not in el.attrib: + innertext = unescape(stashedHTML2text(text, self.md)) + el.attrib["id"] = unique(self.slugify(innertext, self.sep), used_ids) + + if int(el.tag[-1]) >= self.toc_top and int(el.tag[-1]) <= self.toc_bottom: + toc_tokens.append({ + 'level': int(el.tag[-1]), + 'id': el.attrib["id"], + 'name': unescape(stashedHTML2text( + code_escape(el.attrib.get('data-toc-label', text)), + self.md, strip_entities=False + )) + }) + + # Remove the data-toc-label attribute as it is no longer needed + if 'data-toc-label' in el.attrib: + del el.attrib['data-toc-label'] + + if self.use_anchors: + self.add_anchor(el, el.attrib["id"]) + if self.use_permalinks not in [False, None]: + self.add_permalink(el, el.attrib["id"]) + + toc_tokens = nest_toc_tokens(toc_tokens) + div = self.build_toc_div(toc_tokens) + if self.marker: + self.replace_marker(doc, div) + + # serialize and attach to markdown instance. + toc = self.md.serializer(div) + for pp in self.md.postprocessors: + toc = pp.run(toc) + self.md.toc_tokens = toc_tokens + self.md.toc = toc + + +class TocExtension(Extension): + + TreeProcessorClass = TocTreeprocessor + + def __init__(self, **kwargs): + self.config = { + "marker": ['[TOC]', + 'Text to find and replace with Table of Contents - ' + 'Set to an empty string to disable. Defaults to "[TOC]"'], + "title": ["", + "Title to insert into TOC <div> - " + "Defaults to an empty string"], + "anchorlink": [False, + "True if header should be a self link - " + "Defaults to False"], + "anchorlink_class": ['toclink', + 'CSS class(es) used for the link. ' + 'Defaults to "toclink"'], + "permalink": [0, + "True or link text if a Sphinx-style permalink should " + "be added - Defaults to False"], + "permalink_class": ['headerlink', + 'CSS class(es) used for the link. ' + 'Defaults to "headerlink"'], + "permalink_title": ["Permanent link", + "Title attribute of the permalink - " + "Defaults to 'Permanent link'"], + "baselevel": ['1', 'Base level for headers.'], + "slugify": [slugify, + "Function to generate anchors based on header text - " + "Defaults to the headerid ext's slugify function."], + 'separator': ['-', 'Word separator. Defaults to "-".'], + "toc_depth": [6, + 'Define the range of section levels to include in' + 'the Table of Contents. A single integer (b) defines' + 'the bottom section level (<h1>..<hb>) only.' + 'A string consisting of two digits separated by a hyphen' + 'in between ("2-5"), define the top (t) and the' + 'bottom (b) (<ht>..<hb>). Defaults to `6` (bottom).'], + } + + super().__init__(**kwargs) + + def extendMarkdown(self, md): + md.registerExtension(self) + self.md = md + self.reset() + tocext = self.TreeProcessorClass(md, self.getConfigs()) + # Headerid ext is set to '>prettify'. With this set to '_end', + # it should always come after headerid ext (and honor ids assinged + # by the header id extension) if both are used. Same goes for + # attr_list extension. This must come last because we don't want + # to redefine ids after toc is created. But we do want toc prettified. + md.treeprocessors.register(tocext, 'toc', 5) + + def reset(self): + self.md.toc = '' + self.md.toc_tokens = [] + + +def makeExtension(**kwargs): # pragma: no cover + return TocExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/extensions/wikilinks.py b/venv/Lib/site-packages/markdown/extensions/wikilinks.py new file mode 100644 index 0000000..cddee7a --- /dev/null +++ b/venv/Lib/site-packages/markdown/extensions/wikilinks.py @@ -0,0 +1,87 @@ +''' +WikiLinks Extension for Python-Markdown +====================================== + +Converts [[WikiLinks]] to relative links. + +See <https://Python-Markdown.github.io/extensions/wikilinks> +for documentation. + +Original code Copyright [Waylan Limberg](http://achinghead.com/). + +All changes Copyright The Python Markdown Project + +License: [BSD](https://opensource.org/licenses/bsd-license.php) + +''' + +from . import Extension +from ..inlinepatterns import InlineProcessor +import xml.etree.ElementTree as etree +import re + + +def build_url(label, base, end): + """ Build a url from the label, a base, and an end. """ + clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label) + return '{}{}{}'.format(base, clean_label, end) + + +class WikiLinkExtension(Extension): + + def __init__(self, **kwargs): + self.config = { + 'base_url': ['/', 'String to append to beginning or URL.'], + 'end_url': ['/', 'String to append to end of URL.'], + 'html_class': ['wikilink', 'CSS hook. Leave blank for none.'], + 'build_url': [build_url, 'Callable formats URL from label.'], + } + + super().__init__(**kwargs) + + def extendMarkdown(self, md): + self.md = md + + # append to end of inline patterns + WIKILINK_RE = r'\[\[([\w0-9_ -]+)\]\]' + wikilinkPattern = WikiLinksInlineProcessor(WIKILINK_RE, self.getConfigs()) + wikilinkPattern.md = md + md.inlinePatterns.register(wikilinkPattern, 'wikilink', 75) + + +class WikiLinksInlineProcessor(InlineProcessor): + def __init__(self, pattern, config): + super().__init__(pattern) + self.config = config + + def handleMatch(self, m, data): + if m.group(1).strip(): + base_url, end_url, html_class = self._getMeta() + label = m.group(1).strip() + url = self.config['build_url'](label, base_url, end_url) + a = etree.Element('a') + a.text = label + a.set('href', url) + if html_class: + a.set('class', html_class) + else: + a = '' + return a, m.start(0), m.end(0) + + def _getMeta(self): + """ Return meta data or config data. """ + base_url = self.config['base_url'] + end_url = self.config['end_url'] + html_class = self.config['html_class'] + if hasattr(self.md, 'Meta'): + if 'wiki_base_url' in self.md.Meta: + base_url = self.md.Meta['wiki_base_url'][0] + if 'wiki_end_url' in self.md.Meta: + end_url = self.md.Meta['wiki_end_url'][0] + if 'wiki_html_class' in self.md.Meta: + html_class = self.md.Meta['wiki_html_class'][0] + return base_url, end_url, html_class + + +def makeExtension(**kwargs): # pragma: no cover + return WikiLinkExtension(**kwargs) diff --git a/venv/Lib/site-packages/markdown/htmlparser.py b/venv/Lib/site-packages/markdown/htmlparser.py new file mode 100644 index 0000000..c08856a --- /dev/null +++ b/venv/Lib/site-packages/markdown/htmlparser.py @@ -0,0 +1,323 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import re +import importlib +import sys + + +# Import a copy of the html.parser lib as `htmlparser` so we can monkeypatch it. +# Users can still do `from html import parser` and get the default behavior. +spec = importlib.util.find_spec('html.parser') +htmlparser = importlib.util.module_from_spec(spec) +spec.loader.exec_module(htmlparser) +sys.modules['htmlparser'] = htmlparser + +# Monkeypatch HTMLParser to only accept `?>` to close Processing Instructions. +htmlparser.piclose = re.compile(r'\?>') +# Monkeypatch HTMLParser to only recognize entity references with a closing semicolon. +htmlparser.entityref = re.compile(r'&([a-zA-Z][-.a-zA-Z0-9]*);') +# Monkeypatch HTMLParser to no longer support partial entities. We are always feeding a complete block, +# so the 'incomplete' functionality is unnecessary. As the entityref regex is run right before incomplete, +# and the two regex are the same, then incomplete will simply never match and we avoid the logic within. +htmlparser.incomplete = htmlparser.entityref +# Monkeypatch HTMLParser to not accept a backtick in a tag name, attribute name, or bare value. +htmlparser.locatestarttagend_tolerant = re.compile(r""" + <[a-zA-Z][^`\t\n\r\f />\x00]* # tag name <= added backtick here + (?:[\s/]* # optional whitespace before attribute name + (?:(?<=['"\s/])[^`\s/>][^\s/=>]* # attribute name <= added backtick here + (?:\s*=+\s* # value indicator + (?:'[^']*' # LITA-enclosed value + |"[^"]*" # LIT-enclosed value + |(?!['"])[^`>\s]* # bare value <= added backtick here + ) + (?:\s*,)* # possibly followed by a comma + )?(?:\s|/(?!>))* + )* + )? + \s* # trailing whitespace +""", re.VERBOSE) + +# Match a blank line at the start of a block of text (two newlines). +# The newlines may be preceded by additional whitespace. +blank_line_re = re.compile(r'^([ ]*\n){2}') + + +class HTMLExtractor(htmlparser.HTMLParser): + """ + Extract raw HTML from text. + + The raw HTML is stored in the `htmlStash` of the Markdown instance passed + to `md` and the remaining text is stored in `cleandoc` as a list of strings. + """ + + def __init__(self, md, *args, **kwargs): + if 'convert_charrefs' not in kwargs: + kwargs['convert_charrefs'] = False + + # Block tags that should contain no content (self closing) + self.empty_tags = set(['hr']) + + # This calls self.reset + super().__init__(*args, **kwargs) + self.md = md + + def reset(self): + """Reset this instance. Loses all unprocessed data.""" + self.inraw = False + self.intail = False + self.stack = [] # When inraw==True, stack contains a list of tags + self._cache = [] + self.cleandoc = [] + super().reset() + + def close(self): + """Handle any buffered data.""" + super().close() + if len(self.rawdata): + # Temp fix for https://bugs.python.org/issue41989 + # TODO: remove this when the bug is fixed in all supported Python versions. + if self.convert_charrefs and not self.cdata_elem: # pragma: no cover + self.handle_data(htmlparser.unescape(self.rawdata)) + else: + self.handle_data(self.rawdata) + # Handle any unclosed tags. + if len(self._cache): + self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache))) + self._cache = [] + + @property + def line_offset(self): + """Returns char index in self.rawdata for the start of the current line. """ + if self.lineno > 1 and '\n' in self.rawdata: + m = re.match(r'([^\n]*\n){{{}}}'.format(self.lineno-1), self.rawdata) + if m: + return m.end() + else: # pragma: no cover + # Value of self.lineno must exceed total number of lines. + # Find index of begining of last line. + return self.rawdata.rfind('\n') + return 0 + + def at_line_start(self): + """ + Returns True if current position is at start of line. + + Allows for up to three blank spaces at start of line. + """ + if self.offset == 0: + return True + if self.offset > 3: + return False + # Confirm up to first 3 chars are whitespace + return self.rawdata[self.line_offset:self.line_offset + self.offset].strip() == '' + + def get_endtag_text(self, tag): + """ + Returns the text of the end tag. + + If it fails to extract the actual text from the raw data, it builds a closing tag with `tag`. + """ + # Attempt to extract actual tag from raw source text + start = self.line_offset + self.offset + m = htmlparser.endendtag.search(self.rawdata, start) + if m: + return self.rawdata[start:m.end()] + else: # pragma: no cover + # Failed to extract from raw data. Assume well formed and lowercase. + return '</{}>'.format(tag) + + def handle_starttag(self, tag, attrs): + # Handle tags that should always be empty and do not specify a closing tag + if tag in self.empty_tags: + self.handle_startendtag(tag, attrs) + return + + if self.md.is_block_level(tag) and (self.intail or (self.at_line_start() and not self.inraw)): + # Started a new raw block. Prepare stack. + self.inraw = True + self.cleandoc.append('\n') + + text = self.get_starttag_text() + if self.inraw: + self.stack.append(tag) + self._cache.append(text) + else: + self.cleandoc.append(text) + if tag in self.CDATA_CONTENT_ELEMENTS: + # This is presumably a standalone tag in a code span (see #1036). + self.clear_cdata_mode() + + def handle_endtag(self, tag): + text = self.get_endtag_text(tag) + + if self.inraw: + self._cache.append(text) + if tag in self.stack: + # Remove tag from stack + while self.stack: + if self.stack.pop() == tag: + break + if len(self.stack) == 0: + # End of raw block. + if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(text):]): + # Preserve blank line and end of raw block. + self._cache.append('\n') + else: + # More content exists after endtag. + self.intail = True + # Reset stack. + self.inraw = False + self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache))) + # Insert blank line between this and next line. + self.cleandoc.append('\n\n') + self._cache = [] + else: + self.cleandoc.append(text) + + def handle_data(self, data): + if self.intail and '\n' in data: + self.intail = False + if self.inraw: + self._cache.append(data) + else: + self.cleandoc.append(data) + + def handle_empty_tag(self, data, is_block): + """ Handle empty tags (`<data>`). """ + if self.inraw or self.intail: + # Append this to the existing raw block + self._cache.append(data) + elif self.at_line_start() and is_block: + # Handle this as a standalone raw block + if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(data):]): + # Preserve blank line after tag in raw block. + data += '\n' + else: + # More content exists after tag. + self.intail = True + item = self.cleandoc[-1] if self.cleandoc else '' + # If we only have one newline before block element, add another + if not item.endswith('\n\n') and item.endswith('\n'): + self.cleandoc.append('\n') + self.cleandoc.append(self.md.htmlStash.store(data)) + # Insert blank line between this and next line. + self.cleandoc.append('\n\n') + else: + self.cleandoc.append(data) + + def handle_startendtag(self, tag, attrs): + self.handle_empty_tag(self.get_starttag_text(), is_block=self.md.is_block_level(tag)) + + def handle_charref(self, name): + self.handle_empty_tag('&#{};'.format(name), is_block=False) + + def handle_entityref(self, name): + self.handle_empty_tag('&{};'.format(name), is_block=False) + + def handle_comment(self, data): + self.handle_empty_tag('<!--{}-->'.format(data), is_block=True) + + def handle_decl(self, data): + self.handle_empty_tag('<!{}>'.format(data), is_block=True) + + def handle_pi(self, data): + self.handle_empty_tag('<?{}?>'.format(data), is_block=True) + + def unknown_decl(self, data): + end = ']]>' if data.startswith('CDATA[') else ']>' + self.handle_empty_tag('<![{}{}'.format(data, end), is_block=True) + + def parse_pi(self, i): + if self.at_line_start() or self.intail: + return super().parse_pi(i) + # This is not the beginning of a raw block so treat as plain data + # and avoid consuming any tags which may follow (see #1066). + self.handle_data('<?') + return i + 2 + + def parse_html_declaration(self, i): + if self.at_line_start() or self.intail: + return super().parse_html_declaration(i) + # This is not the beginning of a raw block so treat as plain data + # and avoid consuming any tags which may follow (see #1066). + self.handle_data('<!') + return i + 2 + + # The rest has been copied from base class in standard lib to address #1036. + # As __startag_text is private, all references to it must be in this subclass. + # The last few lines of parse_starttag are reversed so that handle_starttag + # can override cdata_mode in certain situations (in a code span). + __starttag_text = None + + def get_starttag_text(self): + """Return full source of start tag: '<...>'.""" + return self.__starttag_text + + def parse_starttag(self, i): # pragma: no cover + self.__starttag_text = None + endpos = self.check_for_whole_start_tag(i) + if endpos < 0: + return endpos + rawdata = self.rawdata + self.__starttag_text = rawdata[i:endpos] + + # Now parse the data between i+1 and j into a tag and attrs + attrs = [] + match = htmlparser.tagfind_tolerant.match(rawdata, i+1) + assert match, 'unexpected call to parse_starttag()' + k = match.end() + self.lasttag = tag = match.group(1).lower() + while k < endpos: + m = htmlparser.attrfind_tolerant.match(rawdata, k) + if not m: + break + attrname, rest, attrvalue = m.group(1, 2, 3) + if not rest: + attrvalue = None + elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ + attrvalue[:1] == '"' == attrvalue[-1:]: # noqa: E127 + attrvalue = attrvalue[1:-1] + if attrvalue: + attrvalue = htmlparser.unescape(attrvalue) + attrs.append((attrname.lower(), attrvalue)) + k = m.end() + + end = rawdata[k:endpos].strip() + if end not in (">", "/>"): + lineno, offset = self.getpos() + if "\n" in self.__starttag_text: + lineno = lineno + self.__starttag_text.count("\n") + offset = len(self.__starttag_text) \ + - self.__starttag_text.rfind("\n") # noqa: E127 + else: + offset = offset + len(self.__starttag_text) + self.handle_data(rawdata[i:endpos]) + return endpos + if end.endswith('/>'): + # XHTML-style empty tag: <span attr="value" /> + self.handle_startendtag(tag, attrs) + else: + # *** set cdata_mode first so we can override it in handle_starttag (see #1036) *** + if tag in self.CDATA_CONTENT_ELEMENTS: + self.set_cdata_mode(tag) + self.handle_starttag(tag, attrs) + return endpos diff --git a/venv/Lib/site-packages/markdown/inlinepatterns.py b/venv/Lib/site-packages/markdown/inlinepatterns.py new file mode 100644 index 0000000..f7d604e --- /dev/null +++ b/venv/Lib/site-packages/markdown/inlinepatterns.py @@ -0,0 +1,892 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). + +INLINE PATTERNS +============================================================================= + +Inline patterns such as *emphasis* are handled by means of auxiliary +objects, one per pattern. Pattern objects must be instances of classes +that extend markdown.Pattern. Each pattern object uses a single regular +expression and needs support the following methods: + + pattern.getCompiledRegExp() # returns a regular expression + + pattern.handleMatch(m) # takes a match object and returns + # an ElementTree element or just plain text + +All of python markdown's built-in patterns subclass from Pattern, +but you can add additional patterns that don't. + +Also note that all the regular expressions used by inline must +capture the whole block. For this reason, they all start with +'^(.*)' and end with '(.*)!'. In case with built-in expression +Pattern takes care of adding the "^(.*)" and "(.*)!". + +Finally, the order in which regular expressions are applied is very +important - e.g. if we first replace http://.../ links with <a> tags +and _then_ try to replace inline html, we would end up with a mess. +So, we apply the expressions in the following order: + +* escape and backticks have to go before everything else, so + that we can preempt any markdown patterns by escaping them. + +* then we handle auto-links (must be done before inline html) + +* then we handle inline HTML. At this point we will simply + replace all inline HTML strings with a placeholder and add + the actual HTML to a hash. + +* then inline images (must be done before links) + +* then bracketed links, first regular then reference-style + +* finally we apply strong and emphasis +""" + +from . import util +from collections import namedtuple +import re +import xml.etree.ElementTree as etree +try: # pragma: no cover + from html import entities +except ImportError: # pragma: no cover + import htmlentitydefs as entities + + +def build_inlinepatterns(md, **kwargs): + """ Build the default set of inline patterns for Markdown. """ + inlinePatterns = util.Registry() + inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE), 'backtick', 190) + inlinePatterns.register(EscapeInlineProcessor(ESCAPE_RE, md), 'escape', 180) + inlinePatterns.register(ReferenceInlineProcessor(REFERENCE_RE, md), 'reference', 170) + inlinePatterns.register(LinkInlineProcessor(LINK_RE, md), 'link', 160) + inlinePatterns.register(ImageInlineProcessor(IMAGE_LINK_RE, md), 'image_link', 150) + inlinePatterns.register( + ImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'image_reference', 140 + ) + inlinePatterns.register( + ShortReferenceInlineProcessor(REFERENCE_RE, md), 'short_reference', 130 + ) + inlinePatterns.register( + ShortImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'short_image_ref', 125 + ) + inlinePatterns.register(AutolinkInlineProcessor(AUTOLINK_RE, md), 'autolink', 120) + inlinePatterns.register(AutomailInlineProcessor(AUTOMAIL_RE, md), 'automail', 110) + inlinePatterns.register(SubstituteTagInlineProcessor(LINE_BREAK_RE, 'br'), 'linebreak', 100) + inlinePatterns.register(HtmlInlineProcessor(HTML_RE, md), 'html', 90) + inlinePatterns.register(HtmlInlineProcessor(ENTITY_RE, md), 'entity', 80) + inlinePatterns.register(SimpleTextInlineProcessor(NOT_STRONG_RE), 'not_strong', 70) + inlinePatterns.register(AsteriskProcessor(r'\*'), 'em_strong', 60) + inlinePatterns.register(UnderscoreProcessor(r'_'), 'em_strong2', 50) + return inlinePatterns + + +""" +The actual regular expressions for patterns +----------------------------------------------------------------------------- +""" + +NOIMG = r'(?<!\!)' + +# `e=f()` or ``e=f("`")`` +BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\2(?!`))' + +# \< +ESCAPE_RE = r'\\(.)' + +# *emphasis* +EMPHASIS_RE = r'(\*)([^\*]+)\1' + +# **strong** +STRONG_RE = r'(\*{2})(.+?)\1' + +# __smart__strong__ +SMART_STRONG_RE = r'(?<!\w)(_{2})(?!_)(.+?)(?<!_)\1(?!\w)' + +# _smart_emphasis_ +SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\1(?!\w)' + +# __strong _em__ +SMART_STRONG_EM_RE = r'(?<!\w)(\_)\1(?!\1)(.+?)(?<!\w)\1(?!\1)(.+?)\1{3}(?!\w)' + +# ***strongem*** or ***em*strong** +EM_STRONG_RE = r'(\*)\1{2}(.+?)\1(.*?)\1{2}' + +# ___strongem___ or ___em_strong__ +EM_STRONG2_RE = r'(_)\1{2}(.+?)\1(.*?)\1{2}' + +# ***strong**em* +STRONG_EM_RE = r'(\*)\1{2}(.+?)\1{2}(.*?)\1' + +# ___strong__em_ +STRONG_EM2_RE = r'(_)\1{2}(.+?)\1{2}(.*?)\1' + +# **strong*em*** +STRONG_EM3_RE = r'(\*)\1(?!\1)([^*]+?)\1(?!\1)(.+?)\1{3}' + +# [text](url) or [text](<url>) or [text](url "title") +LINK_RE = NOIMG + r'\[' + +# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>) +IMAGE_LINK_RE = r'\!\[' + +# [Google][3] +REFERENCE_RE = LINK_RE + +# ![alt text][2] +IMAGE_REFERENCE_RE = IMAGE_LINK_RE + +# stand-alone * or _ +NOT_STRONG_RE = r'((^|\s)(\*|_)(\s|$))' + +# <http://www.123.com> +AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^<>]*)>' + +# <me@example.com> +AUTOMAIL_RE = r'<([^<> !]+@[^@<> ]+)>' + +# <...> +HTML_RE = r'(<(\/?[a-zA-Z][^<>@ ]*( [^<>]*)?|!--(?:(?!<!--|-->).)*--)>)' + +# "&" (decimal) or "&" (hex) or "&" (named) +ENTITY_RE = r'(&(?:\#[0-9]+|\#x[0-9a-fA-F]+|[a-zA-Z0-9]+);)' + +# two spaces at end of line +LINE_BREAK_RE = r' \n' + + +def dequote(string): + """Remove quotes from around a string.""" + if ((string.startswith('"') and string.endswith('"')) or + (string.startswith("'") and string.endswith("'"))): + return string[1:-1] + else: + return string + + +class EmStrongItem(namedtuple('EmStrongItem', ['pattern', 'builder', 'tags'])): + """Emphasis/strong pattern item.""" + + +""" +The pattern classes +----------------------------------------------------------------------------- +""" + + +class Pattern: # pragma: no cover + """Base class that inline patterns subclass. """ + + ANCESTOR_EXCLUDES = tuple() + + def __init__(self, pattern, md=None): + """ + Create an instant of an inline pattern. + + Keyword arguments: + + * pattern: A regular expression that matches a pattern + + """ + self.pattern = pattern + self.compiled_re = re.compile(r"^(.*?)%s(.*)$" % pattern, + re.DOTALL | re.UNICODE) + + self.md = md + + @property + @util.deprecated("Use 'md' instead.") + def markdown(self): + # TODO: remove this later + return self.md + + def getCompiledRegExp(self): + """ Return a compiled regular expression. """ + return self.compiled_re + + def handleMatch(self, m): + """Return a ElementTree element from the given match. + + Subclasses should override this method. + + Keyword arguments: + + * m: A re match object containing a match of the pattern. + + """ + pass # pragma: no cover + + def type(self): + """ Return class name, to define pattern type """ + return self.__class__.__name__ + + def unescape(self, text): + """ Return unescaped text given text with an inline placeholder. """ + try: + stash = self.md.treeprocessors['inline'].stashed_nodes + except KeyError: # pragma: no cover + return text + + def get_stash(m): + id = m.group(1) + if id in stash: + value = stash.get(id) + if isinstance(value, str): + return value + else: + # An etree Element - return text content only + return ''.join(value.itertext()) + return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text) + + +class InlineProcessor(Pattern): + """ + Base class that inline patterns subclass. + + This is the newer style inline processor that uses a more + efficient and flexible search approach. + """ + + def __init__(self, pattern, md=None): + """ + Create an instant of an inline pattern. + + Keyword arguments: + + * pattern: A regular expression that matches a pattern + + """ + self.pattern = pattern + self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE) + + # Api for Markdown to pass safe_mode into instance + self.safe_mode = False + self.md = md + + def handleMatch(self, m, data): + """Return a ElementTree element from the given match and the + start and end index of the matched text. + + If `start` and/or `end` are returned as `None`, it will be + assumed that the processor did not find a valid region of text. + + Subclasses should override this method. + + Keyword arguments: + + * m: A re match object containing a match of the pattern. + * data: The buffer current under analysis + + Returns: + + * el: The ElementTree element, text or None. + * start: The start of the region that has been matched or None. + * end: The end of the region that has been matched or None. + + """ + pass # pragma: no cover + + +class SimpleTextPattern(Pattern): # pragma: no cover + """ Return a simple text of group(2) of a Pattern. """ + def handleMatch(self, m): + return m.group(2) + + +class SimpleTextInlineProcessor(InlineProcessor): + """ Return a simple text of group(1) of a Pattern. """ + def handleMatch(self, m, data): + return m.group(1), m.start(0), m.end(0) + + +class EscapeInlineProcessor(InlineProcessor): + """ Return an escaped character. """ + + def handleMatch(self, m, data): + char = m.group(1) + if char in self.md.ESCAPED_CHARS: + return '{}{}{}'.format(util.STX, ord(char), util.ETX), m.start(0), m.end(0) + else: + return None, m.start(0), m.end(0) + + +class SimpleTagPattern(Pattern): # pragma: no cover + """ + Return element of type `tag` with a text attribute of group(3) + of a Pattern. + + """ + def __init__(self, pattern, tag): + Pattern.__init__(self, pattern) + self.tag = tag + + def handleMatch(self, m): + el = etree.Element(self.tag) + el.text = m.group(3) + return el + + +class SimpleTagInlineProcessor(InlineProcessor): + """ + Return element of type `tag` with a text attribute of group(2) + of a Pattern. + + """ + def __init__(self, pattern, tag): + InlineProcessor.__init__(self, pattern) + self.tag = tag + + def handleMatch(self, m, data): # pragma: no cover + el = etree.Element(self.tag) + el.text = m.group(2) + return el, m.start(0), m.end(0) + + +class SubstituteTagPattern(SimpleTagPattern): # pragma: no cover + """ Return an element of type `tag` with no children. """ + def handleMatch(self, m): + return etree.Element(self.tag) + + +class SubstituteTagInlineProcessor(SimpleTagInlineProcessor): + """ Return an element of type `tag` with no children. """ + def handleMatch(self, m, data): + return etree.Element(self.tag), m.start(0), m.end(0) + + +class BacktickInlineProcessor(InlineProcessor): + """ Return a `<code>` element containing the matching text. """ + def __init__(self, pattern): + InlineProcessor.__init__(self, pattern) + self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX) + self.tag = 'code' + + def handleMatch(self, m, data): + if m.group(3): + el = etree.Element(self.tag) + el.text = util.AtomicString(util.code_escape(m.group(3).strip())) + return el, m.start(0), m.end(0) + else: + return m.group(1).replace('\\\\', self.ESCAPED_BSLASH), m.start(0), m.end(0) + + +class DoubleTagPattern(SimpleTagPattern): # pragma: no cover + """Return a ElementTree element nested in tag2 nested in tag1. + + Useful for strong emphasis etc. + + """ + def handleMatch(self, m): + tag1, tag2 = self.tag.split(",") + el1 = etree.Element(tag1) + el2 = etree.SubElement(el1, tag2) + el2.text = m.group(3) + if len(m.groups()) == 5: + el2.tail = m.group(4) + return el1 + + +class DoubleTagInlineProcessor(SimpleTagInlineProcessor): + """Return a ElementTree element nested in tag2 nested in tag1. + + Useful for strong emphasis etc. + + """ + def handleMatch(self, m, data): # pragma: no cover + tag1, tag2 = self.tag.split(",") + el1 = etree.Element(tag1) + el2 = etree.SubElement(el1, tag2) + el2.text = m.group(2) + if len(m.groups()) == 3: + el2.tail = m.group(3) + return el1, m.start(0), m.end(0) + + +class HtmlInlineProcessor(InlineProcessor): + """ Store raw inline html and return a placeholder. """ + def handleMatch(self, m, data): + rawhtml = self.unescape(m.group(1)) + place_holder = self.md.htmlStash.store(rawhtml) + return place_holder, m.start(0), m.end(0) + + def unescape(self, text): + """ Return unescaped text given text with an inline placeholder. """ + try: + stash = self.md.treeprocessors['inline'].stashed_nodes + except KeyError: # pragma: no cover + return text + + def get_stash(m): + id = m.group(1) + value = stash.get(id) + if value is not None: + try: + return self.md.serializer(value) + except Exception: + return r'\%s' % value + + return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text) + + +class AsteriskProcessor(InlineProcessor): + """Emphasis processor for handling strong and em matches inside asterisks.""" + + PATTERNS = [ + EmStrongItem(re.compile(EM_STRONG_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), + EmStrongItem(re.compile(STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), + EmStrongItem(re.compile(STRONG_EM3_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), + EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), + EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') + ] + + def build_single(self, m, tag, idx): + """Return single tag.""" + el1 = etree.Element(tag) + text = m.group(2) + self.parse_sub_patterns(text, el1, None, idx) + return el1 + + def build_double(self, m, tags, idx): + """Return double tag.""" + + tag1, tag2 = tags.split(",") + el1 = etree.Element(tag1) + el2 = etree.Element(tag2) + text = m.group(2) + self.parse_sub_patterns(text, el2, None, idx) + el1.append(el2) + if len(m.groups()) == 3: + text = m.group(3) + self.parse_sub_patterns(text, el1, el2, idx) + return el1 + + def build_double2(self, m, tags, idx): + """Return double tags (variant 2): `<strong>text <em>text</em></strong>`.""" + + tag1, tag2 = tags.split(",") + el1 = etree.Element(tag1) + el2 = etree.Element(tag2) + text = m.group(2) + self.parse_sub_patterns(text, el1, None, idx) + text = m.group(3) + el1.append(el2) + self.parse_sub_patterns(text, el2, None, idx) + return el1 + + def parse_sub_patterns(self, data, parent, last, idx): + """ + Parses sub patterns. + + `data` (`str`): + text to evaluate. + + `parent` (`etree.Element`): + Parent to attach text and sub elements to. + + `last` (`etree.Element`): + Last appended child to parent. Can also be None if parent has no children. + + `idx` (`int`): + Current pattern index that was used to evaluate the parent. + + """ + + offset = 0 + pos = 0 + + length = len(data) + while pos < length: + # Find the start of potential emphasis or strong tokens + if self.compiled_re.match(data, pos): + matched = False + # See if the we can match an emphasis/strong pattern + for index, item in enumerate(self.PATTERNS): + # Only evaluate patterns that are after what was used on the parent + if index <= idx: + continue + m = item.pattern.match(data, pos) + if m: + # Append child nodes to parent + # Text nodes should be appended to the last + # child if present, and if not, it should + # be added as the parent's text node. + text = data[offset:m.start(0)] + if text: + if last is not None: + last.tail = text + else: + parent.text = text + el = self.build_element(m, item.builder, item.tags, index) + parent.append(el) + last = el + # Move our position past the matched hunk + offset = pos = m.end(0) + matched = True + if not matched: + # We matched nothing, move on to the next character + pos += 1 + else: + # Increment position as no potential emphasis start was found. + pos += 1 + + # Append any leftover text as a text node. + text = data[offset:] + if text: + if last is not None: + last.tail = text + else: + parent.text = text + + def build_element(self, m, builder, tags, index): + """Element builder.""" + + if builder == 'double2': + return self.build_double2(m, tags, index) + elif builder == 'double': + return self.build_double(m, tags, index) + else: + return self.build_single(m, tags, index) + + def handleMatch(self, m, data): + """Parse patterns.""" + + el = None + start = None + end = None + + for index, item in enumerate(self.PATTERNS): + m1 = item.pattern.match(data, m.start(0)) + if m1: + start = m1.start(0) + end = m1.end(0) + el = self.build_element(m1, item.builder, item.tags, index) + break + return el, start, end + + +class UnderscoreProcessor(AsteriskProcessor): + """Emphasis processor for handling strong and em matches inside underscores.""" + + PATTERNS = [ + EmStrongItem(re.compile(EM_STRONG2_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), + EmStrongItem(re.compile(STRONG_EM2_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), + EmStrongItem(re.compile(SMART_STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), + EmStrongItem(re.compile(SMART_STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), + EmStrongItem(re.compile(SMART_EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') + ] + + +class LinkInlineProcessor(InlineProcessor): + """ Return a link element from the given match. """ + RE_LINK = re.compile(r'''\(\s*(?:(<[^<>]*>)\s*(?:('[^']*'|"[^"]*")\s*)?\))?''', re.DOTALL | re.UNICODE) + RE_TITLE_CLEAN = re.compile(r'\s') + + def handleMatch(self, m, data): + text, index, handled = self.getText(data, m.end(0)) + + if not handled: + return None, None, None + + href, title, index, handled = self.getLink(data, index) + if not handled: + return None, None, None + + el = etree.Element("a") + el.text = text + + el.set("href", href) + + if title is not None: + el.set("title", title) + + return el, m.start(0), index + + def getLink(self, data, index): + """Parse data between `()` of `[Text]()` allowing recursive `()`. """ + + href = '' + title = None + handled = False + + m = self.RE_LINK.match(data, pos=index) + if m and m.group(1): + # Matches [Text](<link> "title") + href = m.group(1)[1:-1].strip() + if m.group(2): + title = m.group(2)[1:-1] + index = m.end(0) + handled = True + elif m: + # Track bracket nesting and index in string + bracket_count = 1 + backtrack_count = 1 + start_index = m.end() + index = start_index + last_bracket = -1 + + # Primary (first found) quote tracking. + quote = None + start_quote = -1 + exit_quote = -1 + ignore_matches = False + + # Secondary (second found) quote tracking. + alt_quote = None + start_alt_quote = -1 + exit_alt_quote = -1 + + # Track last character + last = '' + + for pos in range(index, len(data)): + c = data[pos] + if c == '(': + # Count nested ( + # Don't increment the bracket count if we are sure we're in a title. + if not ignore_matches: + bracket_count += 1 + elif backtrack_count > 0: + backtrack_count -= 1 + elif c == ')': + # Match nested ) to ( + # Don't decrement if we are sure we are in a title that is unclosed. + if ((exit_quote != -1 and quote == last) or (exit_alt_quote != -1 and alt_quote == last)): + bracket_count = 0 + elif not ignore_matches: + bracket_count -= 1 + elif backtrack_count > 0: + backtrack_count -= 1 + # We've found our backup end location if the title doesn't reslove. + if backtrack_count == 0: + last_bracket = index + 1 + + elif c in ("'", '"'): + # Quote has started + if not quote: + # We'll assume we are now in a title. + # Brackets are quoted, so no need to match them (except for the final one). + ignore_matches = True + backtrack_count = bracket_count + bracket_count = 1 + start_quote = index + 1 + quote = c + # Secondary quote (in case the first doesn't resolve): [text](link'"title") + elif c != quote and not alt_quote: + start_alt_quote = index + 1 + alt_quote = c + # Update primary quote match + elif c == quote: + exit_quote = index + 1 + # Update secondary quote match + elif alt_quote and c == alt_quote: + exit_alt_quote = index + 1 + + index += 1 + + # Link is closed, so let's break out of the loop + if bracket_count == 0: + # Get the title if we closed a title string right before link closed + if exit_quote >= 0 and quote == last: + href = data[start_index:start_quote - 1] + title = ''.join(data[start_quote:exit_quote - 1]) + elif exit_alt_quote >= 0 and alt_quote == last: + href = data[start_index:start_alt_quote - 1] + title = ''.join(data[start_alt_quote:exit_alt_quote - 1]) + else: + href = data[start_index:index - 1] + break + + if c != ' ': + last = c + + # We have a scenario: [test](link"notitle) + # When we enter a string, we stop tracking bracket resolution in the main counter, + # but we do keep a backup counter up until we discover where we might resolve all brackets + # if the title string fails to resolve. + if bracket_count != 0 and backtrack_count == 0: + href = data[start_index:last_bracket - 1] + index = last_bracket + bracket_count = 0 + + handled = bracket_count == 0 + + if title is not None: + title = self.RE_TITLE_CLEAN.sub(' ', dequote(self.unescape(title.strip()))) + + href = self.unescape(href).strip() + + return href, title, index, handled + + def getText(self, data, index): + """Parse the content between `[]` of the start of an image or link + resolving nested square brackets. + + """ + bracket_count = 1 + text = [] + for pos in range(index, len(data)): + c = data[pos] + if c == ']': + bracket_count -= 1 + elif c == '[': + bracket_count += 1 + index += 1 + if bracket_count == 0: + break + text.append(c) + return ''.join(text), index, bracket_count == 0 + + +class ImageInlineProcessor(LinkInlineProcessor): + """ Return a img element from the given match. """ + + def handleMatch(self, m, data): + text, index, handled = self.getText(data, m.end(0)) + if not handled: + return None, None, None + + src, title, index, handled = self.getLink(data, index) + if not handled: + return None, None, None + + el = etree.Element("img") + + el.set("src", src) + + if title is not None: + el.set("title", title) + + el.set('alt', self.unescape(text)) + return el, m.start(0), index + + +class ReferenceInlineProcessor(LinkInlineProcessor): + """ Match to a stored reference and return link element. """ + NEWLINE_CLEANUP_RE = re.compile(r'\s+', re.MULTILINE) + + RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE) + + def handleMatch(self, m, data): + text, index, handled = self.getText(data, m.end(0)) + if not handled: + return None, None, None + + id, end, handled = self.evalId(data, index, text) + if not handled: + return None, None, None + + # Clean up linebreaks in id + id = self.NEWLINE_CLEANUP_RE.sub(' ', id) + if id not in self.md.references: # ignore undefined refs + return None, m.start(0), end + + href, title = self.md.references[id] + + return self.makeTag(href, title, text), m.start(0), end + + def evalId(self, data, index, text): + """ + Evaluate the id portion of [ref][id]. + + If [ref][] use [ref]. + """ + m = self.RE_LINK.match(data, pos=index) + if not m: + return None, index, False + else: + id = m.group(1).lower() + end = m.end(0) + if not id: + id = text.lower() + return id, end, True + + def makeTag(self, href, title, text): + el = etree.Element('a') + + el.set('href', href) + if title: + el.set('title', title) + + el.text = text + return el + + +class ShortReferenceInlineProcessor(ReferenceInlineProcessor): + """Short form of reference: [google]. """ + def evalId(self, data, index, text): + """Evaluate the id from of [ref] """ + + return text.lower(), index, True + + +class ImageReferenceInlineProcessor(ReferenceInlineProcessor): + """ Match to a stored reference and return img element. """ + def makeTag(self, href, title, text): + el = etree.Element("img") + el.set("src", href) + if title: + el.set("title", title) + el.set("alt", self.unescape(text)) + return el + + +class ShortImageReferenceInlineProcessor(ImageReferenceInlineProcessor): + """ Short form of inage reference: ![ref]. """ + def evalId(self, data, index, text): + """Evaluate the id from of [ref] """ + + return text.lower(), index, True + + +class AutolinkInlineProcessor(InlineProcessor): + """ Return a link Element given an autolink (`<http://example/com>`). """ + def handleMatch(self, m, data): + el = etree.Element("a") + el.set('href', self.unescape(m.group(1))) + el.text = util.AtomicString(m.group(1)) + return el, m.start(0), m.end(0) + + +class AutomailInlineProcessor(InlineProcessor): + """ + Return a mailto link Element given an automail link (`<foo@example.com>`). + """ + def handleMatch(self, m, data): + el = etree.Element('a') + email = self.unescape(m.group(1)) + if email.startswith("mailto:"): + email = email[len("mailto:"):] + + def codepoint2name(code): + """Return entity definition by code, or the code if not defined.""" + entity = entities.codepoint2name.get(code) + if entity: + return "{}{};".format(util.AMP_SUBSTITUTE, entity) + else: + return "%s#%d;" % (util.AMP_SUBSTITUTE, code) + + letters = [codepoint2name(ord(letter)) for letter in email] + el.text = util.AtomicString(''.join(letters)) + + mailto = "mailto:" + email + mailto = "".join([util.AMP_SUBSTITUTE + '#%d;' % + ord(letter) for letter in mailto]) + el.set('href', mailto) + return el, m.start(0), m.end(0) diff --git a/venv/Lib/site-packages/markdown/pep562.py b/venv/Lib/site-packages/markdown/pep562.py new file mode 100644 index 0000000..b130d3b --- /dev/null +++ b/venv/Lib/site-packages/markdown/pep562.py @@ -0,0 +1,245 @@ +""" +Backport of PEP 562. + +https://pypi.org/search/?q=pep562 + +Licensed under MIT +Copyright (c) 2018 Isaac Muse <isaacmuse@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" +import sys +from collections import namedtuple +import re + +__all__ = ('Pep562',) + +RE_VER = re.compile( + r'''(?x) + (?P<major>\d+)(?:\.(?P<minor>\d+))?(?:\.(?P<micro>\d+))? + (?:(?P<type>a|b|rc)(?P<pre>\d+))? + (?:\.post(?P<post>\d+))? + (?:\.dev(?P<dev>\d+))? + ''' +) + +REL_MAP = { + ".dev": "", + ".dev-alpha": "a", + ".dev-beta": "b", + ".dev-candidate": "rc", + "alpha": "a", + "beta": "b", + "candidate": "rc", + "final": "" +} + +DEV_STATUS = { + ".dev": "2 - Pre-Alpha", + ".dev-alpha": "2 - Pre-Alpha", + ".dev-beta": "2 - Pre-Alpha", + ".dev-candidate": "2 - Pre-Alpha", + "alpha": "3 - Alpha", + "beta": "4 - Beta", + "candidate": "4 - Beta", + "final": "5 - Production/Stable" +} + +PRE_REL_MAP = {"a": 'alpha', "b": 'beta', "rc": 'candidate'} + + +class Version(namedtuple("Version", ["major", "minor", "micro", "release", "pre", "post", "dev"])): + """ + Get the version (PEP 440). + + A biased approach to the PEP 440 semantic version. + + Provides a tuple structure which is sorted for comparisons `v1 > v2` etc. + (major, minor, micro, release type, pre-release build, post-release build, development release build) + Release types are named in is such a way they are comparable with ease. + Accessors to check if a development, pre-release, or post-release build. Also provides accessor to get + development status for setup files. + + How it works (currently): + + - You must specify a release type as either `final`, `alpha`, `beta`, or `candidate`. + - To define a development release, you can use either `.dev`, `.dev-alpha`, `.dev-beta`, or `.dev-candidate`. + The dot is used to ensure all development specifiers are sorted before `alpha`. + You can specify a `dev` number for development builds, but do not have to as implicit development releases + are allowed. + - You must specify a `pre` value greater than zero if using a prerelease as this project (not PEP 440) does not + allow implicit prereleases. + - You can optionally set `post` to a value greater than zero to make the build a post release. While post releases + are technically allowed in prereleases, it is strongly discouraged, so we are rejecting them. It should be + noted that we do not allow `post0` even though PEP 440 does not restrict this. This project specifically + does not allow implicit post releases. + - It should be noted that we do not support epochs `1!` or local versions `+some-custom.version-1`. + + Acceptable version releases: + + ``` + Version(1, 0, 0, "final") 1.0 + Version(1, 2, 0, "final") 1.2 + Version(1, 2, 3, "final") 1.2.3 + Version(1, 2, 0, ".dev-alpha", pre=4) 1.2a4 + Version(1, 2, 0, ".dev-beta", pre=4) 1.2b4 + Version(1, 2, 0, ".dev-candidate", pre=4) 1.2rc4 + Version(1, 2, 0, "final", post=1) 1.2.post1 + Version(1, 2, 3, ".dev") 1.2.3.dev0 + Version(1, 2, 3, ".dev", dev=1) 1.2.3.dev1 + ``` + + """ + + def __new__(cls, major, minor, micro, release="final", pre=0, post=0, dev=0): + """Validate version info.""" + + # Ensure all parts are positive integers. + for value in (major, minor, micro, pre, post): + if not (isinstance(value, int) and value >= 0): + raise ValueError("All version parts except 'release' should be integers.") + + if release not in REL_MAP: + raise ValueError("'{}' is not a valid release type.".format(release)) + + # Ensure valid pre-release (we do not allow implicit pre-releases). + if ".dev-candidate" < release < "final": + if pre == 0: + raise ValueError("Implicit pre-releases not allowed.") + elif dev: + raise ValueError("Version is not a development release.") + elif post: + raise ValueError("Post-releases are not allowed with pre-releases.") + + # Ensure valid development or development/pre release + elif release < "alpha": + if release > ".dev" and pre == 0: + raise ValueError("Implicit pre-release not allowed.") + elif post: + raise ValueError("Post-releases are not allowed with pre-releases.") + + # Ensure a valid normal release + else: + if pre: + raise ValueError("Version is not a pre-release.") + elif dev: + raise ValueError("Version is not a development release.") + + return super().__new__(cls, major, minor, micro, release, pre, post, dev) + + def _is_pre(self): + """Is prerelease.""" + + return self.pre > 0 + + def _is_dev(self): + """Is development.""" + + return bool(self.release < "alpha") + + def _is_post(self): + """Is post.""" + + return self.post > 0 + + def _get_dev_status(self): # pragma: no cover + """Get development status string.""" + + return DEV_STATUS[self.release] + + def _get_canonical(self): + """Get the canonical output string.""" + + # Assemble major, minor, micro version and append `pre`, `post`, or `dev` if needed.. + if self.micro == 0: + ver = "{}.{}".format(self.major, self.minor) + else: + ver = "{}.{}.{}".format(self.major, self.minor, self.micro) + if self._is_pre(): + ver += '{}{}'.format(REL_MAP[self.release], self.pre) + if self._is_post(): + ver += ".post{}".format(self.post) + if self._is_dev(): + ver += ".dev{}".format(self.dev) + + return ver + + +def parse_version(ver, pre=False): + """Parse version into a comparable Version tuple.""" + + m = RE_VER.match(ver) + + # Handle major, minor, micro + major = int(m.group('major')) + minor = int(m.group('minor')) if m.group('minor') else 0 + micro = int(m.group('micro')) if m.group('micro') else 0 + + # Handle pre releases + if m.group('type'): + release = PRE_REL_MAP[m.group('type')] + pre = int(m.group('pre')) + else: + release = "final" + pre = 0 + + # Handle development releases + dev = m.group('dev') if m.group('dev') else 0 + if m.group('dev'): + dev = int(m.group('dev')) + release = '.dev-' + release if pre else '.dev' + else: + dev = 0 + + # Handle post + post = int(m.group('post')) if m.group('post') else 0 + + return Version(major, minor, micro, release, pre, post, dev) + + +class Pep562: + """ + Backport of PEP 562 <https://pypi.org/search/?q=pep562>. + + Wraps the module in a class that exposes the mechanics to override `__dir__` and `__getattr__`. + The given module will be searched for overrides of `__dir__` and `__getattr__` and use them when needed. + """ + + def __init__(self, name): + """Acquire `__getattr__` and `__dir__`, but only replace module for versions less than Python 3.7.""" + + self._module = sys.modules[name] + self._get_attr = getattr(self._module, '__getattr__', None) + self._get_dir = getattr(self._module, '__dir__', None) + sys.modules[name] = self + + def __dir__(self): + """Return the overridden `dir` if one was provided, else apply `dir` to the module.""" + + return self._get_dir() if self._get_dir else dir(self._module) + + def __getattr__(self, name): + """Attempt to retrieve the attribute from the module, and if missing, use the overridden function if present.""" + + try: + return getattr(self._module, name) + except AttributeError: + if self._get_attr: + return self._get_attr(name) + raise + + +__version_info__ = Version(1, 0, 0, "final") +__version__ = __version_info__._get_canonical() diff --git a/venv/Lib/site-packages/markdown/postprocessors.py b/venv/Lib/site-packages/markdown/postprocessors.py new file mode 100644 index 0000000..f4fb924 --- /dev/null +++ b/venv/Lib/site-packages/markdown/postprocessors.py @@ -0,0 +1,134 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). + +POST-PROCESSORS +============================================================================= + +Markdown also allows post-processors, which are similar to preprocessors in +that they need to implement a "run" method. However, they are run after core +processing. + +""" + +from collections import OrderedDict +from . import util +import re + + +def build_postprocessors(md, **kwargs): + """ Build the default postprocessors for Markdown. """ + postprocessors = util.Registry() + postprocessors.register(RawHtmlPostprocessor(md), 'raw_html', 30) + postprocessors.register(AndSubstitutePostprocessor(), 'amp_substitute', 20) + postprocessors.register(UnescapePostprocessor(), 'unescape', 10) + return postprocessors + + +class Postprocessor(util.Processor): + """ + Postprocessors are run after the ElementTree it converted back into text. + + Each Postprocessor implements a "run" method that takes a pointer to a + text string, modifies it as necessary and returns a text string. + + Postprocessors must extend markdown.Postprocessor. + + """ + + def run(self, text): + """ + Subclasses of Postprocessor should implement a `run` method, which + takes the html document as a single text string and returns a + (possibly modified) string. + + """ + pass # pragma: no cover + + +class RawHtmlPostprocessor(Postprocessor): + """ Restore raw html to the document. """ + + BLOCK_LEVEL_REGEX = re.compile(r'^\<\/?([^ >]+)') + + def run(self, text): + """ Iterate over html stash and restore html. """ + replacements = OrderedDict() + for i in range(self.md.htmlStash.html_counter): + html = self.stash_to_string(self.md.htmlStash.rawHtmlBlocks[i]) + if self.isblocklevel(html): + replacements["<p>{}</p>".format( + self.md.htmlStash.get_placeholder(i))] = html + replacements[self.md.htmlStash.get_placeholder(i)] = html + + def substitute_match(m): + key = m.group(0) + + if key not in replacements: + if key[3:-4] in replacements: + return f'<p>{ replacements[key[3:-4]] }</p>' + else: + return key + + return replacements[key] + + if replacements: + base_placeholder = util.HTML_PLACEHOLDER % r'([0-9]+)' + pattern = re.compile(f'<p>{ base_placeholder }</p>|{ base_placeholder }') + processed_text = pattern.sub(substitute_match, text) + else: + return text + + if processed_text == text: + return processed_text + else: + return self.run(processed_text) + + def isblocklevel(self, html): + m = self.BLOCK_LEVEL_REGEX.match(html) + if m: + if m.group(1)[0] in ('!', '?', '@', '%'): + # Comment, php etc... + return True + return self.md.is_block_level(m.group(1)) + return False + + def stash_to_string(self, text): + """ Convert a stashed object to a string. """ + return str(text) + + +class AndSubstitutePostprocessor(Postprocessor): + """ Restore valid entities """ + + def run(self, text): + text = text.replace(util.AMP_SUBSTITUTE, "&") + return text + + +class UnescapePostprocessor(Postprocessor): + """ Restore escaped chars """ + + RE = re.compile(r'{}(\d+){}'.format(util.STX, util.ETX)) + + def unescape(self, m): + return chr(int(m.group(1))) + + def run(self, text): + return self.RE.sub(self.unescape, text) diff --git a/venv/Lib/site-packages/markdown/preprocessors.py b/venv/Lib/site-packages/markdown/preprocessors.py new file mode 100644 index 0000000..e1023c5 --- /dev/null +++ b/venv/Lib/site-packages/markdown/preprocessors.py @@ -0,0 +1,82 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). + +PRE-PROCESSORS +============================================================================= + +Preprocessors work on source text before we start doing anything too +complicated. +""" + +from . import util +from .htmlparser import HTMLExtractor +import re + + +def build_preprocessors(md, **kwargs): + """ Build the default set of preprocessors used by Markdown. """ + preprocessors = util.Registry() + preprocessors.register(NormalizeWhitespace(md), 'normalize_whitespace', 30) + preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20) + return preprocessors + + +class Preprocessor(util.Processor): + """ + Preprocessors are run after the text is broken into lines. + + Each preprocessor implements a "run" method that takes a pointer to a + list of lines of the document, modifies it as necessary and returns + either the same pointer or a pointer to a new list. + + Preprocessors must extend markdown.Preprocessor. + + """ + def run(self, lines): + """ + Each subclass of Preprocessor should override the `run` method, which + takes the document as a list of strings split by newlines and returns + the (possibly modified) list of lines. + + """ + pass # pragma: no cover + + +class NormalizeWhitespace(Preprocessor): + """ Normalize whitespace for consistent parsing. """ + + def run(self, lines): + source = '\n'.join(lines) + source = source.replace(util.STX, "").replace(util.ETX, "") + source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n" + source = source.expandtabs(self.md.tab_length) + source = re.sub(r'(?<=\n) +\n', '\n', source) + return source.split('\n') + + +class HtmlBlockPreprocessor(Preprocessor): + """Remove html blocks from the text and store them for later retrieval.""" + + def run(self, lines): + source = '\n'.join(lines) + parser = HTMLExtractor(self.md) + parser.feed(source) + parser.close() + return ''.join(parser.cleandoc).split('\n') diff --git a/venv/Lib/site-packages/markdown/serializers.py b/venv/Lib/site-packages/markdown/serializers.py new file mode 100644 index 0000000..59bab18 --- /dev/null +++ b/venv/Lib/site-packages/markdown/serializers.py @@ -0,0 +1,189 @@ +# markdown/searializers.py +# +# Add x/html serialization to Elementree +# Taken from ElementTree 1.3 preview with slight modifications +# +# Copyright (c) 1999-2007 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# https://www.pythonware.com/ +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + + +from xml.etree.ElementTree import ProcessingInstruction +from xml.etree.ElementTree import Comment, ElementTree, QName +import re + +__all__ = ['to_html_string', 'to_xhtml_string'] + +HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", + "img", "input", "isindex", "link", "meta", "param") +RE_AMP = re.compile(r'&(?!(?:\#[0-9]+|\#x[0-9a-f]+|[0-9a-z]+);)', re.I) + +try: + HTML_EMPTY = set(HTML_EMPTY) +except NameError: # pragma: no cover + pass + + +def _raise_serialization_error(text): # pragma: no cover + raise TypeError( + "cannot serialize {!r} (type {})".format(text, type(text).__name__) + ) + + +def _escape_cdata(text): + # escape character data + try: + # it's worth avoiding do-nothing calls for strings that are + # shorter than 500 character, or so. assume that's, by far, + # the most common case in most applications. + if "&" in text: + # Only replace & when not part of an entity + text = RE_AMP.sub('&', text) + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + return text + except (TypeError, AttributeError): # pragma: no cover + _raise_serialization_error(text) + + +def _escape_attrib(text): + # escape attribute value + try: + if "&" in text: + # Only replace & when not part of an entity + text = RE_AMP.sub('&', text) + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + if "\n" in text: + text = text.replace("\n", " ") + return text + except (TypeError, AttributeError): # pragma: no cover + _raise_serialization_error(text) + + +def _escape_attrib_html(text): + # escape attribute value + try: + if "&" in text: + # Only replace & when not part of an entity + text = RE_AMP.sub('&', text) + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + return text + except (TypeError, AttributeError): # pragma: no cover + _raise_serialization_error(text) + + +def _serialize_html(write, elem, format): + tag = elem.tag + text = elem.text + if tag is Comment: + write("<!--%s-->" % _escape_cdata(text)) + elif tag is ProcessingInstruction: + write("<?%s?>" % _escape_cdata(text)) + elif tag is None: + if text: + write(_escape_cdata(text)) + for e in elem: + _serialize_html(write, e, format) + else: + namespace_uri = None + if isinstance(tag, QName): + # QNAME objects store their data as a string: `{uri}tag` + if tag.text[:1] == "{": + namespace_uri, tag = tag.text[1:].split("}", 1) + else: + raise ValueError('QName objects must define a tag.') + write("<" + tag) + items = elem.items() + if items: + items = sorted(items) # lexical order + for k, v in items: + if isinstance(k, QName): + # Assume a text only QName + k = k.text + if isinstance(v, QName): + # Assume a text only QName + v = v.text + else: + v = _escape_attrib_html(v) + if k == v and format == 'html': + # handle boolean attributes + write(" %s" % v) + else: + write(' {}="{}"'.format(k, v)) + if namespace_uri: + write(' xmlns="%s"' % (_escape_attrib(namespace_uri))) + if format == "xhtml" and tag.lower() in HTML_EMPTY: + write(" />") + else: + write(">") + if text: + if tag.lower() in ["script", "style"]: + write(text) + else: + write(_escape_cdata(text)) + for e in elem: + _serialize_html(write, e, format) + if tag.lower() not in HTML_EMPTY: + write("</" + tag + ">") + if elem.tail: + write(_escape_cdata(elem.tail)) + + +def _write_html(root, format="html"): + assert root is not None + data = [] + write = data.append + _serialize_html(write, root, format) + return "".join(data) + + +# -------------------------------------------------------------------- +# public functions + +def to_html_string(element): + return _write_html(ElementTree(element).getroot(), format="html") + + +def to_xhtml_string(element): + return _write_html(ElementTree(element).getroot(), format="xhtml") diff --git a/venv/Lib/site-packages/markdown/test_tools.py b/venv/Lib/site-packages/markdown/test_tools.py new file mode 100644 index 0000000..21ae1a7 --- /dev/null +++ b/venv/Lib/site-packages/markdown/test_tools.py @@ -0,0 +1,220 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import os +import sys +import unittest +import textwrap +from . import markdown, Markdown, util + +try: + import tidylib +except ImportError: + tidylib = None + +__all__ = ['TestCase', 'LegacyTestCase', 'Kwargs'] + + +class TestCase(unittest.TestCase): + """ + A unittest.TestCase subclass with helpers for testing Markdown output. + + Define `default_kwargs` as a dict of keywords to pass to Markdown for each + test. The defaults can be overridden on individual tests. + + The `assertMarkdownRenders` method accepts the source text, the expected + output, and any keywords to pass to Markdown. The `default_kwargs` are used + except where overridden by `kwargs`. The ouput and expected ouput are passed + to `TestCase.assertMultiLineEqual`. An AssertionError is raised with a diff + if the actual output does not equal the expected output. + + The `dedent` method is available to dedent triple-quoted strings if + necessary. + + In all other respects, behaves as unittest.TestCase. + """ + + default_kwargs = {} + + def assertMarkdownRenders(self, source, expected, expected_attrs=None, **kwargs): + """ + Test that source Markdown text renders to expected output with given keywords. + + `expected_attrs` accepts a dict. Each key should be the name of an attribute + on the `Markdown` instance and the value should be the expected value after + the source text is parsed by Markdown. After the expected output is tested, + the expected value for each attribute is compared against the actual + attribute of the `Markdown` instance using `TestCase.assertEqual`. + """ + + expected_attrs = expected_attrs or {} + kws = self.default_kwargs.copy() + kws.update(kwargs) + md = Markdown(**kws) + output = md.convert(source) + self.assertMultiLineEqual(output, expected) + for key, value in expected_attrs.items(): + self.assertEqual(getattr(md, key), value) + + def dedent(self, text): + """ + Dedent text. + """ + + # TODO: If/when actual output ends with a newline, then use: + # return textwrap.dedent(text.strip('/n')) + return textwrap.dedent(text).strip() + + +class recursionlimit: + """ + A context manager which temporarily modifies the Python recursion limit. + + The testing framework, coverage, etc. may add an arbitrary number of levels to the depth. To maintain consistency + in the tests, the current stack depth is determined when called, then added to the provided limit. + + Example usage: + + with recursionlimit(20): + # test code here + + See https://stackoverflow.com/a/50120316/866026 + """ + + def __init__(self, limit): + self.limit = util._get_stack_depth() + limit + self.old_limit = sys.getrecursionlimit() + + def __enter__(self): + sys.setrecursionlimit(self.limit) + + def __exit__(self, type, value, tb): + sys.setrecursionlimit(self.old_limit) + + +######################### +# Legacy Test Framework # +######################### + + +class Kwargs(dict): + """ A dict like class for holding keyword arguments. """ + pass + + +def _normalize_whitespace(text): + """ Normalize whitespace for a string of html using tidylib. """ + output, errors = tidylib.tidy_fragment(text, options={ + 'drop_empty_paras': 0, + 'fix_backslash': 0, + 'fix_bad_comments': 0, + 'fix_uri': 0, + 'join_styles': 0, + 'lower_literals': 0, + 'merge_divs': 0, + 'output_xhtml': 1, + 'quote_ampersand': 0, + 'newline': 'LF' + }) + return output + + +class LegacyTestMeta(type): + def __new__(cls, name, bases, dct): + + def generate_test(infile, outfile, normalize, kwargs): + def test(self): + with open(infile, encoding="utf-8") as f: + input = f.read() + with open(outfile, encoding="utf-8") as f: + # Normalize line endings + # (on Windows, git may have altered line endings). + expected = f.read().replace("\r\n", "\n") + output = markdown(input, **kwargs) + if tidylib and normalize: + try: + expected = _normalize_whitespace(expected) + output = _normalize_whitespace(output) + except OSError: + self.skipTest("Tidylib's c library not available.") + elif normalize: + self.skipTest('Tidylib not available.') + self.assertMultiLineEqual(output, expected) + return test + + location = dct.get('location', '') + exclude = dct.get('exclude', []) + normalize = dct.get('normalize', False) + input_ext = dct.get('input_ext', '.txt') + output_ext = dct.get('output_ext', '.html') + kwargs = dct.get('default_kwargs', Kwargs()) + + if os.path.isdir(location): + for file in os.listdir(location): + infile = os.path.join(location, file) + if os.path.isfile(infile): + tname, ext = os.path.splitext(file) + if ext == input_ext: + outfile = os.path.join(location, tname + output_ext) + tname = tname.replace(' ', '_').replace('-', '_') + kws = kwargs.copy() + if tname in dct: + kws.update(dct[tname]) + test_name = 'test_%s' % tname + if tname not in exclude: + dct[test_name] = generate_test(infile, outfile, normalize, kws) + else: + dct[test_name] = unittest.skip('Excluded')(lambda: None) + + return type.__new__(cls, name, bases, dct) + + +class LegacyTestCase(unittest.TestCase, metaclass=LegacyTestMeta): + """ + A `unittest.TestCase` subclass for running Markdown's legacy file-based tests. + + A subclass should define various properties which point to a directory of + text-based test files and define various behaviors/defaults for those tests. + The following properties are supported: + + location: A path to the directory fo test files. An absolute path is preferred. + exclude: A list of tests to exclude. Each test name should comprise the filename + without an extension. + normalize: A boolean value indicating if the HTML should be normalized. + Default: `False`. + input_ext: A string containing the file extension of input files. Default: `.txt`. + ouput_ext: A string containing the file extension of expected output files. + Default: `html`. + default_kwargs: A `Kwargs` instance which stores the default set of keyword + arguments for all test files in the directory. + + In addition, properties can be defined for each individual set of test files within + the directory. The property should be given the name of the file without the file + extension. Any spaces and dashes in the filename should be replaced with + underscores. The value of the property should be a `Kwargs` instance which + contains the keyword arguments that should be passed to `Markdown` for that + test file. The keyword arguments will "update" the `default_kwargs`. + + When the class instance is created, it will walk the given directory and create + a separate unitttest for each set of test files using the naming scheme: + `test_filename`. One unittest will be run for each set of input and output files. + """ + pass diff --git a/venv/Lib/site-packages/markdown/treeprocessors.py b/venv/Lib/site-packages/markdown/treeprocessors.py new file mode 100644 index 0000000..eb6bf41 --- /dev/null +++ b/venv/Lib/site-packages/markdown/treeprocessors.py @@ -0,0 +1,436 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import xml.etree.ElementTree as etree +from . import util +from . import inlinepatterns + + +def build_treeprocessors(md, **kwargs): + """ Build the default treeprocessors for Markdown. """ + treeprocessors = util.Registry() + treeprocessors.register(InlineProcessor(md), 'inline', 20) + treeprocessors.register(PrettifyTreeprocessor(md), 'prettify', 10) + return treeprocessors + + +def isString(s): + """ Check if it's string """ + if not isinstance(s, util.AtomicString): + return isinstance(s, str) + return False + + +class Treeprocessor(util.Processor): + """ + Treeprocessors are run on the ElementTree object before serialization. + + Each Treeprocessor implements a "run" method that takes a pointer to an + ElementTree, modifies it as necessary and returns an ElementTree + object. + + Treeprocessors must extend markdown.Treeprocessor. + + """ + def run(self, root): + """ + Subclasses of Treeprocessor should implement a `run` method, which + takes a root ElementTree. This method can return another ElementTree + object, and the existing root ElementTree will be replaced, or it can + modify the current tree and return None. + """ + pass # pragma: no cover + + +class InlineProcessor(Treeprocessor): + """ + A Treeprocessor that traverses a tree, applying inline patterns. + """ + + def __init__(self, md): + self.__placeholder_prefix = util.INLINE_PLACEHOLDER_PREFIX + self.__placeholder_suffix = util.ETX + self.__placeholder_length = 4 + len(self.__placeholder_prefix) \ + + len(self.__placeholder_suffix) + self.__placeholder_re = util.INLINE_PLACEHOLDER_RE + self.md = md + self.inlinePatterns = md.inlinePatterns + self.ancestors = [] + + @property + @util.deprecated("Use 'md' instead.") + def markdown(self): + # TODO: remove this later + return self.md + + def __makePlaceholder(self, type): + """ Generate a placeholder """ + id = "%04d" % len(self.stashed_nodes) + hash = util.INLINE_PLACEHOLDER % id + return hash, id + + def __findPlaceholder(self, data, index): + """ + Extract id from data string, start from index + + Keyword arguments: + + * data: string + * index: index, from which we start search + + Returns: placeholder id and string index, after the found placeholder. + + """ + m = self.__placeholder_re.search(data, index) + if m: + return m.group(1), m.end() + else: + return None, index + 1 + + def __stashNode(self, node, type): + """ Add node to stash """ + placeholder, id = self.__makePlaceholder(type) + self.stashed_nodes[id] = node + return placeholder + + def __handleInline(self, data, patternIndex=0): + """ + Process string with inline patterns and replace it + with placeholders + + Keyword arguments: + + * data: A line of Markdown text + * patternIndex: The index of the inlinePattern to start with + + Returns: String with placeholders. + + """ + if not isinstance(data, util.AtomicString): + startIndex = 0 + count = len(self.inlinePatterns) + while patternIndex < count: + data, matched, startIndex = self.__applyPattern( + self.inlinePatterns[patternIndex], data, patternIndex, startIndex + ) + if not matched: + patternIndex += 1 + return data + + def __processElementText(self, node, subnode, isText=True): + """ + Process placeholders in Element.text or Element.tail + of Elements popped from self.stashed_nodes. + + Keywords arguments: + + * node: parent node + * subnode: processing node + * isText: bool variable, True - it's text, False - it's tail + + Returns: None + + """ + if isText: + text = subnode.text + subnode.text = None + else: + text = subnode.tail + subnode.tail = None + + childResult = self.__processPlaceholders(text, subnode, isText) + + if not isText and node is not subnode: + pos = list(node).index(subnode) + 1 + else: + pos = 0 + + childResult.reverse() + for newChild in childResult: + node.insert(pos, newChild[0]) + + def __processPlaceholders(self, data, parent, isText=True): + """ + Process string with placeholders and generate ElementTree tree. + + Keyword arguments: + + * data: string with placeholders instead of ElementTree elements. + * parent: Element, which contains processing inline data + + Returns: list with ElementTree elements with applied inline patterns. + + """ + def linkText(text): + if text: + if result: + if result[-1][0].tail: + result[-1][0].tail += text + else: + result[-1][0].tail = text + elif not isText: + if parent.tail: + parent.tail += text + else: + parent.tail = text + else: + if parent.text: + parent.text += text + else: + parent.text = text + result = [] + strartIndex = 0 + while data: + index = data.find(self.__placeholder_prefix, strartIndex) + if index != -1: + id, phEndIndex = self.__findPlaceholder(data, index) + + if id in self.stashed_nodes: + node = self.stashed_nodes.get(id) + + if index > 0: + text = data[strartIndex:index] + linkText(text) + + if not isString(node): # it's Element + for child in [node] + list(node): + if child.tail: + if child.tail.strip(): + self.__processElementText( + node, child, False + ) + if child.text: + if child.text.strip(): + self.__processElementText(child, child) + else: # it's just a string + linkText(node) + strartIndex = phEndIndex + continue + + strartIndex = phEndIndex + result.append((node, self.ancestors[:])) + + else: # wrong placeholder + end = index + len(self.__placeholder_prefix) + linkText(data[strartIndex:end]) + strartIndex = end + else: + text = data[strartIndex:] + if isinstance(data, util.AtomicString): + # We don't want to loose the AtomicString + text = util.AtomicString(text) + linkText(text) + data = "" + + return result + + def __applyPattern(self, pattern, data, patternIndex, startIndex=0): + """ + Check if the line fits the pattern, create the necessary + elements, add it to stashed_nodes. + + Keyword arguments: + + * data: the text to be processed + * pattern: the pattern to be checked + * patternIndex: index of current pattern + * startIndex: string index, from which we start searching + + Returns: String with placeholders instead of ElementTree elements. + + """ + new_style = isinstance(pattern, inlinepatterns.InlineProcessor) + + for exclude in pattern.ANCESTOR_EXCLUDES: + if exclude.lower() in self.ancestors: + return data, False, 0 + + if new_style: + match = None + # Since handleMatch may reject our first match, + # we iterate over the buffer looking for matches + # until we can't find any more. + for match in pattern.getCompiledRegExp().finditer(data, startIndex): + node, start, end = pattern.handleMatch(match, data) + if start is None or end is None: + startIndex += match.end(0) + match = None + continue + break + else: # pragma: no cover + match = pattern.getCompiledRegExp().match(data[startIndex:]) + leftData = data[:startIndex] + + if not match: + return data, False, 0 + + if not new_style: # pragma: no cover + node = pattern.handleMatch(match) + start = match.start(0) + end = match.end(0) + + if node is None: + return data, True, end + + if not isString(node): + if not isinstance(node.text, util.AtomicString): + # We need to process current node too + for child in [node] + list(node): + if not isString(node): + if child.text: + self.ancestors.append(child.tag.lower()) + child.text = self.__handleInline( + child.text, patternIndex + 1 + ) + self.ancestors.pop() + if child.tail: + child.tail = self.__handleInline( + child.tail, patternIndex + ) + + placeholder = self.__stashNode(node, pattern.type()) + + if new_style: + return "{}{}{}".format(data[:start], + placeholder, data[end:]), True, 0 + else: # pragma: no cover + return "{}{}{}{}".format(leftData, + match.group(1), + placeholder, match.groups()[-1]), True, 0 + + def __build_ancestors(self, parent, parents): + """Build the ancestor list.""" + ancestors = [] + while parent is not None: + if parent is not None: + ancestors.append(parent.tag.lower()) + parent = self.parent_map.get(parent) + ancestors.reverse() + parents.extend(ancestors) + + def run(self, tree, ancestors=None): + """Apply inline patterns to a parsed Markdown tree. + + Iterate over ElementTree, find elements with inline tag, apply inline + patterns and append newly created Elements to tree. If you don't + want to process your data with inline paterns, instead of normal + string, use subclass AtomicString: + + node.text = markdown.AtomicString("This will not be processed.") + + Arguments: + + * tree: ElementTree object, representing Markdown tree. + * ancestors: List of parent tag names that precede the tree node (if needed). + + Returns: ElementTree object with applied inline patterns. + + """ + self.stashed_nodes = {} + + # Ensure a valid parent list, but copy passed in lists + # to ensure we don't have the user accidentally change it on us. + tree_parents = [] if ancestors is None else ancestors[:] + + self.parent_map = {c: p for p in tree.iter() for c in p} + stack = [(tree, tree_parents)] + + while stack: + currElement, parents = stack.pop() + + self.ancestors = parents + self.__build_ancestors(currElement, self.ancestors) + + insertQueue = [] + for child in currElement: + if child.text and not isinstance( + child.text, util.AtomicString + ): + self.ancestors.append(child.tag.lower()) + text = child.text + child.text = None + lst = self.__processPlaceholders( + self.__handleInline(text), child + ) + for item in lst: + self.parent_map[item[0]] = child + stack += lst + insertQueue.append((child, lst)) + self.ancestors.pop() + if child.tail: + tail = self.__handleInline(child.tail) + dumby = etree.Element('d') + child.tail = None + tailResult = self.__processPlaceholders(tail, dumby, False) + if dumby.tail: + child.tail = dumby.tail + pos = list(currElement).index(child) + 1 + tailResult.reverse() + for newChild in tailResult: + self.parent_map[newChild[0]] = currElement + currElement.insert(pos, newChild[0]) + if len(child): + self.parent_map[child] = currElement + stack.append((child, self.ancestors[:])) + + for element, lst in insertQueue: + for i, obj in enumerate(lst): + newChild = obj[0] + element.insert(i, newChild) + return tree + + +class PrettifyTreeprocessor(Treeprocessor): + """ Add linebreaks to the html document. """ + + def _prettifyETree(self, elem): + """ Recursively add linebreaks to ElementTree children. """ + + i = "\n" + if self.md.is_block_level(elem.tag) and elem.tag not in ['code', 'pre']: + if (not elem.text or not elem.text.strip()) \ + and len(elem) and self.md.is_block_level(elem[0].tag): + elem.text = i + for e in elem: + if self.md.is_block_level(e.tag): + self._prettifyETree(e) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + if not elem.tail or not elem.tail.strip(): + elem.tail = i + + def run(self, root): + """ Add linebreaks to ElementTree root object. """ + + self._prettifyETree(root) + # Do <br />'s separately as they are often in the middle of + # inline content and missed by _prettifyETree. + brs = root.iter('br') + for br in brs: + if not br.tail or not br.tail.strip(): + br.tail = '\n' + else: + br.tail = '\n%s' % br.tail + # Clean up extra empty lines at end of code blocks. + pres = root.iter('pre') + for pre in pres: + if len(pre) and pre[0].tag == 'code': + pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n') diff --git a/venv/Lib/site-packages/markdown/util.py b/venv/Lib/site-packages/markdown/util.py new file mode 100644 index 0000000..98cfbf7 --- /dev/null +++ b/venv/Lib/site-packages/markdown/util.py @@ -0,0 +1,485 @@ +""" +Python Markdown + +A Python implementation of John Gruber's Markdown. + +Documentation: https://python-markdown.github.io/ +GitHub: https://github.com/Python-Markdown/markdown/ +PyPI: https://pypi.org/project/Markdown/ + +Started by Manfred Stienstra (http://www.dwerg.net/). +Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). +Currently maintained by Waylan Limberg (https://github.com/waylan), +Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). + +Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see LICENSE.md for details). +""" + +import re +import sys +import warnings +import xml.etree.ElementTree +from collections import namedtuple +from functools import wraps +from itertools import count + +from .pep562 import Pep562 + +if sys.version_info >= (3, 10): + from importlib import metadata +else: + # <PY310 use backport + import importlib_metadata as metadata + +PY37 = (3, 7) <= sys.version_info + + +# TODO: Remove deprecated variables in a future release. +__deprecated__ = { + 'etree': ('xml.etree.ElementTree', xml.etree.ElementTree), + 'string_type': ('str', str), + 'text_type': ('str', str), + 'int2str': ('chr', chr), + 'iterrange': ('range', range) +} + + +""" +Constants you might want to modify +----------------------------------------------------------------------------- +""" + + +BLOCK_LEVEL_ELEMENTS = [ + # Elements which are invalid to wrap in a `<p>` tag. + # See https://w3c.github.io/html/grouping-content.html#the-p-element + 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', + 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', + 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul', + # Other elements which Markdown should not be mucking up the contents of. + 'canvas', 'colgroup', 'dd', 'body', 'dt', 'group', 'iframe', 'li', 'legend', + 'math', 'map', 'noscript', 'output', 'object', 'option', 'progress', 'script', + 'style', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'video' +] + +# Placeholders +STX = '\u0002' # Use STX ("Start of text") for start-of-placeholder +ETX = '\u0003' # Use ETX ("End of text") for end-of-placeholder +INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:" +INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX +INLINE_PLACEHOLDER_RE = re.compile(INLINE_PLACEHOLDER % r'([0-9]+)') +AMP_SUBSTITUTE = STX+"amp"+ETX +HTML_PLACEHOLDER = STX + "wzxhzdk:%s" + ETX +HTML_PLACEHOLDER_RE = re.compile(HTML_PLACEHOLDER % r'([0-9]+)') +TAG_PLACEHOLDER = STX + "hzzhzkh:%s" + ETX + + +""" +Constants you probably do not need to change +----------------------------------------------------------------------------- +""" + +# Only load extension entry_points once. +INSTALLED_EXTENSIONS = metadata.entry_points(group='markdown.extensions') +RTL_BIDI_RANGES = ( + ('\u0590', '\u07FF'), + # Hebrew (0590-05FF), Arabic (0600-06FF), + # Syriac (0700-074F), Arabic supplement (0750-077F), + # Thaana (0780-07BF), Nko (07C0-07FF). + ('\u2D30', '\u2D7F') # Tifinagh +) + + +""" +AUXILIARY GLOBAL FUNCTIONS +============================================================================= +""" + + +def deprecated(message, stacklevel=2): + """ + Raise a DeprecationWarning when wrapped function/method is called. + + Usage: + @deprecated("This method will be removed in version X; use Y instead.") + def some_method()" + pass + """ + def wrapper(func): + @wraps(func) + def deprecated_func(*args, **kwargs): + warnings.warn( + f"'{func.__name__}' is deprecated. {message}", + category=DeprecationWarning, + stacklevel=stacklevel + ) + return func(*args, **kwargs) + return deprecated_func + return wrapper + + +@deprecated("Use 'Markdown.is_block_level' instead.") +def isBlockLevel(tag): + """Check if the tag is a block level HTML tag.""" + if isinstance(tag, str): + return tag.lower().rstrip('/') in BLOCK_LEVEL_ELEMENTS + # Some ElementTree tags are not strings, so return False. + return False + + +def parseBoolValue(value, fail_on_errors=True, preserve_none=False): + """Parses a string representing bool value. If parsing was successful, + returns True or False. If preserve_none=True, returns True, False, + or None. If parsing was not successful, raises ValueError, or, if + fail_on_errors=False, returns None.""" + if not isinstance(value, str): + if preserve_none and value is None: + return value + return bool(value) + elif preserve_none and value.lower() == 'none': + return None + elif value.lower() in ('true', 'yes', 'y', 'on', '1'): + return True + elif value.lower() in ('false', 'no', 'n', 'off', '0', 'none'): + return False + elif fail_on_errors: + raise ValueError('Cannot parse bool value: %r' % value) + + +def code_escape(text): + """Escape code.""" + if "&" in text: + text = text.replace("&", "&") + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + return text + + +def _get_stack_depth(size=2): + """Get current stack depth, performantly. + """ + frame = sys._getframe(size) + + for size in count(size): + frame = frame.f_back + if not frame: + return size + + +def nearing_recursion_limit(): + """Return true if current stack depth is withing 100 of maximum limit.""" + return sys.getrecursionlimit() - _get_stack_depth() < 100 + + +""" +MISC AUXILIARY CLASSES +============================================================================= +""" + + +class AtomicString(str): + """A string which should not be further processed.""" + pass + + +class Processor: + def __init__(self, md=None): + self.md = md + + @property + @deprecated("Use 'md' instead.") + def markdown(self): + # TODO: remove this later + return self.md + + +class HtmlStash: + """ + This class is used for stashing HTML objects that we extract + in the beginning and replace with place-holders. + """ + + def __init__(self): + """ Create a HtmlStash. """ + self.html_counter = 0 # for counting inline html segments + self.rawHtmlBlocks = [] + self.tag_counter = 0 + self.tag_data = [] # list of dictionaries in the order tags appear + + def store(self, html): + """ + Saves an HTML segment for later reinsertion. Returns a + placeholder string that needs to be inserted into the + document. + + Keyword arguments: + + * html: an html segment + + Returns : a placeholder string + + """ + self.rawHtmlBlocks.append(html) + placeholder = self.get_placeholder(self.html_counter) + self.html_counter += 1 + return placeholder + + def reset(self): + self.html_counter = 0 + self.rawHtmlBlocks = [] + + def get_placeholder(self, key): + return HTML_PLACEHOLDER % key + + def store_tag(self, tag, attrs, left_index, right_index): + """Store tag data and return a placeholder.""" + self.tag_data.append({'tag': tag, 'attrs': attrs, + 'left_index': left_index, + 'right_index': right_index}) + placeholder = TAG_PLACEHOLDER % str(self.tag_counter) + self.tag_counter += 1 # equal to the tag's index in self.tag_data + return placeholder + + +# Used internally by `Registry` for each item in its sorted list. +# Provides an easier to read API when editing the code later. +# For example, `item.name` is more clear than `item[0]`. +_PriorityItem = namedtuple('PriorityItem', ['name', 'priority']) + + +class Registry: + """ + A priority sorted registry. + + A `Registry` instance provides two public methods to alter the data of the + registry: `register` and `deregister`. Use `register` to add items and + `deregister` to remove items. See each method for specifics. + + When registering an item, a "name" and a "priority" must be provided. All + items are automatically sorted by "priority" from highest to lowest. The + "name" is used to remove ("deregister") and get items. + + A `Registry` instance it like a list (which maintains order) when reading + data. You may iterate over the items, get an item and get a count (length) + of all items. You may also check that the registry contains an item. + + When getting an item you may use either the index of the item or the + string-based "name". For example: + + registry = Registry() + registry.register(SomeItem(), 'itemname', 20) + # Get the item by index + item = registry[0] + # Get the item by name + item = registry['itemname'] + + When checking that the registry contains an item, you may use either the + string-based "name", or a reference to the actual item. For example: + + someitem = SomeItem() + registry.register(someitem, 'itemname', 20) + # Contains the name + assert 'itemname' in registry + # Contains the item instance + assert someitem in registry + + The method `get_index_for_name` is also available to obtain the index of + an item using that item's assigned "name". + """ + + def __init__(self): + self._data = {} + self._priority = [] + self._is_sorted = False + + def __contains__(self, item): + if isinstance(item, str): + # Check if an item exists by this name. + return item in self._data.keys() + # Check if this instance exists. + return item in self._data.values() + + def __iter__(self): + self._sort() + return iter([self._data[k] for k, p in self._priority]) + + def __getitem__(self, key): + self._sort() + if isinstance(key, slice): + data = Registry() + for k, p in self._priority[key]: + data.register(self._data[k], k, p) + return data + if isinstance(key, int): + return self._data[self._priority[key].name] + return self._data[key] + + def __len__(self): + return len(self._priority) + + def __repr__(self): + return '<{}({})>'.format(self.__class__.__name__, list(self)) + + def get_index_for_name(self, name): + """ + Return the index of the given name. + """ + if name in self: + self._sort() + return self._priority.index( + [x for x in self._priority if x.name == name][0] + ) + raise ValueError('No item named "{}" exists.'.format(name)) + + def register(self, item, name, priority): + """ + Add an item to the registry with the given name and priority. + + Parameters: + + * `item`: The item being registered. + * `name`: A string used to reference the item. + * `priority`: An integer or float used to sort against all items. + + If an item is registered with a "name" which already exists, the + existing item is replaced with the new item. Tread carefully as the + old item is lost with no way to recover it. The new item will be + sorted according to its priority and will **not** retain the position + of the old item. + """ + if name in self: + # Remove existing item of same name first + self.deregister(name) + self._is_sorted = False + self._data[name] = item + self._priority.append(_PriorityItem(name, priority)) + + def deregister(self, name, strict=True): + """ + Remove an item from the registry. + + Set `strict=False` to fail silently. + """ + try: + index = self.get_index_for_name(name) + del self._priority[index] + del self._data[name] + except ValueError: + if strict: + raise + + def _sort(self): + """ + Sort the registry by priority from highest to lowest. + + This method is called internally and should never be explicitly called. + """ + if not self._is_sorted: + self._priority.sort(key=lambda item: item.priority, reverse=True) + self._is_sorted = True + + # Deprecated Methods which provide a smooth transition from OrderedDict + + def __setitem__(self, key, value): + """ Register item with priorty 5 less than lowest existing priority. """ + if isinstance(key, str): + warnings.warn( + 'Using setitem to register a processor or pattern is deprecated. ' + 'Use the `register` method instead.', + DeprecationWarning, + stacklevel=2, + ) + if key in self: + # Key already exists, replace without altering priority + self._data[key] = value + return + if len(self) == 0: + # This is the first item. Set priority to 50. + priority = 50 + else: + self._sort() + priority = self._priority[-1].priority - 5 + self.register(value, key, priority) + else: + raise TypeError + + def __delitem__(self, key): + """ Deregister an item by name. """ + if key in self: + self.deregister(key) + warnings.warn( + 'Using del to remove a processor or pattern is deprecated. ' + 'Use the `deregister` method instead.', + DeprecationWarning, + stacklevel=2, + ) + else: + raise KeyError('Cannot delete key {}, not registered.'.format(key)) + + def add(self, key, value, location): + """ Register a key by location. """ + if len(self) == 0: + # This is the first item. Set priority to 50. + priority = 50 + elif location == '_begin': + self._sort() + # Set priority 5 greater than highest existing priority + priority = self._priority[0].priority + 5 + elif location == '_end': + self._sort() + # Set priority 5 less than lowest existing priority + priority = self._priority[-1].priority - 5 + elif location.startswith('<') or location.startswith('>'): + # Set priority halfway between existing priorities. + i = self.get_index_for_name(location[1:]) + if location.startswith('<'): + after = self._priority[i].priority + if i > 0: + before = self._priority[i-1].priority + else: + # Location is first item` + before = after + 10 + else: + # location.startswith('>') + before = self._priority[i].priority + if i < len(self) - 1: + after = self._priority[i+1].priority + else: + # location is last item + after = before - 10 + priority = before - ((before - after) / 2) + else: + raise ValueError('Not a valid location: "%s". Location key ' + 'must start with a ">" or "<".' % location) + self.register(value, key, priority) + warnings.warn( + 'Using the add method to register a processor or pattern is deprecated. ' + 'Use the `register` method instead.', + DeprecationWarning, + stacklevel=2, + ) + + +def __getattr__(name): + """Get attribute.""" + + deprecated = __deprecated__.get(name) + if deprecated: + warnings.warn( + "'{}' is deprecated. Use '{}' instead.".format(name, deprecated[0]), + category=DeprecationWarning, + stacklevel=(3 if PY37 else 4) + ) + return deprecated[1] + raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name)) + + +if not PY37: + Pep562(__name__) diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/INSTALLER b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/LICENSE b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/METADATA b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/METADATA new file mode 100644 index 0000000..50821f2 --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/METADATA @@ -0,0 +1,134 @@ +Metadata-Version: 2.1 +Name: mysqlclient +Version: 2.1.0 +Summary: Python interface to MySQL +Home-page: https://github.com/PyMySQL/mysqlclient +Author: Inada Naoki +Author-email: songofacandy@gmail.com +License: GPL +Platform: ALL +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Other Environment +Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Database +Classifier: Topic :: Database :: Database Engines/Servers +Requires-Python: >=3.5 +Description-Content-Type: text/markdown +License-File: LICENSE + +# mysqlclient + +This project is a fork of [MySQLdb1](https://github.com/farcepest/MySQLdb1). +This project adds Python 3 support and fixed many bugs. + +* PyPI: https://pypi.org/project/mysqlclient/ +* GitHub: https://github.com/PyMySQL/mysqlclient + + +## Support + +**Do Not use Github Issue Tracker to ask help. OSS Maintainer is not free tech support** + +When your question looks relating to Python rather than MySQL: + +* Python mailing list [python-list](https://mail.python.org/mailman/listinfo/python-list) +* Slack [pythondev.slack.com](https://pyslackers.com/web/slack) + +Or when you have question about MySQL: + +* [MySQL Community on Slack](https://lefred.be/mysql-community-on-slack/) + + +## Install + +### Windows + +Building mysqlclient on Windows is very hard. +But there are some binary wheels you can install easily. + +If binary wheels do not exist for your version of Python, it may be possible to +build from source, but if this does not work, **do not come asking for support.** +To build from source, download the +[MariaDB C Connector](https://mariadb.com/downloads/#connectors) and install +it. It must be installed in the default location +(usually "C:\Program Files\MariaDB\MariaDB Connector C" or +"C:\Program Files (x86)\MariaDB\MariaDB Connector C" for 32-bit). If you +build the connector yourself or install it in a different location, set the +environment variable `MYSQLCLIENT_CONNECTOR` before installing. Once you have +the connector installed and an appropriate version of Visual Studio for your +version of Python: + +``` +$ pip install mysqlclient +``` + +### macOS (Homebrew) + +Install MySQL and mysqlclient: + +``` +# Assume you are activating Python 3 venv +$ brew install mysql +$ pip install mysqlclient +``` + +If you don't want to install MySQL server, you can use mysql-client instead: + +``` +# Assume you are activating Python 3 venv +$ brew install mysql-client +$ echo 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile +$ export PATH="/usr/local/opt/mysql-client/bin:$PATH" +$ pip install mysqlclient +``` + +### Linux + +**Note that this is a basic step. I can not support complete step for build for all +environment. If you can see some error, you should fix it by yourself, or ask for +support in some user forum. Don't file a issue on the issue tracker.** + +You may need to install the Python 3 and MySQL development headers and libraries like so: + +* `$ sudo apt-get install python3-dev default-libmysqlclient-dev build-essential` # Debian / Ubuntu +* `% sudo yum install python3-devel mysql-devel` # Red Hat / CentOS + +Then you can install mysqlclient via pip now: + +``` +$ pip install mysqlclient +``` + +### Customize build (POSIX) + +mysqlclient uses `mysql_config` or `mariadb_config` by default for finding +compiler/linker flags. + +You can use `MYSQLCLIENT_CFLAGS` and `MYSQLCLIENT_LDFLAGS` environment +variables to customize compiler/linker options. + +``` +$ export MYSQLCLIENT_CFLAGS=`pkg-config mysqlclient --cflags` +$ export MYSQLCLIENT_LDFLAGS=`pkg-config mysqlclient --libs` +$ pip install mysqlclient +``` + +### Documentation + +Documentation is hosted on [Read The Docs](https://mysqlclient.readthedocs.io/) + + diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/RECORD b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/RECORD new file mode 100644 index 0000000..4d31acf --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/RECORD @@ -0,0 +1,34 @@ +MySQLdb/__init__.py,sha256=1dT2l-Bus3V4GmSgTsP6fjJrLwKHYj6-5TbQtkTWkbU,3618 +MySQLdb/__pycache__/__init__.cpython-39.pyc,, +MySQLdb/__pycache__/_exceptions.cpython-39.pyc,, +MySQLdb/__pycache__/connections.cpython-39.pyc,, +MySQLdb/__pycache__/converters.cpython-39.pyc,, +MySQLdb/__pycache__/cursors.cpython-39.pyc,, +MySQLdb/__pycache__/release.cpython-39.pyc,, +MySQLdb/__pycache__/times.cpython-39.pyc,, +MySQLdb/_exceptions.py,sha256=akUFoN0iU1oVSTbEQgE_q4D8HqE5aCvGAe96aGE1JOk,2233 +MySQLdb/_mysql.cp39-win_amd64.pyd,sha256=346K9qUKTa95ZH1ibkrTx9ElORJA-b51l2UjFhDuB1Y,311808 +MySQLdb/connections.py,sha256=576qoAl_Hn_G157jW8-lUPQxc3O85UeHpqoKZCbY8pM,11693 +MySQLdb/constants/CLIENT.py,sha256=fKfOF8ABsQw_Uzn4NDOjOOT5kr1rYCEhW_0ROXiYLDQ,693 +MySQLdb/constants/CR.py,sha256=m3AIucHstnMD9Ojx_287mitkk_dqppkjvnTW-jwYJwg,2933 +MySQLdb/constants/ER.py,sha256=xAYVvxtNJqxF_h_oZJocOb7TA3EiLF7FVIDmrPBOwv8,25715 +MySQLdb/constants/FIELD_TYPE.py,sha256=d_D4WRBz5Pq2gPzbCP-i30HML-Kg5rUSaMIIJa_pz90,606 +MySQLdb/constants/FLAG.py,sha256=8e9XrHeqrcU1Ns9ZP7oXs3lkfBVozNmFAo-d_zrHswY,386 +MySQLdb/constants/__init__.py,sha256=spOtrTWi4_bwend6KO5LyKnEpzEdbJAO05Pp1xGC4yc,56 +MySQLdb/constants/__pycache__/CLIENT.cpython-39.pyc,, +MySQLdb/constants/__pycache__/CR.cpython-39.pyc,, +MySQLdb/constants/__pycache__/ER.cpython-39.pyc,, +MySQLdb/constants/__pycache__/FIELD_TYPE.cpython-39.pyc,, +MySQLdb/constants/__pycache__/FLAG.cpython-39.pyc,, +MySQLdb/constants/__pycache__/__init__.cpython-39.pyc,, +MySQLdb/converters.py,sha256=-Pv7IaSKsZ7WiHjS5CQydktre2Mq0Dfp9ql3TA2L6RA,3583 +MySQLdb/cursors.py,sha256=nO8K8tszehWv-IowSNuCl8UiM6ER6N_phOYCfr3z-xk,16000 +MySQLdb/release.py,sha256=9gRxtIsG3wn2mDa1weltRqIHRS9PbBi3Ijwb6uwmjEs,112 +MySQLdb/times.py,sha256=lVgfMR_LUQCyjj5SDf9pbEAA2J07FOnnC0XANkpr5JM,3754 +mysqlclient-2.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mysqlclient-2.1.0.dist-info/LICENSE,sha256=GJsa-V1mEVHgVM6hDJGz11Tk3k0_7PsHTB-ylHb3Fns,18431 +mysqlclient-2.1.0.dist-info/METADATA,sha256=hHcHxpnyxaAIFDtHvGdYD2E187ip6F5yCKP5kmO0opA,4367 +mysqlclient-2.1.0.dist-info/RECORD,, +mysqlclient-2.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mysqlclient-2.1.0.dist-info/WHEEL,sha256=MRZ_p7RU4olp1XL4U2EYLkuYikriaVRqXBoKVLH_OSE,100 +mysqlclient-2.1.0.dist-info/top_level.txt,sha256=bbG3jvCAFflitTN3RgHQAvYW9On_HHb4lJtvx0DHtEA,8 diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/REQUESTED b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/WHEEL b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/WHEEL new file mode 100644 index 0000000..2cc9fa7 --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/top_level.txt b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..4b05b1c --- /dev/null +++ b/venv/Lib/site-packages/mysqlclient-2.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +MySQLdb diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/INSTALLER b/venv/Lib/site-packages/pytz-2022.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/LICENSE.txt b/venv/Lib/site-packages/pytz-2022.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..5f1c112 --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2003-2019 Stuart Bishop <stuart@stuartbishop.net> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/METADATA b/venv/Lib/site-packages/pytz-2022.1.dist-info/METADATA new file mode 100644 index 0000000..625dd74 --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/METADATA @@ -0,0 +1,635 @@ +Metadata-Version: 2.1 +Name: pytz +Version: 2022.1 +Summary: World timezone definitions, modern and historical +Home-page: http://pythonhosted.org/pytz +Author: Stuart Bishop +Author-email: stuart@stuartbishop.net +Maintainer: Stuart Bishop +Maintainer-email: stuart@stuartbishop.net +License: MIT +Download-URL: https://pypi.org/project/pytz/ +Keywords: timezone,tzinfo,datetime,olson,time +Platform: Independent +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development :: Libraries :: Python Modules + +pytz - World Timezone Definitions for Python +============================================ + +:Author: Stuart Bishop <stuart@stuartbishop.net> + +Introduction +~~~~~~~~~~~~ + +pytz brings the Olson tz database into Python. This library allows +accurate and cross platform timezone calculations using Python 2.4 +or higher. It also solves the issue of ambiguous times at the end +of daylight saving time, which you can read more about in the Python +Library Reference (``datetime.tzinfo``). + +Almost all of the Olson timezones are supported. + +.. note:: + + This library differs from the documented Python API for + tzinfo implementations; if you want to create local wallclock + times you need to use the ``localize()`` method documented in this + document. In addition, if you perform date arithmetic on local + times that cross DST boundaries, the result may be in an incorrect + timezone (ie. subtract 1 minute from 2002-10-27 1:00 EST and you get + 2002-10-27 0:59 EST instead of the correct 2002-10-27 1:59 EDT). A + ``normalize()`` method is provided to correct this. Unfortunately these + issues cannot be resolved without modifying the Python datetime + implementation (see PEP-431). + + +Installation +~~~~~~~~~~~~ + +This package can either be installed using ``pip`` or from a tarball using the +standard Python distutils. + +If you are installing using ``pip``, you don't need to download anything as the +latest version will be downloaded for you from PyPI:: + + pip install pytz + +If you are installing from a tarball, run the following command as an +administrative user:: + + python setup.py install + + +pytz for Enterprise +~~~~~~~~~~~~~~~~~~~ + +Available as part of the Tidelift Subscription. + +The maintainers of pytz and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. `Learn more. <https://tidelift.com/subscription/pkg/pypi-pytz?utm_source=pypi-pytz&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`_. + + +Example & Usage +~~~~~~~~~~~~~~~ + +Localized times and date arithmetic +----------------------------------- + +>>> from datetime import datetime, timedelta +>>> from pytz import timezone +>>> import pytz +>>> utc = pytz.utc +>>> utc.zone +'UTC' +>>> eastern = timezone('US/Eastern') +>>> eastern.zone +'US/Eastern' +>>> amsterdam = timezone('Europe/Amsterdam') +>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z' + +This library only supports two ways of building a localized time. The +first is to use the ``localize()`` method provided by the pytz library. +This is used to localize a naive datetime (datetime with no timezone +information): + +>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0)) +>>> print(loc_dt.strftime(fmt)) +2002-10-27 06:00:00 EST-0500 + +The second way of building a localized time is by converting an existing +localized time using the standard ``astimezone()`` method: + +>>> ams_dt = loc_dt.astimezone(amsterdam) +>>> ams_dt.strftime(fmt) +'2002-10-27 12:00:00 CET+0100' + +Unfortunately using the tzinfo argument of the standard datetime +constructors ''does not work'' with pytz for many timezones. + +>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) # /!\ Does not work this way! +'2002-10-27 12:00:00 LMT+0020' + +It is safe for timezones without daylight saving transitions though, such +as UTC: + +>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) # /!\ Not recommended except for UTC +'2002-10-27 12:00:00 UTC+0000' + +The preferred way of dealing with times is to always work in UTC, +converting to localtime only when generating output to be read +by humans. + +>>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) +>>> loc_dt = utc_dt.astimezone(eastern) +>>> loc_dt.strftime(fmt) +'2002-10-27 01:00:00 EST-0500' + +This library also allows you to do date arithmetic using local +times, although it is more complicated than working in UTC as you +need to use the ``normalize()`` method to handle daylight saving time +and other timezone transitions. In this example, ``loc_dt`` is set +to the instant when daylight saving time ends in the US/Eastern +timezone. + +>>> before = loc_dt - timedelta(minutes=10) +>>> before.strftime(fmt) +'2002-10-27 00:50:00 EST-0500' +>>> eastern.normalize(before).strftime(fmt) +'2002-10-27 01:50:00 EDT-0400' +>>> after = eastern.normalize(before + timedelta(minutes=20)) +>>> after.strftime(fmt) +'2002-10-27 01:10:00 EST-0500' + +Creating local times is also tricky, and the reason why working with +local times is not recommended. Unfortunately, you cannot just pass +a ``tzinfo`` argument when constructing a datetime (see the next +section for more details) + +>>> dt = datetime(2002, 10, 27, 1, 30, 0) +>>> dt1 = eastern.localize(dt, is_dst=True) +>>> dt1.strftime(fmt) +'2002-10-27 01:30:00 EDT-0400' +>>> dt2 = eastern.localize(dt, is_dst=False) +>>> dt2.strftime(fmt) +'2002-10-27 01:30:00 EST-0500' + +Converting between timezones is more easily done, using the +standard astimezone method. + +>>> utc_dt = utc.localize(datetime.utcfromtimestamp(1143408899)) +>>> utc_dt.strftime(fmt) +'2006-03-26 21:34:59 UTC+0000' +>>> au_tz = timezone('Australia/Sydney') +>>> au_dt = utc_dt.astimezone(au_tz) +>>> au_dt.strftime(fmt) +'2006-03-27 08:34:59 AEDT+1100' +>>> utc_dt2 = au_dt.astimezone(utc) +>>> utc_dt2.strftime(fmt) +'2006-03-26 21:34:59 UTC+0000' +>>> utc_dt == utc_dt2 +True + +You can take shortcuts when dealing with the UTC side of timezone +conversions. ``normalize()`` and ``localize()`` are not really +necessary when there are no daylight saving time transitions to +deal with. + +>>> utc_dt = datetime.utcfromtimestamp(1143408899).replace(tzinfo=utc) +>>> utc_dt.strftime(fmt) +'2006-03-26 21:34:59 UTC+0000' +>>> au_tz = timezone('Australia/Sydney') +>>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz)) +>>> au_dt.strftime(fmt) +'2006-03-27 08:34:59 AEDT+1100' +>>> utc_dt2 = au_dt.astimezone(utc) +>>> utc_dt2.strftime(fmt) +'2006-03-26 21:34:59 UTC+0000' + + +``tzinfo`` API +-------------- + +The ``tzinfo`` instances returned by the ``timezone()`` function have +been extended to cope with ambiguous times by adding an ``is_dst`` +parameter to the ``utcoffset()``, ``dst()`` && ``tzname()`` methods. + +>>> tz = timezone('America/St_Johns') + +>>> normal = datetime(2009, 9, 1) +>>> ambiguous = datetime(2009, 10, 31, 23, 30) + +The ``is_dst`` parameter is ignored for most timestamps. It is only used +during DST transition ambiguous periods to resolve that ambiguity. + +>>> print(tz.utcoffset(normal, is_dst=True)) +-1 day, 21:30:00 +>>> print(tz.dst(normal, is_dst=True)) +1:00:00 +>>> tz.tzname(normal, is_dst=True) +'NDT' + +>>> print(tz.utcoffset(ambiguous, is_dst=True)) +-1 day, 21:30:00 +>>> print(tz.dst(ambiguous, is_dst=True)) +1:00:00 +>>> tz.tzname(ambiguous, is_dst=True) +'NDT' + +>>> print(tz.utcoffset(normal, is_dst=False)) +-1 day, 21:30:00 +>>> tz.dst(normal, is_dst=False).seconds +3600 +>>> tz.tzname(normal, is_dst=False) +'NDT' + +>>> print(tz.utcoffset(ambiguous, is_dst=False)) +-1 day, 20:30:00 +>>> tz.dst(ambiguous, is_dst=False) +datetime.timedelta(0) +>>> tz.tzname(ambiguous, is_dst=False) +'NST' + +If ``is_dst`` is not specified, ambiguous timestamps will raise +an ``pytz.exceptions.AmbiguousTimeError`` exception. + +>>> print(tz.utcoffset(normal)) +-1 day, 21:30:00 +>>> print(tz.dst(normal)) +1:00:00 +>>> tz.tzname(normal) +'NDT' + +>>> import pytz.exceptions +>>> try: +... tz.utcoffset(ambiguous) +... except pytz.exceptions.AmbiguousTimeError: +... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) +pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00 +>>> try: +... tz.dst(ambiguous) +... except pytz.exceptions.AmbiguousTimeError: +... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) +pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00 +>>> try: +... tz.tzname(ambiguous) +... except pytz.exceptions.AmbiguousTimeError: +... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) +pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00 + + +Problems with Localtime +~~~~~~~~~~~~~~~~~~~~~~~ + +The major problem we have to deal with is that certain datetimes +may occur twice in a year. For example, in the US/Eastern timezone +on the last Sunday morning in October, the following sequence +happens: + + - 01:00 EDT occurs + - 1 hour later, instead of 2:00am the clock is turned back 1 hour + and 01:00 happens again (this time 01:00 EST) + +In fact, every instant between 01:00 and 02:00 occurs twice. This means +that if you try and create a time in the 'US/Eastern' timezone +the standard datetime syntax, there is no way to specify if you meant +before of after the end-of-daylight-saving-time transition. Using the +pytz custom syntax, the best you can do is make an educated guess: + +>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 1, 30, 00)) +>>> loc_dt.strftime(fmt) +'2002-10-27 01:30:00 EST-0500' + +As you can see, the system has chosen one for you and there is a 50% +chance of it being out by one hour. For some applications, this does +not matter. However, if you are trying to schedule meetings with people +in different timezones or analyze log files it is not acceptable. + +The best and simplest solution is to stick with using UTC. The pytz +package encourages using UTC for internal timezone representation by +including a special UTC implementation based on the standard Python +reference implementation in the Python documentation. + +The UTC timezone unpickles to be the same instance, and pickles to a +smaller size than other pytz tzinfo instances. The UTC implementation +can be obtained as pytz.utc, pytz.UTC, or pytz.timezone('UTC'). + +>>> import pickle, pytz +>>> dt = datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc) +>>> naive = dt.replace(tzinfo=None) +>>> p = pickle.dumps(dt, 1) +>>> naive_p = pickle.dumps(naive, 1) +>>> len(p) - len(naive_p) +17 +>>> new = pickle.loads(p) +>>> new == dt +True +>>> new is dt +False +>>> new.tzinfo is dt.tzinfo +True +>>> pytz.utc is pytz.UTC is pytz.timezone('UTC') +True + +Note that some other timezones are commonly thought of as the same (GMT, +Greenwich, Universal, etc.). The definition of UTC is distinct from these +other timezones, and they are not equivalent. For this reason, they will +not compare the same in Python. + +>>> utc == pytz.timezone('GMT') +False + +See the section `What is UTC`_, below. + +If you insist on working with local times, this library provides a +facility for constructing them unambiguously: + +>>> loc_dt = datetime(2002, 10, 27, 1, 30, 00) +>>> est_dt = eastern.localize(loc_dt, is_dst=True) +>>> edt_dt = eastern.localize(loc_dt, is_dst=False) +>>> print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt)) +2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500 + +If you pass None as the is_dst flag to localize(), pytz will refuse to +guess and raise exceptions if you try to build ambiguous or non-existent +times. + +For example, 1:30am on 27th Oct 2002 happened twice in the US/Eastern +timezone when the clocks where put back at the end of Daylight Saving +Time: + +>>> dt = datetime(2002, 10, 27, 1, 30, 00) +>>> try: +... eastern.localize(dt, is_dst=None) +... except pytz.exceptions.AmbiguousTimeError: +... print('pytz.exceptions.AmbiguousTimeError: %s' % dt) +pytz.exceptions.AmbiguousTimeError: 2002-10-27 01:30:00 + +Similarly, 2:30am on 7th April 2002 never happened at all in the +US/Eastern timezone, as the clocks where put forward at 2:00am skipping +the entire hour: + +>>> dt = datetime(2002, 4, 7, 2, 30, 00) +>>> try: +... eastern.localize(dt, is_dst=None) +... except pytz.exceptions.NonExistentTimeError: +... print('pytz.exceptions.NonExistentTimeError: %s' % dt) +pytz.exceptions.NonExistentTimeError: 2002-04-07 02:30:00 + +Both of these exceptions share a common base class to make error handling +easier: + +>>> isinstance(pytz.AmbiguousTimeError(), pytz.InvalidTimeError) +True +>>> isinstance(pytz.NonExistentTimeError(), pytz.InvalidTimeError) +True + + +A special case is where countries change their timezone definitions +with no daylight savings time switch. For example, in 1915 Warsaw +switched from Warsaw time to Central European time with no daylight savings +transition. So at the stroke of midnight on August 5th 1915 the clocks +were wound back 24 minutes creating an ambiguous time period that cannot +be specified without referring to the timezone abbreviation or the +actual UTC offset. In this case midnight happened twice, neither time +during a daylight saving time period. pytz handles this transition by +treating the ambiguous period before the switch as daylight savings +time, and the ambiguous period after as standard time. + + +>>> warsaw = pytz.timezone('Europe/Warsaw') +>>> amb_dt1 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=True) +>>> amb_dt1.strftime(fmt) +'1915-08-04 23:59:59 WMT+0124' +>>> amb_dt2 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=False) +>>> amb_dt2.strftime(fmt) +'1915-08-04 23:59:59 CET+0100' +>>> switch_dt = warsaw.localize(datetime(1915, 8, 5, 00, 00, 00), is_dst=False) +>>> switch_dt.strftime(fmt) +'1915-08-05 00:00:00 CET+0100' +>>> str(switch_dt - amb_dt1) +'0:24:01' +>>> str(switch_dt - amb_dt2) +'0:00:01' + +The best way of creating a time during an ambiguous time period is +by converting from another timezone such as UTC: + +>>> utc_dt = datetime(1915, 8, 4, 22, 36, tzinfo=pytz.utc) +>>> utc_dt.astimezone(warsaw).strftime(fmt) +'1915-08-04 23:36:00 CET+0100' + +The standard Python way of handling all these ambiguities is not to +handle them, such as demonstrated in this example using the US/Eastern +timezone definition from the Python documentation (Note that this +implementation only works for dates between 1987 and 2006 - it is +included for tests only!): + +>>> from pytz.reference import Eastern # pytz.reference only for tests +>>> dt = datetime(2002, 10, 27, 0, 30, tzinfo=Eastern) +>>> str(dt) +'2002-10-27 00:30:00-04:00' +>>> str(dt + timedelta(hours=1)) +'2002-10-27 01:30:00-05:00' +>>> str(dt + timedelta(hours=2)) +'2002-10-27 02:30:00-05:00' +>>> str(dt + timedelta(hours=3)) +'2002-10-27 03:30:00-05:00' + +Notice the first two results? At first glance you might think they are +correct, but taking the UTC offset into account you find that they are +actually two hours appart instead of the 1 hour we asked for. + +>>> from pytz.reference import UTC # pytz.reference only for tests +>>> str(dt.astimezone(UTC)) +'2002-10-27 04:30:00+00:00' +>>> str((dt + timedelta(hours=1)).astimezone(UTC)) +'2002-10-27 06:30:00+00:00' + + +Country Information +~~~~~~~~~~~~~~~~~~~ + +A mechanism is provided to access the timezones commonly in use +for a particular country, looked up using the ISO 3166 country code. +It returns a list of strings that can be used to retrieve the relevant +tzinfo instance using ``pytz.timezone()``: + +>>> print(' '.join(pytz.country_timezones['nz'])) +Pacific/Auckland Pacific/Chatham + +The Olson database comes with a ISO 3166 country code to English country +name mapping that pytz exposes as a dictionary: + +>>> print(pytz.country_names['nz']) +New Zealand + + +What is UTC +~~~~~~~~~~~ + +'UTC' is `Coordinated Universal Time`_. It is a successor to, but distinct +from, Greenwich Mean Time (GMT) and the various definitions of Universal +Time. UTC is now the worldwide standard for regulating clocks and time +measurement. + +All other timezones are defined relative to UTC, and include offsets like +UTC+0800 - hours to add or subtract from UTC to derive the local time. No +daylight saving time occurs in UTC, making it a useful timezone to perform +date arithmetic without worrying about the confusion and ambiguities caused +by daylight saving time transitions, your country changing its timezone, or +mobile computers that roam through multiple timezones. + +.. _Coordinated Universal Time: https://en.wikipedia.org/wiki/Coordinated_Universal_Time + + +Helpers +~~~~~~~ + +There are two lists of timezones provided. + +``all_timezones`` is the exhaustive list of the timezone names that can +be used. + +>>> from pytz import all_timezones +>>> len(all_timezones) >= 500 +True +>>> 'Etc/Greenwich' in all_timezones +True + +``common_timezones`` is a list of useful, current timezones. It doesn't +contain deprecated zones or historical zones, except for a few I've +deemed in common usage, such as US/Eastern (open a bug report if you +think other timezones are deserving of being included here). It is also +a sequence of strings. + +>>> from pytz import common_timezones +>>> len(common_timezones) < len(all_timezones) +True +>>> 'Etc/Greenwich' in common_timezones +False +>>> 'Australia/Melbourne' in common_timezones +True +>>> 'US/Eastern' in common_timezones +True +>>> 'Canada/Eastern' in common_timezones +True +>>> 'Australia/Yancowinna' in all_timezones +True +>>> 'Australia/Yancowinna' in common_timezones +False + +Both ``common_timezones`` and ``all_timezones`` are alphabetically +sorted: + +>>> common_timezones_dupe = common_timezones[:] +>>> common_timezones_dupe.sort() +>>> common_timezones == common_timezones_dupe +True +>>> all_timezones_dupe = all_timezones[:] +>>> all_timezones_dupe.sort() +>>> all_timezones == all_timezones_dupe +True + +``all_timezones`` and ``common_timezones`` are also available as sets. + +>>> from pytz import all_timezones_set, common_timezones_set +>>> 'US/Eastern' in all_timezones_set +True +>>> 'US/Eastern' in common_timezones_set +True +>>> 'Australia/Victoria' in common_timezones_set +False + +You can also retrieve lists of timezones used by particular countries +using the ``country_timezones()`` function. It requires an ISO-3166 +two letter country code. + +>>> from pytz import country_timezones +>>> print(' '.join(country_timezones('ch'))) +Europe/Zurich +>>> print(' '.join(country_timezones('CH'))) +Europe/Zurich + + +Internationalization - i18n/l10n +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pytz is an interface to the IANA database, which uses ASCII names. The `Unicode Consortium's Unicode Locales (CLDR) <http://cldr.unicode.org>`_ +project provides translations. Thomas Khyn's +`l18n <https://pypi.org/project/l18n/>`_ package can be used to access +these translations from Python. + + +License +~~~~~~~ + +MIT license. + +This code is also available as part of Zope 3 under the Zope Public +License, Version 2.1 (ZPL). + +I'm happy to relicense this code if necessary for inclusion in other +open source projects. + + +Latest Versions +~~~~~~~~~~~~~~~ + +This package will be updated after releases of the Olson timezone +database. The latest version can be downloaded from the `Python Package +Index <https://pypi.org/project/pytz/>`_. The code that is used +to generate this distribution is hosted on launchpad.net and available +using git:: + + git clone https://git.launchpad.net/pytz + +A mirror on github is also available at https://github.com/stub42/pytz + +Announcements of new releases are made on +`Launchpad <https://launchpad.net/pytz>`_, and the +`Atom feed <http://feeds.launchpad.net/pytz/announcements.atom>`_ +hosted there. + + +Bugs, Feature Requests & Patches +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bugs can be reported using `Launchpad Bugs <https://bugs.launchpad.net/pytz>`_. + + +Security Issues +~~~~~~~~~~~~~~~ + +Reports about security issues can be made via `Tidelift <https://tidelift.com/security>`_. + + +Issues & Limitations +~~~~~~~~~~~~~~~~~~~~ + +- Offsets from UTC are rounded to the nearest whole minute, so timezones + such as Europe/Amsterdam pre 1937 will be up to 30 seconds out. This + is a limitation of the Python datetime library. + +- If you think a timezone definition is incorrect, I probably can't fix + it. pytz is a direct translation of the Olson timezone database, and + changes to the timezone definitions need to be made to this source. + If you find errors they should be reported to the time zone mailing + list, linked from http://www.iana.org/time-zones. + + +Further Reading +~~~~~~~~~~~~~~~ + +More info than you want to know about timezones: +http://www.twinsun.com/tz/tz-link.htm + + +Contact +~~~~~~~ + +Stuart Bishop <stuart@stuartbishop.net> + + + + diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/RECORD b/venv/Lib/site-packages/pytz-2022.1.dist-info/RECORD new file mode 100644 index 0000000..392ced4 --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/RECORD @@ -0,0 +1,619 @@ +pytz-2022.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pytz-2022.1.dist-info/LICENSE.txt,sha256=vosaN-vibFkqkPbA6zMQOn84POL010mMCvmlJpkKB7g,1088 +pytz-2022.1.dist-info/METADATA,sha256=6CH3GCROrYmB2ceZyH8e2UQIKzUnwYkYqMsXLDiz6BE,21448 +pytz-2022.1.dist-info/RECORD,, +pytz-2022.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +pytz-2022.1.dist-info/top_level.txt,sha256=6xRYlt934v1yHb1JIrXgHyGxn3cqACvd-yE8ski_kcc,5 +pytz-2022.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +pytz/__init__.py,sha256=m58Mf4zJPVDAqRw2-ClaCPvcIRtSerSsTPbwru_mG8Q,35163 +pytz/__pycache__/__init__.cpython-39.pyc,, +pytz/__pycache__/exceptions.cpython-39.pyc,, +pytz/__pycache__/lazy.cpython-39.pyc,, +pytz/__pycache__/reference.cpython-39.pyc,, +pytz/__pycache__/tzfile.cpython-39.pyc,, +pytz/__pycache__/tzinfo.cpython-39.pyc,, +pytz/exceptions.py,sha256=434ZcuLlpLQY9mWoGq7zJMV1TyiYvVgpKBU1qZkbDjM,1571 +pytz/lazy.py,sha256=toeR5uDWKBj6ezsUZ4elNP6CEMtK7CO2jS9A30nsFbo,5404 +pytz/reference.py,sha256=zUtCki7JFEmrzrjNsfMD7YL0lWDxynKc1Ubo4iXSs74,3778 +pytz/tzfile.py,sha256=K2y7pZs4vydpZVftrfAA_-hgw17y1Szc7z_QCse6udU,4723 +pytz/tzinfo.py,sha256=-5UjW-yqHbtO5NtSaWope7EbSdf2oTES26Kdlxjqdk0,19272 +pytz/zoneinfo/Africa/Abidjan,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Accra,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Addis_Ababa,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Algiers,sha256=vaFpjNVCwObnbfu82rOQzdJvN6nVgmpXpQ1aqzfzsqY,735 +pytz/zoneinfo/Africa/Asmara,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Asmera,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Bamako,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Bangui,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Banjul,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Bissau,sha256=IjuxDP6EZiDHFvl_bHS6NN7sdRxLKXllooBC829poak,194 +pytz/zoneinfo/Africa/Blantyre,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Brazzaville,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Bujumbura,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Cairo,sha256=L6zLQLnQtLkEELOGfm6USaHY33qAEPgGV822-iU1vxc,1955 +pytz/zoneinfo/Africa/Casablanca,sha256=qzlDyFvkLZWy8Bydogdx_cxZCkWzRwEEsuVWstJI_-s,2429 +pytz/zoneinfo/Africa/Ceuta,sha256=jp7xqONgZ3NPnElHzJEVusHKM9rxDK1nxJm4-i7Ln8o,2036 +pytz/zoneinfo/Africa/Conakry,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Dakar,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Dar_es_Salaam,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Djibouti,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Douala,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/El_Aaiun,sha256=Ja0t5t3QHHrvY0EGgxadypAabj4GjMLuQTTbOAur5M0,2295 +pytz/zoneinfo/Africa/Freetown,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Gaborone,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Harare,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Johannesburg,sha256=bBvMdSZo53WFowiuhUO9C8zY6BOGViboCb-U8_49l34,246 +pytz/zoneinfo/Africa/Juba,sha256=UVnIqEPJwHLTMC-r5qZQHNv9opoYVsKdq-ta_5XUw_Q,679 +pytz/zoneinfo/Africa/Kampala,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Khartoum,sha256=MYWDoJ3AcCItZdApoeOgtWWDDxquwTon5v5TOGP70-o,679 +pytz/zoneinfo/Africa/Kigali,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Kinshasa,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Lagos,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Libreville,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Lome,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Luanda,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Lubumbashi,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Lusaka,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Malabo,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Maputo,sha256=k_GelVHViGiuWCB1LSyTpIYSTDZEY9yclInQRY-LxoI,149 +pytz/zoneinfo/Africa/Maseru,sha256=bBvMdSZo53WFowiuhUO9C8zY6BOGViboCb-U8_49l34,246 +pytz/zoneinfo/Africa/Mbabane,sha256=bBvMdSZo53WFowiuhUO9C8zY6BOGViboCb-U8_49l34,246 +pytz/zoneinfo/Africa/Mogadishu,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Monrovia,sha256=-VsJW5cU4KdvfgYaQVv4lcuzmaKIVFMd42nO6RXOBdU,208 +pytz/zoneinfo/Africa/Nairobi,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Africa/Ndjamena,sha256=8T3A0Zm9Gj0Bvm6rd88t3GAXKiKdGUfHlIqYlkYI0KM,199 +pytz/zoneinfo/Africa/Niamey,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Nouakchott,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Ouagadougou,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Porto-Novo,sha256=z_6wKCzL1_ug5JP_hneh5abdUZeIUELkN_ladz-ESEY,235 +pytz/zoneinfo/Africa/Sao_Tome,sha256=MdjxpQ268uzJ7Zx1ZroFUtRUwqsJ6F_yY3AYV9FXw1I,254 +pytz/zoneinfo/Africa/Timbuktu,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Africa/Tripoli,sha256=W1dptGD70T7ppGoo0fczFQeDiIp0nultLNPV66MwB2c,625 +pytz/zoneinfo/Africa/Tunis,sha256=OFVMEM4eYT2Ez0beuhEUCTSIpcFldWxsV2uEoTZIUNI,689 +pytz/zoneinfo/Africa/Windhoek,sha256=xuhvudrMH4alnVmouSTQI8YL8F_HbgsF2EQ7AZKzuHs,955 +pytz/zoneinfo/America/Adak,sha256=IB1DhwJQAKbhPJ9jHLf8zW5Dad7HIkBS-dhv64E1OlM,2356 +pytz/zoneinfo/America/Anchorage,sha256=oZA1NSPS2BWdymYpnCHFO8BlYVS-ll5KLg2Ez9CbETs,2371 +pytz/zoneinfo/America/Anguilla,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Antigua,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Araguaina,sha256=kppiiytmSQeesflyNGYM3r8NVUl1C-ggu08s9_Tt-co,884 +pytz/zoneinfo/America/Argentina/Buenos_Aires,sha256=ntn_GFHadbrFJ4ZuhU6h2uzbFwmDyS9mXV5S28pkGF8,1076 +pytz/zoneinfo/America/Argentina/Catamarca,sha256=diH1f96kbbY-7gJYQnSCNHs3n9dwHJqUhSdGNx1L7I0,1076 +pytz/zoneinfo/America/Argentina/ComodRivadavia,sha256=diH1f96kbbY-7gJYQnSCNHs3n9dwHJqUhSdGNx1L7I0,1076 +pytz/zoneinfo/America/Argentina/Cordoba,sha256=1XqIP8Qo2bPR7909hrAI-qAttybmwEW4ms7FjZA5Yfw,1076 +pytz/zoneinfo/America/Argentina/Jujuy,sha256=5HR0TlZFifwJ5nLTmg7yWXgCTx9mRhahfs4_Wq70wOY,1048 +pytz/zoneinfo/America/Argentina/La_Rioja,sha256=Zf_E3akFE1YUt9MZ4xxbRnOrp2bH1D-Bjsc0SLFfRyU,1090 +pytz/zoneinfo/America/Argentina/Mendoza,sha256=5DJiYYeQpcLBR_IoIJtk43IswJeGYawx5GykszuJ-Nw,1076 +pytz/zoneinfo/America/Argentina/Rio_Gallegos,sha256=T97WADwva6JbxICviNQUt_7iw9c-nloI4QJCscENSck,1076 +pytz/zoneinfo/America/Argentina/Salta,sha256=ATw0uR6szWKPs6jzdn6revS7UxCXD26ORK6jlmsjL18,1048 +pytz/zoneinfo/America/Argentina/San_Juan,sha256=qlW693a0Tnofy-RdcVBuWY3DvTTGxWwcYdKU3Y98pX8,1090 +pytz/zoneinfo/America/Argentina/San_Luis,sha256=WYdcro5-Fe-N6LkQsKwx_1tVozmnBp58DO1-BJs2suo,1102 +pytz/zoneinfo/America/Argentina/Tucuman,sha256=wsjg1a5AM1dP2gjr112k3vt54trcOOM_StF74xzvBJc,1104 +pytz/zoneinfo/America/Argentina/Ushuaia,sha256=9548Vvq_kpw_NX5s65vYuIbqvwGV-PBxqwmcrflLI0U,1076 +pytz/zoneinfo/America/Aruba,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Asuncion,sha256=FTLtFk6MjJoh5VIDgJ2Sf4B_iNeCDxrV0MWwQL-sOVM,2044 +pytz/zoneinfo/America/Atikokan,sha256=kayA_pdpMcSQ0FjIzotdcf-m1JYfbKE-qcFT8LC8zqA,182 +pytz/zoneinfo/America/Atka,sha256=IB1DhwJQAKbhPJ9jHLf8zW5Dad7HIkBS-dhv64E1OlM,2356 +pytz/zoneinfo/America/Bahia,sha256=cmLkSAAzINlzYGXBqADEU3uPgA9S5nt-p1AV3Zy86VY,1024 +pytz/zoneinfo/America/Bahia_Banderas,sha256=BNjbcHSlPsJ4UpJx-gs1hpIyx2ScBieh1nyDuGb0PcE,1546 +pytz/zoneinfo/America/Barbados,sha256=ima-Qrrhazu4Qfvu2Z0-e6E-GTiYknuJBu6c2yVG9LE,436 +pytz/zoneinfo/America/Belem,sha256=_258hQZLCEXBX8xRLyQSw-AE-jiDmjVwJX32mN5UUEk,576 +pytz/zoneinfo/America/Belize,sha256=pkfLY2KfPchbeJa1pWcXmWAwp4ZlRvxWLVezXnrbkws,1614 +pytz/zoneinfo/America/Blanc-Sablon,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Boa_Vista,sha256=V4VVOkrFUV1qUfVp9E974IOJFmA5QxQrctatTBEb-hs,632 +pytz/zoneinfo/America/Bogota,sha256=ZaQKTZi35AMdlROs0vjEDA_phR8ztJOnjA8aLJZ5tHw,246 +pytz/zoneinfo/America/Boise,sha256=Yv4AXa2nSH_oVo3FZqZCR7V7z7c6WnQgKIUyNUpzGXA,2394 +pytz/zoneinfo/America/Buenos_Aires,sha256=ntn_GFHadbrFJ4ZuhU6h2uzbFwmDyS9mXV5S28pkGF8,1076 +pytz/zoneinfo/America/Cambridge_Bay,sha256=Nanl8yH4SshljhEjDe-PZCYEXbUuuZGmkbAAt2dB-bk,2084 +pytz/zoneinfo/America/Campo_Grande,sha256=5BBENR3_8gJp4F_Uj2RRknvRc4JJWNRPnZU9E7tb8QI,1444 +pytz/zoneinfo/America/Cancun,sha256=YR2U5T6mDGd5xm8EVA_TM1NwSRMYPNYWvV7wuthnX0I,782 +pytz/zoneinfo/America/Caracas,sha256=2NpwXPEtQkI82WCZuQWHXf66VCADcawMpfhKTsuA0x4,264 +pytz/zoneinfo/America/Catamarca,sha256=diH1f96kbbY-7gJYQnSCNHs3n9dwHJqUhSdGNx1L7I0,1076 +pytz/zoneinfo/America/Cayenne,sha256=atVbW5ChJiKQ_q-3kFs-DLTTZa9ptkiHkmJlq4AXoY4,198 +pytz/zoneinfo/America/Cayman,sha256=kayA_pdpMcSQ0FjIzotdcf-m1JYfbKE-qcFT8LC8zqA,182 +pytz/zoneinfo/America/Chicago,sha256=4aZFw-svkMyXmSpNufqzK-xveos-oVJDpEyI8Yu9HQE,3576 +pytz/zoneinfo/America/Chihuahua,sha256=cewXJyEw4KCoz33yl8o2tUJZmugBWH4R0Aovdmuqf-o,1484 +pytz/zoneinfo/America/Coral_Harbour,sha256=kayA_pdpMcSQ0FjIzotdcf-m1JYfbKE-qcFT8LC8zqA,182 +pytz/zoneinfo/America/Cordoba,sha256=1XqIP8Qo2bPR7909hrAI-qAttybmwEW4ms7FjZA5Yfw,1076 +pytz/zoneinfo/America/Costa_Rica,sha256=74rYa6lrgIkyls9PkHo8SCYl9oOqiuG5S7MWdnJelP4,316 +pytz/zoneinfo/America/Creston,sha256=nEOwYOnGxENw9zW8m50PGxbtVfTrX3QYAo4x4LgOLfI,328 +pytz/zoneinfo/America/Cuiaba,sha256=M0FsR8T9s4jFSuzD8Qi6pqtb6Rf2NTzyVHKGZrn56n4,1416 +pytz/zoneinfo/America/Curacao,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Danmarkshavn,sha256=YRZAfUCoVtaL1L-MYMYMH1wyOaVQnfUo_gFnvMXSuzw,698 +pytz/zoneinfo/America/Dawson,sha256=rAHhyuMuyjf_eyA2SBG76MRBf_fj_xi5FAuiWVQgJhw,1614 +pytz/zoneinfo/America/Dawson_Creek,sha256=aJXCyP4j3ggE4wGCN-LrS9hpD_5zWHzQTeSAKTWEPUM,1050 +pytz/zoneinfo/America/Denver,sha256=6_yPo1_mvnt9DgpPzr0QdHsjdsfUG6ALnagQLML1DSM,2444 +pytz/zoneinfo/America/Detroit,sha256=hecz8yqY2Cj5B61G3gLZdAVZvRgK9l0P90c_gN-uD5g,2230 +pytz/zoneinfo/America/Dominica,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Edmonton,sha256=-TkIfc3QlvaCf0p8COZ43Y1HRBAl-nARUi-JdXeK1vE,2332 +pytz/zoneinfo/America/Eirunepe,sha256=pS90HZzRwH4Tf8ugmKHfiphX7zCPqZkh_0CNb-fEMAM,656 +pytz/zoneinfo/America/El_Salvador,sha256=gvGN8Lkj-sGm2_rs8OUjAMf1oMtKp2Xes6UfWT0WqgU,224 +pytz/zoneinfo/America/Ensenada,sha256=OHHtvy3J70z6wvKBHgPqMEnGs6SXp8fkf0WX9ZiOODk,2342 +pytz/zoneinfo/America/Fort_Nelson,sha256=erfODr3DrSpz65kAdO7Ts2dGbZxvddEP6gx4BX3y2J0,2240 +pytz/zoneinfo/America/Fort_Wayne,sha256=GrNub1_3Um5Qh67wOx58_TEAz4fwAeAlk2AlMTVA_sI,1666 +pytz/zoneinfo/America/Fortaleza,sha256=mITuMrRLRTWyoiF04Oy_UZ8gxZofTpXDblM8t7ch7Sg,716 +pytz/zoneinfo/America/Glace_Bay,sha256=G8DGLGCapH_aYCF_OhaL5Qonf7FOAgAPwelO5htCWBc,2192 +pytz/zoneinfo/America/Godthab,sha256=FtlXWP_hBNuwBHkI2b1yne_tSUJpwLtWLyTHZoFZkmM,1878 +pytz/zoneinfo/America/Goose_Bay,sha256=JgaLueghSvX2g725FOfIgpgvsqxZGykWOhAZWGpQZRY,3210 +pytz/zoneinfo/America/Grand_Turk,sha256=4YOFEPK60Bel2_fCsY6vSZxUcMJKjiKtyOf_Q0khEwU,1834 +pytz/zoneinfo/America/Grenada,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Guadeloupe,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Guatemala,sha256=dugUgCd6QY52yHkHuUP4jRWzo5x439IQigaYCvEF46Q,280 +pytz/zoneinfo/America/Guayaquil,sha256=PbcF4bvGAm-aFwdtGPotJy3kb4NwoyWwxgwL98BeUWA,246 +pytz/zoneinfo/America/Guyana,sha256=icHu0YLCJhwk9D47f4VCBHgnex6yGrY4JFtjkfMIeDs,262 +pytz/zoneinfo/America/Halifax,sha256=TZpmc5PwWoLfTfQoQ_b3U17BE2iVKSeNkR0Ho8mbTn8,3424 +pytz/zoneinfo/America/Havana,sha256=HUQeAuKBsEkI5SLZjqynXICOUVOajkKzKH5r-Ov5Odc,2416 +pytz/zoneinfo/America/Hermosillo,sha256=9Ij30JYmMscC1XHi4o9v-uSXoUuE8V9zhGz2iV5hVFI,416 +pytz/zoneinfo/America/Indiana/Indianapolis,sha256=GrNub1_3Um5Qh67wOx58_TEAz4fwAeAlk2AlMTVA_sI,1666 +pytz/zoneinfo/America/Indiana/Knox,sha256=BiALShjiOLg1o8mMRWJ1jyTlJkgvwzte7B9WSOvTUNg,2428 +pytz/zoneinfo/America/Indiana/Marengo,sha256=CPYY3XgJFNEzONxei7x04wOGI_b86RAn4jBPewi1HZw,1722 +pytz/zoneinfo/America/Indiana/Petersburg,sha256=axot1SloP27ZWjezmo7kldu9qA2frEtPVqWngcXtft0,1904 +pytz/zoneinfo/America/Indiana/Tell_City,sha256=GrWNjb1i4sbIYlJ8fU0viJ2Q5JmrlvLgcLQILnk3El8,1684 +pytz/zoneinfo/America/Indiana/Vevay,sha256=GGosHbQUoIDOKPZxdal42X40veEITMmrnlKOnLUhb-c,1414 +pytz/zoneinfo/America/Indiana/Vincennes,sha256=gh7LAbHbMD92eo9C_c5IiwQ1fJvxhdJN402Q_4YJdLg,1694 +pytz/zoneinfo/America/Indiana/Winamac,sha256=yS-_aKSC4crd0WdNutkHRHxUjmBCU56QVQcqy7kYpbQ,1778 +pytz/zoneinfo/America/Indianapolis,sha256=GrNub1_3Um5Qh67wOx58_TEAz4fwAeAlk2AlMTVA_sI,1666 +pytz/zoneinfo/America/Inuvik,sha256=MU_oDiidQaijt1KV0B5h9LqHoCrJ8ieldD9tsiJiX5o,1894 +pytz/zoneinfo/America/Iqaluit,sha256=6PitEMSFWcSb-Io8fvm4oQ_7v39G_qANc6reTjXoZJ0,2032 +pytz/zoneinfo/America/Jamaica,sha256=wlagieUPRf5-beie-h7QsONbNzjGsm8vMs8uf28pw28,482 +pytz/zoneinfo/America/Jujuy,sha256=5HR0TlZFifwJ5nLTmg7yWXgCTx9mRhahfs4_Wq70wOY,1048 +pytz/zoneinfo/America/Juneau,sha256=k7hxb0aGRnfnE-DBi3LkcjAzRPyAf0_Hw0vVFfjGeb0,2353 +pytz/zoneinfo/America/Kentucky/Louisville,sha256=-yqgeeHZdq6oP3_WzVvYOmqV9HQv8y7ZWmc9bzHvJAY,2772 +pytz/zoneinfo/America/Kentucky/Monticello,sha256=NJMKjG7jjlRzZhndMPw51bYW0D3jviW2Qbl70YcU0Gg,2352 +pytz/zoneinfo/America/Knox_IN,sha256=BiALShjiOLg1o8mMRWJ1jyTlJkgvwzte7B9WSOvTUNg,2428 +pytz/zoneinfo/America/Kralendijk,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/La_Paz,sha256=PAGF2VU_QOw2xT1Cqdp2P8Aj9hXMVWlCByV7cvfIQ_k,232 +pytz/zoneinfo/America/Lima,sha256=JHDCg95uw6BEu4a4Gfyikm1s8rm8AsYPG8dJxQQNZFs,406 +pytz/zoneinfo/America/Los_Angeles,sha256=VOy1PikdjiVdJ7lukVGzwl8uDxV_KYqznkTm5BLEiDM,2836 +pytz/zoneinfo/America/Louisville,sha256=-yqgeeHZdq6oP3_WzVvYOmqV9HQv8y7ZWmc9bzHvJAY,2772 +pytz/zoneinfo/America/Lower_Princes,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Maceio,sha256=pzjNghmeHhvF4aI3cDq2G_5t71BSNGIbRAF5NmJyDmw,744 +pytz/zoneinfo/America/Managua,sha256=xBzF01AHn2E2fD8Qdy-DHFe36UqoeNpKPfChduBKWdk,430 +pytz/zoneinfo/America/Manaus,sha256=lp6RlkcXJQ7mSsKqnEgC8svJVrFDJk_16xxvfpNSpK4,604 +pytz/zoneinfo/America/Marigot,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Martinique,sha256=fMs80kOU2YFvC0f9y2eje97JeAtTYBamXrnlTunNLzQ,232 +pytz/zoneinfo/America/Matamoros,sha256=RlEMOT_zvCLQ8s7TNvRE2PnC4H9JrxO7MGxmfu5xPPI,1390 +pytz/zoneinfo/America/Mazatlan,sha256=aIyre-8trAXSHtqxbuu6gDDkWCUjI_SdAKPIjz74M2E,1526 +pytz/zoneinfo/America/Mendoza,sha256=5DJiYYeQpcLBR_IoIJtk43IswJeGYawx5GykszuJ-Nw,1076 +pytz/zoneinfo/America/Menominee,sha256=Arv9WLbfhNcpRsUjHDU757BEdwlp08Gt30AixG3gZ04,2274 +pytz/zoneinfo/America/Merida,sha256=BJQ5mzAT-akb_EA7WqGdNheCorDqLBnDS_4X3YJz0rc,1422 +pytz/zoneinfo/America/Metlakatla,sha256=twmieGTVY2V-U8nFxqvx7asYv8GVjeWdLtrOI7UApVI,1423 +pytz/zoneinfo/America/Mexico_City,sha256=DSpTe5TT0KBsxGx79Rs7ah-zJpiGOJKwPjztovRN0b4,1584 +pytz/zoneinfo/America/Miquelon,sha256=LNbkN87EnZUa41Xizko5VIN55EyQvf5Kk5b5AfNQG8Q,1666 +pytz/zoneinfo/America/Moncton,sha256=Wmv-bk9aKKcWWzOpc1UFu67HOfwaIk2Wmh3LgqGctys,3154 +pytz/zoneinfo/America/Monterrey,sha256=HA4yn9jQHk9i0PqiB7fSoFdzXtB1DT1cheGRPXrQNdQ,1390 +pytz/zoneinfo/America/Montevideo,sha256=4jcgTegK5X8F0yNYzk-3oySZ4U9XQ09UbTJ_mlu8N70,1510 +pytz/zoneinfo/America/Montreal,sha256=ggOSzbHkmfgu9wTQzP0MUKsrKMbgveuAeThh1eFl1a0,3494 +pytz/zoneinfo/America/Montserrat,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Nassau,sha256=ggOSzbHkmfgu9wTQzP0MUKsrKMbgveuAeThh1eFl1a0,3494 +pytz/zoneinfo/America/New_York,sha256=7AoiEGjr3wV4P7C4Qs35COZqwr2mjNDq7ocpsSPFOM8,3536 +pytz/zoneinfo/America/Nipigon,sha256=EGPXcOin8mfzFTkYJm4ICpY7fyE24I2pXg4ejafSMyU,2122 +pytz/zoneinfo/America/Nome,sha256=2izM3-P-PqJ9za6MdhzFfMvPFNq7Gim69tAvEwPeY2s,2367 +pytz/zoneinfo/America/Noronha,sha256=3R4lLV8jg5SljhC5OVVCk51Y77Efjo6zCe-oppg_FFo,716 +pytz/zoneinfo/America/North_Dakota/Beulah,sha256=PHlzEk3wsNXYsfMZZSio7ZfdnyxPFpOhK3dS-1AJKGg,2380 +pytz/zoneinfo/America/North_Dakota/Center,sha256=PaM52_JOVMEpVdw5qiOlhkp3qA0xp0d6Z9neOatmLKo,2380 +pytz/zoneinfo/America/North_Dakota/New_Salem,sha256=o0xmH1FUh3lVFLtP5Lb9c0PfSyaPTsRvQSQYwnn_yls,2380 +pytz/zoneinfo/America/Nuuk,sha256=FtlXWP_hBNuwBHkI2b1yne_tSUJpwLtWLyTHZoFZkmM,1878 +pytz/zoneinfo/America/Ojinaga,sha256=cO3V-x_1Q-mpbJgKNd6-WTfxDEHBV1aqS4wzVl5A0Q4,1484 +pytz/zoneinfo/America/Panama,sha256=kayA_pdpMcSQ0FjIzotdcf-m1JYfbKE-qcFT8LC8zqA,182 +pytz/zoneinfo/America/Pangnirtung,sha256=P9Kw_I-NxcUYJIr1j40jTn9q7F8TPAE_FqXsfLYF86A,2094 +pytz/zoneinfo/America/Paramaribo,sha256=Hm5tDwUmnoTrTUPEO4WArfSF74ZjywVEocy4kL51FzA,262 +pytz/zoneinfo/America/Phoenix,sha256=nEOwYOnGxENw9zW8m50PGxbtVfTrX3QYAo4x4LgOLfI,328 +pytz/zoneinfo/America/Port-au-Prince,sha256=09ZAJd4IOiMpfdpUuF1U44R_hRt6BvpAkFXOnYO9yOM,1434 +pytz/zoneinfo/America/Port_of_Spain,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Porto_Acre,sha256=17onkm8P_VgMkErjK9rr0qwNni7qp9tgcUZ93g3ltOs,628 +pytz/zoneinfo/America/Porto_Velho,sha256=ZRfzgGEu26hnl3JPtiZLOSFGj_WBSbOKdiLC1xIyc5c,576 +pytz/zoneinfo/America/Puerto_Rico,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Punta_Arenas,sha256=arlTaQbMOjCBiVMimhpHt9qOgZppSLU01rwVhw93bOY,1902 +pytz/zoneinfo/America/Rainy_River,sha256=r6kx6lD2IzCdygkj-DKyL2tPSn7k0Zil7PSHCBFKOa0,2122 +pytz/zoneinfo/America/Rankin_Inlet,sha256=KpQX97-EuF4MNyxQrtOKP616CK_vjniM-lo14WGVz0c,1892 +pytz/zoneinfo/America/Recife,sha256=ijFN2ZzZe5oBYdl8Ag3SwmGjj2JeVYYX2Vo767g2s6I,716 +pytz/zoneinfo/America/Regina,sha256=yjqT08pHbICYe83H8JmtaDBvCFqRv7Tfze3Y8xuXukw,980 +pytz/zoneinfo/America/Resolute,sha256=VP_u5XsepfSwx7Ou9zjGw2p5Qi10AIA54sP1J2DkppM,1892 +pytz/zoneinfo/America/Rio_Branco,sha256=17onkm8P_VgMkErjK9rr0qwNni7qp9tgcUZ93g3ltOs,628 +pytz/zoneinfo/America/Rosario,sha256=1XqIP8Qo2bPR7909hrAI-qAttybmwEW4ms7FjZA5Yfw,1076 +pytz/zoneinfo/America/Santa_Isabel,sha256=OHHtvy3J70z6wvKBHgPqMEnGs6SXp8fkf0WX9ZiOODk,2342 +pytz/zoneinfo/America/Santarem,sha256=Gl_lI3pPZ57UIYXWcmaTpFqWDA5re6bHh1nWs_Z0-Nc,602 +pytz/zoneinfo/America/Santiago,sha256=yxF9edZctGSER6k5w2e7R16COtd5AABVe0QW79Vgo6Y,2529 +pytz/zoneinfo/America/Santo_Domingo,sha256=DKtaEj8fQ92ybITTWU4Bm160S9pzJmUVbjaWRnenxU4,458 +pytz/zoneinfo/America/Sao_Paulo,sha256=cO3VGekMGdSf1y4f_UgkpDMRes26-l1oGUoDglIiUQg,1444 +pytz/zoneinfo/America/Scoresbysund,sha256=dfHb86egoiNykb3bR3OHXpGFPm_Apck8BLiVTCqVAVc,1916 +pytz/zoneinfo/America/Shiprock,sha256=6_yPo1_mvnt9DgpPzr0QdHsjdsfUG6ALnagQLML1DSM,2444 +pytz/zoneinfo/America/Sitka,sha256=aiS7Fk37hZpzZ9VkeJQeF-BqTLRC1QOTCgMAJwT8UxA,2329 +pytz/zoneinfo/America/St_Barthelemy,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/St_Johns,sha256=r1-17uKv27eZ3JsVkw_DLZQbo6wvjuuVu7C2pDsmOgI,3655 +pytz/zoneinfo/America/St_Kitts,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/St_Lucia,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/St_Thomas,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/St_Vincent,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Swift_Current,sha256=RRKOF7vZC8VvYxD8PP4J1_hUPayKBP7Lu80avRkfPDY,560 +pytz/zoneinfo/America/Tegucigalpa,sha256=EzOz7ntTlreMq69JZ2CcAb8Ps98V9bUMN480tpPIyw4,252 +pytz/zoneinfo/America/Thule,sha256=8xuPRaZU8RgO5ECqFYHYmnHioc81sBOailkVu8Y02i8,1502 +pytz/zoneinfo/America/Thunder_Bay,sha256=cJ9lcf2mDZttEx_ttYYoZAJfuGhSsDgNV2PI-ggWdPE,2202 +pytz/zoneinfo/America/Tijuana,sha256=OHHtvy3J70z6wvKBHgPqMEnGs6SXp8fkf0WX9ZiOODk,2342 +pytz/zoneinfo/America/Toronto,sha256=ggOSzbHkmfgu9wTQzP0MUKsrKMbgveuAeThh1eFl1a0,3494 +pytz/zoneinfo/America/Tortola,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Vancouver,sha256=sknKH0jSPWam-DHfM35qXs8Nam7d5TFlkUI9Sgxryyg,2892 +pytz/zoneinfo/America/Virgin,sha256=hJHlV_-AGoMGUWuMpZRv9fLmghrzFHfrR9fRkcxaZJc,246 +pytz/zoneinfo/America/Whitehorse,sha256=Kfv607qGHJxXGBP1nPJyNg2_duWrmxhZGFQr82ukgq8,1614 +pytz/zoneinfo/America/Winnipeg,sha256=7P-_YQrneFcon7QKSTOnkiGjEppFDn3Z48MJ1qq8VBw,2868 +pytz/zoneinfo/America/Yakutat,sha256=tFwnKbvwhyyn4LNTAn5ye_JWDdxjCerNDt7oOwUwO2M,2305 +pytz/zoneinfo/America/Yellowknife,sha256=pfFvC8NEy373KbO6r6ec-Gw_O0D2h64mXU1X1AsUDgE,1966 +pytz/zoneinfo/Antarctica/Casey,sha256=a_ShNA5q27F-GNPiFPttIhhdHc1MP485jX6pwRjZ_t0,384 +pytz/zoneinfo/Antarctica/Davis,sha256=6PokyOaaISRTN13sisuGgdt5vG5A2YqNooJpfLTb5SQ,297 +pytz/zoneinfo/Antarctica/DumontDUrville,sha256=ei_XjmiRDLh-RU94uvz9CCIIRFH1r0X7WL-sB-6DF60,186 +pytz/zoneinfo/Antarctica/Macquarie,sha256=ie7RlaU8RHTorVVj-MX8StKMqx_oXf4UH2PUqpzcwe0,2260 +pytz/zoneinfo/Antarctica/Mawson,sha256=9TW1g_z0tk5EfeB7K69VJo8agO7-K9ZxWbiqNKnUZNE,199 +pytz/zoneinfo/Antarctica/McMurdo,sha256=gADjoyPo_QISQU6UJrAgcHp3HDaMoOFRdH-d23uBSyc,2437 +pytz/zoneinfo/Antarctica/Palmer,sha256=DW_DXByXg5MnMZ-w1bNdu8b0lKOYD_EgrPRd5EcyEm4,1418 +pytz/zoneinfo/Antarctica/Rothera,sha256=QQI1m1IN4_2e6Bb0z-rOYaOwxp4XjMJDOKM9SFDUPKg,164 +pytz/zoneinfo/Antarctica/South_Pole,sha256=gADjoyPo_QISQU6UJrAgcHp3HDaMoOFRdH-d23uBSyc,2437 +pytz/zoneinfo/Antarctica/Syowa,sha256=rq9KPj8l0FBnnKn93WkMeA1IngNtTzk5_oV4sEZhc4w,165 +pytz/zoneinfo/Antarctica/Troll,sha256=3zrh-P_jMCss9GGwHJJHkypZZydq4mkgo_TDqctn3c4,1162 +pytz/zoneinfo/Antarctica/Vostok,sha256=6tx86WD3MVGJBCbOJUCoA6YlGwCn2BT4B85Zss0vz4Y,165 +pytz/zoneinfo/Arctic/Longyearbyen,sha256=UdCERhj1JYpx3ojmilaRoyVoR4qMA1-PEv6hGwnpsJA,2228 +pytz/zoneinfo/Asia/Aden,sha256=rq9KPj8l0FBnnKn93WkMeA1IngNtTzk5_oV4sEZhc4w,165 +pytz/zoneinfo/Asia/Almaty,sha256=rBIl_pqZNmKZabjEa4mcsLahl9PbAdZJpQMQLVmcfBU,997 +pytz/zoneinfo/Asia/Amman,sha256=S4wAXF0MX5MpeQyIHI08w_fG_735xdah3aRS9VRPFjE,1853 +pytz/zoneinfo/Asia/Anadyr,sha256=hDDTly45ejoVVP9Al07TmKpTACNGJaIPlcXLRbsG_4g,1188 +pytz/zoneinfo/Asia/Aqtau,sha256=A5exZN256JagFJTcasgdCrQ8giOqZ2EFMRVYBWTaqZA,983 +pytz/zoneinfo/Asia/Aqtobe,sha256=LQ7P5LEEe7jbWbjqvzmM79c0o6AdZeCExQS-fOWp8yw,1011 +pytz/zoneinfo/Asia/Ashgabat,sha256=L4DYV2mZWycsYeHIypXzO6ZNY3tD8wjgxfPR2ZPW26c,619 +pytz/zoneinfo/Asia/Ashkhabad,sha256=L4DYV2mZWycsYeHIypXzO6ZNY3tD8wjgxfPR2ZPW26c,619 +pytz/zoneinfo/Asia/Atyrau,sha256=3uEo89ORyDJqQ_TtaQdIf9UPaB8WqIRQVi0geeY9gVE,991 +pytz/zoneinfo/Asia/Baghdad,sha256=lQMSUnOuijbcoTaCqMNnYhnvKtS2IVP_kXFAzePVNDU,983 +pytz/zoneinfo/Asia/Bahrain,sha256=V0rFJdLHIrToJ5Wl28VzVowwCVZoY8ZZSeNp-7kOvjY,199 +pytz/zoneinfo/Asia/Baku,sha256=vhHnliaOdRyNudl0sFJFdLynEg0Hc0I-IiZNfbDeCbM,1227 +pytz/zoneinfo/Asia/Bangkok,sha256=eYq0vh89N1j069URoQvtBu0ndEal6FPrtbF8WCKKpDw,199 +pytz/zoneinfo/Asia/Barnaul,sha256=2c1Cq8XYlBgybRQMP8w0NCf7kaLDrPZtGn4M5iJZbJo,1221 +pytz/zoneinfo/Asia/Beirut,sha256=_Z_2ZAg_iL9vU51JDB8CB04uXBDrf1kLIis-JnXaS2o,2154 +pytz/zoneinfo/Asia/Bishkek,sha256=do_4ki1JvSKupUrvlz9jRkHspDhdvk1D2IkByFskjJM,983 +pytz/zoneinfo/Asia/Brunei,sha256=BMMjwEmZ9rMoNpWfg8IrlLhRbMKbdW48padRF-FGolc,203 +pytz/zoneinfo/Asia/Calcutta,sha256=6Qw0EDbLcgMgDik8s7UTJn4QSjmllPNeGVJU5rwKF88,285 +pytz/zoneinfo/Asia/Chita,sha256=4ICOcAVAEWnP-cdf_YJu1_kCYnYPG2_vYfSbuNI-VwI,1221 +pytz/zoneinfo/Asia/Choibalsan,sha256=sJQAAjiT9VyG73dYhpYkq4tcmfITcPpiAa8YXsDlKag,949 +pytz/zoneinfo/Asia/Chongqing,sha256=ZP_C5DqUQ1oEPAQNHTr36S0DGtx453N68YYbqk7u8-Y,561 +pytz/zoneinfo/Asia/Chungking,sha256=ZP_C5DqUQ1oEPAQNHTr36S0DGtx453N68YYbqk7u8-Y,561 +pytz/zoneinfo/Asia/Colombo,sha256=HGea9jswIIgz7k20LTzbKtQyUun67IP5HvsZrmAJZJY,372 +pytz/zoneinfo/Asia/Dacca,sha256=3K5llGhcpCdZMMcJuomICVv7lZlDRpU4PUb5DtFx8l4,337 +pytz/zoneinfo/Asia/Damascus,sha256=6mcB6bxH1KsLqzb_LmJUT3tUDnq9_ScLFKoMFkcZy3A,2294 +pytz/zoneinfo/Asia/Dhaka,sha256=3K5llGhcpCdZMMcJuomICVv7lZlDRpU4PUb5DtFx8l4,337 +pytz/zoneinfo/Asia/Dili,sha256=ptjbacc9JK0pv2JpD-gHMglrwYNj9LMMIua0U0ZTMUc,227 +pytz/zoneinfo/Asia/Dubai,sha256=-ga0m3ua9Y6kSWREz2_VdtcVAkq83WrW3vxjBI7WNGs,165 +pytz/zoneinfo/Asia/Dushanbe,sha256=FUk9Tt_GimfRulcWamEvuOvA7FQ52YfZqQ2w88qMx6M,591 +pytz/zoneinfo/Asia/Famagusta,sha256=CFrcygd8ude5x6OEtfM_Dw0KYHoxpPPzq46KoHVxjjc,2028 +pytz/zoneinfo/Asia/Gaza,sha256=t7JqgJ17VcFJwwpcC1ifwhFcnuUdgDL3ZjP6qlX_mRM,2422 +pytz/zoneinfo/Asia/Harbin,sha256=ZP_C5DqUQ1oEPAQNHTr36S0DGtx453N68YYbqk7u8-Y,561 +pytz/zoneinfo/Asia/Hebron,sha256=LcQWMBqMNBztWG-Ba_l556OtZ_HZbxtU9yH-oiDhrbI,2450 +pytz/zoneinfo/Asia/Ho_Chi_Minh,sha256=L5TXNg6-odIIn-JAyLTR8fKFiUFBNFwy0HzwZchbnm4,351 +pytz/zoneinfo/Asia/Hong_Kong,sha256=UcnFEc9S8hMWl9giVXni4TAhLPWX0H12XvwSt4AJHew,1203 +pytz/zoneinfo/Asia/Hovd,sha256=JUnOos7PNTi2VRKxD6XnaVR3NpuhsX_Pi18rIzVe1xw,891 +pytz/zoneinfo/Asia/Irkutsk,sha256=iUJZCVBjpfB4rNKJOr6g0zUZtccYYk_Gk0wTklx8Yj0,1243 +pytz/zoneinfo/Asia/Istanbul,sha256=2S0A_f7VxvyErJMMCPqK33AChA29IVkMr1o-SpMtMxk,1947 +pytz/zoneinfo/Asia/Jakarta,sha256=_WRgz6Zb6wxIXtMwpKjG4w4PJtDRzkhdrw-3a4NCBFA,355 +pytz/zoneinfo/Asia/Jayapura,sha256=ihzUd-L8HUVqG-Na10MyPE-YYwjVFj-xerqjTN4EJZs,221 +pytz/zoneinfo/Asia/Jerusalem,sha256=JUuWQmW5Tha0pJjw61Q5aN7CX0z4D7ops9OOSnda6Dc,2388 +pytz/zoneinfo/Asia/Kabul,sha256=ial7SvweHTQXDl79MnXm6QHtiw2i7Zt1e5urLXU8Sq8,208 +pytz/zoneinfo/Asia/Kamchatka,sha256=pBA0RbynKTKsMCmf2hJMZ_hgVUPemms-VceMMJ7QC64,1166 +pytz/zoneinfo/Asia/Karachi,sha256=iB-mWMTXUyfBwAkZdz8_UmEw0xsgxIub-KNI7akzhkk,379 +pytz/zoneinfo/Asia/Kashgar,sha256=AEXDJ5PxQOhePZZw1QZl98moDNa-bW3I3WVNQZHBPYA,165 +pytz/zoneinfo/Asia/Kathmandu,sha256=TUeW7rDSifOTSsNxvo9igIYZfGITEZUf-0EjglyRDWs,212 +pytz/zoneinfo/Asia/Katmandu,sha256=TUeW7rDSifOTSsNxvo9igIYZfGITEZUf-0EjglyRDWs,212 +pytz/zoneinfo/Asia/Khandyga,sha256=XYzE2tsE5Say9pg0cHDQkEE9aTuy2piFSLAGx_d-dmM,1271 +pytz/zoneinfo/Asia/Kolkata,sha256=6Qw0EDbLcgMgDik8s7UTJn4QSjmllPNeGVJU5rwKF88,285 +pytz/zoneinfo/Asia/Krasnoyarsk,sha256=nzRw4PI2AiK_Ge854b8U7TSDw0LGQy3ca5YuOOU2XwI,1207 +pytz/zoneinfo/Asia/Kuala_Lumpur,sha256=RfiIYo6sMEkSA8m5iUmyOyJzKZrgRs8ehGuDZwoq88k,383 +pytz/zoneinfo/Asia/Kuching,sha256=KsAtQ0aocINozixwW7CkorY-1PTLlsj7UUnQGQMEYTQ,483 +pytz/zoneinfo/Asia/Kuwait,sha256=rq9KPj8l0FBnnKn93WkMeA1IngNtTzk5_oV4sEZhc4w,165 +pytz/zoneinfo/Asia/Macao,sha256=MvAkRyRsrA2r052ItlyF5bh2FheRjI0jPwg0uIiH2Yk,1227 +pytz/zoneinfo/Asia/Macau,sha256=MvAkRyRsrA2r052ItlyF5bh2FheRjI0jPwg0uIiH2Yk,1227 +pytz/zoneinfo/Asia/Magadan,sha256=cqwjKQt8TlznM1w2DezAZuz1EjeOfLxPeSY19i9zkfQ,1222 +pytz/zoneinfo/Asia/Makassar,sha256=OhJtCqSTEU-u5n0opBVO5Bu-wQzcYPy9S_6aAhJXgOw,254 +pytz/zoneinfo/Asia/Manila,sha256=ujfq0kl1EhxcYSOrG-FS750aNaYUt1TT4bFuK4EcL_c,328 +pytz/zoneinfo/Asia/Muscat,sha256=-ga0m3ua9Y6kSWREz2_VdtcVAkq83WrW3vxjBI7WNGs,165 +pytz/zoneinfo/Asia/Nicosia,sha256=0Unm0IFT7HyGeQ7F3vTa_-klfysCgrulqFO6BD1plZU,2002 +pytz/zoneinfo/Asia/Novokuznetsk,sha256=vQGcqKdmYmWDdl73QPZTcyadnope1RPJ4oBgZelQu90,1165 +pytz/zoneinfo/Asia/Novosibirsk,sha256=ApL3s20HX2eIAno03HCa2RXdlLotVb9JvnZl7W1sM00,1221 +pytz/zoneinfo/Asia/Omsk,sha256=wxbEesfe7dJOkNPffqTwT6wuTSSTM6E9f0uFMAyzMCM,1207 +pytz/zoneinfo/Asia/Oral,sha256=iMjqD4LvDgyxN15v7CqyEdBDyBFaOlChwX1wHz2JiVQ,1005 +pytz/zoneinfo/Asia/Phnom_Penh,sha256=eYq0vh89N1j069URoQvtBu0ndEal6FPrtbF8WCKKpDw,199 +pytz/zoneinfo/Asia/Pontianak,sha256=inOXwuKtjKv1z_eliPZSIqjSt6whtuxhPeG1YpjU_BQ,353 +pytz/zoneinfo/Asia/Pyongyang,sha256=_-g3GnDAtfDX4XAktXH9jFouLUDmOovnjoOfvRpUDsE,237 +pytz/zoneinfo/Asia/Qatar,sha256=V0rFJdLHIrToJ5Wl28VzVowwCVZoY8ZZSeNp-7kOvjY,199 +pytz/zoneinfo/Asia/Qostanay,sha256=UGYEvmZfAAS9D6EMGd0n6-r_Az_zgTDSWLPeHzFLfu0,1011 +pytz/zoneinfo/Asia/Qyzylorda,sha256=aiSRxwoUbQ-TBHf2wcyaOhQb86j3jQpXwcQaSPnAtwU,1025 +pytz/zoneinfo/Asia/Rangoon,sha256=ZHuX-XVHr8dGJjrPQ5cW7b8jQUv3ihyd-VzN545mlMA,268 +pytz/zoneinfo/Asia/Riyadh,sha256=rq9KPj8l0FBnnKn93WkMeA1IngNtTzk5_oV4sEZhc4w,165 +pytz/zoneinfo/Asia/Saigon,sha256=L5TXNg6-odIIn-JAyLTR8fKFiUFBNFwy0HzwZchbnm4,351 +pytz/zoneinfo/Asia/Sakhalin,sha256=95AdPwOgSe0g9wdx67kKLDbjvY3FtpeVBoAWbJVco0w,1202 +pytz/zoneinfo/Asia/Samarkand,sha256=BBe6Gg_KlSQuS5hAyvvhZWmClcLJaFjnCNGC391HHQM,577 +pytz/zoneinfo/Asia/Seoul,sha256=LI9LsV3XcJC0l-KoQf8zI-y7rk-du57erS-N2Ptdi7Q,617 +pytz/zoneinfo/Asia/Shanghai,sha256=ZP_C5DqUQ1oEPAQNHTr36S0DGtx453N68YYbqk7u8-Y,561 +pytz/zoneinfo/Asia/Singapore,sha256=hIgr_LHMTWh3GgeG-MmLHBp-9anUxQcfMlKFtX8WvmU,383 +pytz/zoneinfo/Asia/Srednekolymsk,sha256=0DllW8q5VgXEMV5c_nLJElZsNpauvNhNACQpcgdqEl0,1208 +pytz/zoneinfo/Asia/Taipei,sha256=DMmQwOpPql25ue3Nf8vAKKT4em06D1Z9rHbLIitxixk,761 +pytz/zoneinfo/Asia/Tashkent,sha256=LS-yTxh0v1vmJoQ9I6fY-IERk7ukPmovVx2Ut_-b-Ig,591 +pytz/zoneinfo/Asia/Tbilisi,sha256=w6UNxgyn4BVVTF5WkAtxo_u7nnIY26makKQ5nRgifds,1035 +pytz/zoneinfo/Asia/Tehran,sha256=ATT50Q0hK6uSba5_WnOE3Px0OWxIwxaqK5Oi10P2A-M,2582 +pytz/zoneinfo/Asia/Tel_Aviv,sha256=JUuWQmW5Tha0pJjw61Q5aN7CX0z4D7ops9OOSnda6Dc,2388 +pytz/zoneinfo/Asia/Thimbu,sha256=uia8or5dtDkxVUZrcLwkjbTz9C7ZhLq0T4jlE4YvuvQ,203 +pytz/zoneinfo/Asia/Thimphu,sha256=uia8or5dtDkxVUZrcLwkjbTz9C7ZhLq0T4jlE4YvuvQ,203 +pytz/zoneinfo/Asia/Tokyo,sha256=oCueZgRNxcNcX3ZGdif9y6Su4cyVhga4XHdwlcrYLOs,309 +pytz/zoneinfo/Asia/Tomsk,sha256=77YgdJLxETRKjQjnaHHf54xBAqNywTDwQQmZ5v6Aq28,1221 +pytz/zoneinfo/Asia/Ujung_Pandang,sha256=OhJtCqSTEU-u5n0opBVO5Bu-wQzcYPy9S_6aAhJXgOw,254 +pytz/zoneinfo/Asia/Ulaanbaatar,sha256=uyQSzIBl0f2TXHrmUm3VPs1C9ro013hYmAlx6yUjh3Y,891 +pytz/zoneinfo/Asia/Ulan_Bator,sha256=uyQSzIBl0f2TXHrmUm3VPs1C9ro013hYmAlx6yUjh3Y,891 +pytz/zoneinfo/Asia/Urumqi,sha256=AEXDJ5PxQOhePZZw1QZl98moDNa-bW3I3WVNQZHBPYA,165 +pytz/zoneinfo/Asia/Ust-Nera,sha256=JAZhRAPdbOL9AL-WHOL8aZjxdZxLmGDNBGMCw9TKtR8,1252 +pytz/zoneinfo/Asia/Vientiane,sha256=eYq0vh89N1j069URoQvtBu0ndEal6FPrtbF8WCKKpDw,199 +pytz/zoneinfo/Asia/Vladivostok,sha256=Wokhgtj2nwUj992h7SyfB_fRNHAKfPNzhsf_oZpim8c,1208 +pytz/zoneinfo/Asia/Yakutsk,sha256=RVCIl52EvMrp2RG2hg2cjDSr9QhsscaAT-NV81xw7zc,1207 +pytz/zoneinfo/Asia/Yangon,sha256=ZHuX-XVHr8dGJjrPQ5cW7b8jQUv3ihyd-VzN545mlMA,268 +pytz/zoneinfo/Asia/Yekaterinburg,sha256=NzVc2DiPeyw0FdMHwSPQJF9k3tvWdtrETZiN58pyxLk,1243 +pytz/zoneinfo/Asia/Yerevan,sha256=k0WHtWQW_cBCjcEv8nP01cVPeTVDlf18lQ0_u6cin1o,1151 +pytz/zoneinfo/Atlantic/Azores,sha256=Q5Jqbe5h6WDi16jwK_B74gWWg58o0TgrdgB_cwbhzz0,3512 +pytz/zoneinfo/Atlantic/Bermuda,sha256=LNGKfMsnYvwImjTyzXrLhMOHHDu7qI67RbYNKvvI15I,2396 +pytz/zoneinfo/Atlantic/Canary,sha256=ymK9ufqphvNjDK3hzikN4GfkcR3QeCBiPKyVc6FjlbA,1897 +pytz/zoneinfo/Atlantic/Cape_Verde,sha256=ESQvE3deMI-lx9mG0yJLEsFX5KRl-7c6gD5O2h0Zm9Q,270 +pytz/zoneinfo/Atlantic/Faeroe,sha256=NibdZPZtapnYR_myIZnMdTaSKGsOBGgujj0_T2NvAzs,1815 +pytz/zoneinfo/Atlantic/Faroe,sha256=NibdZPZtapnYR_myIZnMdTaSKGsOBGgujj0_T2NvAzs,1815 +pytz/zoneinfo/Atlantic/Jan_Mayen,sha256=UdCERhj1JYpx3ojmilaRoyVoR4qMA1-PEv6hGwnpsJA,2228 +pytz/zoneinfo/Atlantic/Madeira,sha256=21Zcy0xRqDN3oY8jmjjO-LI7aC3G9mcS9ytaYg0g7ik,3503 +pytz/zoneinfo/Atlantic/Reykjavik,sha256=mSkaRBGZLeUrm88EeHcaWnEd35Wn-Ag2G10HtI3G2fg,1162 +pytz/zoneinfo/Atlantic/South_Georgia,sha256=QZ72fRKp6Kgvy7DfyHGht1MVnzGgSPujLQd4XMjNrrc,164 +pytz/zoneinfo/Atlantic/St_Helena,sha256=0u-sTl8j2IyV1ywdtCgHFw9S9D3ZiiBa9akqkbny2Zc,148 +pytz/zoneinfo/Atlantic/Stanley,sha256=exKMLw-P952wS1FTxVjnUU1mkD2OvKUDwtDt8IGgf8w,1214 +pytz/zoneinfo/Australia/ACT,sha256=QsOFdYWxbbL4_9R7oZ-qYPRzNA3o1P6TIOp76GFgWQY,2190 +pytz/zoneinfo/Australia/Adelaide,sha256=ld2EbxU75oVgmPe703z-I6aqLg0Kmv62ZcCGzkT5R20,2208 +pytz/zoneinfo/Australia/Brisbane,sha256=eW6Qzze2t0-speJmmvt1JMzbkSadIKdE84XHc7JUtGc,419 +pytz/zoneinfo/Australia/Broken_Hill,sha256=3k_3ljTvS5GSfo7Xh6w71UgR3aAwYPBsnCJ-mlEYCqQ,2229 +pytz/zoneinfo/Australia/Canberra,sha256=QsOFdYWxbbL4_9R7oZ-qYPRzNA3o1P6TIOp76GFgWQY,2190 +pytz/zoneinfo/Australia/Currie,sha256=GLQSzgIfsWxOvmKOrhpfofWqINQf6h36NYy3mcq6gcg,2358 +pytz/zoneinfo/Australia/Darwin,sha256=fn0IZhIW98FAnzLig-_GBtW5LA54jajdeeUzg4tCGvo,325 +pytz/zoneinfo/Australia/Eucla,sha256=LxEuFWyMse_cALVtRWCkf6sIIEk13jQ4JXW8k2agSd8,470 +pytz/zoneinfo/Australia/Hobart,sha256=GLQSzgIfsWxOvmKOrhpfofWqINQf6h36NYy3mcq6gcg,2358 +pytz/zoneinfo/Australia/LHI,sha256=Luf0Lx_iJHuh3kZd4LxRjf36tLF5-wW2UFMVNKNT7gg,1860 +pytz/zoneinfo/Australia/Lindeman,sha256=xM6Udx22oLNoLR1Y7GQhHOYov8nw3xQNqgc_NVQ2JK4,475 +pytz/zoneinfo/Australia/Lord_Howe,sha256=Luf0Lx_iJHuh3kZd4LxRjf36tLF5-wW2UFMVNKNT7gg,1860 +pytz/zoneinfo/Australia/Melbourne,sha256=lvx_MQcunMc6u2smIrl8X427bLsXvjkgpCSdjYCTNBM,2190 +pytz/zoneinfo/Australia/NSW,sha256=QsOFdYWxbbL4_9R7oZ-qYPRzNA3o1P6TIOp76GFgWQY,2190 +pytz/zoneinfo/Australia/North,sha256=fn0IZhIW98FAnzLig-_GBtW5LA54jajdeeUzg4tCGvo,325 +pytz/zoneinfo/Australia/Perth,sha256=Al1DOUh4U_ofMUQSeVlzSyD3x7SUjP9dchSaBUGmeWg,446 +pytz/zoneinfo/Australia/Queensland,sha256=eW6Qzze2t0-speJmmvt1JMzbkSadIKdE84XHc7JUtGc,419 +pytz/zoneinfo/Australia/South,sha256=ld2EbxU75oVgmPe703z-I6aqLg0Kmv62ZcCGzkT5R20,2208 +pytz/zoneinfo/Australia/Sydney,sha256=QsOFdYWxbbL4_9R7oZ-qYPRzNA3o1P6TIOp76GFgWQY,2190 +pytz/zoneinfo/Australia/Tasmania,sha256=GLQSzgIfsWxOvmKOrhpfofWqINQf6h36NYy3mcq6gcg,2358 +pytz/zoneinfo/Australia/Victoria,sha256=lvx_MQcunMc6u2smIrl8X427bLsXvjkgpCSdjYCTNBM,2190 +pytz/zoneinfo/Australia/West,sha256=Al1DOUh4U_ofMUQSeVlzSyD3x7SUjP9dchSaBUGmeWg,446 +pytz/zoneinfo/Australia/Yancowinna,sha256=3k_3ljTvS5GSfo7Xh6w71UgR3aAwYPBsnCJ-mlEYCqQ,2229 +pytz/zoneinfo/Brazil/Acre,sha256=17onkm8P_VgMkErjK9rr0qwNni7qp9tgcUZ93g3ltOs,628 +pytz/zoneinfo/Brazil/DeNoronha,sha256=3R4lLV8jg5SljhC5OVVCk51Y77Efjo6zCe-oppg_FFo,716 +pytz/zoneinfo/Brazil/East,sha256=cO3VGekMGdSf1y4f_UgkpDMRes26-l1oGUoDglIiUQg,1444 +pytz/zoneinfo/Brazil/West,sha256=lp6RlkcXJQ7mSsKqnEgC8svJVrFDJk_16xxvfpNSpK4,604 +pytz/zoneinfo/CET,sha256=o4omkrM_IsITxooUo8krM921XfBdvRs9JhwGXGd-Ypg,2094 +pytz/zoneinfo/CST6CDT,sha256=WGbtZ1FwjRX6Jeo_TCXKsfeDs4V9uhXGJfcnLJhk3s0,2310 +pytz/zoneinfo/Canada/Atlantic,sha256=TZpmc5PwWoLfTfQoQ_b3U17BE2iVKSeNkR0Ho8mbTn8,3424 +pytz/zoneinfo/Canada/Central,sha256=7P-_YQrneFcon7QKSTOnkiGjEppFDn3Z48MJ1qq8VBw,2868 +pytz/zoneinfo/Canada/Eastern,sha256=ggOSzbHkmfgu9wTQzP0MUKsrKMbgveuAeThh1eFl1a0,3494 +pytz/zoneinfo/Canada/Mountain,sha256=-TkIfc3QlvaCf0p8COZ43Y1HRBAl-nARUi-JdXeK1vE,2332 +pytz/zoneinfo/Canada/Newfoundland,sha256=r1-17uKv27eZ3JsVkw_DLZQbo6wvjuuVu7C2pDsmOgI,3655 +pytz/zoneinfo/Canada/Pacific,sha256=sknKH0jSPWam-DHfM35qXs8Nam7d5TFlkUI9Sgxryyg,2892 +pytz/zoneinfo/Canada/Saskatchewan,sha256=yjqT08pHbICYe83H8JmtaDBvCFqRv7Tfze3Y8xuXukw,980 +pytz/zoneinfo/Canada/Yukon,sha256=Kfv607qGHJxXGBP1nPJyNg2_duWrmxhZGFQr82ukgq8,1614 +pytz/zoneinfo/Chile/Continental,sha256=yxF9edZctGSER6k5w2e7R16COtd5AABVe0QW79Vgo6Y,2529 +pytz/zoneinfo/Chile/EasterIsland,sha256=paHp1QRXIa02kgd0-4V6vWXdqcwheow-hJQD9VqacfQ,2233 +pytz/zoneinfo/Cuba,sha256=HUQeAuKBsEkI5SLZjqynXICOUVOajkKzKH5r-Ov5Odc,2416 +pytz/zoneinfo/EET,sha256=gGVsW5-qnI7ty8vqVK1ADWhunrvAT8kUC79GUf-_7G8,1908 +pytz/zoneinfo/EST,sha256=uKE_VPKfxGyYEsyqV_DdE2MW55vs_qUioOdIn5Goobc,114 +pytz/zoneinfo/EST5EDT,sha256=fwzEMT1jgnY2dDjd0EqDl26_7LC-oF48Bd4ng5311H0,2310 +pytz/zoneinfo/Egypt,sha256=L6zLQLnQtLkEELOGfm6USaHY33qAEPgGV822-iU1vxc,1955 +pytz/zoneinfo/Eire,sha256=-JSA3vsi44F1DE8supVjSppH2Vpp12WjJI0_COtAmqU,3492 +pytz/zoneinfo/Etc/GMT,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Etc/GMT+0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Etc/GMT+1,sha256=1Qzl2X9rQ_RXEf11yH09wQZCr_ph6UdFP7E0yu9s-IQ,116 +pytz/zoneinfo/Etc/GMT+10,sha256=JEQyQyQlkC0o6ZTdeVjZhCIOh6cK5TF7H00Pkls-sUI,117 +pytz/zoneinfo/Etc/GMT+11,sha256=tWvcvYMFCaE60nJVvDrrov7stJvs1KQYOyrhl3dzcUs,117 +pytz/zoneinfo/Etc/GMT+12,sha256=b70HEhErq8IJmq8x7cOZy4eR__3fq5uHHpjvPBEHqMA,117 +pytz/zoneinfo/Etc/GMT+2,sha256=T6Ep5zhslBKbYaECFUB6gUKh3iTZPyMoW1kjhonxrUo,116 +pytz/zoneinfo/Etc/GMT+3,sha256=QGoYrE04bUJ-OzL37dt2MZT5FxWNLpJDPVXgJbstYZA,116 +pytz/zoneinfo/Etc/GMT+4,sha256=RWrkNki-wV7X-coe0VvufBe6LrWVpkPJgia5QQYEnBo,116 +pytz/zoneinfo/Etc/GMT+5,sha256=oRmeC41dgYXT-zzyZIRKXN9IvdL2Da5nTuwmG2_prIA,116 +pytz/zoneinfo/Etc/GMT+6,sha256=d6dAnwiejyFI2n7AzFlFW0aFAT6zYNEjBIEG0uu0sbQ,116 +pytz/zoneinfo/Etc/GMT+7,sha256=TqjYbzd0YHpx1wisFg08J19wTpg6ztJLLongZY_lozs,116 +pytz/zoneinfo/Etc/GMT+8,sha256=th_8bIMmYgRPCesBrbmBhRr0jQO7whd70LiY9HfwJyk,116 +pytz/zoneinfo/Etc/GMT+9,sha256=Qq5E6iUS7JMJIymT7YoqlI8MtqtVy0mr9t6zWFtWc9Y,116 +pytz/zoneinfo/Etc/GMT-0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Etc/GMT-1,sha256=73F1eU8uAQGP3mcoB2q99CjfManGFHk3fefljp9pYC4,117 +pytz/zoneinfo/Etc/GMT-10,sha256=fKWWNwLBOp1OkKjtc1w9LIXJR1mTTD-JdvYflRy1IrU,118 +pytz/zoneinfo/Etc/GMT-11,sha256=D2S79n6psa9t9_2vj5wIrFpHH2OJLcCKP6vtwzFZINY,118 +pytz/zoneinfo/Etc/GMT-12,sha256=me4V6lmWI8gSr8H7N41WAD0Eww1anh_EF34Qr9UoSnI,118 +pytz/zoneinfo/Etc/GMT-13,sha256=xbmbG1BQA6Dlpa_iUwEGyJxW4a3t6lmawdPKAE8vbR8,118 +pytz/zoneinfo/Etc/GMT-14,sha256=PpXoREBh02qFpvxVMj2pV9IAzSQvBE7XPvnN9qSZ-Kc,118 +pytz/zoneinfo/Etc/GMT-2,sha256=ve6hWLdeuiLhqagaWLqMD6HNybS1chRwjudfTZ2bYBE,117 +pytz/zoneinfo/Etc/GMT-3,sha256=N77jILanuLDVkLsdujXZSu-dsHiwN5MIpwh7fMUifso,117 +pytz/zoneinfo/Etc/GMT-4,sha256=LSko5fVHqPl5zfwjGqkbMa_OFnvtpT6o_4xYxNz9n5o,117 +pytz/zoneinfo/Etc/GMT-5,sha256=uLaSR5Mb18HRTsAA5SveY9PAJ97dO8QzIWqNXe3wZb4,117 +pytz/zoneinfo/Etc/GMT-6,sha256=JSN-RUAphJ50fpIv7cYC6unrtrz9S1Wma-piDHlGe7c,117 +pytz/zoneinfo/Etc/GMT-7,sha256=vVAOF8xU9T9ESnw68c0SFXpcvkoopaiwTR0zbefHHSU,117 +pytz/zoneinfo/Etc/GMT-8,sha256=S7xFQbFMpiDZy4v5L4D9fCrjRIzzoLC5p8Se23xi7us,117 +pytz/zoneinfo/Etc/GMT-9,sha256=I5vHNmUK-Yyg_S1skFN44VGVzBgktjFgVQiDIKO4aMI,117 +pytz/zoneinfo/Etc/GMT0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Etc/Greenwich,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Etc/UCT,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/Etc/UTC,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/Etc/Universal,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/Etc/Zulu,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/Europe/Amsterdam,sha256=pw8HngVt3bU5QrRzu70qOmf69TIyklkglvVUte9ntKo,2910 +pytz/zoneinfo/Europe/Andorra,sha256=gTB5jCQmvIw3JJi1_vAcOYuhtzPBR6RXUx9gVV6p6ug,1742 +pytz/zoneinfo/Europe/Astrakhan,sha256=ywtzL92KVfoybOmAhE9eHqmMcvJZm5b0js5GDdWIJEQ,1165 +pytz/zoneinfo/Europe/Athens,sha256=XDY-FBUddRyQHN8GxQLZ4awjuOlWlzlUdjv7OdXFNzA,2262 +pytz/zoneinfo/Europe/Belfast,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/Europe/Belgrade,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/Berlin,sha256=XuR19xoPwaMvrrhJ-MOcbnqmbW1B7HQrl7OnQ2s7BwE,2298 +pytz/zoneinfo/Europe/Bratislava,sha256=G9fdhUXmzx651BnyZ6V7AOYIV9EV5aMJMm44eJaLLZw,2301 +pytz/zoneinfo/Europe/Brussels,sha256=gS9Vrrbozend9HhuFetCVrIegs9fXSjaG60X2UVwysA,2933 +pytz/zoneinfo/Europe/Bucharest,sha256=nfg6-bU2D6DMEWb9EMIBR5kxnNsbDSx0UKfHH_ZzqFc,2184 +pytz/zoneinfo/Europe/Budapest,sha256=lNwqxWciBvw9ei81VQwIKHbC_ZDJjpgHU6HFg4wCUkY,2368 +pytz/zoneinfo/Europe/Busingen,sha256=K5QY7Ujj2VUchKR4bhhb0hgdAJhmwED71ykXDQOGKe8,1909 +pytz/zoneinfo/Europe/Chisinau,sha256=p1J_rqFE13pL8cpBRrEFe-teCI8f0fKK4uTUy_4diF4,2390 +pytz/zoneinfo/Europe/Copenhagen,sha256=q7iAbkd7y9QvbAi6XGZEUOTwNDCRYWRu9VQCxUrZ01U,2137 +pytz/zoneinfo/Europe/Dublin,sha256=-JSA3vsi44F1DE8supVjSppH2Vpp12WjJI0_COtAmqU,3492 +pytz/zoneinfo/Europe/Gibraltar,sha256=egOcazf2u1njGZ0tDj-f1NzZT_K5rpUKSqtShxO7U6c,3052 +pytz/zoneinfo/Europe/Guernsey,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/Europe/Helsinki,sha256=GEkB7LsVhmegt7YuuWheCDvDGC7b7Nw9bTdDGS9qkJc,1900 +pytz/zoneinfo/Europe/Isle_of_Man,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/Europe/Istanbul,sha256=2S0A_f7VxvyErJMMCPqK33AChA29IVkMr1o-SpMtMxk,1947 +pytz/zoneinfo/Europe/Jersey,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/Europe/Kaliningrad,sha256=s7GXSe1YvMcs7AiUhHNTA6I4nAOQn_Kmz_ZqJYO-LMM,1493 +pytz/zoneinfo/Europe/Kiev,sha256=-wrpG9jPuIKFP1NgBVvnxsMRf9L_h5z3J6Q3jj1AwNM,2120 +pytz/zoneinfo/Europe/Kirov,sha256=Sr4HEUwk3tPTXioeCLhvlgKbCAFU7Gy2UB3f--uWLDc,1153 +pytz/zoneinfo/Europe/Lisbon,sha256=mpUpxGexMhbOBImDLSQs5-GAk7pm7tg4qYW044Kkle0,3497 +pytz/zoneinfo/Europe/Ljubljana,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/London,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/Europe/Luxembourg,sha256=974Dvf_X1QISKG1zIiTJJIfGavobO21HUVS-HfysOcY,2946 +pytz/zoneinfo/Europe/Madrid,sha256=MTTMnrbDDtexRikd72-FbQEpCZjc63_UtBIiDomD95c,2614 +pytz/zoneinfo/Europe/Malta,sha256=xRwBfrV8hOihGtqcek5_B6l5hjc206g3yfbEWXIaUis,2620 +pytz/zoneinfo/Europe/Mariehamn,sha256=GEkB7LsVhmegt7YuuWheCDvDGC7b7Nw9bTdDGS9qkJc,1900 +pytz/zoneinfo/Europe/Minsk,sha256=mn86zdrNWpJYDfE51Iy9n1-Zi2piTyb9EPaS2A-uGJQ,1321 +pytz/zoneinfo/Europe/Monaco,sha256=50uVZXYXXqfnr-K4tsSNl26CZbRju65C-STp818wX84,2944 +pytz/zoneinfo/Europe/Moscow,sha256=KmkofRcj6T8Ph28PJChm8JVp13uRvef6TZ0GuPzUiDw,1535 +pytz/zoneinfo/Europe/Nicosia,sha256=0Unm0IFT7HyGeQ7F3vTa_-klfysCgrulqFO6BD1plZU,2002 +pytz/zoneinfo/Europe/Oslo,sha256=UdCERhj1JYpx3ojmilaRoyVoR4qMA1-PEv6hGwnpsJA,2228 +pytz/zoneinfo/Europe/Paris,sha256=q3ehSIot1GZ6TyMHIjbg0oRf4ghAXuwbSDSYVim6evg,2962 +pytz/zoneinfo/Europe/Podgorica,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/Prague,sha256=G9fdhUXmzx651BnyZ6V7AOYIV9EV5aMJMm44eJaLLZw,2301 +pytz/zoneinfo/Europe/Riga,sha256=hJ2_0m1taW9IuA-hMyP5n-WX7YOrR0heKszJhgljRWk,2198 +pytz/zoneinfo/Europe/Rome,sha256=-X5F_d3Dz0kBRWiUTXUN-fgeCHbUEHLaaHIwEPZEdUQ,2641 +pytz/zoneinfo/Europe/Samara,sha256=z2innqSZ8_lkEy8cIyF9JM_FfnO2sWZaqeFqOh8pD7M,1215 +pytz/zoneinfo/Europe/San_Marino,sha256=-X5F_d3Dz0kBRWiUTXUN-fgeCHbUEHLaaHIwEPZEdUQ,2641 +pytz/zoneinfo/Europe/Sarajevo,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/Saratov,sha256=BMej49HlQG24CWCh5VOENrB3jPuJPScPszRtb7MrJ3I,1183 +pytz/zoneinfo/Europe/Simferopol,sha256=W1y6rp4xKAT0-RR7KyoHEUVxjhtEk8zuGvsmXhzXKz4,1469 +pytz/zoneinfo/Europe/Skopje,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/Sofia,sha256=hCQKXfMNrnA5xHNw_uzTjKzVw4-Bvsq5oGO4yUCv5tY,2077 +pytz/zoneinfo/Europe/Stockholm,sha256=Xgp4GSh8-pzdeJeP8TQ20jWDDUj17R69h6RYTbLYd2g,1909 +pytz/zoneinfo/Europe/Tallinn,sha256=4a6JC0aIpMzqIV7O35zoG0LLJwkQq5AoXZ2ivkic6-w,2148 +pytz/zoneinfo/Europe/Tirane,sha256=ztlZyCS9WCXeVW8nBun3Tyi5HUY0EtFbiBbEc1gucuw,2084 +pytz/zoneinfo/Europe/Tiraspol,sha256=p1J_rqFE13pL8cpBRrEFe-teCI8f0fKK4uTUy_4diF4,2390 +pytz/zoneinfo/Europe/Ulyanovsk,sha256=nFsgcVTmTiiFzHtyJDRnO-3H4GRAfAeceb6b2jFHLUQ,1267 +pytz/zoneinfo/Europe/Uzhgorod,sha256=msjVPUQsr1jTQ7KLWASRFpPIORKyzM_HpAWjAdk0tGo,2066 +pytz/zoneinfo/Europe/Vaduz,sha256=K5QY7Ujj2VUchKR4bhhb0hgdAJhmwED71ykXDQOGKe8,1909 +pytz/zoneinfo/Europe/Vatican,sha256=-X5F_d3Dz0kBRWiUTXUN-fgeCHbUEHLaaHIwEPZEdUQ,2641 +pytz/zoneinfo/Europe/Vienna,sha256=ZmI3kADE6bnrJEccqh73XXBY36L1G4DkpiTQImtNrUk,2200 +pytz/zoneinfo/Europe/Vilnius,sha256=UFzRX3orCTB8d9IzlxJPy5eUA2oBPuCu1UJl-2D7C3U,2162 +pytz/zoneinfo/Europe/Volgograd,sha256=XZNEUXwnmGdOTld_9Lug2CFfXbFCJFZC45nOMb59FRk,1165 +pytz/zoneinfo/Europe/Warsaw,sha256=TiLDPbeVF0ckgLVEkaSeDaKZ8wctdJDOl_HE_Wd5rKs,2654 +pytz/zoneinfo/Europe/Zagreb,sha256=OpWtsGFWBE_S-mYoQcAmjCta9HwbGQANnSmVY9OHCTo,1920 +pytz/zoneinfo/Europe/Zaporozhye,sha256=bbzGnKLNgoB7wKJxLQjLIx_wHzISSdMrnbEZluO2GfA,2138 +pytz/zoneinfo/Europe/Zurich,sha256=K5QY7Ujj2VUchKR4bhhb0hgdAJhmwED71ykXDQOGKe8,1909 +pytz/zoneinfo/Factory,sha256=aFFlKx93HXoJoF4SSuTlD8cZtJA-ne5oKzAa6eX2V4k,116 +pytz/zoneinfo/GB,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/GB-Eire,sha256=xp08wV44TZMmAdBqppttDChQAb8tRN03GcEht99RYtY,3648 +pytz/zoneinfo/GMT,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/GMT+0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/GMT-0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/GMT0,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/Greenwich,sha256=bZ83iIPAefhsA4elVHqSxEmGnYBuB94QCEqwTwJJAY0,114 +pytz/zoneinfo/HST,sha256=1YkCncvgL9Z5CmUo4Vk8VbQmgA7ZAQ0PtE37j1yOli8,115 +pytz/zoneinfo/Hongkong,sha256=UcnFEc9S8hMWl9giVXni4TAhLPWX0H12XvwSt4AJHew,1203 +pytz/zoneinfo/Iceland,sha256=mSkaRBGZLeUrm88EeHcaWnEd35Wn-Ag2G10HtI3G2fg,1162 +pytz/zoneinfo/Indian/Antananarivo,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Indian/Chagos,sha256=23B26pwwK0gxW7TP76GltyY-RU_o6RGGSrF93pF7S1E,199 +pytz/zoneinfo/Indian/Christmas,sha256=J4I0WDX_LYAJxsx2vU0EdxFJQKRE-rRL1UvNQv09pCs,165 +pytz/zoneinfo/Indian/Cocos,sha256=PX-k8JpghajjvhljtBjWozaiu9NhUSpVeoACy2cAxN8,174 +pytz/zoneinfo/Indian/Comoro,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Indian/Kerguelen,sha256=oIvd6bmQFMLUefoBn4c1fQTOAawGcrPcmge2jU7BsYo,165 +pytz/zoneinfo/Indian/Mahe,sha256=ZNXjaoL_o6572xXgsgSmbd5D_SkaCaayolpSN1je82w,165 +pytz/zoneinfo/Indian/Maldives,sha256=dUQBbrmoB3odWsMt3K1YUnB447A6nkW3aR1aHzdLF7M,199 +pytz/zoneinfo/Indian/Mauritius,sha256=k6vWUVcfU3gS1K12e_aMw6BeSdMvdLyCJRCAL7CD0go,241 +pytz/zoneinfo/Indian/Mayotte,sha256=yJsuJTqJJqbOz37_NOS_zbf-JNr_IthHGMMN7sDqSWg,265 +pytz/zoneinfo/Indian/Reunion,sha256=lHnSVh7CYCuDBEM4dYsWDk006BSAznkCPxjiTtL_WiI,165 +pytz/zoneinfo/Iran,sha256=ATT50Q0hK6uSba5_WnOE3Px0OWxIwxaqK5Oi10P2A-M,2582 +pytz/zoneinfo/Israel,sha256=JUuWQmW5Tha0pJjw61Q5aN7CX0z4D7ops9OOSnda6Dc,2388 +pytz/zoneinfo/Jamaica,sha256=wlagieUPRf5-beie-h7QsONbNzjGsm8vMs8uf28pw28,482 +pytz/zoneinfo/Japan,sha256=oCueZgRNxcNcX3ZGdif9y6Su4cyVhga4XHdwlcrYLOs,309 +pytz/zoneinfo/Kwajalein,sha256=L4nH3qxv5EBKVRxYt67b9IfZfBzg5KJk19iu7x3oBMk,316 +pytz/zoneinfo/Libya,sha256=W1dptGD70T7ppGoo0fczFQeDiIp0nultLNPV66MwB2c,625 +pytz/zoneinfo/MET,sha256=i3CKSuP4N_PAj7o-Cbk8zPEdFs0CWWBCAfg2JXDx5V8,2094 +pytz/zoneinfo/MST,sha256=6IQwvtT12Bz1pTiqFuoVxNY-4ViS7ZrYHo5nPWwzKPw,114 +pytz/zoneinfo/MST7MDT,sha256=910Ek32FKoSyZWY_H19VHaVvqb-JsvnWTOOHvhrKsE0,2310 +pytz/zoneinfo/Mexico/BajaNorte,sha256=OHHtvy3J70z6wvKBHgPqMEnGs6SXp8fkf0WX9ZiOODk,2342 +pytz/zoneinfo/Mexico/BajaSur,sha256=aIyre-8trAXSHtqxbuu6gDDkWCUjI_SdAKPIjz74M2E,1526 +pytz/zoneinfo/Mexico/General,sha256=DSpTe5TT0KBsxGx79Rs7ah-zJpiGOJKwPjztovRN0b4,1584 +pytz/zoneinfo/NZ,sha256=gADjoyPo_QISQU6UJrAgcHp3HDaMoOFRdH-d23uBSyc,2437 +pytz/zoneinfo/NZ-CHAT,sha256=lkVqaSF1WWpv_B2K-k2uJp2setRVK6XbjsQ38gDGVEg,2068 +pytz/zoneinfo/Navajo,sha256=6_yPo1_mvnt9DgpPzr0QdHsjdsfUG6ALnagQLML1DSM,2444 +pytz/zoneinfo/PRC,sha256=ZP_C5DqUQ1oEPAQNHTr36S0DGtx453N68YYbqk7u8-Y,561 +pytz/zoneinfo/PST8PDT,sha256=Q7TCLkE69a6g7mPoPAkqhg-0dStyiAC0jVlM72KG_R8,2310 +pytz/zoneinfo/Pacific/Apia,sha256=cm6S6D0VdHsdqLJkupUJH6pLynao5QlwpMmRI9m5ZH4,612 +pytz/zoneinfo/Pacific/Auckland,sha256=gADjoyPo_QISQU6UJrAgcHp3HDaMoOFRdH-d23uBSyc,2437 +pytz/zoneinfo/Pacific/Bougainville,sha256=ZKDa_S_2gSlmOWizV1DqxH3wbE58rfK1vKZHZqrrtjI,268 +pytz/zoneinfo/Pacific/Chatham,sha256=lkVqaSF1WWpv_B2K-k2uJp2setRVK6XbjsQ38gDGVEg,2068 +pytz/zoneinfo/Pacific/Chuuk,sha256=6IYDKViuRDC_RVx1AJOxazVET6cZtdv_LFE6xbtGItI,269 +pytz/zoneinfo/Pacific/Easter,sha256=paHp1QRXIa02kgd0-4V6vWXdqcwheow-hJQD9VqacfQ,2233 +pytz/zoneinfo/Pacific/Efate,sha256=pG4NMVeM3hBJTZnZmqeLqz3Q5oCggTW4HO-R9Fe926A,538 +pytz/zoneinfo/Pacific/Enderbury,sha256=UvE7fVt5vGS7loKX10ibhNilliiNqwvQAXV9NRhYhgM,234 +pytz/zoneinfo/Pacific/Fakaofo,sha256=gow-SgE5r5c8J_Ag5nvJ5SUPDg6yH8pth_a-QLDcPv8,200 +pytz/zoneinfo/Pacific/Fiji,sha256=6gQaENzYya4ThAYG-mNkBdUo3pweeqPi3Q-9i7JTjaE,1049 +pytz/zoneinfo/Pacific/Funafuti,sha256=P-XYwlWQpWvS3Q_TYFe37BrgxKJy5tg7PHEQNCDGv5U,166 +pytz/zoneinfo/Pacific/Galapagos,sha256=MdtlC-ffp8reICzDxsQ8tWMsTkq5ZcN-j3OyyhjokV8,238 +pytz/zoneinfo/Pacific/Gambier,sha256=z6eYF8sszLjkfpqmWnbBBAUB-ibaR5nodKaAYbvXOe0,164 +pytz/zoneinfo/Pacific/Guadalcanal,sha256=6GX-XpxcCyA64qUMdxJMFMq4sPk0ZjhexqGbryzfgjE,166 +pytz/zoneinfo/Pacific/Guam,sha256=Ex9znmf6rNfGze6gNpZJCMr1TT4rkl2SnrhecrdJufI,494 +pytz/zoneinfo/Pacific/Honolulu,sha256=fwPRv1Jk56sCOi75uZfd_Iy2k2aSQHx3B2K5xUlSPzM,329 +pytz/zoneinfo/Pacific/Johnston,sha256=fwPRv1Jk56sCOi75uZfd_Iy2k2aSQHx3B2K5xUlSPzM,329 +pytz/zoneinfo/Pacific/Kanton,sha256=UvE7fVt5vGS7loKX10ibhNilliiNqwvQAXV9NRhYhgM,234 +pytz/zoneinfo/Pacific/Kiritimati,sha256=VHR3iuwiv3tx65WtitVHCoQEg3VJd812VZ5djuSyUxc,238 +pytz/zoneinfo/Pacific/Kosrae,sha256=Vm5AKI6NvuYSz58s8922WNIiWoqPcix2JOJOix1mlSU,351 +pytz/zoneinfo/Pacific/Kwajalein,sha256=L4nH3qxv5EBKVRxYt67b9IfZfBzg5KJk19iu7x3oBMk,316 +pytz/zoneinfo/Pacific/Majuro,sha256=Dwqh7gXoz7Duwu1n7XF8yEjhM4ULEs42LSQyy7F-qzQ,310 +pytz/zoneinfo/Pacific/Marquesas,sha256=uzsjVolutGRXp_FRnvXoU0ApDEb4ZaYoz_r60D7jufg,173 +pytz/zoneinfo/Pacific/Midway,sha256=fCYrYphYY6rUfxOw712y5cyRe104AC3pouqD3bCINFg,175 +pytz/zoneinfo/Pacific/Nauru,sha256=oGxocYsqssZ_EeQHf3cUP5cg0qtqzx1BzoEjVWjE_7g,252 +pytz/zoneinfo/Pacific/Niue,sha256=Kc0BRgsu7g2QTR9e37DuqRo1sUCWDFMowAQ4wO6YNQ0,203 +pytz/zoneinfo/Pacific/Norfolk,sha256=CdEXM9SKYC9Wn7aMxD2sV5i8zE88NQo25Z_L874JthI,880 +pytz/zoneinfo/Pacific/Noumea,sha256=FSanpAOCE7WHQeiop4QErKV9ZC3Tzu2GxkH8-tIXsHY,304 +pytz/zoneinfo/Pacific/Pago_Pago,sha256=fCYrYphYY6rUfxOw712y5cyRe104AC3pouqD3bCINFg,175 +pytz/zoneinfo/Pacific/Palau,sha256=CRW__McXPlOaxo2S9kHMHaBdjv7u59ZWEwYuJConzmQ,180 +pytz/zoneinfo/Pacific/Pitcairn,sha256=O65Ed1FOCF_0rEjpYPAquDwtAF3hxyJNiujgpgZV0kc,202 +pytz/zoneinfo/Pacific/Pohnpei,sha256=YqXrKwjhUnxWyV6PFg1L6_zu84MfPW82dypf0S7pHtQ,303 +pytz/zoneinfo/Pacific/Ponape,sha256=YqXrKwjhUnxWyV6PFg1L6_zu84MfPW82dypf0S7pHtQ,303 +pytz/zoneinfo/Pacific/Port_Moresby,sha256=ei_XjmiRDLh-RU94uvz9CCIIRFH1r0X7WL-sB-6DF60,186 +pytz/zoneinfo/Pacific/Rarotonga,sha256=3ur0jiBQqU20VyKMI3bSfA-HBaQ-HhjElTqsHWk1kic,603 +pytz/zoneinfo/Pacific/Saipan,sha256=Ex9znmf6rNfGze6gNpZJCMr1TT4rkl2SnrhecrdJufI,494 +pytz/zoneinfo/Pacific/Samoa,sha256=fCYrYphYY6rUfxOw712y5cyRe104AC3pouqD3bCINFg,175 +pytz/zoneinfo/Pacific/Tahiti,sha256=9iozXRFYDhBOLijmDk2mRS4Mb-LXWW1u7n790jBNKxM,165 +pytz/zoneinfo/Pacific/Tarawa,sha256=vT6UxW7KeGptdh80Fj9ASATGmLx8Wai630lML4mwg80,166 +pytz/zoneinfo/Pacific/Tongatapu,sha256=b0TbbaYBUDEkPIpcS-EnIKCZ5KSg2HNOGIZJ9Pa8TEI,372 +pytz/zoneinfo/Pacific/Truk,sha256=6IYDKViuRDC_RVx1AJOxazVET6cZtdv_LFE6xbtGItI,269 +pytz/zoneinfo/Pacific/Wake,sha256=dTJxldgcad-kGrODwo4cAHGRSsS-K3fjeZ62WEUhmFk,166 +pytz/zoneinfo/Pacific/Wallis,sha256=CAlw1H5gkER5lkvtmHY-ppoGL3hNmYxfMaXQpI0fTOE,166 +pytz/zoneinfo/Pacific/Yap,sha256=6IYDKViuRDC_RVx1AJOxazVET6cZtdv_LFE6xbtGItI,269 +pytz/zoneinfo/Poland,sha256=TiLDPbeVF0ckgLVEkaSeDaKZ8wctdJDOl_HE_Wd5rKs,2654 +pytz/zoneinfo/Portugal,sha256=mpUpxGexMhbOBImDLSQs5-GAk7pm7tg4qYW044Kkle0,3497 +pytz/zoneinfo/ROC,sha256=DMmQwOpPql25ue3Nf8vAKKT4em06D1Z9rHbLIitxixk,761 +pytz/zoneinfo/ROK,sha256=LI9LsV3XcJC0l-KoQf8zI-y7rk-du57erS-N2Ptdi7Q,617 +pytz/zoneinfo/Singapore,sha256=hIgr_LHMTWh3GgeG-MmLHBp-9anUxQcfMlKFtX8WvmU,383 +pytz/zoneinfo/Turkey,sha256=2S0A_f7VxvyErJMMCPqK33AChA29IVkMr1o-SpMtMxk,1947 +pytz/zoneinfo/UCT,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/US/Alaska,sha256=oZA1NSPS2BWdymYpnCHFO8BlYVS-ll5KLg2Ez9CbETs,2371 +pytz/zoneinfo/US/Aleutian,sha256=IB1DhwJQAKbhPJ9jHLf8zW5Dad7HIkBS-dhv64E1OlM,2356 +pytz/zoneinfo/US/Arizona,sha256=nEOwYOnGxENw9zW8m50PGxbtVfTrX3QYAo4x4LgOLfI,328 +pytz/zoneinfo/US/Central,sha256=4aZFw-svkMyXmSpNufqzK-xveos-oVJDpEyI8Yu9HQE,3576 +pytz/zoneinfo/US/East-Indiana,sha256=GrNub1_3Um5Qh67wOx58_TEAz4fwAeAlk2AlMTVA_sI,1666 +pytz/zoneinfo/US/Eastern,sha256=7AoiEGjr3wV4P7C4Qs35COZqwr2mjNDq7ocpsSPFOM8,3536 +pytz/zoneinfo/US/Hawaii,sha256=fwPRv1Jk56sCOi75uZfd_Iy2k2aSQHx3B2K5xUlSPzM,329 +pytz/zoneinfo/US/Indiana-Starke,sha256=BiALShjiOLg1o8mMRWJ1jyTlJkgvwzte7B9WSOvTUNg,2428 +pytz/zoneinfo/US/Michigan,sha256=hecz8yqY2Cj5B61G3gLZdAVZvRgK9l0P90c_gN-uD5g,2230 +pytz/zoneinfo/US/Mountain,sha256=6_yPo1_mvnt9DgpPzr0QdHsjdsfUG6ALnagQLML1DSM,2444 +pytz/zoneinfo/US/Pacific,sha256=VOy1PikdjiVdJ7lukVGzwl8uDxV_KYqznkTm5BLEiDM,2836 +pytz/zoneinfo/US/Samoa,sha256=fCYrYphYY6rUfxOw712y5cyRe104AC3pouqD3bCINFg,175 +pytz/zoneinfo/UTC,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/Universal,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/W-SU,sha256=KmkofRcj6T8Ph28PJChm8JVp13uRvef6TZ0GuPzUiDw,1535 +pytz/zoneinfo/WET,sha256=Sc0l03EfVs_aIi17I4KyZJFkwiAHat5BgpjuuFDhgQ0,1905 +pytz/zoneinfo/Zulu,sha256=i4WEZ5GrLIpUY8g6W-PAQ-JXDXRIQ01BOYlp7Ufj5vI,114 +pytz/zoneinfo/iso3166.tab,sha256=BMh_yY7MXp8DMEy71jarFX3IJSNpwuEyIjIo2HKUXD4,4463 +pytz/zoneinfo/leapseconds,sha256=XqJcaqBog5UPa6fd84zib9yPUBoo6g-DqsGUfxqB-sE,3392 +pytz/zoneinfo/tzdata.zi,sha256=Nl0BSbgc-gXNYuMGwhVECQ-44rgo0iL8LMjm3ZIGAzo,112793 +pytz/zoneinfo/zone.tab,sha256=S1ouKxk9vkeA_AEHFmw2kJPVuJTUzPK0YrSZLGgURK4,19419 +pytz/zoneinfo/zone1970.tab,sha256=UlmR6Qfjsqd0DkTzQExCWGTeOfbtP0ruBBfI4_MKKQI,17593 diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/WHEEL b/venv/Lib/site-packages/pytz-2022.1.dist-info/WHEEL new file mode 100644 index 0000000..ef99c6c --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/top_level.txt b/venv/Lib/site-packages/pytz-2022.1.dist-info/top_level.txt new file mode 100644 index 0000000..af44f19 --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pytz diff --git a/venv/Lib/site-packages/pytz-2022.1.dist-info/zip-safe b/venv/Lib/site-packages/pytz-2022.1.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/pytz-2022.1.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/pytz/__init__.py b/venv/Lib/site-packages/pytz/__init__.py new file mode 100644 index 0000000..900e8ca --- /dev/null +++ b/venv/Lib/site-packages/pytz/__init__.py @@ -0,0 +1,1559 @@ +''' +datetime.tzinfo timezone definitions generated from the +Olson timezone database: + + ftp://elsie.nci.nih.gov/pub/tz*.tar.gz + +See the datetime section of the Python Library Reference for information +on how to use these modules. +''' + +import sys +import datetime +import os.path + +from pytz.exceptions import AmbiguousTimeError +from pytz.exceptions import InvalidTimeError +from pytz.exceptions import NonExistentTimeError +from pytz.exceptions import UnknownTimeZoneError +from pytz.lazy import LazyDict, LazyList, LazySet # noqa +from pytz.tzinfo import unpickler, BaseTzInfo +from pytz.tzfile import build_tzinfo + + +# The IANA (nee Olson) database is updated several times a year. +OLSON_VERSION = '2022a' +VERSION = '2022.1' # pip compatible version number. +__version__ = VERSION + +OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling + +__all__ = [ + 'timezone', 'utc', 'country_timezones', 'country_names', + 'AmbiguousTimeError', 'InvalidTimeError', + 'NonExistentTimeError', 'UnknownTimeZoneError', + 'all_timezones', 'all_timezones_set', + 'common_timezones', 'common_timezones_set', + 'BaseTzInfo', 'FixedOffset', +] + + +if sys.version_info[0] > 2: # Python 3.x + + # Python 3.x doesn't have unicode(), making writing code + # for Python 2.3 and Python 3.x a pain. + unicode = str + + def ascii(s): + r""" + >>> ascii('Hello') + 'Hello' + >>> ascii('\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnicodeEncodeError: ... + """ + if type(s) == bytes: + s = s.decode('ASCII') + else: + s.encode('ASCII') # Raise an exception if not ASCII + return s # But the string - not a byte string. + +else: # Python 2.x + + def ascii(s): + r""" + >>> ascii('Hello') + 'Hello' + >>> ascii(u'Hello') + 'Hello' + >>> ascii(u'\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnicodeEncodeError: ... + """ + return s.encode('ASCII') + + +def open_resource(name): + """Open a resource from the zoneinfo subdir for reading. + + Uses the pkg_resources module if available and no standard file + found at the calculated location. + + It is possible to specify different location for zoneinfo + subdir by using the PYTZ_TZDATADIR environment variable. + """ + name_parts = name.lstrip('/').split('/') + for part in name_parts: + if part == os.path.pardir or os.path.sep in part: + raise ValueError('Bad path segment: %r' % part) + zoneinfo_dir = os.environ.get('PYTZ_TZDATADIR', None) + if zoneinfo_dir is not None: + filename = os.path.join(zoneinfo_dir, *name_parts) + else: + filename = os.path.join(os.path.dirname(__file__), + 'zoneinfo', *name_parts) + if not os.path.exists(filename): + # http://bugs.launchpad.net/bugs/383171 - we avoid using this + # unless absolutely necessary to help when a broken version of + # pkg_resources is installed. + try: + from pkg_resources import resource_stream + except ImportError: + resource_stream = None + + if resource_stream is not None: + return resource_stream(__name__, 'zoneinfo/' + name) + return open(filename, 'rb') + + +def resource_exists(name): + """Return true if the given resource exists""" + try: + if os.environ.get('PYTZ_SKIPEXISTSCHECK', ''): + # In "standard" distributions, we can assume that + # all the listed timezones are present. As an + # import-speed optimization, you can set the + # PYTZ_SKIPEXISTSCHECK flag to skip checking + # for the presence of the resource file on disk. + return True + open_resource(name).close() + return True + except IOError: + return False + + +_tzinfo_cache = {} + + +def timezone(zone): + r''' Return a datetime.tzinfo implementation for the given timezone + + >>> from datetime import datetime, timedelta + >>> utc = timezone('UTC') + >>> eastern = timezone('US/Eastern') + >>> eastern.zone + 'US/Eastern' + >>> timezone(unicode('US/Eastern')) is eastern + True + >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) + >>> loc_dt = utc_dt.astimezone(eastern) + >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' + >>> loc_dt.strftime(fmt) + '2002-10-27 01:00:00 EST (-0500)' + >>> (loc_dt - timedelta(minutes=10)).strftime(fmt) + '2002-10-27 00:50:00 EST (-0500)' + >>> eastern.normalize(loc_dt - timedelta(minutes=10)).strftime(fmt) + '2002-10-27 01:50:00 EDT (-0400)' + >>> (loc_dt + timedelta(minutes=10)).strftime(fmt) + '2002-10-27 01:10:00 EST (-0500)' + + Raises UnknownTimeZoneError if passed an unknown zone. + + >>> try: + ... timezone('Asia/Shangri-La') + ... except UnknownTimeZoneError: + ... print('Unknown') + Unknown + + >>> try: + ... timezone(unicode('\N{TRADE MARK SIGN}')) + ... except UnknownTimeZoneError: + ... print('Unknown') + Unknown + + ''' + if zone is None: + raise UnknownTimeZoneError(None) + + if zone.upper() == 'UTC': + return utc + + try: + zone = ascii(zone) + except UnicodeEncodeError: + # All valid timezones are ASCII + raise UnknownTimeZoneError(zone) + + zone = _case_insensitive_zone_lookup(_unmunge_zone(zone)) + if zone not in _tzinfo_cache: + if zone in all_timezones_set: # noqa + fp = open_resource(zone) + try: + _tzinfo_cache[zone] = build_tzinfo(zone, fp) + finally: + fp.close() + else: + raise UnknownTimeZoneError(zone) + + return _tzinfo_cache[zone] + + +def _unmunge_zone(zone): + """Undo the time zone name munging done by older versions of pytz.""" + return zone.replace('_plus_', '+').replace('_minus_', '-') + + +_all_timezones_lower_to_standard = None + + +def _case_insensitive_zone_lookup(zone): + """case-insensitively matching timezone, else return zone unchanged""" + global _all_timezones_lower_to_standard + if _all_timezones_lower_to_standard is None: + _all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in all_timezones) # noqa + return _all_timezones_lower_to_standard.get(zone.lower()) or zone # noqa + + +ZERO = datetime.timedelta(0) +HOUR = datetime.timedelta(hours=1) + + +class UTC(BaseTzInfo): + """UTC + + Optimized UTC implementation. It unpickles using the single module global + instance defined beneath this class declaration. + """ + zone = "UTC" + + _utcoffset = ZERO + _dst = ZERO + _tzname = zone + + def fromutc(self, dt): + if dt.tzinfo is None: + return self.localize(dt) + return super(utc.__class__, self).fromutc(dt) + + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + + def __reduce__(self): + return _UTC, () + + def localize(self, dt, is_dst=False): + '''Convert naive time to local time''' + if dt.tzinfo is not None: + raise ValueError('Not naive datetime (tzinfo is already set)') + return dt.replace(tzinfo=self) + + def normalize(self, dt, is_dst=False): + '''Correct the timezone information on the given datetime''' + if dt.tzinfo is self: + return dt + if dt.tzinfo is None: + raise ValueError('Naive time - no tzinfo set') + return dt.astimezone(self) + + def __repr__(self): + return "<UTC>" + + def __str__(self): + return "UTC" + + +UTC = utc = UTC() # UTC is a singleton + + +def _UTC(): + """Factory function for utc unpickling. + + Makes sure that unpickling a utc instance always returns the same + module global. + + These examples belong in the UTC class above, but it is obscured; or in + the README.rst, but we are not depending on Python 2.4 so integrating + the README.rst examples with the unit tests is not trivial. + + >>> import datetime, pickle + >>> dt = datetime.datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc) + >>> naive = dt.replace(tzinfo=None) + >>> p = pickle.dumps(dt, 1) + >>> naive_p = pickle.dumps(naive, 1) + >>> len(p) - len(naive_p) + 17 + >>> new = pickle.loads(p) + >>> new == dt + True + >>> new is dt + False + >>> new.tzinfo is dt.tzinfo + True + >>> utc is UTC is timezone('UTC') + True + >>> utc is timezone('GMT') + False + """ + return utc + + +_UTC.__safe_for_unpickling__ = True + + +def _p(*args): + """Factory function for unpickling pytz tzinfo instances. + + Just a wrapper around tzinfo.unpickler to save a few bytes in each pickle + by shortening the path. + """ + return unpickler(*args) + + +_p.__safe_for_unpickling__ = True + + +class _CountryTimezoneDict(LazyDict): + """Map ISO 3166 country code to a list of timezone names commonly used + in that country. + + iso3166_code is the two letter code used to identify the country. + + >>> def print_list(list_of_strings): + ... 'We use a helper so doctests work under Python 2.3 -> 3.x' + ... for s in list_of_strings: + ... print(s) + + >>> print_list(country_timezones['nz']) + Pacific/Auckland + Pacific/Chatham + >>> print_list(country_timezones['ch']) + Europe/Zurich + >>> print_list(country_timezones['CH']) + Europe/Zurich + >>> print_list(country_timezones[unicode('ch')]) + Europe/Zurich + >>> print_list(country_timezones['XXX']) + Traceback (most recent call last): + ... + KeyError: 'XXX' + + Previously, this information was exposed as a function rather than a + dictionary. This is still supported:: + + >>> print_list(country_timezones('nz')) + Pacific/Auckland + Pacific/Chatham + """ + def __call__(self, iso3166_code): + """Backwards compatibility.""" + return self[iso3166_code] + + def _fill(self): + data = {} + zone_tab = open_resource('zone.tab') + try: + for line in zone_tab: + line = line.decode('UTF-8') + if line.startswith('#'): + continue + code, coordinates, zone = line.split(None, 4)[:3] + if zone not in all_timezones_set: # noqa + continue + try: + data[code].append(zone) + except KeyError: + data[code] = [zone] + self.data = data + finally: + zone_tab.close() + + +country_timezones = _CountryTimezoneDict() + + +class _CountryNameDict(LazyDict): + '''Dictionary proving ISO3166 code -> English name. + + >>> print(country_names['au']) + Australia + ''' + def _fill(self): + data = {} + zone_tab = open_resource('iso3166.tab') + try: + for line in zone_tab.readlines(): + line = line.decode('UTF-8') + if line.startswith('#'): + continue + code, name = line.split(None, 1) + data[code] = name.strip() + self.data = data + finally: + zone_tab.close() + + +country_names = _CountryNameDict() + + +# Time-zone info based solely on fixed offsets + +class _FixedOffset(datetime.tzinfo): + + zone = None # to match the standard pytz API + + def __init__(self, minutes): + if abs(minutes) >= 1440: + raise ValueError("absolute offset is too large", minutes) + self._minutes = minutes + self._offset = datetime.timedelta(minutes=minutes) + + def utcoffset(self, dt): + return self._offset + + def __reduce__(self): + return FixedOffset, (self._minutes, ) + + def dst(self, dt): + return ZERO + + def tzname(self, dt): + return None + + def __repr__(self): + return 'pytz.FixedOffset(%d)' % self._minutes + + def localize(self, dt, is_dst=False): + '''Convert naive time to local time''' + if dt.tzinfo is not None: + raise ValueError('Not naive datetime (tzinfo is already set)') + return dt.replace(tzinfo=self) + + def normalize(self, dt, is_dst=False): + '''Correct the timezone information on the given datetime''' + if dt.tzinfo is self: + return dt + if dt.tzinfo is None: + raise ValueError('Naive time - no tzinfo set') + return dt.astimezone(self) + + +def FixedOffset(offset, _tzinfos={}): + """return a fixed-offset timezone based off a number of minutes. + + >>> one = FixedOffset(-330) + >>> one + pytz.FixedOffset(-330) + >>> str(one.utcoffset(datetime.datetime.now())) + '-1 day, 18:30:00' + >>> str(one.dst(datetime.datetime.now())) + '0:00:00' + + >>> two = FixedOffset(1380) + >>> two + pytz.FixedOffset(1380) + >>> str(two.utcoffset(datetime.datetime.now())) + '23:00:00' + >>> str(two.dst(datetime.datetime.now())) + '0:00:00' + + The datetime.timedelta must be between the range of -1 and 1 day, + non-inclusive. + + >>> FixedOffset(1440) + Traceback (most recent call last): + ... + ValueError: ('absolute offset is too large', 1440) + + >>> FixedOffset(-1440) + Traceback (most recent call last): + ... + ValueError: ('absolute offset is too large', -1440) + + An offset of 0 is special-cased to return UTC. + + >>> FixedOffset(0) is UTC + True + + There should always be only one instance of a FixedOffset per timedelta. + This should be true for multiple creation calls. + + >>> FixedOffset(-330) is one + True + >>> FixedOffset(1380) is two + True + + It should also be true for pickling. + + >>> import pickle + >>> pickle.loads(pickle.dumps(one)) is one + True + >>> pickle.loads(pickle.dumps(two)) is two + True + """ + if offset == 0: + return UTC + + info = _tzinfos.get(offset) + if info is None: + # We haven't seen this one before. we need to save it. + + # Use setdefault to avoid a race condition and make sure we have + # only one + info = _tzinfos.setdefault(offset, _FixedOffset(offset)) + + return info + + +FixedOffset.__safe_for_unpickling__ = True + + +def _test(): + import doctest + sys.path.insert(0, os.pardir) + import pytz + return doctest.testmod(pytz) + + +if __name__ == '__main__': + _test() +all_timezones = \ +['Africa/Abidjan', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Algiers', + 'Africa/Asmara', + 'Africa/Asmera', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Bissau', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/El_Aaiun', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Kampala', + 'Africa/Khartoum', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Lagos', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maputo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Sao_Tome', + 'Africa/Timbuktu', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Anguilla', + 'America/Antigua', + 'America/Araguaina', + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/ComodRivadavia', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Aruba', + 'America/Asuncion', + 'America/Atikokan', + 'America/Atka', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Blanc-Sablon', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Buenos_Aires', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Catamarca', + 'America/Cayenne', + 'America/Cayman', + 'America/Chicago', + 'America/Chihuahua', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Costa_Rica', + 'America/Creston', + 'America/Cuiaba', + 'America/Curacao', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Dominica', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Ensenada', + 'America/Fort_Nelson', + 'America/Fort_Wayne', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Godthab', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Indianapolis', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Jujuy', + 'America/Juneau', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/Knox_IN', + 'America/Kralendijk', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Louisville', + 'America/Lower_Princes', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Marigot', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Mendoza', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/Montreal', + 'America/Montserrat', + 'America/Nassau', + 'America/New_York', + 'America/Nipigon', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Nuuk', + 'America/Ojinaga', + 'America/Panama', + 'America/Pangnirtung', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Port_of_Spain', + 'America/Porto_Acre', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Punta_Arenas', + 'America/Rainy_River', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Rosario', + 'America/Santa_Isabel', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Shiprock', + 'America/Sitka', + 'America/St_Barthelemy', + 'America/St_Johns', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Thunder_Bay', + 'America/Tijuana', + 'America/Toronto', + 'America/Tortola', + 'America/Vancouver', + 'America/Virgin', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'America/Yellowknife', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/McMurdo', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/South_Pole', + 'Antarctica/Syowa', + 'Antarctica/Troll', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Ashkhabad', + 'Asia/Atyrau', + 'Asia/Baghdad', + 'Asia/Bahrain', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Barnaul', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Brunei', + 'Asia/Calcutta', + 'Asia/Chita', + 'Asia/Choibalsan', + 'Asia/Chongqing', + 'Asia/Chungking', + 'Asia/Colombo', + 'Asia/Dacca', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Famagusta', + 'Asia/Gaza', + 'Asia/Harbin', + 'Asia/Hebron', + 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Istanbul', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Kashgar', + 'Asia/Kathmandu', + 'Asia/Katmandu', + 'Asia/Khandyga', + 'Asia/Kolkata', + 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur', + 'Asia/Kuching', + 'Asia/Kuwait', + 'Asia/Macao', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Muscat', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Phnom_Penh', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qostanay', + 'Asia/Qyzylorda', + 'Asia/Rangoon', + 'Asia/Riyadh', + 'Asia/Saigon', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Tel_Aviv', + 'Asia/Thimbu', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Tomsk', + 'Asia/Ujung_Pandang', + 'Asia/Ulaanbaatar', + 'Asia/Ulan_Bator', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vientiane', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yangon', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faeroe', + 'Atlantic/Faroe', + 'Atlantic/Jan_Mayen', + 'Atlantic/Madeira', + 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia', + 'Atlantic/St_Helena', + 'Atlantic/Stanley', + 'Australia/ACT', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Canberra', + 'Australia/Currie', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/LHI', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/NSW', + 'Australia/North', + 'Australia/Perth', + 'Australia/Queensland', + 'Australia/South', + 'Australia/Sydney', + 'Australia/Tasmania', + 'Australia/Victoria', + 'Australia/West', + 'Australia/Yancowinna', + 'Brazil/Acre', + 'Brazil/DeNoronha', + 'Brazil/East', + 'Brazil/West', + 'CET', + 'CST6CDT', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Canada/Saskatchewan', + 'Canada/Yukon', + 'Chile/Continental', + 'Chile/EasterIsland', + 'Cuba', + 'EET', + 'EST', + 'EST5EDT', + 'Egypt', + 'Eire', + 'Etc/GMT', + 'Etc/GMT+0', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-0', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/GMT0', + 'Etc/Greenwich', + 'Etc/UCT', + 'Etc/UTC', + 'Etc/Universal', + 'Etc/Zulu', + 'Europe/Amsterdam', + 'Europe/Andorra', + 'Europe/Astrakhan', + 'Europe/Athens', + 'Europe/Belfast', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Bratislava', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Busingen', + 'Europe/Chisinau', + 'Europe/Copenhagen', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Guernsey', + 'Europe/Helsinki', + 'Europe/Isle_of_Man', + 'Europe/Istanbul', + 'Europe/Jersey', + 'Europe/Kaliningrad', + 'Europe/Kiev', + 'Europe/Kirov', + 'Europe/Lisbon', + 'Europe/Ljubljana', + 'Europe/London', + 'Europe/Luxembourg', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Mariehamn', + 'Europe/Minsk', + 'Europe/Monaco', + 'Europe/Moscow', + 'Europe/Nicosia', + 'Europe/Oslo', + 'Europe/Paris', + 'Europe/Podgorica', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Saratov', + 'Europe/Simferopol', + 'Europe/Skopje', + 'Europe/Sofia', + 'Europe/Stockholm', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Tiraspol', + 'Europe/Ulyanovsk', + 'Europe/Uzhgorod', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zagreb', + 'Europe/Zaporozhye', + 'Europe/Zurich', + 'GB', + 'GB-Eire', + 'GMT', + 'GMT+0', + 'GMT-0', + 'GMT0', + 'Greenwich', + 'HST', + 'Hongkong', + 'Iceland', + 'Indian/Antananarivo', + 'Indian/Chagos', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Maldives', + 'Indian/Mauritius', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Iran', + 'Israel', + 'Jamaica', + 'Japan', + 'Kwajalein', + 'Libya', + 'MET', + 'MST', + 'MST7MDT', + 'Mexico/BajaNorte', + 'Mexico/BajaSur', + 'Mexico/General', + 'NZ', + 'NZ-CHAT', + 'Navajo', + 'PRC', + 'PST8PDT', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Chuuk', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Enderbury', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Funafuti', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Johnston', + 'Pacific/Kanton', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Majuro', + 'Pacific/Marquesas', + 'Pacific/Midway', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Pohnpei', + 'Pacific/Ponape', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Saipan', + 'Pacific/Samoa', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + 'Pacific/Truk', + 'Pacific/Wake', + 'Pacific/Wallis', + 'Pacific/Yap', + 'Poland', + 'Portugal', + 'ROC', + 'ROK', + 'Singapore', + 'Turkey', + 'UCT', + 'US/Alaska', + 'US/Aleutian', + 'US/Arizona', + 'US/Central', + 'US/East-Indiana', + 'US/Eastern', + 'US/Hawaii', + 'US/Indiana-Starke', + 'US/Michigan', + 'US/Mountain', + 'US/Pacific', + 'US/Samoa', + 'UTC', + 'Universal', + 'W-SU', + 'WET', + 'Zulu'] +all_timezones = LazyList( + tz for tz in all_timezones if resource_exists(tz)) + +all_timezones_set = LazySet(all_timezones) +common_timezones = \ +['Africa/Abidjan', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Algiers', + 'Africa/Asmara', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Bissau', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/El_Aaiun', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Kampala', + 'Africa/Khartoum', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Lagos', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maputo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Sao_Tome', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Anguilla', + 'America/Antigua', + 'America/Araguaina', + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Aruba', + 'America/Asuncion', + 'America/Atikokan', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Blanc-Sablon', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Cayenne', + 'America/Cayman', + 'America/Chicago', + 'America/Chihuahua', + 'America/Costa_Rica', + 'America/Creston', + 'America/Cuiaba', + 'America/Curacao', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Dominica', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Fort_Nelson', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Juneau', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/Kralendijk', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Lower_Princes', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Marigot', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/Montserrat', + 'America/Nassau', + 'America/New_York', + 'America/Nipigon', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Nuuk', + 'America/Ojinaga', + 'America/Panama', + 'America/Pangnirtung', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Port_of_Spain', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Punta_Arenas', + 'America/Rainy_River', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Sitka', + 'America/St_Barthelemy', + 'America/St_Johns', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Thunder_Bay', + 'America/Tijuana', + 'America/Toronto', + 'America/Tortola', + 'America/Vancouver', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'America/Yellowknife', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/McMurdo', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/Syowa', + 'Antarctica/Troll', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Atyrau', + 'Asia/Baghdad', + 'Asia/Bahrain', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Barnaul', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Brunei', + 'Asia/Chita', + 'Asia/Choibalsan', + 'Asia/Colombo', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Famagusta', + 'Asia/Gaza', + 'Asia/Hebron', + 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Kathmandu', + 'Asia/Khandyga', + 'Asia/Kolkata', + 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur', + 'Asia/Kuching', + 'Asia/Kuwait', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Muscat', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Phnom_Penh', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qostanay', + 'Asia/Qyzylorda', + 'Asia/Riyadh', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Tomsk', + 'Asia/Ulaanbaatar', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vientiane', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yangon', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faroe', + 'Atlantic/Madeira', + 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia', + 'Atlantic/St_Helena', + 'Atlantic/Stanley', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/Perth', + 'Australia/Sydney', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Europe/Amsterdam', + 'Europe/Andorra', + 'Europe/Astrakhan', + 'Europe/Athens', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Bratislava', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Busingen', + 'Europe/Chisinau', + 'Europe/Copenhagen', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Guernsey', + 'Europe/Helsinki', + 'Europe/Isle_of_Man', + 'Europe/Istanbul', + 'Europe/Jersey', + 'Europe/Kaliningrad', + 'Europe/Kiev', + 'Europe/Kirov', + 'Europe/Lisbon', + 'Europe/Ljubljana', + 'Europe/London', + 'Europe/Luxembourg', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Mariehamn', + 'Europe/Minsk', + 'Europe/Monaco', + 'Europe/Moscow', + 'Europe/Oslo', + 'Europe/Paris', + 'Europe/Podgorica', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Saratov', + 'Europe/Simferopol', + 'Europe/Skopje', + 'Europe/Sofia', + 'Europe/Stockholm', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Ulyanovsk', + 'Europe/Uzhgorod', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zagreb', + 'Europe/Zaporozhye', + 'Europe/Zurich', + 'GMT', + 'Indian/Antananarivo', + 'Indian/Chagos', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Maldives', + 'Indian/Mauritius', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Chuuk', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Funafuti', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Kanton', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Majuro', + 'Pacific/Marquesas', + 'Pacific/Midway', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Pohnpei', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Saipan', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + 'Pacific/Wake', + 'Pacific/Wallis', + 'US/Alaska', + 'US/Arizona', + 'US/Central', + 'US/Eastern', + 'US/Hawaii', + 'US/Mountain', + 'US/Pacific', + 'UTC'] +common_timezones = LazyList( + tz for tz in common_timezones if tz in all_timezones) + +common_timezones_set = LazySet(common_timezones) diff --git a/venv/Lib/site-packages/pytz/exceptions.py b/venv/Lib/site-packages/pytz/exceptions.py new file mode 100644 index 0000000..4b20bde --- /dev/null +++ b/venv/Lib/site-packages/pytz/exceptions.py @@ -0,0 +1,59 @@ +''' +Custom exceptions raised by pytz. +''' + +__all__ = [ + 'UnknownTimeZoneError', 'InvalidTimeError', 'AmbiguousTimeError', + 'NonExistentTimeError', +] + + +class Error(Exception): + '''Base class for all exceptions raised by the pytz library''' + + +class UnknownTimeZoneError(KeyError, Error): + '''Exception raised when pytz is passed an unknown timezone. + + >>> isinstance(UnknownTimeZoneError(), LookupError) + True + + This class is actually a subclass of KeyError to provide backwards + compatibility with code relying on the undocumented behavior of earlier + pytz releases. + + >>> isinstance(UnknownTimeZoneError(), KeyError) + True + + And also a subclass of pytz.exceptions.Error, as are other pytz + exceptions. + + >>> isinstance(UnknownTimeZoneError(), Error) + True + + ''' + pass + + +class InvalidTimeError(Error): + '''Base class for invalid time exceptions.''' + + +class AmbiguousTimeError(InvalidTimeError): + '''Exception raised when attempting to create an ambiguous wallclock time. + + At the end of a DST transition period, a particular wallclock time will + occur twice (once before the clocks are set back, once after). Both + possibilities may be correct, unless further information is supplied. + + See DstTzInfo.normalize() for more info + ''' + + +class NonExistentTimeError(InvalidTimeError): + '''Exception raised when attempting to create a wallclock time that + cannot exist. + + At the start of a DST transition period, the wallclock time jumps forward. + The instants jumped over never occur. + ''' diff --git a/venv/Lib/site-packages/pytz/lazy.py b/venv/Lib/site-packages/pytz/lazy.py new file mode 100644 index 0000000..39344fc --- /dev/null +++ b/venv/Lib/site-packages/pytz/lazy.py @@ -0,0 +1,172 @@ +from threading import RLock +try: + from collections.abc import Mapping as DictMixin +except ImportError: # Python < 3.3 + try: + from UserDict import DictMixin # Python 2 + except ImportError: # Python 3.0-3.3 + from collections import Mapping as DictMixin + + +# With lazy loading, we might end up with multiple threads triggering +# it at the same time. We need a lock. +_fill_lock = RLock() + + +class LazyDict(DictMixin): + """Dictionary populated on first use.""" + data = None + + def __getitem__(self, key): + if self.data is None: + _fill_lock.acquire() + try: + if self.data is None: + self._fill() + finally: + _fill_lock.release() + return self.data[key.upper()] + + def __contains__(self, key): + if self.data is None: + _fill_lock.acquire() + try: + if self.data is None: + self._fill() + finally: + _fill_lock.release() + return key in self.data + + def __iter__(self): + if self.data is None: + _fill_lock.acquire() + try: + if self.data is None: + self._fill() + finally: + _fill_lock.release() + return iter(self.data) + + def __len__(self): + if self.data is None: + _fill_lock.acquire() + try: + if self.data is None: + self._fill() + finally: + _fill_lock.release() + return len(self.data) + + def keys(self): + if self.data is None: + _fill_lock.acquire() + try: + if self.data is None: + self._fill() + finally: + _fill_lock.release() + return self.data.keys() + + +class LazyList(list): + """List populated on first use.""" + + _props = [ + '__str__', '__repr__', '__unicode__', + '__hash__', '__sizeof__', '__cmp__', + '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', + 'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove', + 'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__', + '__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__', + '__getitem__', '__setitem__', '__delitem__', '__iter__', + '__reversed__', '__getslice__', '__setslice__', '__delslice__'] + + def __new__(cls, fill_iter=None): + + if fill_iter is None: + return list() + + # We need a new class as we will be dynamically messing with its + # methods. + class LazyList(list): + pass + + fill_iter = [fill_iter] + + def lazy(name): + def _lazy(self, *args, **kw): + _fill_lock.acquire() + try: + if len(fill_iter) > 0: + list.extend(self, fill_iter.pop()) + for method_name in cls._props: + delattr(LazyList, method_name) + finally: + _fill_lock.release() + return getattr(list, name)(self, *args, **kw) + return _lazy + + for name in cls._props: + setattr(LazyList, name, lazy(name)) + + new_list = LazyList() + return new_list + +# Not all versions of Python declare the same magic methods. +# Filter out properties that don't exist in this version of Python +# from the list. +LazyList._props = [prop for prop in LazyList._props if hasattr(list, prop)] + + +class LazySet(set): + """Set populated on first use.""" + + _props = ( + '__str__', '__repr__', '__unicode__', + '__hash__', '__sizeof__', '__cmp__', + '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', + '__contains__', '__len__', '__nonzero__', + '__getitem__', '__setitem__', '__delitem__', '__iter__', + '__sub__', '__and__', '__xor__', '__or__', + '__rsub__', '__rand__', '__rxor__', '__ror__', + '__isub__', '__iand__', '__ixor__', '__ior__', + 'add', 'clear', 'copy', 'difference', 'difference_update', + 'discard', 'intersection', 'intersection_update', 'isdisjoint', + 'issubset', 'issuperset', 'pop', 'remove', + 'symmetric_difference', 'symmetric_difference_update', + 'union', 'update') + + def __new__(cls, fill_iter=None): + + if fill_iter is None: + return set() + + class LazySet(set): + pass + + fill_iter = [fill_iter] + + def lazy(name): + def _lazy(self, *args, **kw): + _fill_lock.acquire() + try: + if len(fill_iter) > 0: + for i in fill_iter.pop(): + set.add(self, i) + for method_name in cls._props: + delattr(LazySet, method_name) + finally: + _fill_lock.release() + return getattr(set, name)(self, *args, **kw) + return _lazy + + for name in cls._props: + setattr(LazySet, name, lazy(name)) + + new_set = LazySet() + return new_set + +# Not all versions of Python declare the same magic methods. +# Filter out properties that don't exist in this version of Python +# from the list. +LazySet._props = [prop for prop in LazySet._props if hasattr(set, prop)] diff --git a/venv/Lib/site-packages/pytz/reference.py b/venv/Lib/site-packages/pytz/reference.py new file mode 100644 index 0000000..f765ca0 --- /dev/null +++ b/venv/Lib/site-packages/pytz/reference.py @@ -0,0 +1,140 @@ +''' +Reference tzinfo implementations from the Python docs. +Used for testing against as they are only correct for the years +1987 to 2006. Do not use these for real code. +''' + +from datetime import tzinfo, timedelta, datetime +from pytz import HOUR, ZERO, UTC + +__all__ = [ + 'FixedOffset', + 'LocalTimezone', + 'USTimeZone', + 'Eastern', + 'Central', + 'Mountain', + 'Pacific', + 'UTC' +] + + +# A class building tzinfo objects for fixed-offset time zones. +# Note that FixedOffset(0, "UTC") is a different way to build a +# UTC tzinfo object. +class FixedOffset(tzinfo): + """Fixed offset in minutes east from UTC.""" + + def __init__(self, offset, name): + self.__offset = timedelta(minutes=offset) + self.__name = name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + + +import time as _time + +STDOFFSET = timedelta(seconds=-_time.timezone) +if _time.daylight: + DSTOFFSET = timedelta(seconds=-_time.altzone) +else: + DSTOFFSET = STDOFFSET + +DSTDIFF = DSTOFFSET - STDOFFSET + + +# A class capturing the platform's idea of local time. +class LocalTimezone(tzinfo): + + def utcoffset(self, dt): + if self._isdst(dt): + return DSTOFFSET + else: + return STDOFFSET + + def dst(self, dt): + if self._isdst(dt): + return DSTDIFF + else: + return ZERO + + def tzname(self, dt): + return _time.tzname[self._isdst(dt)] + + def _isdst(self, dt): + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, -1) + stamp = _time.mktime(tt) + tt = _time.localtime(stamp) + return tt.tm_isdst > 0 + +Local = LocalTimezone() + + +def first_sunday_on_or_after(dt): + days_to_go = 6 - dt.weekday() + if days_to_go: + dt += timedelta(days_to_go) + return dt + + +# In the US, DST starts at 2am (standard time) on the first Sunday in April. +DSTSTART = datetime(1, 4, 1, 2) +# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct. +# which is the first Sunday on or after Oct 25. +DSTEND = datetime(1, 10, 25, 1) + + +# A complete implementation of current DST rules for major US time zones. +class USTimeZone(tzinfo): + + def __init__(self, hours, reprname, stdname, dstname): + self.stdoffset = timedelta(hours=hours) + self.reprname = reprname + self.stdname = stdname + self.dstname = dstname + + def __repr__(self): + return self.reprname + + def tzname(self, dt): + if self.dst(dt): + return self.dstname + else: + return self.stdname + + def utcoffset(self, dt): + return self.stdoffset + self.dst(dt) + + def dst(self, dt): + if dt is None or dt.tzinfo is None: + # An exception may be sensible here, in one or both cases. + # It depends on how you want to treat them. The default + # fromutc() implementation (called by the default astimezone() + # implementation) passes a datetime with dt.tzinfo is self. + return ZERO + assert dt.tzinfo is self + + # Find first Sunday in April & the last in October. + start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) + end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) + + # Can't compare naive to aware objects, so strip the timezone from + # dt first. + if start <= dt.replace(tzinfo=None) < end: + return HOUR + else: + return ZERO + +Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") +Central = USTimeZone(-6, "Central", "CST", "CDT") +Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") +Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") diff --git a/venv/Lib/site-packages/pytz/tzfile.py b/venv/Lib/site-packages/pytz/tzfile.py new file mode 100644 index 0000000..99e7448 --- /dev/null +++ b/venv/Lib/site-packages/pytz/tzfile.py @@ -0,0 +1,133 @@ +''' +$Id: tzfile.py,v 1.8 2004/06/03 00:15:24 zenzen Exp $ +''' + +from datetime import datetime +from struct import unpack, calcsize + +from pytz.tzinfo import StaticTzInfo, DstTzInfo, memorized_ttinfo +from pytz.tzinfo import memorized_datetime, memorized_timedelta + + +def _byte_string(s): + """Cast a string or byte string to an ASCII byte string.""" + return s.encode('ASCII') + +_NULL = _byte_string('\0') + + +def _std_string(s): + """Cast a string or byte string to an ASCII string.""" + return str(s.decode('ASCII')) + + +def build_tzinfo(zone, fp): + head_fmt = '>4s c 15x 6l' + head_size = calcsize(head_fmt) + (magic, format, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, + typecnt, charcnt) = unpack(head_fmt, fp.read(head_size)) + + # Make sure it is a tzfile(5) file + assert magic == _byte_string('TZif'), 'Got magic %s' % repr(magic) + + # Read out the transition times, localtime indices and ttinfo structures. + data_fmt = '>%(timecnt)dl %(timecnt)dB %(ttinfo)s %(charcnt)ds' % dict( + timecnt=timecnt, ttinfo='lBB' * typecnt, charcnt=charcnt) + data_size = calcsize(data_fmt) + data = unpack(data_fmt, fp.read(data_size)) + + # make sure we unpacked the right number of values + assert len(data) == 2 * timecnt + 3 * typecnt + 1 + transitions = [memorized_datetime(trans) + for trans in data[:timecnt]] + lindexes = list(data[timecnt:2 * timecnt]) + ttinfo_raw = data[2 * timecnt:-1] + tznames_raw = data[-1] + del data + + # Process ttinfo into separate structs + ttinfo = [] + tznames = {} + i = 0 + while i < len(ttinfo_raw): + # have we looked up this timezone name yet? + tzname_offset = ttinfo_raw[i + 2] + if tzname_offset not in tznames: + nul = tznames_raw.find(_NULL, tzname_offset) + if nul < 0: + nul = len(tznames_raw) + tznames[tzname_offset] = _std_string( + tznames_raw[tzname_offset:nul]) + ttinfo.append((ttinfo_raw[i], + bool(ttinfo_raw[i + 1]), + tznames[tzname_offset])) + i += 3 + + # Now build the timezone object + if len(ttinfo) == 1 or len(transitions) == 0: + ttinfo[0][0], ttinfo[0][2] + cls = type(zone, (StaticTzInfo,), dict( + zone=zone, + _utcoffset=memorized_timedelta(ttinfo[0][0]), + _tzname=ttinfo[0][2])) + else: + # Early dates use the first standard time ttinfo + i = 0 + while ttinfo[i][1]: + i += 1 + if ttinfo[i] == ttinfo[lindexes[0]]: + transitions[0] = datetime.min + else: + transitions.insert(0, datetime.min) + lindexes.insert(0, i) + + # calculate transition info + transition_info = [] + for i in range(len(transitions)): + inf = ttinfo[lindexes[i]] + utcoffset = inf[0] + if not inf[1]: + dst = 0 + else: + for j in range(i - 1, -1, -1): + prev_inf = ttinfo[lindexes[j]] + if not prev_inf[1]: + break + dst = inf[0] - prev_inf[0] # dst offset + + # Bad dst? Look further. DST > 24 hours happens when + # a timzone has moved across the international dateline. + if dst <= 0 or dst > 3600 * 3: + for j in range(i + 1, len(transitions)): + stdinf = ttinfo[lindexes[j]] + if not stdinf[1]: + dst = inf[0] - stdinf[0] + if dst > 0: + break # Found a useful std time. + + tzname = inf[2] + + # Round utcoffset and dst to the nearest minute or the + # datetime library will complain. Conversions to these timezones + # might be up to plus or minus 30 seconds out, but it is + # the best we can do. + utcoffset = int((utcoffset + 30) // 60) * 60 + dst = int((dst + 30) // 60) * 60 + transition_info.append(memorized_ttinfo(utcoffset, dst, tzname)) + + cls = type(zone, (DstTzInfo,), dict( + zone=zone, + _utc_transition_times=transitions, + _transition_info=transition_info)) + + return cls() + +if __name__ == '__main__': + import os.path + from pprint import pprint + base = os.path.join(os.path.dirname(__file__), 'zoneinfo') + tz = build_tzinfo('Australia/Melbourne', + open(os.path.join(base, 'Australia', 'Melbourne'), 'rb')) + tz = build_tzinfo('US/Eastern', + open(os.path.join(base, 'US', 'Eastern'), 'rb')) + pprint(tz._utc_transition_times) diff --git a/venv/Lib/site-packages/pytz/tzinfo.py b/venv/Lib/site-packages/pytz/tzinfo.py new file mode 100644 index 0000000..725978d --- /dev/null +++ b/venv/Lib/site-packages/pytz/tzinfo.py @@ -0,0 +1,577 @@ +'''Base classes and helpers for building zone specific tzinfo classes''' + +from datetime import datetime, timedelta, tzinfo +from bisect import bisect_right +try: + set +except NameError: + from sets import Set as set + +import pytz +from pytz.exceptions import AmbiguousTimeError, NonExistentTimeError + +__all__ = [] + +_timedelta_cache = {} + + +def memorized_timedelta(seconds): + '''Create only one instance of each distinct timedelta''' + try: + return _timedelta_cache[seconds] + except KeyError: + delta = timedelta(seconds=seconds) + _timedelta_cache[seconds] = delta + return delta + +_epoch = datetime.utcfromtimestamp(0) +_datetime_cache = {0: _epoch} + + +def memorized_datetime(seconds): + '''Create only one instance of each distinct datetime''' + try: + return _datetime_cache[seconds] + except KeyError: + # NB. We can't just do datetime.utcfromtimestamp(seconds) as this + # fails with negative values under Windows (Bug #90096) + dt = _epoch + timedelta(seconds=seconds) + _datetime_cache[seconds] = dt + return dt + +_ttinfo_cache = {} + + +def memorized_ttinfo(*args): + '''Create only one instance of each distinct tuple''' + try: + return _ttinfo_cache[args] + except KeyError: + ttinfo = ( + memorized_timedelta(args[0]), + memorized_timedelta(args[1]), + args[2] + ) + _ttinfo_cache[args] = ttinfo + return ttinfo + +_notime = memorized_timedelta(0) + + +def _to_seconds(td): + '''Convert a timedelta to seconds''' + return td.seconds + td.days * 24 * 60 * 60 + + +class BaseTzInfo(tzinfo): + # Overridden in subclass + _utcoffset = None + _tzname = None + zone = None + + def __str__(self): + return self.zone + + +class StaticTzInfo(BaseTzInfo): + '''A timezone that has a constant offset from UTC + + These timezones are rare, as most locations have changed their + offset at some point in their history + ''' + def fromutc(self, dt): + '''See datetime.tzinfo.fromutc''' + if dt.tzinfo is not None and dt.tzinfo is not self: + raise ValueError('fromutc: dt.tzinfo is not self') + return (dt + self._utcoffset).replace(tzinfo=self) + + def utcoffset(self, dt, is_dst=None): + '''See datetime.tzinfo.utcoffset + + is_dst is ignored for StaticTzInfo, and exists only to + retain compatibility with DstTzInfo. + ''' + return self._utcoffset + + def dst(self, dt, is_dst=None): + '''See datetime.tzinfo.dst + + is_dst is ignored for StaticTzInfo, and exists only to + retain compatibility with DstTzInfo. + ''' + return _notime + + def tzname(self, dt, is_dst=None): + '''See datetime.tzinfo.tzname + + is_dst is ignored for StaticTzInfo, and exists only to + retain compatibility with DstTzInfo. + ''' + return self._tzname + + def localize(self, dt, is_dst=False): + '''Convert naive time to local time''' + if dt.tzinfo is not None: + raise ValueError('Not naive datetime (tzinfo is already set)') + return dt.replace(tzinfo=self) + + def normalize(self, dt, is_dst=False): + '''Correct the timezone information on the given datetime. + + This is normally a no-op, as StaticTzInfo timezones never have + ambiguous cases to correct: + + >>> from pytz import timezone + >>> gmt = timezone('GMT') + >>> isinstance(gmt, StaticTzInfo) + True + >>> dt = datetime(2011, 5, 8, 1, 2, 3, tzinfo=gmt) + >>> gmt.normalize(dt) is dt + True + + The supported method of converting between timezones is to use + datetime.astimezone(). Currently normalize() also works: + + >>> la = timezone('America/Los_Angeles') + >>> dt = la.localize(datetime(2011, 5, 7, 1, 2, 3)) + >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' + >>> gmt.normalize(dt).strftime(fmt) + '2011-05-07 08:02:03 GMT (+0000)' + ''' + if dt.tzinfo is self: + return dt + if dt.tzinfo is None: + raise ValueError('Naive time - no tzinfo set') + return dt.astimezone(self) + + def __repr__(self): + return '<StaticTzInfo %r>' % (self.zone,) + + def __reduce__(self): + # Special pickle to zone remains a singleton and to cope with + # database changes. + return pytz._p, (self.zone,) + + +class DstTzInfo(BaseTzInfo): + '''A timezone that has a variable offset from UTC + + The offset might change if daylight saving time comes into effect, + or at a point in history when the region decides to change their + timezone definition. + ''' + # Overridden in subclass + + # Sorted list of DST transition times, UTC + _utc_transition_times = None + + # [(utcoffset, dstoffset, tzname)] corresponding to + # _utc_transition_times entries + _transition_info = None + + zone = None + + # Set in __init__ + + _tzinfos = None + _dst = None # DST offset + + def __init__(self, _inf=None, _tzinfos=None): + if _inf: + self._tzinfos = _tzinfos + self._utcoffset, self._dst, self._tzname = _inf + else: + _tzinfos = {} + self._tzinfos = _tzinfos + self._utcoffset, self._dst, self._tzname = ( + self._transition_info[0]) + _tzinfos[self._transition_info[0]] = self + for inf in self._transition_info[1:]: + if inf not in _tzinfos: + _tzinfos[inf] = self.__class__(inf, _tzinfos) + + def fromutc(self, dt): + '''See datetime.tzinfo.fromutc''' + if (dt.tzinfo is not None and + getattr(dt.tzinfo, '_tzinfos', None) is not self._tzinfos): + raise ValueError('fromutc: dt.tzinfo is not self') + dt = dt.replace(tzinfo=None) + idx = max(0, bisect_right(self._utc_transition_times, dt) - 1) + inf = self._transition_info[idx] + return (dt + inf[0]).replace(tzinfo=self._tzinfos[inf]) + + def normalize(self, dt): + '''Correct the timezone information on the given datetime + + If date arithmetic crosses DST boundaries, the tzinfo + is not magically adjusted. This method normalizes the + tzinfo to the correct one. + + To test, first we need to do some setup + + >>> from pytz import timezone + >>> utc = timezone('UTC') + >>> eastern = timezone('US/Eastern') + >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' + + We next create a datetime right on an end-of-DST transition point, + the instant when the wallclocks are wound back one hour. + + >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) + >>> loc_dt = utc_dt.astimezone(eastern) + >>> loc_dt.strftime(fmt) + '2002-10-27 01:00:00 EST (-0500)' + + Now, if we subtract a few minutes from it, note that the timezone + information has not changed. + + >>> before = loc_dt - timedelta(minutes=10) + >>> before.strftime(fmt) + '2002-10-27 00:50:00 EST (-0500)' + + But we can fix that by calling the normalize method + + >>> before = eastern.normalize(before) + >>> before.strftime(fmt) + '2002-10-27 01:50:00 EDT (-0400)' + + The supported method of converting between timezones is to use + datetime.astimezone(). Currently, normalize() also works: + + >>> th = timezone('Asia/Bangkok') + >>> am = timezone('Europe/Amsterdam') + >>> dt = th.localize(datetime(2011, 5, 7, 1, 2, 3)) + >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' + >>> am.normalize(dt).strftime(fmt) + '2011-05-06 20:02:03 CEST (+0200)' + ''' + if dt.tzinfo is None: + raise ValueError('Naive time - no tzinfo set') + + # Convert dt in localtime to UTC + offset = dt.tzinfo._utcoffset + dt = dt.replace(tzinfo=None) + dt = dt - offset + # convert it back, and return it + return self.fromutc(dt) + + def localize(self, dt, is_dst=False): + '''Convert naive time to local time. + + This method should be used to construct localtimes, rather + than passing a tzinfo argument to a datetime constructor. + + is_dst is used to determine the correct timezone in the ambigous + period at the end of daylight saving time. + + >>> from pytz import timezone + >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' + >>> amdam = timezone('Europe/Amsterdam') + >>> dt = datetime(2004, 10, 31, 2, 0, 0) + >>> loc_dt1 = amdam.localize(dt, is_dst=True) + >>> loc_dt2 = amdam.localize(dt, is_dst=False) + >>> loc_dt1.strftime(fmt) + '2004-10-31 02:00:00 CEST (+0200)' + >>> loc_dt2.strftime(fmt) + '2004-10-31 02:00:00 CET (+0100)' + >>> str(loc_dt2 - loc_dt1) + '1:00:00' + + Use is_dst=None to raise an AmbiguousTimeError for ambiguous + times at the end of daylight saving time + + >>> try: + ... loc_dt1 = amdam.localize(dt, is_dst=None) + ... except AmbiguousTimeError: + ... print('Ambiguous') + Ambiguous + + is_dst defaults to False + + >>> amdam.localize(dt) == amdam.localize(dt, False) + True + + is_dst is also used to determine the correct timezone in the + wallclock times jumped over at the start of daylight saving time. + + >>> pacific = timezone('US/Pacific') + >>> dt = datetime(2008, 3, 9, 2, 0, 0) + >>> ploc_dt1 = pacific.localize(dt, is_dst=True) + >>> ploc_dt2 = pacific.localize(dt, is_dst=False) + >>> ploc_dt1.strftime(fmt) + '2008-03-09 02:00:00 PDT (-0700)' + >>> ploc_dt2.strftime(fmt) + '2008-03-09 02:00:00 PST (-0800)' + >>> str(ploc_dt2 - ploc_dt1) + '1:00:00' + + Use is_dst=None to raise a NonExistentTimeError for these skipped + times. + + >>> try: + ... loc_dt1 = pacific.localize(dt, is_dst=None) + ... except NonExistentTimeError: + ... print('Non-existent') + Non-existent + ''' + if dt.tzinfo is not None: + raise ValueError('Not naive datetime (tzinfo is already set)') + + # Find the two best possibilities. + possible_loc_dt = set() + for delta in [timedelta(days=-1), timedelta(days=1)]: + loc_dt = dt + delta + idx = max(0, bisect_right( + self._utc_transition_times, loc_dt) - 1) + inf = self._transition_info[idx] + tzinfo = self._tzinfos[inf] + loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) + if loc_dt.replace(tzinfo=None) == dt: + possible_loc_dt.add(loc_dt) + + if len(possible_loc_dt) == 1: + return possible_loc_dt.pop() + + # If there are no possibly correct timezones, we are attempting + # to convert a time that never happened - the time period jumped + # during the start-of-DST transition period. + if len(possible_loc_dt) == 0: + # If we refuse to guess, raise an exception. + if is_dst is None: + raise NonExistentTimeError(dt) + + # If we are forcing the pre-DST side of the DST transition, we + # obtain the correct timezone by winding the clock forward a few + # hours. + elif is_dst: + return self.localize( + dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6) + + # If we are forcing the post-DST side of the DST transition, we + # obtain the correct timezone by winding the clock back. + else: + return self.localize( + dt - timedelta(hours=6), + is_dst=False) + timedelta(hours=6) + + # If we get this far, we have multiple possible timezones - this + # is an ambiguous case occuring during the end-of-DST transition. + + # If told to be strict, raise an exception since we have an + # ambiguous case + if is_dst is None: + raise AmbiguousTimeError(dt) + + # Filter out the possiblilities that don't match the requested + # is_dst + filtered_possible_loc_dt = [ + p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst + ] + + # Hopefully we only have one possibility left. Return it. + if len(filtered_possible_loc_dt) == 1: + return filtered_possible_loc_dt[0] + + if len(filtered_possible_loc_dt) == 0: + filtered_possible_loc_dt = list(possible_loc_dt) + + # If we get this far, we have in a wierd timezone transition + # where the clocks have been wound back but is_dst is the same + # in both (eg. Europe/Warsaw 1915 when they switched to CET). + # At this point, we just have to guess unless we allow more + # hints to be passed in (such as the UTC offset or abbreviation), + # but that is just getting silly. + # + # Choose the earliest (by UTC) applicable timezone if is_dst=True + # Choose the latest (by UTC) applicable timezone if is_dst=False + # i.e., behave like end-of-DST transition + dates = {} # utc -> local + for local_dt in filtered_possible_loc_dt: + utc_time = ( + local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset) + assert utc_time not in dates + dates[utc_time] = local_dt + return dates[[min, max][not is_dst](dates)] + + def utcoffset(self, dt, is_dst=None): + '''See datetime.tzinfo.utcoffset + + The is_dst parameter may be used to remove ambiguity during DST + transitions. + + >>> from pytz import timezone + >>> tz = timezone('America/St_Johns') + >>> ambiguous = datetime(2009, 10, 31, 23, 30) + + >>> str(tz.utcoffset(ambiguous, is_dst=False)) + '-1 day, 20:30:00' + + >>> str(tz.utcoffset(ambiguous, is_dst=True)) + '-1 day, 21:30:00' + + >>> try: + ... tz.utcoffset(ambiguous) + ... except AmbiguousTimeError: + ... print('Ambiguous') + Ambiguous + + ''' + if dt is None: + return None + elif dt.tzinfo is not self: + dt = self.localize(dt, is_dst) + return dt.tzinfo._utcoffset + else: + return self._utcoffset + + def dst(self, dt, is_dst=None): + '''See datetime.tzinfo.dst + + The is_dst parameter may be used to remove ambiguity during DST + transitions. + + >>> from pytz import timezone + >>> tz = timezone('America/St_Johns') + + >>> normal = datetime(2009, 9, 1) + + >>> str(tz.dst(normal)) + '1:00:00' + >>> str(tz.dst(normal, is_dst=False)) + '1:00:00' + >>> str(tz.dst(normal, is_dst=True)) + '1:00:00' + + >>> ambiguous = datetime(2009, 10, 31, 23, 30) + + >>> str(tz.dst(ambiguous, is_dst=False)) + '0:00:00' + >>> str(tz.dst(ambiguous, is_dst=True)) + '1:00:00' + >>> try: + ... tz.dst(ambiguous) + ... except AmbiguousTimeError: + ... print('Ambiguous') + Ambiguous + + ''' + if dt is None: + return None + elif dt.tzinfo is not self: + dt = self.localize(dt, is_dst) + return dt.tzinfo._dst + else: + return self._dst + + def tzname(self, dt, is_dst=None): + '''See datetime.tzinfo.tzname + + The is_dst parameter may be used to remove ambiguity during DST + transitions. + + >>> from pytz import timezone + >>> tz = timezone('America/St_Johns') + + >>> normal = datetime(2009, 9, 1) + + >>> tz.tzname(normal) + 'NDT' + >>> tz.tzname(normal, is_dst=False) + 'NDT' + >>> tz.tzname(normal, is_dst=True) + 'NDT' + + >>> ambiguous = datetime(2009, 10, 31, 23, 30) + + >>> tz.tzname(ambiguous, is_dst=False) + 'NST' + >>> tz.tzname(ambiguous, is_dst=True) + 'NDT' + >>> try: + ... tz.tzname(ambiguous) + ... except AmbiguousTimeError: + ... print('Ambiguous') + Ambiguous + ''' + if dt is None: + return self.zone + elif dt.tzinfo is not self: + dt = self.localize(dt, is_dst) + return dt.tzinfo._tzname + else: + return self._tzname + + def __repr__(self): + if self._dst: + dst = 'DST' + else: + dst = 'STD' + if self._utcoffset > _notime: + return '<DstTzInfo %r %s+%s %s>' % ( + self.zone, self._tzname, self._utcoffset, dst + ) + else: + return '<DstTzInfo %r %s%s %s>' % ( + self.zone, self._tzname, self._utcoffset, dst + ) + + def __reduce__(self): + # Special pickle to zone remains a singleton and to cope with + # database changes. + return pytz._p, ( + self.zone, + _to_seconds(self._utcoffset), + _to_seconds(self._dst), + self._tzname + ) + + +def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None): + """Factory function for unpickling pytz tzinfo instances. + + This is shared for both StaticTzInfo and DstTzInfo instances, because + database changes could cause a zones implementation to switch between + these two base classes and we can't break pickles on a pytz version + upgrade. + """ + # Raises a KeyError if zone no longer exists, which should never happen + # and would be a bug. + tz = pytz.timezone(zone) + + # A StaticTzInfo - just return it + if utcoffset is None: + return tz + + # This pickle was created from a DstTzInfo. We need to + # determine which of the list of tzinfo instances for this zone + # to use in order to restore the state of any datetime instances using + # it correctly. + utcoffset = memorized_timedelta(utcoffset) + dstoffset = memorized_timedelta(dstoffset) + try: + return tz._tzinfos[(utcoffset, dstoffset, tzname)] + except KeyError: + # The particular state requested in this timezone no longer exists. + # This indicates a corrupt pickle, or the timezone database has been + # corrected violently enough to make this particular + # (utcoffset,dstoffset) no longer exist in the zone, or the + # abbreviation has been changed. + pass + + # See if we can find an entry differing only by tzname. Abbreviations + # get changed from the initial guess by the database maintainers to + # match reality when this information is discovered. + for localized_tz in tz._tzinfos.values(): + if (localized_tz._utcoffset == utcoffset and + localized_tz._dst == dstoffset): + return localized_tz + + # This (utcoffset, dstoffset) information has been removed from the + # zone. Add it back. This might occur when the database maintainers have + # corrected incorrect information. datetime instances using this + # incorrect information will continue to do so, exactly as they were + # before being pickled. This is purely an overly paranoid safety net - I + # doubt this will ever been needed in real life. + inf = (utcoffset, dstoffset, tzname) + tz._tzinfos[inf] = tz.__class__(inf, tz._tzinfos) + return tz._tzinfos[inf] diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Abidjan b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Abidjan new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Abidjan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Accra b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Accra new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Accra differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Addis_Ababa b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Addis_Ababa new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Addis_Ababa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Algiers b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Algiers new file mode 100644 index 0000000..6cfd8a1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Algiers differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmara b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmara new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmara differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmera b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmera new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Asmera differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bamako b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bamako new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bamako differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bangui b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bangui new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bangui differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Banjul b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Banjul new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Banjul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bissau b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bissau new file mode 100644 index 0000000..82ea5aa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bissau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Blantyre b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Blantyre new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Blantyre differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Brazzaville b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Brazzaville new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Brazzaville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bujumbura b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bujumbura new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Bujumbura differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Cairo b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Cairo new file mode 100644 index 0000000..d3f8196 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Cairo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Casablanca b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Casablanca new file mode 100644 index 0000000..17e0d1b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Casablanca differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ceuta b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ceuta new file mode 100644 index 0000000..850c8f0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ceuta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Conakry b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Conakry new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Conakry differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dakar b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dakar new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dakar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dar_es_Salaam b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dar_es_Salaam new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Dar_es_Salaam differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Djibouti b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Djibouti new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Djibouti differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Douala b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Douala new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Douala differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/El_Aaiun b/venv/Lib/site-packages/pytz/zoneinfo/Africa/El_Aaiun new file mode 100644 index 0000000..64f1b76 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/El_Aaiun differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Freetown b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Freetown new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Freetown differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Gaborone b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Gaborone new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Gaborone differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Harare b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Harare new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Harare differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Johannesburg b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Johannesburg new file mode 100644 index 0000000..b1c425d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Johannesburg differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Juba b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Juba new file mode 100644 index 0000000..0648294 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Juba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kampala b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kampala new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kampala differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Khartoum b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Khartoum new file mode 100644 index 0000000..8ee8cb9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Khartoum differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kigali b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kigali new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kigali differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kinshasa b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kinshasa new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Kinshasa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lagos b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lagos new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lagos differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Libreville b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Libreville new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Libreville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lome b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lome new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lome differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Luanda b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Luanda new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Luanda differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lubumbashi b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lubumbashi new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lubumbashi differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lusaka b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lusaka new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Lusaka differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Malabo b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Malabo new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Malabo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maputo b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maputo new file mode 100644 index 0000000..52753c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maputo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maseru b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maseru new file mode 100644 index 0000000..b1c425d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Maseru differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mbabane b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mbabane new file mode 100644 index 0000000..b1c425d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mbabane differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mogadishu b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mogadishu new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Mogadishu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Monrovia b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Monrovia new file mode 100644 index 0000000..6d68850 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Monrovia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nairobi b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nairobi new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nairobi differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ndjamena b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ndjamena new file mode 100644 index 0000000..a968845 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ndjamena differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Niamey b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Niamey new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Niamey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nouakchott b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nouakchott new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Nouakchott differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ouagadougou b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ouagadougou new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Ouagadougou differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Porto-Novo b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Porto-Novo new file mode 100644 index 0000000..afb6a4a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Porto-Novo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Sao_Tome b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Sao_Tome new file mode 100644 index 0000000..59f3759 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Sao_Tome differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Timbuktu b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Timbuktu new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Timbuktu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tripoli b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tripoli new file mode 100644 index 0000000..07b393b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tripoli differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tunis b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tunis new file mode 100644 index 0000000..427fa56 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Tunis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Africa/Windhoek b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Windhoek new file mode 100644 index 0000000..abecd13 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Africa/Windhoek differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Adak b/venv/Lib/site-packages/pytz/zoneinfo/America/Adak new file mode 100644 index 0000000..4323649 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Adak differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Anchorage b/venv/Lib/site-packages/pytz/zoneinfo/America/Anchorage new file mode 100644 index 0000000..9bbb2fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Anchorage differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Anguilla b/venv/Lib/site-packages/pytz/zoneinfo/America/Anguilla new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Anguilla differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Antigua b/venv/Lib/site-packages/pytz/zoneinfo/America/Antigua new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Antigua differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Araguaina b/venv/Lib/site-packages/pytz/zoneinfo/America/Araguaina new file mode 100644 index 0000000..49381b4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Araguaina differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Buenos_Aires b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Buenos_Aires new file mode 100644 index 0000000..260f86a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Buenos_Aires differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Catamarca b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Catamarca new file mode 100644 index 0000000..0ae222a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Catamarca differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/ComodRivadavia b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/ComodRivadavia new file mode 100644 index 0000000..0ae222a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/ComodRivadavia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Cordoba b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Cordoba new file mode 100644 index 0000000..da4c23a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Cordoba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Jujuy b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Jujuy new file mode 100644 index 0000000..604b856 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Jujuy differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/La_Rioja b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/La_Rioja new file mode 100644 index 0000000..2218e36 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/La_Rioja differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Mendoza b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Mendoza new file mode 100644 index 0000000..f9e677f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Mendoza differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Rio_Gallegos b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Rio_Gallegos new file mode 100644 index 0000000..c36587e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Rio_Gallegos differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Salta b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Salta new file mode 100644 index 0000000..0e797f2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Salta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Juan b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Juan new file mode 100644 index 0000000..2698495 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Juan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Luis b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Luis new file mode 100644 index 0000000..fe50f62 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/San_Luis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Tucuman b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Tucuman new file mode 100644 index 0000000..c954000 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Tucuman differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Ushuaia b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Ushuaia new file mode 100644 index 0000000..3643628 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Argentina/Ushuaia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Aruba b/venv/Lib/site-packages/pytz/zoneinfo/America/Aruba new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Aruba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Asuncion b/venv/Lib/site-packages/pytz/zoneinfo/America/Asuncion new file mode 100644 index 0000000..2f3bbda Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Asuncion differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Atikokan b/venv/Lib/site-packages/pytz/zoneinfo/America/Atikokan new file mode 100644 index 0000000..9964b9a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Atikokan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Atka b/venv/Lib/site-packages/pytz/zoneinfo/America/Atka new file mode 100644 index 0000000..4323649 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Atka differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia b/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia new file mode 100644 index 0000000..15808d3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia_Banderas b/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia_Banderas new file mode 100644 index 0000000..896af3f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Bahia_Banderas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Barbados b/venv/Lib/site-packages/pytz/zoneinfo/America/Barbados new file mode 100644 index 0000000..00cd045 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Barbados differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Belem b/venv/Lib/site-packages/pytz/zoneinfo/America/Belem new file mode 100644 index 0000000..60b5924 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Belem differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Belize b/venv/Lib/site-packages/pytz/zoneinfo/America/Belize new file mode 100644 index 0000000..e6f5dfa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Belize differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Blanc-Sablon b/venv/Lib/site-packages/pytz/zoneinfo/America/Blanc-Sablon new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Blanc-Sablon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Boa_Vista b/venv/Lib/site-packages/pytz/zoneinfo/America/Boa_Vista new file mode 100644 index 0000000..978c331 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Boa_Vista differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Bogota b/venv/Lib/site-packages/pytz/zoneinfo/America/Bogota new file mode 100644 index 0000000..b2647d7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Bogota differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Boise b/venv/Lib/site-packages/pytz/zoneinfo/America/Boise new file mode 100644 index 0000000..f8d54e2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Boise differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Buenos_Aires b/venv/Lib/site-packages/pytz/zoneinfo/America/Buenos_Aires new file mode 100644 index 0000000..260f86a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Buenos_Aires differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cambridge_Bay b/venv/Lib/site-packages/pytz/zoneinfo/America/Cambridge_Bay new file mode 100644 index 0000000..f8db4b6 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cambridge_Bay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Campo_Grande b/venv/Lib/site-packages/pytz/zoneinfo/America/Campo_Grande new file mode 100644 index 0000000..8120624 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Campo_Grande differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cancun b/venv/Lib/site-packages/pytz/zoneinfo/America/Cancun new file mode 100644 index 0000000..f907f0a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cancun differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Caracas b/venv/Lib/site-packages/pytz/zoneinfo/America/Caracas new file mode 100644 index 0000000..eedf725 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Caracas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Catamarca b/venv/Lib/site-packages/pytz/zoneinfo/America/Catamarca new file mode 100644 index 0000000..0ae222a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Catamarca differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cayenne b/venv/Lib/site-packages/pytz/zoneinfo/America/Cayenne new file mode 100644 index 0000000..e5bc06f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cayenne differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cayman b/venv/Lib/site-packages/pytz/zoneinfo/America/Cayman new file mode 100644 index 0000000..9964b9a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cayman differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Chicago b/venv/Lib/site-packages/pytz/zoneinfo/America/Chicago new file mode 100644 index 0000000..a5b1617 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Chicago differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Chihuahua b/venv/Lib/site-packages/pytz/zoneinfo/America/Chihuahua new file mode 100644 index 0000000..8ed5f93 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Chihuahua differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Coral_Harbour b/venv/Lib/site-packages/pytz/zoneinfo/America/Coral_Harbour new file mode 100644 index 0000000..9964b9a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Coral_Harbour differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cordoba b/venv/Lib/site-packages/pytz/zoneinfo/America/Cordoba new file mode 100644 index 0000000..da4c23a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cordoba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Costa_Rica b/venv/Lib/site-packages/pytz/zoneinfo/America/Costa_Rica new file mode 100644 index 0000000..37cb85e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Costa_Rica differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Creston b/venv/Lib/site-packages/pytz/zoneinfo/America/Creston new file mode 100644 index 0000000..ac6bb0c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Creston differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Cuiaba b/venv/Lib/site-packages/pytz/zoneinfo/America/Cuiaba new file mode 100644 index 0000000..9bea3d4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Cuiaba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Curacao b/venv/Lib/site-packages/pytz/zoneinfo/America/Curacao new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Curacao differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Danmarkshavn b/venv/Lib/site-packages/pytz/zoneinfo/America/Danmarkshavn new file mode 100644 index 0000000..9549adc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Danmarkshavn differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson b/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson new file mode 100644 index 0000000..343b632 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson_Creek b/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson_Creek new file mode 100644 index 0000000..db9e339 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Dawson_Creek differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Denver b/venv/Lib/site-packages/pytz/zoneinfo/America/Denver new file mode 100644 index 0000000..5fbe26b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Denver differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Detroit b/venv/Lib/site-packages/pytz/zoneinfo/America/Detroit new file mode 100644 index 0000000..e104faa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Detroit differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Dominica b/venv/Lib/site-packages/pytz/zoneinfo/America/Dominica new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Dominica differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Edmonton b/venv/Lib/site-packages/pytz/zoneinfo/America/Edmonton new file mode 100644 index 0000000..cd78a6f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Edmonton differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Eirunepe b/venv/Lib/site-packages/pytz/zoneinfo/America/Eirunepe new file mode 100644 index 0000000..39d6dae Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Eirunepe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/El_Salvador b/venv/Lib/site-packages/pytz/zoneinfo/America/El_Salvador new file mode 100644 index 0000000..e2f2230 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/El_Salvador differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Ensenada b/venv/Lib/site-packages/pytz/zoneinfo/America/Ensenada new file mode 100644 index 0000000..ada6bf7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Ensenada differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Nelson b/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Nelson new file mode 100644 index 0000000..5a0b7f1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Nelson differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Wayne b/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Wayne new file mode 100644 index 0000000..09511cc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Fort_Wayne differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Fortaleza b/venv/Lib/site-packages/pytz/zoneinfo/America/Fortaleza new file mode 100644 index 0000000..be57dc2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Fortaleza differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Glace_Bay b/venv/Lib/site-packages/pytz/zoneinfo/America/Glace_Bay new file mode 100644 index 0000000..48412a4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Glace_Bay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Godthab b/venv/Lib/site-packages/pytz/zoneinfo/America/Godthab new file mode 100644 index 0000000..0160308 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Godthab differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Goose_Bay b/venv/Lib/site-packages/pytz/zoneinfo/America/Goose_Bay new file mode 100644 index 0000000..a3f2990 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Goose_Bay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Grand_Turk b/venv/Lib/site-packages/pytz/zoneinfo/America/Grand_Turk new file mode 100644 index 0000000..06da1a6 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Grand_Turk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Grenada b/venv/Lib/site-packages/pytz/zoneinfo/America/Grenada new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Grenada differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Guadeloupe b/venv/Lib/site-packages/pytz/zoneinfo/America/Guadeloupe new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Guadeloupe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Guatemala b/venv/Lib/site-packages/pytz/zoneinfo/America/Guatemala new file mode 100644 index 0000000..407138c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Guatemala differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Guayaquil b/venv/Lib/site-packages/pytz/zoneinfo/America/Guayaquil new file mode 100644 index 0000000..0559a7a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Guayaquil differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Guyana b/venv/Lib/site-packages/pytz/zoneinfo/America/Guyana new file mode 100644 index 0000000..7af58e5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Guyana differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Halifax b/venv/Lib/site-packages/pytz/zoneinfo/America/Halifax new file mode 100644 index 0000000..756099a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Halifax differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Havana b/venv/Lib/site-packages/pytz/zoneinfo/America/Havana new file mode 100644 index 0000000..b69ac45 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Havana differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Hermosillo b/venv/Lib/site-packages/pytz/zoneinfo/America/Hermosillo new file mode 100644 index 0000000..791a9fa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Hermosillo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Indianapolis b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Indianapolis new file mode 100644 index 0000000..09511cc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Indianapolis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Knox b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Knox new file mode 100644 index 0000000..fcd408d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Knox differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Marengo b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Marengo new file mode 100644 index 0000000..1abf75e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Marengo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Petersburg b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Petersburg new file mode 100644 index 0000000..0133548 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Petersburg differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Tell_City b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Tell_City new file mode 100644 index 0000000..7bbb653 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Tell_City differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vevay b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vevay new file mode 100644 index 0000000..d236b7c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vevay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vincennes b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vincennes new file mode 100644 index 0000000..c818929 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Vincennes differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Winamac b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Winamac new file mode 100644 index 0000000..630935c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indiana/Winamac differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Indianapolis b/venv/Lib/site-packages/pytz/zoneinfo/America/Indianapolis new file mode 100644 index 0000000..09511cc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Indianapolis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Inuvik b/venv/Lib/site-packages/pytz/zoneinfo/America/Inuvik new file mode 100644 index 0000000..87bb355 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Inuvik differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Iqaluit b/venv/Lib/site-packages/pytz/zoneinfo/America/Iqaluit new file mode 100644 index 0000000..c8138bd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Iqaluit differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Jamaica b/venv/Lib/site-packages/pytz/zoneinfo/America/Jamaica new file mode 100644 index 0000000..2a9b7fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Jamaica differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Jujuy b/venv/Lib/site-packages/pytz/zoneinfo/America/Jujuy new file mode 100644 index 0000000..604b856 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Jujuy differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Juneau b/venv/Lib/site-packages/pytz/zoneinfo/America/Juneau new file mode 100644 index 0000000..451f349 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Juneau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Louisville b/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Louisville new file mode 100644 index 0000000..177836e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Louisville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Monticello b/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Monticello new file mode 100644 index 0000000..438e3ea Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Kentucky/Monticello differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Knox_IN b/venv/Lib/site-packages/pytz/zoneinfo/America/Knox_IN new file mode 100644 index 0000000..fcd408d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Knox_IN differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Kralendijk b/venv/Lib/site-packages/pytz/zoneinfo/America/Kralendijk new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Kralendijk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/La_Paz b/venv/Lib/site-packages/pytz/zoneinfo/America/La_Paz new file mode 100644 index 0000000..a101372 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/La_Paz differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Lima b/venv/Lib/site-packages/pytz/zoneinfo/America/Lima new file mode 100644 index 0000000..3c6529b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Lima differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Los_Angeles b/venv/Lib/site-packages/pytz/zoneinfo/America/Los_Angeles new file mode 100644 index 0000000..9dad4f4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Los_Angeles differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Louisville b/venv/Lib/site-packages/pytz/zoneinfo/America/Louisville new file mode 100644 index 0000000..177836e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Louisville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Lower_Princes b/venv/Lib/site-packages/pytz/zoneinfo/America/Lower_Princes new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Lower_Princes differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Maceio b/venv/Lib/site-packages/pytz/zoneinfo/America/Maceio new file mode 100644 index 0000000..bc8b951 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Maceio differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Managua b/venv/Lib/site-packages/pytz/zoneinfo/America/Managua new file mode 100644 index 0000000..e0242bf Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Managua differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Manaus b/venv/Lib/site-packages/pytz/zoneinfo/America/Manaus new file mode 100644 index 0000000..63d58f8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Manaus differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Marigot b/venv/Lib/site-packages/pytz/zoneinfo/America/Marigot new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Marigot differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Martinique b/venv/Lib/site-packages/pytz/zoneinfo/America/Martinique new file mode 100644 index 0000000..8df43dc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Martinique differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Matamoros b/venv/Lib/site-packages/pytz/zoneinfo/America/Matamoros new file mode 100644 index 0000000..047968d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Matamoros differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Mazatlan b/venv/Lib/site-packages/pytz/zoneinfo/America/Mazatlan new file mode 100644 index 0000000..e4a7857 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Mazatlan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Mendoza b/venv/Lib/site-packages/pytz/zoneinfo/America/Mendoza new file mode 100644 index 0000000..f9e677f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Mendoza differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Menominee b/venv/Lib/site-packages/pytz/zoneinfo/America/Menominee new file mode 100644 index 0000000..3146138 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Menominee differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Merida b/venv/Lib/site-packages/pytz/zoneinfo/America/Merida new file mode 100644 index 0000000..ea852da Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Merida differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Metlakatla b/venv/Lib/site-packages/pytz/zoneinfo/America/Metlakatla new file mode 100644 index 0000000..1e94be3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Metlakatla differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Mexico_City b/venv/Lib/site-packages/pytz/zoneinfo/America/Mexico_City new file mode 100644 index 0000000..e7fb6f2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Mexico_City differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Miquelon b/venv/Lib/site-packages/pytz/zoneinfo/America/Miquelon new file mode 100644 index 0000000..b924b71 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Miquelon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Moncton b/venv/Lib/site-packages/pytz/zoneinfo/America/Moncton new file mode 100644 index 0000000..9df8d0f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Moncton differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Monterrey b/venv/Lib/site-packages/pytz/zoneinfo/America/Monterrey new file mode 100644 index 0000000..a8928c8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Monterrey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Montevideo b/venv/Lib/site-packages/pytz/zoneinfo/America/Montevideo new file mode 100644 index 0000000..2f357bc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Montevideo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Montreal b/venv/Lib/site-packages/pytz/zoneinfo/America/Montreal new file mode 100644 index 0000000..6752c5b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Montreal differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Montserrat b/venv/Lib/site-packages/pytz/zoneinfo/America/Montserrat new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Montserrat differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Nassau b/venv/Lib/site-packages/pytz/zoneinfo/America/Nassau new file mode 100644 index 0000000..6752c5b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Nassau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/New_York b/venv/Lib/site-packages/pytz/zoneinfo/America/New_York new file mode 100644 index 0000000..2f75480 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/New_York differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Nipigon b/venv/Lib/site-packages/pytz/zoneinfo/America/Nipigon new file mode 100644 index 0000000..f6a856e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Nipigon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Nome b/venv/Lib/site-packages/pytz/zoneinfo/America/Nome new file mode 100644 index 0000000..10998df Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Nome differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Noronha b/venv/Lib/site-packages/pytz/zoneinfo/America/Noronha new file mode 100644 index 0000000..f140726 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Noronha differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Beulah b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Beulah new file mode 100644 index 0000000..246345d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Beulah differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Center b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Center new file mode 100644 index 0000000..1fa0703 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/Center differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/New_Salem b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/New_Salem new file mode 100644 index 0000000..123f2ae Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/North_Dakota/New_Salem differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Nuuk b/venv/Lib/site-packages/pytz/zoneinfo/America/Nuuk new file mode 100644 index 0000000..0160308 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Nuuk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Ojinaga b/venv/Lib/site-packages/pytz/zoneinfo/America/Ojinaga new file mode 100644 index 0000000..fc4a03e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Ojinaga differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Panama b/venv/Lib/site-packages/pytz/zoneinfo/America/Panama new file mode 100644 index 0000000..9964b9a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Panama differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Pangnirtung b/venv/Lib/site-packages/pytz/zoneinfo/America/Pangnirtung new file mode 100644 index 0000000..3e4e0db Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Pangnirtung differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Paramaribo b/venv/Lib/site-packages/pytz/zoneinfo/America/Paramaribo new file mode 100644 index 0000000..bc8a6ed Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Paramaribo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Phoenix b/venv/Lib/site-packages/pytz/zoneinfo/America/Phoenix new file mode 100644 index 0000000..ac6bb0c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Phoenix differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Port-au-Prince b/venv/Lib/site-packages/pytz/zoneinfo/America/Port-au-Prince new file mode 100644 index 0000000..287f143 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Port-au-Prince differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Port_of_Spain b/venv/Lib/site-packages/pytz/zoneinfo/America/Port_of_Spain new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Port_of_Spain differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Acre b/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Acre new file mode 100644 index 0000000..a374cb4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Acre differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Velho b/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Velho new file mode 100644 index 0000000..2e873a5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Porto_Velho differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Puerto_Rico b/venv/Lib/site-packages/pytz/zoneinfo/America/Puerto_Rico new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Puerto_Rico differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Punta_Arenas b/venv/Lib/site-packages/pytz/zoneinfo/America/Punta_Arenas new file mode 100644 index 0000000..13bd1d9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Punta_Arenas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Rainy_River b/venv/Lib/site-packages/pytz/zoneinfo/America/Rainy_River new file mode 100644 index 0000000..ea66099 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Rainy_River differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Rankin_Inlet b/venv/Lib/site-packages/pytz/zoneinfo/America/Rankin_Inlet new file mode 100644 index 0000000..3a70587 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Rankin_Inlet differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Recife b/venv/Lib/site-packages/pytz/zoneinfo/America/Recife new file mode 100644 index 0000000..d7abb16 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Recife differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Regina b/venv/Lib/site-packages/pytz/zoneinfo/America/Regina new file mode 100644 index 0000000..20c9c84 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Regina differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Resolute b/venv/Lib/site-packages/pytz/zoneinfo/America/Resolute new file mode 100644 index 0000000..0a73b75 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Resolute differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Rio_Branco b/venv/Lib/site-packages/pytz/zoneinfo/America/Rio_Branco new file mode 100644 index 0000000..a374cb4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Rio_Branco differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Rosario b/venv/Lib/site-packages/pytz/zoneinfo/America/Rosario new file mode 100644 index 0000000..da4c23a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Rosario differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Santa_Isabel b/venv/Lib/site-packages/pytz/zoneinfo/America/Santa_Isabel new file mode 100644 index 0000000..ada6bf7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Santa_Isabel differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Santarem b/venv/Lib/site-packages/pytz/zoneinfo/America/Santarem new file mode 100644 index 0000000..c28f360 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Santarem differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Santiago b/venv/Lib/site-packages/pytz/zoneinfo/America/Santiago new file mode 100644 index 0000000..aa29060 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Santiago differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Santo_Domingo b/venv/Lib/site-packages/pytz/zoneinfo/America/Santo_Domingo new file mode 100644 index 0000000..4fe36fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Santo_Domingo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Sao_Paulo b/venv/Lib/site-packages/pytz/zoneinfo/America/Sao_Paulo new file mode 100644 index 0000000..13ff083 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Sao_Paulo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Scoresbysund b/venv/Lib/site-packages/pytz/zoneinfo/America/Scoresbysund new file mode 100644 index 0000000..e20e9e1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Scoresbysund differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Shiprock b/venv/Lib/site-packages/pytz/zoneinfo/America/Shiprock new file mode 100644 index 0000000..5fbe26b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Shiprock differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Sitka b/venv/Lib/site-packages/pytz/zoneinfo/America/Sitka new file mode 100644 index 0000000..31f7061 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Sitka differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Barthelemy b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Barthelemy new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Barthelemy differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Johns b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Johns new file mode 100644 index 0000000..65a5b0c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Johns differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Kitts b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Kitts new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Kitts differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Lucia b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Lucia new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Lucia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Thomas b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Thomas new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Thomas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/St_Vincent b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Vincent new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/St_Vincent differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Swift_Current b/venv/Lib/site-packages/pytz/zoneinfo/America/Swift_Current new file mode 100644 index 0000000..8e9ef25 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Swift_Current differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Tegucigalpa b/venv/Lib/site-packages/pytz/zoneinfo/America/Tegucigalpa new file mode 100644 index 0000000..2adacb2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Tegucigalpa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Thule b/venv/Lib/site-packages/pytz/zoneinfo/America/Thule new file mode 100644 index 0000000..6f802f1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Thule differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Thunder_Bay b/venv/Lib/site-packages/pytz/zoneinfo/America/Thunder_Bay new file mode 100644 index 0000000..e504c9a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Thunder_Bay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Tijuana b/venv/Lib/site-packages/pytz/zoneinfo/America/Tijuana new file mode 100644 index 0000000..ada6bf7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Tijuana differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Toronto b/venv/Lib/site-packages/pytz/zoneinfo/America/Toronto new file mode 100644 index 0000000..6752c5b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Toronto differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Tortola b/venv/Lib/site-packages/pytz/zoneinfo/America/Tortola new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Tortola differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Vancouver b/venv/Lib/site-packages/pytz/zoneinfo/America/Vancouver new file mode 100644 index 0000000..bb60cbc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Vancouver differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Virgin b/venv/Lib/site-packages/pytz/zoneinfo/America/Virgin new file mode 100644 index 0000000..a662a57 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Virgin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Whitehorse b/venv/Lib/site-packages/pytz/zoneinfo/America/Whitehorse new file mode 100644 index 0000000..9ee229c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Whitehorse differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Winnipeg b/venv/Lib/site-packages/pytz/zoneinfo/America/Winnipeg new file mode 100644 index 0000000..ac40299 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Winnipeg differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Yakutat b/venv/Lib/site-packages/pytz/zoneinfo/America/Yakutat new file mode 100644 index 0000000..da209f9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Yakutat differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/America/Yellowknife b/venv/Lib/site-packages/pytz/zoneinfo/America/Yellowknife new file mode 100644 index 0000000..e6afa39 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/America/Yellowknife differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Casey b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Casey new file mode 100644 index 0000000..cbcbe4e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Casey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Davis b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Davis new file mode 100644 index 0000000..916f2c2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Davis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/DumontDUrville b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/DumontDUrville new file mode 100644 index 0000000..920ad27 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/DumontDUrville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Macquarie b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Macquarie new file mode 100644 index 0000000..9e7cc68 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Macquarie differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Mawson b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Mawson new file mode 100644 index 0000000..b32e7fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Mawson differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/McMurdo b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/McMurdo new file mode 100644 index 0000000..6575fdc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/McMurdo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Palmer b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Palmer new file mode 100644 index 0000000..3dd85f8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Palmer differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Rothera b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Rothera new file mode 100644 index 0000000..8b2430a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Rothera differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/South_Pole b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/South_Pole new file mode 100644 index 0000000..6575fdc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/South_Pole differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Syowa b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Syowa new file mode 100644 index 0000000..2aea25f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Syowa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Troll b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Troll new file mode 100644 index 0000000..5e565da Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Troll differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Vostok b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Vostok new file mode 100644 index 0000000..7283053 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Antarctica/Vostok differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Arctic/Longyearbyen b/venv/Lib/site-packages/pytz/zoneinfo/Arctic/Longyearbyen new file mode 100644 index 0000000..15a34c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Arctic/Longyearbyen differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aden b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aden new file mode 100644 index 0000000..2aea25f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aden differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Almaty b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Almaty new file mode 100644 index 0000000..a4b0077 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Almaty differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Amman b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Amman new file mode 100644 index 0000000..5dcf7e0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Amman differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Anadyr b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Anadyr new file mode 100644 index 0000000..6ed8b7c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Anadyr differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtau b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtau new file mode 100644 index 0000000..e2d0f91 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtobe b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtobe new file mode 100644 index 0000000..06f0a13 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Aqtobe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashgabat b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashgabat new file mode 100644 index 0000000..73891af Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashgabat differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashkhabad b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashkhabad new file mode 100644 index 0000000..73891af Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ashkhabad differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Atyrau b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Atyrau new file mode 100644 index 0000000..8b5153e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Atyrau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baghdad b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baghdad new file mode 100644 index 0000000..f7162ed Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baghdad differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bahrain b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bahrain new file mode 100644 index 0000000..63188b2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bahrain differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baku b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baku new file mode 100644 index 0000000..a0de74b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Baku differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bangkok b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bangkok new file mode 100644 index 0000000..c292ac5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bangkok differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Barnaul b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Barnaul new file mode 100644 index 0000000..759592a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Barnaul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Beirut b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Beirut new file mode 100644 index 0000000..fb266ed Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Beirut differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bishkek b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bishkek new file mode 100644 index 0000000..f6e20dd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Bishkek differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Brunei b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Brunei new file mode 100644 index 0000000..3dab0ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Brunei differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Calcutta b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Calcutta new file mode 100644 index 0000000..0014046 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Calcutta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chita b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chita new file mode 100644 index 0000000..c4149c0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chita differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Choibalsan b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Choibalsan new file mode 100644 index 0000000..e48daa8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Choibalsan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chongqing b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chongqing new file mode 100644 index 0000000..91f6f8b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chongqing differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chungking b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chungking new file mode 100644 index 0000000..91f6f8b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Chungking differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Colombo b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Colombo new file mode 100644 index 0000000..62c64d8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Colombo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dacca b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dacca new file mode 100644 index 0000000..b11c928 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dacca differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Damascus b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Damascus new file mode 100644 index 0000000..d9104a7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Damascus differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dhaka b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dhaka new file mode 100644 index 0000000..b11c928 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dhaka differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dili b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dili new file mode 100644 index 0000000..30943bb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dili differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dubai b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dubai new file mode 100644 index 0000000..fc0a589 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dubai differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dushanbe b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dushanbe new file mode 100644 index 0000000..82d85b8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Dushanbe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Famagusta b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Famagusta new file mode 100644 index 0000000..653b146 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Famagusta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Gaza b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Gaza new file mode 100644 index 0000000..266981a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Gaza differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Harbin b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Harbin new file mode 100644 index 0000000..91f6f8b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Harbin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hebron b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hebron new file mode 100644 index 0000000..0078bf0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hebron differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ho_Chi_Minh b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ho_Chi_Minh new file mode 100644 index 0000000..e2934e3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ho_Chi_Minh differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hong_Kong b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hong_Kong new file mode 100644 index 0000000..23d0375 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hong_Kong differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hovd b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hovd new file mode 100644 index 0000000..4cb800a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Hovd differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Irkutsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Irkutsk new file mode 100644 index 0000000..4dcbbb7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Irkutsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Istanbul b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Istanbul new file mode 100644 index 0000000..508446b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Istanbul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jakarta b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jakarta new file mode 100644 index 0000000..5baa3a8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jakarta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jayapura b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jayapura new file mode 100644 index 0000000..3002c82 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jayapura differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jerusalem b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jerusalem new file mode 100644 index 0000000..1ebd066 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Jerusalem differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kabul b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kabul new file mode 100644 index 0000000..d19b9bd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kabul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kamchatka b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kamchatka new file mode 100644 index 0000000..3e80b4e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kamchatka differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Karachi b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Karachi new file mode 100644 index 0000000..ba65c0e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Karachi differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kashgar b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kashgar new file mode 100644 index 0000000..faa14d9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kashgar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kathmandu b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kathmandu new file mode 100644 index 0000000..a5d5107 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kathmandu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Katmandu b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Katmandu new file mode 100644 index 0000000..a5d5107 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Katmandu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Khandyga b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Khandyga new file mode 100644 index 0000000..72bea64 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Khandyga differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kolkata b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kolkata new file mode 100644 index 0000000..0014046 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kolkata differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Krasnoyarsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Krasnoyarsk new file mode 100644 index 0000000..30c6f16 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Krasnoyarsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuala_Lumpur b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuala_Lumpur new file mode 100644 index 0000000..612b01e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuala_Lumpur differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuching b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuching new file mode 100644 index 0000000..c86750c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuching differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuwait b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuwait new file mode 100644 index 0000000..2aea25f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Kuwait differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macao b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macao new file mode 100644 index 0000000..cac6506 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macao differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macau b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macau new file mode 100644 index 0000000..cac6506 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Macau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Magadan b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Magadan new file mode 100644 index 0000000..b4fcac1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Magadan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Makassar b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Makassar new file mode 100644 index 0000000..556ba86 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Makassar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Manila b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Manila new file mode 100644 index 0000000..f4f4b04 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Manila differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Muscat b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Muscat new file mode 100644 index 0000000..fc0a589 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Muscat differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Nicosia b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Nicosia new file mode 100644 index 0000000..f7f10ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Nicosia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novokuznetsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novokuznetsk new file mode 100644 index 0000000..d983276 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novokuznetsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novosibirsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novosibirsk new file mode 100644 index 0000000..e0ee5fc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Novosibirsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Omsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Omsk new file mode 100644 index 0000000..b29b769 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Omsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Oral b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Oral new file mode 100644 index 0000000..ad1f9ca Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Oral differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Phnom_Penh b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Phnom_Penh new file mode 100644 index 0000000..c292ac5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Phnom_Penh differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pontianak b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pontianak new file mode 100644 index 0000000..12ce24c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pontianak differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pyongyang b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pyongyang new file mode 100644 index 0000000..7ad7e0b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Pyongyang differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qatar b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qatar new file mode 100644 index 0000000..63188b2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qatar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qostanay b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qostanay new file mode 100644 index 0000000..73b9d96 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qostanay differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qyzylorda b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qyzylorda new file mode 100644 index 0000000..c2fe4c1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Qyzylorda differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Rangoon b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Rangoon new file mode 100644 index 0000000..dd77395 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Rangoon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Riyadh b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Riyadh new file mode 100644 index 0000000..2aea25f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Riyadh differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Saigon b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Saigon new file mode 100644 index 0000000..e2934e3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Saigon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Sakhalin b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Sakhalin new file mode 100644 index 0000000..485459c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Sakhalin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Samarkand b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Samarkand new file mode 100644 index 0000000..030d47c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Samarkand differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Seoul b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Seoul new file mode 100644 index 0000000..96199e7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Seoul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Shanghai b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Shanghai new file mode 100644 index 0000000..91f6f8b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Shanghai differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Singapore b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Singapore new file mode 100644 index 0000000..2364b21 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Singapore differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Srednekolymsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Srednekolymsk new file mode 100644 index 0000000..261a983 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Srednekolymsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Taipei b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Taipei new file mode 100644 index 0000000..24c4344 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Taipei differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tashkent b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tashkent new file mode 100644 index 0000000..32a9d7d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tashkent differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tbilisi b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tbilisi new file mode 100644 index 0000000..b608d79 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tbilisi differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tehran b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tehran new file mode 100644 index 0000000..8cec5ad Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tehran differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tel_Aviv b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tel_Aviv new file mode 100644 index 0000000..1ebd066 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tel_Aviv differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimbu b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimbu new file mode 100644 index 0000000..fe409c7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimbu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimphu b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimphu new file mode 100644 index 0000000..fe409c7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Thimphu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tokyo b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tokyo new file mode 100644 index 0000000..26f4d34 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tokyo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tomsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tomsk new file mode 100644 index 0000000..670e2ad Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Tomsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ujung_Pandang b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ujung_Pandang new file mode 100644 index 0000000..556ba86 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ujung_Pandang differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulaanbaatar b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulaanbaatar new file mode 100644 index 0000000..2e20cc3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulaanbaatar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulan_Bator b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulan_Bator new file mode 100644 index 0000000..2e20cc3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ulan_Bator differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Urumqi b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Urumqi new file mode 100644 index 0000000..faa14d9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Urumqi differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ust-Nera b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ust-Nera new file mode 100644 index 0000000..9e4a78f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Ust-Nera differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vientiane b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vientiane new file mode 100644 index 0000000..c292ac5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vientiane differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vladivostok b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vladivostok new file mode 100644 index 0000000..8ab253c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Vladivostok differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yakutsk b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yakutsk new file mode 100644 index 0000000..c815e99 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yakutsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yangon b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yangon new file mode 100644 index 0000000..dd77395 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yangon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yekaterinburg b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yekaterinburg new file mode 100644 index 0000000..6958d7e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yekaterinburg differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yerevan b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yerevan new file mode 100644 index 0000000..250bfe0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Asia/Yerevan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Azores b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Azores new file mode 100644 index 0000000..00a564f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Azores differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Bermuda b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Bermuda new file mode 100644 index 0000000..527524e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Bermuda differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Canary b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Canary new file mode 100644 index 0000000..f319215 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Canary differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Cape_Verde b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Cape_Verde new file mode 100644 index 0000000..e2a49d2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Cape_Verde differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faeroe b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faeroe new file mode 100644 index 0000000..4dab7ef Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faeroe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faroe b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faroe new file mode 100644 index 0000000..4dab7ef Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Faroe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Jan_Mayen b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Jan_Mayen new file mode 100644 index 0000000..15a34c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Jan_Mayen differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Madeira b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Madeira new file mode 100644 index 0000000..7ddcd88 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Madeira differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Reykjavik b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Reykjavik new file mode 100644 index 0000000..10e0fc8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Reykjavik differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/South_Georgia b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/South_Georgia new file mode 100644 index 0000000..4466608 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/South_Georgia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/St_Helena b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/St_Helena new file mode 100644 index 0000000..28b32ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/St_Helena differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Stanley b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Stanley new file mode 100644 index 0000000..88077f1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Atlantic/Stanley differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/ACT b/venv/Lib/site-packages/pytz/zoneinfo/Australia/ACT new file mode 100644 index 0000000..0aea4c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/ACT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Adelaide b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Adelaide new file mode 100644 index 0000000..f5dedca Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Adelaide differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Brisbane b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Brisbane new file mode 100644 index 0000000..7ff9949 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Brisbane differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Broken_Hill b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Broken_Hill new file mode 100644 index 0000000..698c76e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Broken_Hill differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Canberra b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Canberra new file mode 100644 index 0000000..0aea4c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Canberra differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Currie b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Currie new file mode 100644 index 0000000..3adb8e1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Currie differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Darwin b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Darwin new file mode 100644 index 0000000..74a3087 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Darwin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Eucla b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Eucla new file mode 100644 index 0000000..3bf1171 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Eucla differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Hobart b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Hobart new file mode 100644 index 0000000..3adb8e1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Hobart differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/LHI b/venv/Lib/site-packages/pytz/zoneinfo/Australia/LHI new file mode 100644 index 0000000..9e04a80 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/LHI differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lindeman b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lindeman new file mode 100644 index 0000000..4ee1825 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lindeman differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lord_Howe b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lord_Howe new file mode 100644 index 0000000..9e04a80 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Lord_Howe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Melbourne b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Melbourne new file mode 100644 index 0000000..ee903f4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Melbourne differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/NSW b/venv/Lib/site-packages/pytz/zoneinfo/Australia/NSW new file mode 100644 index 0000000..0aea4c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/NSW differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/North b/venv/Lib/site-packages/pytz/zoneinfo/Australia/North new file mode 100644 index 0000000..74a3087 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/North differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Perth b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Perth new file mode 100644 index 0000000..f8ddbdf Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Perth differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Queensland b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Queensland new file mode 100644 index 0000000..7ff9949 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Queensland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/South b/venv/Lib/site-packages/pytz/zoneinfo/Australia/South new file mode 100644 index 0000000..f5dedca Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/South differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Sydney b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Sydney new file mode 100644 index 0000000..0aea4c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Sydney differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Tasmania b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Tasmania new file mode 100644 index 0000000..3adb8e1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Tasmania differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Victoria b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Victoria new file mode 100644 index 0000000..ee903f4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Victoria differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/West b/venv/Lib/site-packages/pytz/zoneinfo/Australia/West new file mode 100644 index 0000000..f8ddbdf Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/West differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Australia/Yancowinna b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Yancowinna new file mode 100644 index 0000000..698c76e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Australia/Yancowinna differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Brazil/Acre b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/Acre new file mode 100644 index 0000000..a374cb4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/Acre differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Brazil/DeNoronha b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/DeNoronha new file mode 100644 index 0000000..f140726 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/DeNoronha differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Brazil/East b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/East new file mode 100644 index 0000000..13ff083 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/East differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Brazil/West b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/West new file mode 100644 index 0000000..63d58f8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Brazil/West differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/CET b/venv/Lib/site-packages/pytz/zoneinfo/CET new file mode 100644 index 0000000..122e934 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/CET differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/CST6CDT b/venv/Lib/site-packages/pytz/zoneinfo/CST6CDT new file mode 100644 index 0000000..ca67929 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/CST6CDT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Atlantic b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Atlantic new file mode 100644 index 0000000..756099a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Atlantic differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Central b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Central new file mode 100644 index 0000000..ac40299 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Central differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Eastern b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Eastern new file mode 100644 index 0000000..6752c5b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Eastern differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Mountain b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Mountain new file mode 100644 index 0000000..cd78a6f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Mountain differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Newfoundland b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Newfoundland new file mode 100644 index 0000000..65a5b0c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Newfoundland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Pacific b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Pacific new file mode 100644 index 0000000..bb60cbc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Pacific differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Saskatchewan b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Saskatchewan new file mode 100644 index 0000000..20c9c84 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Saskatchewan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Canada/Yukon b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Yukon new file mode 100644 index 0000000..9ee229c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Canada/Yukon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Chile/Continental b/venv/Lib/site-packages/pytz/zoneinfo/Chile/Continental new file mode 100644 index 0000000..aa29060 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Chile/Continental differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Chile/EasterIsland b/venv/Lib/site-packages/pytz/zoneinfo/Chile/EasterIsland new file mode 100644 index 0000000..cae3744 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Chile/EasterIsland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Cuba b/venv/Lib/site-packages/pytz/zoneinfo/Cuba new file mode 100644 index 0000000..b69ac45 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Cuba differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/EET b/venv/Lib/site-packages/pytz/zoneinfo/EET new file mode 100644 index 0000000..cbdb71d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/EET differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/EST b/venv/Lib/site-packages/pytz/zoneinfo/EST new file mode 100644 index 0000000..21ebc00 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/EST differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/EST5EDT b/venv/Lib/site-packages/pytz/zoneinfo/EST5EDT new file mode 100644 index 0000000..9bce500 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/EST5EDT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Egypt b/venv/Lib/site-packages/pytz/zoneinfo/Egypt new file mode 100644 index 0000000..d3f8196 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Egypt differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Eire b/venv/Lib/site-packages/pytz/zoneinfo/Eire new file mode 100644 index 0000000..1d99490 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Eire differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+0 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+1 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+1 new file mode 100644 index 0000000..4dab6f9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+1 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+10 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+10 new file mode 100644 index 0000000..c749290 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+10 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+11 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+11 new file mode 100644 index 0000000..d969982 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+11 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+12 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+12 new file mode 100644 index 0000000..cdeec90 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+12 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+2 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+2 new file mode 100644 index 0000000..fbd2a94 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+2 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+3 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+3 new file mode 100644 index 0000000..ee246ef Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+3 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+4 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+4 new file mode 100644 index 0000000..5a25ff2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+4 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+5 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+5 new file mode 100644 index 0000000..c0b745f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+5 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+6 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+6 new file mode 100644 index 0000000..06e777d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+6 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+7 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+7 new file mode 100644 index 0000000..4e0b53a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+7 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+8 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+8 new file mode 100644 index 0000000..714b0c5 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+8 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+9 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+9 new file mode 100644 index 0000000..78b9daa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT+9 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-0 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-1 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-1 new file mode 100644 index 0000000..a838beb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-1 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-10 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-10 new file mode 100644 index 0000000..68ff77d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-10 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-11 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-11 new file mode 100644 index 0000000..66af5a4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-11 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-12 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-12 new file mode 100644 index 0000000..17ba505 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-12 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-13 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-13 new file mode 100644 index 0000000..5f3706c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-13 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-14 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-14 new file mode 100644 index 0000000..7e9f9c4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-14 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-2 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-2 new file mode 100644 index 0000000..fcef6d9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-2 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-3 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-3 new file mode 100644 index 0000000..27973bc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-3 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-4 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-4 new file mode 100644 index 0000000..1efd841 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-4 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-5 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-5 new file mode 100644 index 0000000..1f76184 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-5 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-6 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-6 new file mode 100644 index 0000000..952681e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-6 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-7 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-7 new file mode 100644 index 0000000..cefc912 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-7 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-8 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-8 new file mode 100644 index 0000000..afb093d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-8 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-9 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-9 new file mode 100644 index 0000000..9265fb7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT-9 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT0 b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/GMT0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/Greenwich b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Greenwich new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Greenwich differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/UCT b/venv/Lib/site-packages/pytz/zoneinfo/Etc/UCT new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/UCT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/UTC b/venv/Lib/site-packages/pytz/zoneinfo/Etc/UTC new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/UTC differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/Universal b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Universal new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Universal differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Etc/Zulu b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Zulu new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Etc/Zulu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Amsterdam b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Amsterdam new file mode 100644 index 0000000..c3ff07b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Amsterdam differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Andorra b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Andorra new file mode 100644 index 0000000..5962550 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Andorra differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Astrakhan b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Astrakhan new file mode 100644 index 0000000..73a4d01 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Astrakhan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Athens b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Athens new file mode 100644 index 0000000..9f3a067 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Athens differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belfast b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belfast new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belfast differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belgrade b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belgrade new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Belgrade differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Berlin b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Berlin new file mode 100644 index 0000000..7f6d958 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Berlin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bratislava b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bratislava new file mode 100644 index 0000000..ce8f433 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bratislava differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Brussels b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Brussels new file mode 100644 index 0000000..40d7124 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Brussels differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bucharest b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bucharest new file mode 100644 index 0000000..4303b90 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Bucharest differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Budapest b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Budapest new file mode 100644 index 0000000..b76c873 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Budapest differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Busingen b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Busingen new file mode 100644 index 0000000..ad6cf59 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Busingen differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Chisinau b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Chisinau new file mode 100644 index 0000000..5ee23fe Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Chisinau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Copenhagen b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Copenhagen new file mode 100644 index 0000000..776be6e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Copenhagen differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Dublin b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Dublin new file mode 100644 index 0000000..1d99490 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Dublin differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Gibraltar b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Gibraltar new file mode 100644 index 0000000..117aadb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Gibraltar differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Guernsey b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Guernsey new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Guernsey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Helsinki b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Helsinki new file mode 100644 index 0000000..b4f8f9c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Helsinki differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Isle_of_Man b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Isle_of_Man new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Isle_of_Man differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Istanbul b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Istanbul new file mode 100644 index 0000000..508446b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Istanbul differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Jersey b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Jersey new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Jersey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kaliningrad b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kaliningrad new file mode 100644 index 0000000..cc99bea Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kaliningrad differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kiev b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kiev new file mode 100644 index 0000000..52efea8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kiev differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kirov b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kirov new file mode 100644 index 0000000..a3b5320 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Kirov differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Lisbon b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Lisbon new file mode 100644 index 0000000..55f0193 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Lisbon differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ljubljana b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ljubljana new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ljubljana differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/London b/venv/Lib/site-packages/pytz/zoneinfo/Europe/London new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/London differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Luxembourg b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Luxembourg new file mode 100644 index 0000000..c4ca733 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Luxembourg differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Madrid b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Madrid new file mode 100644 index 0000000..16f6420 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Madrid differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Malta b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Malta new file mode 100644 index 0000000..bf2452d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Malta differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Mariehamn b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Mariehamn new file mode 100644 index 0000000..b4f8f9c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Mariehamn differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Minsk b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Minsk new file mode 100644 index 0000000..453306c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Minsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Monaco b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Monaco new file mode 100644 index 0000000..adbe45d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Monaco differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Moscow b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Moscow new file mode 100644 index 0000000..ddb3f4e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Moscow differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Nicosia b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Nicosia new file mode 100644 index 0000000..f7f10ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Nicosia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Oslo b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Oslo new file mode 100644 index 0000000..15a34c3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Oslo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Paris b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Paris new file mode 100644 index 0000000..7d366c6 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Paris differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Podgorica b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Podgorica new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Podgorica differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Prague b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Prague new file mode 100644 index 0000000..ce8f433 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Prague differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Riga b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Riga new file mode 100644 index 0000000..8db477d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Riga differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Rome b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Rome new file mode 100644 index 0000000..ac4c163 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Rome differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Samara b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Samara new file mode 100644 index 0000000..97d5dd9 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Samara differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/San_Marino b/venv/Lib/site-packages/pytz/zoneinfo/Europe/San_Marino new file mode 100644 index 0000000..ac4c163 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/San_Marino differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sarajevo b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sarajevo new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sarajevo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Saratov b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Saratov new file mode 100644 index 0000000..8fd5f6d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Saratov differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Simferopol b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Simferopol new file mode 100644 index 0000000..29107a0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Simferopol differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Skopje b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Skopje new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Skopje differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sofia b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sofia new file mode 100644 index 0000000..0e4d879 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Sofia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Stockholm b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Stockholm new file mode 100644 index 0000000..f3e0c7f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Stockholm differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tallinn b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tallinn new file mode 100644 index 0000000..b5acca3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tallinn differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tirane b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tirane new file mode 100644 index 0000000..0b86017 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tirane differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tiraspol b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tiraspol new file mode 100644 index 0000000..5ee23fe Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Tiraspol differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ulyanovsk b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ulyanovsk new file mode 100644 index 0000000..7b61bdc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Ulyanovsk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Uzhgorod b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Uzhgorod new file mode 100644 index 0000000..0eb2150 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Uzhgorod differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vaduz b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vaduz new file mode 100644 index 0000000..ad6cf59 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vaduz differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vatican b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vatican new file mode 100644 index 0000000..ac4c163 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vatican differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vienna b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vienna new file mode 100644 index 0000000..3582bb1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vienna differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vilnius b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vilnius new file mode 100644 index 0000000..7abd63f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Vilnius differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Volgograd b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Volgograd new file mode 100644 index 0000000..11739ac Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Volgograd differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Warsaw b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Warsaw new file mode 100644 index 0000000..e33cf67 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Warsaw differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zagreb b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zagreb new file mode 100644 index 0000000..27de456 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zagreb differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zaporozhye b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zaporozhye new file mode 100644 index 0000000..f0406c1 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zaporozhye differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zurich b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zurich new file mode 100644 index 0000000..ad6cf59 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Europe/Zurich differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Factory b/venv/Lib/site-packages/pytz/zoneinfo/Factory new file mode 100644 index 0000000..60aa2a0 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Factory differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GB b/venv/Lib/site-packages/pytz/zoneinfo/GB new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GB differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GB-Eire b/venv/Lib/site-packages/pytz/zoneinfo/GB-Eire new file mode 100644 index 0000000..ac02a81 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GB-Eire differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GMT b/venv/Lib/site-packages/pytz/zoneinfo/GMT new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GMT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GMT+0 b/venv/Lib/site-packages/pytz/zoneinfo/GMT+0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GMT+0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GMT-0 b/venv/Lib/site-packages/pytz/zoneinfo/GMT-0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GMT-0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/GMT0 b/venv/Lib/site-packages/pytz/zoneinfo/GMT0 new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/GMT0 differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Greenwich b/venv/Lib/site-packages/pytz/zoneinfo/Greenwich new file mode 100644 index 0000000..c634746 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Greenwich differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/HST b/venv/Lib/site-packages/pytz/zoneinfo/HST new file mode 100644 index 0000000..cccd45e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/HST differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Hongkong b/venv/Lib/site-packages/pytz/zoneinfo/Hongkong new file mode 100644 index 0000000..23d0375 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Hongkong differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Iceland b/venv/Lib/site-packages/pytz/zoneinfo/Iceland new file mode 100644 index 0000000..10e0fc8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Iceland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Antananarivo b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Antananarivo new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Antananarivo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Chagos b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Chagos new file mode 100644 index 0000000..93d6dda Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Chagos differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Christmas b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Christmas new file mode 100644 index 0000000..d18c381 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Christmas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Cocos b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Cocos new file mode 100644 index 0000000..f8116e7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Cocos differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Comoro b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Comoro new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Comoro differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Kerguelen b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Kerguelen new file mode 100644 index 0000000..cde4cf7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Kerguelen differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mahe b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mahe new file mode 100644 index 0000000..208f938 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mahe differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Maldives b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Maldives new file mode 100644 index 0000000..7c839cf Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Maldives differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mauritius b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mauritius new file mode 100644 index 0000000..17f2616 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mauritius differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mayotte b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mayotte new file mode 100644 index 0000000..9dcfc19 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Mayotte differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Indian/Reunion b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Reunion new file mode 100644 index 0000000..dfe0831 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Indian/Reunion differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Iran b/venv/Lib/site-packages/pytz/zoneinfo/Iran new file mode 100644 index 0000000..8cec5ad Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Iran differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Israel b/venv/Lib/site-packages/pytz/zoneinfo/Israel new file mode 100644 index 0000000..1ebd066 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Israel differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Jamaica b/venv/Lib/site-packages/pytz/zoneinfo/Jamaica new file mode 100644 index 0000000..2a9b7fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Jamaica differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Japan b/venv/Lib/site-packages/pytz/zoneinfo/Japan new file mode 100644 index 0000000..26f4d34 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Japan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Kwajalein b/venv/Lib/site-packages/pytz/zoneinfo/Kwajalein new file mode 100644 index 0000000..1a7975f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Kwajalein differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Libya b/venv/Lib/site-packages/pytz/zoneinfo/Libya new file mode 100644 index 0000000..07b393b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Libya differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/MET b/venv/Lib/site-packages/pytz/zoneinfo/MET new file mode 100644 index 0000000..4a826bb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/MET differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/MST b/venv/Lib/site-packages/pytz/zoneinfo/MST new file mode 100644 index 0000000..c93a58e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/MST differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/MST7MDT b/venv/Lib/site-packages/pytz/zoneinfo/MST7MDT new file mode 100644 index 0000000..4506a6e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/MST7MDT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaNorte b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaNorte new file mode 100644 index 0000000..ada6bf7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaNorte differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaSur b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaSur new file mode 100644 index 0000000..e4a7857 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/BajaSur differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Mexico/General b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/General new file mode 100644 index 0000000..e7fb6f2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Mexico/General differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/NZ b/venv/Lib/site-packages/pytz/zoneinfo/NZ new file mode 100644 index 0000000..6575fdc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/NZ differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/NZ-CHAT b/venv/Lib/site-packages/pytz/zoneinfo/NZ-CHAT new file mode 100644 index 0000000..c004109 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/NZ-CHAT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Navajo b/venv/Lib/site-packages/pytz/zoneinfo/Navajo new file mode 100644 index 0000000..5fbe26b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Navajo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/PRC b/venv/Lib/site-packages/pytz/zoneinfo/PRC new file mode 100644 index 0000000..91f6f8b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/PRC differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/PST8PDT b/venv/Lib/site-packages/pytz/zoneinfo/PST8PDT new file mode 100644 index 0000000..99d246b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/PST8PDT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Apia b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Apia new file mode 100644 index 0000000..999c367 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Apia differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Auckland b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Auckland new file mode 100644 index 0000000..6575fdc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Auckland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Bougainville b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Bougainville new file mode 100644 index 0000000..2892d26 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Bougainville differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chatham b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chatham new file mode 100644 index 0000000..c004109 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chatham differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chuuk b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chuuk new file mode 100644 index 0000000..07c84b7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Chuuk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Easter b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Easter new file mode 100644 index 0000000..cae3744 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Easter differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Efate b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Efate new file mode 100644 index 0000000..d8d4093 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Efate differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Enderbury b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Enderbury new file mode 100644 index 0000000..39b786e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Enderbury differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fakaofo b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fakaofo new file mode 100644 index 0000000..e40307f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fakaofo differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fiji b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fiji new file mode 100644 index 0000000..af07ac8 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Fiji differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Funafuti b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Funafuti new file mode 100644 index 0000000..ea72863 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Funafuti differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Galapagos b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Galapagos new file mode 100644 index 0000000..31f0921 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Galapagos differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Gambier b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Gambier new file mode 100644 index 0000000..e1fc3da Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Gambier differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guadalcanal b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guadalcanal new file mode 100644 index 0000000..7e9d10a Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guadalcanal differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guam b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guam new file mode 100644 index 0000000..66490d2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Guam differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Honolulu b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Honolulu new file mode 100644 index 0000000..c7cd060 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Honolulu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Johnston b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Johnston new file mode 100644 index 0000000..c7cd060 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Johnston differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kanton b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kanton new file mode 100644 index 0000000..39b786e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kanton differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kiritimati b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kiritimati new file mode 100644 index 0000000..7cae0cb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kiritimati differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kosrae b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kosrae new file mode 100644 index 0000000..a584aae Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kosrae differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kwajalein b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kwajalein new file mode 100644 index 0000000..1a7975f Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Kwajalein differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Majuro b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Majuro new file mode 100644 index 0000000..9ef8374 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Majuro differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Marquesas b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Marquesas new file mode 100644 index 0000000..74d6792 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Marquesas differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Midway b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Midway new file mode 100644 index 0000000..cb56709 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Midway differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Nauru b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Nauru new file mode 100644 index 0000000..acec042 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Nauru differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Niue b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Niue new file mode 100644 index 0000000..89117b3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Niue differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Norfolk b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Norfolk new file mode 100644 index 0000000..53c1aad Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Norfolk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Noumea b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Noumea new file mode 100644 index 0000000..931a1a3 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Noumea differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pago_Pago b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pago_Pago new file mode 100644 index 0000000..cb56709 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pago_Pago differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Palau b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Palau new file mode 100644 index 0000000..146b351 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Palau differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pitcairn b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pitcairn new file mode 100644 index 0000000..ef91b06 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pitcairn differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pohnpei b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pohnpei new file mode 100644 index 0000000..c298ddd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Pohnpei differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Ponape b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Ponape new file mode 100644 index 0000000..c298ddd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Ponape differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Port_Moresby b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Port_Moresby new file mode 100644 index 0000000..920ad27 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Port_Moresby differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Rarotonga b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Rarotonga new file mode 100644 index 0000000..eea37ab Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Rarotonga differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Saipan b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Saipan new file mode 100644 index 0000000..66490d2 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Saipan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Samoa b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Samoa new file mode 100644 index 0000000..cb56709 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Samoa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tahiti b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tahiti new file mode 100644 index 0000000..442b8eb Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tahiti differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tarawa b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tarawa new file mode 100644 index 0000000..3db6c75 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tarawa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tongatapu b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tongatapu new file mode 100644 index 0000000..c2e5999 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Tongatapu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Truk b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Truk new file mode 100644 index 0000000..07c84b7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Truk differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wake b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wake new file mode 100644 index 0000000..c9e3106 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wake differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wallis b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wallis new file mode 100644 index 0000000..b35344b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Wallis differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Yap b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Yap new file mode 100644 index 0000000..07c84b7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Pacific/Yap differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Poland b/venv/Lib/site-packages/pytz/zoneinfo/Poland new file mode 100644 index 0000000..e33cf67 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Poland differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Portugal b/venv/Lib/site-packages/pytz/zoneinfo/Portugal new file mode 100644 index 0000000..55f0193 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Portugal differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/ROC b/venv/Lib/site-packages/pytz/zoneinfo/ROC new file mode 100644 index 0000000..24c4344 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/ROC differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/ROK b/venv/Lib/site-packages/pytz/zoneinfo/ROK new file mode 100644 index 0000000..96199e7 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/ROK differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Singapore b/venv/Lib/site-packages/pytz/zoneinfo/Singapore new file mode 100644 index 0000000..2364b21 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Singapore differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Turkey b/venv/Lib/site-packages/pytz/zoneinfo/Turkey new file mode 100644 index 0000000..508446b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Turkey differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/UCT b/venv/Lib/site-packages/pytz/zoneinfo/UCT new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/UCT differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Alaska b/venv/Lib/site-packages/pytz/zoneinfo/US/Alaska new file mode 100644 index 0000000..9bbb2fd Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Alaska differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Aleutian b/venv/Lib/site-packages/pytz/zoneinfo/US/Aleutian new file mode 100644 index 0000000..4323649 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Aleutian differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Arizona b/venv/Lib/site-packages/pytz/zoneinfo/US/Arizona new file mode 100644 index 0000000..ac6bb0c Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Arizona differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Central b/venv/Lib/site-packages/pytz/zoneinfo/US/Central new file mode 100644 index 0000000..a5b1617 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Central differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/East-Indiana b/venv/Lib/site-packages/pytz/zoneinfo/US/East-Indiana new file mode 100644 index 0000000..09511cc Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/East-Indiana differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Eastern b/venv/Lib/site-packages/pytz/zoneinfo/US/Eastern new file mode 100644 index 0000000..2f75480 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Eastern differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Hawaii b/venv/Lib/site-packages/pytz/zoneinfo/US/Hawaii new file mode 100644 index 0000000..c7cd060 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Hawaii differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Indiana-Starke b/venv/Lib/site-packages/pytz/zoneinfo/US/Indiana-Starke new file mode 100644 index 0000000..fcd408d Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Indiana-Starke differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Michigan b/venv/Lib/site-packages/pytz/zoneinfo/US/Michigan new file mode 100644 index 0000000..e104faa Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Michigan differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Mountain b/venv/Lib/site-packages/pytz/zoneinfo/US/Mountain new file mode 100644 index 0000000..5fbe26b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Mountain differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Pacific b/venv/Lib/site-packages/pytz/zoneinfo/US/Pacific new file mode 100644 index 0000000..9dad4f4 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Pacific differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/US/Samoa b/venv/Lib/site-packages/pytz/zoneinfo/US/Samoa new file mode 100644 index 0000000..cb56709 Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/US/Samoa differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/UTC b/venv/Lib/site-packages/pytz/zoneinfo/UTC new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/UTC differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Universal b/venv/Lib/site-packages/pytz/zoneinfo/Universal new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Universal differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/W-SU b/venv/Lib/site-packages/pytz/zoneinfo/W-SU new file mode 100644 index 0000000..ddb3f4e Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/W-SU differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/WET b/venv/Lib/site-packages/pytz/zoneinfo/WET new file mode 100644 index 0000000..c27390b Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/WET differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/Zulu b/venv/Lib/site-packages/pytz/zoneinfo/Zulu new file mode 100644 index 0000000..91558be Binary files /dev/null and b/venv/Lib/site-packages/pytz/zoneinfo/Zulu differ diff --git a/venv/Lib/site-packages/pytz/zoneinfo/iso3166.tab b/venv/Lib/site-packages/pytz/zoneinfo/iso3166.tab new file mode 100644 index 0000000..a4ff61a --- /dev/null +++ b/venv/Lib/site-packages/pytz/zoneinfo/iso3166.tab @@ -0,0 +1,274 @@ +# ISO 3166 alpha-2 country codes +# +# This file is in the public domain, so clarified as of +# 2009-05-17 by Arthur David Olson. +# +# From Paul Eggert (2015-05-02): +# This file contains a table of two-letter country codes. Columns are +# separated by a single tab. Lines beginning with '#' are comments. +# All text uses UTF-8 encoding. The columns of the table are as follows: +# +# 1. ISO 3166-1 alpha-2 country code, current as of +# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1 +# https://isotc.iso.org/livelink/livelink/Open/16944257 +# 2. The usual English name for the coded region, +# chosen so that alphabetic sorting of subsets produces helpful lists. +# This is not the same as the English name in the ISO 3166 tables. +# +# The table is sorted by country code. +# +# This table is intended as an aid for users, to help them select time +# zone data appropriate for their practical needs. It is not intended +# to take or endorse any position on legal or territorial claims. +# +#country- +#code name of country, territory, area, or subdivision +AD Andorra +AE United Arab Emirates +AF Afghanistan +AG Antigua & Barbuda +AI Anguilla +AL Albania +AM Armenia +AO Angola +AQ Antarctica +AR Argentina +AS Samoa (American) +AT Austria +AU Australia +AW Aruba +AX Åland Islands +AZ Azerbaijan +BA Bosnia & Herzegovina +BB Barbados +BD Bangladesh +BE Belgium +BF Burkina Faso +BG Bulgaria +BH Bahrain +BI Burundi +BJ Benin +BL St Barthelemy +BM Bermuda +BN Brunei +BO Bolivia +BQ Caribbean NL +BR Brazil +BS Bahamas +BT Bhutan +BV Bouvet Island +BW Botswana +BY Belarus +BZ Belize +CA Canada +CC Cocos (Keeling) Islands +CD Congo (Dem. Rep.) +CF Central African Rep. +CG Congo (Rep.) +CH Switzerland +CI Côte d'Ivoire +CK Cook Islands +CL Chile +CM Cameroon +CN China +CO Colombia +CR Costa Rica +CU Cuba +CV Cape Verde +CW Curaçao +CX Christmas Island +CY Cyprus +CZ Czech Republic +DE Germany +DJ Djibouti +DK Denmark +DM Dominica +DO Dominican Republic +DZ Algeria +EC Ecuador +EE Estonia +EG Egypt +EH Western Sahara +ER Eritrea +ES Spain +ET Ethiopia +FI Finland +FJ Fiji +FK Falkland Islands +FM Micronesia +FO Faroe Islands +FR France +GA Gabon +GB Britain (UK) +GD Grenada +GE Georgia +GF French Guiana +GG Guernsey +GH Ghana +GI Gibraltar +GL Greenland +GM Gambia +GN Guinea +GP Guadeloupe +GQ Equatorial Guinea +GR Greece +GS South Georgia & the South Sandwich Islands +GT Guatemala +GU Guam +GW Guinea-Bissau +GY Guyana +HK Hong Kong +HM Heard Island & McDonald Islands +HN Honduras +HR Croatia +HT Haiti +HU Hungary +ID Indonesia +IE Ireland +IL Israel +IM Isle of Man +IN India +IO British Indian Ocean Territory +IQ Iraq +IR Iran +IS Iceland +IT Italy +JE Jersey +JM Jamaica +JO Jordan +JP Japan +KE Kenya +KG Kyrgyzstan +KH Cambodia +KI Kiribati +KM Comoros +KN St Kitts & Nevis +KP Korea (North) +KR Korea (South) +KW Kuwait +KY Cayman Islands +KZ Kazakhstan +LA Laos +LB Lebanon +LC St Lucia +LI Liechtenstein +LK Sri Lanka +LR Liberia +LS Lesotho +LT Lithuania +LU Luxembourg +LV Latvia +LY Libya +MA Morocco +MC Monaco +MD Moldova +ME Montenegro +MF St Martin (French) +MG Madagascar +MH Marshall Islands +MK North Macedonia +ML Mali +MM Myanmar (Burma) +MN Mongolia +MO Macau +MP Northern Mariana Islands +MQ Martinique +MR Mauritania +MS Montserrat +MT Malta +MU Mauritius +MV Maldives +MW Malawi +MX Mexico +MY Malaysia +MZ Mozambique +NA Namibia +NC New Caledonia +NE Niger +NF Norfolk Island +NG Nigeria +NI Nicaragua +NL Netherlands +NO Norway +NP Nepal +NR Nauru +NU Niue +NZ New Zealand +OM Oman +PA Panama +PE Peru +PF French Polynesia +PG Papua New Guinea +PH Philippines +PK Pakistan +PL Poland +PM St Pierre & Miquelon +PN Pitcairn +PR Puerto Rico +PS Palestine +PT Portugal +PW Palau +PY Paraguay +QA Qatar +RE Réunion +RO Romania +RS Serbia +RU Russia +RW Rwanda +SA Saudi Arabia +SB Solomon Islands +SC Seychelles +SD Sudan +SE Sweden +SG Singapore +SH St Helena +SI Slovenia +SJ Svalbard & Jan Mayen +SK Slovakia +SL Sierra Leone +SM San Marino +SN Senegal +SO Somalia +SR Suriname +SS South Sudan +ST Sao Tome & Principe +SV El Salvador +SX St Maarten (Dutch) +SY Syria +SZ Eswatini (Swaziland) +TC Turks & Caicos Is +TD Chad +TF French Southern & Antarctic Lands +TG Togo +TH Thailand +TJ Tajikistan +TK Tokelau +TL East Timor +TM Turkmenistan +TN Tunisia +TO Tonga +TR Turkey +TT Trinidad & Tobago +TV Tuvalu +TW Taiwan +TZ Tanzania +UA Ukraine +UG Uganda +UM US minor outlying islands +US United States +UY Uruguay +UZ Uzbekistan +VA Vatican City +VC St Vincent +VE Venezuela +VG Virgin Islands (UK) +VI Virgin Islands (US) +VN Vietnam +VU Vanuatu +WF Wallis & Futuna +WS Samoa (western) +YE Yemen +YT Mayotte +ZA South Africa +ZM Zambia +ZW Zimbabwe diff --git a/venv/Lib/site-packages/pytz/zoneinfo/leapseconds b/venv/Lib/site-packages/pytz/zoneinfo/leapseconds new file mode 100644 index 0000000..ffa5eb8 --- /dev/null +++ b/venv/Lib/site-packages/pytz/zoneinfo/leapseconds @@ -0,0 +1,82 @@ +# Allowance for leap seconds added to each time zone file. + +# This file is in the public domain. + +# This file is generated automatically from the data in the public-domain +# NIST format leap-seconds.list file, which can be copied from +# <ftp://ftp.nist.gov/pub/time/leap-seconds.list> +# or <ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list>. +# The NIST file is used instead of its IERS upstream counterpart +# <https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list> +# because under US law the NIST file is public domain +# whereas the IERS file's copyright and license status is unclear. +# For more about leap-seconds.list, please see +# The NTP Timescale and Leap Seconds +# <https://www.eecis.udel.edu/~mills/leap.html>. + +# The rules for leap seconds are specified in Annex 1 (Time scales) of: +# Standard-frequency and time-signal emissions. +# International Telecommunication Union - Radiocommunication Sector +# (ITU-R) Recommendation TF.460-6 (02/2002) +# <https://www.itu.int/rec/R-REC-TF.460-6-200202-I/>. +# The International Earth Rotation and Reference Systems Service (IERS) +# periodically uses leap seconds to keep UTC to within 0.9 s of UT1 +# (a proxy for Earth's angle in space as measured by astronomers) +# and publishes leap second data in a copyrighted file +# <https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat>. +# See: Levine J. Coordinated Universal Time and the leap second. +# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995 +# <https://ieeexplore.ieee.org/document/7909995>. + +# There were no leap seconds before 1972, as no official mechanism +# accounted for the discrepancy between atomic time (TAI) and the earth's +# rotation. The first ("1 Jan 1972") data line in leap-seconds.list +# does not denote a leap second; it denotes the start of the current definition +# of UTC. + +# All leap-seconds are Stationary (S) at the given UTC time. +# The correction (+ or -) is made at the given time, so in the unlikely +# event of a negative leap second, a line would look like this: +# Leap YEAR MON DAY 23:59:59 - S +# Typical lines look like this: +# Leap YEAR MON DAY 23:59:60 + S +Leap 1972 Jun 30 23:59:60 + S +Leap 1972 Dec 31 23:59:60 + S +Leap 1973 Dec 31 23:59:60 + S +Leap 1974 Dec 31 23:59:60 + S +Leap 1975 Dec 31 23:59:60 + S +Leap 1976 Dec 31 23:59:60 + S +Leap 1977 Dec 31 23:59:60 + S +Leap 1978 Dec 31 23:59:60 + S +Leap 1979 Dec 31 23:59:60 + S +Leap 1981 Jun 30 23:59:60 + S +Leap 1982 Jun 30 23:59:60 + S +Leap 1983 Jun 30 23:59:60 + S +Leap 1985 Jun 30 23:59:60 + S +Leap 1987 Dec 31 23:59:60 + S +Leap 1989 Dec 31 23:59:60 + S +Leap 1990 Dec 31 23:59:60 + S +Leap 1992 Jun 30 23:59:60 + S +Leap 1993 Jun 30 23:59:60 + S +Leap 1994 Jun 30 23:59:60 + S +Leap 1995 Dec 31 23:59:60 + S +Leap 1997 Jun 30 23:59:60 + S +Leap 1998 Dec 31 23:59:60 + S +Leap 2005 Dec 31 23:59:60 + S +Leap 2008 Dec 31 23:59:60 + S +Leap 2012 Jun 30 23:59:60 + S +Leap 2015 Jun 30 23:59:60 + S +Leap 2016 Dec 31 23:59:60 + S + +# UTC timestamp when this leap second list expires. +# Any additional leap seconds will come after this. +# This Expires line is commented out for now, +# so that pre-2020a zic implementations do not reject this file. +#Expires 2022 Dec 28 00:00:00 + +# POSIX timestamps for the data in this file: +#updated 1467936000 (2016-07-08 00:00:00 UTC) +#expires 1672185600 (2022-12-28 00:00:00 UTC) + +# Updated through IERS Bulletin C63 +# File expires on: 28 December 2022 diff --git a/venv/Lib/site-packages/pytz/zoneinfo/tzdata.zi b/venv/Lib/site-packages/pytz/zoneinfo/tzdata.zi new file mode 100644 index 0000000..e21fc92 --- /dev/null +++ b/venv/Lib/site-packages/pytz/zoneinfo/tzdata.zi @@ -0,0 +1,4437 @@ +# version unknown-dirty +# This zic input file is in the public domain. +R d 1916 o - Jun 14 23s 1 S +R d 1916 1919 - O Su>=1 23s 0 - +R d 1917 o - Mar 24 23s 1 S +R d 1918 o - Mar 9 23s 1 S +R d 1919 o - Mar 1 23s 1 S +R d 1920 o - F 14 23s 1 S +R d 1920 o - O 23 23s 0 - +R d 1921 o - Mar 14 23s 1 S +R d 1921 o - Jun 21 23s 0 - +R d 1939 o - S 11 23s 1 S +R d 1939 o - N 19 1 0 - +R d 1944 1945 - Ap M>=1 2 1 S +R d 1944 o - O 8 2 0 - +R d 1945 o - S 16 1 0 - +R d 1971 o - Ap 25 23s 1 S +R d 1971 o - S 26 23s 0 - +R d 1977 o - May 6 0 1 S +R d 1977 o - O 21 0 0 - +R d 1978 o - Mar 24 1 1 S +R d 1978 o - S 22 3 0 - +R d 1980 o - Ap 25 0 1 S +R d 1980 o - O 31 2 0 - +Z Africa/Algiers 0:12:12 - LMT 1891 Mar 16 +0:9:21 - PMT 1911 Mar 11 +0 d WE%sT 1940 F 25 2 +1 d CE%sT 1946 O 7 +0 - WET 1956 Ja 29 +1 - CET 1963 Ap 14 +0 d WE%sT 1977 O 21 +1 d CE%sT 1979 O 26 +0 d WE%sT 1981 May +1 - CET +Z Atlantic/Cape_Verde -1:34:4 - LMT 1912 Ja 1 2u +-2 - -02 1942 S +-2 1 -01 1945 O 15 +-2 - -02 1975 N 25 2 +-1 - -01 +Z Africa/Ndjamena 1:0:12 - LMT 1912 +1 - WAT 1979 O 14 +1 1 WAST 1980 Mar 8 +1 - WAT +Z Africa/Abidjan -0:16:8 - LMT 1912 +0 - GMT +L Africa/Abidjan Africa/Accra +L Africa/Abidjan Africa/Bamako +L Africa/Abidjan Africa/Banjul +L Africa/Abidjan Africa/Conakry +L Africa/Abidjan Africa/Dakar +L Africa/Abidjan Africa/Freetown +L Africa/Abidjan Africa/Lome +L Africa/Abidjan Africa/Nouakchott +L Africa/Abidjan Africa/Ouagadougou +L Africa/Abidjan Atlantic/St_Helena +R K 1940 o - Jul 15 0 1 S +R K 1940 o - O 1 0 0 - +R K 1941 o - Ap 15 0 1 S +R K 1941 o - S 16 0 0 - +R K 1942 1944 - Ap 1 0 1 S +R K 1942 o - O 27 0 0 - +R K 1943 1945 - N 1 0 0 - +R K 1945 o - Ap 16 0 1 S +R K 1957 o - May 10 0 1 S +R K 1957 1958 - O 1 0 0 - +R K 1958 o - May 1 0 1 S +R K 1959 1981 - May 1 1 1 S +R K 1959 1965 - S 30 3 0 - +R K 1966 1994 - O 1 3 0 - +R K 1982 o - Jul 25 1 1 S +R K 1983 o - Jul 12 1 1 S +R K 1984 1988 - May 1 1 1 S +R K 1989 o - May 6 1 1 S +R K 1990 1994 - May 1 1 1 S +R K 1995 2010 - Ap lastF 0s 1 S +R K 1995 2005 - S lastTh 24 0 - +R K 2006 o - S 21 24 0 - +R K 2007 o - S Th>=1 24 0 - +R K 2008 o - Au lastTh 24 0 - +R K 2009 o - Au 20 24 0 - +R K 2010 o - Au 10 24 0 - +R K 2010 o - S 9 24 1 S +R K 2010 o - S lastTh 24 0 - +R K 2014 o - May 15 24 1 S +R K 2014 o - Jun 26 24 0 - +R K 2014 o - Jul 31 24 1 S +R K 2014 o - S lastTh 24 0 - +Z Africa/Cairo 2:5:9 - LMT 1900 O +2 K EE%sT +Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u +-1 - -01 1975 +0 - GMT +Z Africa/Nairobi 2:27:16 - LMT 1908 May +2:30 - +0230 1928 Jun 30 24 +3 - EAT 1930 Ja 4 24 +2:30 - +0230 1936 D 31 24 +2:45 - +0245 1942 Jul 31 24 +3 - EAT +L Africa/Nairobi Africa/Addis_Ababa +L Africa/Nairobi Africa/Asmara +L Africa/Nairobi Africa/Dar_es_Salaam +L Africa/Nairobi Africa/Djibouti +L Africa/Nairobi Africa/Kampala +L Africa/Nairobi Africa/Mogadishu +L Africa/Nairobi Indian/Antananarivo +L Africa/Nairobi Indian/Comoro +L Africa/Nairobi Indian/Mayotte +Z Africa/Monrovia -0:43:8 - LMT 1882 +-0:43:8 - MMT 1919 Mar +-0:44:30 - MMT 1972 Ja 7 +0 - GMT +R L 1951 o - O 14 2 1 S +R L 1952 o - Ja 1 0 0 - +R L 1953 o - O 9 2 1 S +R L 1954 o - Ja 1 0 0 - +R L 1955 o - S 30 0 1 S +R L 1956 o - Ja 1 0 0 - +R L 1982 1984 - Ap 1 0 1 S +R L 1982 1985 - O 1 0 0 - +R L 1985 o - Ap 6 0 1 S +R L 1986 o - Ap 4 0 1 S +R L 1986 o - O 3 0 0 - +R L 1987 1989 - Ap 1 0 1 S +R L 1987 1989 - O 1 0 0 - +R L 1997 o - Ap 4 0 1 S +R L 1997 o - O 4 0 0 - +R L 2013 o - Mar lastF 1 1 S +R L 2013 o - O lastF 2 0 - +Z Africa/Tripoli 0:52:44 - LMT 1920 +1 L CE%sT 1959 +2 - EET 1982 +1 L CE%sT 1990 May 4 +2 - EET 1996 S 30 +1 L CE%sT 1997 O 4 +2 - EET 2012 N 10 2 +1 L CE%sT 2013 O 25 2 +2 - EET +R MU 1982 o - O 10 0 1 - +R MU 1983 o - Mar 21 0 0 - +R MU 2008 o - O lastSu 2 1 - +R MU 2009 o - Mar lastSu 2 0 - +Z Indian/Mauritius 3:50 - LMT 1907 +4 MU +04/+05 +R M 1939 o - S 12 0 1 - +R M 1939 o - N 19 0 0 - +R M 1940 o - F 25 0 1 - +R M 1945 o - N 18 0 0 - +R M 1950 o - Jun 11 0 1 - +R M 1950 o - O 29 0 0 - +R M 1967 o - Jun 3 12 1 - +R M 1967 o - O 1 0 0 - +R M 1974 o - Jun 24 0 1 - +R M 1974 o - S 1 0 0 - +R M 1976 1977 - May 1 0 1 - +R M 1976 o - Au 1 0 0 - +R M 1977 o - S 28 0 0 - +R M 1978 o - Jun 1 0 1 - +R M 1978 o - Au 4 0 0 - +R M 2008 o - Jun 1 0 1 - +R M 2008 o - S 1 0 0 - +R M 2009 o - Jun 1 0 1 - +R M 2009 o - Au 21 0 0 - +R M 2010 o - May 2 0 1 - +R M 2010 o - Au 8 0 0 - +R M 2011 o - Ap 3 0 1 - +R M 2011 o - Jul 31 0 0 - +R M 2012 2013 - Ap lastSu 2 1 - +R M 2012 o - Jul 20 3 0 - +R M 2012 o - Au 20 2 1 - +R M 2012 o - S 30 3 0 - +R M 2013 o - Jul 7 3 0 - +R M 2013 o - Au 10 2 1 - +R M 2013 2018 - O lastSu 3 0 - +R M 2014 2018 - Mar lastSu 2 1 - +R M 2014 o - Jun 28 3 0 - +R M 2014 o - Au 2 2 1 - +R M 2015 o - Jun 14 3 0 - +R M 2015 o - Jul 19 2 1 - +R M 2016 o - Jun 5 3 0 - +R M 2016 o - Jul 10 2 1 - +R M 2017 o - May 21 3 0 - +R M 2017 o - Jul 2 2 1 - +R M 2018 o - May 13 3 0 - +R M 2018 o - Jun 17 2 1 - +R M 2019 o - May 5 3 -1 - +R M 2019 o - Jun 9 2 0 - +R M 2020 o - Ap 19 3 -1 - +R M 2020 o - May 31 2 0 - +R M 2021 o - Ap 11 3 -1 - +R M 2021 o - May 16 2 0 - +R M 2022 o - Mar 27 3 -1 - +R M 2022 o - May 8 2 0 - +R M 2023 o - Mar 19 3 -1 - +R M 2023 o - Ap 30 2 0 - +R M 2024 o - Mar 10 3 -1 - +R M 2024 o - Ap 14 2 0 - +R M 2025 o - F 23 3 -1 - +R M 2025 o - Ap 6 2 0 - +R M 2026 o - F 15 3 -1 - +R M 2026 o - Mar 22 2 0 - +R M 2027 o - F 7 3 -1 - +R M 2027 o - Mar 14 2 0 - +R M 2028 o - Ja 23 3 -1 - +R M 2028 o - Mar 5 2 0 - +R M 2029 o - Ja 14 3 -1 - +R M 2029 o - F 18 2 0 - +R M 2029 o - D 30 3 -1 - +R M 2030 o - F 10 2 0 - +R M 2030 o - D 22 3 -1 - +R M 2031 o - F 2 2 0 - +R M 2031 o - D 14 3 -1 - +R M 2032 o - Ja 18 2 0 - +R M 2032 o - N 28 3 -1 - +R M 2033 o - Ja 9 2 0 - +R M 2033 o - N 20 3 -1 - +R M 2033 o - D 25 2 0 - +R M 2034 o - N 5 3 -1 - +R M 2034 o - D 17 2 0 - +R M 2035 o - O 28 3 -1 - +R M 2035 o - D 9 2 0 - +R M 2036 o - O 19 3 -1 - +R M 2036 o - N 23 2 0 - +R M 2037 o - O 4 3 -1 - +R M 2037 o - N 15 2 0 - +R M 2038 o - S 26 3 -1 - +R M 2038 o - N 7 2 0 - +R M 2039 o - S 18 3 -1 - +R M 2039 o - O 23 2 0 - +R M 2040 o - S 2 3 -1 - +R M 2040 o - O 14 2 0 - +R M 2041 o - Au 25 3 -1 - +R M 2041 o - S 29 2 0 - +R M 2042 o - Au 10 3 -1 - +R M 2042 o - S 21 2 0 - +R M 2043 o - Au 2 3 -1 - +R M 2043 o - S 13 2 0 - +R M 2044 o - Jul 24 3 -1 - +R M 2044 o - Au 28 2 0 - +R M 2045 o - Jul 9 3 -1 - +R M 2045 o - Au 20 2 0 - +R M 2046 o - Jul 1 3 -1 - +R M 2046 o - Au 12 2 0 - +R M 2047 o - Jun 23 3 -1 - +R M 2047 o - Jul 28 2 0 - +R M 2048 o - Jun 7 3 -1 - +R M 2048 o - Jul 19 2 0 - +R M 2049 o - May 30 3 -1 - +R M 2049 o - Jul 4 2 0 - +R M 2050 o - May 15 3 -1 - +R M 2050 o - Jun 26 2 0 - +R M 2051 o - May 7 3 -1 - +R M 2051 o - Jun 18 2 0 - +R M 2052 o - Ap 28 3 -1 - +R M 2052 o - Jun 2 2 0 - +R M 2053 o - Ap 13 3 -1 - +R M 2053 o - May 25 2 0 - +R M 2054 o - Ap 5 3 -1 - +R M 2054 o - May 17 2 0 - +R M 2055 o - Mar 28 3 -1 - +R M 2055 o - May 2 2 0 - +R M 2056 o - Mar 12 3 -1 - +R M 2056 o - Ap 23 2 0 - +R M 2057 o - Mar 4 3 -1 - +R M 2057 o - Ap 8 2 0 - +R M 2058 o - F 17 3 -1 - +R M 2058 o - Mar 31 2 0 - +R M 2059 o - F 9 3 -1 - +R M 2059 o - Mar 23 2 0 - +R M 2060 o - F 1 3 -1 - +R M 2060 o - Mar 7 2 0 - +R M 2061 o - Ja 16 3 -1 - +R M 2061 o - F 27 2 0 - +R M 2062 o - Ja 8 3 -1 - +R M 2062 o - F 19 2 0 - +R M 2062 o - D 31 3 -1 - +R M 2063 o - F 4 2 0 - +R M 2063 o - D 16 3 -1 - +R M 2064 o - Ja 27 2 0 - +R M 2064 o - D 7 3 -1 - +R M 2065 o - Ja 11 2 0 - +R M 2065 o - N 22 3 -1 - +R M 2066 o - Ja 3 2 0 - +R M 2066 o - N 14 3 -1 - +R M 2066 o - D 26 2 0 - +R M 2067 o - N 6 3 -1 - +R M 2067 o - D 11 2 0 - +R M 2068 o - O 21 3 -1 - +R M 2068 o - D 2 2 0 - +R M 2069 o - O 13 3 -1 - +R M 2069 o - N 24 2 0 - +R M 2070 o - O 5 3 -1 - +R M 2070 o - N 9 2 0 - +R M 2071 o - S 20 3 -1 - +R M 2071 o - N 1 2 0 - +R M 2072 o - S 11 3 -1 - +R M 2072 o - O 16 2 0 - +R M 2073 o - Au 27 3 -1 - +R M 2073 o - O 8 2 0 - +R M 2074 o - Au 19 3 -1 - +R M 2074 o - S 30 2 0 - +R M 2075 o - Au 11 3 -1 - +R M 2075 o - S 15 2 0 - +R M 2076 o - Jul 26 3 -1 - +R M 2076 o - S 6 2 0 - +R M 2077 o - Jul 18 3 -1 - +R M 2077 o - Au 29 2 0 - +R M 2078 o - Jul 10 3 -1 - +R M 2078 o - Au 14 2 0 - +R M 2079 o - Jun 25 3 -1 - +R M 2079 o - Au 6 2 0 - +R M 2080 o - Jun 16 3 -1 - +R M 2080 o - Jul 21 2 0 - +R M 2081 o - Jun 1 3 -1 - +R M 2081 o - Jul 13 2 0 - +R M 2082 o - May 24 3 -1 - +R M 2082 o - Jul 5 2 0 - +R M 2083 o - May 16 3 -1 - +R M 2083 o - Jun 20 2 0 - +R M 2084 o - Ap 30 3 -1 - +R M 2084 o - Jun 11 2 0 - +R M 2085 o - Ap 22 3 -1 - +R M 2085 o - Jun 3 2 0 - +R M 2086 o - Ap 14 3 -1 - +R M 2086 o - May 19 2 0 - +R M 2087 o - Mar 30 3 -1 - +R M 2087 o - May 11 2 0 - +Z Africa/Casablanca -0:30:20 - LMT 1913 O 26 +0 M +00/+01 1984 Mar 16 +1 - +01 1986 +0 M +00/+01 2018 O 28 3 +1 M +01/+00 +Z Africa/El_Aaiun -0:52:48 - LMT 1934 +-1 - -01 1976 Ap 14 +0 M +00/+01 2018 O 28 3 +1 M +01/+00 +Z Africa/Maputo 2:10:20 - LMT 1903 Mar +2 - CAT +L Africa/Maputo Africa/Blantyre +L Africa/Maputo Africa/Bujumbura +L Africa/Maputo Africa/Gaborone +L Africa/Maputo Africa/Harare +L Africa/Maputo Africa/Kigali +L Africa/Maputo Africa/Lubumbashi +L Africa/Maputo Africa/Lusaka +R NA 1994 o - Mar 21 0 -1 WAT +R NA 1994 2017 - S Su>=1 2 0 CAT +R NA 1995 2017 - Ap Su>=1 2 -1 WAT +Z Africa/Windhoek 1:8:24 - LMT 1892 F 8 +1:30 - +0130 1903 Mar +2 - SAST 1942 S 20 2 +2 1 SAST 1943 Mar 21 2 +2 - SAST 1990 Mar 21 +2 NA %s +Z Africa/Lagos 0:13:35 - LMT 1905 Jul +0 - GMT 1908 Jul +0:13:35 - LMT 1914 +0:30 - +0030 1919 S +1 - WAT +L Africa/Lagos Africa/Bangui +L Africa/Lagos Africa/Brazzaville +L Africa/Lagos Africa/Douala +L Africa/Lagos Africa/Kinshasa +L Africa/Lagos Africa/Libreville +L Africa/Lagos Africa/Luanda +L Africa/Lagos Africa/Malabo +L Africa/Lagos Africa/Niamey +L Africa/Lagos Africa/Porto-Novo +Z Indian/Reunion 3:41:52 - LMT 1911 Jun +4 - +04 +Z Africa/Sao_Tome 0:26:56 - LMT 1884 +-0:36:45 - LMT 1912 Ja 1 0u +0 - GMT 2018 Ja 1 1 +1 - WAT 2019 Ja 1 2 +0 - GMT +Z Indian/Mahe 3:41:48 - LMT 1907 +4 - +04 +R SA 1942 1943 - S Su>=15 2 1 - +R SA 1943 1944 - Mar Su>=15 2 0 - +Z Africa/Johannesburg 1:52 - LMT 1892 F 8 +1:30 - SAST 1903 Mar +2 SA SAST +L Africa/Johannesburg Africa/Maseru +L Africa/Johannesburg Africa/Mbabane +R SD 1970 o - May 1 0 1 S +R SD 1970 1985 - O 15 0 0 - +R SD 1971 o - Ap 30 0 1 S +R SD 1972 1985 - Ap lastSu 0 1 S +Z Africa/Khartoum 2:10:8 - LMT 1931 +2 SD CA%sT 2000 Ja 15 12 +3 - EAT 2017 N +2 - CAT +Z Africa/Juba 2:6:28 - LMT 1931 +2 SD CA%sT 2000 Ja 15 12 +3 - EAT 2021 F +2 - CAT +R n 1939 o - Ap 15 23s 1 S +R n 1939 o - N 18 23s 0 - +R n 1940 o - F 25 23s 1 S +R n 1941 o - O 6 0 0 - +R n 1942 o - Mar 9 0 1 S +R n 1942 o - N 2 3 0 - +R n 1943 o - Mar 29 2 1 S +R n 1943 o - Ap 17 2 0 - +R n 1943 o - Ap 25 2 1 S +R n 1943 o - O 4 2 0 - +R n 1944 1945 - Ap M>=1 2 1 S +R n 1944 o - O 8 0 0 - +R n 1945 o - S 16 0 0 - +R n 1977 o - Ap 30 0s 1 S +R n 1977 o - S 24 0s 0 - +R n 1978 o - May 1 0s 1 S +R n 1978 o - O 1 0s 0 - +R n 1988 o - Jun 1 0s 1 S +R n 1988 1990 - S lastSu 0s 0 - +R n 1989 o - Mar 26 0s 1 S +R n 1990 o - May 1 0s 1 S +R n 2005 o - May 1 0s 1 S +R n 2005 o - S 30 1s 0 - +R n 2006 2008 - Mar lastSu 2s 1 S +R n 2006 2008 - O lastSu 2s 0 - +Z Africa/Tunis 0:40:44 - LMT 1881 May 12 +0:9:21 - PMT 1911 Mar 11 +1 n CE%sT +Z Antarctica/Casey 0 - -00 1969 +8 - +08 2009 O 18 2 +11 - +11 2010 Mar 5 2 +8 - +08 2011 O 28 2 +11 - +11 2012 F 21 17u +8 - +08 2016 O 22 +11 - +11 2018 Mar 11 4 +8 - +08 2018 O 7 4 +11 - +11 2019 Mar 17 3 +8 - +08 2019 O 4 3 +11 - +11 2020 Mar 8 3 +8 - +08 2020 O 4 0:1 +11 - +11 +Z Antarctica/Davis 0 - -00 1957 Ja 13 +7 - +07 1964 N +0 - -00 1969 F +7 - +07 2009 O 18 2 +5 - +05 2010 Mar 10 20u +7 - +07 2011 O 28 2 +5 - +05 2012 F 21 20u +7 - +07 +Z Antarctica/Mawson 0 - -00 1954 F 13 +6 - +06 2009 O 18 2 +5 - +05 +Z Indian/Kerguelen 0 - -00 1950 +5 - +05 +R Tr 2005 ma - Mar lastSu 1u 2 +02 +R Tr 2004 ma - O lastSu 1u 0 +00 +Z Antarctica/Troll 0 - -00 2005 F 12 +0 Tr %s +Z Antarctica/Vostok 0 - -00 1957 D 16 +6 - +06 +Z Antarctica/Rothera 0 - -00 1976 D +-3 - -03 +Z Asia/Kabul 4:36:48 - LMT 1890 +4 - +04 1945 +4:30 - +0430 +R AM 2011 o - Mar lastSu 2s 1 - +R AM 2011 o - O lastSu 2s 0 - +Z Asia/Yerevan 2:58 - LMT 1924 May 2 +3 - +03 1957 Mar +4 R +04/+05 1991 Mar 31 2s +3 R +03/+04 1995 S 24 2s +4 - +04 1997 +4 R +04/+05 2011 +4 AM +04/+05 +R AZ 1997 2015 - Mar lastSu 4 1 - +R AZ 1997 2015 - O lastSu 5 0 - +Z Asia/Baku 3:19:24 - LMT 1924 May 2 +3 - +03 1957 Mar +4 R +04/+05 1991 Mar 31 2s +3 R +03/+04 1992 S lastSu 2s +4 - +04 1996 +4 E +04/+05 1997 +4 AZ +04/+05 +R BD 2009 o - Jun 19 23 1 - +R BD 2009 o - D 31 24 0 - +Z Asia/Dhaka 6:1:40 - LMT 1890 +5:53:20 - HMT 1941 O +6:30 - +0630 1942 May 15 +5:30 - +0530 1942 S +6:30 - +0630 1951 S 30 +6 - +06 2009 +6 BD +06/+07 +Z Asia/Thimphu 5:58:36 - LMT 1947 Au 15 +5:30 - +0530 1987 O +6 - +06 +Z Indian/Chagos 4:49:40 - LMT 1907 +5 - +05 1996 +6 - +06 +Z Asia/Brunei 7:39:40 - LMT 1926 Mar +7:30 - +0730 1933 +8 - +08 +Z Asia/Yangon 6:24:47 - LMT 1880 +6:24:47 - RMT 1920 +6:30 - +0630 1942 May +9 - +09 1945 May 3 +6:30 - +0630 +R Sh 1919 o - Ap 12 24 1 D +R Sh 1919 o - S 30 24 0 S +R Sh 1940 o - Jun 1 0 1 D +R Sh 1940 o - O 12 24 0 S +R Sh 1941 o - Mar 15 0 1 D +R Sh 1941 o - N 1 24 0 S +R Sh 1942 o - Ja 31 0 1 D +R Sh 1945 o - S 1 24 0 S +R Sh 1946 o - May 15 0 1 D +R Sh 1946 o - S 30 24 0 S +R Sh 1947 o - Ap 15 0 1 D +R Sh 1947 o - O 31 24 0 S +R Sh 1948 1949 - May 1 0 1 D +R Sh 1948 1949 - S 30 24 0 S +R CN 1986 o - May 4 2 1 D +R CN 1986 1991 - S Su>=11 2 0 S +R CN 1987 1991 - Ap Su>=11 2 1 D +Z Asia/Shanghai 8:5:43 - LMT 1901 +8 Sh C%sT 1949 May 28 +8 CN C%sT +Z Asia/Urumqi 5:50:20 - LMT 1928 +6 - +06 +R HK 1946 o - Ap 21 0 1 S +R HK 1946 o - D 1 3:30s 0 - +R HK 1947 o - Ap 13 3:30s 1 S +R HK 1947 o - N 30 3:30s 0 - +R HK 1948 o - May 2 3:30s 1 S +R HK 1948 1952 - O Su>=28 3:30s 0 - +R HK 1949 1953 - Ap Su>=1 3:30 1 S +R HK 1953 1964 - O Su>=31 3:30 0 - +R HK 1954 1964 - Mar Su>=18 3:30 1 S +R HK 1965 1976 - Ap Su>=16 3:30 1 S +R HK 1965 1976 - O Su>=16 3:30 0 - +R HK 1973 o - D 30 3:30 1 S +R HK 1979 o - May 13 3:30 1 S +R HK 1979 o - O 21 3:30 0 - +Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42 +8 - HKT 1941 Jun 15 3 +8 1 HKST 1941 O 1 4 +8 0:30 HKWT 1941 D 25 +9 - JST 1945 N 18 2 +8 HK HK%sT +R f 1946 o - May 15 0 1 D +R f 1946 o - O 1 0 0 S +R f 1947 o - Ap 15 0 1 D +R f 1947 o - N 1 0 0 S +R f 1948 1951 - May 1 0 1 D +R f 1948 1951 - O 1 0 0 S +R f 1952 o - Mar 1 0 1 D +R f 1952 1954 - N 1 0 0 S +R f 1953 1959 - Ap 1 0 1 D +R f 1955 1961 - O 1 0 0 S +R f 1960 1961 - Jun 1 0 1 D +R f 1974 1975 - Ap 1 0 1 D +R f 1974 1975 - O 1 0 0 S +R f 1979 o - Jul 1 0 1 D +R f 1979 o - O 1 0 0 S +Z Asia/Taipei 8:6 - LMT 1896 +8 - CST 1937 O +9 - JST 1945 S 21 1 +8 f C%sT +R _ 1942 1943 - Ap 30 23 1 - +R _ 1942 o - N 17 23 0 - +R _ 1943 o - S 30 23 0 S +R _ 1946 o - Ap 30 23s 1 D +R _ 1946 o - S 30 23s 0 S +R _ 1947 o - Ap 19 23s 1 D +R _ 1947 o - N 30 23s 0 S +R _ 1948 o - May 2 23s 1 D +R _ 1948 o - O 31 23s 0 S +R _ 1949 1950 - Ap Sa>=1 23s 1 D +R _ 1949 1950 - O lastSa 23s 0 S +R _ 1951 o - Mar 31 23s 1 D +R _ 1951 o - O 28 23s 0 S +R _ 1952 1953 - Ap Sa>=1 23s 1 D +R _ 1952 o - N 1 23s 0 S +R _ 1953 1954 - O lastSa 23s 0 S +R _ 1954 1956 - Mar Sa>=17 23s 1 D +R _ 1955 o - N 5 23s 0 S +R _ 1956 1964 - N Su>=1 3:30 0 S +R _ 1957 1964 - Mar Su>=18 3:30 1 D +R _ 1965 1973 - Ap Su>=16 3:30 1 D +R _ 1965 1966 - O Su>=16 2:30 0 S +R _ 1967 1976 - O Su>=16 3:30 0 S +R _ 1973 o - D 30 3:30 1 D +R _ 1975 1976 - Ap Su>=16 3:30 1 D +R _ 1979 o - May 13 3:30 1 D +R _ 1979 o - O Su>=16 3:30 0 S +Z Asia/Macau 7:34:10 - LMT 1904 O 30 +8 - CST 1941 D 21 23 +9 _ +09/+10 1945 S 30 24 +8 _ C%sT +R CY 1975 o - Ap 13 0 1 S +R CY 1975 o - O 12 0 0 - +R CY 1976 o - May 15 0 1 S +R CY 1976 o - O 11 0 0 - +R CY 1977 1980 - Ap Su>=1 0 1 S +R CY 1977 o - S 25 0 0 - +R CY 1978 o - O 2 0 0 - +R CY 1979 1997 - S lastSu 0 0 - +R CY 1981 1998 - Mar lastSu 0 1 S +Z Asia/Nicosia 2:13:28 - LMT 1921 N 14 +2 CY EE%sT 1998 S +2 E EE%sT +Z Asia/Famagusta 2:15:48 - LMT 1921 N 14 +2 CY EE%sT 1998 S +2 E EE%sT 2016 S 8 +3 - +03 2017 O 29 1u +2 E EE%sT +L Asia/Nicosia Europe/Nicosia +Z Asia/Tbilisi 2:59:11 - LMT 1880 +2:59:11 - TBMT 1924 May 2 +3 - +03 1957 Mar +4 R +04/+05 1991 Mar 31 2s +3 R +03/+04 1992 +3 e +03/+04 1994 S lastSu +4 e +04/+05 1996 O lastSu +4 1 +05 1997 Mar lastSu +4 e +04/+05 2004 Jun 27 +3 R +03/+04 2005 Mar lastSu 2 +4 - +04 +Z Asia/Dili 8:22:20 - LMT 1912 +8 - +08 1942 F 21 23 +9 - +09 1976 May 3 +8 - +08 2000 S 17 +9 - +09 +Z Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 +5:53:20 - HMT 1870 +5:21:10 - MMT 1906 +5:30 - IST 1941 O +5:30 1 +0630 1942 May 15 +5:30 - IST 1942 S +5:30 1 +0630 1945 O 15 +5:30 - IST +Z Asia/Jakarta 7:7:12 - LMT 1867 Au 10 +7:7:12 - BMT 1923 D 31 23:47:12 +7:20 - +0720 1932 N +7:30 - +0730 1942 Mar 23 +9 - +09 1945 S 23 +7:30 - +0730 1948 May +8 - +08 1950 May +7:30 - +0730 1964 +7 - WIB +Z Asia/Pontianak 7:17:20 - LMT 1908 May +7:17:20 - PMT 1932 N +7:30 - +0730 1942 Ja 29 +9 - +09 1945 S 23 +7:30 - +0730 1948 May +8 - +08 1950 May +7:30 - +0730 1964 +8 - WITA 1988 +7 - WIB +Z Asia/Makassar 7:57:36 - LMT 1920 +7:57:36 - MMT 1932 N +8 - +08 1942 F 9 +9 - +09 1945 S 23 +8 - WITA +Z Asia/Jayapura 9:22:48 - LMT 1932 N +9 - +09 1944 S +9:30 - +0930 1964 +9 - WIT +R i 1978 1980 - Mar 20 24 1 - +R i 1978 o - O 20 24 0 - +R i 1979 o - S 18 24 0 - +R i 1980 o - S 22 24 0 - +R i 1991 o - May 2 24 1 - +R i 1992 1995 - Mar 21 24 1 - +R i 1991 1995 - S 21 24 0 - +R i 1996 o - Mar 20 24 1 - +R i 1996 o - S 20 24 0 - +R i 1997 1999 - Mar 21 24 1 - +R i 1997 1999 - S 21 24 0 - +R i 2000 o - Mar 20 24 1 - +R i 2000 o - S 20 24 0 - +R i 2001 2003 - Mar 21 24 1 - +R i 2001 2003 - S 21 24 0 - +R i 2004 o - Mar 20 24 1 - +R i 2004 o - S 20 24 0 - +R i 2005 o - Mar 21 24 1 - +R i 2005 o - S 21 24 0 - +R i 2008 o - Mar 20 24 1 - +R i 2008 o - S 20 24 0 - +R i 2009 2011 - Mar 21 24 1 - +R i 2009 2011 - S 21 24 0 - +R i 2012 o - Mar 20 24 1 - +R i 2012 o - S 20 24 0 - +R i 2013 2015 - Mar 21 24 1 - +R i 2013 2015 - S 21 24 0 - +R i 2016 o - Mar 20 24 1 - +R i 2016 o - S 20 24 0 - +R i 2017 2019 - Mar 21 24 1 - +R i 2017 2019 - S 21 24 0 - +R i 2020 o - Mar 20 24 1 - +R i 2020 o - S 20 24 0 - +R i 2021 2023 - Mar 21 24 1 - +R i 2021 2023 - S 21 24 0 - +R i 2024 o - Mar 20 24 1 - +R i 2024 o - S 20 24 0 - +R i 2025 2027 - Mar 21 24 1 - +R i 2025 2027 - S 21 24 0 - +R i 2028 2029 - Mar 20 24 1 - +R i 2028 2029 - S 20 24 0 - +R i 2030 2031 - Mar 21 24 1 - +R i 2030 2031 - S 21 24 0 - +R i 2032 2033 - Mar 20 24 1 - +R i 2032 2033 - S 20 24 0 - +R i 2034 2035 - Mar 21 24 1 - +R i 2034 2035 - S 21 24 0 - +R i 2036 2037 - Mar 20 24 1 - +R i 2036 2037 - S 20 24 0 - +R i 2038 2039 - Mar 21 24 1 - +R i 2038 2039 - S 21 24 0 - +R i 2040 2041 - Mar 20 24 1 - +R i 2040 2041 - S 20 24 0 - +R i 2042 2043 - Mar 21 24 1 - +R i 2042 2043 - S 21 24 0 - +R i 2044 2045 - Mar 20 24 1 - +R i 2044 2045 - S 20 24 0 - +R i 2046 2047 - Mar 21 24 1 - +R i 2046 2047 - S 21 24 0 - +R i 2048 2049 - Mar 20 24 1 - +R i 2048 2049 - S 20 24 0 - +R i 2050 2051 - Mar 21 24 1 - +R i 2050 2051 - S 21 24 0 - +R i 2052 2053 - Mar 20 24 1 - +R i 2052 2053 - S 20 24 0 - +R i 2054 2055 - Mar 21 24 1 - +R i 2054 2055 - S 21 24 0 - +R i 2056 2057 - Mar 20 24 1 - +R i 2056 2057 - S 20 24 0 - +R i 2058 2059 - Mar 21 24 1 - +R i 2058 2059 - S 21 24 0 - +R i 2060 2062 - Mar 20 24 1 - +R i 2060 2062 - S 20 24 0 - +R i 2063 o - Mar 21 24 1 - +R i 2063 o - S 21 24 0 - +R i 2064 2066 - Mar 20 24 1 - +R i 2064 2066 - S 20 24 0 - +R i 2067 o - Mar 21 24 1 - +R i 2067 o - S 21 24 0 - +R i 2068 2070 - Mar 20 24 1 - +R i 2068 2070 - S 20 24 0 - +R i 2071 o - Mar 21 24 1 - +R i 2071 o - S 21 24 0 - +R i 2072 2074 - Mar 20 24 1 - +R i 2072 2074 - S 20 24 0 - +R i 2075 o - Mar 21 24 1 - +R i 2075 o - S 21 24 0 - +R i 2076 2078 - Mar 20 24 1 - +R i 2076 2078 - S 20 24 0 - +R i 2079 o - Mar 21 24 1 - +R i 2079 o - S 21 24 0 - +R i 2080 2082 - Mar 20 24 1 - +R i 2080 2082 - S 20 24 0 - +R i 2083 o - Mar 21 24 1 - +R i 2083 o - S 21 24 0 - +R i 2084 2086 - Mar 20 24 1 - +R i 2084 2086 - S 20 24 0 - +R i 2087 o - Mar 21 24 1 - +R i 2087 o - S 21 24 0 - +R i 2088 ma - Mar 20 24 1 - +R i 2088 ma - S 20 24 0 - +Z Asia/Tehran 3:25:44 - LMT 1916 +3:25:44 - TMT 1946 +3:30 - +0330 1977 N +4 i +04/+05 1979 +3:30 i +0330/+0430 +R IQ 1982 o - May 1 0 1 - +R IQ 1982 1984 - O 1 0 0 - +R IQ 1983 o - Mar 31 0 1 - +R IQ 1984 1985 - Ap 1 0 1 - +R IQ 1985 1990 - S lastSu 1s 0 - +R IQ 1986 1990 - Mar lastSu 1s 1 - +R IQ 1991 2007 - Ap 1 3s 1 - +R IQ 1991 2007 - O 1 3s 0 - +Z Asia/Baghdad 2:57:40 - LMT 1890 +2:57:36 - BMT 1918 +3 - +03 1982 May +3 IQ +03/+04 +R Z 1940 o - May 31 24u 1 D +R Z 1940 o - S 30 24u 0 S +R Z 1940 o - N 16 24u 1 D +R Z 1942 1946 - O 31 24u 0 S +R Z 1943 1944 - Mar 31 24u 1 D +R Z 1945 1946 - Ap 15 24u 1 D +R Z 1948 o - May 22 24u 2 DD +R Z 1948 o - Au 31 24u 1 D +R Z 1948 1949 - O 31 24u 0 S +R Z 1949 o - Ap 30 24u 1 D +R Z 1950 o - Ap 15 24u 1 D +R Z 1950 o - S 14 24u 0 S +R Z 1951 o - Mar 31 24u 1 D +R Z 1951 o - N 10 24u 0 S +R Z 1952 o - Ap 19 24u 1 D +R Z 1952 o - O 18 24u 0 S +R Z 1953 o - Ap 11 24u 1 D +R Z 1953 o - S 12 24u 0 S +R Z 1954 o - Jun 12 24u 1 D +R Z 1954 o - S 11 24u 0 S +R Z 1955 o - Jun 11 24u 1 D +R Z 1955 o - S 10 24u 0 S +R Z 1956 o - Jun 2 24u 1 D +R Z 1956 o - S 29 24u 0 S +R Z 1957 o - Ap 27 24u 1 D +R Z 1957 o - S 21 24u 0 S +R Z 1974 o - Jul 6 24 1 D +R Z 1974 o - O 12 24 0 S +R Z 1975 o - Ap 19 24 1 D +R Z 1975 o - Au 30 24 0 S +R Z 1980 o - Au 2 24s 1 D +R Z 1980 o - S 13 24s 0 S +R Z 1984 o - May 5 24s 1 D +R Z 1984 o - Au 25 24s 0 S +R Z 1985 o - Ap 13 24 1 D +R Z 1985 o - Au 31 24 0 S +R Z 1986 o - May 17 24 1 D +R Z 1986 o - S 6 24 0 S +R Z 1987 o - Ap 14 24 1 D +R Z 1987 o - S 12 24 0 S +R Z 1988 o - Ap 9 24 1 D +R Z 1988 o - S 3 24 0 S +R Z 1989 o - Ap 29 24 1 D +R Z 1989 o - S 2 24 0 S +R Z 1990 o - Mar 24 24 1 D +R Z 1990 o - Au 25 24 0 S +R Z 1991 o - Mar 23 24 1 D +R Z 1991 o - Au 31 24 0 S +R Z 1992 o - Mar 28 24 1 D +R Z 1992 o - S 5 24 0 S +R Z 1993 o - Ap 2 0 1 D +R Z 1993 o - S 5 0 0 S +R Z 1994 o - Ap 1 0 1 D +R Z 1994 o - Au 28 0 0 S +R Z 1995 o - Mar 31 0 1 D +R Z 1995 o - S 3 0 0 S +R Z 1996 o - Mar 14 24 1 D +R Z 1996 o - S 15 24 0 S +R Z 1997 o - Mar 20 24 1 D +R Z 1997 o - S 13 24 0 S +R Z 1998 o - Mar 20 0 1 D +R Z 1998 o - S 6 0 0 S +R Z 1999 o - Ap 2 2 1 D +R Z 1999 o - S 3 2 0 S +R Z 2000 o - Ap 14 2 1 D +R Z 2000 o - O 6 1 0 S +R Z 2001 o - Ap 9 1 1 D +R Z 2001 o - S 24 1 0 S +R Z 2002 o - Mar 29 1 1 D +R Z 2002 o - O 7 1 0 S +R Z 2003 o - Mar 28 1 1 D +R Z 2003 o - O 3 1 0 S +R Z 2004 o - Ap 7 1 1 D +R Z 2004 o - S 22 1 0 S +R Z 2005 2012 - Ap F<=1 2 1 D +R Z 2005 o - O 9 2 0 S +R Z 2006 o - O 1 2 0 S +R Z 2007 o - S 16 2 0 S +R Z 2008 o - O 5 2 0 S +R Z 2009 o - S 27 2 0 S +R Z 2010 o - S 12 2 0 S +R Z 2011 o - O 2 2 0 S +R Z 2012 o - S 23 2 0 S +R Z 2013 ma - Mar F>=23 2 1 D +R Z 2013 ma - O lastSu 2 0 S +Z Asia/Jerusalem 2:20:54 - LMT 1880 +2:20:40 - JMT 1918 +2 Z I%sT +R JP 1948 o - May Sa>=1 24 1 D +R JP 1948 1951 - S Sa>=8 25 0 S +R JP 1949 o - Ap Sa>=1 24 1 D +R JP 1950 1951 - May Sa>=1 24 1 D +Z Asia/Tokyo 9:18:59 - LMT 1887 D 31 15u +9 JP J%sT +R J 1973 o - Jun 6 0 1 S +R J 1973 1975 - O 1 0 0 - +R J 1974 1977 - May 1 0 1 S +R J 1976 o - N 1 0 0 - +R J 1977 o - O 1 0 0 - +R J 1978 o - Ap 30 0 1 S +R J 1978 o - S 30 0 0 - +R J 1985 o - Ap 1 0 1 S +R J 1985 o - O 1 0 0 - +R J 1986 1988 - Ap F>=1 0 1 S +R J 1986 1990 - O F>=1 0 0 - +R J 1989 o - May 8 0 1 S +R J 1990 o - Ap 27 0 1 S +R J 1991 o - Ap 17 0 1 S +R J 1991 o - S 27 0 0 - +R J 1992 o - Ap 10 0 1 S +R J 1992 1993 - O F>=1 0 0 - +R J 1993 1998 - Ap F>=1 0 1 S +R J 1994 o - S F>=15 0 0 - +R J 1995 1998 - S F>=15 0s 0 - +R J 1999 o - Jul 1 0s 1 S +R J 1999 2002 - S lastF 0s 0 - +R J 2000 2001 - Mar lastTh 0s 1 S +R J 2002 2012 - Mar lastTh 24 1 S +R J 2003 o - O 24 0s 0 - +R J 2004 o - O 15 0s 0 - +R J 2005 o - S lastF 0s 0 - +R J 2006 2011 - O lastF 0s 0 - +R J 2013 o - D 20 0 0 - +R J 2014 2021 - Mar lastTh 24 1 S +R J 2014 ma - O lastF 0s 0 - +R J 2022 ma - F lastTh 24 1 S +Z Asia/Amman 2:23:44 - LMT 1931 +2 J EE%sT +Z Asia/Almaty 5:7:48 - LMT 1924 May 2 +5 - +05 1930 Jun 21 +6 R +06/+07 1991 Mar 31 2s +5 R +05/+06 1992 Ja 19 2s +6 R +06/+07 2004 O 31 2s +6 - +06 +Z Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 - +05 1981 Ap +5 1 +06 1981 O +6 - +06 1982 Ap +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1991 S 29 2s +5 R +05/+06 1992 Ja 19 2s +6 R +06/+07 1992 Mar 29 2s +5 R +05/+06 2004 O 31 2s +6 - +06 2018 D 21 +5 - +05 +Z Asia/Qostanay 4:14:28 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 - +05 1981 Ap +5 1 +06 1981 O +6 - +06 1982 Ap +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 2004 O 31 2s +6 - +06 +Z Asia/Aqtobe 3:48:40 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 - +05 1981 Ap +5 1 +06 1981 O +6 - +06 1982 Ap +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 2004 O 31 2s +5 - +05 +Z Asia/Aqtau 3:21:4 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 - +05 1981 O +6 - +06 1982 Ap +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 1994 S 25 2s +4 R +04/+05 2004 O 31 2s +5 - +05 +Z Asia/Atyrau 3:27:44 - LMT 1924 May 2 +3 - +03 1930 Jun 21 +5 - +05 1981 O +6 - +06 1982 Ap +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 1999 Mar 28 2s +4 R +04/+05 2004 O 31 2s +5 - +05 +Z Asia/Oral 3:25:24 - LMT 1924 May 2 +3 - +03 1930 Jun 21 +5 - +05 1981 Ap +5 1 +06 1981 O +6 - +06 1982 Ap +5 R +05/+06 1989 Mar 26 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 1992 Mar 29 2s +4 R +04/+05 2004 O 31 2s +5 - +05 +R KG 1992 1996 - Ap Su>=7 0s 1 - +R KG 1992 1996 - S lastSu 0 0 - +R KG 1997 2005 - Mar lastSu 2:30 1 - +R KG 1997 2004 - O lastSu 2:30 0 - +Z Asia/Bishkek 4:58:24 - LMT 1924 May 2 +5 - +05 1930 Jun 21 +6 R +06/+07 1991 Mar 31 2s +5 R +05/+06 1991 Au 31 2 +5 KG +05/+06 2005 Au 12 +6 - +06 +R KR 1948 o - Jun 1 0 1 D +R KR 1948 o - S 12 24 0 S +R KR 1949 o - Ap 3 0 1 D +R KR 1949 1951 - S Sa>=7 24 0 S +R KR 1950 o - Ap 1 0 1 D +R KR 1951 o - May 6 0 1 D +R KR 1955 o - May 5 0 1 D +R KR 1955 o - S 8 24 0 S +R KR 1956 o - May 20 0 1 D +R KR 1956 o - S 29 24 0 S +R KR 1957 1960 - May Su>=1 0 1 D +R KR 1957 1960 - S Sa>=17 24 0 S +R KR 1987 1988 - May Su>=8 2 1 D +R KR 1987 1988 - O Su>=8 3 0 S +Z Asia/Seoul 8:27:52 - LMT 1908 Ap +8:30 - KST 1912 +9 - JST 1945 S 8 +9 KR K%sT 1954 Mar 21 +8:30 KR K%sT 1961 Au 10 +9 KR K%sT +Z Asia/Pyongyang 8:23 - LMT 1908 Ap +8:30 - KST 1912 +9 - JST 1945 Au 24 +9 - KST 2015 Au 15 +8:30 - KST 2018 May 4 23:30 +9 - KST +R l 1920 o - Mar 28 0 1 S +R l 1920 o - O 25 0 0 - +R l 1921 o - Ap 3 0 1 S +R l 1921 o - O 3 0 0 - +R l 1922 o - Mar 26 0 1 S +R l 1922 o - O 8 0 0 - +R l 1923 o - Ap 22 0 1 S +R l 1923 o - S 16 0 0 - +R l 1957 1961 - May 1 0 1 S +R l 1957 1961 - O 1 0 0 - +R l 1972 o - Jun 22 0 1 S +R l 1972 1977 - O 1 0 0 - +R l 1973 1977 - May 1 0 1 S +R l 1978 o - Ap 30 0 1 S +R l 1978 o - S 30 0 0 - +R l 1984 1987 - May 1 0 1 S +R l 1984 1991 - O 16 0 0 - +R l 1988 o - Jun 1 0 1 S +R l 1989 o - May 10 0 1 S +R l 1990 1992 - May 1 0 1 S +R l 1992 o - O 4 0 0 - +R l 1993 ma - Mar lastSu 0 1 S +R l 1993 1998 - S lastSu 0 0 - +R l 1999 ma - O lastSu 0 0 - +Z Asia/Beirut 2:22 - LMT 1880 +2 l EE%sT +R NB 1935 1941 - S 14 0 0:20 - +R NB 1935 1941 - D 14 0 0 - +Z Asia/Kuala_Lumpur 6:46:46 - LMT 1901 +6:55:25 - SMT 1905 Jun +7 - +07 1933 +7 0:20 +0720 1936 +7:20 - +0720 1941 S +7:30 - +0730 1942 F 16 +9 - +09 1945 S 12 +7:30 - +0730 1982 +8 - +08 +Z Asia/Kuching 7:21:20 - LMT 1926 Mar +7:30 - +0730 1933 +8 NB +08/+0820 1942 F 16 +9 - +09 1945 S 12 +8 - +08 +Z Indian/Maldives 4:54 - LMT 1880 +4:54 - MMT 1960 +5 - +05 +R X 1983 1984 - Ap 1 0 1 - +R X 1983 o - O 1 0 0 - +R X 1985 1998 - Mar lastSu 0 1 - +R X 1984 1998 - S lastSu 0 0 - +R X 2001 o - Ap lastSa 2 1 - +R X 2001 2006 - S lastSa 2 0 - +R X 2002 2006 - Mar lastSa 2 1 - +R X 2015 2016 - Mar lastSa 2 1 - +R X 2015 2016 - S lastSa 0 0 - +Z Asia/Hovd 6:6:36 - LMT 1905 Au +6 - +06 1978 +7 X +07/+08 +Z Asia/Ulaanbaatar 7:7:32 - LMT 1905 Au +7 - +07 1978 +8 X +08/+09 +Z Asia/Choibalsan 7:38 - LMT 1905 Au +7 - +07 1978 +8 - +08 1983 Ap +9 X +09/+10 2008 Mar 31 +8 X +08/+09 +Z Asia/Kathmandu 5:41:16 - LMT 1920 +5:30 - +0530 1986 +5:45 - +0545 +R PK 2002 o - Ap Su>=2 0 1 S +R PK 2002 o - O Su>=2 0 0 - +R PK 2008 o - Jun 1 0 1 S +R PK 2008 2009 - N 1 0 0 - +R PK 2009 o - Ap 15 0 1 S +Z Asia/Karachi 4:28:12 - LMT 1907 +5:30 - +0530 1942 S +5:30 1 +0630 1945 O 15 +5:30 - +0530 1951 S 30 +5 - +05 1971 Mar 26 +5 PK PK%sT +R P 1999 2005 - Ap F>=15 0 1 S +R P 1999 2003 - O F>=15 0 0 - +R P 2004 o - O 1 1 0 - +R P 2005 o - O 4 2 0 - +R P 2006 2007 - Ap 1 0 1 S +R P 2006 o - S 22 0 0 - +R P 2007 o - S 13 2 0 - +R P 2008 2009 - Mar lastF 0 1 S +R P 2008 o - S 1 0 0 - +R P 2009 o - S 4 1 0 - +R P 2010 o - Mar 26 0 1 S +R P 2010 o - Au 11 0 0 - +R P 2011 o - Ap 1 0:1 1 S +R P 2011 o - Au 1 0 0 - +R P 2011 o - Au 30 0 1 S +R P 2011 o - S 30 0 0 - +R P 2012 2014 - Mar lastTh 24 1 S +R P 2012 o - S 21 1 0 - +R P 2013 o - S 27 0 0 - +R P 2014 o - O 24 0 0 - +R P 2015 o - Mar 28 0 1 S +R P 2015 o - O 23 1 0 - +R P 2016 2018 - Mar Sa>=24 1 1 S +R P 2016 2018 - O Sa>=24 1 0 - +R P 2019 o - Mar 29 0 1 S +R P 2019 o - O Sa>=24 0 0 - +R P 2020 2021 - Mar Sa>=24 0 1 S +R P 2020 o - O 24 1 0 - +R P 2021 ma - O F>=23 1 0 - +R P 2022 ma - Mar Su>=25 0 1 S +Z Asia/Gaza 2:17:52 - LMT 1900 O +2 Z EET/EEST 1948 May 15 +2 K EE%sT 1967 Jun 5 +2 Z I%sT 1996 +2 J EE%sT 1999 +2 P EE%sT 2008 Au 29 +2 - EET 2008 S +2 P EE%sT 2010 +2 - EET 2010 Mar 27 0:1 +2 P EE%sT 2011 Au +2 - EET 2012 +2 P EE%sT +Z Asia/Hebron 2:20:23 - LMT 1900 O +2 Z EET/EEST 1948 May 15 +2 K EE%sT 1967 Jun 5 +2 Z I%sT 1996 +2 J EE%sT 1999 +2 P EE%sT +R PH 1936 o - N 1 0 1 D +R PH 1937 o - F 1 0 0 S +R PH 1954 o - Ap 12 0 1 D +R PH 1954 o - Jul 1 0 0 S +R PH 1978 o - Mar 22 0 1 D +R PH 1978 o - S 21 0 0 S +Z Asia/Manila -15:56 - LMT 1844 D 31 +8:4 - LMT 1899 May 11 +8 PH P%sT 1942 May +9 - JST 1944 N +8 PH P%sT +Z Asia/Qatar 3:26:8 - LMT 1920 +4 - +04 1972 Jun +3 - +03 +L Asia/Qatar Asia/Bahrain +Z Asia/Riyadh 3:6:52 - LMT 1947 Mar 14 +3 - +03 +L Asia/Riyadh Antarctica/Syowa +L Asia/Riyadh Asia/Aden +L Asia/Riyadh Asia/Kuwait +Z Asia/Singapore 6:55:25 - LMT 1901 +6:55:25 - SMT 1905 Jun +7 - +07 1933 +7 0:20 +0720 1936 +7:20 - +0720 1941 S +7:30 - +0730 1942 F 16 +9 - +09 1945 S 12 +7:30 - +0730 1982 +8 - +08 +Z Asia/Colombo 5:19:24 - LMT 1880 +5:19:32 - MMT 1906 +5:30 - +0530 1942 Ja 5 +5:30 0:30 +06 1942 S +5:30 1 +0630 1945 O 16 2 +5:30 - +0530 1996 May 25 +6:30 - +0630 1996 O 26 0:30 +6 - +06 2006 Ap 15 0:30 +5:30 - +0530 +R S 1920 1923 - Ap Su>=15 2 1 S +R S 1920 1923 - O Su>=1 2 0 - +R S 1962 o - Ap 29 2 1 S +R S 1962 o - O 1 2 0 - +R S 1963 1965 - May 1 2 1 S +R S 1963 o - S 30 2 0 - +R S 1964 o - O 1 2 0 - +R S 1965 o - S 30 2 0 - +R S 1966 o - Ap 24 2 1 S +R S 1966 1976 - O 1 2 0 - +R S 1967 1978 - May 1 2 1 S +R S 1977 1978 - S 1 2 0 - +R S 1983 1984 - Ap 9 2 1 S +R S 1983 1984 - O 1 2 0 - +R S 1986 o - F 16 2 1 S +R S 1986 o - O 9 2 0 - +R S 1987 o - Mar 1 2 1 S +R S 1987 1988 - O 31 2 0 - +R S 1988 o - Mar 15 2 1 S +R S 1989 o - Mar 31 2 1 S +R S 1989 o - O 1 2 0 - +R S 1990 o - Ap 1 2 1 S +R S 1990 o - S 30 2 0 - +R S 1991 o - Ap 1 0 1 S +R S 1991 1992 - O 1 0 0 - +R S 1992 o - Ap 8 0 1 S +R S 1993 o - Mar 26 0 1 S +R S 1993 o - S 25 0 0 - +R S 1994 1996 - Ap 1 0 1 S +R S 1994 2005 - O 1 0 0 - +R S 1997 1998 - Mar lastM 0 1 S +R S 1999 2006 - Ap 1 0 1 S +R S 2006 o - S 22 0 0 - +R S 2007 o - Mar lastF 0 1 S +R S 2007 o - N F>=1 0 0 - +R S 2008 o - Ap F>=1 0 1 S +R S 2008 o - N 1 0 0 - +R S 2009 o - Mar lastF 0 1 S +R S 2010 2011 - Ap F>=1 0 1 S +R S 2012 ma - Mar lastF 0 1 S +R S 2009 ma - O lastF 0 0 - +Z Asia/Damascus 2:25:12 - LMT 1920 +2 S EE%sT +Z Asia/Dushanbe 4:35:12 - LMT 1924 May 2 +5 - +05 1930 Jun 21 +6 R +06/+07 1991 Mar 31 2s +5 1 +05/+06 1991 S 9 2s +5 - +05 +Z Asia/Bangkok 6:42:4 - LMT 1880 +6:42:4 - BMT 1920 Ap +7 - +07 +L Asia/Bangkok Asia/Phnom_Penh +L Asia/Bangkok Asia/Vientiane +Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 R +05/+06 1991 Mar 31 2 +4 R +04/+05 1992 Ja 19 2 +5 - +05 +Z Asia/Dubai 3:41:12 - LMT 1920 +4 - +04 +L Asia/Dubai Asia/Muscat +Z Asia/Samarkand 4:27:53 - LMT 1924 May 2 +4 - +04 1930 Jun 21 +5 - +05 1981 Ap +5 1 +06 1981 O +6 - +06 1982 Ap +5 R +05/+06 1992 +5 - +05 +Z Asia/Tashkent 4:37:11 - LMT 1924 May 2 +5 - +05 1930 Jun 21 +6 R +06/+07 1991 Mar 31 2 +5 R +05/+06 1992 +5 - +05 +Z Asia/Ho_Chi_Minh 7:6:40 - LMT 1906 Jul +7:6:30 - PLMT 1911 May +7 - +07 1942 D 31 23 +8 - +08 1945 Mar 14 23 +9 - +09 1945 S 2 +7 - +07 1947 Ap +8 - +08 1955 Jul +7 - +07 1959 D 31 23 +8 - +08 1975 Jun 13 +7 - +07 +R AU 1917 o - Ja 1 2s 1 D +R AU 1917 o - Mar lastSu 2s 0 S +R AU 1942 o - Ja 1 2s 1 D +R AU 1942 o - Mar lastSu 2s 0 S +R AU 1942 o - S 27 2s 1 D +R AU 1943 1944 - Mar lastSu 2s 0 S +R AU 1943 o - O 3 2s 1 D +Z Australia/Darwin 8:43:20 - LMT 1895 F +9 - ACST 1899 May +9:30 AU AC%sT +R AW 1974 o - O lastSu 2s 1 D +R AW 1975 o - Mar Su>=1 2s 0 S +R AW 1983 o - O lastSu 2s 1 D +R AW 1984 o - Mar Su>=1 2s 0 S +R AW 1991 o - N 17 2s 1 D +R AW 1992 o - Mar Su>=1 2s 0 S +R AW 2006 o - D 3 2s 1 D +R AW 2007 2009 - Mar lastSu 2s 0 S +R AW 2007 2008 - O lastSu 2s 1 D +Z Australia/Perth 7:43:24 - LMT 1895 D +8 AU AW%sT 1943 Jul +8 AW AW%sT +Z Australia/Eucla 8:35:28 - LMT 1895 D +8:45 AU +0845/+0945 1943 Jul +8:45 AW +0845/+0945 +R AQ 1971 o - O lastSu 2s 1 D +R AQ 1972 o - F lastSu 2s 0 S +R AQ 1989 1991 - O lastSu 2s 1 D +R AQ 1990 1992 - Mar Su>=1 2s 0 S +R Ho 1992 1993 - O lastSu 2s 1 D +R Ho 1993 1994 - Mar Su>=1 2s 0 S +Z Australia/Brisbane 10:12:8 - LMT 1895 +10 AU AE%sT 1971 +10 AQ AE%sT +Z Australia/Lindeman 9:55:56 - LMT 1895 +10 AU AE%sT 1971 +10 AQ AE%sT 1992 Jul +10 Ho AE%sT +R AS 1971 1985 - O lastSu 2s 1 D +R AS 1986 o - O 19 2s 1 D +R AS 1987 2007 - O lastSu 2s 1 D +R AS 1972 o - F 27 2s 0 S +R AS 1973 1985 - Mar Su>=1 2s 0 S +R AS 1986 1990 - Mar Su>=15 2s 0 S +R AS 1991 o - Mar 3 2s 0 S +R AS 1992 o - Mar 22 2s 0 S +R AS 1993 o - Mar 7 2s 0 S +R AS 1994 o - Mar 20 2s 0 S +R AS 1995 2005 - Mar lastSu 2s 0 S +R AS 2006 o - Ap 2 2s 0 S +R AS 2007 o - Mar lastSu 2s 0 S +R AS 2008 ma - Ap Su>=1 2s 0 S +R AS 2008 ma - O Su>=1 2s 1 D +Z Australia/Adelaide 9:14:20 - LMT 1895 F +9 - ACST 1899 May +9:30 AU AC%sT 1971 +9:30 AS AC%sT +R AT 1916 o - O Su>=1 2s 1 D +R AT 1917 o - Mar lastSu 2s 0 S +R AT 1917 1918 - O Su>=22 2s 1 D +R AT 1918 1919 - Mar Su>=1 2s 0 S +R AT 1967 o - O Su>=1 2s 1 D +R AT 1968 o - Mar Su>=29 2s 0 S +R AT 1968 1985 - O lastSu 2s 1 D +R AT 1969 1971 - Mar Su>=8 2s 0 S +R AT 1972 o - F lastSu 2s 0 S +R AT 1973 1981 - Mar Su>=1 2s 0 S +R AT 1982 1983 - Mar lastSu 2s 0 S +R AT 1984 1986 - Mar Su>=1 2s 0 S +R AT 1986 o - O Su>=15 2s 1 D +R AT 1987 1990 - Mar Su>=15 2s 0 S +R AT 1987 o - O Su>=22 2s 1 D +R AT 1988 1990 - O lastSu 2s 1 D +R AT 1991 1999 - O Su>=1 2s 1 D +R AT 1991 2005 - Mar lastSu 2s 0 S +R AT 2000 o - Au lastSu 2s 1 D +R AT 2001 ma - O Su>=1 2s 1 D +R AT 2006 o - Ap Su>=1 2s 0 S +R AT 2007 o - Mar lastSu 2s 0 S +R AT 2008 ma - Ap Su>=1 2s 0 S +Z Australia/Hobart 9:49:16 - LMT 1895 S +10 AT AE%sT 1919 O 24 +10 AU AE%sT 1967 +10 AT AE%sT +R AV 1971 1985 - O lastSu 2s 1 D +R AV 1972 o - F lastSu 2s 0 S +R AV 1973 1985 - Mar Su>=1 2s 0 S +R AV 1986 1990 - Mar Su>=15 2s 0 S +R AV 1986 1987 - O Su>=15 2s 1 D +R AV 1988 1999 - O lastSu 2s 1 D +R AV 1991 1994 - Mar Su>=1 2s 0 S +R AV 1995 2005 - Mar lastSu 2s 0 S +R AV 2000 o - Au lastSu 2s 1 D +R AV 2001 2007 - O lastSu 2s 1 D +R AV 2006 o - Ap Su>=1 2s 0 S +R AV 2007 o - Mar lastSu 2s 0 S +R AV 2008 ma - Ap Su>=1 2s 0 S +R AV 2008 ma - O Su>=1 2s 1 D +Z Australia/Melbourne 9:39:52 - LMT 1895 F +10 AU AE%sT 1971 +10 AV AE%sT +R AN 1971 1985 - O lastSu 2s 1 D +R AN 1972 o - F 27 2s 0 S +R AN 1973 1981 - Mar Su>=1 2s 0 S +R AN 1982 o - Ap Su>=1 2s 0 S +R AN 1983 1985 - Mar Su>=1 2s 0 S +R AN 1986 1989 - Mar Su>=15 2s 0 S +R AN 1986 o - O 19 2s 1 D +R AN 1987 1999 - O lastSu 2s 1 D +R AN 1990 1995 - Mar Su>=1 2s 0 S +R AN 1996 2005 - Mar lastSu 2s 0 S +R AN 2000 o - Au lastSu 2s 1 D +R AN 2001 2007 - O lastSu 2s 1 D +R AN 2006 o - Ap Su>=1 2s 0 S +R AN 2007 o - Mar lastSu 2s 0 S +R AN 2008 ma - Ap Su>=1 2s 0 S +R AN 2008 ma - O Su>=1 2s 1 D +Z Australia/Sydney 10:4:52 - LMT 1895 F +10 AU AE%sT 1971 +10 AN AE%sT +Z Australia/Broken_Hill 9:25:48 - LMT 1895 F +10 - AEST 1896 Au 23 +9 - ACST 1899 May +9:30 AU AC%sT 1971 +9:30 AN AC%sT 2000 +9:30 AS AC%sT +R LH 1981 1984 - O lastSu 2 1 - +R LH 1982 1985 - Mar Su>=1 2 0 - +R LH 1985 o - O lastSu 2 0:30 - +R LH 1986 1989 - Mar Su>=15 2 0 - +R LH 1986 o - O 19 2 0:30 - +R LH 1987 1999 - O lastSu 2 0:30 - +R LH 1990 1995 - Mar Su>=1 2 0 - +R LH 1996 2005 - Mar lastSu 2 0 - +R LH 2000 o - Au lastSu 2 0:30 - +R LH 2001 2007 - O lastSu 2 0:30 - +R LH 2006 o - Ap Su>=1 2 0 - +R LH 2007 o - Mar lastSu 2 0 - +R LH 2008 ma - Ap Su>=1 2 0 - +R LH 2008 ma - O Su>=1 2 0:30 - +Z Australia/Lord_Howe 10:36:20 - LMT 1895 F +10 - AEST 1981 Mar +10:30 LH +1030/+1130 1985 Jul +10:30 LH +1030/+11 +Z Antarctica/Macquarie 0 - -00 1899 N +10 - AEST 1916 O 1 2 +10 1 AEDT 1917 F +10 AU AE%sT 1919 Ap 1 0s +0 - -00 1948 Mar 25 +10 AU AE%sT 1967 +10 AT AE%sT 2010 +10 1 AEDT 2011 +10 AT AE%sT +Z Indian/Christmas 7:2:52 - LMT 1895 F +7 - +07 +Z Indian/Cocos 6:27:40 - LMT 1900 +6:30 - +0630 +R FJ 1998 1999 - N Su>=1 2 1 - +R FJ 1999 2000 - F lastSu 3 0 - +R FJ 2009 o - N 29 2 1 - +R FJ 2010 o - Mar lastSu 3 0 - +R FJ 2010 2013 - O Su>=21 2 1 - +R FJ 2011 o - Mar Su>=1 3 0 - +R FJ 2012 2013 - Ja Su>=18 3 0 - +R FJ 2014 o - Ja Su>=18 2 0 - +R FJ 2014 2018 - N Su>=1 2 1 - +R FJ 2015 2021 - Ja Su>=12 3 0 - +R FJ 2019 o - N Su>=8 2 1 - +R FJ 2020 o - D 20 2 1 - +R FJ 2022 ma - N Su>=8 2 1 - +R FJ 2023 ma - Ja Su>=12 3 0 - +Z Pacific/Fiji 11:55:44 - LMT 1915 O 26 +12 FJ +12/+13 +Z Pacific/Gambier -8:59:48 - LMT 1912 O +-9 - -09 +Z Pacific/Marquesas -9:18 - LMT 1912 O +-9:30 - -0930 +Z Pacific/Tahiti -9:58:16 - LMT 1912 O +-10 - -10 +R Gu 1959 o - Jun 27 2 1 D +R Gu 1961 o - Ja 29 2 0 S +R Gu 1967 o - S 1 2 1 D +R Gu 1969 o - Ja 26 0:1 0 S +R Gu 1969 o - Jun 22 2 1 D +R Gu 1969 o - Au 31 2 0 S +R Gu 1970 1971 - Ap lastSu 2 1 D +R Gu 1970 1971 - S Su>=1 2 0 S +R Gu 1973 o - D 16 2 1 D +R Gu 1974 o - F 24 2 0 S +R Gu 1976 o - May 26 2 1 D +R Gu 1976 o - Au 22 2:1 0 S +R Gu 1977 o - Ap 24 2 1 D +R Gu 1977 o - Au 28 2 0 S +Z Pacific/Guam -14:21 - LMT 1844 D 31 +9:39 - LMT 1901 +10 - GST 1941 D 10 +9 - +09 1944 Jul 31 +10 Gu G%sT 2000 D 23 +10 - ChST +L Pacific/Guam Pacific/Saipan +Z Pacific/Tarawa 11:32:4 - LMT 1901 +12 - +12 +Z Pacific/Kanton 0 - -00 1937 Au 31 +-12 - -12 1979 O +-11 - -11 1994 D 31 +13 - +13 +Z Pacific/Kiritimati -10:29:20 - LMT 1901 +-10:40 - -1040 1979 O +-10 - -10 1994 D 31 +14 - +14 +Z Pacific/Majuro 11:24:48 - LMT 1901 +11 - +11 1914 O +9 - +09 1919 F +11 - +11 1937 +10 - +10 1941 Ap +9 - +09 1944 Ja 30 +11 - +11 1969 O +12 - +12 +Z Pacific/Kwajalein 11:9:20 - LMT 1901 +11 - +11 1937 +10 - +10 1941 Ap +9 - +09 1944 F 6 +11 - +11 1969 O +-12 - -12 1993 Au 20 24 +12 - +12 +Z Pacific/Chuuk -13:52:52 - LMT 1844 D 31 +10:7:8 - LMT 1901 +10 - +10 1914 O +9 - +09 1919 F +10 - +10 1941 Ap +9 - +09 1945 Au +10 - +10 +Z Pacific/Pohnpei -13:27:8 - LMT 1844 D 31 +10:32:52 - LMT 1901 +11 - +11 1914 O +9 - +09 1919 F +11 - +11 1937 +10 - +10 1941 Ap +9 - +09 1945 Au +11 - +11 +Z Pacific/Kosrae -13:8:4 - LMT 1844 D 31 +10:51:56 - LMT 1901 +11 - +11 1914 O +9 - +09 1919 F +11 - +11 1937 +10 - +10 1941 Ap +9 - +09 1945 Au +11 - +11 1969 O +12 - +12 1999 +11 - +11 +Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15 +11:30 - +1130 1942 Au 29 +9 - +09 1945 S 8 +11:30 - +1130 1979 F 10 2 +12 - +12 +R NC 1977 1978 - D Su>=1 0 1 - +R NC 1978 1979 - F 27 0 0 - +R NC 1996 o - D 1 2s 1 - +R NC 1997 o - Mar 2 2s 0 - +Z Pacific/Noumea 11:5:48 - LMT 1912 Ja 13 +11 NC +11/+12 +R NZ 1927 o - N 6 2 1 S +R NZ 1928 o - Mar 4 2 0 M +R NZ 1928 1933 - O Su>=8 2 0:30 S +R NZ 1929 1933 - Mar Su>=15 2 0 M +R NZ 1934 1940 - Ap lastSu 2 0 M +R NZ 1934 1940 - S lastSu 2 0:30 S +R NZ 1946 o - Ja 1 0 0 S +R NZ 1974 o - N Su>=1 2s 1 D +R k 1974 o - N Su>=1 2:45s 1 - +R NZ 1975 o - F lastSu 2s 0 S +R k 1975 o - F lastSu 2:45s 0 - +R NZ 1975 1988 - O lastSu 2s 1 D +R k 1975 1988 - O lastSu 2:45s 1 - +R NZ 1976 1989 - Mar Su>=1 2s 0 S +R k 1976 1989 - Mar Su>=1 2:45s 0 - +R NZ 1989 o - O Su>=8 2s 1 D +R k 1989 o - O Su>=8 2:45s 1 - +R NZ 1990 2006 - O Su>=1 2s 1 D +R k 1990 2006 - O Su>=1 2:45s 1 - +R NZ 1990 2007 - Mar Su>=15 2s 0 S +R k 1990 2007 - Mar Su>=15 2:45s 0 - +R NZ 2007 ma - S lastSu 2s 1 D +R k 2007 ma - S lastSu 2:45s 1 - +R NZ 2008 ma - Ap Su>=1 2s 0 S +R k 2008 ma - Ap Su>=1 2:45s 0 - +Z Pacific/Auckland 11:39:4 - LMT 1868 N 2 +11:30 NZ NZ%sT 1946 +12 NZ NZ%sT +Z Pacific/Chatham 12:13:48 - LMT 1868 N 2 +12:15 - +1215 1946 +12:45 k +1245/+1345 +L Pacific/Auckland Antarctica/McMurdo +R CK 1978 o - N 12 0 0:30 - +R CK 1979 1991 - Mar Su>=1 0 0 - +R CK 1979 1990 - O lastSu 0 0:30 - +Z Pacific/Rarotonga 13:20:56 - LMT 1899 D 26 +-10:39:4 - LMT 1952 O 16 +-10:30 - -1030 1978 N 12 +-10 CK -10/-0930 +Z Pacific/Niue -11:19:40 - LMT 1952 O 16 +-11:20 - -1120 1964 Jul +-11 - -11 +Z Pacific/Norfolk 11:11:52 - LMT 1901 +11:12 - +1112 1951 +11:30 - +1130 1974 O 27 2s +11:30 1 +1230 1975 Mar 2 2s +11:30 - +1130 2015 O 4 2s +11 - +11 2019 Jul +11 AN +11/+12 +Z Pacific/Palau -15:2:4 - LMT 1844 D 31 +8:57:56 - LMT 1901 +9 - +09 +Z Pacific/Port_Moresby 9:48:40 - LMT 1880 +9:48:32 - PMMT 1895 +10 - +10 +L Pacific/Port_Moresby Antarctica/DumontDUrville +Z Pacific/Bougainville 10:22:16 - LMT 1880 +9:48:32 - PMMT 1895 +10 - +10 1942 Jul +9 - +09 1945 Au 21 +10 - +10 2014 D 28 2 +11 - +11 +Z Pacific/Pitcairn -8:40:20 - LMT 1901 +-8:30 - -0830 1998 Ap 27 +-8 - -08 +Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 +-11:22:48 - LMT 1911 +-11 - SST +L Pacific/Pago_Pago Pacific/Midway +R WS 2010 o - S lastSu 0 1 - +R WS 2011 o - Ap Sa>=1 4 0 - +R WS 2011 o - S lastSa 3 1 - +R WS 2012 2021 - Ap Su>=1 4 0 - +R WS 2012 2020 - S lastSu 3 1 - +Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5 +-11:26:56 - LMT 1911 +-11:30 - -1130 1950 +-11 WS -11/-10 2011 D 29 24 +13 WS +13/+14 +Z Pacific/Guadalcanal 10:39:48 - LMT 1912 O +11 - +11 +Z Pacific/Fakaofo -11:24:56 - LMT 1901 +-11 - -11 2011 D 30 +13 - +13 +R TO 1999 o - O 7 2s 1 - +R TO 2000 o - Mar 19 2s 0 - +R TO 2000 2001 - N Su>=1 2 1 - +R TO 2001 2002 - Ja lastSu 2 0 - +R TO 2016 o - N Su>=1 2 1 - +R TO 2017 o - Ja Su>=15 3 0 - +Z Pacific/Tongatapu 12:19:12 - LMT 1945 S 10 +12:20 - +1220 1961 +13 - +13 1999 +13 TO +13/+14 +Z Pacific/Funafuti 11:56:52 - LMT 1901 +12 - +12 +Z Pacific/Wake 11:6:28 - LMT 1901 +12 - +12 +R VU 1973 o - D 22 12u 1 - +R VU 1974 o - Mar 30 12u 0 - +R VU 1983 1991 - S Sa>=22 24 1 - +R VU 1984 1991 - Mar Sa>=22 24 0 - +R VU 1992 1993 - Ja Sa>=22 24 0 - +R VU 1992 o - O Sa>=22 24 1 - +Z Pacific/Efate 11:13:16 - LMT 1912 Ja 13 +11 VU +11/+12 +Z Pacific/Wallis 12:15:20 - LMT 1901 +12 - +12 +R G 1916 o - May 21 2s 1 BST +R G 1916 o - O 1 2s 0 GMT +R G 1917 o - Ap 8 2s 1 BST +R G 1917 o - S 17 2s 0 GMT +R G 1918 o - Mar 24 2s 1 BST +R G 1918 o - S 30 2s 0 GMT +R G 1919 o - Mar 30 2s 1 BST +R G 1919 o - S 29 2s 0 GMT +R G 1920 o - Mar 28 2s 1 BST +R G 1920 o - O 25 2s 0 GMT +R G 1921 o - Ap 3 2s 1 BST +R G 1921 o - O 3 2s 0 GMT +R G 1922 o - Mar 26 2s 1 BST +R G 1922 o - O 8 2s 0 GMT +R G 1923 o - Ap Su>=16 2s 1 BST +R G 1923 1924 - S Su>=16 2s 0 GMT +R G 1924 o - Ap Su>=9 2s 1 BST +R G 1925 1926 - Ap Su>=16 2s 1 BST +R G 1925 1938 - O Su>=2 2s 0 GMT +R G 1927 o - Ap Su>=9 2s 1 BST +R G 1928 1929 - Ap Su>=16 2s 1 BST +R G 1930 o - Ap Su>=9 2s 1 BST +R G 1931 1932 - Ap Su>=16 2s 1 BST +R G 1933 o - Ap Su>=9 2s 1 BST +R G 1934 o - Ap Su>=16 2s 1 BST +R G 1935 o - Ap Su>=9 2s 1 BST +R G 1936 1937 - Ap Su>=16 2s 1 BST +R G 1938 o - Ap Su>=9 2s 1 BST +R G 1939 o - Ap Su>=16 2s 1 BST +R G 1939 o - N Su>=16 2s 0 GMT +R G 1940 o - F Su>=23 2s 1 BST +R G 1941 o - May Su>=2 1s 2 BDST +R G 1941 1943 - Au Su>=9 1s 1 BST +R G 1942 1944 - Ap Su>=2 1s 2 BDST +R G 1944 o - S Su>=16 1s 1 BST +R G 1945 o - Ap M>=2 1s 2 BDST +R G 1945 o - Jul Su>=9 1s 1 BST +R G 1945 1946 - O Su>=2 2s 0 GMT +R G 1946 o - Ap Su>=9 2s 1 BST +R G 1947 o - Mar 16 2s 1 BST +R G 1947 o - Ap 13 1s 2 BDST +R G 1947 o - Au 10 1s 1 BST +R G 1947 o - N 2 2s 0 GMT +R G 1948 o - Mar 14 2s 1 BST +R G 1948 o - O 31 2s 0 GMT +R G 1949 o - Ap 3 2s 1 BST +R G 1949 o - O 30 2s 0 GMT +R G 1950 1952 - Ap Su>=14 2s 1 BST +R G 1950 1952 - O Su>=21 2s 0 GMT +R G 1953 o - Ap Su>=16 2s 1 BST +R G 1953 1960 - O Su>=2 2s 0 GMT +R G 1954 o - Ap Su>=9 2s 1 BST +R G 1955 1956 - Ap Su>=16 2s 1 BST +R G 1957 o - Ap Su>=9 2s 1 BST +R G 1958 1959 - Ap Su>=16 2s 1 BST +R G 1960 o - Ap Su>=9 2s 1 BST +R G 1961 1963 - Mar lastSu 2s 1 BST +R G 1961 1968 - O Su>=23 2s 0 GMT +R G 1964 1967 - Mar Su>=19 2s 1 BST +R G 1968 o - F 18 2s 1 BST +R G 1972 1980 - Mar Su>=16 2s 1 BST +R G 1972 1980 - O Su>=23 2s 0 GMT +R G 1981 1995 - Mar lastSu 1u 1 BST +R G 1981 1989 - O Su>=23 1u 0 GMT +R G 1990 1995 - O Su>=22 1u 0 GMT +Z Europe/London -0:1:15 - LMT 1847 D 1 0s +0 G %s 1968 O 27 +1 - BST 1971 O 31 2u +0 G %s 1996 +0 E GMT/BST +L Europe/London Europe/Jersey +L Europe/London Europe/Guernsey +L Europe/London Europe/Isle_of_Man +R IE 1971 o - O 31 2u -1 - +R IE 1972 1980 - Mar Su>=16 2u 0 - +R IE 1972 1980 - O Su>=23 2u -1 - +R IE 1981 ma - Mar lastSu 1u 0 - +R IE 1981 1989 - O Su>=23 1u -1 - +R IE 1990 1995 - O Su>=22 1u -1 - +R IE 1996 ma - O lastSu 1u -1 - +Z Europe/Dublin -0:25 - LMT 1880 Au 2 +-0:25:21 - DMT 1916 May 21 2s +-0:25:21 1 IST 1916 O 1 2s +0 G %s 1921 D 6 +0 G GMT/IST 1940 F 25 2s +0 1 IST 1946 O 6 2s +0 - GMT 1947 Mar 16 2s +0 1 IST 1947 N 2 2s +0 - GMT 1948 Ap 18 2s +0 G GMT/IST 1968 O 27 +1 IE IST/GMT +R E 1977 1980 - Ap Su>=1 1u 1 S +R E 1977 o - S lastSu 1u 0 - +R E 1978 o - O 1 1u 0 - +R E 1979 1995 - S lastSu 1u 0 - +R E 1981 ma - Mar lastSu 1u 1 S +R E 1996 ma - O lastSu 1u 0 - +R W- 1977 1980 - Ap Su>=1 1s 1 S +R W- 1977 o - S lastSu 1s 0 - +R W- 1978 o - O 1 1s 0 - +R W- 1979 1995 - S lastSu 1s 0 - +R W- 1981 ma - Mar lastSu 1s 1 S +R W- 1996 ma - O lastSu 1s 0 - +R c 1916 o - Ap 30 23 1 S +R c 1916 o - O 1 1 0 - +R c 1917 1918 - Ap M>=15 2s 1 S +R c 1917 1918 - S M>=15 2s 0 - +R c 1940 o - Ap 1 2s 1 S +R c 1942 o - N 2 2s 0 - +R c 1943 o - Mar 29 2s 1 S +R c 1943 o - O 4 2s 0 - +R c 1944 1945 - Ap M>=1 2s 1 S +R c 1944 o - O 2 2s 0 - +R c 1945 o - S 16 2s 0 - +R c 1977 1980 - Ap Su>=1 2s 1 S +R c 1977 o - S lastSu 2s 0 - +R c 1978 o - O 1 2s 0 - +R c 1979 1995 - S lastSu 2s 0 - +R c 1981 ma - Mar lastSu 2s 1 S +R c 1996 ma - O lastSu 2s 0 - +R e 1977 1980 - Ap Su>=1 0 1 S +R e 1977 o - S lastSu 0 0 - +R e 1978 o - O 1 0 0 - +R e 1979 1995 - S lastSu 0 0 - +R e 1981 ma - Mar lastSu 0 1 S +R e 1996 ma - O lastSu 0 0 - +R R 1917 o - Jul 1 23 1 MST +R R 1917 o - D 28 0 0 MMT +R R 1918 o - May 31 22 2 MDST +R R 1918 o - S 16 1 1 MST +R R 1919 o - May 31 23 2 MDST +R R 1919 o - Jul 1 0u 1 MSD +R R 1919 o - Au 16 0 0 MSK +R R 1921 o - F 14 23 1 MSD +R R 1921 o - Mar 20 23 2 +05 +R R 1921 o - S 1 0 1 MSD +R R 1921 o - O 1 0 0 - +R R 1981 1984 - Ap 1 0 1 S +R R 1981 1983 - O 1 0 0 - +R R 1984 1995 - S lastSu 2s 0 - +R R 1985 2010 - Mar lastSu 2s 1 S +R R 1996 2010 - O lastSu 2s 0 - +Z WET 0 E WE%sT +Z CET 1 c CE%sT +Z MET 1 c ME%sT +Z EET 2 E EE%sT +R q 1940 o - Jun 16 0 1 S +R q 1942 o - N 2 3 0 - +R q 1943 o - Mar 29 2 1 S +R q 1943 o - Ap 10 3 0 - +R q 1974 o - May 4 0 1 S +R q 1974 o - O 2 0 0 - +R q 1975 o - May 1 0 1 S +R q 1975 o - O 2 0 0 - +R q 1976 o - May 2 0 1 S +R q 1976 o - O 3 0 0 - +R q 1977 o - May 8 0 1 S +R q 1977 o - O 2 0 0 - +R q 1978 o - May 6 0 1 S +R q 1978 o - O 1 0 0 - +R q 1979 o - May 5 0 1 S +R q 1979 o - S 30 0 0 - +R q 1980 o - May 3 0 1 S +R q 1980 o - O 4 0 0 - +R q 1981 o - Ap 26 0 1 S +R q 1981 o - S 27 0 0 - +R q 1982 o - May 2 0 1 S +R q 1982 o - O 3 0 0 - +R q 1983 o - Ap 18 0 1 S +R q 1983 o - O 1 0 0 - +R q 1984 o - Ap 1 0 1 S +Z Europe/Tirane 1:19:20 - LMT 1914 +1 - CET 1940 Jun 16 +1 q CE%sT 1984 Jul +1 E CE%sT +Z Europe/Andorra 0:6:4 - LMT 1901 +0 - WET 1946 S 30 +1 - CET 1985 Mar 31 2 +1 E CE%sT +R a 1920 o - Ap 5 2s 1 S +R a 1920 o - S 13 2s 0 - +R a 1946 o - Ap 14 2s 1 S +R a 1946 o - O 7 2s 0 - +R a 1947 1948 - O Su>=1 2s 0 - +R a 1947 o - Ap 6 2s 1 S +R a 1948 o - Ap 18 2s 1 S +R a 1980 o - Ap 6 0 1 S +R a 1980 o - S 28 0 0 - +Z Europe/Vienna 1:5:21 - LMT 1893 Ap +1 c CE%sT 1920 +1 a CE%sT 1940 Ap 1 2s +1 c CE%sT 1945 Ap 2 2s +1 1 CEST 1945 Ap 12 2s +1 - CET 1946 +1 a CE%sT 1981 +1 E CE%sT +Z Europe/Minsk 1:50:16 - LMT 1880 +1:50 - MMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 Jun 28 +1 c CE%sT 1944 Jul 3 +3 R MSK/MSD 1990 +3 - MSK 1991 Mar 31 2s +2 R EE%sT 2011 Mar 27 2s +3 - +03 +R b 1918 o - Mar 9 0s 1 S +R b 1918 1919 - O Sa>=1 23s 0 - +R b 1919 o - Mar 1 23s 1 S +R b 1920 o - F 14 23s 1 S +R b 1920 o - O 23 23s 0 - +R b 1921 o - Mar 14 23s 1 S +R b 1921 o - O 25 23s 0 - +R b 1922 o - Mar 25 23s 1 S +R b 1922 1927 - O Sa>=1 23s 0 - +R b 1923 o - Ap 21 23s 1 S +R b 1924 o - Mar 29 23s 1 S +R b 1925 o - Ap 4 23s 1 S +R b 1926 o - Ap 17 23s 1 S +R b 1927 o - Ap 9 23s 1 S +R b 1928 o - Ap 14 23s 1 S +R b 1928 1938 - O Su>=2 2s 0 - +R b 1929 o - Ap 21 2s 1 S +R b 1930 o - Ap 13 2s 1 S +R b 1931 o - Ap 19 2s 1 S +R b 1932 o - Ap 3 2s 1 S +R b 1933 o - Mar 26 2s 1 S +R b 1934 o - Ap 8 2s 1 S +R b 1935 o - Mar 31 2s 1 S +R b 1936 o - Ap 19 2s 1 S +R b 1937 o - Ap 4 2s 1 S +R b 1938 o - Mar 27 2s 1 S +R b 1939 o - Ap 16 2s 1 S +R b 1939 o - N 19 2s 0 - +R b 1940 o - F 25 2s 1 S +R b 1944 o - S 17 2s 0 - +R b 1945 o - Ap 2 2s 1 S +R b 1945 o - S 16 2s 0 - +R b 1946 o - May 19 2s 1 S +R b 1946 o - O 7 2s 0 - +Z Europe/Brussels 0:17:30 - LMT 1880 +0:17:30 - BMT 1892 May 1 0:17:30 +0 - WET 1914 N 8 +1 - CET 1916 May +1 c CE%sT 1918 N 11 11u +0 b WE%sT 1940 May 20 2s +1 c CE%sT 1944 S 3 +1 b CE%sT 1977 +1 E CE%sT +R BG 1979 o - Mar 31 23 1 S +R BG 1979 o - O 1 1 0 - +R BG 1980 1982 - Ap Sa>=1 23 1 S +R BG 1980 o - S 29 1 0 - +R BG 1981 o - S 27 2 0 - +Z Europe/Sofia 1:33:16 - LMT 1880 +1:56:56 - IMT 1894 N 30 +2 - EET 1942 N 2 3 +1 c CE%sT 1945 +1 - CET 1945 Ap 2 3 +2 - EET 1979 Mar 31 23 +2 BG EE%sT 1982 S 26 3 +2 c EE%sT 1991 +2 e EE%sT 1997 +2 E EE%sT +R CZ 1945 o - Ap M>=1 2s 1 S +R CZ 1945 o - O 1 2s 0 - +R CZ 1946 o - May 6 2s 1 S +R CZ 1946 1949 - O Su>=1 2s 0 - +R CZ 1947 1948 - Ap Su>=15 2s 1 S +R CZ 1949 o - Ap 9 2s 1 S +Z Europe/Prague 0:57:44 - LMT 1850 +0:57:44 - PMT 1891 O +1 c CE%sT 1945 May 9 +1 CZ CE%sT 1946 D 1 3 +1 -1 GMT 1947 F 23 2 +1 CZ CE%sT 1979 +1 E CE%sT +R D 1916 o - May 14 23 1 S +R D 1916 o - S 30 23 0 - +R D 1940 o - May 15 0 1 S +R D 1945 o - Ap 2 2s 1 S +R D 1945 o - Au 15 2s 0 - +R D 1946 o - May 1 2s 1 S +R D 1946 o - S 1 2s 0 - +R D 1947 o - May 4 2s 1 S +R D 1947 o - Au 10 2s 0 - +R D 1948 o - May 9 2s 1 S +R D 1948 o - Au 8 2s 0 - +Z Europe/Copenhagen 0:50:20 - LMT 1890 +0:50:20 - CMT 1894 +1 D CE%sT 1942 N 2 2s +1 c CE%sT 1945 Ap 2 2 +1 D CE%sT 1980 +1 E CE%sT +Z Atlantic/Faroe -0:27:4 - LMT 1908 Ja 11 +0 - WET 1981 +0 E WE%sT +R Th 1991 1992 - Mar lastSu 2 1 D +R Th 1991 1992 - S lastSu 2 0 S +R Th 1993 2006 - Ap Su>=1 2 1 D +R Th 1993 2006 - O lastSu 2 0 S +R Th 2007 ma - Mar Su>=8 2 1 D +R Th 2007 ma - N Su>=1 2 0 S +Z America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 +-3 - -03 1980 Ap 6 2 +-3 E -03/-02 1996 +0 - GMT +Z America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 +-2 - -02 1980 Ap 6 2 +-2 c -02/-01 1981 Mar 29 +-1 E -01/+00 +Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 +-3 - -03 1980 Ap 6 2 +-3 E -03/-02 +Z America/Thule -4:35:8 - LMT 1916 Jul 28 +-4 Th A%sT +Z Europe/Tallinn 1:39 - LMT 1880 +1:39 - TMT 1918 F +1 c CE%sT 1919 Jul +1:39 - TMT 1921 May +2 - EET 1940 Au 6 +3 - MSK 1941 S 15 +1 c CE%sT 1944 S 22 +3 R MSK/MSD 1989 Mar 26 2s +2 1 EEST 1989 S 24 2s +2 c EE%sT 1998 S 22 +2 E EE%sT 1999 O 31 4 +2 - EET 2002 F 21 +2 E EE%sT +R FI 1942 o - Ap 2 24 1 S +R FI 1942 o - O 4 1 0 - +R FI 1981 1982 - Mar lastSu 2 1 S +R FI 1981 1982 - S lastSu 3 0 - +Z Europe/Helsinki 1:39:49 - LMT 1878 May 31 +1:39:49 - HMT 1921 May +2 FI EE%sT 1983 +2 E EE%sT +L Europe/Helsinki Europe/Mariehamn +R F 1916 o - Jun 14 23s 1 S +R F 1916 1919 - O Su>=1 23s 0 - +R F 1917 o - Mar 24 23s 1 S +R F 1918 o - Mar 9 23s 1 S +R F 1919 o - Mar 1 23s 1 S +R F 1920 o - F 14 23s 1 S +R F 1920 o - O 23 23s 0 - +R F 1921 o - Mar 14 23s 1 S +R F 1921 o - O 25 23s 0 - +R F 1922 o - Mar 25 23s 1 S +R F 1922 1938 - O Sa>=1 23s 0 - +R F 1923 o - May 26 23s 1 S +R F 1924 o - Mar 29 23s 1 S +R F 1925 o - Ap 4 23s 1 S +R F 1926 o - Ap 17 23s 1 S +R F 1927 o - Ap 9 23s 1 S +R F 1928 o - Ap 14 23s 1 S +R F 1929 o - Ap 20 23s 1 S +R F 1930 o - Ap 12 23s 1 S +R F 1931 o - Ap 18 23s 1 S +R F 1932 o - Ap 2 23s 1 S +R F 1933 o - Mar 25 23s 1 S +R F 1934 o - Ap 7 23s 1 S +R F 1935 o - Mar 30 23s 1 S +R F 1936 o - Ap 18 23s 1 S +R F 1937 o - Ap 3 23s 1 S +R F 1938 o - Mar 26 23s 1 S +R F 1939 o - Ap 15 23s 1 S +R F 1939 o - N 18 23s 0 - +R F 1940 o - F 25 2 1 S +R F 1941 o - May 5 0 2 M +R F 1941 o - O 6 0 1 S +R F 1942 o - Mar 9 0 2 M +R F 1942 o - N 2 3 1 S +R F 1943 o - Mar 29 2 2 M +R F 1943 o - O 4 3 1 S +R F 1944 o - Ap 3 2 2 M +R F 1944 o - O 8 1 1 S +R F 1945 o - Ap 2 2 2 M +R F 1945 o - S 16 3 0 - +R F 1976 o - Mar 28 1 1 S +R F 1976 o - S 26 1 0 - +Z Europe/Paris 0:9:21 - LMT 1891 Mar 16 +0:9:21 - PMT 1911 Mar 11 +0 F WE%sT 1940 Jun 14 23 +1 c CE%sT 1944 Au 25 +0 F WE%sT 1945 S 16 3 +1 F CE%sT 1977 +1 E CE%sT +R DE 1946 o - Ap 14 2s 1 S +R DE 1946 o - O 7 2s 0 - +R DE 1947 1949 - O Su>=1 2s 0 - +R DE 1947 o - Ap 6 3s 1 S +R DE 1947 o - May 11 2s 2 M +R DE 1947 o - Jun 29 3 1 S +R DE 1948 o - Ap 18 2s 1 S +R DE 1949 o - Ap 10 2s 1 S +R So 1945 o - May 24 2 2 M +R So 1945 o - S 24 3 1 S +R So 1945 o - N 18 2s 0 - +Z Europe/Berlin 0:53:28 - LMT 1893 Ap +1 c CE%sT 1945 May 24 2 +1 So CE%sT 1946 +1 DE CE%sT 1980 +1 E CE%sT +L Europe/Zurich Europe/Busingen +Z Europe/Gibraltar -0:21:24 - LMT 1880 Au 2 0s +0 G %s 1957 Ap 14 2 +1 - CET 1982 +1 E CE%sT +R g 1932 o - Jul 7 0 1 S +R g 1932 o - S 1 0 0 - +R g 1941 o - Ap 7 0 1 S +R g 1942 o - N 2 3 0 - +R g 1943 o - Mar 30 0 1 S +R g 1943 o - O 4 0 0 - +R g 1952 o - Jul 1 0 1 S +R g 1952 o - N 2 0 0 - +R g 1975 o - Ap 12 0s 1 S +R g 1975 o - N 26 0s 0 - +R g 1976 o - Ap 11 2s 1 S +R g 1976 o - O 10 2s 0 - +R g 1977 1978 - Ap Su>=1 2s 1 S +R g 1977 o - S 26 2s 0 - +R g 1978 o - S 24 4 0 - +R g 1979 o - Ap 1 9 1 S +R g 1979 o - S 29 2 0 - +R g 1980 o - Ap 1 0 1 S +R g 1980 o - S 28 0 0 - +Z Europe/Athens 1:34:52 - LMT 1895 S 14 +1:34:52 - AMT 1916 Jul 28 0:1 +2 g EE%sT 1941 Ap 30 +1 g CE%sT 1944 Ap 4 +2 g EE%sT 1981 +2 E EE%sT +R h 1918 1919 - Ap 15 2 1 S +R h 1918 1920 - S M>=15 3 0 - +R h 1920 o - Ap 5 2 1 S +R h 1945 o - May 1 23 1 S +R h 1945 o - N 1 1 0 - +R h 1946 o - Mar 31 2s 1 S +R h 1946 o - O 7 2 0 - +R h 1947 1949 - Ap Su>=4 2s 1 S +R h 1947 1949 - O Su>=1 2s 0 - +R h 1954 o - May 23 0 1 S +R h 1954 o - O 3 0 0 - +R h 1955 o - May 22 2 1 S +R h 1955 o - O 2 3 0 - +R h 1956 1957 - Jun Su>=1 2 1 S +R h 1956 1957 - S lastSu 3 0 - +R h 1980 o - Ap 6 0 1 S +R h 1980 o - S 28 1 0 - +R h 1981 1983 - Mar lastSu 0 1 S +R h 1981 1983 - S lastSu 1 0 - +Z Europe/Budapest 1:16:20 - LMT 1890 N +1 c CE%sT 1918 +1 h CE%sT 1941 Ap 7 23 +1 c CE%sT 1945 +1 h CE%sT 1984 +1 E CE%sT +R w 1917 1919 - F 19 23 1 - +R w 1917 o - O 21 1 0 - +R w 1918 1919 - N 16 1 0 - +R w 1921 o - Mar 19 23 1 - +R w 1921 o - Jun 23 1 0 - +R w 1939 o - Ap 29 23 1 - +R w 1939 o - O 29 2 0 - +R w 1940 o - F 25 2 1 - +R w 1940 1941 - N Su>=2 1s 0 - +R w 1941 1942 - Mar Su>=2 1s 1 - +R w 1943 1946 - Mar Su>=1 1s 1 - +R w 1942 1948 - O Su>=22 1s 0 - +R w 1947 1967 - Ap Su>=1 1s 1 - +R w 1949 o - O 30 1s 0 - +R w 1950 1966 - O Su>=22 1s 0 - +R w 1967 o - O 29 1s 0 - +Z Atlantic/Reykjavik -1:28 - LMT 1908 +-1 w -01/+00 1968 Ap 7 1s +0 - GMT +R I 1916 o - Jun 3 24 1 S +R I 1916 1917 - S 30 24 0 - +R I 1917 o - Mar 31 24 1 S +R I 1918 o - Mar 9 24 1 S +R I 1918 o - O 6 24 0 - +R I 1919 o - Mar 1 24 1 S +R I 1919 o - O 4 24 0 - +R I 1920 o - Mar 20 24 1 S +R I 1920 o - S 18 24 0 - +R I 1940 o - Jun 14 24 1 S +R I 1942 o - N 2 2s 0 - +R I 1943 o - Mar 29 2s 1 S +R I 1943 o - O 4 2s 0 - +R I 1944 o - Ap 2 2s 1 S +R I 1944 o - S 17 2s 0 - +R I 1945 o - Ap 2 2 1 S +R I 1945 o - S 15 1 0 - +R I 1946 o - Mar 17 2s 1 S +R I 1946 o - O 6 2s 0 - +R I 1947 o - Mar 16 0s 1 S +R I 1947 o - O 5 0s 0 - +R I 1948 o - F 29 2s 1 S +R I 1948 o - O 3 2s 0 - +R I 1966 1968 - May Su>=22 0s 1 S +R I 1966 o - S 24 24 0 - +R I 1967 1969 - S Su>=22 0s 0 - +R I 1969 o - Jun 1 0s 1 S +R I 1970 o - May 31 0s 1 S +R I 1970 o - S lastSu 0s 0 - +R I 1971 1972 - May Su>=22 0s 1 S +R I 1971 o - S lastSu 0s 0 - +R I 1972 o - O 1 0s 0 - +R I 1973 o - Jun 3 0s 1 S +R I 1973 1974 - S lastSu 0s 0 - +R I 1974 o - May 26 0s 1 S +R I 1975 o - Jun 1 0s 1 S +R I 1975 1977 - S lastSu 0s 0 - +R I 1976 o - May 30 0s 1 S +R I 1977 1979 - May Su>=22 0s 1 S +R I 1978 o - O 1 0s 0 - +R I 1979 o - S 30 0s 0 - +Z Europe/Rome 0:49:56 - LMT 1866 D 12 +0:49:56 - RMT 1893 O 31 23:49:56 +1 I CE%sT 1943 S 10 +1 c CE%sT 1944 Jun 4 +1 I CE%sT 1980 +1 E CE%sT +L Europe/Rome Europe/Vatican +L Europe/Rome Europe/San_Marino +R LV 1989 1996 - Mar lastSu 2s 1 S +R LV 1989 1996 - S lastSu 2s 0 - +Z Europe/Riga 1:36:34 - LMT 1880 +1:36:34 - RMT 1918 Ap 15 2 +1:36:34 1 LST 1918 S 16 3 +1:36:34 - RMT 1919 Ap 1 2 +1:36:34 1 LST 1919 May 22 3 +1:36:34 - RMT 1926 May 11 +2 - EET 1940 Au 5 +3 - MSK 1941 Jul +1 c CE%sT 1944 O 13 +3 R MSK/MSD 1989 Mar lastSu 2s +2 1 EEST 1989 S lastSu 2s +2 LV EE%sT 1997 Ja 21 +2 E EE%sT 2000 F 29 +2 - EET 2001 Ja 2 +2 E EE%sT +L Europe/Zurich Europe/Vaduz +Z Europe/Vilnius 1:41:16 - LMT 1880 +1:24 - WMT 1917 +1:35:36 - KMT 1919 O 10 +1 - CET 1920 Jul 12 +2 - EET 1920 O 9 +1 - CET 1940 Au 3 +3 - MSK 1941 Jun 24 +1 c CE%sT 1944 Au +3 R MSK/MSD 1989 Mar 26 2s +2 R EE%sT 1991 S 29 2s +2 c EE%sT 1998 +2 - EET 1998 Mar 29 1u +1 E CE%sT 1999 O 31 1u +2 - EET 2003 +2 E EE%sT +R LX 1916 o - May 14 23 1 S +R LX 1916 o - O 1 1 0 - +R LX 1917 o - Ap 28 23 1 S +R LX 1917 o - S 17 1 0 - +R LX 1918 o - Ap M>=15 2s 1 S +R LX 1918 o - S M>=15 2s 0 - +R LX 1919 o - Mar 1 23 1 S +R LX 1919 o - O 5 3 0 - +R LX 1920 o - F 14 23 1 S +R LX 1920 o - O 24 2 0 - +R LX 1921 o - Mar 14 23 1 S +R LX 1921 o - O 26 2 0 - +R LX 1922 o - Mar 25 23 1 S +R LX 1922 o - O Su>=2 1 0 - +R LX 1923 o - Ap 21 23 1 S +R LX 1923 o - O Su>=2 2 0 - +R LX 1924 o - Mar 29 23 1 S +R LX 1924 1928 - O Su>=2 1 0 - +R LX 1925 o - Ap 5 23 1 S +R LX 1926 o - Ap 17 23 1 S +R LX 1927 o - Ap 9 23 1 S +R LX 1928 o - Ap 14 23 1 S +R LX 1929 o - Ap 20 23 1 S +Z Europe/Luxembourg 0:24:36 - LMT 1904 Jun +1 LX CE%sT 1918 N 25 +0 LX WE%sT 1929 O 6 2s +0 b WE%sT 1940 May 14 3 +1 c WE%sT 1944 S 18 3 +1 b CE%sT 1977 +1 E CE%sT +R MT 1973 o - Mar 31 0s 1 S +R MT 1973 o - S 29 0s 0 - +R MT 1974 o - Ap 21 0s 1 S +R MT 1974 o - S 16 0s 0 - +R MT 1975 1979 - Ap Su>=15 2 1 S +R MT 1975 1980 - S Su>=15 2 0 - +R MT 1980 o - Mar 31 2 1 S +Z Europe/Malta 0:58:4 - LMT 1893 N 2 0s +1 I CE%sT 1973 Mar 31 +1 MT CE%sT 1981 +1 E CE%sT +R MD 1997 ma - Mar lastSu 2 1 S +R MD 1997 ma - O lastSu 3 0 - +Z Europe/Chisinau 1:55:20 - LMT 1880 +1:55 - CMT 1918 F 15 +1:44:24 - BMT 1931 Jul 24 +2 z EE%sT 1940 Au 15 +2 1 EEST 1941 Jul 17 +1 c CE%sT 1944 Au 24 +3 R MSK/MSD 1990 May 6 2 +2 R EE%sT 1992 +2 e EE%sT 1997 +2 MD EE%sT +Z Europe/Monaco 0:29:32 - LMT 1892 Jun +0:9:21 - PMT 1911 Mar 29 +0 F WE%sT 1945 S 16 3 +1 F CE%sT 1977 +1 E CE%sT +R N 1916 o - May 1 0 1 NST +R N 1916 o - O 1 0 0 AMT +R N 1917 o - Ap 16 2s 1 NST +R N 1917 o - S 17 2s 0 AMT +R N 1918 1921 - Ap M>=1 2s 1 NST +R N 1918 1921 - S lastM 2s 0 AMT +R N 1922 o - Mar lastSu 2s 1 NST +R N 1922 1936 - O Su>=2 2s 0 AMT +R N 1923 o - Jun F>=1 2s 1 NST +R N 1924 o - Mar lastSu 2s 1 NST +R N 1925 o - Jun F>=1 2s 1 NST +R N 1926 1931 - May 15 2s 1 NST +R N 1932 o - May 22 2s 1 NST +R N 1933 1936 - May 15 2s 1 NST +R N 1937 o - May 22 2s 1 NST +R N 1937 o - Jul 1 0 1 S +R N 1937 1939 - O Su>=2 2s 0 - +R N 1938 1939 - May 15 2s 1 S +R N 1945 o - Ap 2 2s 1 S +R N 1945 o - S 16 2s 0 - +Z Europe/Amsterdam 0:19:32 - LMT 1835 +0:19:32 N %s 1937 Jul +0:20 N +0020/+0120 1940 May 16 +1 c CE%sT 1945 Ap 2 2 +1 N CE%sT 1977 +1 E CE%sT +R NO 1916 o - May 22 1 1 S +R NO 1916 o - S 30 0 0 - +R NO 1945 o - Ap 2 2s 1 S +R NO 1945 o - O 1 2s 0 - +R NO 1959 1964 - Mar Su>=15 2s 1 S +R NO 1959 1965 - S Su>=15 2s 0 - +R NO 1965 o - Ap 25 2s 1 S +Z Europe/Oslo 0:43 - LMT 1895 +1 NO CE%sT 1940 Au 10 23 +1 c CE%sT 1945 Ap 2 2 +1 NO CE%sT 1980 +1 E CE%sT +L Europe/Oslo Arctic/Longyearbyen +R O 1918 1919 - S 16 2s 0 - +R O 1919 o - Ap 15 2s 1 S +R O 1944 o - Ap 3 2s 1 S +R O 1944 o - O 4 2 0 - +R O 1945 o - Ap 29 0 1 S +R O 1945 o - N 1 0 0 - +R O 1946 o - Ap 14 0s 1 S +R O 1946 o - O 7 2s 0 - +R O 1947 o - May 4 2s 1 S +R O 1947 1949 - O Su>=1 2s 0 - +R O 1948 o - Ap 18 2s 1 S +R O 1949 o - Ap 10 2s 1 S +R O 1957 o - Jun 2 1s 1 S +R O 1957 1958 - S lastSu 1s 0 - +R O 1958 o - Mar 30 1s 1 S +R O 1959 o - May 31 1s 1 S +R O 1959 1961 - O Su>=1 1s 0 - +R O 1960 o - Ap 3 1s 1 S +R O 1961 1964 - May lastSu 1s 1 S +R O 1962 1964 - S lastSu 1s 0 - +Z Europe/Warsaw 1:24 - LMT 1880 +1:24 - WMT 1915 Au 5 +1 c CE%sT 1918 S 16 3 +2 O EE%sT 1922 Jun +1 O CE%sT 1940 Jun 23 2 +1 c CE%sT 1944 O +1 O CE%sT 1977 +1 W- CE%sT 1988 +1 E CE%sT +R p 1916 o - Jun 17 23 1 S +R p 1916 o - N 1 1 0 - +R p 1917 o - F 28 23s 1 S +R p 1917 1921 - O 14 23s 0 - +R p 1918 o - Mar 1 23s 1 S +R p 1919 o - F 28 23s 1 S +R p 1920 o - F 29 23s 1 S +R p 1921 o - F 28 23s 1 S +R p 1924 o - Ap 16 23s 1 S +R p 1924 o - O 14 23s 0 - +R p 1926 o - Ap 17 23s 1 S +R p 1926 1929 - O Sa>=1 23s 0 - +R p 1927 o - Ap 9 23s 1 S +R p 1928 o - Ap 14 23s 1 S +R p 1929 o - Ap 20 23s 1 S +R p 1931 o - Ap 18 23s 1 S +R p 1931 1932 - O Sa>=1 23s 0 - +R p 1932 o - Ap 2 23s 1 S +R p 1934 o - Ap 7 23s 1 S +R p 1934 1938 - O Sa>=1 23s 0 - +R p 1935 o - Mar 30 23s 1 S +R p 1936 o - Ap 18 23s 1 S +R p 1937 o - Ap 3 23s 1 S +R p 1938 o - Mar 26 23s 1 S +R p 1939 o - Ap 15 23s 1 S +R p 1939 o - N 18 23s 0 - +R p 1940 o - F 24 23s 1 S +R p 1940 1941 - O 5 23s 0 - +R p 1941 o - Ap 5 23s 1 S +R p 1942 1945 - Mar Sa>=8 23s 1 S +R p 1942 o - Ap 25 22s 2 M +R p 1942 o - Au 15 22s 1 S +R p 1942 1945 - O Sa>=24 23s 0 - +R p 1943 o - Ap 17 22s 2 M +R p 1943 1945 - Au Sa>=25 22s 1 S +R p 1944 1945 - Ap Sa>=21 22s 2 M +R p 1946 o - Ap Sa>=1 23s 1 S +R p 1946 o - O Sa>=1 23s 0 - +R p 1947 1965 - Ap Su>=1 2s 1 S +R p 1947 1965 - O Su>=1 2s 0 - +R p 1977 o - Mar 27 0s 1 S +R p 1977 o - S 25 0s 0 - +R p 1978 1979 - Ap Su>=1 0s 1 S +R p 1978 o - O 1 0s 0 - +R p 1979 1982 - S lastSu 1s 0 - +R p 1980 o - Mar lastSu 0s 1 S +R p 1981 1982 - Mar lastSu 1s 1 S +R p 1983 o - Mar lastSu 2s 1 S +Z Europe/Lisbon -0:36:45 - LMT 1884 +-0:36:45 - LMT 1912 Ja 1 0u +0 p WE%sT 1966 Ap 3 2 +1 - CET 1976 S 26 1 +0 p WE%sT 1983 S 25 1s +0 W- WE%sT 1992 S 27 1s +1 E CE%sT 1996 Mar 31 1u +0 E WE%sT +Z Atlantic/Azores -1:42:40 - LMT 1884 +-1:54:32 - HMT 1912 Ja 1 2u +-2 p -02/-01 1942 Ap 25 22s +-2 p +00 1942 Au 15 22s +-2 p -02/-01 1943 Ap 17 22s +-2 p +00 1943 Au 28 22s +-2 p -02/-01 1944 Ap 22 22s +-2 p +00 1944 Au 26 22s +-2 p -02/-01 1945 Ap 21 22s +-2 p +00 1945 Au 25 22s +-2 p -02/-01 1966 Ap 3 2 +-1 p -01/+00 1983 S 25 1s +-1 W- -01/+00 1992 S 27 1s +0 E WE%sT 1993 Mar 28 1u +-1 E -01/+00 +Z Atlantic/Madeira -1:7:36 - LMT 1884 +-1:7:36 - FMT 1912 Ja 1 1u +-1 p -01/+00 1942 Ap 25 22s +-1 p +01 1942 Au 15 22s +-1 p -01/+00 1943 Ap 17 22s +-1 p +01 1943 Au 28 22s +-1 p -01/+00 1944 Ap 22 22s +-1 p +01 1944 Au 26 22s +-1 p -01/+00 1945 Ap 21 22s +-1 p +01 1945 Au 25 22s +-1 p -01/+00 1966 Ap 3 2 +0 p WE%sT 1983 S 25 1s +0 E WE%sT +R z 1932 o - May 21 0s 1 S +R z 1932 1939 - O Su>=1 0s 0 - +R z 1933 1939 - Ap Su>=2 0s 1 S +R z 1979 o - May 27 0 1 S +R z 1979 o - S lastSu 0 0 - +R z 1980 o - Ap 5 23 1 S +R z 1980 o - S lastSu 1 0 - +R z 1991 1993 - Mar lastSu 0s 1 S +R z 1991 1993 - S lastSu 0s 0 - +Z Europe/Bucharest 1:44:24 - LMT 1891 O +1:44:24 - BMT 1931 Jul 24 +2 z EE%sT 1981 Mar 29 2s +2 c EE%sT 1991 +2 z EE%sT 1994 +2 e EE%sT 1997 +2 E EE%sT +Z Europe/Kaliningrad 1:22 - LMT 1893 Ap +1 c CE%sT 1945 Ap 10 +2 O EE%sT 1946 Ap 7 +3 R MSK/MSD 1989 Mar 26 2s +2 R EE%sT 2011 Mar 27 2s +3 - +03 2014 O 26 2s +2 - EET +Z Europe/Moscow 2:30:17 - LMT 1880 +2:30:17 - MMT 1916 Jul 3 +2:31:19 R %s 1919 Jul 1 0u +3 R %s 1921 O +3 R MSK/MSD 1922 O +2 - EET 1930 Jun 21 +3 R MSK/MSD 1991 Mar 31 2s +2 R EE%sT 1992 Ja 19 2s +3 R MSK/MSD 2011 Mar 27 2s +4 - MSK 2014 O 26 2s +3 - MSK +Z Europe/Simferopol 2:16:24 - LMT 1880 +2:16 - SMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 N +1 c CE%sT 1944 Ap 13 +3 R MSK/MSD 1990 +3 - MSK 1990 Jul 1 2 +2 - EET 1992 Mar 20 +2 c EE%sT 1994 May +3 e MSK/MSD 1996 Mar 31 0s +3 1 MSD 1996 O 27 3s +3 R MSK/MSD 1997 +3 - MSK 1997 Mar lastSu 1u +2 E EE%sT 2014 Mar 30 2 +4 - MSK 2014 O 26 2s +3 - MSK +Z Europe/Astrakhan 3:12:12 - LMT 1924 May +3 - +03 1930 Jun 21 +4 R +04/+05 1989 Mar 26 2s +3 R +03/+04 1991 Mar 31 2s +4 - +04 1992 Mar 29 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 2014 O 26 2s +3 - +03 2016 Mar 27 2s +4 - +04 +Z Europe/Volgograd 2:57:40 - LMT 1920 Ja 3 +3 - +03 1930 Jun 21 +4 - +04 1961 N 11 +4 R +04/+05 1988 Mar 27 2s +3 R +03/+04 1991 Mar 31 2s +4 - +04 1992 Mar 29 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 2014 O 26 2s +3 - +03 2018 O 28 2s +4 - +04 2020 D 27 2s +3 - +03 +Z Europe/Saratov 3:4:18 - LMT 1919 Jul 1 0u +3 - +03 1930 Jun 21 +4 R +04/+05 1988 Mar 27 2s +3 R +03/+04 1991 Mar 31 2s +4 - +04 1992 Mar 29 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 2014 O 26 2s +3 - +03 2016 D 4 2s +4 - +04 +Z Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0u +3 - +03 1930 Jun 21 +4 R +04/+05 1989 Mar 26 2s +3 R +03/+04 1991 Mar 31 2s +4 - +04 1992 Mar 29 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 2014 O 26 2s +3 - +03 +Z Europe/Samara 3:20:20 - LMT 1919 Jul 1 0u +3 - +03 1930 Jun 21 +4 - +04 1935 Ja 27 +4 R +04/+05 1989 Mar 26 2s +3 R +03/+04 1991 Mar 31 2s +2 R +02/+03 1991 S 29 2s +3 - +03 1991 O 20 3 +4 R +04/+05 2010 Mar 28 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 +Z Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0u +3 - +03 1930 Jun 21 +4 R +04/+05 1989 Mar 26 2s +3 R +03/+04 1991 Mar 31 2s +2 R +02/+03 1992 Ja 19 2s +3 R +03/+04 2011 Mar 27 2s +4 - +04 2014 O 26 2s +3 - +03 2016 Mar 27 2s +4 - +04 +Z Asia/Yekaterinburg 4:2:33 - LMT 1916 Jul 3 +3:45:5 - PMT 1919 Jul 15 4 +4 - +04 1930 Jun 21 +5 R +05/+06 1991 Mar 31 2s +4 R +04/+05 1992 Ja 19 2s +5 R +05/+06 2011 Mar 27 2s +6 - +06 2014 O 26 2s +5 - +05 +Z Asia/Omsk 4:53:30 - LMT 1919 N 14 +5 - +05 1930 Jun 21 +6 R +06/+07 1991 Mar 31 2s +5 R +05/+06 1992 Ja 19 2s +6 R +06/+07 2011 Mar 27 2s +7 - +07 2014 O 26 2s +6 - +06 +Z Asia/Barnaul 5:35 - LMT 1919 D 10 +6 - +06 1930 Jun 21 +7 R +07/+08 1991 Mar 31 2s +6 R +06/+07 1992 Ja 19 2s +7 R +07/+08 1995 May 28 +6 R +06/+07 2011 Mar 27 2s +7 - +07 2014 O 26 2s +6 - +06 2016 Mar 27 2s +7 - +07 +Z Asia/Novosibirsk 5:31:40 - LMT 1919 D 14 6 +6 - +06 1930 Jun 21 +7 R +07/+08 1991 Mar 31 2s +6 R +06/+07 1992 Ja 19 2s +7 R +07/+08 1993 May 23 +6 R +06/+07 2011 Mar 27 2s +7 - +07 2014 O 26 2s +6 - +06 2016 Jul 24 2s +7 - +07 +Z Asia/Tomsk 5:39:51 - LMT 1919 D 22 +6 - +06 1930 Jun 21 +7 R +07/+08 1991 Mar 31 2s +6 R +06/+07 1992 Ja 19 2s +7 R +07/+08 2002 May 1 3 +6 R +06/+07 2011 Mar 27 2s +7 - +07 2014 O 26 2s +6 - +06 2016 May 29 2s +7 - +07 +Z Asia/Novokuznetsk 5:48:48 - LMT 1924 May +6 - +06 1930 Jun 21 +7 R +07/+08 1991 Mar 31 2s +6 R +06/+07 1992 Ja 19 2s +7 R +07/+08 2010 Mar 28 2s +6 R +06/+07 2011 Mar 27 2s +7 - +07 +Z Asia/Krasnoyarsk 6:11:26 - LMT 1920 Ja 6 +6 - +06 1930 Jun 21 +7 R +07/+08 1991 Mar 31 2s +6 R +06/+07 1992 Ja 19 2s +7 R +07/+08 2011 Mar 27 2s +8 - +08 2014 O 26 2s +7 - +07 +Z Asia/Irkutsk 6:57:5 - LMT 1880 +6:57:5 - IMT 1920 Ja 25 +7 - +07 1930 Jun 21 +8 R +08/+09 1991 Mar 31 2s +7 R +07/+08 1992 Ja 19 2s +8 R +08/+09 2011 Mar 27 2s +9 - +09 2014 O 26 2s +8 - +08 +Z Asia/Chita 7:33:52 - LMT 1919 D 15 +8 - +08 1930 Jun 21 +9 R +09/+10 1991 Mar 31 2s +8 R +08/+09 1992 Ja 19 2s +9 R +09/+10 2011 Mar 27 2s +10 - +10 2014 O 26 2s +8 - +08 2016 Mar 27 2 +9 - +09 +Z Asia/Yakutsk 8:38:58 - LMT 1919 D 15 +8 - +08 1930 Jun 21 +9 R +09/+10 1991 Mar 31 2s +8 R +08/+09 1992 Ja 19 2s +9 R +09/+10 2011 Mar 27 2s +10 - +10 2014 O 26 2s +9 - +09 +Z Asia/Vladivostok 8:47:31 - LMT 1922 N 15 +9 - +09 1930 Jun 21 +10 R +10/+11 1991 Mar 31 2s +9 R +09/+10 1992 Ja 19 2s +10 R +10/+11 2011 Mar 27 2s +11 - +11 2014 O 26 2s +10 - +10 +Z Asia/Khandyga 9:2:13 - LMT 1919 D 15 +8 - +08 1930 Jun 21 +9 R +09/+10 1991 Mar 31 2s +8 R +08/+09 1992 Ja 19 2s +9 R +09/+10 2004 +10 R +10/+11 2011 Mar 27 2s +11 - +11 2011 S 13 0s +10 - +10 2014 O 26 2s +9 - +09 +Z Asia/Sakhalin 9:30:48 - LMT 1905 Au 23 +9 - +09 1945 Au 25 +11 R +11/+12 1991 Mar 31 2s +10 R +10/+11 1992 Ja 19 2s +11 R +11/+12 1997 Mar lastSu 2s +10 R +10/+11 2011 Mar 27 2s +11 - +11 2014 O 26 2s +10 - +10 2016 Mar 27 2s +11 - +11 +Z Asia/Magadan 10:3:12 - LMT 1924 May 2 +10 - +10 1930 Jun 21 +11 R +11/+12 1991 Mar 31 2s +10 R +10/+11 1992 Ja 19 2s +11 R +11/+12 2011 Mar 27 2s +12 - +12 2014 O 26 2s +10 - +10 2016 Ap 24 2s +11 - +11 +Z Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 +10 - +10 1930 Jun 21 +11 R +11/+12 1991 Mar 31 2s +10 R +10/+11 1992 Ja 19 2s +11 R +11/+12 2011 Mar 27 2s +12 - +12 2014 O 26 2s +11 - +11 +Z Asia/Ust-Nera 9:32:54 - LMT 1919 D 15 +8 - +08 1930 Jun 21 +9 R +09/+10 1981 Ap +11 R +11/+12 1991 Mar 31 2s +10 R +10/+11 1992 Ja 19 2s +11 R +11/+12 2011 Mar 27 2s +12 - +12 2011 S 13 0s +11 - +11 2014 O 26 2s +10 - +10 +Z Asia/Kamchatka 10:34:36 - LMT 1922 N 10 +11 - +11 1930 Jun 21 +12 R +12/+13 1991 Mar 31 2s +11 R +11/+12 1992 Ja 19 2s +12 R +12/+13 2010 Mar 28 2s +11 R +11/+12 2011 Mar 27 2s +12 - +12 +Z Asia/Anadyr 11:49:56 - LMT 1924 May 2 +12 - +12 1930 Jun 21 +13 R +13/+14 1982 Ap 1 0s +12 R +12/+13 1991 Mar 31 2s +11 R +11/+12 1992 Ja 19 2s +12 R +12/+13 2010 Mar 28 2s +11 R +11/+12 2011 Mar 27 2s +12 - +12 +Z Europe/Belgrade 1:22 - LMT 1884 +1 - CET 1941 Ap 18 23 +1 c CE%sT 1945 +1 - CET 1945 May 8 2s +1 1 CEST 1945 S 16 2s +1 - CET 1982 N 27 +1 E CE%sT +L Europe/Belgrade Europe/Ljubljana +L Europe/Belgrade Europe/Podgorica +L Europe/Belgrade Europe/Sarajevo +L Europe/Belgrade Europe/Skopje +L Europe/Belgrade Europe/Zagreb +L Europe/Prague Europe/Bratislava +R s 1918 o - Ap 15 23 1 S +R s 1918 1919 - O 6 24s 0 - +R s 1919 o - Ap 6 23 1 S +R s 1924 o - Ap 16 23 1 S +R s 1924 o - O 4 24s 0 - +R s 1926 o - Ap 17 23 1 S +R s 1926 1929 - O Sa>=1 24s 0 - +R s 1927 o - Ap 9 23 1 S +R s 1928 o - Ap 15 0 1 S +R s 1929 o - Ap 20 23 1 S +R s 1937 o - Jun 16 23 1 S +R s 1937 o - O 2 24s 0 - +R s 1938 o - Ap 2 23 1 S +R s 1938 o - Ap 30 23 2 M +R s 1938 o - O 2 24 1 S +R s 1939 o - O 7 24s 0 - +R s 1942 o - May 2 23 1 S +R s 1942 o - S 1 1 0 - +R s 1943 1946 - Ap Sa>=13 23 1 S +R s 1943 1944 - O Su>=1 1 0 - +R s 1945 1946 - S lastSu 1 0 - +R s 1949 o - Ap 30 23 1 S +R s 1949 o - O 2 1 0 - +R s 1974 1975 - Ap Sa>=12 23 1 S +R s 1974 1975 - O Su>=1 1 0 - +R s 1976 o - Mar 27 23 1 S +R s 1976 1977 - S lastSu 1 0 - +R s 1977 o - Ap 2 23 1 S +R s 1978 o - Ap 2 2s 1 S +R s 1978 o - O 1 2s 0 - +R Sp 1967 o - Jun 3 12 1 S +R Sp 1967 o - O 1 0 0 - +R Sp 1974 o - Jun 24 0 1 S +R Sp 1974 o - S 1 0 0 - +R Sp 1976 1977 - May 1 0 1 S +R Sp 1976 o - Au 1 0 0 - +R Sp 1977 o - S 28 0 0 - +R Sp 1978 o - Jun 1 0 1 S +R Sp 1978 o - Au 4 0 0 - +Z Europe/Madrid -0:14:44 - LMT 1900 D 31 23:45:16 +0 s WE%sT 1940 Mar 16 23 +1 s CE%sT 1979 +1 E CE%sT +Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44 +0 - WET 1918 May 6 23 +0 1 WEST 1918 O 7 23 +0 - WET 1924 +0 s WE%sT 1929 +0 - WET 1967 +0 Sp WE%sT 1984 Mar 16 +1 - CET 1986 +1 E CE%sT +Z Atlantic/Canary -1:1:36 - LMT 1922 Mar +-1 - -01 1946 S 30 1 +0 - WET 1980 Ap 6 0s +0 1 WEST 1980 S 28 1u +0 E WE%sT +Z Europe/Stockholm 1:12:12 - LMT 1879 +1:0:14 - SET 1900 +1 - CET 1916 May 14 23 +1 1 CEST 1916 O 1 1 +1 - CET 1980 +1 E CE%sT +R CH 1941 1942 - May M>=1 1 1 S +R CH 1941 1942 - O M>=1 2 0 - +Z Europe/Zurich 0:34:8 - LMT 1853 Jul 16 +0:29:46 - BMT 1894 Jun +1 CH CE%sT 1981 +1 E CE%sT +R T 1916 o - May 1 0 1 S +R T 1916 o - O 1 0 0 - +R T 1920 o - Mar 28 0 1 S +R T 1920 o - O 25 0 0 - +R T 1921 o - Ap 3 0 1 S +R T 1921 o - O 3 0 0 - +R T 1922 o - Mar 26 0 1 S +R T 1922 o - O 8 0 0 - +R T 1924 o - May 13 0 1 S +R T 1924 1925 - O 1 0 0 - +R T 1925 o - May 1 0 1 S +R T 1940 o - Jul 1 0 1 S +R T 1940 o - O 6 0 0 - +R T 1940 o - D 1 0 1 S +R T 1941 o - S 21 0 0 - +R T 1942 o - Ap 1 0 1 S +R T 1945 o - O 8 0 0 - +R T 1946 o - Jun 1 0 1 S +R T 1946 o - O 1 0 0 - +R T 1947 1948 - Ap Su>=16 0 1 S +R T 1947 1951 - O Su>=2 0 0 - +R T 1949 o - Ap 10 0 1 S +R T 1950 o - Ap 16 0 1 S +R T 1951 o - Ap 22 0 1 S +R T 1962 o - Jul 15 0 1 S +R T 1963 o - O 30 0 0 - +R T 1964 o - May 15 0 1 S +R T 1964 o - O 1 0 0 - +R T 1973 o - Jun 3 1 1 S +R T 1973 1976 - O Su>=31 2 0 - +R T 1974 o - Mar 31 2 1 S +R T 1975 o - Mar 22 2 1 S +R T 1976 o - Mar 21 2 1 S +R T 1977 1978 - Ap Su>=1 2 1 S +R T 1977 1978 - O Su>=15 2 0 - +R T 1978 o - Jun 29 0 0 - +R T 1983 o - Jul 31 2 1 S +R T 1983 o - O 2 2 0 - +R T 1985 o - Ap 20 1s 1 S +R T 1985 o - S 28 1s 0 - +R T 1986 1993 - Mar lastSu 1s 1 S +R T 1986 1995 - S lastSu 1s 0 - +R T 1994 o - Mar 20 1s 1 S +R T 1995 2006 - Mar lastSu 1s 1 S +R T 1996 2006 - O lastSu 1s 0 - +Z Europe/Istanbul 1:55:52 - LMT 1880 +1:56:56 - IMT 1910 O +2 T EE%sT 1978 Jun 29 +3 T +03/+04 1984 N 1 2 +2 T EE%sT 2007 +2 E EE%sT 2011 Mar 27 1u +2 - EET 2011 Mar 28 1u +2 E EE%sT 2014 Mar 30 1u +2 - EET 2014 Mar 31 1u +2 E EE%sT 2015 O 25 1u +2 1 EEST 2015 N 8 1u +2 E EE%sT 2016 S 7 +3 - +03 +L Europe/Istanbul Asia/Istanbul +Z Europe/Kiev 2:2:4 - LMT 1880 +2:2:4 - KMT 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 S 20 +1 c CE%sT 1943 N 6 +3 R MSK/MSD 1990 Jul 1 2 +2 1 EEST 1991 S 29 3 +2 c EE%sT 1996 May 13 +2 E EE%sT +Z Europe/Uzhgorod 1:29:12 - LMT 1890 O +1 - CET 1940 +1 c CE%sT 1944 O +1 1 CEST 1944 O 26 +1 - CET 1945 Jun 29 +3 R MSK/MSD 1990 +3 - MSK 1990 Jul 1 2 +1 - CET 1991 Mar 31 3 +2 - EET 1992 Mar 20 +2 c EE%sT 1996 May 13 +2 E EE%sT +Z Europe/Zaporozhye 2:20:40 - LMT 1880 +2:20 - +0220 1924 May 2 +2 - EET 1930 Jun 21 +3 - MSK 1941 Au 25 +1 c CE%sT 1943 O 25 +3 R MSK/MSD 1991 Mar 31 2 +2 e EE%sT 1992 Mar 20 +2 c EE%sT 1996 May 13 +2 E EE%sT +R u 1918 1919 - Mar lastSu 2 1 D +R u 1918 1919 - O lastSu 2 0 S +R u 1942 o - F 9 2 1 W +R u 1945 o - Au 14 23u 1 P +R u 1945 o - S 30 2 0 S +R u 1967 2006 - O lastSu 2 0 S +R u 1967 1973 - Ap lastSu 2 1 D +R u 1974 o - Ja 6 2 1 D +R u 1975 o - F lastSu 2 1 D +R u 1976 1986 - Ap lastSu 2 1 D +R u 1987 2006 - Ap Su>=1 2 1 D +R u 2007 ma - Mar Su>=8 2 1 D +R u 2007 ma - N Su>=1 2 0 S +Z EST -5 - EST +Z MST -7 - MST +Z HST -10 - HST +Z EST5EDT -5 u E%sT +Z CST6CDT -6 u C%sT +Z MST7MDT -7 u M%sT +Z PST8PDT -8 u P%sT +R NY 1920 o - Mar lastSu 2 1 D +R NY 1920 o - O lastSu 2 0 S +R NY 1921 1966 - Ap lastSu 2 1 D +R NY 1921 1954 - S lastSu 2 0 S +R NY 1955 1966 - O lastSu 2 0 S +Z America/New_York -4:56:2 - LMT 1883 N 18 12:3:58 +-5 u E%sT 1920 +-5 NY E%sT 1942 +-5 u E%sT 1946 +-5 NY E%sT 1967 +-5 u E%sT +R Ch 1920 o - Jun 13 2 1 D +R Ch 1920 1921 - O lastSu 2 0 S +R Ch 1921 o - Mar lastSu 2 1 D +R Ch 1922 1966 - Ap lastSu 2 1 D +R Ch 1922 1954 - S lastSu 2 0 S +R Ch 1955 1966 - O lastSu 2 0 S +Z America/Chicago -5:50:36 - LMT 1883 N 18 12:9:24 +-6 u C%sT 1920 +-6 Ch C%sT 1936 Mar 1 2 +-5 - EST 1936 N 15 2 +-6 Ch C%sT 1942 +-6 u C%sT 1946 +-6 Ch C%sT 1967 +-6 u C%sT +Z America/North_Dakota/Center -6:45:12 - LMT 1883 N 18 12:14:48 +-7 u M%sT 1992 O 25 2 +-6 u C%sT +Z America/North_Dakota/New_Salem -6:45:39 - LMT 1883 N 18 12:14:21 +-7 u M%sT 2003 O 26 2 +-6 u C%sT +Z America/North_Dakota/Beulah -6:47:7 - LMT 1883 N 18 12:12:53 +-7 u M%sT 2010 N 7 2 +-6 u C%sT +R De 1920 1921 - Mar lastSu 2 1 D +R De 1920 o - O lastSu 2 0 S +R De 1921 o - May 22 2 0 S +R De 1965 1966 - Ap lastSu 2 1 D +R De 1965 1966 - O lastSu 2 0 S +Z America/Denver -6:59:56 - LMT 1883 N 18 12:0:4 +-7 u M%sT 1920 +-7 De M%sT 1942 +-7 u M%sT 1946 +-7 De M%sT 1967 +-7 u M%sT +R CA 1948 o - Mar 14 2:1 1 D +R CA 1949 o - Ja 1 2 0 S +R CA 1950 1966 - Ap lastSu 1 1 D +R CA 1950 1961 - S lastSu 2 0 S +R CA 1962 1966 - O lastSu 2 0 S +Z America/Los_Angeles -7:52:58 - LMT 1883 N 18 12:7:2 +-8 u P%sT 1946 +-8 CA P%sT 1967 +-8 u P%sT +Z America/Juneau 15:2:19 - LMT 1867 O 19 15:33:32 +-8:57:41 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1980 Ap 27 2 +-9 u Y%sT 1980 O 26 2 +-8 u P%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Sitka 14:58:47 - LMT 1867 O 19 15:30 +-9:1:13 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Metlakatla 15:13:42 - LMT 1867 O 19 15:44:55 +-8:46:18 - LMT 1900 Au 20 12 +-8 - PST 1942 +-8 u P%sT 1946 +-8 - PST 1969 +-8 u P%sT 1983 O 30 2 +-8 - PST 2015 N 1 2 +-9 u AK%sT 2018 N 4 2 +-8 - PST 2019 Ja 20 2 +-9 u AK%sT +Z America/Yakutat 14:41:5 - LMT 1867 O 19 15:12:18 +-9:18:55 - LMT 1900 Au 20 12 +-9 - YST 1942 +-9 u Y%sT 1946 +-9 - YST 1969 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Anchorage 14:0:24 - LMT 1867 O 19 14:31:37 +-9:59:36 - LMT 1900 Au 20 12 +-10 - AST 1942 +-10 u A%sT 1967 Ap +-10 - AHST 1969 +-10 u AH%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Nome 12:58:22 - LMT 1867 O 19 13:29:35 +-11:1:38 - LMT 1900 Au 20 12 +-11 - NST 1942 +-11 u N%sT 1946 +-11 - NST 1967 Ap +-11 - BST 1969 +-11 u B%sT 1983 O 30 2 +-9 u Y%sT 1983 N 30 +-9 u AK%sT +Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35 +-11:46:38 - LMT 1900 Au 20 12 +-11 - NST 1942 +-11 u N%sT 1946 +-11 - NST 1967 Ap +-11 - BST 1969 +-11 u B%sT 1983 O 30 2 +-10 u AH%sT 1983 N 30 +-10 u H%sT +Z Pacific/Honolulu -10:31:26 - LMT 1896 Ja 13 12 +-10:30 - HST 1933 Ap 30 2 +-10:30 1 HDT 1933 May 21 12 +-10:30 u H%sT 1947 Jun 8 2 +-10 - HST +Z America/Phoenix -7:28:18 - LMT 1883 N 18 11:31:42 +-7 u M%sT 1944 Ja 1 0:1 +-7 - MST 1944 Ap 1 0:1 +-7 u M%sT 1944 O 1 0:1 +-7 - MST 1967 +-7 u M%sT 1968 Mar 21 +-7 - MST +L America/Phoenix America/Creston +Z America/Boise -7:44:49 - LMT 1883 N 18 12:15:11 +-8 u P%sT 1923 May 13 2 +-7 u M%sT 1974 +-7 - MST 1974 F 3 2 +-7 u M%sT +R In 1941 o - Jun 22 2 1 D +R In 1941 1954 - S lastSu 2 0 S +R In 1946 1954 - Ap lastSu 2 1 D +Z America/Indiana/Indianapolis -5:44:38 - LMT 1883 N 18 12:15:22 +-6 u C%sT 1920 +-6 In C%sT 1942 +-6 u C%sT 1946 +-6 In C%sT 1955 Ap 24 2 +-5 - EST 1957 S 29 2 +-6 - CST 1958 Ap 27 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 +-5 u E%sT +R Ma 1951 o - Ap lastSu 2 1 D +R Ma 1951 o - S lastSu 2 0 S +R Ma 1954 1960 - Ap lastSu 2 1 D +R Ma 1954 1960 - S lastSu 2 0 S +Z America/Indiana/Marengo -5:45:23 - LMT 1883 N 18 12:14:37 +-6 u C%sT 1951 +-6 Ma C%sT 1961 Ap 30 2 +-5 - EST 1969 +-5 u E%sT 1974 Ja 6 2 +-6 1 CDT 1974 O 27 2 +-5 u E%sT 1976 +-5 - EST 2006 +-5 u E%sT +R V 1946 o - Ap lastSu 2 1 D +R V 1946 o - S lastSu 2 0 S +R V 1953 1954 - Ap lastSu 2 1 D +R V 1953 1959 - S lastSu 2 0 S +R V 1955 o - May 1 0 1 D +R V 1956 1963 - Ap lastSu 2 1 D +R V 1960 o - O lastSu 2 0 S +R V 1961 o - S lastSu 2 0 S +R V 1962 1963 - O lastSu 2 0 S +Z America/Indiana/Vincennes -5:50:7 - LMT 1883 N 18 12:9:53 +-6 u C%sT 1946 +-6 V C%sT 1964 Ap 26 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 N 4 2 +-5 u E%sT +R Pe 1955 o - May 1 0 1 D +R Pe 1955 1960 - S lastSu 2 0 S +R Pe 1956 1963 - Ap lastSu 2 1 D +R Pe 1961 1963 - O lastSu 2 0 S +Z America/Indiana/Tell_City -5:47:3 - LMT 1883 N 18 12:12:57 +-6 u C%sT 1946 +-6 Pe C%sT 1964 Ap 26 2 +-5 - EST 1967 O 29 2 +-6 u C%sT 1969 Ap 27 2 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT +R Pi 1955 o - May 1 0 1 D +R Pi 1955 1960 - S lastSu 2 0 S +R Pi 1956 1964 - Ap lastSu 2 1 D +R Pi 1961 1964 - O lastSu 2 0 S +Z America/Indiana/Petersburg -5:49:7 - LMT 1883 N 18 12:10:53 +-6 u C%sT 1955 +-6 Pi C%sT 1965 Ap 25 2 +-5 - EST 1966 O 30 2 +-6 u C%sT 1977 O 30 2 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 N 4 2 +-5 u E%sT +R St 1947 1961 - Ap lastSu 2 1 D +R St 1947 1954 - S lastSu 2 0 S +R St 1955 1956 - O lastSu 2 0 S +R St 1957 1958 - S lastSu 2 0 S +R St 1959 1961 - O lastSu 2 0 S +Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 12:13:30 +-6 u C%sT 1947 +-6 St C%sT 1962 Ap 29 2 +-5 - EST 1963 O 27 2 +-6 u C%sT 1991 O 27 2 +-5 - EST 2006 Ap 2 2 +-6 u C%sT +R Pu 1946 1960 - Ap lastSu 2 1 D +R Pu 1946 1954 - S lastSu 2 0 S +R Pu 1955 1956 - O lastSu 2 0 S +R Pu 1957 1960 - S lastSu 2 0 S +Z America/Indiana/Winamac -5:46:25 - LMT 1883 N 18 12:13:35 +-6 u C%sT 1946 +-6 Pu C%sT 1961 Ap 30 2 +-5 - EST 1969 +-5 u E%sT 1971 +-5 - EST 2006 Ap 2 2 +-6 u C%sT 2007 Mar 11 2 +-5 u E%sT +Z America/Indiana/Vevay -5:40:16 - LMT 1883 N 18 12:19:44 +-6 u C%sT 1954 Ap 25 2 +-5 - EST 1969 +-5 u E%sT 1973 +-5 - EST 2006 +-5 u E%sT +R v 1921 o - May 1 2 1 D +R v 1921 o - S 1 2 0 S +R v 1941 o - Ap lastSu 2 1 D +R v 1941 o - S lastSu 2 0 S +R v 1946 o - Ap lastSu 0:1 1 D +R v 1946 o - Jun 2 2 0 S +R v 1950 1961 - Ap lastSu 2 1 D +R v 1950 1955 - S lastSu 2 0 S +R v 1956 1961 - O lastSu 2 0 S +Z America/Kentucky/Louisville -5:43:2 - LMT 1883 N 18 12:16:58 +-6 u C%sT 1921 +-6 v C%sT 1942 +-6 u C%sT 1946 +-6 v C%sT 1961 Jul 23 2 +-5 - EST 1968 +-5 u E%sT 1974 Ja 6 2 +-6 1 CDT 1974 O 27 2 +-5 u E%sT +Z America/Kentucky/Monticello -5:39:24 - LMT 1883 N 18 12:20:36 +-6 u C%sT 1946 +-6 - CST 1968 +-6 u C%sT 2000 O 29 2 +-5 u E%sT +R Dt 1948 o - Ap lastSu 2 1 D +R Dt 1948 o - S lastSu 2 0 S +Z America/Detroit -5:32:11 - LMT 1905 +-6 - CST 1915 May 15 2 +-5 - EST 1942 +-5 u E%sT 1946 +-5 Dt E%sT 1967 Jun 14 0:1 +-5 u E%sT 1969 +-5 - EST 1973 +-5 u E%sT 1975 +-5 - EST 1975 Ap 27 2 +-5 u E%sT +R Me 1946 o - Ap lastSu 2 1 D +R Me 1946 o - S lastSu 2 0 S +R Me 1966 o - Ap lastSu 2 1 D +R Me 1966 o - O lastSu 2 0 S +Z America/Menominee -5:50:27 - LMT 1885 S 18 12 +-6 u C%sT 1946 +-6 Me C%sT 1969 Ap 27 2 +-5 - EST 1973 Ap 29 2 +-6 u C%sT +R C 1918 o - Ap 14 2 1 D +R C 1918 o - O 27 2 0 S +R C 1942 o - F 9 2 1 W +R C 1945 o - Au 14 23u 1 P +R C 1945 o - S 30 2 0 S +R C 1974 1986 - Ap lastSu 2 1 D +R C 1974 2006 - O lastSu 2 0 S +R C 1987 2006 - Ap Su>=1 2 1 D +R C 2007 ma - Mar Su>=8 2 1 D +R C 2007 ma - N Su>=1 2 0 S +R j 1917 o - Ap 8 2 1 D +R j 1917 o - S 17 2 0 S +R j 1919 o - May 5 23 1 D +R j 1919 o - Au 12 23 0 S +R j 1920 1935 - May Su>=1 23 1 D +R j 1920 1935 - O lastSu 23 0 S +R j 1936 1941 - May M>=9 0 1 D +R j 1936 1941 - O M>=2 0 0 S +R j 1946 1950 - May Su>=8 2 1 D +R j 1946 1950 - O Su>=2 2 0 S +R j 1951 1986 - Ap lastSu 2 1 D +R j 1951 1959 - S lastSu 2 0 S +R j 1960 1986 - O lastSu 2 0 S +R j 1987 o - Ap Su>=1 0:1 1 D +R j 1987 2006 - O lastSu 0:1 0 S +R j 1988 o - Ap Su>=1 0:1 2 DD +R j 1989 2006 - Ap Su>=1 0:1 1 D +R j 2007 2011 - Mar Su>=8 0:1 1 D +R j 2007 2010 - N Su>=1 0:1 0 S +Z America/St_Johns -3:30:52 - LMT 1884 +-3:30:52 j N%sT 1918 +-3:30:52 C N%sT 1919 +-3:30:52 j N%sT 1935 Mar 30 +-3:30 j N%sT 1942 May 11 +-3:30 C N%sT 1946 +-3:30 j N%sT 2011 N +-3:30 C N%sT +Z America/Goose_Bay -4:1:40 - LMT 1884 +-3:30:52 - NST 1918 +-3:30:52 C N%sT 1919 +-3:30:52 - NST 1935 Mar 30 +-3:30 - NST 1936 +-3:30 j N%sT 1942 May 11 +-3:30 C N%sT 1946 +-3:30 j N%sT 1966 Mar 15 2 +-4 j A%sT 2011 N +-4 C A%sT +R H 1916 o - Ap 1 0 1 D +R H 1916 o - O 1 0 0 S +R H 1920 o - May 9 0 1 D +R H 1920 o - Au 29 0 0 S +R H 1921 o - May 6 0 1 D +R H 1921 1922 - S 5 0 0 S +R H 1922 o - Ap 30 0 1 D +R H 1923 1925 - May Su>=1 0 1 D +R H 1923 o - S 4 0 0 S +R H 1924 o - S 15 0 0 S +R H 1925 o - S 28 0 0 S +R H 1926 o - May 16 0 1 D +R H 1926 o - S 13 0 0 S +R H 1927 o - May 1 0 1 D +R H 1927 o - S 26 0 0 S +R H 1928 1931 - May Su>=8 0 1 D +R H 1928 o - S 9 0 0 S +R H 1929 o - S 3 0 0 S +R H 1930 o - S 15 0 0 S +R H 1931 1932 - S M>=24 0 0 S +R H 1932 o - May 1 0 1 D +R H 1933 o - Ap 30 0 1 D +R H 1933 o - O 2 0 0 S +R H 1934 o - May 20 0 1 D +R H 1934 o - S 16 0 0 S +R H 1935 o - Jun 2 0 1 D +R H 1935 o - S 30 0 0 S +R H 1936 o - Jun 1 0 1 D +R H 1936 o - S 14 0 0 S +R H 1937 1938 - May Su>=1 0 1 D +R H 1937 1941 - S M>=24 0 0 S +R H 1939 o - May 28 0 1 D +R H 1940 1941 - May Su>=1 0 1 D +R H 1946 1949 - Ap lastSu 2 1 D +R H 1946 1949 - S lastSu 2 0 S +R H 1951 1954 - Ap lastSu 2 1 D +R H 1951 1954 - S lastSu 2 0 S +R H 1956 1959 - Ap lastSu 2 1 D +R H 1956 1959 - S lastSu 2 0 S +R H 1962 1973 - Ap lastSu 2 1 D +R H 1962 1973 - O lastSu 2 0 S +Z America/Halifax -4:14:24 - LMT 1902 Jun 15 +-4 H A%sT 1918 +-4 C A%sT 1919 +-4 H A%sT 1942 F 9 2s +-4 C A%sT 1946 +-4 H A%sT 1974 +-4 C A%sT +Z America/Glace_Bay -3:59:48 - LMT 1902 Jun 15 +-4 C A%sT 1953 +-4 H A%sT 1954 +-4 - AST 1972 +-4 H A%sT 1974 +-4 C A%sT +R o 1933 1935 - Jun Su>=8 1 1 D +R o 1933 1935 - S Su>=8 1 0 S +R o 1936 1938 - Jun Su>=1 1 1 D +R o 1936 1938 - S Su>=1 1 0 S +R o 1939 o - May 27 1 1 D +R o 1939 1941 - S Sa>=21 1 0 S +R o 1940 o - May 19 1 1 D +R o 1941 o - May 4 1 1 D +R o 1946 1972 - Ap lastSu 2 1 D +R o 1946 1956 - S lastSu 2 0 S +R o 1957 1972 - O lastSu 2 0 S +R o 1993 2006 - Ap Su>=1 0:1 1 D +R o 1993 2006 - O lastSu 0:1 0 S +Z America/Moncton -4:19:8 - LMT 1883 D 9 +-5 - EST 1902 Jun 15 +-4 C A%sT 1933 +-4 o A%sT 1942 +-4 C A%sT 1946 +-4 o A%sT 1973 +-4 C A%sT 1993 +-4 o A%sT 2007 +-4 C A%sT +R t 1919 o - Mar 30 23:30 1 D +R t 1919 o - O 26 0 0 S +R t 1920 o - May 2 2 1 D +R t 1920 o - S 26 0 0 S +R t 1921 o - May 15 2 1 D +R t 1921 o - S 15 2 0 S +R t 1922 1923 - May Su>=8 2 1 D +R t 1922 1926 - S Su>=15 2 0 S +R t 1924 1927 - May Su>=1 2 1 D +R t 1927 1937 - S Su>=25 2 0 S +R t 1928 1937 - Ap Su>=25 2 1 D +R t 1938 1940 - Ap lastSu 2 1 D +R t 1938 1939 - S lastSu 2 0 S +R t 1945 1946 - S lastSu 2 0 S +R t 1946 o - Ap lastSu 2 1 D +R t 1947 1949 - Ap lastSu 0 1 D +R t 1947 1948 - S lastSu 0 0 S +R t 1949 o - N lastSu 0 0 S +R t 1950 1973 - Ap lastSu 2 1 D +R t 1950 o - N lastSu 2 0 S +R t 1951 1956 - S lastSu 2 0 S +R t 1957 1973 - O lastSu 2 0 S +Z America/Toronto -5:17:32 - LMT 1895 +-5 C E%sT 1919 +-5 t E%sT 1942 F 9 2s +-5 C E%sT 1946 +-5 t E%sT 1974 +-5 C E%sT +L America/Toronto America/Nassau +Z America/Thunder_Bay -5:57 - LMT 1895 +-6 - CST 1910 +-5 - EST 1942 +-5 C E%sT 1970 +-5 t E%sT 1973 +-5 - EST 1974 +-5 C E%sT +Z America/Nipigon -5:53:4 - LMT 1895 +-5 C E%sT 1940 S 29 +-5 1 EDT 1942 F 9 2s +-5 C E%sT +Z America/Rainy_River -6:18:16 - LMT 1895 +-6 C C%sT 1940 S 29 +-6 1 CDT 1942 F 9 2s +-6 C C%sT +R W 1916 o - Ap 23 0 1 D +R W 1916 o - S 17 0 0 S +R W 1918 o - Ap 14 2 1 D +R W 1918 o - O 27 2 0 S +R W 1937 o - May 16 2 1 D +R W 1937 o - S 26 2 0 S +R W 1942 o - F 9 2 1 W +R W 1945 o - Au 14 23u 1 P +R W 1945 o - S lastSu 2 0 S +R W 1946 o - May 12 2 1 D +R W 1946 o - O 13 2 0 S +R W 1947 1949 - Ap lastSu 2 1 D +R W 1947 1949 - S lastSu 2 0 S +R W 1950 o - May 1 2 1 D +R W 1950 o - S 30 2 0 S +R W 1951 1960 - Ap lastSu 2 1 D +R W 1951 1958 - S lastSu 2 0 S +R W 1959 o - O lastSu 2 0 S +R W 1960 o - S lastSu 2 0 S +R W 1963 o - Ap lastSu 2 1 D +R W 1963 o - S 22 2 0 S +R W 1966 1986 - Ap lastSu 2s 1 D +R W 1966 2005 - O lastSu 2s 0 S +R W 1987 2005 - Ap Su>=1 2s 1 D +Z America/Winnipeg -6:28:36 - LMT 1887 Jul 16 +-6 W C%sT 2006 +-6 C C%sT +R r 1918 o - Ap 14 2 1 D +R r 1918 o - O 27 2 0 S +R r 1930 1934 - May Su>=1 0 1 D +R r 1930 1934 - O Su>=1 0 0 S +R r 1937 1941 - Ap Su>=8 0 1 D +R r 1937 o - O Su>=8 0 0 S +R r 1938 o - O Su>=1 0 0 S +R r 1939 1941 - O Su>=8 0 0 S +R r 1942 o - F 9 2 1 W +R r 1945 o - Au 14 23u 1 P +R r 1945 o - S lastSu 2 0 S +R r 1946 o - Ap Su>=8 2 1 D +R r 1946 o - O Su>=8 2 0 S +R r 1947 1957 - Ap lastSu 2 1 D +R r 1947 1957 - S lastSu 2 0 S +R r 1959 o - Ap lastSu 2 1 D +R r 1959 o - O lastSu 2 0 S +R Sw 1957 o - Ap lastSu 2 1 D +R Sw 1957 o - O lastSu 2 0 S +R Sw 1959 1961 - Ap lastSu 2 1 D +R Sw 1959 o - O lastSu 2 0 S +R Sw 1960 1961 - S lastSu 2 0 S +Z America/Regina -6:58:36 - LMT 1905 S +-7 r M%sT 1960 Ap lastSu 2 +-6 - CST +Z America/Swift_Current -7:11:20 - LMT 1905 S +-7 C M%sT 1946 Ap lastSu 2 +-7 r M%sT 1950 +-7 Sw M%sT 1972 Ap lastSu 2 +-6 - CST +R Ed 1918 1919 - Ap Su>=8 2 1 D +R Ed 1918 o - O 27 2 0 S +R Ed 1919 o - May 27 2 0 S +R Ed 1920 1923 - Ap lastSu 2 1 D +R Ed 1920 o - O lastSu 2 0 S +R Ed 1921 1923 - S lastSu 2 0 S +R Ed 1942 o - F 9 2 1 W +R Ed 1945 o - Au 14 23u 1 P +R Ed 1945 o - S lastSu 2 0 S +R Ed 1947 o - Ap lastSu 2 1 D +R Ed 1947 o - S lastSu 2 0 S +R Ed 1972 1986 - Ap lastSu 2 1 D +R Ed 1972 2006 - O lastSu 2 0 S +Z America/Edmonton -7:33:52 - LMT 1906 S +-7 Ed M%sT 1987 +-7 C M%sT +R Va 1918 o - Ap 14 2 1 D +R Va 1918 o - O 27 2 0 S +R Va 1942 o - F 9 2 1 W +R Va 1945 o - Au 14 23u 1 P +R Va 1945 o - S 30 2 0 S +R Va 1946 1986 - Ap lastSu 2 1 D +R Va 1946 o - S 29 2 0 S +R Va 1947 1961 - S lastSu 2 0 S +R Va 1962 2006 - O lastSu 2 0 S +Z America/Vancouver -8:12:28 - LMT 1884 +-8 Va P%sT 1987 +-8 C P%sT +Z America/Dawson_Creek -8:0:56 - LMT 1884 +-8 C P%sT 1947 +-8 Va P%sT 1972 Au 30 2 +-7 - MST +Z America/Fort_Nelson -8:10:47 - LMT 1884 +-8 Va P%sT 1946 +-8 - PST 1947 +-8 Va P%sT 1987 +-8 C P%sT 2015 Mar 8 2 +-7 - MST +R Y 1918 o - Ap 14 2 1 D +R Y 1918 o - O 27 2 0 S +R Y 1919 o - May 25 2 1 D +R Y 1919 o - N 1 0 0 S +R Y 1942 o - F 9 2 1 W +R Y 1945 o - Au 14 23u 1 P +R Y 1945 o - S 30 2 0 S +R Y 1965 o - Ap lastSu 0 2 DD +R Y 1965 o - O lastSu 2 0 S +R Y 1980 1986 - Ap lastSu 2 1 D +R Y 1980 2006 - O lastSu 2 0 S +R Y 1987 2006 - Ap Su>=1 2 1 D +Z America/Pangnirtung 0 - -00 1921 +-4 Y A%sT 1995 Ap Su>=1 2 +-5 C E%sT 1999 O 31 2 +-6 C C%sT 2000 O 29 2 +-5 C E%sT +Z America/Iqaluit 0 - -00 1942 Au +-5 Y E%sT 1999 O 31 2 +-6 C C%sT 2000 O 29 2 +-5 C E%sT +Z America/Resolute 0 - -00 1947 Au 31 +-6 Y C%sT 2000 O 29 2 +-5 - EST 2001 Ap 1 3 +-6 C C%sT 2006 O 29 2 +-5 - EST 2007 Mar 11 3 +-6 C C%sT +Z America/Rankin_Inlet 0 - -00 1957 +-6 Y C%sT 2000 O 29 2 +-5 - EST 2001 Ap 1 3 +-6 C C%sT +Z America/Cambridge_Bay 0 - -00 1920 +-7 Y M%sT 1999 O 31 2 +-6 C C%sT 2000 O 29 2 +-5 - EST 2000 N 5 +-6 - CST 2001 Ap 1 3 +-7 C M%sT +Z America/Yellowknife 0 - -00 1935 +-7 Y M%sT 1980 +-7 C M%sT +Z America/Inuvik 0 - -00 1953 +-8 Y P%sT 1979 Ap lastSu 2 +-7 Y M%sT 1980 +-7 C M%sT +Z America/Whitehorse -9:0:12 - LMT 1900 Au 20 +-9 Y Y%sT 1967 May 28 +-8 Y P%sT 1980 +-8 C P%sT 2020 N +-7 - MST +Z America/Dawson -9:17:40 - LMT 1900 Au 20 +-9 Y Y%sT 1973 O 28 +-8 Y P%sT 1980 +-8 C P%sT 2020 N +-7 - MST +R m 1939 o - F 5 0 1 D +R m 1939 o - Jun 25 0 0 S +R m 1940 o - D 9 0 1 D +R m 1941 o - Ap 1 0 0 S +R m 1943 o - D 16 0 1 W +R m 1944 o - May 1 0 0 S +R m 1950 o - F 12 0 1 D +R m 1950 o - Jul 30 0 0 S +R m 1996 2000 - Ap Su>=1 2 1 D +R m 1996 2000 - O lastSu 2 0 S +R m 2001 o - May Su>=1 2 1 D +R m 2001 o - S lastSu 2 0 S +R m 2002 ma - Ap Su>=1 2 1 D +R m 2002 ma - O lastSu 2 0 S +Z America/Cancun -5:47:4 - LMT 1922 Ja 1 0:12:56 +-6 - CST 1981 D 23 +-5 m E%sT 1998 Au 2 2 +-6 m C%sT 2015 F 1 2 +-5 - EST +Z America/Merida -5:58:28 - LMT 1922 Ja 1 0:1:32 +-6 - CST 1981 D 23 +-5 - EST 1982 D 2 +-6 m C%sT +Z America/Matamoros -6:40 - LMT 1921 D 31 23:20 +-6 - CST 1988 +-6 u C%sT 1989 +-6 m C%sT 2010 +-6 u C%sT +Z America/Monterrey -6:41:16 - LMT 1921 D 31 23:18:44 +-6 - CST 1988 +-6 u C%sT 1989 +-6 m C%sT +Z America/Mexico_City -6:36:36 - LMT 1922 Ja 1 0:23:24 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 m C%sT 2001 S 30 2 +-6 - CST 2002 F 20 +-6 m C%sT +Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 0:2:20 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT 2010 +-7 u M%sT +Z America/Chihuahua -7:4:20 - LMT 1921 D 31 23:55:40 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 - CST 1996 +-6 m C%sT 1998 +-6 - CST 1998 Ap Su>=1 3 +-7 m M%sT +Z America/Hermosillo -7:23:52 - LMT 1921 D 31 23:36:8 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1949 Ja 14 +-8 - PST 1970 +-7 m M%sT 1999 +-7 - MST +Z America/Mazatlan -7:5:40 - LMT 1921 D 31 23:54:20 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1949 Ja 14 +-8 - PST 1970 +-7 m M%sT +Z America/Bahia_Banderas -7:1 - LMT 1921 D 31 23:59 +-7 - MST 1927 Jun 10 23 +-6 - CST 1930 N 15 +-7 - MST 1931 May 1 23 +-6 - CST 1931 O +-7 - MST 1932 Ap +-6 - CST 1942 Ap 24 +-7 - MST 1949 Ja 14 +-8 - PST 1970 +-7 m M%sT 2010 Ap 4 2 +-6 m C%sT +Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 0:11:56 +-7 - MST 1924 +-8 - PST 1927 Jun 10 23 +-7 - MST 1930 N 15 +-8 - PST 1931 Ap +-8 1 PDT 1931 S 30 +-8 - PST 1942 Ap 24 +-8 1 PWT 1945 Au 14 23u +-8 1 PPT 1945 N 12 +-8 - PST 1948 Ap 5 +-8 1 PDT 1949 Ja 14 +-8 - PST 1954 +-8 CA P%sT 1961 +-8 - PST 1976 +-8 u P%sT 1996 +-8 m P%sT 2001 +-8 u P%sT 2002 F 20 +-8 m P%sT 2010 +-8 u P%sT +R BB 1942 o - Ap 19 5u 1 D +R BB 1942 o - Au 31 6u 0 S +R BB 1943 o - May 2 5u 1 D +R BB 1943 o - S 5 6u 0 S +R BB 1944 o - Ap 10 5u 0:30 - +R BB 1944 o - S 10 6u 0 S +R BB 1977 o - Jun 12 2 1 D +R BB 1977 1978 - O Su>=1 2 0 S +R BB 1978 1980 - Ap Su>=15 2 1 D +R BB 1979 o - S 30 2 0 S +R BB 1980 o - S 25 2 0 S +Z America/Barbados -3:58:29 - LMT 1911 Au 28 +-4 BB A%sT 1944 +-4 BB AST/-0330 1945 +-4 BB A%sT +R BZ 1918 1941 - O Sa>=1 24 0:30 -0530 +R BZ 1919 1942 - F Sa>=8 24 0 CST +R BZ 1942 o - Jun 27 24 1 CWT +R BZ 1945 o - Au 14 23u 1 CPT +R BZ 1945 o - D 15 24 0 CST +R BZ 1947 1967 - O Sa>=1 24 0:30 -0530 +R BZ 1948 1968 - F Sa>=8 24 0 CST +R BZ 1973 o - D 5 0 1 CDT +R BZ 1974 o - F 9 0 0 CST +R BZ 1982 o - D 18 0 1 CDT +R BZ 1983 o - F 12 0 0 CST +Z America/Belize -5:52:48 - LMT 1912 Ap +-6 BZ %s +R Be 1917 o - Ap 5 24 1 - +R Be 1917 o - S 30 24 0 - +R Be 1918 o - Ap 13 24 1 - +R Be 1918 o - S 15 24 0 S +R Be 1942 o - Ja 11 2 1 D +R Be 1942 o - O 18 2 0 S +R Be 1943 o - Mar 21 2 1 D +R Be 1943 o - O 31 2 0 S +R Be 1944 1945 - Mar Su>=8 2 1 D +R Be 1944 1945 - N Su>=1 2 0 S +R Be 1947 o - May Su>=15 2 1 D +R Be 1947 o - S Su>=8 2 0 S +R Be 1948 1952 - May Su>=22 2 1 D +R Be 1948 1952 - S Su>=1 2 0 S +R Be 1956 o - May Su>=22 2 1 D +R Be 1956 o - O lastSu 2 0 S +Z Atlantic/Bermuda -4:19:18 - LMT 1890 +-4:19:18 Be BMT/BST 1930 Ja 1 2 +-4 Be A%sT 1974 Ap 28 2 +-4 C A%sT 1976 +-4 u A%sT +R CR 1979 1980 - F lastSu 0 1 D +R CR 1979 1980 - Jun Su>=1 0 0 S +R CR 1991 1992 - Ja Sa>=15 0 1 D +R CR 1991 o - Jul 1 0 0 S +R CR 1992 o - Mar 15 0 0 S +Z America/Costa_Rica -5:36:13 - LMT 1890 +-5:36:13 - SJMT 1921 Ja 15 +-6 CR C%sT +R Q 1928 o - Jun 10 0 1 D +R Q 1928 o - O 10 0 0 S +R Q 1940 1942 - Jun Su>=1 0 1 D +R Q 1940 1942 - S Su>=1 0 0 S +R Q 1945 1946 - Jun Su>=1 0 1 D +R Q 1945 1946 - S Su>=1 0 0 S +R Q 1965 o - Jun 1 0 1 D +R Q 1965 o - S 30 0 0 S +R Q 1966 o - May 29 0 1 D +R Q 1966 o - O 2 0 0 S +R Q 1967 o - Ap 8 0 1 D +R Q 1967 1968 - S Su>=8 0 0 S +R Q 1968 o - Ap 14 0 1 D +R Q 1969 1977 - Ap lastSu 0 1 D +R Q 1969 1971 - O lastSu 0 0 S +R Q 1972 1974 - O 8 0 0 S +R Q 1975 1977 - O lastSu 0 0 S +R Q 1978 o - May 7 0 1 D +R Q 1978 1990 - O Su>=8 0 0 S +R Q 1979 1980 - Mar Su>=15 0 1 D +R Q 1981 1985 - May Su>=5 0 1 D +R Q 1986 1989 - Mar Su>=14 0 1 D +R Q 1990 1997 - Ap Su>=1 0 1 D +R Q 1991 1995 - O Su>=8 0s 0 S +R Q 1996 o - O 6 0s 0 S +R Q 1997 o - O 12 0s 0 S +R Q 1998 1999 - Mar lastSu 0s 1 D +R Q 1998 2003 - O lastSu 0s 0 S +R Q 2000 2003 - Ap Su>=1 0s 1 D +R Q 2004 o - Mar lastSu 0s 1 D +R Q 2006 2010 - O lastSu 0s 0 S +R Q 2007 o - Mar Su>=8 0s 1 D +R Q 2008 o - Mar Su>=15 0s 1 D +R Q 2009 2010 - Mar Su>=8 0s 1 D +R Q 2011 o - Mar Su>=15 0s 1 D +R Q 2011 o - N 13 0s 0 S +R Q 2012 o - Ap 1 0s 1 D +R Q 2012 ma - N Su>=1 0s 0 S +R Q 2013 ma - Mar Su>=8 0s 1 D +Z America/Havana -5:29:28 - LMT 1890 +-5:29:36 - HMT 1925 Jul 19 12 +-5 Q C%sT +R DO 1966 o - O 30 0 1 EDT +R DO 1967 o - F 28 0 0 EST +R DO 1969 1973 - O lastSu 0 0:30 -0430 +R DO 1970 o - F 21 0 0 EST +R DO 1971 o - Ja 20 0 0 EST +R DO 1972 1974 - Ja 21 0 0 EST +Z America/Santo_Domingo -4:39:36 - LMT 1890 +-4:40 - SDMT 1933 Ap 1 12 +-5 DO %s 1974 O 27 +-4 - AST 2000 O 29 2 +-5 u E%sT 2000 D 3 1 +-4 - AST +R SV 1987 1988 - May Su>=1 0 1 D +R SV 1987 1988 - S lastSu 0 0 S +Z America/El_Salvador -5:56:48 - LMT 1921 +-6 SV C%sT +R GT 1973 o - N 25 0 1 D +R GT 1974 o - F 24 0 0 S +R GT 1983 o - May 21 0 1 D +R GT 1983 o - S 22 0 0 S +R GT 1991 o - Mar 23 0 1 D +R GT 1991 o - S 7 0 0 S +R GT 2006 o - Ap 30 0 1 D +R GT 2006 o - O 1 0 0 S +Z America/Guatemala -6:2:4 - LMT 1918 O 5 +-6 GT C%sT +R HT 1983 o - May 8 0 1 D +R HT 1984 1987 - Ap lastSu 0 1 D +R HT 1983 1987 - O lastSu 0 0 S +R HT 1988 1997 - Ap Su>=1 1s 1 D +R HT 1988 1997 - O lastSu 1s 0 S +R HT 2005 2006 - Ap Su>=1 0 1 D +R HT 2005 2006 - O lastSu 0 0 S +R HT 2012 2015 - Mar Su>=8 2 1 D +R HT 2012 2015 - N Su>=1 2 0 S +R HT 2017 ma - Mar Su>=8 2 1 D +R HT 2017 ma - N Su>=1 2 0 S +Z America/Port-au-Prince -4:49:20 - LMT 1890 +-4:49 - PPMT 1917 Ja 24 12 +-5 HT E%sT +R HN 1987 1988 - May Su>=1 0 1 D +R HN 1987 1988 - S lastSu 0 0 S +R HN 2006 o - May Su>=1 0 1 D +R HN 2006 o - Au M>=1 0 0 S +Z America/Tegucigalpa -5:48:52 - LMT 1921 Ap +-6 HN C%sT +Z America/Jamaica -5:7:10 - LMT 1890 +-5:7:10 - KMT 1912 F +-5 - EST 1974 +-5 u E%sT 1984 +-5 - EST +Z America/Martinique -4:4:20 - LMT 1890 +-4:4:20 - FFMT 1911 May +-4 - AST 1980 Ap 6 +-4 1 ADT 1980 S 28 +-4 - AST +R NI 1979 1980 - Mar Su>=16 0 1 D +R NI 1979 1980 - Jun M>=23 0 0 S +R NI 2005 o - Ap 10 0 1 D +R NI 2005 o - O Su>=1 0 0 S +R NI 2006 o - Ap 30 2 1 D +R NI 2006 o - O Su>=1 1 0 S +Z America/Managua -5:45:8 - LMT 1890 +-5:45:12 - MMT 1934 Jun 23 +-6 - CST 1973 May +-5 - EST 1975 F 16 +-6 NI C%sT 1992 Ja 1 4 +-5 - EST 1992 S 24 +-6 - CST 1993 +-5 - EST 1997 +-6 NI C%sT +Z America/Panama -5:18:8 - LMT 1890 +-5:19:36 - CMT 1908 Ap 22 +-5 - EST +L America/Panama America/Atikokan +L America/Panama America/Cayman +Z America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12 +-4 - AST 1942 May 3 +-4 u A%sT 1946 +-4 - AST +L America/Puerto_Rico America/Anguilla +L America/Puerto_Rico America/Antigua +L America/Puerto_Rico America/Aruba +L America/Puerto_Rico America/Curacao +L America/Puerto_Rico America/Blanc-Sablon +L America/Puerto_Rico America/Dominica +L America/Puerto_Rico America/Grenada +L America/Puerto_Rico America/Guadeloupe +L America/Puerto_Rico America/Kralendijk +L America/Puerto_Rico America/Lower_Princes +L America/Puerto_Rico America/Marigot +L America/Puerto_Rico America/Montserrat +L America/Puerto_Rico America/Port_of_Spain +L America/Puerto_Rico America/St_Barthelemy +L America/Puerto_Rico America/St_Kitts +L America/Puerto_Rico America/St_Lucia +L America/Puerto_Rico America/St_Thomas +L America/Puerto_Rico America/St_Vincent +L America/Puerto_Rico America/Tortola +Z America/Miquelon -3:44:40 - LMT 1911 May 15 +-4 - AST 1980 May +-3 - -03 1987 +-3 C -03/-02 +Z America/Grand_Turk -4:44:32 - LMT 1890 +-5:7:10 - KMT 1912 F +-5 - EST 1979 +-5 u E%sT 2015 Mar 8 2 +-4 - AST 2018 Mar 11 3 +-5 u E%sT +R A 1930 o - D 1 0 1 - +R A 1931 o - Ap 1 0 0 - +R A 1931 o - O 15 0 1 - +R A 1932 1940 - Mar 1 0 0 - +R A 1932 1939 - N 1 0 1 - +R A 1940 o - Jul 1 0 1 - +R A 1941 o - Jun 15 0 0 - +R A 1941 o - O 15 0 1 - +R A 1943 o - Au 1 0 0 - +R A 1943 o - O 15 0 1 - +R A 1946 o - Mar 1 0 0 - +R A 1946 o - O 1 0 1 - +R A 1963 o - O 1 0 0 - +R A 1963 o - D 15 0 1 - +R A 1964 1966 - Mar 1 0 0 - +R A 1964 1966 - O 15 0 1 - +R A 1967 o - Ap 2 0 0 - +R A 1967 1968 - O Su>=1 0 1 - +R A 1968 1969 - Ap Su>=1 0 0 - +R A 1974 o - Ja 23 0 1 - +R A 1974 o - May 1 0 0 - +R A 1988 o - D 1 0 1 - +R A 1989 1993 - Mar Su>=1 0 0 - +R A 1989 1992 - O Su>=15 0 1 - +R A 1999 o - O Su>=1 0 1 - +R A 2000 o - Mar 3 0 0 - +R A 2007 o - D 30 0 1 - +R A 2008 2009 - Mar Su>=15 0 0 - +R A 2008 o - O Su>=15 0 1 - +Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 A -03/-02 +Z America/Argentina/Cordoba -4:16:48 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar 3 +-4 - -04 1991 O 20 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 A -03/-02 +Z America/Argentina/Salta -4:21:40 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar 3 +-4 - -04 1991 O 20 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/Tucuman -4:20:52 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar 3 +-4 - -04 1991 O 20 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 Jun +-4 - -04 2004 Jun 13 +-3 A -03/-02 +Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar +-4 - -04 1991 May 7 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 Jun +-4 - -04 2004 Jun 20 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/San_Juan -4:34:4 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar +-4 - -04 1991 May 7 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 May 31 +-4 - -04 2004 Jul 25 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/Jujuy -4:21:12 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1990 Mar 4 +-4 - -04 1990 O 28 +-4 1 -03 1991 Mar 17 +-4 - -04 1991 O 6 +-3 1 -02 1992 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/Catamarca -4:23:8 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1991 Mar 3 +-4 - -04 1991 O 20 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 Jun +-4 - -04 2004 Jun 20 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/Mendoza -4:35:16 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1990 Mar 4 +-4 - -04 1990 O 15 +-4 1 -03 1991 Mar +-4 - -04 1991 O 15 +-4 1 -03 1992 Mar +-4 - -04 1992 O 18 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 May 23 +-4 - -04 2004 S 26 +-3 A -03/-02 2008 O 18 +-3 - -03 +R Sa 2008 2009 - Mar Su>=8 0 0 - +R Sa 2007 2008 - O Su>=8 0 1 - +Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1990 +-3 1 -02 1990 Mar 14 +-4 - -04 1990 O 15 +-4 1 -03 1991 Mar +-4 - -04 1991 Jun +-3 - -03 1999 O 3 +-4 1 -03 2000 Mar 3 +-3 - -03 2004 May 31 +-4 - -04 2004 Jul 25 +-3 A -03/-02 2008 Ja 21 +-4 Sa -04/-03 2009 O 11 +-3 - -03 +Z America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 Jun +-4 - -04 2004 Jun 20 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/Argentina/Ushuaia -4:33:12 - LMT 1894 O 31 +-4:16:48 - CMT 1920 May +-4 - -04 1930 D +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1999 O 3 +-4 A -04/-03 2000 Mar 3 +-3 - -03 2004 May 30 +-4 - -04 2004 Jun 20 +-3 A -03/-02 2008 O 18 +-3 - -03 +Z America/La_Paz -4:32:36 - LMT 1890 +-4:32:36 - CMT 1931 O 15 +-4:32:36 1 BST 1932 Mar 21 +-4 - -04 +R B 1931 o - O 3 11 1 - +R B 1932 1933 - Ap 1 0 0 - +R B 1932 o - O 3 0 1 - +R B 1949 1952 - D 1 0 1 - +R B 1950 o - Ap 16 1 0 - +R B 1951 1952 - Ap 1 0 0 - +R B 1953 o - Mar 1 0 0 - +R B 1963 o - D 9 0 1 - +R B 1964 o - Mar 1 0 0 - +R B 1965 o - Ja 31 0 1 - +R B 1965 o - Mar 31 0 0 - +R B 1965 o - D 1 0 1 - +R B 1966 1968 - Mar 1 0 0 - +R B 1966 1967 - N 1 0 1 - +R B 1985 o - N 2 0 1 - +R B 1986 o - Mar 15 0 0 - +R B 1986 o - O 25 0 1 - +R B 1987 o - F 14 0 0 - +R B 1987 o - O 25 0 1 - +R B 1988 o - F 7 0 0 - +R B 1988 o - O 16 0 1 - +R B 1989 o - Ja 29 0 0 - +R B 1989 o - O 15 0 1 - +R B 1990 o - F 11 0 0 - +R B 1990 o - O 21 0 1 - +R B 1991 o - F 17 0 0 - +R B 1991 o - O 20 0 1 - +R B 1992 o - F 9 0 0 - +R B 1992 o - O 25 0 1 - +R B 1993 o - Ja 31 0 0 - +R B 1993 1995 - O Su>=11 0 1 - +R B 1994 1995 - F Su>=15 0 0 - +R B 1996 o - F 11 0 0 - +R B 1996 o - O 6 0 1 - +R B 1997 o - F 16 0 0 - +R B 1997 o - O 6 0 1 - +R B 1998 o - Mar 1 0 0 - +R B 1998 o - O 11 0 1 - +R B 1999 o - F 21 0 0 - +R B 1999 o - O 3 0 1 - +R B 2000 o - F 27 0 0 - +R B 2000 2001 - O Su>=8 0 1 - +R B 2001 2006 - F Su>=15 0 0 - +R B 2002 o - N 3 0 1 - +R B 2003 o - O 19 0 1 - +R B 2004 o - N 2 0 1 - +R B 2005 o - O 16 0 1 - +R B 2006 o - N 5 0 1 - +R B 2007 o - F 25 0 0 - +R B 2007 o - O Su>=8 0 1 - +R B 2008 2017 - O Su>=15 0 1 - +R B 2008 2011 - F Su>=15 0 0 - +R B 2012 o - F Su>=22 0 0 - +R B 2013 2014 - F Su>=15 0 0 - +R B 2015 o - F Su>=22 0 0 - +R B 2016 2019 - F Su>=15 0 0 - +R B 2018 o - N Su>=1 0 1 - +Z America/Noronha -2:9:40 - LMT 1914 +-2 B -02/-01 1990 S 17 +-2 - -02 1999 S 30 +-2 B -02/-01 2000 O 15 +-2 - -02 2001 S 13 +-2 B -02/-01 2002 O +-2 - -02 +Z America/Belem -3:13:56 - LMT 1914 +-3 B -03/-02 1988 S 12 +-3 - -03 +Z America/Santarem -3:38:48 - LMT 1914 +-4 B -04/-03 1988 S 12 +-4 - -04 2008 Jun 24 +-3 - -03 +Z America/Fortaleza -2:34 - LMT 1914 +-3 B -03/-02 1990 S 17 +-3 - -03 1999 S 30 +-3 B -03/-02 2000 O 22 +-3 - -03 2001 S 13 +-3 B -03/-02 2002 O +-3 - -03 +Z America/Recife -2:19:36 - LMT 1914 +-3 B -03/-02 1990 S 17 +-3 - -03 1999 S 30 +-3 B -03/-02 2000 O 15 +-3 - -03 2001 S 13 +-3 B -03/-02 2002 O +-3 - -03 +Z America/Araguaina -3:12:48 - LMT 1914 +-3 B -03/-02 1990 S 17 +-3 - -03 1995 S 14 +-3 B -03/-02 2003 S 24 +-3 - -03 2012 O 21 +-3 B -03/-02 2013 S +-3 - -03 +Z America/Maceio -2:22:52 - LMT 1914 +-3 B -03/-02 1990 S 17 +-3 - -03 1995 O 13 +-3 B -03/-02 1996 S 4 +-3 - -03 1999 S 30 +-3 B -03/-02 2000 O 22 +-3 - -03 2001 S 13 +-3 B -03/-02 2002 O +-3 - -03 +Z America/Bahia -2:34:4 - LMT 1914 +-3 B -03/-02 2003 S 24 +-3 - -03 2011 O 16 +-3 B -03/-02 2012 O 21 +-3 - -03 +Z America/Sao_Paulo -3:6:28 - LMT 1914 +-3 B -03/-02 1963 O 23 +-3 1 -02 1964 +-3 B -03/-02 +Z America/Campo_Grande -3:38:28 - LMT 1914 +-4 B -04/-03 +Z America/Cuiaba -3:44:20 - LMT 1914 +-4 B -04/-03 2003 S 24 +-4 - -04 2004 O +-4 B -04/-03 +Z America/Porto_Velho -4:15:36 - LMT 1914 +-4 B -04/-03 1988 S 12 +-4 - -04 +Z America/Boa_Vista -4:2:40 - LMT 1914 +-4 B -04/-03 1988 S 12 +-4 - -04 1999 S 30 +-4 B -04/-03 2000 O 15 +-4 - -04 +Z America/Manaus -4:0:4 - LMT 1914 +-4 B -04/-03 1988 S 12 +-4 - -04 1993 S 28 +-4 B -04/-03 1994 S 22 +-4 - -04 +Z America/Eirunepe -4:39:28 - LMT 1914 +-5 B -05/-04 1988 S 12 +-5 - -05 1993 S 28 +-5 B -05/-04 1994 S 22 +-5 - -05 2008 Jun 24 +-4 - -04 2013 N 10 +-5 - -05 +Z America/Rio_Branco -4:31:12 - LMT 1914 +-5 B -05/-04 1988 S 12 +-5 - -05 2008 Jun 24 +-4 - -04 2013 N 10 +-5 - -05 +R x 1927 1931 - S 1 0 1 - +R x 1928 1932 - Ap 1 0 0 - +R x 1968 o - N 3 4u 1 - +R x 1969 o - Mar 30 3u 0 - +R x 1969 o - N 23 4u 1 - +R x 1970 o - Mar 29 3u 0 - +R x 1971 o - Mar 14 3u 0 - +R x 1970 1972 - O Su>=9 4u 1 - +R x 1972 1986 - Mar Su>=9 3u 0 - +R x 1973 o - S 30 4u 1 - +R x 1974 1987 - O Su>=9 4u 1 - +R x 1987 o - Ap 12 3u 0 - +R x 1988 1990 - Mar Su>=9 3u 0 - +R x 1988 1989 - O Su>=9 4u 1 - +R x 1990 o - S 16 4u 1 - +R x 1991 1996 - Mar Su>=9 3u 0 - +R x 1991 1997 - O Su>=9 4u 1 - +R x 1997 o - Mar 30 3u 0 - +R x 1998 o - Mar Su>=9 3u 0 - +R x 1998 o - S 27 4u 1 - +R x 1999 o - Ap 4 3u 0 - +R x 1999 2010 - O Su>=9 4u 1 - +R x 2000 2007 - Mar Su>=9 3u 0 - +R x 2008 o - Mar 30 3u 0 - +R x 2009 o - Mar Su>=9 3u 0 - +R x 2010 o - Ap Su>=1 3u 0 - +R x 2011 o - May Su>=2 3u 0 - +R x 2011 o - Au Su>=16 4u 1 - +R x 2012 2014 - Ap Su>=23 3u 0 - +R x 2012 2014 - S Su>=2 4u 1 - +R x 2016 2018 - May Su>=9 3u 0 - +R x 2016 2018 - Au Su>=9 4u 1 - +R x 2019 ma - Ap Su>=2 3u 0 - +R x 2019 ma - S Su>=2 4u 1 - +Z America/Santiago -4:42:45 - LMT 1890 +-4:42:45 - SMT 1910 Ja 10 +-5 - -05 1916 Jul +-4:42:45 - SMT 1918 S 10 +-4 - -04 1919 Jul +-4:42:45 - SMT 1927 S +-5 x -05/-04 1932 S +-4 - -04 1942 Jun +-5 - -05 1942 Au +-4 - -04 1946 Jul 15 +-4 1 -03 1946 S +-4 - -04 1947 Ap +-5 - -05 1947 May 21 23 +-4 x -04/-03 +Z America/Punta_Arenas -4:43:40 - LMT 1890 +-4:42:45 - SMT 1910 Ja 10 +-5 - -05 1916 Jul +-4:42:45 - SMT 1918 S 10 +-4 - -04 1919 Jul +-4:42:45 - SMT 1927 S +-5 x -05/-04 1932 S +-4 - -04 1942 Jun +-5 - -05 1942 Au +-4 - -04 1947 Ap +-5 - -05 1947 May 21 23 +-4 x -04/-03 2016 D 4 +-3 - -03 +Z Pacific/Easter -7:17:28 - LMT 1890 +-7:17:28 - EMT 1932 S +-7 x -07/-06 1982 Mar 14 3u +-6 x -06/-05 +Z Antarctica/Palmer 0 - -00 1965 +-4 A -04/-03 1969 O 5 +-3 A -03/-02 1982 May +-4 x -04/-03 2016 D 4 +-3 - -03 +R CO 1992 o - May 3 0 1 - +R CO 1993 o - Ap 4 0 0 - +Z America/Bogota -4:56:16 - LMT 1884 Mar 13 +-4:56:16 - BMT 1914 N 23 +-5 CO -05/-04 +R EC 1992 o - N 28 0 1 - +R EC 1993 o - F 5 0 0 - +Z America/Guayaquil -5:19:20 - LMT 1890 +-5:14 - QMT 1931 +-5 EC -05/-04 +Z Pacific/Galapagos -5:58:24 - LMT 1931 +-5 - -05 1986 +-6 EC -06/-05 +R FK 1937 1938 - S lastSu 0 1 - +R FK 1938 1942 - Mar Su>=19 0 0 - +R FK 1939 o - O 1 0 1 - +R FK 1940 1942 - S lastSu 0 1 - +R FK 1943 o - Ja 1 0 0 - +R FK 1983 o - S lastSu 0 1 - +R FK 1984 1985 - Ap lastSu 0 0 - +R FK 1984 o - S 16 0 1 - +R FK 1985 2000 - S Su>=9 0 1 - +R FK 1986 2000 - Ap Su>=16 0 0 - +R FK 2001 2010 - Ap Su>=15 2 0 - +R FK 2001 2010 - S Su>=1 2 1 - +Z Atlantic/Stanley -3:51:24 - LMT 1890 +-3:51:24 - SMT 1912 Mar 12 +-4 FK -04/-03 1983 May +-3 FK -03/-02 1985 S 15 +-4 FK -04/-03 2010 S 5 2 +-3 - -03 +Z America/Cayenne -3:29:20 - LMT 1911 Jul +-4 - -04 1967 O +-3 - -03 +Z America/Guyana -3:52:39 - LMT 1911 Au +-4 - -04 1915 Mar +-3:45 - -0345 1975 Au +-3 - -03 1992 Mar 29 1 +-4 - -04 +R y 1975 1988 - O 1 0 1 - +R y 1975 1978 - Mar 1 0 0 - +R y 1979 1991 - Ap 1 0 0 - +R y 1989 o - O 22 0 1 - +R y 1990 o - O 1 0 1 - +R y 1991 o - O 6 0 1 - +R y 1992 o - Mar 1 0 0 - +R y 1992 o - O 5 0 1 - +R y 1993 o - Mar 31 0 0 - +R y 1993 1995 - O 1 0 1 - +R y 1994 1995 - F lastSu 0 0 - +R y 1996 o - Mar 1 0 0 - +R y 1996 2001 - O Su>=1 0 1 - +R y 1997 o - F lastSu 0 0 - +R y 1998 2001 - Mar Su>=1 0 0 - +R y 2002 2004 - Ap Su>=1 0 0 - +R y 2002 2003 - S Su>=1 0 1 - +R y 2004 2009 - O Su>=15 0 1 - +R y 2005 2009 - Mar Su>=8 0 0 - +R y 2010 ma - O Su>=1 0 1 - +R y 2010 2012 - Ap Su>=8 0 0 - +R y 2013 ma - Mar Su>=22 0 0 - +Z America/Asuncion -3:50:40 - LMT 1890 +-3:50:40 - AMT 1931 O 10 +-4 - -04 1972 O +-3 - -03 1974 Ap +-4 y -04/-03 +R PE 1938 o - Ja 1 0 1 - +R PE 1938 o - Ap 1 0 0 - +R PE 1938 1939 - S lastSu 0 1 - +R PE 1939 1940 - Mar Su>=24 0 0 - +R PE 1986 1987 - Ja 1 0 1 - +R PE 1986 1987 - Ap 1 0 0 - +R PE 1990 o - Ja 1 0 1 - +R PE 1990 o - Ap 1 0 0 - +R PE 1994 o - Ja 1 0 1 - +R PE 1994 o - Ap 1 0 0 - +Z America/Lima -5:8:12 - LMT 1890 +-5:8:36 - LMT 1908 Jul 28 +-5 PE -05/-04 +Z Atlantic/South_Georgia -2:26:8 - LMT 1890 +-2 - -02 +Z America/Paramaribo -3:40:40 - LMT 1911 +-3:40:52 - PMT 1935 +-3:40:36 - PMT 1945 O +-3:30 - -0330 1984 O +-3 - -03 +R U 1923 1925 - O 1 0 0:30 - +R U 1924 1926 - Ap 1 0 0 - +R U 1933 1938 - O lastSu 0 0:30 - +R U 1934 1941 - Mar lastSa 24 0 - +R U 1939 o - O 1 0 0:30 - +R U 1940 o - O 27 0 0:30 - +R U 1941 o - Au 1 0 0:30 - +R U 1942 o - D 14 0 0:30 - +R U 1943 o - Mar 14 0 0 - +R U 1959 o - May 24 0 0:30 - +R U 1959 o - N 15 0 0 - +R U 1960 o - Ja 17 0 1 - +R U 1960 o - Mar 6 0 0 - +R U 1965 o - Ap 4 0 1 - +R U 1965 o - S 26 0 0 - +R U 1968 o - May 27 0 0:30 - +R U 1968 o - D 1 0 0 - +R U 1970 o - Ap 25 0 1 - +R U 1970 o - Jun 14 0 0 - +R U 1972 o - Ap 23 0 1 - +R U 1972 o - Jul 16 0 0 - +R U 1974 o - Ja 13 0 1:30 - +R U 1974 o - Mar 10 0 0:30 - +R U 1974 o - S 1 0 0 - +R U 1974 o - D 22 0 1 - +R U 1975 o - Mar 30 0 0 - +R U 1976 o - D 19 0 1 - +R U 1977 o - Mar 6 0 0 - +R U 1977 o - D 4 0 1 - +R U 1978 1979 - Mar Su>=1 0 0 - +R U 1978 o - D 17 0 1 - +R U 1979 o - Ap 29 0 1 - +R U 1980 o - Mar 16 0 0 - +R U 1987 o - D 14 0 1 - +R U 1988 o - F 28 0 0 - +R U 1988 o - D 11 0 1 - +R U 1989 o - Mar 5 0 0 - +R U 1989 o - O 29 0 1 - +R U 1990 o - F 25 0 0 - +R U 1990 1991 - O Su>=21 0 1 - +R U 1991 1992 - Mar Su>=1 0 0 - +R U 1992 o - O 18 0 1 - +R U 1993 o - F 28 0 0 - +R U 2004 o - S 19 0 1 - +R U 2005 o - Mar 27 2 0 - +R U 2005 o - O 9 2 1 - +R U 2006 2015 - Mar Su>=8 2 0 - +R U 2006 2014 - O Su>=1 2 1 - +Z America/Montevideo -3:44:51 - LMT 1908 Jun 10 +-3:44:51 - MMT 1920 May +-4 - -04 1923 O +-3:30 U -0330/-03 1942 D 14 +-3 U -03/-0230 1960 +-3 U -03/-02 1968 +-3 U -03/-0230 1970 +-3 U -03/-02 1974 +-3 U -03/-0130 1974 Mar 10 +-3 U -03/-0230 1974 D 22 +-3 U -03/-02 +Z America/Caracas -4:27:44 - LMT 1890 +-4:27:40 - CMT 1912 F 12 +-4:30 - -0430 1965 +-4 - -04 2007 D 9 3 +-4:30 - -0430 2016 May 1 2:30 +-4 - -04 +Z Etc/GMT 0 - GMT +Z Etc/UTC 0 - UTC +L Etc/GMT GMT +L Etc/UTC Etc/Universal +L Etc/UTC Etc/Zulu +L Etc/GMT Etc/Greenwich +L Etc/GMT Etc/GMT-0 +L Etc/GMT Etc/GMT+0 +L Etc/GMT Etc/GMT0 +Z Etc/GMT-14 14 - +14 +Z Etc/GMT-13 13 - +13 +Z Etc/GMT-12 12 - +12 +Z Etc/GMT-11 11 - +11 +Z Etc/GMT-10 10 - +10 +Z Etc/GMT-9 9 - +09 +Z Etc/GMT-8 8 - +08 +Z Etc/GMT-7 7 - +07 +Z Etc/GMT-6 6 - +06 +Z Etc/GMT-5 5 - +05 +Z Etc/GMT-4 4 - +04 +Z Etc/GMT-3 3 - +03 +Z Etc/GMT-2 2 - +02 +Z Etc/GMT-1 1 - +01 +Z Etc/GMT+1 -1 - -01 +Z Etc/GMT+2 -2 - -02 +Z Etc/GMT+3 -3 - -03 +Z Etc/GMT+4 -4 - -04 +Z Etc/GMT+5 -5 - -05 +Z Etc/GMT+6 -6 - -06 +Z Etc/GMT+7 -7 - -07 +Z Etc/GMT+8 -8 - -08 +Z Etc/GMT+9 -9 - -09 +Z Etc/GMT+10 -10 - -10 +Z Etc/GMT+11 -11 - -11 +Z Etc/GMT+12 -12 - -12 +Z Factory 0 - -00 +L Africa/Nairobi Africa/Asmera +L Africa/Abidjan Africa/Timbuktu +L America/Argentina/Catamarca America/Argentina/ComodRivadavia +L America/Adak America/Atka +L America/Argentina/Buenos_Aires America/Buenos_Aires +L America/Argentina/Catamarca America/Catamarca +L America/Panama America/Coral_Harbour +L America/Argentina/Cordoba America/Cordoba +L America/Tijuana America/Ensenada +L America/Indiana/Indianapolis America/Fort_Wayne +L America/Nuuk America/Godthab +L America/Indiana/Indianapolis America/Indianapolis +L America/Argentina/Jujuy America/Jujuy +L America/Indiana/Knox America/Knox_IN +L America/Kentucky/Louisville America/Louisville +L America/Argentina/Mendoza America/Mendoza +L America/Toronto America/Montreal +L America/Rio_Branco America/Porto_Acre +L America/Argentina/Cordoba America/Rosario +L America/Tijuana America/Santa_Isabel +L America/Denver America/Shiprock +L America/Puerto_Rico America/Virgin +L Pacific/Auckland Antarctica/South_Pole +L Asia/Ashgabat Asia/Ashkhabad +L Asia/Kolkata Asia/Calcutta +L Asia/Shanghai Asia/Chongqing +L Asia/Shanghai Asia/Chungking +L Asia/Dhaka Asia/Dacca +L Asia/Shanghai Asia/Harbin +L Asia/Urumqi Asia/Kashgar +L Asia/Kathmandu Asia/Katmandu +L Asia/Macau Asia/Macao +L Asia/Yangon Asia/Rangoon +L Asia/Ho_Chi_Minh Asia/Saigon +L Asia/Jerusalem Asia/Tel_Aviv +L Asia/Thimphu Asia/Thimbu +L Asia/Makassar Asia/Ujung_Pandang +L Asia/Ulaanbaatar Asia/Ulan_Bator +L Atlantic/Faroe Atlantic/Faeroe +L Europe/Oslo Atlantic/Jan_Mayen +L Australia/Sydney Australia/ACT +L Australia/Sydney Australia/Canberra +L Australia/Hobart Australia/Currie +L Australia/Lord_Howe Australia/LHI +L Australia/Sydney Australia/NSW +L Australia/Darwin Australia/North +L Australia/Brisbane Australia/Queensland +L Australia/Adelaide Australia/South +L Australia/Hobart Australia/Tasmania +L Australia/Melbourne Australia/Victoria +L Australia/Perth Australia/West +L Australia/Broken_Hill Australia/Yancowinna +L America/Rio_Branco Brazil/Acre +L America/Noronha Brazil/DeNoronha +L America/Sao_Paulo Brazil/East +L America/Manaus Brazil/West +L America/Halifax Canada/Atlantic +L America/Winnipeg Canada/Central +L America/Toronto Canada/Eastern +L America/Edmonton Canada/Mountain +L America/St_Johns Canada/Newfoundland +L America/Vancouver Canada/Pacific +L America/Regina Canada/Saskatchewan +L America/Whitehorse Canada/Yukon +L America/Santiago Chile/Continental +L Pacific/Easter Chile/EasterIsland +L America/Havana Cuba +L Africa/Cairo Egypt +L Europe/Dublin Eire +L Etc/UTC Etc/UCT +L Europe/London Europe/Belfast +L Europe/Chisinau Europe/Tiraspol +L Europe/London GB +L Europe/London GB-Eire +L Etc/GMT GMT+0 +L Etc/GMT GMT-0 +L Etc/GMT GMT0 +L Etc/GMT Greenwich +L Asia/Hong_Kong Hongkong +L Atlantic/Reykjavik Iceland +L Asia/Tehran Iran +L Asia/Jerusalem Israel +L America/Jamaica Jamaica +L Asia/Tokyo Japan +L Pacific/Kwajalein Kwajalein +L Africa/Tripoli Libya +L America/Tijuana Mexico/BajaNorte +L America/Mazatlan Mexico/BajaSur +L America/Mexico_City Mexico/General +L Pacific/Auckland NZ +L Pacific/Chatham NZ-CHAT +L America/Denver Navajo +L Asia/Shanghai PRC +L Pacific/Kanton Pacific/Enderbury +L Pacific/Honolulu Pacific/Johnston +L Pacific/Pohnpei Pacific/Ponape +L Pacific/Pago_Pago Pacific/Samoa +L Pacific/Chuuk Pacific/Truk +L Pacific/Chuuk Pacific/Yap +L Europe/Warsaw Poland +L Europe/Lisbon Portugal +L Asia/Taipei ROC +L Asia/Seoul ROK +L Asia/Singapore Singapore +L Europe/Istanbul Turkey +L Etc/UTC UCT +L America/Anchorage US/Alaska +L America/Adak US/Aleutian +L America/Phoenix US/Arizona +L America/Chicago US/Central +L America/Indiana/Indianapolis US/East-Indiana +L America/New_York US/Eastern +L Pacific/Honolulu US/Hawaii +L America/Indiana/Knox US/Indiana-Starke +L America/Detroit US/Michigan +L America/Denver US/Mountain +L America/Los_Angeles US/Pacific +L Pacific/Pago_Pago US/Samoa +L Etc/UTC UTC +L Etc/UTC Universal +L Europe/Moscow W-SU +L Etc/UTC Zulu diff --git a/venv/Lib/site-packages/pytz/zoneinfo/zone.tab b/venv/Lib/site-packages/pytz/zoneinfo/zone.tab new file mode 100644 index 0000000..086458f --- /dev/null +++ b/venv/Lib/site-packages/pytz/zoneinfo/zone.tab @@ -0,0 +1,454 @@ +# tzdb timezone descriptions (deprecated version) +# +# This file is in the public domain, so clarified as of +# 2009-05-17 by Arthur David Olson. +# +# From Paul Eggert (2021-09-20): +# This file is intended as a backward-compatibility aid for older programs. +# New programs should use zone1970.tab. This file is like zone1970.tab (see +# zone1970.tab's comments), but with the following additional restrictions: +# +# 1. This file contains only ASCII characters. +# 2. The first data column contains exactly one country code. +# +# Because of (2), each row stands for an area that is the intersection +# of a region identified by a country code and of a timezone where civil +# clocks have agreed since 1970; this is a narrower definition than +# that of zone1970.tab. +# +# Unlike zone1970.tab, a row's third column can be a Link from +# 'backward' instead of a Zone. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#country- +#code coordinates TZ comments +AD +4230+00131 Europe/Andorra +AE +2518+05518 Asia/Dubai +AF +3431+06912 Asia/Kabul +AG +1703-06148 America/Antigua +AI +1812-06304 America/Anguilla +AL +4120+01950 Europe/Tirane +AM +4011+04430 Asia/Yerevan +AO -0848+01314 Africa/Luanda +AQ -7750+16636 Antarctica/McMurdo New Zealand time - McMurdo, South Pole +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -690022+0393524 Antarctica/Syowa Syowa +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok +AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) +AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) +AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) +AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) +AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) +AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) +AR -3319-06621 America/Argentina/San_Luis San Luis (SL) +AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) +AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) +AS -1416-17042 Pacific/Pago_Pago +AT +4813+01620 Europe/Vienna +AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island +AU -4253+14719 Australia/Hobart Tasmania +AU -3749+14458 Australia/Melbourne Victoria +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) +AU -3455+13835 Australia/Adelaide South Australia +AU -1228+13050 Australia/Darwin Northern Territory +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) +AW +1230-06958 America/Aruba +AX +6006+01957 Europe/Mariehamn +AZ +4023+04951 Asia/Baku +BA +4352+01825 Europe/Sarajevo +BB +1306-05937 America/Barbados +BD +2343+09025 Asia/Dhaka +BE +5050+00420 Europe/Brussels +BF +1222-00131 Africa/Ouagadougou +BG +4241+02319 Europe/Sofia +BH +2623+05035 Asia/Bahrain +BI -0323+02922 Africa/Bujumbura +BJ +0629+00237 Africa/Porto-Novo +BL +1753-06251 America/St_Barthelemy +BM +3217-06446 Atlantic/Bermuda +BN +0456+11455 Asia/Brunei +BO -1630-06809 America/La_Paz +BQ +120903-0681636 America/Kralendijk +BR -0351-03225 America/Noronha Atlantic islands +BR -0127-04829 America/Belem Para (east); Amapa +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) +BR -0803-03454 America/Recife Pernambuco +BR -0712-04812 America/Araguaina Tocantins +BR -0940-03543 America/Maceio Alagoas, Sergipe +BR -1259-03831 America/Bahia Bahia +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2027-05437 America/Campo_Grande Mato Grosso do Sul +BR -1535-05605 America/Cuiaba Mato Grosso +BR -0226-05452 America/Santarem Para (west) +BR -0846-06354 America/Porto_Velho Rondonia +BR +0249-06040 America/Boa_Vista Roraima +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) +BR -0958-06748 America/Rio_Branco Acre +BS +2505-07721 America/Nassau +BT +2728+08939 Asia/Thimphu +BW -2439+02555 Africa/Gaborone +BY +5354+02734 Europe/Minsk +BZ +1730-08812 America/Belize +CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) +CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) +CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) +CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +6227-11421 America/Yellowknife Mountain - NT (central) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +4906-11631 America/Creston MST - BC (Creston) +CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +6043-13503 America/Whitehorse MST - Yukon (east) +CA +6404-13925 America/Dawson MST - Yukon (west) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CC -1210+09655 Indian/Cocos +CD -0418+01518 Africa/Kinshasa Dem. Rep. of Congo (west) +CD -1140+02728 Africa/Lubumbashi Dem. Rep. of Congo (east) +CF +0422+01835 Africa/Bangui +CG -0416+01517 Africa/Brazzaville +CH +4723+00832 Europe/Zurich +CI +0519-00402 Africa/Abidjan +CK -2114-15946 Pacific/Rarotonga +CL -3327-07040 America/Santiago Chile (most areas) +CL -5309-07055 America/Punta_Arenas Region of Magallanes +CL -2709-10926 Pacific/Easter Easter Island +CM +0403+00942 Africa/Douala +CN +3114+12128 Asia/Shanghai Beijing Time +CN +4348+08735 Asia/Urumqi Xinjiang Time +CO +0436-07405 America/Bogota +CR +0956-08405 America/Costa_Rica +CU +2308-08222 America/Havana +CV +1455-02331 Atlantic/Cape_Verde +CW +1211-06900 America/Curacao +CX -1025+10543 Indian/Christmas +CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3507+03357 Asia/Famagusta Northern Cyprus +CZ +5005+01426 Europe/Prague +DE +5230+01322 Europe/Berlin Germany (most areas) +DE +4742+00841 Europe/Busingen Busingen +DJ +1136+04309 Africa/Djibouti +DK +5540+01235 Europe/Copenhagen +DM +1518-06124 America/Dominica +DO +1828-06954 America/Santo_Domingo +DZ +3647+00303 Africa/Algiers +EC -0210-07950 America/Guayaquil Ecuador (mainland) +EC -0054-08936 Pacific/Galapagos Galapagos Islands +EE +5925+02445 Europe/Tallinn +EG +3003+03115 Africa/Cairo +EH +2709-01312 Africa/El_Aaiun +ER +1520+03853 Africa/Asmara +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla +ES +2806-01524 Atlantic/Canary Canary Islands +ET +0902+03842 Africa/Addis_Ababa +FI +6010+02458 Europe/Helsinki +FJ -1808+17825 Pacific/Fiji +FK -5142-05751 Atlantic/Stanley +FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap +FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape +FM +0519+16259 Pacific/Kosrae Kosrae +FO +6201-00646 Atlantic/Faroe +FR +4852+00220 Europe/Paris +GA +0023+00927 Africa/Libreville +GB +513030-0000731 Europe/London +GD +1203-06145 America/Grenada +GE +4143+04449 Asia/Tbilisi +GF +0456-05220 America/Cayenne +GG +492717-0023210 Europe/Guernsey +GH +0533-00013 Africa/Accra +GI +3608-00521 Europe/Gibraltar +GL +6411-05144 America/Nuuk Greenland (most areas) +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik +GM +1328-01639 Africa/Banjul +GN +0931-01343 Africa/Conakry +GP +1614-06132 America/Guadeloupe +GQ +0345+00847 Africa/Malabo +GR +3758+02343 Europe/Athens +GS -5416-03632 Atlantic/South_Georgia +GT +1438-09031 America/Guatemala +GU +1328+14445 Pacific/Guam +GW +1151-01535 Africa/Bissau +GY +0648-05810 America/Guyana +HK +2217+11409 Asia/Hong_Kong +HN +1406-08713 America/Tegucigalpa +HR +4548+01558 Europe/Zagreb +HT +1832-07220 America/Port-au-Prince +HU +4730+01905 Europe/Budapest +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +IE +5320-00615 Europe/Dublin +IL +314650+0351326 Asia/Jerusalem +IM +5409-00428 Europe/Isle_of_Man +IN +2232+08822 Asia/Kolkata +IO -0720+07225 Indian/Chagos +IQ +3321+04425 Asia/Baghdad +IR +3540+05126 Asia/Tehran +IS +6409-02151 Atlantic/Reykjavik +IT +4154+01229 Europe/Rome +JE +491101-0020624 Europe/Jersey +JM +175805-0764736 America/Jamaica +JO +3157+03556 Asia/Amman +JP +353916+1394441 Asia/Tokyo +KE -0117+03649 Africa/Nairobi +KG +4254+07436 Asia/Bishkek +KH +1133+10455 Asia/Phnom_Penh +KI +0125+17300 Pacific/Tarawa Gilbert Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands +KI +0152-15720 Pacific/Kiritimati Line Islands +KM -1141+04316 Indian/Comoro +KN +1718-06243 America/St_Kitts +KP +3901+12545 Asia/Pyongyang +KR +3733+12658 Asia/Seoul +KW +2920+04759 Asia/Kuwait +KY +1918-08123 America/Cayman +KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystau/Mankistau +KZ +4707+05156 Asia/Atyrau Atyrau/Atirau/Gur'yev +KZ +5113+05121 Asia/Oral West Kazakhstan +LA +1758+10236 Asia/Vientiane +LB +3353+03530 Asia/Beirut +LC +1401-06100 America/St_Lucia +LI +4709+00931 Europe/Vaduz +LK +0656+07951 Asia/Colombo +LR +0618-01047 Africa/Monrovia +LS -2928+02730 Africa/Maseru +LT +5441+02519 Europe/Vilnius +LU +4936+00609 Europe/Luxembourg +LV +5657+02406 Europe/Riga +LY +3254+01311 Africa/Tripoli +MA +3339-00735 Africa/Casablanca +MC +4342+00723 Europe/Monaco +MD +4700+02850 Europe/Chisinau +ME +4226+01916 Europe/Podgorica +MF +1804-06305 America/Marigot +MG -1855+04731 Indian/Antananarivo +MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) +MH +0905+16720 Pacific/Kwajalein Kwajalein +MK +4159+02126 Europe/Skopje +ML +1239-00800 Africa/Bamako +MM +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) +MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan +MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar +MO +221150+1133230 Asia/Macau +MP +1512+14545 Pacific/Saipan +MQ +1436-06105 America/Martinique +MR +1806-01557 Africa/Nouakchott +MS +1643-06213 America/Montserrat +MT +3554+01431 Europe/Malta +MU -2010+05730 Indian/Mauritius +MV +0410+07330 Indian/Maldives +MW -1547+03500 Africa/Blantyre +MX +1924-09909 America/Mexico_City Central Time +MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo +MX +2058-08937 America/Merida Central Time - Campeche, Yucatan +MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo Leon, Tamaulipas (US border) +MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa +MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) +MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) +MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora +MX +3232-11701 America/Tijuana Pacific Time US - Baja California +MX +2048-10515 America/Bahia_Banderas Central Time - Bahia de Banderas +MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) +MY +0133+11020 Asia/Kuching Sabah, Sarawak +MZ -2558+03235 Africa/Maputo +NA -2234+01706 Africa/Windhoek +NC -2216+16627 Pacific/Noumea +NE +1331+00207 Africa/Niamey +NF -2903+16758 Pacific/Norfolk +NG +0627+00324 Africa/Lagos +NI +1209-08617 America/Managua +NL +5222+00454 Europe/Amsterdam +NO +5955+01045 Europe/Oslo +NP +2743+08519 Asia/Kathmandu +NR -0031+16655 Pacific/Nauru +NU -1901-16955 Pacific/Niue +NZ -3652+17446 Pacific/Auckland New Zealand (most areas) +NZ -4357-17633 Pacific/Chatham Chatham Islands +OM +2336+05835 Asia/Muscat +PA +0858-07932 America/Panama +PE -1203-07703 America/Lima +PF -1732-14934 Pacific/Tahiti Society Islands +PF -0900-13930 Pacific/Marquesas Marquesas Islands +PF -2308-13457 Pacific/Gambier Gambier Islands +PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG -0613+15534 Pacific/Bougainville Bougainville +PH +1435+12100 Asia/Manila +PK +2452+06703 Asia/Karachi +PL +5215+02100 Europe/Warsaw +PM +4703-05620 America/Miquelon +PN -2504-13005 Pacific/Pitcairn +PR +182806-0660622 America/Puerto_Rico +PS +3130+03428 Asia/Gaza Gaza Strip +PS +313200+0350542 Asia/Hebron West Bank +PT +3843-00908 Europe/Lisbon Portugal (mainland) +PT +3238-01654 Atlantic/Madeira Madeira Islands +PT +3744-02540 Atlantic/Azores Azores +PW +0720+13429 Pacific/Palau +PY -2516-05740 America/Asuncion +QA +2517+05132 Asia/Qatar +RE -2052+05528 Indian/Reunion +RO +4426+02606 Europe/Bucharest +RS +4450+02030 Europe/Belgrade +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# The obsolescent zone.tab format cannot represent Europe/Simferopol well. +# Put it in RU section and list as UA. See "territorial claims" above. +# Programs should use zone1970.tab instead; see above. +UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea +RW -0157+03004 Africa/Kigali +SA +2438+04643 Asia/Riyadh +SB -0932+16012 Pacific/Guadalcanal +SC -0440+05528 Indian/Mahe +SD +1536+03232 Africa/Khartoum +SE +5920+01803 Europe/Stockholm +SG +0117+10351 Asia/Singapore +SH -1555-00542 Atlantic/St_Helena +SI +4603+01431 Europe/Ljubljana +SJ +7800+01600 Arctic/Longyearbyen +SK +4809+01707 Europe/Bratislava +SL +0830-01315 Africa/Freetown +SM +4355+01228 Europe/San_Marino +SN +1440-01726 Africa/Dakar +SO +0204+04522 Africa/Mogadishu +SR +0550-05510 America/Paramaribo +SS +0451+03137 Africa/Juba +ST +0020+00644 Africa/Sao_Tome +SV +1342-08912 America/El_Salvador +SX +180305-0630250 America/Lower_Princes +SY +3330+03618 Asia/Damascus +SZ -2618+03106 Africa/Mbabane +TC +2128-07108 America/Grand_Turk +TD +1207+01503 Africa/Ndjamena +TF -492110+0701303 Indian/Kerguelen +TG +0608+00113 Africa/Lome +TH +1345+10031 Asia/Bangkok +TJ +3835+06848 Asia/Dushanbe +TK -0922-17114 Pacific/Fakaofo +TL -0833+12535 Asia/Dili +TM +3757+05823 Asia/Ashgabat +TN +3648+01011 Africa/Tunis +TO -210800-1751200 Pacific/Tongatapu +TR +4101+02858 Europe/Istanbul +TT +1039-06131 America/Port_of_Spain +TV -0831+17913 Pacific/Funafuti +TW +2503+12130 Asia/Taipei +TZ -0648+03917 Africa/Dar_es_Salaam +UA +5026+03031 Europe/Kiev Ukraine (most areas) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk +UG +0019+03225 Africa/Kampala +UM +2813-17722 Pacific/Midway Midway Islands +UM +1917+16637 Pacific/Wake Wake Island +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) +US +515248-1763929 America/Adak Aleutian Islands +US +211825-1575130 Pacific/Honolulu Hawaii +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) +VA +415408+0122711 Europe/Vatican +VC +1309-06114 America/St_Vincent +VE +1030-06656 America/Caracas +VG +1827-06437 America/Tortola +VI +1821-06456 America/St_Thomas +VN +1045+10640 Asia/Ho_Chi_Minh +VU -1740+16825 Pacific/Efate +WF -1318-17610 Pacific/Wallis +WS -1350-17144 Pacific/Apia +YE +1245+04512 Asia/Aden +YT -1247+04514 Indian/Mayotte +ZA -2615+02800 Africa/Johannesburg +ZM -1525+02817 Africa/Lusaka +ZW -1750+03103 Africa/Harare diff --git a/venv/Lib/site-packages/pytz/zoneinfo/zone1970.tab b/venv/Lib/site-packages/pytz/zoneinfo/zone1970.tab new file mode 100644 index 0000000..c614be8 --- /dev/null +++ b/venv/Lib/site-packages/pytz/zoneinfo/zone1970.tab @@ -0,0 +1,374 @@ +# tzdb timezone descriptions +# +# This file is in the public domain. +# +# From Paul Eggert (2018-06-27): +# This file contains a table where each row stands for a timezone where +# civil timestamps have agreed since 1970. Columns are separated by +# a single tab. Lines beginning with '#' are comments. All text uses +# UTF-8 encoding. The columns of the table are as follows: +# +# 1. The countries that overlap the timezone, as a comma-separated list +# of ISO 3166 2-character country codes. See the file 'iso3166.tab'. +# 2. Latitude and longitude of the timezone's principal location +# in ISO 6709 sign-degrees-minutes-seconds format, +# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS, +# first latitude (+ is north), then longitude (+ is east). +# 3. Timezone name used in value of TZ environment variable. +# Please see the theory.html file for how these names are chosen. +# If multiple timezones overlap a country, each has a row in the +# table, with each column 1 containing the country code. +# 4. Comments; present if and only if a country has multiple timezones. +# +# If a timezone covers multiple countries, the most-populous city is used, +# and that country is listed first in column 1; any other countries +# are listed alphabetically by country code. The table is sorted +# first by country code, then (if possible) by an order within the +# country that (1) makes some geographical sense, and (2) puts the +# most populous timezones first, where that does not contradict (1). +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#country- +#codes coordinates TZ comments +AD +4230+00131 Europe/Andorra +AE,OM +2518+05518 Asia/Dubai +AF +3431+06912 Asia/Kabul +AL +4120+01950 Europe/Tirane +AM +4011+04430 Asia/Yerevan +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok +AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) +AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) +AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) +AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) +AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) +AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) +AR -3319-06621 America/Argentina/San_Luis San Luis (SL) +AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) +AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) +AS,UM -1416-17042 Pacific/Pago_Pago Samoa, Midway +AT +4813+01620 Europe/Vienna +AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island +AU -4253+14719 Australia/Hobart Tasmania +AU -3749+14458 Australia/Melbourne Victoria +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) +AU -3455+13835 Australia/Adelaide South Australia +AU -1228+13050 Australia/Darwin Northern Territory +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) +AZ +4023+04951 Asia/Baku +BB +1306-05937 America/Barbados +BD +2343+09025 Asia/Dhaka +BE +5050+00420 Europe/Brussels +BG +4241+02319 Europe/Sofia +BM +3217-06446 Atlantic/Bermuda +BN +0456+11455 Asia/Brunei +BO -1630-06809 America/La_Paz +BR -0351-03225 America/Noronha Atlantic islands +BR -0127-04829 America/Belem Pará (east); Amapá +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) +BR -0803-03454 America/Recife Pernambuco +BR -0712-04812 America/Araguaina Tocantins +BR -0940-03543 America/Maceio Alagoas, Sergipe +BR -1259-03831 America/Bahia Bahia +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2027-05437 America/Campo_Grande Mato Grosso do Sul +BR -1535-05605 America/Cuiaba Mato Grosso +BR -0226-05452 America/Santarem Pará (west) +BR -0846-06354 America/Porto_Velho Rondônia +BR +0249-06040 America/Boa_Vista Roraima +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) +BR -0958-06748 America/Rio_Branco Acre +BT +2728+08939 Asia/Thimphu +BY +5354+02734 Europe/Minsk +BZ +1730-08812 America/Belize +CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas +CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) +CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) +CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +6227-11421 America/Yellowknife Mountain - NT (central) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +6043-13503 America/Whitehorse MST - Yukon (east) +CA +6404-13925 America/Dawson MST - Yukon (west) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CC -1210+09655 Indian/Cocos +CH,DE,LI +4723+00832 Europe/Zurich Swiss time +CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan +CK -2114-15946 Pacific/Rarotonga +CL -3327-07040 America/Santiago Chile (most areas) +CL -5309-07055 America/Punta_Arenas Region of Magallanes +CL -2709-10926 Pacific/Easter Easter Island +CN +3114+12128 Asia/Shanghai Beijing Time +CN +4348+08735 Asia/Urumqi Xinjiang Time +CO +0436-07405 America/Bogota +CR +0956-08405 America/Costa_Rica +CU +2308-08222 America/Havana +CV +1455-02331 Atlantic/Cape_Verde +CX -1025+10543 Indian/Christmas +CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3507+03357 Asia/Famagusta Northern Cyprus +CZ,SK +5005+01426 Europe/Prague +DE +5230+01322 Europe/Berlin Germany (most areas) +DK +5540+01235 Europe/Copenhagen +DO +1828-06954 America/Santo_Domingo +DZ +3647+00303 Africa/Algiers +EC -0210-07950 America/Guayaquil Ecuador (mainland) +EC -0054-08936 Pacific/Galapagos Galápagos Islands +EE +5925+02445 Europe/Tallinn +EG +3003+03115 Africa/Cairo +EH +2709-01312 Africa/El_Aaiun +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla +ES +2806-01524 Atlantic/Canary Canary Islands +FI,AX +6010+02458 Europe/Helsinki +FJ -1808+17825 Pacific/Fiji +FK -5142-05751 Atlantic/Stanley +FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap +FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape +FM +0519+16259 Pacific/Kosrae Kosrae +FO +6201-00646 Atlantic/Faroe +FR +4852+00220 Europe/Paris +GB,GG,IM,JE +513030-0000731 Europe/London +GE +4143+04449 Asia/Tbilisi +GF +0456-05220 America/Cayenne +GI +3608-00521 Europe/Gibraltar +GL +6411-05144 America/Nuuk Greenland (most areas) +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik +GR +3758+02343 Europe/Athens +GS -5416-03632 Atlantic/South_Georgia +GT +1438-09031 America/Guatemala +GU,MP +1328+14445 Pacific/Guam +GW +1151-01535 Africa/Bissau +GY +0648-05810 America/Guyana +HK +2217+11409 Asia/Hong_Kong +HN +1406-08713 America/Tegucigalpa +HT +1832-07220 America/Port-au-Prince +HU +4730+01905 Europe/Budapest +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +IE +5320-00615 Europe/Dublin +IL +314650+0351326 Asia/Jerusalem +IN +2232+08822 Asia/Kolkata +IO -0720+07225 Indian/Chagos +IQ +3321+04425 Asia/Baghdad +IR +3540+05126 Asia/Tehran +IS +6409-02151 Atlantic/Reykjavik +IT,SM,VA +4154+01229 Europe/Rome +JM +175805-0764736 America/Jamaica +JO +3157+03556 Asia/Amman +JP +353916+1394441 Asia/Tokyo +KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi +KG +4254+07436 Asia/Bishkek +KI +0125+17300 Pacific/Tarawa Gilbert Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands +KI +0152-15720 Pacific/Kiritimati Line Islands +KP +3901+12545 Asia/Pyongyang +KR +3733+12658 Asia/Seoul +KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau +KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur'yev +KZ +5113+05121 Asia/Oral West Kazakhstan +LB +3353+03530 Asia/Beirut +LK +0656+07951 Asia/Colombo +LR +0618-01047 Africa/Monrovia +LT +5441+02519 Europe/Vilnius +LU +4936+00609 Europe/Luxembourg +LV +5657+02406 Europe/Riga +LY +3254+01311 Africa/Tripoli +MA +3339-00735 Africa/Casablanca +MC +4342+00723 Europe/Monaco +MD +4700+02850 Europe/Chisinau +MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) +MH +0905+16720 Pacific/Kwajalein Kwajalein +MM +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) +MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan +MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar +MO +221150+1133230 Asia/Macau +MQ +1436-06105 America/Martinique +MT +3554+01431 Europe/Malta +MU -2010+05730 Indian/Mauritius +MV +0410+07330 Indian/Maldives +MX +1924-09909 America/Mexico_City Central Time +MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo +MX +2058-08937 America/Merida Central Time - Campeche, Yucatán +MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo León, Tamaulipas (US border) +MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa +MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) +MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) +MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora +MX +3232-11701 America/Tijuana Pacific Time US - Baja California +MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas +MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) +MY +0133+11020 Asia/Kuching Sabah, Sarawak +MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time +NA -2234+01706 Africa/Windhoek +NC -2216+16627 Pacific/Noumea +NF -2903+16758 Pacific/Norfolk +NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time +NI +1209-08617 America/Managua +NL +5222+00454 Europe/Amsterdam +NO,SJ +5955+01045 Europe/Oslo +NP +2743+08519 Asia/Kathmandu +NR -0031+16655 Pacific/Nauru +NU -1901-16955 Pacific/Niue +NZ,AQ -3652+17446 Pacific/Auckland New Zealand time +NZ -4357-17633 Pacific/Chatham Chatham Islands +PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H) +PE -1203-07703 America/Lima +PF -1732-14934 Pacific/Tahiti Society Islands +PF -0900-13930 Pacific/Marquesas Marquesas Islands +PF -2308-13457 Pacific/Gambier Gambier Islands +PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville +PG -0613+15534 Pacific/Bougainville Bougainville +PH +1435+12100 Asia/Manila +PK +2452+06703 Asia/Karachi +PL +5215+02100 Europe/Warsaw +PM +4703-05620 America/Miquelon +PN -2504-13005 Pacific/Pitcairn +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST +PS +3130+03428 Asia/Gaza Gaza Strip +PS +313200+0350542 Asia/Hebron West Bank +PT +3843-00908 Europe/Lisbon Portugal (mainland) +PT +3238-01654 Atlantic/Madeira Madeira Islands +PT +3744-02540 Atlantic/Azores Azores +PW +0720+13429 Pacific/Palau +PY -2516-05740 America/Asuncion +QA,BH +2517+05132 Asia/Qatar +RE,TF -2052+05528 Indian/Reunion Réunion, Crozet, Scattered Islands +RO +4426+02606 Europe/Bucharest +RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# Mention RU and UA alphabetically. See "territorial claims" above. +RU,UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea +SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa +SB -0932+16012 Pacific/Guadalcanal +SC -0440+05528 Indian/Mahe +SD +1536+03232 Africa/Khartoum +SE +5920+01803 Europe/Stockholm +SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia +SR +0550-05510 America/Paramaribo +SS +0451+03137 Africa/Juba +ST +0020+00644 Africa/Sao_Tome +SV +1342-08912 America/El_Salvador +SY +3330+03618 Asia/Damascus +TC +2128-07108 America/Grand_Turk +TD +1207+01503 Africa/Ndjamena +TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul Island, Amsterdam Island +TH,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas) +TJ +3835+06848 Asia/Dushanbe +TK -0922-17114 Pacific/Fakaofo +TL -0833+12535 Asia/Dili +TM +3757+05823 Asia/Ashgabat +TN +3648+01011 Africa/Tunis +TO -210800-1751200 Pacific/Tongatapu +TR +4101+02858 Europe/Istanbul +TV -0831+17913 Pacific/Funafuti +TW +2503+12130 Asia/Taipei +UA +5026+03031 Europe/Kiev Ukraine (most areas) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk +UM +1917+16637 Pacific/Wake Wake Island +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) +US +515248-1763929 America/Adak Aleutian Islands +US,UM +211825-1575130 Pacific/Honolulu Hawaii +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) +VE +1030-06656 America/Caracas +VN +1045+10640 Asia/Ho_Chi_Minh Vietnam (south) +VU -1740+16825 Pacific/Efate +WF -1318-17610 Pacific/Wallis +WS -1350-17144 Pacific/Apia +ZA,LS,SZ -2615+02800 Africa/Johannesburg diff --git a/venv/Lib/site-packages/rest_framework/__init__.py b/venv/Lib/site-packages/rest_framework/__init__.py new file mode 100644 index 0000000..8b0679e --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/__init__.py @@ -0,0 +1,37 @@ +r""" +______ _____ _____ _____ __ +| ___ \ ___/ ___|_ _| / _| | | +| |_/ / |__ \ `--. | | | |_ _ __ __ _ _ __ ___ _____ _____ _ __| |__ +| /| __| `--. \ | | | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / +| |\ \| |___/\__/ / | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < +\_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_| +""" + +import django + +__title__ = 'Django REST framework' +__version__ = '3.13.1' +__author__ = 'Tom Christie' +__license__ = 'BSD 3-Clause' +__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' + +# Version synonym +VERSION = __version__ + +# Header encoding (see RFC5987) +HTTP_HEADER_ENCODING = 'iso-8859-1' + +# Default datetime input and output formats +ISO_8601 = 'iso-8601' + + +if django.VERSION < (3, 2): + default_app_config = 'rest_framework.apps.RestFrameworkConfig' + + +class RemovedInDRF313Warning(DeprecationWarning): + pass + + +class RemovedInDRF314Warning(PendingDeprecationWarning): + pass diff --git a/venv/Lib/site-packages/rest_framework/apps.py b/venv/Lib/site-packages/rest_framework/apps.py new file mode 100644 index 0000000..f6013eb --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/apps.py @@ -0,0 +1,10 @@ +from django.apps import AppConfig + + +class RestFrameworkConfig(AppConfig): + name = 'rest_framework' + verbose_name = "Django REST framework" + + def ready(self): + # Add System checks + from .checks import pagination_system_check # NOQA diff --git a/venv/Lib/site-packages/rest_framework/authentication.py b/venv/Lib/site-packages/rest_framework/authentication.py new file mode 100644 index 0000000..382abf1 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authentication.py @@ -0,0 +1,232 @@ +""" +Provides various authentication policies. +""" +import base64 +import binascii + +from django.contrib.auth import authenticate, get_user_model +from django.middleware.csrf import CsrfViewMiddleware +from django.utils.translation import gettext_lazy as _ + +from rest_framework import HTTP_HEADER_ENCODING, exceptions + + +def get_authorization_header(request): + """ + Return request's 'Authorization:' header, as a bytestring. + + Hide some test client ickyness where the header can be unicode. + """ + auth = request.META.get('HTTP_AUTHORIZATION', b'') + if isinstance(auth, str): + # Work around django test client oddness + auth = auth.encode(HTTP_HEADER_ENCODING) + return auth + + +class CSRFCheck(CsrfViewMiddleware): + def _reject(self, request, reason): + # Return the failure reason instead of an HttpResponse + return reason + + +class BaseAuthentication: + """ + All authentication classes should extend BaseAuthentication. + """ + + def authenticate(self, request): + """ + Authenticate the request and return a two-tuple of (user, token). + """ + raise NotImplementedError(".authenticate() must be overridden.") + + def authenticate_header(self, request): + """ + Return a string to be used as the value of the `WWW-Authenticate` + header in a `401 Unauthenticated` response, or `None` if the + authentication scheme should return `403 Permission Denied` responses. + """ + pass + + +class BasicAuthentication(BaseAuthentication): + """ + HTTP Basic authentication against username/password. + """ + www_authenticate_realm = 'api' + + def authenticate(self, request): + """ + Returns a `User` if a correct username and password have been supplied + using HTTP Basic authentication. Otherwise returns `None`. + """ + auth = get_authorization_header(request).split() + + if not auth or auth[0].lower() != b'basic': + return None + + if len(auth) == 1: + msg = _('Invalid basic header. No credentials provided.') + raise exceptions.AuthenticationFailed(msg) + elif len(auth) > 2: + msg = _('Invalid basic header. Credentials string should not contain spaces.') + raise exceptions.AuthenticationFailed(msg) + + try: + try: + auth_decoded = base64.b64decode(auth[1]).decode('utf-8') + except UnicodeDecodeError: + auth_decoded = base64.b64decode(auth[1]).decode('latin-1') + auth_parts = auth_decoded.partition(':') + except (TypeError, UnicodeDecodeError, binascii.Error): + msg = _('Invalid basic header. Credentials not correctly base64 encoded.') + raise exceptions.AuthenticationFailed(msg) + + userid, password = auth_parts[0], auth_parts[2] + return self.authenticate_credentials(userid, password, request) + + def authenticate_credentials(self, userid, password, request=None): + """ + Authenticate the userid and password against username and password + with optional request for context. + """ + credentials = { + get_user_model().USERNAME_FIELD: userid, + 'password': password + } + user = authenticate(request=request, **credentials) + + if user is None: + raise exceptions.AuthenticationFailed(_('Invalid username/password.')) + + if not user.is_active: + raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + + return (user, None) + + def authenticate_header(self, request): + return 'Basic realm="%s"' % self.www_authenticate_realm + + +class SessionAuthentication(BaseAuthentication): + """ + Use Django's session framework for authentication. + """ + + def authenticate(self, request): + """ + Returns a `User` if the request session currently has a logged in user. + Otherwise returns `None`. + """ + + # Get the session-based user from the underlying HttpRequest object + user = getattr(request._request, 'user', None) + + # Unauthenticated, CSRF validation not required + if not user or not user.is_active: + return None + + self.enforce_csrf(request) + + # CSRF passed with authenticated user + return (user, None) + + def enforce_csrf(self, request): + """ + Enforce CSRF validation for session based authentication. + """ + def dummy_get_response(request): # pragma: no cover + return None + + check = CSRFCheck(dummy_get_response) + # populates request.META['CSRF_COOKIE'], which is used in process_view() + check.process_request(request) + reason = check.process_view(request, None, (), {}) + if reason: + # CSRF failed, bail with explicit error message + raise exceptions.PermissionDenied('CSRF Failed: %s' % reason) + + +class TokenAuthentication(BaseAuthentication): + """ + Simple token based authentication. + + Clients should authenticate by passing the token key in the "Authorization" + HTTP header, prepended with the string "Token ". For example: + + Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a + """ + + keyword = 'Token' + model = None + + def get_model(self): + if self.model is not None: + return self.model + from rest_framework.authtoken.models import Token + return Token + + """ + A custom token model may be used, but must have the following properties. + + * key -- The string identifying the token + * user -- The user to which the token belongs + """ + + def authenticate(self, request): + auth = get_authorization_header(request).split() + + if not auth or auth[0].lower() != self.keyword.lower().encode(): + return None + + if len(auth) == 1: + msg = _('Invalid token header. No credentials provided.') + raise exceptions.AuthenticationFailed(msg) + elif len(auth) > 2: + msg = _('Invalid token header. Token string should not contain spaces.') + raise exceptions.AuthenticationFailed(msg) + + try: + token = auth[1].decode() + except UnicodeError: + msg = _('Invalid token header. Token string should not contain invalid characters.') + raise exceptions.AuthenticationFailed(msg) + + return self.authenticate_credentials(token) + + def authenticate_credentials(self, key): + model = self.get_model() + try: + token = model.objects.select_related('user').get(key=key) + except model.DoesNotExist: + raise exceptions.AuthenticationFailed(_('Invalid token.')) + + if not token.user.is_active: + raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + + return (token.user, token) + + def authenticate_header(self, request): + return self.keyword + + +class RemoteUserAuthentication(BaseAuthentication): + """ + REMOTE_USER authentication. + + To use this, set up your web server to perform authentication, which will + set the REMOTE_USER environment variable. You will need to have + 'django.contrib.auth.backends.RemoteUserBackend in your + AUTHENTICATION_BACKENDS setting + """ + + # Name of request header to grab username from. This will be the key as + # used in the request.META dictionary, i.e. the normalization of headers to + # all uppercase and the addition of "HTTP_" prefix apply. + header = "REMOTE_USER" + + def authenticate(self, request): + user = authenticate(request=request, remote_user=request.META.get(self.header)) + if user and user.is_active: + return (user, None) diff --git a/venv/Lib/site-packages/rest_framework/authtoken/__init__.py b/venv/Lib/site-packages/rest_framework/authtoken/__init__.py new file mode 100644 index 0000000..285fe15 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/__init__.py @@ -0,0 +1,4 @@ +import django + +if django.VERSION < (3, 2): + default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig' diff --git a/venv/Lib/site-packages/rest_framework/authtoken/admin.py b/venv/Lib/site-packages/rest_framework/authtoken/admin.py new file mode 100644 index 0000000..b359e4c --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/admin.py @@ -0,0 +1,51 @@ +from django.contrib import admin +from django.contrib.admin.utils import quote +from django.contrib.admin.views.main import ChangeList +from django.contrib.auth import get_user_model +from django.core.exceptions import ValidationError +from django.urls import reverse + +from rest_framework.authtoken.models import Token, TokenProxy + +User = get_user_model() + + +class TokenChangeList(ChangeList): + """Map to matching User id""" + def url_for_result(self, result): + pk = result.user.pk + return reverse('admin:%s_%s_change' % (self.opts.app_label, + self.opts.model_name), + args=(quote(pk),), + current_app=self.model_admin.admin_site.name) + + +class TokenAdmin(admin.ModelAdmin): + list_display = ('key', 'user', 'created') + fields = ('user',) + ordering = ('-created',) + actions = None # Actions not compatible with mapped IDs. + + def get_changelist(self, request, **kwargs): + return TokenChangeList + + def get_object(self, request, object_id, from_field=None): + """ + Map from User ID to matching Token. + """ + queryset = self.get_queryset(request) + field = User._meta.pk + try: + object_id = field.to_python(object_id) + user = User.objects.get(**{field.name: object_id}) + return queryset.get(user=user) + except (queryset.model.DoesNotExist, User.DoesNotExist, ValidationError, ValueError): + return None + + def delete_model(self, request, obj): + # Map back to actual Token, since delete() uses pk. + token = Token.objects.get(key=obj.key) + return super().delete_model(request, token) + + +admin.site.register(TokenProxy, TokenAdmin) diff --git a/venv/Lib/site-packages/rest_framework/authtoken/apps.py b/venv/Lib/site-packages/rest_framework/authtoken/apps.py new file mode 100644 index 0000000..f90fe96 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class AuthTokenConfig(AppConfig): + name = 'rest_framework.authtoken' + verbose_name = _("Auth Token") diff --git a/venv/Lib/site-packages/rest_framework/authtoken/management/__init__.py b/venv/Lib/site-packages/rest_framework/authtoken/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/authtoken/management/commands/__init__.py b/venv/Lib/site-packages/rest_framework/authtoken/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/authtoken/management/commands/drf_create_token.py b/venv/Lib/site-packages/rest_framework/authtoken/management/commands/drf_create_token.py new file mode 100644 index 0000000..3d65392 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/management/commands/drf_create_token.py @@ -0,0 +1,45 @@ +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand, CommandError + +from rest_framework.authtoken.models import Token + +UserModel = get_user_model() + + +class Command(BaseCommand): + help = 'Create DRF Token for a given user' + + def create_user_token(self, username, reset_token): + user = UserModel._default_manager.get_by_natural_key(username) + + if reset_token: + Token.objects.filter(user=user).delete() + + token = Token.objects.get_or_create(user=user) + return token[0] + + def add_arguments(self, parser): + parser.add_argument('username', type=str) + + parser.add_argument( + '-r', + '--reset', + action='store_true', + dest='reset_token', + default=False, + help='Reset existing User token and create a new one', + ) + + def handle(self, *args, **options): + username = options['username'] + reset_token = options['reset_token'] + + try: + token = self.create_user_token(username, reset_token) + except UserModel.DoesNotExist: + raise CommandError( + 'Cannot create the Token: user {} does not exist'.format( + username) + ) + self.stdout.write( + 'Generated token {} for user {}'.format(token.key, username)) diff --git a/venv/Lib/site-packages/rest_framework/authtoken/migrations/0001_initial.py b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0001_initial.py new file mode 100644 index 0000000..6a46ccf --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0001_initial.py @@ -0,0 +1,23 @@ +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Token', + fields=[ + ('key', models.CharField(primary_key=True, serialize=False, max_length=40)), + ('created', models.DateTimeField(auto_now_add=True)), + ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='auth_token', on_delete=models.CASCADE)), + ], + options={ + }, + bases=(models.Model,), + ), + ] diff --git a/venv/Lib/site-packages/rest_framework/authtoken/migrations/0002_auto_20160226_1747.py b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0002_auto_20160226_1747.py new file mode 100644 index 0000000..4311909 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0002_auto_20160226_1747.py @@ -0,0 +1,31 @@ +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authtoken', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='token', + options={'verbose_name_plural': 'Tokens', 'verbose_name': 'Token'}, + ), + migrations.AlterField( + model_name='token', + name='created', + field=models.DateTimeField(verbose_name='Created', auto_now_add=True), + ), + migrations.AlterField( + model_name='token', + name='key', + field=models.CharField(verbose_name='Key', max_length=40, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='token', + name='user', + field=models.OneToOneField(to=settings.AUTH_USER_MODEL, verbose_name='User', related_name='auth_token', on_delete=models.CASCADE), + ), + ] diff --git a/venv/Lib/site-packages/rest_framework/authtoken/migrations/0003_tokenproxy.py b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0003_tokenproxy.py new file mode 100644 index 0000000..79405a7 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/migrations/0003_tokenproxy.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.1 on 2020-09-28 09:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('authtoken', '0002_auto_20160226_1747'), + ] + + operations = [ + migrations.CreateModel( + name='TokenProxy', + fields=[ + ], + options={ + 'verbose_name': 'token', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('authtoken.token',), + ), + ] diff --git a/venv/Lib/site-packages/rest_framework/authtoken/migrations/__init__.py b/venv/Lib/site-packages/rest_framework/authtoken/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/authtoken/models.py b/venv/Lib/site-packages/rest_framework/authtoken/models.py new file mode 100644 index 0000000..5a143d9 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/models.py @@ -0,0 +1,54 @@ +import binascii +import os + +from django.conf import settings +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Token(models.Model): + """ + The default authorization token model. + """ + key = models.CharField(_("Key"), max_length=40, primary_key=True) + user = models.OneToOneField( + settings.AUTH_USER_MODEL, related_name='auth_token', + on_delete=models.CASCADE, verbose_name=_("User") + ) + created = models.DateTimeField(_("Created"), auto_now_add=True) + + class Meta: + # Work around for a bug in Django: + # https://code.djangoproject.com/ticket/19422 + # + # Also see corresponding ticket: + # https://github.com/encode/django-rest-framework/issues/705 + abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS + verbose_name = _("Token") + verbose_name_plural = _("Tokens") + + def save(self, *args, **kwargs): + if not self.key: + self.key = self.generate_key() + return super().save(*args, **kwargs) + + @classmethod + def generate_key(cls): + return binascii.hexlify(os.urandom(20)).decode() + + def __str__(self): + return self.key + + +class TokenProxy(Token): + """ + Proxy mapping pk to user pk for use in admin. + """ + @property + def pk(self): + return self.user_id + + class Meta: + proxy = 'rest_framework.authtoken' in settings.INSTALLED_APPS + abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS + verbose_name = "token" diff --git a/venv/Lib/site-packages/rest_framework/authtoken/serializers.py b/venv/Lib/site-packages/rest_framework/authtoken/serializers.py new file mode 100644 index 0000000..63e64d6 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/serializers.py @@ -0,0 +1,42 @@ +from django.contrib.auth import authenticate +from django.utils.translation import gettext_lazy as _ + +from rest_framework import serializers + + +class AuthTokenSerializer(serializers.Serializer): + username = serializers.CharField( + label=_("Username"), + write_only=True + ) + password = serializers.CharField( + label=_("Password"), + style={'input_type': 'password'}, + trim_whitespace=False, + write_only=True + ) + token = serializers.CharField( + label=_("Token"), + read_only=True + ) + + def validate(self, attrs): + username = attrs.get('username') + password = attrs.get('password') + + if username and password: + user = authenticate(request=self.context.get('request'), + username=username, password=password) + + # The authenticate call simply returns None for is_active=False + # users. (Assuming the default ModelBackend authentication + # backend.) + if not user: + msg = _('Unable to log in with provided credentials.') + raise serializers.ValidationError(msg, code='authorization') + else: + msg = _('Must include "username" and "password".') + raise serializers.ValidationError(msg, code='authorization') + + attrs['user'] = user + return attrs diff --git a/venv/Lib/site-packages/rest_framework/authtoken/views.py b/venv/Lib/site-packages/rest_framework/authtoken/views.py new file mode 100644 index 0000000..50f9acb --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/authtoken/views.py @@ -0,0 +1,62 @@ +from rest_framework import parsers, renderers +from rest_framework.authtoken.models import Token +from rest_framework.authtoken.serializers import AuthTokenSerializer +from rest_framework.compat import coreapi, coreschema +from rest_framework.response import Response +from rest_framework.schemas import ManualSchema +from rest_framework.schemas import coreapi as coreapi_schema +from rest_framework.views import APIView + + +class ObtainAuthToken(APIView): + throttle_classes = () + permission_classes = () + parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,) + renderer_classes = (renderers.JSONRenderer,) + serializer_class = AuthTokenSerializer + + if coreapi_schema.is_enabled(): + schema = ManualSchema( + fields=[ + coreapi.Field( + name="username", + required=True, + location='form', + schema=coreschema.String( + title="Username", + description="Valid username for authentication", + ), + ), + coreapi.Field( + name="password", + required=True, + location='form', + schema=coreschema.String( + title="Password", + description="Valid password for authentication", + ), + ), + ], + encoding="application/json", + ) + + def get_serializer_context(self): + return { + 'request': self.request, + 'format': self.format_kwarg, + 'view': self + } + + def get_serializer(self, *args, **kwargs): + kwargs['context'] = self.get_serializer_context() + return self.serializer_class(*args, **kwargs) + + def post(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data['user'] + token, created = Token.objects.get_or_create(user=user) + return Response({'token': token.key}) + + +obtain_auth_token = ObtainAuthToken.as_view() diff --git a/venv/Lib/site-packages/rest_framework/checks.py b/venv/Lib/site-packages/rest_framework/checks.py new file mode 100644 index 0000000..d5d77bc --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/checks.py @@ -0,0 +1,21 @@ +from django.core.checks import Tags, Warning, register + + +@register(Tags.compatibility) +def pagination_system_check(app_configs, **kwargs): + errors = [] + # Use of default page size setting requires a default Paginator class + from rest_framework.settings import api_settings + if api_settings.PAGE_SIZE and not api_settings.DEFAULT_PAGINATION_CLASS: + errors.append( + Warning( + "You have specified a default PAGE_SIZE pagination rest_framework setting, " + "without specifying also a DEFAULT_PAGINATION_CLASS.", + hint="The default for DEFAULT_PAGINATION_CLASS is None. " + "In previous versions this was PageNumberPagination. " + "If you wish to define PAGE_SIZE globally whilst defining " + "pagination_class on a per-view basis you may silence this check.", + id="rest_framework.W001" + ) + ) + return errors diff --git a/venv/Lib/site-packages/rest_framework/compat.py b/venv/Lib/site-packages/rest_framework/compat.py new file mode 100644 index 0000000..4bae772 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/compat.py @@ -0,0 +1,159 @@ +""" +The `compat` module provides support for backwards compatibility with older +versions of Django/Python, and compatibility wrappers around optional packages. +""" +from django.conf import settings +from django.views.generic import View + + +def unicode_http_header(value): + # Coerce HTTP header value to unicode. + if isinstance(value, bytes): + return value.decode('iso-8859-1') + return value + + +def distinct(queryset, base): + if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": + # distinct analogue for Oracle users + return base.filter(pk__in=set(queryset.values_list('pk', flat=True))) + return queryset.distinct() + + +# django.contrib.postgres requires psycopg2 +try: + from django.contrib.postgres import fields as postgres_fields +except ImportError: + postgres_fields = None + + +# coreapi is required for CoreAPI schema generation +try: + import coreapi +except ImportError: + coreapi = None + +# uritemplate is required for OpenAPI and CoreAPI schema generation +try: + import uritemplate +except ImportError: + uritemplate = None + + +# coreschema is optional +try: + import coreschema +except ImportError: + coreschema = None + + +# pyyaml is optional +try: + import yaml +except ImportError: + yaml = None + + +# requests is optional +try: + import requests +except ImportError: + requests = None + + +# PATCH method is not implemented by Django +if 'patch' not in View.http_method_names: + View.http_method_names = View.http_method_names + ['patch'] + + +# Markdown is optional (version 3.0+ required) +try: + import markdown + + HEADERID_EXT_PATH = 'markdown.extensions.toc' + LEVEL_PARAM = 'baselevel' + + def apply_markdown(text): + """ + Simple wrapper around :func:`markdown.markdown` to set the base level + of '#' style headers to <h2>. + """ + extensions = [HEADERID_EXT_PATH] + extension_configs = { + HEADERID_EXT_PATH: { + LEVEL_PARAM: '2' + } + } + md = markdown.Markdown( + extensions=extensions, extension_configs=extension_configs + ) + md_filter_add_syntax_highlight(md) + return md.convert(text) +except ImportError: + apply_markdown = None + markdown = None + + +try: + import pygments + from pygments.formatters import HtmlFormatter + from pygments.lexers import TextLexer, get_lexer_by_name + + def pygments_highlight(text, lang, style): + lexer = get_lexer_by_name(lang, stripall=False) + formatter = HtmlFormatter(nowrap=True, style=style) + return pygments.highlight(text, lexer, formatter) + + def pygments_css(style): + formatter = HtmlFormatter(style=style) + return formatter.get_style_defs('.highlight') + +except ImportError: + pygments = None + + def pygments_highlight(text, lang, style): + return text + + def pygments_css(style): + return None + +if markdown is not None and pygments is not None: + # starting from this blogpost and modified to support current markdown extensions API + # https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/ + + import re + + from markdown.preprocessors import Preprocessor + + class CodeBlockPreprocessor(Preprocessor): + pattern = re.compile( + r'^\s*``` *([^\n]+)\n(.+?)^\s*```', re.M | re.S) + + formatter = HtmlFormatter() + + def run(self, lines): + def repl(m): + try: + lexer = get_lexer_by_name(m.group(1)) + except (ValueError, NameError): + lexer = TextLexer() + code = m.group(2).replace('\t', ' ') + code = pygments.highlight(code, lexer, self.formatter) + code = code.replace('\n\n', '\n \n').replace('\n', '<br />').replace('\\@', '@') + return '\n\n%s\n\n' % code + ret = self.pattern.sub(repl, "\n".join(lines)) + return ret.split("\n") + + def md_filter_add_syntax_highlight(md): + md.preprocessors.register(CodeBlockPreprocessor(), 'highlight', 40) + return True +else: + def md_filter_add_syntax_highlight(md): + return False + + +# `separators` argument to `json.dumps()` differs between 2.x and 3.x +# See: https://bugs.python.org/issue22767 +SHORT_SEPARATORS = (',', ':') +LONG_SEPARATORS = (', ', ': ') +INDENT_SEPARATORS = (',', ': ') diff --git a/venv/Lib/site-packages/rest_framework/decorators.py b/venv/Lib/site-packages/rest_framework/decorators.py new file mode 100644 index 0000000..30b9d84 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/decorators.py @@ -0,0 +1,233 @@ +""" +The most important decorator in this module is `@api_view`, which is used +for writing function-based views with REST framework. + +There are also various decorators for setting the API policies on function +based views, as well as the `@action` decorator, which is used to annotate +methods on viewsets that should be included by routers. +""" +import types + +from django.forms.utils import pretty_name + +from rest_framework.views import APIView + + +def api_view(http_method_names=None): + """ + Decorator that converts a function-based view into an APIView subclass. + Takes a list of allowed methods for the view as an argument. + """ + http_method_names = ['GET'] if (http_method_names is None) else http_method_names + + def decorator(func): + + WrappedAPIView = type( + 'WrappedAPIView', + (APIView,), + {'__doc__': func.__doc__} + ) + + # Note, the above allows us to set the docstring. + # It is the equivalent of: + # + # class WrappedAPIView(APIView): + # pass + # WrappedAPIView.__doc__ = func.doc <--- Not possible to do this + + # api_view applied without (method_names) + assert not(isinstance(http_method_names, types.FunctionType)), \ + '@api_view missing list of allowed HTTP methods' + + # api_view applied with eg. string instead of list of strings + assert isinstance(http_method_names, (list, tuple)), \ + '@api_view expected a list of strings, received %s' % type(http_method_names).__name__ + + allowed_methods = set(http_method_names) | {'options'} + WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods] + + def handler(self, *args, **kwargs): + return func(*args, **kwargs) + + for method in http_method_names: + setattr(WrappedAPIView, method.lower(), handler) + + WrappedAPIView.__name__ = func.__name__ + WrappedAPIView.__module__ = func.__module__ + + WrappedAPIView.renderer_classes = getattr(func, 'renderer_classes', + APIView.renderer_classes) + + WrappedAPIView.parser_classes = getattr(func, 'parser_classes', + APIView.parser_classes) + + WrappedAPIView.authentication_classes = getattr(func, 'authentication_classes', + APIView.authentication_classes) + + WrappedAPIView.throttle_classes = getattr(func, 'throttle_classes', + APIView.throttle_classes) + + WrappedAPIView.permission_classes = getattr(func, 'permission_classes', + APIView.permission_classes) + + WrappedAPIView.schema = getattr(func, 'schema', + APIView.schema) + + return WrappedAPIView.as_view() + + return decorator + + +def renderer_classes(renderer_classes): + def decorator(func): + func.renderer_classes = renderer_classes + return func + return decorator + + +def parser_classes(parser_classes): + def decorator(func): + func.parser_classes = parser_classes + return func + return decorator + + +def authentication_classes(authentication_classes): + def decorator(func): + func.authentication_classes = authentication_classes + return func + return decorator + + +def throttle_classes(throttle_classes): + def decorator(func): + func.throttle_classes = throttle_classes + return func + return decorator + + +def permission_classes(permission_classes): + def decorator(func): + func.permission_classes = permission_classes + return func + return decorator + + +def schema(view_inspector): + def decorator(func): + func.schema = view_inspector + return func + return decorator + + +def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): + """ + Mark a ViewSet method as a routable action. + + `@action`-decorated functions will be endowed with a `mapping` property, + a `MethodMapper` that can be used to add additional method-based behaviors + on the routed action. + + :param methods: A list of HTTP method names this action responds to. + Defaults to GET only. + :param detail: Required. Determines whether this action applies to + instance/detail requests or collection/list requests. + :param url_path: Define the URL segment for this action. Defaults to the + name of the method decorated. + :param url_name: Define the internal (`reverse`) URL name for this action. + Defaults to the name of the method decorated with underscores + replaced with dashes. + :param kwargs: Additional properties to set on the view. This can be used + to override viewset-level *_classes settings, equivalent to + how the `@renderer_classes` etc. decorators work for function- + based API views. + """ + methods = ['get'] if (methods is None) else methods + methods = [method.lower() for method in methods] + + assert detail is not None, ( + "@action() missing required argument: 'detail'" + ) + + # name and suffix are mutually exclusive + if 'name' in kwargs and 'suffix' in kwargs: + raise TypeError("`name` and `suffix` are mutually exclusive arguments.") + + def decorator(func): + func.mapping = MethodMapper(func, methods) + + func.detail = detail + func.url_path = url_path if url_path else func.__name__ + func.url_name = url_name if url_name else func.__name__.replace('_', '-') + + # These kwargs will end up being passed to `ViewSet.as_view()` within + # the router, which eventually delegates to Django's CBV `View`, + # which assigns them as instance attributes for each request. + func.kwargs = kwargs + + # Set descriptive arguments for viewsets + if 'name' not in kwargs and 'suffix' not in kwargs: + func.kwargs['name'] = pretty_name(func.__name__) + func.kwargs['description'] = func.__doc__ or None + + return func + return decorator + + +class MethodMapper(dict): + """ + Enables mapping HTTP methods to different ViewSet methods for a single, + logical action. + + Example usage: + + class MyViewSet(ViewSet): + + @action(detail=False) + def example(self, request, **kwargs): + ... + + @example.mapping.post + def create_example(self, request, **kwargs): + ... + """ + + def __init__(self, action, methods): + self.action = action + for method in methods: + self[method] = self.action.__name__ + + def _map(self, method, func): + assert method not in self, ( + "Method '%s' has already been mapped to '.%s'." % (method, self[method])) + assert func.__name__ != self.action.__name__, ( + "Method mapping does not behave like the property decorator. You " + "cannot use the same method name for each mapping declaration.") + + self[method] = func.__name__ + + return func + + def get(self, func): + return self._map('get', func) + + def post(self, func): + return self._map('post', func) + + def put(self, func): + return self._map('put', func) + + def patch(self, func): + return self._map('patch', func) + + def delete(self, func): + return self._map('delete', func) + + def head(self, func): + return self._map('head', func) + + def options(self, func): + return self._map('options', func) + + def trace(self, func): + return self._map('trace', func) diff --git a/venv/Lib/site-packages/rest_framework/documentation.py b/venv/Lib/site-packages/rest_framework/documentation.py new file mode 100644 index 0000000..53e5ab5 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/documentation.py @@ -0,0 +1,88 @@ +from django.urls import include, path + +from rest_framework.renderers import ( + CoreJSONRenderer, DocumentationRenderer, SchemaJSRenderer +) +from rest_framework.schemas import SchemaGenerator, get_schema_view +from rest_framework.settings import api_settings + + +def get_docs_view( + title=None, description=None, schema_url=None, urlconf=None, + public=True, patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES, + renderer_classes=None): + + if renderer_classes is None: + renderer_classes = [DocumentationRenderer, CoreJSONRenderer] + + return get_schema_view( + title=title, + url=schema_url, + urlconf=urlconf, + description=description, + renderer_classes=renderer_classes, + public=public, + patterns=patterns, + generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, + ) + + +def get_schemajs_view( + title=None, description=None, schema_url=None, urlconf=None, + public=True, patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES): + renderer_classes = [SchemaJSRenderer] + + return get_schema_view( + title=title, + url=schema_url, + urlconf=urlconf, + description=description, + renderer_classes=renderer_classes, + public=public, + patterns=patterns, + generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, + ) + + +def include_docs_urls( + title=None, description=None, schema_url=None, urlconf=None, + public=True, patterns=None, generator_class=SchemaGenerator, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES, + renderer_classes=None): + docs_view = get_docs_view( + title=title, + description=description, + schema_url=schema_url, + urlconf=urlconf, + public=public, + patterns=patterns, + generator_class=generator_class, + authentication_classes=authentication_classes, + renderer_classes=renderer_classes, + permission_classes=permission_classes, + ) + schema_js_view = get_schemajs_view( + title=title, + description=description, + schema_url=schema_url, + urlconf=urlconf, + public=public, + patterns=patterns, + generator_class=generator_class, + authentication_classes=authentication_classes, + permission_classes=permission_classes, + ) + urls = [ + path('', docs_view, name='docs-index'), + path('schema.js', schema_js_view, name='schema-js') + ] + return include((urls, 'api-docs'), namespace='api-docs') diff --git a/venv/Lib/site-packages/rest_framework/exceptions.py b/venv/Lib/site-packages/rest_framework/exceptions.py new file mode 100644 index 0000000..fee8f02 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/exceptions.py @@ -0,0 +1,261 @@ +""" +Handled exceptions raised by REST framework. + +In addition Django's built in 403 and 404 exceptions are handled. +(`django.http.Http404` and `django.core.exceptions.PermissionDenied`) +""" +import math + +from django.http import JsonResponse +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext + +from rest_framework import status +from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList + + +def _get_error_details(data, default_code=None): + """ + Descend into a nested data structure, forcing any + lazy translation strings or strings into `ErrorDetail`. + """ + if isinstance(data, (list, tuple)): + ret = [ + _get_error_details(item, default_code) for item in data + ] + if isinstance(data, ReturnList): + return ReturnList(ret, serializer=data.serializer) + return ret + elif isinstance(data, dict): + ret = { + key: _get_error_details(value, default_code) + for key, value in data.items() + } + if isinstance(data, ReturnDict): + return ReturnDict(ret, serializer=data.serializer) + return ret + + text = force_str(data) + code = getattr(data, 'code', default_code) + return ErrorDetail(text, code) + + +def _get_codes(detail): + if isinstance(detail, list): + return [_get_codes(item) for item in detail] + elif isinstance(detail, dict): + return {key: _get_codes(value) for key, value in detail.items()} + return detail.code + + +def _get_full_details(detail): + if isinstance(detail, list): + return [_get_full_details(item) for item in detail] + elif isinstance(detail, dict): + return {key: _get_full_details(value) for key, value in detail.items()} + return { + 'message': detail, + 'code': detail.code + } + + +class ErrorDetail(str): + """ + A string-like object that can additionally have a code. + """ + code = None + + def __new__(cls, string, code=None): + self = super().__new__(cls, string) + self.code = code + return self + + def __eq__(self, other): + r = super().__eq__(other) + if r is NotImplemented: + return NotImplemented + try: + return r and self.code == other.code + except AttributeError: + return r + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return 'ErrorDetail(string=%r, code=%r)' % ( + str(self), + self.code, + ) + + def __hash__(self): + return hash(str(self)) + + +class APIException(Exception): + """ + Base class for REST framework exceptions. + Subclasses should provide `.status_code` and `.default_detail` properties. + """ + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + default_detail = _('A server error occurred.') + default_code = 'error' + + def __init__(self, detail=None, code=None): + if detail is None: + detail = self.default_detail + if code is None: + code = self.default_code + + self.detail = _get_error_details(detail, code) + + def __str__(self): + return str(self.detail) + + def get_codes(self): + """ + Return only the code part of the error details. + + Eg. {"name": ["required"]} + """ + return _get_codes(self.detail) + + def get_full_details(self): + """ + Return both the message & code parts of the error details. + + Eg. {"name": [{"message": "This field is required.", "code": "required"}]} + """ + return _get_full_details(self.detail) + + +# The recommended style for using `ValidationError` is to keep it namespaced +# under `serializers`, in order to minimize potential confusion with Django's +# built in `ValidationError`. For example: +# +# from rest_framework import serializers +# raise serializers.ValidationError('Value was invalid') + +class ValidationError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = _('Invalid input.') + default_code = 'invalid' + + def __init__(self, detail=None, code=None): + if detail is None: + detail = self.default_detail + if code is None: + code = self.default_code + + # For validation failures, we may collect many errors together, + # so the details should always be coerced to a list if not already. + if isinstance(detail, tuple): + detail = list(detail) + elif not isinstance(detail, dict) and not isinstance(detail, list): + detail = [detail] + + self.detail = _get_error_details(detail, code) + + +class ParseError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = _('Malformed request.') + default_code = 'parse_error' + + +class AuthenticationFailed(APIException): + status_code = status.HTTP_401_UNAUTHORIZED + default_detail = _('Incorrect authentication credentials.') + default_code = 'authentication_failed' + + +class NotAuthenticated(APIException): + status_code = status.HTTP_401_UNAUTHORIZED + default_detail = _('Authentication credentials were not provided.') + default_code = 'not_authenticated' + + +class PermissionDenied(APIException): + status_code = status.HTTP_403_FORBIDDEN + default_detail = _('You do not have permission to perform this action.') + default_code = 'permission_denied' + + +class NotFound(APIException): + status_code = status.HTTP_404_NOT_FOUND + default_detail = _('Not found.') + default_code = 'not_found' + + +class MethodNotAllowed(APIException): + status_code = status.HTTP_405_METHOD_NOT_ALLOWED + default_detail = _('Method "{method}" not allowed.') + default_code = 'method_not_allowed' + + def __init__(self, method, detail=None, code=None): + if detail is None: + detail = force_str(self.default_detail).format(method=method) + super().__init__(detail, code) + + +class NotAcceptable(APIException): + status_code = status.HTTP_406_NOT_ACCEPTABLE + default_detail = _('Could not satisfy the request Accept header.') + default_code = 'not_acceptable' + + def __init__(self, detail=None, code=None, available_renderers=None): + self.available_renderers = available_renderers + super().__init__(detail, code) + + +class UnsupportedMediaType(APIException): + status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE + default_detail = _('Unsupported media type "{media_type}" in request.') + default_code = 'unsupported_media_type' + + def __init__(self, media_type, detail=None, code=None): + if detail is None: + detail = force_str(self.default_detail).format(media_type=media_type) + super().__init__(detail, code) + + +class Throttled(APIException): + status_code = status.HTTP_429_TOO_MANY_REQUESTS + default_detail = _('Request was throttled.') + extra_detail_singular = _('Expected available in {wait} second.') + extra_detail_plural = _('Expected available in {wait} seconds.') + default_code = 'throttled' + + def __init__(self, wait=None, detail=None, code=None): + if detail is None: + detail = force_str(self.default_detail) + if wait is not None: + wait = math.ceil(wait) + detail = ' '.join(( + detail, + force_str(ngettext(self.extra_detail_singular.format(wait=wait), + self.extra_detail_plural.format(wait=wait), + wait)))) + self.wait = wait + super().__init__(detail, code) + + +def server_error(request, *args, **kwargs): + """ + Generic 500 error handler. + """ + data = { + 'error': 'Server Error (500)' + } + return JsonResponse(data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +def bad_request(request, exception, *args, **kwargs): + """ + Generic 400 error handler. + """ + data = { + 'error': 'Bad Request (400)' + } + return JsonResponse(data, status=status.HTTP_400_BAD_REQUEST) diff --git a/venv/Lib/site-packages/rest_framework/fields.py b/venv/Lib/site-packages/rest_framework/fields.py new file mode 100644 index 0000000..d7e7816 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/fields.py @@ -0,0 +1,1926 @@ +import copy +import datetime +import decimal +import functools +import inspect +import re +import uuid +import warnings +from collections import OrderedDict +from collections.abc import Mapping + +from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError as DjangoValidationError +from django.core.validators import ( + EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator, + MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, + URLValidator, ip_address_validators +) +from django.forms import FilePathField as DjangoFilePathField +from django.forms import ImageField as DjangoImageField +from django.utils import timezone +from django.utils.dateparse import ( + parse_date, parse_datetime, parse_duration, parse_time +) +from django.utils.duration import duration_string +from django.utils.encoding import is_protected_type, smart_str +from django.utils.formats import localize_input, sanitize_separators +from django.utils.ipv6 import clean_ipv6_address +from django.utils.timezone import utc +from django.utils.translation import gettext_lazy as _ +from pytz.exceptions import InvalidTimeError + +from rest_framework import ( + ISO_8601, RemovedInDRF313Warning, RemovedInDRF314Warning +) +from rest_framework.exceptions import ErrorDetail, ValidationError +from rest_framework.settings import api_settings +from rest_framework.utils import html, humanize_datetime, json, representation +from rest_framework.utils.formatting import lazy_format +from rest_framework.validators import ProhibitSurrogateCharactersValidator + + +class empty: + """ + This class is used to represent no data being provided for a given input + or output value. + + It is required because `None` may be a valid input or output value. + """ + pass + + +class BuiltinSignatureError(Exception): + """ + Built-in function signatures are not inspectable. This exception is raised + so the serializer can raise a helpful error message. + """ + pass + + +def is_simple_callable(obj): + """ + True if the object is a callable that takes no arguments. + """ + # Bail early since we cannot inspect built-in function signatures. + if inspect.isbuiltin(obj): + raise BuiltinSignatureError( + 'Built-in function signatures are not inspectable. ' + 'Wrap the function call in a simple, pure Python function.') + + if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)): + return False + + sig = inspect.signature(obj) + params = sig.parameters.values() + return all( + param.kind == param.VAR_POSITIONAL or + param.kind == param.VAR_KEYWORD or + param.default != param.empty + for param in params + ) + + +def get_attribute(instance, attrs): + """ + Similar to Python's built in `getattr(instance, attr)`, + but takes a list of nested attributes, instead of a single attribute. + + Also accepts either attribute lookup on objects or dictionary lookups. + """ + for attr in attrs: + try: + if isinstance(instance, Mapping): + instance = instance[attr] + else: + instance = getattr(instance, attr) + except ObjectDoesNotExist: + return None + if is_simple_callable(instance): + try: + instance = instance() + except (AttributeError, KeyError) as exc: + # If we raised an Attribute or KeyError here it'd get treated + # as an omitted field in `Field.get_attribute()`. Instead we + # raise a ValueError to ensure the exception is not masked. + raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc)) + + return instance + + +def set_value(dictionary, keys, value): + """ + Similar to Python's built in `dictionary[key] = value`, + but takes a list of nested keys instead of a single key. + + set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2} + set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2} + set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}} + """ + if not keys: + dictionary.update(value) + return + + for key in keys[:-1]: + if key not in dictionary: + dictionary[key] = {} + dictionary = dictionary[key] + + dictionary[keys[-1]] = value + + +def to_choices_dict(choices): + """ + Convert choices into key/value dicts. + + to_choices_dict([1]) -> {1: 1} + to_choices_dict([(1, '1st'), (2, '2nd')]) -> {1: '1st', 2: '2nd'} + to_choices_dict([('Group', ((1, '1st'), 2))]) -> {'Group': {1: '1st', 2: '2'}} + """ + # Allow single, paired or grouped choices style: + # choices = [1, 2, 3] + # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] + # choices = [('Category', ((1, 'First'), (2, 'Second'))), (3, 'Third')] + ret = OrderedDict() + for choice in choices: + if not isinstance(choice, (list, tuple)): + # single choice + ret[choice] = choice + else: + key, value = choice + if isinstance(value, (list, tuple)): + # grouped choices (category, sub choices) + ret[key] = to_choices_dict(value) + else: + # paired choice (key, display value) + ret[key] = value + return ret + + +def flatten_choices_dict(choices): + """ + Convert a group choices dict into a flat dict of choices. + + flatten_choices_dict({1: '1st', 2: '2nd'}) -> {1: '1st', 2: '2nd'} + flatten_choices_dict({'Group': {1: '1st', 2: '2nd'}}) -> {1: '1st', 2: '2nd'} + """ + ret = OrderedDict() + for key, value in choices.items(): + if isinstance(value, dict): + # grouped choices (category, sub choices) + for sub_key, sub_value in value.items(): + ret[sub_key] = sub_value + else: + # choice (key, display value) + ret[key] = value + return ret + + +def iter_options(grouped_choices, cutoff=None, cutoff_text=None): + """ + Helper function for options and option groups in templates. + """ + class StartOptionGroup: + start_option_group = True + end_option_group = False + + def __init__(self, label): + self.label = label + + class EndOptionGroup: + start_option_group = False + end_option_group = True + + class Option: + start_option_group = False + end_option_group = False + + def __init__(self, value, display_text, disabled=False): + self.value = value + self.display_text = display_text + self.disabled = disabled + + count = 0 + + for key, value in grouped_choices.items(): + if cutoff and count >= cutoff: + break + + if isinstance(value, dict): + yield StartOptionGroup(label=key) + for sub_key, sub_value in value.items(): + if cutoff and count >= cutoff: + break + yield Option(value=sub_key, display_text=sub_value) + count += 1 + yield EndOptionGroup() + else: + yield Option(value=key, display_text=value) + count += 1 + + if cutoff and count >= cutoff and cutoff_text: + cutoff_text = cutoff_text.format(count=cutoff) + yield Option(value='n/a', display_text=cutoff_text, disabled=True) + + +def get_error_detail(exc_info): + """ + Given a Django ValidationError, return a list of ErrorDetail, + with the `code` populated. + """ + code = getattr(exc_info, 'code', None) or 'invalid' + + try: + error_dict = exc_info.error_dict + except AttributeError: + return [ + ErrorDetail((error.message % error.params) if error.params else error.message, + code=error.code if error.code else code) + for error in exc_info.error_list] + return { + k: [ + ErrorDetail((error.message % error.params) if error.params else error.message, + code=error.code if error.code else code) + for error in errors + ] for k, errors in error_dict.items() + } + + +class CreateOnlyDefault: + """ + This class may be used to provide default values that are only used + for create operations, but that do not return any value for update + operations. + """ + requires_context = True + + def __init__(self, default): + self.default = default + + def __call__(self, serializer_field): + is_update = serializer_field.parent.instance is not None + if is_update: + raise SkipField() + if callable(self.default): + if hasattr(self.default, 'set_context'): + warnings.warn( + "Method `set_context` on defaults is deprecated and will " + "no longer be called starting with 3.13. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + RemovedInDRF313Warning, stacklevel=2 + ) + self.default.set_context(self) + + if getattr(self.default, 'requires_context', False): + return self.default(serializer_field) + else: + return self.default() + return self.default + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, repr(self.default)) + + +class CurrentUserDefault: + requires_context = True + + def __call__(self, serializer_field): + return serializer_field.context['request'].user + + def __repr__(self): + return '%s()' % self.__class__.__name__ + + +class SkipField(Exception): + pass + + +REGEX_TYPE = type(re.compile('')) + +NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`' +NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`' +NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`' +USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField' +MISSING_ERROR_MESSAGE = ( + 'ValidationError raised by `{class_name}`, but error key `{key}` does ' + 'not exist in the `error_messages` dictionary.' +) + + +class Field: + _creation_counter = 0 + + default_error_messages = { + 'required': _('This field is required.'), + 'null': _('This field may not be null.') + } + default_validators = [] + default_empty_html = empty + initial = None + + def __init__(self, *, read_only=False, write_only=False, + required=None, default=empty, initial=empty, source=None, + label=None, help_text=None, style=None, + error_messages=None, validators=None, allow_null=False): + self._creation_counter = Field._creation_counter + Field._creation_counter += 1 + + # If `required` is unset, then use `True` unless a default is provided. + if required is None: + required = default is empty and not read_only + + # Some combinations of keyword arguments do not make sense. + assert not (read_only and write_only), NOT_READ_ONLY_WRITE_ONLY + assert not (read_only and required), NOT_READ_ONLY_REQUIRED + assert not (required and default is not empty), NOT_REQUIRED_DEFAULT + assert not (read_only and self.__class__ == Field), USE_READONLYFIELD + + self.read_only = read_only + self.write_only = write_only + self.required = required + self.default = default + self.source = source + self.initial = self.initial if (initial is empty) else initial + self.label = label + self.help_text = help_text + self.style = {} if style is None else style + self.allow_null = allow_null + + if self.default_empty_html is not empty: + if default is not empty: + self.default_empty_html = default + + if validators is not None: + self.validators = list(validators) + + # These are set up by `.bind()` when the field is added to a serializer. + self.field_name = None + self.parent = None + + # Collect default error message from self and parent classes + messages = {} + for cls in reversed(self.__class__.__mro__): + messages.update(getattr(cls, 'default_error_messages', {})) + messages.update(error_messages or {}) + self.error_messages = messages + + def bind(self, field_name, parent): + """ + Initializes the field name and parent for the field instance. + Called when a field is added to the parent serializer instance. + """ + + # In order to enforce a consistent style, we error if a redundant + # 'source' argument has been used. For example: + # my_field = serializer.CharField(source='my_field') + assert self.source != field_name, ( + "It is redundant to specify `source='%s'` on field '%s' in " + "serializer '%s', because it is the same as the field name. " + "Remove the `source` keyword argument." % + (field_name, self.__class__.__name__, parent.__class__.__name__) + ) + + self.field_name = field_name + self.parent = parent + + # `self.label` should default to being based on the field name. + if self.label is None: + self.label = field_name.replace('_', ' ').capitalize() + + # self.source should default to being the same as the field name. + if self.source is None: + self.source = field_name + + # self.source_attrs is a list of attributes that need to be looked up + # when serializing the instance, or populating the validated data. + if self.source == '*': + self.source_attrs = [] + else: + self.source_attrs = self.source.split('.') + + # .validators is a lazily loaded property, that gets its default + # value from `get_validators`. + @property + def validators(self): + if not hasattr(self, '_validators'): + self._validators = self.get_validators() + return self._validators + + @validators.setter + def validators(self, validators): + self._validators = validators + + def get_validators(self): + return list(self.default_validators) + + def get_initial(self): + """ + Return a value to use when the field is being returned as a primitive + value, without any object instance. + """ + if callable(self.initial): + return self.initial() + return self.initial + + def get_value(self, dictionary): + """ + Given the *incoming* primitive data, return the value for this field + that should be validated and transformed to a native value. + """ + if html.is_html_input(dictionary): + # HTML forms will represent empty fields as '', and cannot + # represent None or False values directly. + if self.field_name not in dictionary: + if getattr(self.root, 'partial', False): + return empty + return self.default_empty_html + ret = dictionary[self.field_name] + if ret == '' and self.allow_null: + # If the field is blank, and null is a valid value then + # determine if we should use null instead. + return '' if getattr(self, 'allow_blank', False) else None + elif ret == '' and not self.required: + # If the field is blank, and emptiness is valid then + # determine if we should use emptiness instead. + return '' if getattr(self, 'allow_blank', False) else empty + return ret + return dictionary.get(self.field_name, empty) + + def get_attribute(self, instance): + """ + Given the *outgoing* object instance, return the primitive value + that should be used for this field. + """ + try: + return get_attribute(instance, self.source_attrs) + except BuiltinSignatureError as exc: + msg = ( + 'Field source for `{serializer}.{field}` maps to a built-in ' + 'function type and is invalid. Define a property or method on ' + 'the `{instance}` instance that wraps the call to the built-in ' + 'function.'.format( + serializer=self.parent.__class__.__name__, + field=self.field_name, + instance=instance.__class__.__name__, + ) + ) + raise type(exc)(msg) + except (KeyError, AttributeError) as exc: + if self.default is not empty: + return self.get_default() + if self.allow_null: + return None + if not self.required: + raise SkipField() + msg = ( + 'Got {exc_type} when attempting to get a value for field ' + '`{field}` on serializer `{serializer}`.\nThe serializer ' + 'field might be named incorrectly and not match ' + 'any attribute or key on the `{instance}` instance.\n' + 'Original exception text was: {exc}.'.format( + exc_type=type(exc).__name__, + field=self.field_name, + serializer=self.parent.__class__.__name__, + instance=instance.__class__.__name__, + exc=exc + ) + ) + raise type(exc)(msg) + + def get_default(self): + """ + Return the default value to use when validating data if no input + is provided for this field. + + If a default has not been set for this field then this will simply + raise `SkipField`, indicating that no value should be set in the + validated data for this field. + """ + if self.default is empty or getattr(self.root, 'partial', False): + # No default, or this is a partial update. + raise SkipField() + if callable(self.default): + if hasattr(self.default, 'set_context'): + warnings.warn( + "Method `set_context` on defaults is deprecated and will " + "no longer be called starting with 3.13. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + RemovedInDRF313Warning, stacklevel=2 + ) + self.default.set_context(self) + + if getattr(self.default, 'requires_context', False): + return self.default(self) + else: + return self.default() + + return self.default + + def validate_empty_values(self, data): + """ + Validate empty values, and either: + + * Raise `ValidationError`, indicating invalid data. + * Raise `SkipField`, indicating that the field should be ignored. + * Return (True, data), indicating an empty value that should be + returned without any further validation being applied. + * Return (False, data), indicating a non-empty value, that should + have validation applied as normal. + """ + if self.read_only: + return (True, self.get_default()) + + if data is empty: + if getattr(self.root, 'partial', False): + raise SkipField() + if self.required: + self.fail('required') + return (True, self.get_default()) + + if data is None: + if not self.allow_null: + self.fail('null') + # Nullable `source='*'` fields should not be skipped when its named + # field is given a null value. This is because `source='*'` means + # the field is passed the entire object, which is not null. + elif self.source == '*': + return (False, None) + return (True, None) + + return (False, data) + + def run_validation(self, data=empty): + """ + Validate a simple representation and return the internal value. + + The provided data may be `empty` if no representation was included + in the input. + + May raise `SkipField` if the field should not be included in the + validated data. + """ + (is_empty_value, data) = self.validate_empty_values(data) + if is_empty_value: + return data + value = self.to_internal_value(data) + self.run_validators(value) + return value + + def run_validators(self, value): + """ + Test the given value against all the validators on the field, + and either raise a `ValidationError` or simply return. + """ + errors = [] + for validator in self.validators: + if hasattr(validator, 'set_context'): + warnings.warn( + "Method `set_context` on validators is deprecated and will " + "no longer be called starting with 3.13. Instead set " + "`requires_context = True` on the class, and accept the " + "context as an additional argument.", + RemovedInDRF313Warning, stacklevel=2 + ) + validator.set_context(self) + + try: + if getattr(validator, 'requires_context', False): + validator(value, self) + else: + validator(value) + except ValidationError as exc: + # If the validation error contains a mapping of fields to + # errors then simply raise it immediately rather than + # attempting to accumulate a list of errors. + if isinstance(exc.detail, dict): + raise + errors.extend(exc.detail) + except DjangoValidationError as exc: + errors.extend(get_error_detail(exc)) + if errors: + raise ValidationError(errors) + + def to_internal_value(self, data): + """ + Transform the *incoming* primitive data into a native value. + """ + raise NotImplementedError( + '{cls}.to_internal_value() must be implemented for field ' + '{field_name}. If you do not need to support write operations ' + 'you probably want to subclass `ReadOnlyField` instead.'.format( + cls=self.__class__.__name__, + field_name=self.field_name, + ) + ) + + def to_representation(self, value): + """ + Transform the *outgoing* native value into primitive data. + """ + raise NotImplementedError( + '{cls}.to_representation() must be implemented for field {field_name}.'.format( + cls=self.__class__.__name__, + field_name=self.field_name, + ) + ) + + def fail(self, key, **kwargs): + """ + A helper method that simply raises a validation error. + """ + try: + msg = self.error_messages[key] + except KeyError: + class_name = self.__class__.__name__ + msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key) + raise AssertionError(msg) + message_string = msg.format(**kwargs) + raise ValidationError(message_string, code=key) + + @property + def root(self): + """ + Returns the top-level serializer for this field. + """ + root = self + while root.parent is not None: + root = root.parent + return root + + @property + def context(self): + """ + Returns the context as passed to the root serializer on initialization. + """ + return getattr(self.root, '_context', {}) + + def __new__(cls, *args, **kwargs): + """ + When a field is instantiated, we store the arguments that were used, + so that we can present a helpful representation of the object. + """ + instance = super().__new__(cls) + instance._args = args + instance._kwargs = kwargs + return instance + + def __deepcopy__(self, memo): + """ + When cloning fields we instantiate using the arguments it was + originally created with, rather than copying the complete state. + """ + # Treat regexes and validators as immutable. + # See https://github.com/encode/django-rest-framework/issues/1954 + # and https://github.com/encode/django-rest-framework/pull/4489 + args = [ + copy.deepcopy(item) if not isinstance(item, REGEX_TYPE) else item + for item in self._args + ] + kwargs = { + key: (copy.deepcopy(value, memo) if (key not in ('validators', 'regex')) else value) + for key, value in self._kwargs.items() + } + return self.__class__(*args, **kwargs) + + def __repr__(self): + """ + Fields are represented using their initial calling arguments. + This allows us to create descriptive representations for serializer + instances that show all the declared fields on the serializer. + """ + return representation.field_repr(self) + + +# Boolean types... + +class BooleanField(Field): + default_error_messages = { + 'invalid': _('Must be a valid boolean.') + } + default_empty_html = False + initial = False + TRUE_VALUES = { + 't', 'T', + 'y', 'Y', 'yes', 'Yes', 'YES', + 'true', 'True', 'TRUE', + 'on', 'On', 'ON', + '1', 1, + True + } + FALSE_VALUES = { + 'f', 'F', + 'n', 'N', 'no', 'No', 'NO', + 'false', 'False', 'FALSE', + 'off', 'Off', 'OFF', + '0', 0, 0.0, + False + } + NULL_VALUES = {'null', 'Null', 'NULL', '', None} + + def to_internal_value(self, data): + try: + if data in self.TRUE_VALUES: + return True + elif data in self.FALSE_VALUES: + return False + elif data in self.NULL_VALUES and self.allow_null: + return None + except TypeError: # Input is an unhashable type + pass + self.fail('invalid', input=data) + + def to_representation(self, value): + if value in self.TRUE_VALUES: + return True + elif value in self.FALSE_VALUES: + return False + if value in self.NULL_VALUES and self.allow_null: + return None + return bool(value) + + +class NullBooleanField(BooleanField): + initial = None + + def __init__(self, **kwargs): + warnings.warn( + "The `NullBooleanField` is deprecated and will be removed starting " + "with 3.14. Instead use the `BooleanField` field and set " + "`allow_null=True` which does the same thing.", + RemovedInDRF314Warning, stacklevel=2 + ) + + assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.' + kwargs['allow_null'] = True + + super().__init__(**kwargs) + + +# String types... + +class CharField(Field): + default_error_messages = { + 'invalid': _('Not a valid string.'), + 'blank': _('This field may not be blank.'), + 'max_length': _('Ensure this field has no more than {max_length} characters.'), + 'min_length': _('Ensure this field has at least {min_length} characters.'), + } + initial = '' + + def __init__(self, **kwargs): + self.allow_blank = kwargs.pop('allow_blank', False) + self.trim_whitespace = kwargs.pop('trim_whitespace', True) + self.max_length = kwargs.pop('max_length', None) + self.min_length = kwargs.pop('min_length', None) + super().__init__(**kwargs) + if self.max_length is not None: + message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) + self.validators.append( + MaxLengthValidator(self.max_length, message=message)) + if self.min_length is not None: + message = lazy_format(self.error_messages['min_length'], min_length=self.min_length) + self.validators.append( + MinLengthValidator(self.min_length, message=message)) + + self.validators.append(ProhibitNullCharactersValidator()) + self.validators.append(ProhibitSurrogateCharactersValidator()) + + def run_validation(self, data=empty): + # Test for the empty string here so that it does not get validated, + # and so that subclasses do not need to handle it explicitly + # inside the `to_internal_value()` method. + if data == '' or (self.trim_whitespace and str(data).strip() == ''): + if not self.allow_blank: + self.fail('blank') + return '' + return super().run_validation(data) + + def to_internal_value(self, data): + # We're lenient with allowing basic numerics to be coerced into strings, + # but other types should fail. Eg. unclear if booleans should represent as `true` or `True`, + # and composites such as lists are likely user error. + if isinstance(data, bool) or not isinstance(data, (str, int, float,)): + self.fail('invalid') + value = str(data) + return value.strip() if self.trim_whitespace else value + + def to_representation(self, value): + return str(value) + + +class EmailField(CharField): + default_error_messages = { + 'invalid': _('Enter a valid email address.') + } + + def __init__(self, **kwargs): + super().__init__(**kwargs) + validator = EmailValidator(message=self.error_messages['invalid']) + self.validators.append(validator) + + +class RegexField(CharField): + default_error_messages = { + 'invalid': _('This value does not match the required pattern.') + } + + def __init__(self, regex, **kwargs): + super().__init__(**kwargs) + validator = RegexValidator(regex, message=self.error_messages['invalid']) + self.validators.append(validator) + + +class SlugField(CharField): + default_error_messages = { + 'invalid': _('Enter a valid "slug" consisting of letters, numbers, underscores or hyphens.'), + 'invalid_unicode': _('Enter a valid "slug" consisting of Unicode letters, numbers, underscores, or hyphens.') + } + + def __init__(self, allow_unicode=False, **kwargs): + super().__init__(**kwargs) + self.allow_unicode = allow_unicode + if self.allow_unicode: + validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode']) + else: + validator = RegexValidator(re.compile(r'^[-a-zA-Z0-9_]+$'), message=self.error_messages['invalid']) + self.validators.append(validator) + + +class URLField(CharField): + default_error_messages = { + 'invalid': _('Enter a valid URL.') + } + + def __init__(self, **kwargs): + super().__init__(**kwargs) + validator = URLValidator(message=self.error_messages['invalid']) + self.validators.append(validator) + + +class UUIDField(Field): + valid_formats = ('hex_verbose', 'hex', 'int', 'urn') + + default_error_messages = { + 'invalid': _('Must be a valid UUID.'), + } + + def __init__(self, **kwargs): + self.uuid_format = kwargs.pop('format', 'hex_verbose') + if self.uuid_format not in self.valid_formats: + raise ValueError( + 'Invalid format for uuid representation. ' + 'Must be one of "{}"'.format('", "'.join(self.valid_formats)) + ) + super().__init__(**kwargs) + + def to_internal_value(self, data): + if not isinstance(data, uuid.UUID): + try: + if isinstance(data, int): + return uuid.UUID(int=data) + elif isinstance(data, str): + return uuid.UUID(hex=data) + else: + self.fail('invalid', value=data) + except (ValueError): + self.fail('invalid', value=data) + return data + + def to_representation(self, value): + if self.uuid_format == 'hex_verbose': + return str(value) + else: + return getattr(value, self.uuid_format) + + +class IPAddressField(CharField): + """Support both IPAddressField and GenericIPAddressField""" + + default_error_messages = { + 'invalid': _('Enter a valid IPv4 or IPv6 address.'), + } + + def __init__(self, protocol='both', **kwargs): + self.protocol = protocol.lower() + self.unpack_ipv4 = (self.protocol == 'both') + super().__init__(**kwargs) + validators, error_message = ip_address_validators(protocol, self.unpack_ipv4) + self.validators.extend(validators) + + def to_internal_value(self, data): + if not isinstance(data, str): + self.fail('invalid', value=data) + + if ':' in data: + try: + if self.protocol in ('both', 'ipv6'): + return clean_ipv6_address(data, self.unpack_ipv4) + except DjangoValidationError: + self.fail('invalid', value=data) + + return super().to_internal_value(data) + + +# Number types... + +class IntegerField(Field): + default_error_messages = { + 'invalid': _('A valid integer is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_string_length': _('String value too large.') + } + MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. + re_decimal = re.compile(r'\.0*\s*$') # allow e.g. '1.0' as an int, but not '1.2' + + def __init__(self, **kwargs): + self.max_value = kwargs.pop('max_value', None) + self.min_value = kwargs.pop('min_value', None) + super().__init__(**kwargs) + if self.max_value is not None: + message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) + if self.min_value is not None: + message = lazy_format(self.error_messages['min_value'], min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) + + def to_internal_value(self, data): + if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH: + self.fail('max_string_length') + + try: + data = int(self.re_decimal.sub('', str(data))) + except (ValueError, TypeError): + self.fail('invalid') + return data + + def to_representation(self, value): + return int(value) + + +class FloatField(Field): + default_error_messages = { + 'invalid': _('A valid number is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_string_length': _('String value too large.') + } + MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. + + def __init__(self, **kwargs): + self.max_value = kwargs.pop('max_value', None) + self.min_value = kwargs.pop('min_value', None) + super().__init__(**kwargs) + if self.max_value is not None: + message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) + if self.min_value is not None: + message = lazy_format(self.error_messages['min_value'], min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) + + def to_internal_value(self, data): + + if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH: + self.fail('max_string_length') + + try: + return float(data) + except (TypeError, ValueError): + self.fail('invalid') + + def to_representation(self, value): + return float(value) + + +class DecimalField(Field): + default_error_messages = { + 'invalid': _('A valid number is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'), + 'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'), + 'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'), + 'max_string_length': _('String value too large.') + } + MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. + + def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, + localize=False, rounding=None, **kwargs): + self.max_digits = max_digits + self.decimal_places = decimal_places + self.localize = localize + if coerce_to_string is not None: + self.coerce_to_string = coerce_to_string + if self.localize: + self.coerce_to_string = True + + self.max_value = max_value + self.min_value = min_value + + if self.max_digits is not None and self.decimal_places is not None: + self.max_whole_digits = self.max_digits - self.decimal_places + else: + self.max_whole_digits = None + + super().__init__(**kwargs) + + if self.max_value is not None: + message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) + if self.min_value is not None: + message = lazy_format(self.error_messages['min_value'], min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) + + if rounding is not None: + valid_roundings = [v for k, v in vars(decimal).items() if k.startswith('ROUND_')] + assert rounding in valid_roundings, ( + 'Invalid rounding option %s. Valid values for rounding are: %s' % (rounding, valid_roundings)) + self.rounding = rounding + + def validate_empty_values(self, data): + if smart_str(data).strip() == '' and self.allow_null: + return (True, None) + return super().validate_empty_values(data) + + def to_internal_value(self, data): + """ + Validate that the input is a decimal number and return a Decimal + instance. + """ + + data = smart_str(data).strip() + + if self.localize: + data = sanitize_separators(data) + + if len(data) > self.MAX_STRING_LENGTH: + self.fail('max_string_length') + + try: + value = decimal.Decimal(data) + except decimal.DecimalException: + self.fail('invalid') + + if value.is_nan(): + self.fail('invalid') + + # Check for infinity and negative infinity. + if value in (decimal.Decimal('Inf'), decimal.Decimal('-Inf')): + self.fail('invalid') + + return self.quantize(self.validate_precision(value)) + + def validate_precision(self, value): + """ + Ensure that there are no more than max_digits in the number, and no + more than decimal_places digits after the decimal point. + + Override this method to disable the precision validation for input + values or to enhance it in any way you need to. + """ + sign, digittuple, exponent = value.as_tuple() + + if exponent >= 0: + # 1234500.0 + total_digits = len(digittuple) + exponent + whole_digits = total_digits + decimal_places = 0 + elif len(digittuple) > abs(exponent): + # 123.45 + total_digits = len(digittuple) + whole_digits = total_digits - abs(exponent) + decimal_places = abs(exponent) + else: + # 0.001234 + total_digits = abs(exponent) + whole_digits = 0 + decimal_places = total_digits + + if self.max_digits is not None and total_digits > self.max_digits: + self.fail('max_digits', max_digits=self.max_digits) + if self.decimal_places is not None and decimal_places > self.decimal_places: + self.fail('max_decimal_places', max_decimal_places=self.decimal_places) + if self.max_whole_digits is not None and whole_digits > self.max_whole_digits: + self.fail('max_whole_digits', max_whole_digits=self.max_whole_digits) + + return value + + def to_representation(self, value): + coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING) + + if value is None: + if coerce_to_string: + return '' + else: + return None + + if not isinstance(value, decimal.Decimal): + value = decimal.Decimal(str(value).strip()) + + quantized = self.quantize(value) + + if not coerce_to_string: + return quantized + if self.localize: + return localize_input(quantized) + + return '{:f}'.format(quantized) + + def quantize(self, value): + """ + Quantize the decimal value to the configured precision. + """ + if self.decimal_places is None: + return value + + context = decimal.getcontext().copy() + if self.max_digits is not None: + context.prec = self.max_digits + return value.quantize( + decimal.Decimal('.1') ** self.decimal_places, + rounding=self.rounding, + context=context + ) + + +# Date & time fields... + +class DateTimeField(Field): + default_error_messages = { + 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), + 'date': _('Expected a datetime but got a date.'), + 'make_aware': _('Invalid datetime for the timezone "{timezone}".'), + 'overflow': _('Datetime value out of range.') + } + datetime_parser = datetime.datetime.strptime + + def __init__(self, format=empty, input_formats=None, default_timezone=None, **kwargs): + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats + if default_timezone is not None: + self.timezone = default_timezone + super().__init__(**kwargs) + + def enforce_timezone(self, value): + """ + When `self.default_timezone` is `None`, always return naive datetimes. + When `self.default_timezone` is not `None`, always return aware datetimes. + """ + field_timezone = getattr(self, 'timezone', self.default_timezone()) + + if field_timezone is not None: + if timezone.is_aware(value): + try: + return value.astimezone(field_timezone) + except OverflowError: + self.fail('overflow') + try: + return timezone.make_aware(value, field_timezone) + except InvalidTimeError: + self.fail('make_aware', timezone=field_timezone) + elif (field_timezone is None) and timezone.is_aware(value): + return timezone.make_naive(value, utc) + return value + + def default_timezone(self): + return timezone.get_current_timezone() if settings.USE_TZ else None + + def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS) + + if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime): + self.fail('date') + + if isinstance(value, datetime.datetime): + return self.enforce_timezone(value) + + for input_format in input_formats: + if input_format.lower() == ISO_8601: + try: + parsed = parse_datetime(value) + if parsed is not None: + return self.enforce_timezone(parsed) + except (ValueError, TypeError): + pass + else: + try: + parsed = self.datetime_parser(value, input_format) + return self.enforce_timezone(parsed) + except (ValueError, TypeError): + pass + + humanized_format = humanize_datetime.datetime_formats(input_formats) + self.fail('invalid', format=humanized_format) + + def to_representation(self, value): + if not value: + return None + + output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT) + + if output_format is None or isinstance(value, str): + return value + + value = self.enforce_timezone(value) + + if output_format.lower() == ISO_8601: + value = value.isoformat() + if value.endswith('+00:00'): + value = value[:-6] + 'Z' + return value + return value.strftime(output_format) + + +class DateField(Field): + default_error_messages = { + 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'), + 'datetime': _('Expected a date but got a datetime.'), + } + datetime_parser = datetime.datetime.strptime + + def __init__(self, format=empty, input_formats=None, **kwargs): + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats + super().__init__(**kwargs) + + def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS) + + if isinstance(value, datetime.datetime): + self.fail('datetime') + + if isinstance(value, datetime.date): + return value + + for input_format in input_formats: + if input_format.lower() == ISO_8601: + try: + parsed = parse_date(value) + except (ValueError, TypeError): + pass + else: + if parsed is not None: + return parsed + else: + try: + parsed = self.datetime_parser(value, input_format) + except (ValueError, TypeError): + pass + else: + return parsed.date() + + humanized_format = humanize_datetime.date_formats(input_formats) + self.fail('invalid', format=humanized_format) + + def to_representation(self, value): + if not value: + return None + + output_format = getattr(self, 'format', api_settings.DATE_FORMAT) + + if output_format is None or isinstance(value, str): + return value + + # Applying a `DateField` to a datetime value is almost always + # not a sensible thing to do, as it means naively dropping + # any explicit or implicit timezone info. + assert not isinstance(value, datetime.datetime), ( + 'Expected a `date`, but got a `datetime`. Refusing to coerce, ' + 'as this may mean losing timezone information. Use a custom ' + 'read-only field and deal with timezone issues explicitly.' + ) + + if output_format.lower() == ISO_8601: + return value.isoformat() + + return value.strftime(output_format) + + +class TimeField(Field): + default_error_messages = { + 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'), + } + datetime_parser = datetime.datetime.strptime + + def __init__(self, format=empty, input_formats=None, **kwargs): + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats + super().__init__(**kwargs) + + def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS) + + if isinstance(value, datetime.time): + return value + + for input_format in input_formats: + if input_format.lower() == ISO_8601: + try: + parsed = parse_time(value) + except (ValueError, TypeError): + pass + else: + if parsed is not None: + return parsed + else: + try: + parsed = self.datetime_parser(value, input_format) + except (ValueError, TypeError): + pass + else: + return parsed.time() + + humanized_format = humanize_datetime.time_formats(input_formats) + self.fail('invalid', format=humanized_format) + + def to_representation(self, value): + if value in (None, ''): + return None + + output_format = getattr(self, 'format', api_settings.TIME_FORMAT) + + if output_format is None or isinstance(value, str): + return value + + # Applying a `TimeField` to a datetime value is almost always + # not a sensible thing to do, as it means naively dropping + # any explicit or implicit timezone info. + assert not isinstance(value, datetime.datetime), ( + 'Expected a `time`, but got a `datetime`. Refusing to coerce, ' + 'as this may mean losing timezone information. Use a custom ' + 'read-only field and deal with timezone issues explicitly.' + ) + + if output_format.lower() == ISO_8601: + return value.isoformat() + return value.strftime(output_format) + + +class DurationField(Field): + default_error_messages = { + 'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + } + + def __init__(self, **kwargs): + self.max_value = kwargs.pop('max_value', None) + self.min_value = kwargs.pop('min_value', None) + super().__init__(**kwargs) + if self.max_value is not None: + message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) + if self.min_value is not None: + message = lazy_format(self.error_messages['min_value'], min_value=self.min_value) + self.validators.append( + MinValueValidator(self.min_value, message=message)) + + def to_internal_value(self, value): + if isinstance(value, datetime.timedelta): + return value + parsed = parse_duration(str(value)) + if parsed is not None: + return parsed + self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]') + + def to_representation(self, value): + return duration_string(value) + + +# Choice types... + +class ChoiceField(Field): + default_error_messages = { + 'invalid_choice': _('"{input}" is not a valid choice.') + } + html_cutoff = None + html_cutoff_text = _('More than {count} items...') + + def __init__(self, choices, **kwargs): + self.choices = choices + self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) + self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) + + self.allow_blank = kwargs.pop('allow_blank', False) + + super().__init__(**kwargs) + + def to_internal_value(self, data): + if data == '' and self.allow_blank: + return '' + + try: + return self.choice_strings_to_values[str(data)] + except KeyError: + self.fail('invalid_choice', input=data) + + def to_representation(self, value): + if value in ('', None): + return value + return self.choice_strings_to_values.get(str(value), value) + + def iter_options(self): + """ + Helper method for use with templates rendering select widgets. + """ + return iter_options( + self.grouped_choices, + cutoff=self.html_cutoff, + cutoff_text=self.html_cutoff_text + ) + + def _get_choices(self): + return self._choices + + def _set_choices(self, choices): + self.grouped_choices = to_choices_dict(choices) + self._choices = flatten_choices_dict(self.grouped_choices) + + # Map the string representation of choices to the underlying value. + # Allows us to deal with eg. integer choices while supporting either + # integer or string input, but still get the correct datatype out. + self.choice_strings_to_values = { + str(key): key for key in self.choices + } + + choices = property(_get_choices, _set_choices) + + +class MultipleChoiceField(ChoiceField): + default_error_messages = { + 'invalid_choice': _('"{input}" is not a valid choice.'), + 'not_a_list': _('Expected a list of items but got type "{input_type}".'), + 'empty': _('This selection may not be empty.') + } + default_empty_html = [] + + def __init__(self, **kwargs): + self.allow_empty = kwargs.pop('allow_empty', True) + super().__init__(**kwargs) + + def get_value(self, dictionary): + if self.field_name not in dictionary: + if getattr(self.root, 'partial', False): + return empty + # We override the default field access in order to support + # lists in HTML forms. + if html.is_html_input(dictionary): + return dictionary.getlist(self.field_name) + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + if isinstance(data, str) or not hasattr(data, '__iter__'): + self.fail('not_a_list', input_type=type(data).__name__) + if not self.allow_empty and len(data) == 0: + self.fail('empty') + + return { + # Arguments for super() are needed because of scoping inside + # comprehensions. + super(MultipleChoiceField, self).to_internal_value(item) + for item in data + } + + def to_representation(self, value): + return { + self.choice_strings_to_values.get(str(item), item) for item in value + } + + +class FilePathField(ChoiceField): + default_error_messages = { + 'invalid_choice': _('"{input}" is not a valid path choice.') + } + + def __init__(self, path, match=None, recursive=False, allow_files=True, + allow_folders=False, required=None, **kwargs): + # Defer to Django's FilePathField implementation to get the + # valid set of choices. + field = DjangoFilePathField( + path, match=match, recursive=recursive, allow_files=allow_files, + allow_folders=allow_folders, required=required + ) + kwargs['choices'] = field.choices + super().__init__(**kwargs) + + +# File types... + +class FileField(Field): + default_error_messages = { + 'required': _('No file was submitted.'), + 'invalid': _('The submitted data was not a file. Check the encoding type on the form.'), + 'no_name': _('No filename could be determined.'), + 'empty': _('The submitted file is empty.'), + 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), + } + + def __init__(self, **kwargs): + self.max_length = kwargs.pop('max_length', None) + self.allow_empty_file = kwargs.pop('allow_empty_file', False) + if 'use_url' in kwargs: + self.use_url = kwargs.pop('use_url') + super().__init__(**kwargs) + + def to_internal_value(self, data): + try: + # `UploadedFile` objects should have name and size attributes. + file_name = data.name + file_size = data.size + except AttributeError: + self.fail('invalid') + + if not file_name: + self.fail('no_name') + if not self.allow_empty_file and not file_size: + self.fail('empty') + if self.max_length and len(file_name) > self.max_length: + self.fail('max_length', max_length=self.max_length, length=len(file_name)) + + return data + + def to_representation(self, value): + if not value: + return None + + use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL) + if use_url: + try: + url = value.url + except AttributeError: + return None + request = self.context.get('request', None) + if request is not None: + return request.build_absolute_uri(url) + return url + + return value.name + + +class ImageField(FileField): + default_error_messages = { + 'invalid_image': _( + 'Upload a valid image. The file you uploaded was either not an image or a corrupted image.' + ), + } + + def __init__(self, **kwargs): + self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField) + super().__init__(**kwargs) + + def to_internal_value(self, data): + # Image validation is a bit grungy, so we'll just outright + # defer to Django's implementation so we don't need to + # consider it, or treat PIL as a test dependency. + file_object = super().to_internal_value(data) + django_field = self._DjangoImageField() + django_field.error_messages = self.error_messages + return django_field.clean(file_object) + + +# Composite field types... + +class _UnvalidatedField(Field): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.allow_blank = True + self.allow_null = True + + def to_internal_value(self, data): + return data + + def to_representation(self, value): + return value + + +class ListField(Field): + child = _UnvalidatedField() + initial = [] + default_error_messages = { + 'not_a_list': _('Expected a list of items but got type "{input_type}".'), + 'empty': _('This list may not be empty.'), + 'min_length': _('Ensure this field has at least {min_length} elements.'), + 'max_length': _('Ensure this field has no more than {max_length} elements.') + } + + def __init__(self, **kwargs): + self.child = kwargs.pop('child', copy.deepcopy(self.child)) + self.allow_empty = kwargs.pop('allow_empty', True) + self.max_length = kwargs.pop('max_length', None) + self.min_length = kwargs.pop('min_length', None) + + assert not inspect.isclass(self.child), '`child` has not been instantiated.' + assert self.child.source is None, ( + "The `source` argument is not meaningful when applied to a `child=` field. " + "Remove `source=` from the field declaration." + ) + + super().__init__(**kwargs) + self.child.bind(field_name='', parent=self) + if self.max_length is not None: + message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) + self.validators.append(MaxLengthValidator(self.max_length, message=message)) + if self.min_length is not None: + message = lazy_format(self.error_messages['min_length'], min_length=self.min_length) + self.validators.append(MinLengthValidator(self.min_length, message=message)) + + def get_value(self, dictionary): + if self.field_name not in dictionary: + if getattr(self.root, 'partial', False): + return empty + # We override the default field access in order to support + # lists in HTML forms. + if html.is_html_input(dictionary): + val = dictionary.getlist(self.field_name, []) + if len(val) > 0: + # Support QueryDict lists in HTML input. + return val + return html.parse_html_list(dictionary, prefix=self.field_name, default=empty) + + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + """ + List of dicts of native values <- List of dicts of primitive datatypes. + """ + if html.is_html_input(data): + data = html.parse_html_list(data, default=[]) + if isinstance(data, (str, Mapping)) or not hasattr(data, '__iter__'): + self.fail('not_a_list', input_type=type(data).__name__) + if not self.allow_empty and len(data) == 0: + self.fail('empty') + return self.run_child_validation(data) + + def to_representation(self, data): + """ + List of object instances -> List of dicts of primitive datatypes. + """ + return [self.child.to_representation(item) if item is not None else None for item in data] + + def run_child_validation(self, data): + result = [] + errors = OrderedDict() + + for idx, item in enumerate(data): + try: + result.append(self.child.run_validation(item)) + except ValidationError as e: + errors[idx] = e.detail + + if not errors: + return result + raise ValidationError(errors) + + +class DictField(Field): + child = _UnvalidatedField() + initial = {} + default_error_messages = { + 'not_a_dict': _('Expected a dictionary of items but got type "{input_type}".'), + 'empty': _('This dictionary may not be empty.'), + } + + def __init__(self, **kwargs): + self.child = kwargs.pop('child', copy.deepcopy(self.child)) + self.allow_empty = kwargs.pop('allow_empty', True) + + assert not inspect.isclass(self.child), '`child` has not been instantiated.' + assert self.child.source is None, ( + "The `source` argument is not meaningful when applied to a `child=` field. " + "Remove `source=` from the field declaration." + ) + + super().__init__(**kwargs) + self.child.bind(field_name='', parent=self) + + def get_value(self, dictionary): + # We override the default field access in order to support + # dictionaries in HTML forms. + if html.is_html_input(dictionary): + return html.parse_html_dict(dictionary, prefix=self.field_name) + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + """ + Dicts of native values <- Dicts of primitive datatypes. + """ + if html.is_html_input(data): + data = html.parse_html_dict(data) + if not isinstance(data, dict): + self.fail('not_a_dict', input_type=type(data).__name__) + if not self.allow_empty and len(data) == 0: + self.fail('empty') + + return self.run_child_validation(data) + + def to_representation(self, value): + return { + str(key): self.child.to_representation(val) if val is not None else None + for key, val in value.items() + } + + def run_child_validation(self, data): + result = {} + errors = OrderedDict() + + for key, value in data.items(): + key = str(key) + + try: + result[key] = self.child.run_validation(value) + except ValidationError as e: + errors[key] = e.detail + + if not errors: + return result + raise ValidationError(errors) + + +class HStoreField(DictField): + child = CharField(allow_blank=True, allow_null=True) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + assert isinstance(self.child, CharField), ( + "The `child` argument must be an instance of `CharField`, " + "as the hstore extension stores values as strings." + ) + + +class JSONField(Field): + default_error_messages = { + 'invalid': _('Value must be valid JSON.') + } + + # Workaround for isinstance calls when importing the field isn't possible + _is_jsonfield = True + + def __init__(self, **kwargs): + self.binary = kwargs.pop('binary', False) + self.encoder = kwargs.pop('encoder', None) + self.decoder = kwargs.pop('decoder', None) + super().__init__(**kwargs) + + def get_value(self, dictionary): + if html.is_html_input(dictionary) and self.field_name in dictionary: + # When HTML form input is used, mark up the input + # as being a JSON string, rather than a JSON primitive. + class JSONString(str): + def __new__(cls, value): + ret = str.__new__(cls, value) + ret.is_json_string = True + return ret + return JSONString(dictionary[self.field_name]) + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + try: + if self.binary or getattr(data, 'is_json_string', False): + if isinstance(data, bytes): + data = data.decode() + return json.loads(data, cls=self.decoder) + else: + json.dumps(data, cls=self.encoder) + except (TypeError, ValueError): + self.fail('invalid') + return data + + def to_representation(self, value): + if self.binary: + value = json.dumps(value, cls=self.encoder) + value = value.encode() + return value + + +# Miscellaneous field types... + +class ReadOnlyField(Field): + """ + A read-only field that simply returns the field value. + + If the field is a method with no parameters, the method will be called + and its return value used as the representation. + + For example, the following would call `get_expiry_date()` on the object: + + class ExampleSerializer(Serializer): + expiry_date = ReadOnlyField(source='get_expiry_date') + """ + + def __init__(self, **kwargs): + kwargs['read_only'] = True + super().__init__(**kwargs) + + def to_representation(self, value): + return value + + +class HiddenField(Field): + """ + A hidden field does not take input from the user, or present any output, + but it does populate a field in `validated_data`, based on its default + value. This is particularly useful when we have a `unique_for_date` + constraint on a pair of fields, as we need some way to include the date in + the validated data. + """ + def __init__(self, **kwargs): + assert 'default' in kwargs, 'default is a required argument.' + kwargs['write_only'] = True + super().__init__(**kwargs) + + def get_value(self, dictionary): + # We always use the default value for `HiddenField`. + # User input is never provided or accepted. + return empty + + def to_internal_value(self, data): + return data + + +class SerializerMethodField(Field): + """ + A read-only field that get its representation from calling a method on the + parent serializer class. The method called will be of the form + "get_{field_name}", and should take a single argument, which is the + object being serialized. + + For example: + + class ExampleSerializer(self): + extra_info = SerializerMethodField() + + def get_extra_info(self, obj): + return ... # Calculate some data to return. + """ + def __init__(self, method_name=None, **kwargs): + self.method_name = method_name + kwargs['source'] = '*' + kwargs['read_only'] = True + super().__init__(**kwargs) + + def bind(self, field_name, parent): + # The method name defaults to `get_{field_name}`. + if self.method_name is None: + self.method_name = 'get_{field_name}'.format(field_name=field_name) + + super().bind(field_name, parent) + + def to_representation(self, value): + method = getattr(self.parent, self.method_name) + return method(value) + + +class ModelField(Field): + """ + A generic field that can be used against an arbitrary model field. + + This is used by `ModelSerializer` when dealing with custom model fields, + that do not have a serializer field to be mapped to. + """ + default_error_messages = { + 'max_length': _('Ensure this field has no more than {max_length} characters.'), + } + + def __init__(self, model_field, **kwargs): + self.model_field = model_field + # The `max_length` option is supported by Django's base `Field` class, + # so we'd better support it here. + self.max_length = kwargs.pop('max_length', None) + super().__init__(**kwargs) + if self.max_length is not None: + message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) + self.validators.append( + MaxLengthValidator(self.max_length, message=message)) + + def to_internal_value(self, data): + rel = self.model_field.remote_field + if rel is not None: + return rel.model._meta.get_field(rel.field_name).to_python(data) + return self.model_field.to_python(data) + + def get_attribute(self, obj): + # We pass the object instance onto `to_representation`, + # not just the field attribute. + return obj + + def to_representation(self, obj): + value = self.model_field.value_from_object(obj) + if is_protected_type(value): + return value + return self.model_field.value_to_string(obj) diff --git a/venv/Lib/site-packages/rest_framework/filters.py b/venv/Lib/site-packages/rest_framework/filters.py new file mode 100644 index 0000000..1ffd9ed --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/filters.py @@ -0,0 +1,333 @@ +""" +Provides generic filtering backends that can be used to filter the results +returned by list views. +""" +import operator +from functools import reduce + +from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.db.models.constants import LOOKUP_SEP +from django.template import loader +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ + +from rest_framework.compat import coreapi, coreschema, distinct +from rest_framework.settings import api_settings + + +class BaseFilterBackend: + """ + A base class from which all filter backend classes should inherit. + """ + + def filter_queryset(self, request, queryset, view): + """ + Return a filtered queryset. + """ + raise NotImplementedError(".filter_queryset() must be overridden.") + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + return [] + + def get_schema_operation_parameters(self, view): + return [] + + +class SearchFilter(BaseFilterBackend): + # The URL query parameter used for the search. + search_param = api_settings.SEARCH_PARAM + template = 'rest_framework/filters/search.html' + lookup_prefixes = { + '^': 'istartswith', + '=': 'iexact', + '@': 'search', + '$': 'iregex', + } + search_title = _('Search') + search_description = _('A search term.') + + def get_search_fields(self, view, request): + """ + Search fields are obtained from the view, but the request is always + passed to this method. Sub-classes can override this method to + dynamically change the search fields based on request content. + """ + return getattr(view, 'search_fields', None) + + def get_search_terms(self, request): + """ + Search terms are set by a ?search=... query parameter, + and may be comma and/or whitespace delimited. + """ + params = request.query_params.get(self.search_param, '') + params = params.replace('\x00', '') # strip null characters + params = params.replace(',', ' ') + return params.split() + + def construct_search(self, field_name): + lookup = self.lookup_prefixes.get(field_name[0]) + if lookup: + field_name = field_name[1:] + else: + lookup = 'icontains' + return LOOKUP_SEP.join([field_name, lookup]) + + def must_call_distinct(self, queryset, search_fields): + """ + Return True if 'distinct()' should be used to query the given lookups. + """ + for search_field in search_fields: + opts = queryset.model._meta + if search_field[0] in self.lookup_prefixes: + search_field = search_field[1:] + # Annotated fields do not need to be distinct + if isinstance(queryset, models.QuerySet) and search_field in queryset.query.annotations: + continue + parts = search_field.split(LOOKUP_SEP) + for part in parts: + field = opts.get_field(part) + if hasattr(field, 'get_path_info'): + # This field is a relation, update opts to follow the relation + path_info = field.get_path_info() + opts = path_info[-1].to_opts + if any(path.m2m for path in path_info): + # This field is a m2m relation so we know we need to call distinct + return True + else: + # This field has a custom __ query transform but is not a relational field. + break + return False + + def filter_queryset(self, request, queryset, view): + search_fields = self.get_search_fields(view, request) + search_terms = self.get_search_terms(request) + + if not search_fields or not search_terms: + return queryset + + orm_lookups = [ + self.construct_search(str(search_field)) + for search_field in search_fields + ] + + base = queryset + conditions = [] + for search_term in search_terms: + queries = [ + models.Q(**{orm_lookup: search_term}) + for orm_lookup in orm_lookups + ] + conditions.append(reduce(operator.or_, queries)) + queryset = queryset.filter(reduce(operator.and_, conditions)) + + if self.must_call_distinct(queryset, search_fields): + # Filtering against a many-to-many field requires us to + # call queryset.distinct() in order to avoid duplicate items + # in the resulting queryset. + # We try to avoid this if possible, for performance reasons. + queryset = distinct(queryset, base) + return queryset + + def to_html(self, request, queryset, view): + if not getattr(view, 'search_fields', None): + return '' + + term = self.get_search_terms(request) + term = term[0] if term else '' + context = { + 'param': self.search_param, + 'term': term + } + template = loader.get_template(self.template) + return template.render(context) + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + return [ + coreapi.Field( + name=self.search_param, + required=False, + location='query', + schema=coreschema.String( + title=force_str(self.search_title), + description=force_str(self.search_description) + ) + ) + ] + + def get_schema_operation_parameters(self, view): + return [ + { + 'name': self.search_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.search_description), + 'schema': { + 'type': 'string', + }, + }, + ] + + +class OrderingFilter(BaseFilterBackend): + # The URL query parameter used for the ordering. + ordering_param = api_settings.ORDERING_PARAM + ordering_fields = None + ordering_title = _('Ordering') + ordering_description = _('Which field to use when ordering the results.') + template = 'rest_framework/filters/ordering.html' + + def get_ordering(self, request, queryset, view): + """ + Ordering is set by a comma delimited ?ordering=... query parameter. + + The `ordering` query parameter can be overridden by setting + the `ordering_param` value on the OrderingFilter or by + specifying an `ORDERING_PARAM` value in the API settings. + """ + params = request.query_params.get(self.ordering_param) + if params: + fields = [param.strip() for param in params.split(',')] + ordering = self.remove_invalid_fields(queryset, fields, view, request) + if ordering: + return ordering + + # No ordering was included, or all the ordering fields were invalid + return self.get_default_ordering(view) + + def get_default_ordering(self, view): + ordering = getattr(view, 'ordering', None) + if isinstance(ordering, str): + return (ordering,) + return ordering + + def get_default_valid_fields(self, queryset, view, context={}): + # If `ordering_fields` is not specified, then we determine a default + # based on the serializer class, if one exists on the view. + if hasattr(view, 'get_serializer_class'): + try: + serializer_class = view.get_serializer_class() + except AssertionError: + # Raised by the default implementation if + # no serializer_class was found + serializer_class = None + else: + serializer_class = getattr(view, 'serializer_class', None) + + if serializer_class is None: + msg = ( + "Cannot use %s on a view which does not have either a " + "'serializer_class', an overriding 'get_serializer_class' " + "or 'ordering_fields' attribute." + ) + raise ImproperlyConfigured(msg % self.__class__.__name__) + + model_class = queryset.model + model_property_names = [ + # 'pk' is a property added in Django's Model class, however it is valid for ordering. + attr for attr in dir(model_class) if isinstance(getattr(model_class, attr), property) and attr != 'pk' + ] + + return [ + (field.source.replace('.', '__') or field_name, field.label) + for field_name, field in serializer_class(context=context).fields.items() + if ( + not getattr(field, 'write_only', False) and + not field.source == '*' and + field.source not in model_property_names + ) + ] + + def get_valid_fields(self, queryset, view, context={}): + valid_fields = getattr(view, 'ordering_fields', self.ordering_fields) + + if valid_fields is None: + # Default to allowing filtering on serializer fields + return self.get_default_valid_fields(queryset, view, context) + + elif valid_fields == '__all__': + # View explicitly allows filtering on any model field + valid_fields = [ + (field.name, field.verbose_name) for field in queryset.model._meta.fields + ] + valid_fields += [ + (key, key.title().split('__')) + for key in queryset.query.annotations + ] + else: + valid_fields = [ + (item, item) if isinstance(item, str) else item + for item in valid_fields + ] + + return valid_fields + + def remove_invalid_fields(self, queryset, fields, view, request): + valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})] + + def term_valid(term): + if term.startswith("-"): + term = term[1:] + return term in valid_fields + + return [term for term in fields if term_valid(term)] + + def filter_queryset(self, request, queryset, view): + ordering = self.get_ordering(request, queryset, view) + + if ordering: + return queryset.order_by(*ordering) + + return queryset + + def get_template_context(self, request, queryset, view): + current = self.get_ordering(request, queryset, view) + current = None if not current else current[0] + options = [] + context = { + 'request': request, + 'current': current, + 'param': self.ordering_param, + } + for key, label in self.get_valid_fields(queryset, view, context): + options.append((key, '%s - %s' % (label, _('ascending')))) + options.append(('-' + key, '%s - %s' % (label, _('descending')))) + context['options'] = options + return context + + def to_html(self, request, queryset, view): + template = loader.get_template(self.template) + context = self.get_template_context(request, queryset, view) + return template.render(context) + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + return [ + coreapi.Field( + name=self.ordering_param, + required=False, + location='query', + schema=coreschema.String( + title=force_str(self.ordering_title), + description=force_str(self.ordering_description) + ) + ) + ] + + def get_schema_operation_parameters(self, view): + return [ + { + 'name': self.ordering_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.ordering_description), + 'schema': { + 'type': 'string', + }, + }, + ] diff --git a/venv/Lib/site-packages/rest_framework/generics.py b/venv/Lib/site-packages/rest_framework/generics.py new file mode 100644 index 0000000..55cfafd --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/generics.py @@ -0,0 +1,291 @@ +""" +Generic views that provide commonly needed behaviour. +""" +from django.core.exceptions import ValidationError +from django.db.models.query import QuerySet +from django.http import Http404 +from django.shortcuts import get_object_or_404 as _get_object_or_404 + +from rest_framework import mixins, views +from rest_framework.settings import api_settings + + +def get_object_or_404(queryset, *filter_args, **filter_kwargs): + """ + Same as Django's standard shortcut, but make sure to also raise 404 + if the filter_kwargs don't match the required types. + """ + try: + return _get_object_or_404(queryset, *filter_args, **filter_kwargs) + except (TypeError, ValueError, ValidationError): + raise Http404 + + +class GenericAPIView(views.APIView): + """ + Base class for all other generic views. + """ + # You'll need to either set these attributes, + # or override `get_queryset()`/`get_serializer_class()`. + # If you are overriding a view method, it is important that you call + # `get_queryset()` instead of accessing the `queryset` property directly, + # as `queryset` will get evaluated only once, and those results are cached + # for all subsequent requests. + queryset = None + serializer_class = None + + # If you want to use object lookups other than pk, set 'lookup_field'. + # For more complex lookup requirements override `get_object()`. + lookup_field = 'pk' + lookup_url_kwarg = None + + # The filter backend classes to use for queryset filtering + filter_backends = api_settings.DEFAULT_FILTER_BACKENDS + + # The style to use for queryset pagination. + pagination_class = api_settings.DEFAULT_PAGINATION_CLASS + + def get_queryset(self): + """ + Get the list of items for this view. + This must be an iterable, and may be a queryset. + Defaults to using `self.queryset`. + + This method should always be used rather than accessing `self.queryset` + directly, as `self.queryset` gets evaluated only once, and those results + are cached for all subsequent requests. + + You may want to override this if you need to provide different + querysets depending on the incoming request. + + (Eg. return a list of items that is specific to the user) + """ + assert self.queryset is not None, ( + "'%s' should either include a `queryset` attribute, " + "or override the `get_queryset()` method." + % self.__class__.__name__ + ) + + queryset = self.queryset + if isinstance(queryset, QuerySet): + # Ensure queryset is re-evaluated on each request. + queryset = queryset.all() + return queryset + + def get_object(self): + """ + Returns the object the view is displaying. + + You may want to override this if you need to provide non-standard + queryset lookups. Eg if objects are referenced using multiple + keyword arguments in the url conf. + """ + queryset = self.filter_queryset(self.get_queryset()) + + # Perform the lookup filtering. + lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field + + assert lookup_url_kwarg in self.kwargs, ( + 'Expected view %s to be called with a URL keyword argument ' + 'named "%s". Fix your URL conf, or set the `.lookup_field` ' + 'attribute on the view correctly.' % + (self.__class__.__name__, lookup_url_kwarg) + ) + + filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} + obj = get_object_or_404(queryset, **filter_kwargs) + + # May raise a permission denied + self.check_object_permissions(self.request, obj) + + return obj + + def get_serializer(self, *args, **kwargs): + """ + Return the serializer instance that should be used for validating and + deserializing input, and for serializing output. + """ + serializer_class = self.get_serializer_class() + kwargs.setdefault('context', self.get_serializer_context()) + return serializer_class(*args, **kwargs) + + def get_serializer_class(self): + """ + Return the class to use for the serializer. + Defaults to using `self.serializer_class`. + + You may want to override this if you need to provide different + serializations depending on the incoming request. + + (Eg. admins get full serialization, others get basic serialization) + """ + assert self.serializer_class is not None, ( + "'%s' should either include a `serializer_class` attribute, " + "or override the `get_serializer_class()` method." + % self.__class__.__name__ + ) + + return self.serializer_class + + def get_serializer_context(self): + """ + Extra context provided to the serializer class. + """ + return { + 'request': self.request, + 'format': self.format_kwarg, + 'view': self + } + + def filter_queryset(self, queryset): + """ + Given a queryset, filter it with whichever filter backend is in use. + + You are unlikely to want to override this method, although you may need + to call it either from a list view, or from a custom `get_object` + method if you want to apply the configured filtering backend to the + default queryset. + """ + for backend in list(self.filter_backends): + queryset = backend().filter_queryset(self.request, queryset, self) + return queryset + + @property + def paginator(self): + """ + The paginator instance associated with the view, or `None`. + """ + if not hasattr(self, '_paginator'): + if self.pagination_class is None: + self._paginator = None + else: + self._paginator = self.pagination_class() + return self._paginator + + def paginate_queryset(self, queryset): + """ + Return a single page of results, or `None` if pagination is disabled. + """ + if self.paginator is None: + return None + return self.paginator.paginate_queryset(queryset, self.request, view=self) + + def get_paginated_response(self, data): + """ + Return a paginated style `Response` object for the given output data. + """ + assert self.paginator is not None + return self.paginator.get_paginated_response(data) + + +# Concrete view classes that provide method handlers +# by composing the mixin classes with the base view. + +class CreateAPIView(mixins.CreateModelMixin, + GenericAPIView): + """ + Concrete view for creating a model instance. + """ + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + +class ListAPIView(mixins.ListModelMixin, + GenericAPIView): + """ + Concrete view for listing a queryset. + """ + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + +class RetrieveAPIView(mixins.RetrieveModelMixin, + GenericAPIView): + """ + Concrete view for retrieving a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + +class DestroyAPIView(mixins.DestroyModelMixin, + GenericAPIView): + """ + Concrete view for deleting a model instance. + """ + def delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) + + +class UpdateAPIView(mixins.UpdateModelMixin, + GenericAPIView): + """ + Concrete view for updating a model instance. + """ + def put(self, request, *args, **kwargs): + return self.update(request, *args, **kwargs) + + def patch(self, request, *args, **kwargs): + return self.partial_update(request, *args, **kwargs) + + +class ListCreateAPIView(mixins.ListModelMixin, + mixins.CreateModelMixin, + GenericAPIView): + """ + Concrete view for listing a queryset or creating a model instance. + """ + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + +class RetrieveUpdateAPIView(mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + GenericAPIView): + """ + Concrete view for retrieving, updating a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def put(self, request, *args, **kwargs): + return self.update(request, *args, **kwargs) + + def patch(self, request, *args, **kwargs): + return self.partial_update(request, *args, **kwargs) + + +class RetrieveDestroyAPIView(mixins.RetrieveModelMixin, + mixins.DestroyModelMixin, + GenericAPIView): + """ + Concrete view for retrieving or deleting a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) + + +class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + GenericAPIView): + """ + Concrete view for retrieving, updating or deleting a model instance. + """ + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def put(self, request, *args, **kwargs): + return self.update(request, *args, **kwargs) + + def patch(self, request, *args, **kwargs): + return self.partial_update(request, *args, **kwargs) + + def delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) diff --git a/venv/Lib/site-packages/rest_framework/locale/ach/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ach/LC_MESSAGES/django.mo new file mode 100644 index 0000000..0fe8c3e Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ach/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ar/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000..f793d7c Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ar/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/az/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/az/LC_MESSAGES/django.mo new file mode 100644 index 0000000..0864835 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/az/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/be/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/be/LC_MESSAGES/django.mo new file mode 100644 index 0000000..62321f7 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/be/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/bg/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/bg/LC_MESSAGES/django.mo new file mode 100644 index 0000000..71a0d8a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/bg/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ca/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ca/LC_MESSAGES/django.mo new file mode 100644 index 0000000..7da9971 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ca/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ca_ES/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ca_ES/LC_MESSAGES/django.mo new file mode 100644 index 0000000..f46cd7f Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ca_ES/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/cs/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/cs/LC_MESSAGES/django.mo new file mode 100644 index 0000000..ebf7db5 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/cs/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/da/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/da/LC_MESSAGES/django.mo new file mode 100644 index 0000000..d70bc13 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/da/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/de/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000..9868803 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/de/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/el/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/el/LC_MESSAGES/django.mo new file mode 100644 index 0000000..f434e6f Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/el/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/el_GR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/el_GR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..2f23ce5 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/el_GR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/en/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000..0770a9d Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/en/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/en_AU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/en_AU/LC_MESSAGES/django.mo new file mode 100644 index 0000000..3a595d9 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/en_AU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/en_CA/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/en_CA/LC_MESSAGES/django.mo new file mode 100644 index 0000000..930db1b Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/en_CA/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/en_US/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/en_US/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6c5906d Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/en_US/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/es/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000..16df627 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/es/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/et/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/et/LC_MESSAGES/django.mo new file mode 100644 index 0000000..e14ea9e Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/et/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/fa/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/fa/LC_MESSAGES/django.mo new file mode 100644 index 0000000..099318e Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/fa/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/fa_IR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/fa_IR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..52d3f3b Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/fa_IR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/fi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/fi/LC_MESSAGES/django.mo new file mode 100644 index 0000000..50a6d0f Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/fi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/fr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000..a1c0b3c Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/fr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/fr_CA/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/fr_CA/LC_MESSAGES/django.mo new file mode 100644 index 0000000..77bcff1 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/fr_CA/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/gl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/gl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..87d65da Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/gl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/gl_ES/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/gl_ES/LC_MESSAGES/django.mo new file mode 100644 index 0000000..a9a0273 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/gl_ES/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/he_IL/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/he_IL/LC_MESSAGES/django.mo new file mode 100644 index 0000000..acd15d9 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/he_IL/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/hu/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/hu/LC_MESSAGES/django.mo new file mode 100644 index 0000000..61f2852 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/hu/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/hy/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/hy/LC_MESSAGES/django.mo new file mode 100644 index 0000000..c5f3ebc Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/hy/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/id/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/id/LC_MESSAGES/django.mo new file mode 100644 index 0000000..c67e2a1 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/id/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/it/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6c84273 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/it/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ja/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ja/LC_MESSAGES/django.mo new file mode 100644 index 0000000..e949a57 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ja/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..c3aeb27 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/lt/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/lt/LC_MESSAGES/django.mo new file mode 100644 index 0000000..b43a6a8 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/lt/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/lv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/lv/LC_MESSAGES/django.mo new file mode 100644 index 0000000..7ee7172 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/lv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/mk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/mk/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4c13e5a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/mk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/nb/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/nb/LC_MESSAGES/django.mo new file mode 100644 index 0000000..bd983b2 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/nb/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ne_NP/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ne_NP/LC_MESSAGES/django.mo new file mode 100644 index 0000000..a9071d0 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ne_NP/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/nl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/nl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4691ca6 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/nl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/nn/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/nn/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6c28661 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/nn/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/no/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/no/LC_MESSAGES/django.mo new file mode 100644 index 0000000..808e0a2 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/no/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/pl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..f265186 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/pl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/pt/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/pt/LC_MESSAGES/django.mo new file mode 100644 index 0000000..653cce9 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/pt/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..5a6e378 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo new file mode 100644 index 0000000..67da616 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ro/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ro/LC_MESSAGES/django.mo new file mode 100644 index 0000000..0c9fb9c Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ro/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ru/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000..82688a4 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ru/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/ru_RU/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/ru_RU/LC_MESSAGES/django.mo new file mode 100644 index 0000000..d6da8f4 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/ru_RU/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/sk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/sk/LC_MESSAGES/django.mo new file mode 100644 index 0000000..561c98e Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/sk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/sl/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/sl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..7ec83f8 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/sl/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/sv/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/sv/LC_MESSAGES/django.mo new file mode 100644 index 0000000..fb1a9f6 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/sv/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/th/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/th/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4a2b85a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/th/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/tr/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/tr/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6386aab Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/tr/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/tr_TR/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/tr_TR/LC_MESSAGES/django.mo new file mode 100644 index 0000000..3751732 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/tr_TR/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/uk/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/uk/LC_MESSAGES/django.mo new file mode 100644 index 0000000..18c3242 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/uk/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/vi/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/vi/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6809b65 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/vi/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo new file mode 100644 index 0000000..3bd1fd3 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo new file mode 100644 index 0000000..670228a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo new file mode 100644 index 0000000..26ea0ce Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/locale/zh_TW/LC_MESSAGES/django.mo b/venv/Lib/site-packages/rest_framework/locale/zh_TW/LC_MESSAGES/django.mo new file mode 100644 index 0000000..2f85c2f Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/locale/zh_TW/LC_MESSAGES/django.mo differ diff --git a/venv/Lib/site-packages/rest_framework/management/__init__.py b/venv/Lib/site-packages/rest_framework/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/management/commands/__init__.py b/venv/Lib/site-packages/rest_framework/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/management/commands/generateschema.py b/venv/Lib/site-packages/rest_framework/management/commands/generateschema.py new file mode 100644 index 0000000..024306b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/management/commands/generateschema.py @@ -0,0 +1,69 @@ +from django.core.management.base import BaseCommand +from django.utils.module_loading import import_string + +from rest_framework import renderers +from rest_framework.schemas import coreapi +from rest_framework.schemas.openapi import SchemaGenerator + +OPENAPI_MODE = 'openapi' +COREAPI_MODE = 'coreapi' + + +class Command(BaseCommand): + help = "Generates configured API schema for project." + + def get_mode(self): + return COREAPI_MODE if coreapi.is_enabled() else OPENAPI_MODE + + def add_arguments(self, parser): + parser.add_argument('--title', dest="title", default='', type=str) + parser.add_argument('--url', dest="url", default=None, type=str) + parser.add_argument('--description', dest="description", default=None, type=str) + if self.get_mode() == COREAPI_MODE: + parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json', 'corejson'], default='openapi', type=str) + else: + parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json'], default='openapi', type=str) + parser.add_argument('--urlconf', dest="urlconf", default=None, type=str) + parser.add_argument('--generator_class', dest="generator_class", default=None, type=str) + parser.add_argument('--file', dest="file", default=None, type=str) + + def handle(self, *args, **options): + if options['generator_class']: + generator_class = import_string(options['generator_class']) + else: + generator_class = self.get_generator_class() + generator = generator_class( + url=options['url'], + title=options['title'], + description=options['description'], + urlconf=options['urlconf'], + ) + schema = generator.get_schema(request=None, public=True) + renderer = self.get_renderer(options['format']) + output = renderer.render(schema, renderer_context={}) + + if options['file']: + with open(options['file'], 'wb') as f: + f.write(output) + else: + self.stdout.write(output.decode()) + + def get_renderer(self, format): + if self.get_mode() == COREAPI_MODE: + renderer_cls = { + 'corejson': renderers.CoreJSONRenderer, + 'openapi': renderers.CoreAPIOpenAPIRenderer, + 'openapi-json': renderers.CoreAPIJSONOpenAPIRenderer, + }[format] + return renderer_cls() + + renderer_cls = { + 'openapi': renderers.OpenAPIRenderer, + 'openapi-json': renderers.JSONOpenAPIRenderer, + }[format] + return renderer_cls() + + def get_generator_class(self): + if self.get_mode() == COREAPI_MODE: + return coreapi.SchemaGenerator + return SchemaGenerator diff --git a/venv/Lib/site-packages/rest_framework/metadata.py b/venv/Lib/site-packages/rest_framework/metadata.py new file mode 100644 index 0000000..8a44f2a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/metadata.py @@ -0,0 +1,151 @@ +""" +The metadata API is used to allow customization of how `OPTIONS` requests +are handled. We currently provide a single default implementation that returns +some fairly ad-hoc information about the view. + +Future implementations might use JSON schema or other definitions in order +to return this information in a more standardized way. +""" +from collections import OrderedDict + +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.utils.encoding import force_str + +from rest_framework import exceptions, serializers +from rest_framework.request import clone_request +from rest_framework.utils.field_mapping import ClassLookupDict + + +class BaseMetadata: + def determine_metadata(self, request, view): + """ + Return a dictionary of metadata about the view. + Used to return responses for OPTIONS requests. + """ + raise NotImplementedError(".determine_metadata() must be overridden.") + + +class SimpleMetadata(BaseMetadata): + """ + This is the default metadata implementation. + It returns an ad-hoc set of information about the view. + There are not any formalized standards for `OPTIONS` responses + for us to base this on. + """ + label_lookup = ClassLookupDict({ + serializers.Field: 'field', + serializers.BooleanField: 'boolean', + serializers.NullBooleanField: 'boolean', + serializers.CharField: 'string', + serializers.UUIDField: 'string', + serializers.URLField: 'url', + serializers.EmailField: 'email', + serializers.RegexField: 'regex', + serializers.SlugField: 'slug', + serializers.IntegerField: 'integer', + serializers.FloatField: 'float', + serializers.DecimalField: 'decimal', + serializers.DateField: 'date', + serializers.DateTimeField: 'datetime', + serializers.TimeField: 'time', + serializers.ChoiceField: 'choice', + serializers.MultipleChoiceField: 'multiple choice', + serializers.FileField: 'file upload', + serializers.ImageField: 'image upload', + serializers.ListField: 'list', + serializers.DictField: 'nested object', + serializers.Serializer: 'nested object', + }) + + def determine_metadata(self, request, view): + metadata = OrderedDict() + metadata['name'] = view.get_view_name() + metadata['description'] = view.get_view_description() + metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes] + metadata['parses'] = [parser.media_type for parser in view.parser_classes] + if hasattr(view, 'get_serializer'): + actions = self.determine_actions(request, view) + if actions: + metadata['actions'] = actions + return metadata + + def determine_actions(self, request, view): + """ + For generic class based views we return information about + the fields that are accepted for 'PUT' and 'POST' methods. + """ + actions = {} + for method in {'PUT', 'POST'} & set(view.allowed_methods): + view.request = clone_request(request, method) + try: + # Test global permissions + if hasattr(view, 'check_permissions'): + view.check_permissions(view.request) + # Test object permissions + if method == 'PUT' and hasattr(view, 'get_object'): + view.get_object() + except (exceptions.APIException, PermissionDenied, Http404): + pass + else: + # If user has appropriate permissions for the view, include + # appropriate metadata about the fields that should be supplied. + serializer = view.get_serializer() + actions[method] = self.get_serializer_info(serializer) + finally: + view.request = request + + return actions + + def get_serializer_info(self, serializer): + """ + Given an instance of a serializer, return a dictionary of metadata + about its fields. + """ + if hasattr(serializer, 'child'): + # If this is a `ListSerializer` then we want to examine the + # underlying child serializer instance instead. + serializer = serializer.child + return OrderedDict([ + (field_name, self.get_field_info(field)) + for field_name, field in serializer.fields.items() + if not isinstance(field, serializers.HiddenField) + ]) + + def get_field_info(self, field): + """ + Given an instance of a serializer field, return a dictionary + of metadata about it. + """ + field_info = OrderedDict() + field_info['type'] = self.label_lookup[field] + field_info['required'] = getattr(field, 'required', False) + + attrs = [ + 'read_only', 'label', 'help_text', + 'min_length', 'max_length', + 'min_value', 'max_value' + ] + + for attr in attrs: + value = getattr(field, attr, None) + if value is not None and value != '': + field_info[attr] = force_str(value, strings_only=True) + + if getattr(field, 'child', None): + field_info['child'] = self.get_field_info(field.child) + elif getattr(field, 'fields', None): + field_info['children'] = self.get_serializer_info(field) + + if (not field_info.get('read_only') and + not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and + hasattr(field, 'choices')): + field_info['choices'] = [ + { + 'value': choice_value, + 'display_name': force_str(choice_name, strings_only=True) + } + for choice_value, choice_name in field.choices.items() + ] + + return field_info diff --git a/venv/Lib/site-packages/rest_framework/mixins.py b/venv/Lib/site-packages/rest_framework/mixins.py new file mode 100644 index 0000000..7fa8947 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/mixins.py @@ -0,0 +1,95 @@ +""" +Basic building blocks for generic class based views. + +We don't bind behaviour to http method handlers yet, +which allows mixin classes to be composed in interesting ways. +""" +from rest_framework import status +from rest_framework.response import Response +from rest_framework.settings import api_settings + + +class CreateModelMixin: + """ + Create a model instance. + """ + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + def perform_create(self, serializer): + serializer.save() + + def get_success_headers(self, data): + try: + return {'Location': str(data[api_settings.URL_FIELD_NAME])} + except (TypeError, KeyError): + return {} + + +class ListModelMixin: + """ + List a queryset. + """ + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + +class RetrieveModelMixin: + """ + Retrieve a model instance. + """ + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + return Response(serializer.data) + + +class UpdateModelMixin: + """ + Update a model instance. + """ + def update(self, request, *args, **kwargs): + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=partial) + serializer.is_valid(raise_exception=True) + self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} + + return Response(serializer.data) + + def perform_update(self, serializer): + serializer.save() + + def partial_update(self, request, *args, **kwargs): + kwargs['partial'] = True + return self.update(request, *args, **kwargs) + + +class DestroyModelMixin: + """ + Destroy a model instance. + """ + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + self.perform_destroy(instance) + return Response(status=status.HTTP_204_NO_CONTENT) + + def perform_destroy(self, instance): + instance.delete() diff --git a/venv/Lib/site-packages/rest_framework/negotiation.py b/venv/Lib/site-packages/rest_framework/negotiation.py new file mode 100644 index 0000000..76113a8 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/negotiation.py @@ -0,0 +1,95 @@ +""" +Content negotiation deals with selecting an appropriate renderer given the +incoming request. Typically this will be based on the request's Accept header. +""" +from django.http import Http404 + +from rest_framework import HTTP_HEADER_ENCODING, exceptions +from rest_framework.settings import api_settings +from rest_framework.utils.mediatypes import ( + _MediaType, media_type_matches, order_by_precedence +) + + +class BaseContentNegotiation: + def select_parser(self, request, parsers): + raise NotImplementedError('.select_parser() must be implemented') + + def select_renderer(self, request, renderers, format_suffix=None): + raise NotImplementedError('.select_renderer() must be implemented') + + +class DefaultContentNegotiation(BaseContentNegotiation): + settings = api_settings + + def select_parser(self, request, parsers): + """ + Given a list of parsers and a media type, return the appropriate + parser to handle the incoming request. + """ + for parser in parsers: + if media_type_matches(parser.media_type, request.content_type): + return parser + return None + + def select_renderer(self, request, renderers, format_suffix=None): + """ + Given a request and a list of renderers, return a two-tuple of: + (renderer, media type). + """ + # Allow URL style format override. eg. "?format=json + format_query_param = self.settings.URL_FORMAT_OVERRIDE + format = format_suffix or request.query_params.get(format_query_param) + + if format: + renderers = self.filter_renderers(renderers, format) + + accepts = self.get_accept_list(request) + + # Check the acceptable media types against each renderer, + # attempting more specific media types first + # NB. The inner loop here isn't as bad as it first looks :) + # Worst case is we're looping over len(accept_list) * len(self.renderers) + for media_type_set in order_by_precedence(accepts): + for renderer in renderers: + for media_type in media_type_set: + if media_type_matches(renderer.media_type, media_type): + # Return the most specific media type as accepted. + media_type_wrapper = _MediaType(media_type) + if ( + _MediaType(renderer.media_type).precedence > + media_type_wrapper.precedence + ): + # Eg client requests '*/*' + # Accepted media type is 'application/json' + full_media_type = ';'.join( + (renderer.media_type,) + + tuple('{}={}'.format( + key, value.decode(HTTP_HEADER_ENCODING)) + for key, value in media_type_wrapper.params.items())) + return renderer, full_media_type + else: + # Eg client requests 'application/json; indent=8' + # Accepted media type is 'application/json; indent=8' + return renderer, media_type + + raise exceptions.NotAcceptable(available_renderers=renderers) + + def filter_renderers(self, renderers, format): + """ + If there is a '.json' style format suffix, filter the renderers + so that we only negotiation against those that accept that format. + """ + renderers = [renderer for renderer in renderers + if renderer.format == format] + if not renderers: + raise Http404 + return renderers + + def get_accept_list(self, request): + """ + Given the incoming request, return a tokenized list of media + type strings. + """ + header = request.META.get('HTTP_ACCEPT', '*/*') + return [token.strip() for token in header.split(',')] diff --git a/venv/Lib/site-packages/rest_framework/pagination.py b/venv/Lib/site-packages/rest_framework/pagination.py new file mode 100644 index 0000000..e815d8d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/pagination.py @@ -0,0 +1,980 @@ +""" +Pagination serializers determine the structure of the output that should +be used for paginated responses. +""" +from base64 import b64decode, b64encode +from collections import OrderedDict, namedtuple +from urllib import parse + +from django.core.paginator import InvalidPage +from django.core.paginator import Paginator as DjangoPaginator +from django.template import loader +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ + +from rest_framework.compat import coreapi, coreschema +from rest_framework.exceptions import NotFound +from rest_framework.response import Response +from rest_framework.settings import api_settings +from rest_framework.utils.urls import remove_query_param, replace_query_param + + +def _positive_int(integer_string, strict=False, cutoff=None): + """ + Cast a string to a strictly positive integer. + """ + ret = int(integer_string) + if ret < 0 or (ret == 0 and strict): + raise ValueError() + if cutoff: + return min(ret, cutoff) + return ret + + +def _divide_with_ceil(a, b): + """ + Returns 'a' divided by 'b', with any remainder rounded up. + """ + if a % b: + return (a // b) + 1 + + return a // b + + +def _get_displayed_page_numbers(current, final): + """ + This utility function determines a list of page numbers to display. + This gives us a nice contextually relevant set of page numbers. + + For example: + current=14, final=16 -> [1, None, 13, 14, 15, 16] + + This implementation gives one page to each side of the cursor, + or two pages to the side when the cursor is at the edge, then + ensures that any breaks between non-continuous page numbers never + remove only a single page. + + For an alternative implementation which gives two pages to each side of + the cursor, eg. as in GitHub issue list pagination, see: + + https://gist.github.com/tomchristie/321140cebb1c4a558b15 + """ + assert current >= 1 + assert final >= current + + if final <= 5: + return list(range(1, final + 1)) + + # We always include the first two pages, last two pages, and + # two pages either side of the current page. + included = {1, current - 1, current, current + 1, final} + + # If the break would only exclude a single page number then we + # may as well include the page number instead of the break. + if current <= 4: + included.add(2) + included.add(3) + if current >= final - 3: + included.add(final - 1) + included.add(final - 2) + + # Now sort the page numbers and drop anything outside the limits. + included = [ + idx for idx in sorted(included) + if 0 < idx <= final + ] + + # Finally insert any `...` breaks + if current > 4: + included.insert(1, None) + if current < final - 3: + included.insert(len(included) - 1, None) + return included + + +def _get_page_links(page_numbers, current, url_func): + """ + Given a list of page numbers and `None` page breaks, + return a list of `PageLink` objects. + """ + page_links = [] + for page_number in page_numbers: + if page_number is None: + page_link = PAGE_BREAK + else: + page_link = PageLink( + url=url_func(page_number), + number=page_number, + is_active=(page_number == current), + is_break=False + ) + page_links.append(page_link) + return page_links + + +def _reverse_ordering(ordering_tuple): + """ + Given an order_by tuple such as `('-created', 'uuid')` reverse the + ordering and return a new tuple, eg. `('created', '-uuid')`. + """ + def invert(x): + return x[1:] if x.startswith('-') else '-' + x + + return tuple([invert(item) for item in ordering_tuple]) + + +Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) +PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break']) + +PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True) + + +class BasePagination: + display_page_controls = False + + def paginate_queryset(self, queryset, request, view=None): # pragma: no cover + raise NotImplementedError('paginate_queryset() must be implemented.') + + def get_paginated_response(self, data): # pragma: no cover + raise NotImplementedError('get_paginated_response() must be implemented.') + + def get_paginated_response_schema(self, schema): + return schema + + def to_html(self): # pragma: no cover + raise NotImplementedError('to_html() must be implemented to display page controls.') + + def get_results(self, data): + return data['results'] + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [] + + def get_schema_operation_parameters(self, view): + return [] + + +class PageNumberPagination(BasePagination): + """ + A simple page number based style that supports page numbers as + query parameters. For example: + + http://api.example.org/accounts/?page=4 + http://api.example.org/accounts/?page=4&page_size=100 + """ + # The default page size. + # Defaults to `None`, meaning pagination is disabled. + page_size = api_settings.PAGE_SIZE + + django_paginator_class = DjangoPaginator + + # Client can control the page using this query parameter. + page_query_param = 'page' + page_query_description = _('A page number within the paginated result set.') + + # Client can control the page size using this query parameter. + # Default is 'None'. Set to eg 'page_size' to enable usage. + page_size_query_param = None + page_size_query_description = _('Number of results to return per page.') + + # Set to an integer to limit the maximum page size the client may request. + # Only relevant if 'page_size_query_param' has also been set. + max_page_size = None + + last_page_strings = ('last',) + + template = 'rest_framework/pagination/numbers.html' + + invalid_page_message = _('Invalid page.') + + def paginate_queryset(self, queryset, request, view=None): + """ + Paginate a queryset if required, either returning a + page object, or `None` if pagination is not configured for this view. + """ + page_size = self.get_page_size(request) + if not page_size: + return None + + paginator = self.django_paginator_class(queryset, page_size) + page_number = self.get_page_number(request, paginator) + + try: + self.page = paginator.page(page_number) + except InvalidPage as exc: + msg = self.invalid_page_message.format( + page_number=page_number, message=str(exc) + ) + raise NotFound(msg) + + if paginator.num_pages > 1 and self.template is not None: + # The browsable API should display pagination controls. + self.display_page_controls = True + + self.request = request + return list(self.page) + + def get_page_number(self, request, paginator): + page_number = request.query_params.get(self.page_query_param, 1) + if page_number in self.last_page_strings: + page_number = paginator.num_pages + return page_number + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('count', self.page.paginator.count), + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', data) + ])) + + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{page_query_param}=4'.format( + page_query_param=self.page_query_param) + }, + 'previous': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{page_query_param}=2'.format( + page_query_param=self.page_query_param) + }, + 'results': schema, + }, + } + + def get_page_size(self, request): + if self.page_size_query_param: + try: + return _positive_int( + request.query_params[self.page_size_query_param], + strict=True, + cutoff=self.max_page_size + ) + except (KeyError, ValueError): + pass + + return self.page_size + + def get_next_link(self): + if not self.page.has_next(): + return None + url = self.request.build_absolute_uri() + page_number = self.page.next_page_number() + return replace_query_param(url, self.page_query_param, page_number) + + def get_previous_link(self): + if not self.page.has_previous(): + return None + url = self.request.build_absolute_uri() + page_number = self.page.previous_page_number() + if page_number == 1: + return remove_query_param(url, self.page_query_param) + return replace_query_param(url, self.page_query_param, page_number) + + def get_html_context(self): + base_url = self.request.build_absolute_uri() + + def page_number_to_url(page_number): + if page_number == 1: + return remove_query_param(base_url, self.page_query_param) + else: + return replace_query_param(base_url, self.page_query_param, page_number) + + current = self.page.number + final = self.page.paginator.num_pages + page_numbers = _get_displayed_page_numbers(current, final) + page_links = _get_page_links(page_numbers, current, page_number_to_url) + + return { + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link(), + 'page_links': page_links + } + + def to_html(self): + template = loader.get_template(self.template) + context = self.get_html_context() + return template.render(context) + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + fields = [ + coreapi.Field( + name=self.page_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Page', + description=force_str(self.page_query_description) + ) + ) + ] + if self.page_size_query_param is not None: + fields.append( + coreapi.Field( + name=self.page_size_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Page size', + description=force_str(self.page_size_query_description) + ) + ) + ) + return fields + + def get_schema_operation_parameters(self, view): + parameters = [ + { + 'name': self.page_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.page_query_description), + 'schema': { + 'type': 'integer', + }, + }, + ] + if self.page_size_query_param is not None: + parameters.append( + { + 'name': self.page_size_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.page_size_query_description), + 'schema': { + 'type': 'integer', + }, + }, + ) + return parameters + + +class LimitOffsetPagination(BasePagination): + """ + A limit/offset based style. For example: + + http://api.example.org/accounts/?limit=100 + http://api.example.org/accounts/?offset=400&limit=100 + """ + default_limit = api_settings.PAGE_SIZE + limit_query_param = 'limit' + limit_query_description = _('Number of results to return per page.') + offset_query_param = 'offset' + offset_query_description = _('The initial index from which to return the results.') + max_limit = None + template = 'rest_framework/pagination/numbers.html' + + def paginate_queryset(self, queryset, request, view=None): + self.limit = self.get_limit(request) + if self.limit is None: + return None + + self.count = self.get_count(queryset) + self.offset = self.get_offset(request) + self.request = request + if self.count > self.limit and self.template is not None: + self.display_page_controls = True + + if self.count == 0 or self.offset > self.count: + return [] + return list(queryset[self.offset:self.offset + self.limit]) + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('count', self.count), + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', data) + ])) + + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'count': { + 'type': 'integer', + 'example': 123, + }, + 'next': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{offset_param}=400&{limit_param}=100'.format( + offset_param=self.offset_query_param, limit_param=self.limit_query_param), + }, + 'previous': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{offset_param}=200&{limit_param}=100'.format( + offset_param=self.offset_query_param, limit_param=self.limit_query_param), + }, + 'results': schema, + }, + } + + def get_limit(self, request): + if self.limit_query_param: + try: + return _positive_int( + request.query_params[self.limit_query_param], + strict=True, + cutoff=self.max_limit + ) + except (KeyError, ValueError): + pass + + return self.default_limit + + def get_offset(self, request): + try: + return _positive_int( + request.query_params[self.offset_query_param], + ) + except (KeyError, ValueError): + return 0 + + def get_next_link(self): + if self.offset + self.limit >= self.count: + return None + + url = self.request.build_absolute_uri() + url = replace_query_param(url, self.limit_query_param, self.limit) + + offset = self.offset + self.limit + return replace_query_param(url, self.offset_query_param, offset) + + def get_previous_link(self): + if self.offset <= 0: + return None + + url = self.request.build_absolute_uri() + url = replace_query_param(url, self.limit_query_param, self.limit) + + if self.offset - self.limit <= 0: + return remove_query_param(url, self.offset_query_param) + + offset = self.offset - self.limit + return replace_query_param(url, self.offset_query_param, offset) + + def get_html_context(self): + base_url = self.request.build_absolute_uri() + + if self.limit: + current = _divide_with_ceil(self.offset, self.limit) + 1 + + # The number of pages is a little bit fiddly. + # We need to sum both the number of pages from current offset to end + # plus the number of pages up to the current offset. + # When offset is not strictly divisible by the limit then we may + # end up introducing an extra page as an artifact. + final = ( + _divide_with_ceil(self.count - self.offset, self.limit) + + _divide_with_ceil(self.offset, self.limit) + ) + + final = max(final, 1) + else: + current = 1 + final = 1 + + if current > final: + current = final + + def page_number_to_url(page_number): + if page_number == 1: + return remove_query_param(base_url, self.offset_query_param) + else: + offset = self.offset + ((page_number - current) * self.limit) + return replace_query_param(base_url, self.offset_query_param, offset) + + page_numbers = _get_displayed_page_numbers(current, final) + page_links = _get_page_links(page_numbers, current, page_number_to_url) + + return { + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link(), + 'page_links': page_links + } + + def to_html(self): + template = loader.get_template(self.template) + context = self.get_html_context() + return template.render(context) + + def get_count(self, queryset): + """ + Determine an object count, supporting either querysets or regular lists. + """ + try: + return queryset.count() + except (AttributeError, TypeError): + return len(queryset) + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + return [ + coreapi.Field( + name=self.limit_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Limit', + description=force_str(self.limit_query_description) + ) + ), + coreapi.Field( + name=self.offset_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Offset', + description=force_str(self.offset_query_description) + ) + ) + ] + + def get_schema_operation_parameters(self, view): + parameters = [ + { + 'name': self.limit_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.limit_query_description), + 'schema': { + 'type': 'integer', + }, + }, + { + 'name': self.offset_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.offset_query_description), + 'schema': { + 'type': 'integer', + }, + }, + ] + return parameters + + +class CursorPagination(BasePagination): + """ + The cursor pagination implementation is necessarily complex. + For an overview of the position/offset style we use, see this post: + https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api + """ + cursor_query_param = 'cursor' + cursor_query_description = _('The pagination cursor value.') + page_size = api_settings.PAGE_SIZE + invalid_cursor_message = _('Invalid cursor') + ordering = '-created' + template = 'rest_framework/pagination/previous_and_next.html' + + # Client can control the page size using this query parameter. + # Default is 'None'. Set to eg 'page_size' to enable usage. + page_size_query_param = None + page_size_query_description = _('Number of results to return per page.') + + # Set to an integer to limit the maximum page size the client may request. + # Only relevant if 'page_size_query_param' has also been set. + max_page_size = None + + # The offset in the cursor is used in situations where we have a + # nearly-unique index. (Eg millisecond precision creation timestamps) + # We guard against malicious users attempting to cause expensive database + # queries, by having a hard cap on the maximum possible size of the offset. + offset_cutoff = 1000 + + def paginate_queryset(self, queryset, request, view=None): + self.page_size = self.get_page_size(request) + if not self.page_size: + return None + + self.base_url = request.build_absolute_uri() + self.ordering = self.get_ordering(request, queryset, view) + + self.cursor = self.decode_cursor(request) + if self.cursor is None: + (offset, reverse, current_position) = (0, False, None) + else: + (offset, reverse, current_position) = self.cursor + + # Cursor pagination always enforces an ordering. + if reverse: + queryset = queryset.order_by(*_reverse_ordering(self.ordering)) + else: + queryset = queryset.order_by(*self.ordering) + + # If we have a cursor with a fixed position then filter by that. + if current_position is not None: + order = self.ordering[0] + is_reversed = order.startswith('-') + order_attr = order.lstrip('-') + + # Test for: (cursor reversed) XOR (queryset reversed) + if self.cursor.reverse != is_reversed: + kwargs = {order_attr + '__lt': current_position} + else: + kwargs = {order_attr + '__gt': current_position} + + queryset = queryset.filter(**kwargs) + + # If we have an offset cursor then offset the entire page by that amount. + # We also always fetch an extra item in order to determine if there is a + # page following on from this one. + results = list(queryset[offset:offset + self.page_size + 1]) + self.page = list(results[:self.page_size]) + + # Determine the position of the final item following the page. + if len(results) > len(self.page): + has_following_position = True + following_position = self._get_position_from_instance(results[-1], self.ordering) + else: + has_following_position = False + following_position = None + + if reverse: + # If we have a reverse queryset, then the query ordering was in reverse + # so we need to reverse the items again before returning them to the user. + self.page = list(reversed(self.page)) + + # Determine next and previous positions for reverse cursors. + self.has_next = (current_position is not None) or (offset > 0) + self.has_previous = has_following_position + if self.has_next: + self.next_position = current_position + if self.has_previous: + self.previous_position = following_position + else: + # Determine next and previous positions for forward cursors. + self.has_next = has_following_position + self.has_previous = (current_position is not None) or (offset > 0) + if self.has_next: + self.next_position = following_position + if self.has_previous: + self.previous_position = current_position + + # Display page controls in the browsable API if there is more + # than one page. + if (self.has_previous or self.has_next) and self.template is not None: + self.display_page_controls = True + + return self.page + + def get_page_size(self, request): + if self.page_size_query_param: + try: + return _positive_int( + request.query_params[self.page_size_query_param], + strict=True, + cutoff=self.max_page_size + ) + except (KeyError, ValueError): + pass + + return self.page_size + + def get_next_link(self): + if not self.has_next: + return None + + if self.page and self.cursor and self.cursor.reverse and self.cursor.offset != 0: + # If we're reversing direction and we have an offset cursor + # then we cannot use the first position we find as a marker. + compare = self._get_position_from_instance(self.page[-1], self.ordering) + else: + compare = self.next_position + offset = 0 + + has_item_with_unique_position = False + for item in reversed(self.page): + position = self._get_position_from_instance(item, self.ordering) + if position != compare: + # The item in this position and the item following it + # have different positions. We can use this position as + # our marker. + has_item_with_unique_position = True + break + + # The item in this position has the same position as the item + # following it, we can't use it as a marker position, so increment + # the offset and keep seeking to the previous item. + compare = position + offset += 1 + + if self.page and not has_item_with_unique_position: + # There were no unique positions in the page. + if not self.has_previous: + # We are on the first page. + # Our cursor will have an offset equal to the page size, + # but no position to filter against yet. + offset = self.page_size + position = None + elif self.cursor.reverse: + # The change in direction will introduce a paging artifact, + # where we end up skipping forward a few extra items. + offset = 0 + position = self.previous_position + else: + # Use the position from the existing cursor and increment + # it's offset by the page size. + offset = self.cursor.offset + self.page_size + position = self.previous_position + + if not self.page: + position = self.next_position + + cursor = Cursor(offset=offset, reverse=False, position=position) + return self.encode_cursor(cursor) + + def get_previous_link(self): + if not self.has_previous: + return None + + if self.page and self.cursor and not self.cursor.reverse and self.cursor.offset != 0: + # If we're reversing direction and we have an offset cursor + # then we cannot use the first position we find as a marker. + compare = self._get_position_from_instance(self.page[0], self.ordering) + else: + compare = self.previous_position + offset = 0 + + has_item_with_unique_position = False + for item in self.page: + position = self._get_position_from_instance(item, self.ordering) + if position != compare: + # The item in this position and the item following it + # have different positions. We can use this position as + # our marker. + has_item_with_unique_position = True + break + + # The item in this position has the same position as the item + # following it, we can't use it as a marker position, so increment + # the offset and keep seeking to the previous item. + compare = position + offset += 1 + + if self.page and not has_item_with_unique_position: + # There were no unique positions in the page. + if not self.has_next: + # We are on the final page. + # Our cursor will have an offset equal to the page size, + # but no position to filter against yet. + offset = self.page_size + position = None + elif self.cursor.reverse: + # Use the position from the existing cursor and increment + # it's offset by the page size. + offset = self.cursor.offset + self.page_size + position = self.next_position + else: + # The change in direction will introduce a paging artifact, + # where we end up skipping back a few extra items. + offset = 0 + position = self.next_position + + if not self.page: + position = self.previous_position + + cursor = Cursor(offset=offset, reverse=True, position=position) + return self.encode_cursor(cursor) + + def get_ordering(self, request, queryset, view): + """ + Return a tuple of strings, that may be used in an `order_by` method. + """ + ordering_filters = [ + filter_cls for filter_cls in getattr(view, 'filter_backends', []) + if hasattr(filter_cls, 'get_ordering') + ] + + if ordering_filters: + # If a filter exists on the view that implements `get_ordering` + # then we defer to that filter to determine the ordering. + filter_cls = ordering_filters[0] + filter_instance = filter_cls() + ordering = filter_instance.get_ordering(request, queryset, view) + assert ordering is not None, ( + 'Using cursor pagination, but filter class {filter_cls} ' + 'returned a `None` ordering.'.format( + filter_cls=filter_cls.__name__ + ) + ) + else: + # The default case is to check for an `ordering` attribute + # on this pagination instance. + ordering = self.ordering + assert ordering is not None, ( + 'Using cursor pagination, but no ordering attribute was declared ' + 'on the pagination class.' + ) + assert '__' not in ordering, ( + 'Cursor pagination does not support double underscore lookups ' + 'for orderings. Orderings should be an unchanging, unique or ' + 'nearly-unique field on the model, such as "-created" or "pk".' + ) + + assert isinstance(ordering, (str, list, tuple)), ( + 'Invalid ordering. Expected string or tuple, but got {type}'.format( + type=type(ordering).__name__ + ) + ) + + if isinstance(ordering, str): + return (ordering,) + return tuple(ordering) + + def decode_cursor(self, request): + """ + Given a request with a cursor, return a `Cursor` instance. + """ + # Determine if we have a cursor, and if so then decode it. + encoded = request.query_params.get(self.cursor_query_param) + if encoded is None: + return None + + try: + querystring = b64decode(encoded.encode('ascii')).decode('ascii') + tokens = parse.parse_qs(querystring, keep_blank_values=True) + + offset = tokens.get('o', ['0'])[0] + offset = _positive_int(offset, cutoff=self.offset_cutoff) + + reverse = tokens.get('r', ['0'])[0] + reverse = bool(int(reverse)) + + position = tokens.get('p', [None])[0] + except (TypeError, ValueError): + raise NotFound(self.invalid_cursor_message) + + return Cursor(offset=offset, reverse=reverse, position=position) + + def encode_cursor(self, cursor): + """ + Given a Cursor instance, return an url with encoded cursor. + """ + tokens = {} + if cursor.offset != 0: + tokens['o'] = str(cursor.offset) + if cursor.reverse: + tokens['r'] = '1' + if cursor.position is not None: + tokens['p'] = cursor.position + + querystring = parse.urlencode(tokens, doseq=True) + encoded = b64encode(querystring.encode('ascii')).decode('ascii') + return replace_query_param(self.base_url, self.cursor_query_param, encoded) + + def _get_position_from_instance(self, instance, ordering): + field_name = ordering[0].lstrip('-') + if isinstance(instance, dict): + attr = instance[field_name] + else: + attr = getattr(instance, field_name) + return str(attr) + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', data) + ])) + + def get_paginated_response_schema(self, schema): + return { + 'type': 'object', + 'properties': { + 'next': { + 'type': 'string', + 'nullable': True, + }, + 'previous': { + 'type': 'string', + 'nullable': True, + }, + 'results': schema, + }, + } + + def get_html_context(self): + return { + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link() + } + + def to_html(self): + template = loader.get_template(self.template) + context = self.get_html_context() + return template.render(context) + + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`' + fields = [ + coreapi.Field( + name=self.cursor_query_param, + required=False, + location='query', + schema=coreschema.String( + title='Cursor', + description=force_str(self.cursor_query_description) + ) + ) + ] + if self.page_size_query_param is not None: + fields.append( + coreapi.Field( + name=self.page_size_query_param, + required=False, + location='query', + schema=coreschema.Integer( + title='Page size', + description=force_str(self.page_size_query_description) + ) + ) + ) + return fields + + def get_schema_operation_parameters(self, view): + parameters = [ + { + 'name': self.cursor_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.cursor_query_description), + 'schema': { + 'type': 'string', + }, + } + ] + if self.page_size_query_param is not None: + parameters.append( + { + 'name': self.page_size_query_param, + 'required': False, + 'in': 'query', + 'description': force_str(self.page_size_query_description), + 'schema': { + 'type': 'integer', + }, + } + ) + return parameters diff --git a/venv/Lib/site-packages/rest_framework/parsers.py b/venv/Lib/site-packages/rest_framework/parsers.py new file mode 100644 index 0000000..fc4eb14 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/parsers.py @@ -0,0 +1,223 @@ +""" +Parsers are used to parse the content of incoming HTTP requests. + +They give us a generic way of being able to handle various media types +on the request, such as form content or json encoded data. +""" +import codecs +from urllib import parse + +from django.conf import settings +from django.core.files.uploadhandler import StopFutureHandlers +from django.http import QueryDict +from django.http.multipartparser import ChunkIter +from django.http.multipartparser import \ + MultiPartParser as DjangoMultiPartParser +from django.http.multipartparser import MultiPartParserError, parse_header +from django.utils.encoding import force_str + +from rest_framework import renderers +from rest_framework.exceptions import ParseError +from rest_framework.settings import api_settings +from rest_framework.utils import json + + +class DataAndFiles: + def __init__(self, data, files): + self.data = data + self.files = files + + +class BaseParser: + """ + All parsers should extend `BaseParser`, specifying a `media_type` + attribute, and overriding the `.parse()` method. + """ + media_type = None + + def parse(self, stream, media_type=None, parser_context=None): + """ + Given a stream to read from, return the parsed representation. + Should return parsed data, or a `DataAndFiles` object consisting of the + parsed data and files. + """ + raise NotImplementedError(".parse() must be overridden.") + + +class JSONParser(BaseParser): + """ + Parses JSON-serialized data. + """ + media_type = 'application/json' + renderer_class = renderers.JSONRenderer + strict = api_settings.STRICT_JSON + + def parse(self, stream, media_type=None, parser_context=None): + """ + Parses the incoming bytestream as JSON and returns the resulting data. + """ + parser_context = parser_context or {} + encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + + try: + decoded_stream = codecs.getreader(encoding)(stream) + parse_constant = json.strict_constant if self.strict else None + return json.load(decoded_stream, parse_constant=parse_constant) + except ValueError as exc: + raise ParseError('JSON parse error - %s' % str(exc)) + + +class FormParser(BaseParser): + """ + Parser for form data. + """ + media_type = 'application/x-www-form-urlencoded' + + def parse(self, stream, media_type=None, parser_context=None): + """ + Parses the incoming bytestream as a URL encoded form, + and returns the resulting QueryDict. + """ + parser_context = parser_context or {} + encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + return QueryDict(stream.read(), encoding=encoding) + + +class MultiPartParser(BaseParser): + """ + Parser for multipart form data, which may include file data. + """ + media_type = 'multipart/form-data' + + def parse(self, stream, media_type=None, parser_context=None): + """ + Parses the incoming bytestream as a multipart encoded form, + and returns a DataAndFiles object. + + `.data` will be a `QueryDict` containing all the form parameters. + `.files` will be a `QueryDict` containing all the form files. + """ + parser_context = parser_context or {} + request = parser_context['request'] + encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + meta = request.META.copy() + meta['CONTENT_TYPE'] = media_type + upload_handlers = request.upload_handlers + + try: + parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding) + data, files = parser.parse() + return DataAndFiles(data, files) + except MultiPartParserError as exc: + raise ParseError('Multipart form parse error - %s' % str(exc)) + + +class FileUploadParser(BaseParser): + """ + Parser for file upload data. + """ + media_type = '*/*' + errors = { + 'unhandled': 'FileUpload parse error - none of upload handlers can handle the stream', + 'no_filename': 'Missing filename. Request should include a Content-Disposition header with a filename parameter.', + } + + def parse(self, stream, media_type=None, parser_context=None): + """ + Treats the incoming bytestream as a raw file upload and returns + a `DataAndFiles` object. + + `.data` will be None (we expect request body to be a file content). + `.files` will be a `QueryDict` containing one 'file' element. + """ + parser_context = parser_context or {} + request = parser_context['request'] + encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) + meta = request.META + upload_handlers = request.upload_handlers + filename = self.get_filename(stream, media_type, parser_context) + + if not filename: + raise ParseError(self.errors['no_filename']) + + # Note that this code is extracted from Django's handling of + # file uploads in MultiPartParser. + content_type = meta.get('HTTP_CONTENT_TYPE', + meta.get('CONTENT_TYPE', '')) + try: + content_length = int(meta.get('HTTP_CONTENT_LENGTH', + meta.get('CONTENT_LENGTH', 0))) + except (ValueError, TypeError): + content_length = None + + # See if the handler will want to take care of the parsing. + for handler in upload_handlers: + result = handler.handle_raw_input(stream, + meta, + content_length, + None, + encoding) + if result is not None: + return DataAndFiles({}, {'file': result[1]}) + + # This is the standard case. + possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] + chunk_size = min([2 ** 31 - 4] + possible_sizes) + chunks = ChunkIter(stream, chunk_size) + counters = [0] * len(upload_handlers) + + for index, handler in enumerate(upload_handlers): + try: + handler.new_file(None, filename, content_type, + content_length, encoding) + except StopFutureHandlers: + upload_handlers = upload_handlers[:index + 1] + break + + for chunk in chunks: + for index, handler in enumerate(upload_handlers): + chunk_length = len(chunk) + chunk = handler.receive_data_chunk(chunk, counters[index]) + counters[index] += chunk_length + if chunk is None: + break + + for index, handler in enumerate(upload_handlers): + file_obj = handler.file_complete(counters[index]) + if file_obj is not None: + return DataAndFiles({}, {'file': file_obj}) + + raise ParseError(self.errors['unhandled']) + + def get_filename(self, stream, media_type, parser_context): + """ + Detects the uploaded file name. First searches a 'filename' url kwarg. + Then tries to parse Content-Disposition header. + """ + try: + return parser_context['kwargs']['filename'] + except KeyError: + pass + + try: + meta = parser_context['request'].META + disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode()) + filename_parm = disposition[1] + if 'filename*' in filename_parm: + return self.get_encoded_filename(filename_parm) + return force_str(filename_parm['filename']) + except (AttributeError, KeyError, ValueError): + pass + + def get_encoded_filename(self, filename_parm): + """ + Handle encoded filenames per RFC6266. See also: + https://tools.ietf.org/html/rfc2231#section-4 + """ + encoded_filename = force_str(filename_parm['filename*']) + try: + charset, lang, filename = encoded_filename.split('\'', 2) + filename = parse.unquote(filename) + except (ValueError, LookupError): + filename = force_str(filename_parm['filename']) + return filename diff --git a/venv/Lib/site-packages/rest_framework/permissions.py b/venv/Lib/site-packages/rest_framework/permissions.py new file mode 100644 index 0000000..3a8c580 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/permissions.py @@ -0,0 +1,300 @@ +""" +Provides a set of pluggable permission policies. +""" +from django.http import Http404 + +from rest_framework import exceptions + +SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') + + +class OperationHolderMixin: + def __and__(self, other): + return OperandHolder(AND, self, other) + + def __or__(self, other): + return OperandHolder(OR, self, other) + + def __rand__(self, other): + return OperandHolder(AND, other, self) + + def __ror__(self, other): + return OperandHolder(OR, other, self) + + def __invert__(self): + return SingleOperandHolder(NOT, self) + + +class SingleOperandHolder(OperationHolderMixin): + def __init__(self, operator_class, op1_class): + self.operator_class = operator_class + self.op1_class = op1_class + + def __call__(self, *args, **kwargs): + op1 = self.op1_class(*args, **kwargs) + return self.operator_class(op1) + + +class OperandHolder(OperationHolderMixin): + def __init__(self, operator_class, op1_class, op2_class): + self.operator_class = operator_class + self.op1_class = op1_class + self.op2_class = op2_class + + def __call__(self, *args, **kwargs): + op1 = self.op1_class(*args, **kwargs) + op2 = self.op2_class(*args, **kwargs) + return self.operator_class(op1, op2) + + +class AND: + def __init__(self, op1, op2): + self.op1 = op1 + self.op2 = op2 + + def has_permission(self, request, view): + return ( + self.op1.has_permission(request, view) and + self.op2.has_permission(request, view) + ) + + def has_object_permission(self, request, view, obj): + return ( + self.op1.has_object_permission(request, view, obj) and + self.op2.has_object_permission(request, view, obj) + ) + + +class OR: + def __init__(self, op1, op2): + self.op1 = op1 + self.op2 = op2 + + def has_permission(self, request, view): + return ( + self.op1.has_permission(request, view) or + self.op2.has_permission(request, view) + ) + + def has_object_permission(self, request, view, obj): + return ( + self.op1.has_object_permission(request, view, obj) or + self.op2.has_object_permission(request, view, obj) + ) + + +class NOT: + def __init__(self, op1): + self.op1 = op1 + + def has_permission(self, request, view): + return not self.op1.has_permission(request, view) + + def has_object_permission(self, request, view, obj): + return not self.op1.has_object_permission(request, view, obj) + + +class BasePermissionMetaclass(OperationHolderMixin, type): + pass + + +class BasePermission(metaclass=BasePermissionMetaclass): + """ + A base class from which all permission classes should inherit. + """ + + def has_permission(self, request, view): + """ + Return `True` if permission is granted, `False` otherwise. + """ + return True + + def has_object_permission(self, request, view, obj): + """ + Return `True` if permission is granted, `False` otherwise. + """ + return True + + +class AllowAny(BasePermission): + """ + Allow any access. + This isn't strictly required, since you could use an empty + permission_classes list, but it's useful because it makes the intention + more explicit. + """ + + def has_permission(self, request, view): + return True + + +class IsAuthenticated(BasePermission): + """ + Allows access only to authenticated users. + """ + + def has_permission(self, request, view): + return bool(request.user and request.user.is_authenticated) + + +class IsAdminUser(BasePermission): + """ + Allows access only to admin users. + """ + + def has_permission(self, request, view): + return bool(request.user and request.user.is_staff) + + +class IsAuthenticatedOrReadOnly(BasePermission): + """ + The request is authenticated as a user, or is a read-only request. + """ + + def has_permission(self, request, view): + return bool( + request.method in SAFE_METHODS or + request.user and + request.user.is_authenticated + ) + + +class DjangoModelPermissions(BasePermission): + """ + The request is authenticated using `django.contrib.auth` permissions. + See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions + + It ensures that the user is authenticated, and has the appropriate + `add`/`change`/`delete` permissions on the model. + + This permission can only be applied against view classes that + provide a `.queryset` attribute. + """ + + # Map methods into required permission codes. + # Override this if you need to also provide 'view' permissions, + # or if you want to provide custom permission codes. + perms_map = { + 'GET': [], + 'OPTIONS': [], + 'HEAD': [], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } + + authenticated_users_only = True + + def get_required_permissions(self, method, model_cls): + """ + Given a model and an HTTP method, return the list of permission + codes that the user is required to have. + """ + kwargs = { + 'app_label': model_cls._meta.app_label, + 'model_name': model_cls._meta.model_name + } + + if method not in self.perms_map: + raise exceptions.MethodNotAllowed(method) + + return [perm % kwargs for perm in self.perms_map[method]] + + def _queryset(self, view): + assert hasattr(view, 'get_queryset') \ + or getattr(view, 'queryset', None) is not None, ( + 'Cannot apply {} on a view that does not set ' + '`.queryset` or have a `.get_queryset()` method.' + ).format(self.__class__.__name__) + + if hasattr(view, 'get_queryset'): + queryset = view.get_queryset() + assert queryset is not None, ( + '{}.get_queryset() returned None'.format(view.__class__.__name__) + ) + return queryset + return view.queryset + + def has_permission(self, request, view): + # Workaround to ensure DjangoModelPermissions are not applied + # to the root view when using DefaultRouter. + if getattr(view, '_ignore_model_permissions', False): + return True + + if not request.user or ( + not request.user.is_authenticated and self.authenticated_users_only): + return False + + queryset = self._queryset(view) + perms = self.get_required_permissions(request.method, queryset.model) + + return request.user.has_perms(perms) + + +class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): + """ + Similar to DjangoModelPermissions, except that anonymous users are + allowed read-only access. + """ + authenticated_users_only = False + + +class DjangoObjectPermissions(DjangoModelPermissions): + """ + The request is authenticated using Django's object-level permissions. + It requires an object-permissions-enabled backend, such as Django Guardian. + + It ensures that the user is authenticated, and has the appropriate + `add`/`change`/`delete` permissions on the object using .has_perms. + + This permission can only be applied against view classes that + provide a `.queryset` attribute. + """ + perms_map = { + 'GET': [], + 'OPTIONS': [], + 'HEAD': [], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } + + def get_required_object_permissions(self, method, model_cls): + kwargs = { + 'app_label': model_cls._meta.app_label, + 'model_name': model_cls._meta.model_name + } + + if method not in self.perms_map: + raise exceptions.MethodNotAllowed(method) + + return [perm % kwargs for perm in self.perms_map[method]] + + def has_object_permission(self, request, view, obj): + # authentication checks have already executed via has_permission + queryset = self._queryset(view) + model_cls = queryset.model + user = request.user + + perms = self.get_required_object_permissions(request.method, model_cls) + + if not user.has_perms(perms, obj): + # If the user does not have permissions we need to determine if + # they have read permissions to see 403, or not, and simply see + # a 404 response. + + if request.method in SAFE_METHODS: + # Read permissions already checked and failed, no need + # to make another lookup. + raise Http404 + + read_perms = self.get_required_object_permissions('GET', model_cls) + if not user.has_perms(read_perms, obj): + raise Http404 + + # Has read permissions. + return False + + return True diff --git a/venv/Lib/site-packages/rest_framework/relations.py b/venv/Lib/site-packages/rest_framework/relations.py new file mode 100644 index 0000000..c987007 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/relations.py @@ -0,0 +1,563 @@ +import sys +from collections import OrderedDict +from urllib import parse + +from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist +from django.db.models import Manager +from django.db.models.query import QuerySet +from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve +from django.utils.encoding import smart_str, uri_to_iri +from django.utils.translation import gettext_lazy as _ + +from rest_framework.fields import ( + Field, empty, get_attribute, is_simple_callable, iter_options +) +from rest_framework.reverse import reverse +from rest_framework.settings import api_settings +from rest_framework.utils import html + + +def method_overridden(method_name, klass, instance): + """ + Determine if a method has been overridden. + """ + method = getattr(klass, method_name) + default_method = getattr(method, '__func__', method) # Python 3 compat + return default_method is not getattr(instance, method_name).__func__ + + +class ObjectValueError(ValueError): + """ + Raised when `queryset.get()` failed due to an underlying `ValueError`. + Wrapping prevents calling code conflating this with unrelated errors. + """ + + +class ObjectTypeError(TypeError): + """ + Raised when `queryset.get()` failed due to an underlying `TypeError`. + Wrapping prevents calling code conflating this with unrelated errors. + """ + + +class Hyperlink(str): + """ + A string like object that additionally has an associated name. + We use this for hyperlinked URLs that may render as a named link + in some contexts, or render as a plain URL in others. + """ + def __new__(cls, url, obj): + ret = super().__new__(cls, url) + ret.obj = obj + return ret + + def __getnewargs__(self): + return (str(self), self.name) + + @property + def name(self): + # This ensures that we only called `__str__` lazily, + # as in some cases calling __str__ on a model instances *might* + # involve a database lookup. + return str(self.obj) + + is_hyperlink = True + + +class PKOnlyObject: + """ + This is a mock object, used for when we only need the pk of the object + instance, but still want to return an object with a .pk attribute, + in order to keep the same interface as a regular model instance. + """ + def __init__(self, pk): + self.pk = pk + + def __str__(self): + return "%s" % self.pk + + +# We assume that 'validators' are intended for the child serializer, +# rather than the parent serializer. +MANY_RELATION_KWARGS = ( + 'read_only', 'write_only', 'required', 'default', 'initial', 'source', + 'label', 'help_text', 'style', 'error_messages', 'allow_empty', + 'html_cutoff', 'html_cutoff_text' +) + + +class RelatedField(Field): + queryset = None + html_cutoff = None + html_cutoff_text = None + + def __init__(self, **kwargs): + self.queryset = kwargs.pop('queryset', self.queryset) + + cutoff_from_settings = api_settings.HTML_SELECT_CUTOFF + if cutoff_from_settings is not None: + cutoff_from_settings = int(cutoff_from_settings) + self.html_cutoff = kwargs.pop('html_cutoff', cutoff_from_settings) + + self.html_cutoff_text = kwargs.pop( + 'html_cutoff_text', + self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) + ) + if not method_overridden('get_queryset', RelatedField, self): + assert self.queryset is not None or kwargs.get('read_only'), ( + 'Relational field must provide a `queryset` argument, ' + 'override `get_queryset`, or set read_only=`True`.' + ) + assert not (self.queryset is not None and kwargs.get('read_only')), ( + 'Relational fields should not provide a `queryset` argument, ' + 'when setting read_only=`True`.' + ) + kwargs.pop('many', None) + kwargs.pop('allow_empty', None) + super().__init__(**kwargs) + + def __new__(cls, *args, **kwargs): + # We override this method in order to automagically create + # `ManyRelatedField` classes instead when `many=True` is set. + if kwargs.pop('many', False): + return cls.many_init(*args, **kwargs) + return super().__new__(cls, *args, **kwargs) + + @classmethod + def many_init(cls, *args, **kwargs): + """ + This method handles creating a parent `ManyRelatedField` instance + when the `many=True` keyword argument is passed. + + Typically you won't need to override this method. + + Note that we're over-cautious in passing most arguments to both parent + and child classes in order to try to cover the general case. If you're + overriding this method you'll probably want something much simpler, eg: + + @classmethod + def many_init(cls, *args, **kwargs): + kwargs['child'] = cls() + return CustomManyRelatedField(*args, **kwargs) + """ + list_kwargs = {'child_relation': cls(*args, **kwargs)} + for key in kwargs: + if key in MANY_RELATION_KWARGS: + list_kwargs[key] = kwargs[key] + return ManyRelatedField(**list_kwargs) + + def run_validation(self, data=empty): + # We force empty strings to None values for relational fields. + if data == '': + data = None + return super().run_validation(data) + + def get_queryset(self): + queryset = self.queryset + if isinstance(queryset, (QuerySet, Manager)): + # Ensure queryset is re-evaluated whenever used. + # Note that actually a `Manager` class may also be used as the + # queryset argument. This occurs on ModelSerializer fields, + # as it allows us to generate a more expressive 'repr' output + # for the field. + # Eg: 'MyRelationship(queryset=ExampleModel.objects.all())' + queryset = queryset.all() + return queryset + + def use_pk_only_optimization(self): + return False + + def get_attribute(self, instance): + if self.use_pk_only_optimization() and self.source_attrs: + # Optimized case, return a mock object only containing the pk attribute. + try: + attribute_instance = get_attribute(instance, self.source_attrs[:-1]) + value = attribute_instance.serializable_value(self.source_attrs[-1]) + if is_simple_callable(value): + # Handle edge case where the relationship `source` argument + # points to a `get_relationship()` method on the model. + value = value() + + # Handle edge case where relationship `source` argument points + # to an instance instead of a pk (e.g., a `@property`). + value = getattr(value, 'pk', value) + + return PKOnlyObject(pk=value) + except AttributeError: + pass + + # Standard case, return the object instance. + return super().get_attribute(instance) + + def get_choices(self, cutoff=None): + queryset = self.get_queryset() + if queryset is None: + # Ensure that field.choices returns something sensible + # even when accessed with a read-only field. + return {} + + if cutoff is not None: + queryset = queryset[:cutoff] + + return OrderedDict([ + ( + self.to_representation(item), + self.display_value(item) + ) + for item in queryset + ]) + + @property + def choices(self): + return self.get_choices() + + @property + def grouped_choices(self): + return self.choices + + def iter_options(self): + return iter_options( + self.get_choices(cutoff=self.html_cutoff), + cutoff=self.html_cutoff, + cutoff_text=self.html_cutoff_text + ) + + def display_value(self, instance): + return str(instance) + + +class StringRelatedField(RelatedField): + """ + A read only field that represents its targets using their + plain string representation. + """ + + def __init__(self, **kwargs): + kwargs['read_only'] = True + super().__init__(**kwargs) + + def to_representation(self, value): + return str(value) + + +class PrimaryKeyRelatedField(RelatedField): + default_error_messages = { + 'required': _('This field is required.'), + 'does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'), + 'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'), + } + + def __init__(self, **kwargs): + self.pk_field = kwargs.pop('pk_field', None) + super().__init__(**kwargs) + + def use_pk_only_optimization(self): + return True + + def to_internal_value(self, data): + if self.pk_field is not None: + data = self.pk_field.to_internal_value(data) + queryset = self.get_queryset() + try: + if isinstance(data, bool): + raise TypeError + return queryset.get(pk=data) + except ObjectDoesNotExist: + self.fail('does_not_exist', pk_value=data) + except (TypeError, ValueError): + self.fail('incorrect_type', data_type=type(data).__name__) + + def to_representation(self, value): + if self.pk_field is not None: + return self.pk_field.to_representation(value.pk) + return value.pk + + +class HyperlinkedRelatedField(RelatedField): + lookup_field = 'pk' + view_name = None + + default_error_messages = { + 'required': _('This field is required.'), + 'no_match': _('Invalid hyperlink - No URL match.'), + 'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'), + 'does_not_exist': _('Invalid hyperlink - Object does not exist.'), + 'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'), + } + + def __init__(self, view_name=None, **kwargs): + if view_name is not None: + self.view_name = view_name + assert self.view_name is not None, 'The `view_name` argument is required.' + self.lookup_field = kwargs.pop('lookup_field', self.lookup_field) + self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field) + self.format = kwargs.pop('format', None) + + # We include this simply for dependency injection in tests. + # We can't add it as a class attributes or it would expect an + # implicit `self` argument to be passed. + self.reverse = reverse + + super().__init__(**kwargs) + + def use_pk_only_optimization(self): + return self.lookup_field == 'pk' + + def get_object(self, view_name, view_args, view_kwargs): + """ + Return the object corresponding to a matched URL. + + Takes the matched URL conf arguments, and should return an + object instance, or raise an `ObjectDoesNotExist` exception. + """ + lookup_value = view_kwargs[self.lookup_url_kwarg] + lookup_kwargs = {self.lookup_field: lookup_value} + queryset = self.get_queryset() + + try: + return queryset.get(**lookup_kwargs) + except ValueError: + exc = ObjectValueError(str(sys.exc_info()[1])) + raise exc.with_traceback(sys.exc_info()[2]) + except TypeError: + exc = ObjectTypeError(str(sys.exc_info()[1])) + raise exc.with_traceback(sys.exc_info()[2]) + + def get_url(self, obj, view_name, request, format): + """ + Given an object, return the URL that hyperlinks to the object. + + May raise a `NoReverseMatch` if the `view_name` and `lookup_field` + attributes are not configured to correctly match the URL conf. + """ + # Unsaved objects will not yet have a valid URL. + if hasattr(obj, 'pk') and obj.pk in (None, ''): + return None + + lookup_value = getattr(obj, self.lookup_field) + kwargs = {self.lookup_url_kwarg: lookup_value} + return self.reverse(view_name, kwargs=kwargs, request=request, format=format) + + def to_internal_value(self, data): + request = self.context.get('request') + try: + http_prefix = data.startswith(('http:', 'https:')) + except AttributeError: + self.fail('incorrect_type', data_type=type(data).__name__) + + if http_prefix: + # If needed convert absolute URLs to relative path + data = parse.urlparse(data).path + prefix = get_script_prefix() + if data.startswith(prefix): + data = '/' + data[len(prefix):] + + data = uri_to_iri(parse.unquote(data)) + + try: + match = resolve(data) + except Resolver404: + self.fail('no_match') + + try: + expected_viewname = request.versioning_scheme.get_versioned_viewname( + self.view_name, request + ) + except AttributeError: + expected_viewname = self.view_name + + if match.view_name != expected_viewname: + self.fail('incorrect_match') + + try: + return self.get_object(match.view_name, match.args, match.kwargs) + except (ObjectDoesNotExist, ObjectValueError, ObjectTypeError): + self.fail('does_not_exist') + + def to_representation(self, value): + assert 'request' in self.context, ( + "`%s` requires the request in the serializer" + " context. Add `context={'request': request}` when instantiating " + "the serializer." % self.__class__.__name__ + ) + + request = self.context['request'] + format = self.context.get('format') + + # By default use whatever format is given for the current context + # unless the target is a different type to the source. + # + # Eg. Consider a HyperlinkedIdentityField pointing from a json + # representation to an html property of that representation... + # + # '/snippets/1/' should link to '/snippets/1/highlight/' + # ...but... + # '/snippets/1/.json' should link to '/snippets/1/highlight/.html' + if format and self.format and self.format != format: + format = self.format + + # Return the hyperlink, or error if incorrectly configured. + try: + url = self.get_url(value, self.view_name, request, format) + except NoReverseMatch: + msg = ( + 'Could not resolve URL for hyperlinked relationship using ' + 'view name "%s". You may have failed to include the related ' + 'model in your API, or incorrectly configured the ' + '`lookup_field` attribute on this field.' + ) + if value in ('', None): + value_string = {'': 'the empty string', None: 'None'}[value] + msg += ( + " WARNING: The value of the field on the model instance " + "was %s, which may be why it didn't match any " + "entries in your URL conf." % value_string + ) + raise ImproperlyConfigured(msg % self.view_name) + + if url is None: + return None + + return Hyperlink(url, value) + + +class HyperlinkedIdentityField(HyperlinkedRelatedField): + """ + A read-only field that represents the identity URL for an object, itself. + + This is in contrast to `HyperlinkedRelatedField` which represents the + URL of relationships to other objects. + """ + + def __init__(self, view_name=None, **kwargs): + assert view_name is not None, 'The `view_name` argument is required.' + kwargs['read_only'] = True + kwargs['source'] = '*' + super().__init__(view_name, **kwargs) + + def use_pk_only_optimization(self): + # We have the complete object instance already. We don't need + # to run the 'only get the pk for this relationship' code. + return False + + +class SlugRelatedField(RelatedField): + """ + A read-write field that represents the target of the relationship + by a unique 'slug' attribute. + """ + default_error_messages = { + 'does_not_exist': _('Object with {slug_name}={value} does not exist.'), + 'invalid': _('Invalid value.'), + } + + def __init__(self, slug_field=None, **kwargs): + assert slug_field is not None, 'The `slug_field` argument is required.' + self.slug_field = slug_field + super().__init__(**kwargs) + + def to_internal_value(self, data): + queryset = self.get_queryset() + try: + return queryset.get(**{self.slug_field: data}) + except ObjectDoesNotExist: + self.fail('does_not_exist', slug_name=self.slug_field, value=smart_str(data)) + except (TypeError, ValueError): + self.fail('invalid') + + def to_representation(self, obj): + return getattr(obj, self.slug_field) + + +class ManyRelatedField(Field): + """ + Relationships with `many=True` transparently get coerced into instead being + a ManyRelatedField with a child relationship. + + The `ManyRelatedField` class is responsible for handling iterating through + the values and passing each one to the child relationship. + + This class is treated as private API. + You shouldn't generally need to be using this class directly yourself, + and should instead simply set 'many=True' on the relationship. + """ + initial = [] + default_empty_html = [] + default_error_messages = { + 'not_a_list': _('Expected a list of items but got type "{input_type}".'), + 'empty': _('This list may not be empty.') + } + html_cutoff = None + html_cutoff_text = None + + def __init__(self, child_relation=None, *args, **kwargs): + self.child_relation = child_relation + self.allow_empty = kwargs.pop('allow_empty', True) + + cutoff_from_settings = api_settings.HTML_SELECT_CUTOFF + if cutoff_from_settings is not None: + cutoff_from_settings = int(cutoff_from_settings) + self.html_cutoff = kwargs.pop('html_cutoff', cutoff_from_settings) + + self.html_cutoff_text = kwargs.pop( + 'html_cutoff_text', + self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) + ) + assert child_relation is not None, '`child_relation` is a required argument.' + super().__init__(*args, **kwargs) + self.child_relation.bind(field_name='', parent=self) + + def get_value(self, dictionary): + # We override the default field access in order to support + # lists in HTML forms. + if html.is_html_input(dictionary): + # Don't return [] if the update is partial + if self.field_name not in dictionary: + if getattr(self.root, 'partial', False): + return empty + return dictionary.getlist(self.field_name) + + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + if isinstance(data, str) or not hasattr(data, '__iter__'): + self.fail('not_a_list', input_type=type(data).__name__) + if not self.allow_empty and len(data) == 0: + self.fail('empty') + + return [ + self.child_relation.to_internal_value(item) + for item in data + ] + + def get_attribute(self, instance): + # Can't have any relationships if not created + if hasattr(instance, 'pk') and instance.pk is None: + return [] + + relationship = get_attribute(instance, self.source_attrs) + return relationship.all() if hasattr(relationship, 'all') else relationship + + def to_representation(self, iterable): + return [ + self.child_relation.to_representation(value) + for value in iterable + ] + + def get_choices(self, cutoff=None): + return self.child_relation.get_choices(cutoff) + + @property + def choices(self): + return self.get_choices() + + @property + def grouped_choices(self): + return self.choices + + def iter_options(self): + return iter_options( + self.get_choices(cutoff=self.html_cutoff), + cutoff=self.html_cutoff, + cutoff_text=self.html_cutoff_text + ) diff --git a/venv/Lib/site-packages/rest_framework/renderers.py b/venv/Lib/site-packages/rest_framework/renderers.py new file mode 100644 index 0000000..b0ddca2 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/renderers.py @@ -0,0 +1,1076 @@ +""" +Renderers are used to serialize a response into specific media types. + +They give us a generic way of being able to handle various media types +on the response, such as JSON encoded data or HTML output. + +REST framework also provides an HTML renderer that renders the browsable API. +""" +import base64 +from collections import OrderedDict +from urllib import parse + +from django import forms +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.paginator import Page +from django.http.multipartparser import parse_header +from django.template import engines, loader +from django.urls import NoReverseMatch +from django.utils.html import mark_safe + +from rest_framework import VERSION, exceptions, serializers, status +from rest_framework.compat import ( + INDENT_SEPARATORS, LONG_SEPARATORS, SHORT_SEPARATORS, coreapi, coreschema, + pygments_css, yaml +) +from rest_framework.exceptions import ParseError +from rest_framework.request import is_form_media_type, override_method +from rest_framework.settings import api_settings +from rest_framework.utils import encoders, json +from rest_framework.utils.breadcrumbs import get_breadcrumbs +from rest_framework.utils.field_mapping import ClassLookupDict + + +def zero_as_none(value): + return None if value == 0 else value + + +class BaseRenderer: + """ + All renderers should extend this class, setting the `media_type` + and `format` attributes, and override the `.render()` method. + """ + media_type = None + format = None + charset = 'utf-8' + render_style = 'text' + + def render(self, data, accepted_media_type=None, renderer_context=None): + raise NotImplementedError('Renderer class requires .render() to be implemented') + + +class JSONRenderer(BaseRenderer): + """ + Renderer which serializes to JSON. + """ + media_type = 'application/json' + format = 'json' + encoder_class = encoders.JSONEncoder + ensure_ascii = not api_settings.UNICODE_JSON + compact = api_settings.COMPACT_JSON + strict = api_settings.STRICT_JSON + + # We don't set a charset because JSON is a binary encoding, + # that can be encoded as utf-8, utf-16 or utf-32. + # See: https://www.ietf.org/rfc/rfc4627.txt + # Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/ + charset = None + + def get_indent(self, accepted_media_type, renderer_context): + if accepted_media_type: + # If the media type looks like 'application/json; indent=4', + # then pretty print the result. + # Note that we coerce `indent=0` into `indent=None`. + base_media_type, params = parse_header(accepted_media_type.encode('ascii')) + try: + return zero_as_none(max(min(int(params['indent']), 8), 0)) + except (KeyError, ValueError, TypeError): + pass + + # If 'indent' is provided in the context, then pretty print the result. + # E.g. If we're being called by the BrowsableAPIRenderer. + return renderer_context.get('indent', None) + + def render(self, data, accepted_media_type=None, renderer_context=None): + """ + Render `data` into JSON, returning a bytestring. + """ + if data is None: + return b'' + + renderer_context = renderer_context or {} + indent = self.get_indent(accepted_media_type, renderer_context) + + if indent is None: + separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS + else: + separators = INDENT_SEPARATORS + + ret = json.dumps( + data, cls=self.encoder_class, + indent=indent, ensure_ascii=self.ensure_ascii, + allow_nan=not self.strict, separators=separators + ) + + # We always fully escape \u2028 and \u2029 to ensure we output JSON + # that is a strict javascript subset. + # See: http://timelessrepo.com/json-isnt-a-javascript-subset + ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029') + return ret.encode() + + +class TemplateHTMLRenderer(BaseRenderer): + """ + An HTML renderer for use with templates. + + The data supplied to the Response object should be a dictionary that will + be used as context for the template. + + The template name is determined by (in order of preference): + + 1. An explicit `.template_name` attribute set on the response. + 2. An explicit `.template_name` attribute set on this class. + 3. The return result of calling `view.get_template_names()`. + + For example: + data = {'users': User.objects.all()} + return Response(data, template_name='users.html') + + For pre-rendered HTML, see StaticHTMLRenderer. + """ + media_type = 'text/html' + format = 'html' + template_name = None + exception_template_names = [ + '%(status_code)s.html', + 'api_exception.html' + ] + charset = 'utf-8' + + def render(self, data, accepted_media_type=None, renderer_context=None): + """ + Renders data to HTML, using Django's standard template rendering. + + The template name is determined by (in order of preference): + + 1. An explicit .template_name set on the response. + 2. An explicit .template_name set on this class. + 3. The return result of calling view.get_template_names(). + """ + renderer_context = renderer_context or {} + view = renderer_context['view'] + request = renderer_context['request'] + response = renderer_context['response'] + + if response.exception: + template = self.get_exception_template(response) + else: + template_names = self.get_template_names(response, view) + template = self.resolve_template(template_names) + + if hasattr(self, 'resolve_context'): + # Fallback for older versions. + context = self.resolve_context(data, request, response) + else: + context = self.get_template_context(data, renderer_context) + return template.render(context, request=request) + + def resolve_template(self, template_names): + return loader.select_template(template_names) + + def get_template_context(self, data, renderer_context): + response = renderer_context['response'] + if response.exception: + data['status_code'] = response.status_code + return data + + def get_template_names(self, response, view): + if response.template_name: + return [response.template_name] + elif self.template_name: + return [self.template_name] + elif hasattr(view, 'get_template_names'): + return view.get_template_names() + elif hasattr(view, 'template_name'): + return [view.template_name] + raise ImproperlyConfigured( + 'Returned a template response with no `template_name` attribute set on either the view or response' + ) + + def get_exception_template(self, response): + template_names = [name % {'status_code': response.status_code} + for name in self.exception_template_names] + + try: + # Try to find an appropriate error template + return self.resolve_template(template_names) + except Exception: + # Fall back to using eg '404 Not Found' + body = '%d %s' % (response.status_code, response.status_text.title()) + template = engines['django'].from_string(body) + return template + + +# Note, subclass TemplateHTMLRenderer simply for the exception behavior +class StaticHTMLRenderer(TemplateHTMLRenderer): + """ + An HTML renderer class that simply returns pre-rendered HTML. + + The data supplied to the Response object should be a string representing + the pre-rendered HTML content. + + For example: + data = '<html><body>example</body></html>' + return Response(data) + + For template rendered HTML, see TemplateHTMLRenderer. + """ + media_type = 'text/html' + format = 'html' + charset = 'utf-8' + + def render(self, data, accepted_media_type=None, renderer_context=None): + renderer_context = renderer_context or {} + response = renderer_context.get('response') + + if response and response.exception: + request = renderer_context['request'] + template = self.get_exception_template(response) + if hasattr(self, 'resolve_context'): + context = self.resolve_context(data, request, response) + else: + context = self.get_template_context(data, renderer_context) + return template.render(context, request=request) + + return data + + +class HTMLFormRenderer(BaseRenderer): + """ + Renderers serializer data into an HTML form. + + If the serializer was instantiated without an object then this will + return an HTML form not bound to any object, + otherwise it will return an HTML form with the appropriate initial data + populated from the object. + + Note that rendering of field and form errors is not currently supported. + """ + media_type = 'text/html' + format = 'form' + charset = 'utf-8' + template_pack = 'rest_framework/vertical/' + base_template = 'form.html' + + default_style = ClassLookupDict({ + serializers.Field: { + 'base_template': 'input.html', + 'input_type': 'text' + }, + serializers.EmailField: { + 'base_template': 'input.html', + 'input_type': 'email' + }, + serializers.URLField: { + 'base_template': 'input.html', + 'input_type': 'url' + }, + serializers.IntegerField: { + 'base_template': 'input.html', + 'input_type': 'number' + }, + serializers.FloatField: { + 'base_template': 'input.html', + 'input_type': 'number' + }, + serializers.DateTimeField: { + 'base_template': 'input.html', + 'input_type': 'datetime-local' + }, + serializers.DateField: { + 'base_template': 'input.html', + 'input_type': 'date' + }, + serializers.TimeField: { + 'base_template': 'input.html', + 'input_type': 'time' + }, + serializers.FileField: { + 'base_template': 'input.html', + 'input_type': 'file' + }, + serializers.BooleanField: { + 'base_template': 'checkbox.html' + }, + serializers.ChoiceField: { + 'base_template': 'select.html', # Also valid: 'radio.html' + }, + serializers.MultipleChoiceField: { + 'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html' + }, + serializers.RelatedField: { + 'base_template': 'select.html', # Also valid: 'radio.html' + }, + serializers.ManyRelatedField: { + 'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html' + }, + serializers.Serializer: { + 'base_template': 'fieldset.html' + }, + serializers.ListSerializer: { + 'base_template': 'list_fieldset.html' + }, + serializers.ListField: { + 'base_template': 'list_field.html' + }, + serializers.DictField: { + 'base_template': 'dict_field.html' + }, + serializers.FilePathField: { + 'base_template': 'select.html', + }, + serializers.JSONField: { + 'base_template': 'textarea.html', + }, + }) + + def render_field(self, field, parent_style): + if isinstance(field._field, serializers.HiddenField): + return '' + + style = self.default_style[field].copy() + style.update(field.style) + if 'template_pack' not in style: + style['template_pack'] = parent_style.get('template_pack', self.template_pack) + style['renderer'] = self + + # Get a clone of the field with text-only value representation. + field = field.as_form_field() + + if style.get('input_type') == 'datetime-local' and isinstance(field.value, str): + field.value = field.value.rstrip('Z') + + if 'template' in style: + template_name = style['template'] + else: + template_name = style['template_pack'].strip('/') + '/' + style['base_template'] + + template = loader.get_template(template_name) + context = {'field': field, 'style': style} + return template.render(context) + + def render(self, data, accepted_media_type=None, renderer_context=None): + """ + Render serializer data and return an HTML form, as a string. + """ + renderer_context = renderer_context or {} + form = data.serializer + + style = renderer_context.get('style', {}) + if 'template_pack' not in style: + style['template_pack'] = self.template_pack + style['renderer'] = self + + template_pack = style['template_pack'].strip('/') + template_name = template_pack + '/' + self.base_template + template = loader.get_template(template_name) + context = { + 'form': form, + 'style': style + } + return template.render(context) + + +class BrowsableAPIRenderer(BaseRenderer): + """ + HTML renderer used to self-document the API. + """ + media_type = 'text/html' + format = 'api' + template = 'rest_framework/api.html' + filter_template = 'rest_framework/filters/base.html' + code_style = 'emacs' + charset = 'utf-8' + form_renderer_class = HTMLFormRenderer + + def get_default_renderer(self, view): + """ + Return an instance of the first valid renderer. + (Don't use another documenting renderer.) + """ + renderers = [renderer for renderer in view.renderer_classes + if not issubclass(renderer, BrowsableAPIRenderer)] + non_template_renderers = [renderer for renderer in renderers + if not hasattr(renderer, 'get_template_names')] + + if not renderers: + return None + elif non_template_renderers: + return non_template_renderers[0]() + return renderers[0]() + + def get_content(self, renderer, data, + accepted_media_type, renderer_context): + """ + Get the content as if it had been rendered by the default + non-documenting renderer. + """ + if not renderer: + return '[No renderers were found]' + + renderer_context['indent'] = 4 + content = renderer.render(data, accepted_media_type, renderer_context) + + render_style = getattr(renderer, 'render_style', 'text') + assert render_style in ['text', 'binary'], 'Expected .render_style ' \ + '"text" or "binary", but got "%s"' % render_style + if render_style == 'binary': + return '[%d bytes of binary content]' % len(content) + + return content.decode('utf-8') if isinstance(content, bytes) else content + + def show_form_for_method(self, view, method, request, obj): + """ + Returns True if a form should be shown for this method. + """ + if method not in view.allowed_methods: + return # Not a valid method + + try: + view.check_permissions(request) + if obj is not None: + view.check_object_permissions(request, obj) + except exceptions.APIException: + return False # Doesn't have permissions + return True + + def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs): + kwargs['context'] = { + 'request': request, + 'format': self.format, + 'view': view_instance + } + return serializer_class(*args, **kwargs) + + def get_rendered_html_form(self, data, view, method, request): + """ + Return a string representing a rendered HTML form, possibly bound to + either the input or output data. + + In the absence of the View having an associated form then return None. + """ + # See issue #2089 for refactoring this. + serializer = getattr(data, 'serializer', None) + if serializer and not getattr(serializer, 'many', False): + instance = getattr(serializer, 'instance', None) + if isinstance(instance, Page): + instance = None + else: + instance = None + + # If this is valid serializer data, and the form is for the same + # HTTP method as was used in the request then use the existing + # serializer instance, rather than dynamically creating a new one. + if request.method == method and serializer is not None: + try: + kwargs = {'data': request.data} + except ParseError: + kwargs = {} + existing_serializer = serializer + else: + kwargs = {} + existing_serializer = None + + with override_method(view, request, method) as request: + if not self.show_form_for_method(view, method, request, instance): + return + + if method in ('DELETE', 'OPTIONS'): + return True # Don't actually need to return a form + + has_serializer = getattr(view, 'get_serializer', None) + has_serializer_class = getattr(view, 'serializer_class', None) + + if ( + (not has_serializer and not has_serializer_class) or + not any(is_form_media_type(parser.media_type) for parser in view.parser_classes) + ): + return + + if existing_serializer is not None: + try: + return self.render_form_for_serializer(existing_serializer) + except TypeError: + pass + + if has_serializer: + if method in ('PUT', 'PATCH'): + serializer = view.get_serializer(instance=instance, **kwargs) + else: + serializer = view.get_serializer(**kwargs) + else: + # at this point we must have a serializer_class + if method in ('PUT', 'PATCH'): + serializer = self._get_serializer(view.serializer_class, view, + request, instance=instance, **kwargs) + else: + serializer = self._get_serializer(view.serializer_class, view, + request, **kwargs) + + return self.render_form_for_serializer(serializer) + + def render_form_for_serializer(self, serializer): + if hasattr(serializer, 'initial_data'): + serializer.is_valid() + + form_renderer = self.form_renderer_class() + return form_renderer.render( + serializer.data, + self.accepted_media_type, + {'style': {'template_pack': 'rest_framework/horizontal'}} + ) + + def get_raw_data_form(self, data, view, method, request): + """ + Returns a form that allows for arbitrary content types to be tunneled + via standard HTML forms. + (Which are typically application/x-www-form-urlencoded) + """ + # See issue #2089 for refactoring this. + serializer = getattr(data, 'serializer', None) + if serializer and not getattr(serializer, 'many', False): + instance = getattr(serializer, 'instance', None) + if isinstance(instance, Page): + instance = None + else: + instance = None + + with override_method(view, request, method) as request: + # Check permissions + if not self.show_form_for_method(view, method, request, instance): + return + + # If possible, serialize the initial content for the generic form + default_parser = view.parser_classes[0] + renderer_class = getattr(default_parser, 'renderer_class', None) + if hasattr(view, 'get_serializer') and renderer_class: + # View has a serializer defined and parser class has a + # corresponding renderer that can be used to render the data. + + if method in ('PUT', 'PATCH'): + serializer = view.get_serializer(instance=instance) + else: + serializer = view.get_serializer() + + # Render the raw data content + renderer = renderer_class() + accepted = self.accepted_media_type + context = self.renderer_context.copy() + context['indent'] = 4 + + # strip HiddenField from output + data = serializer.data.copy() + for name, field in serializer.fields.items(): + if isinstance(field, serializers.HiddenField): + data.pop(name, None) + content = renderer.render(data, accepted, context) + # Renders returns bytes, but CharField expects a str. + content = content.decode() + else: + content = None + + # Generate a generic form that includes a content type field, + # and a content field. + media_types = [parser.media_type for parser in view.parser_classes] + choices = [(media_type, media_type) for media_type in media_types] + initial = media_types[0] + + class GenericContentForm(forms.Form): + _content_type = forms.ChoiceField( + label='Media type', + choices=choices, + initial=initial, + widget=forms.Select(attrs={'data-override': 'content-type'}) + ) + _content = forms.CharField( + label='Content', + widget=forms.Textarea(attrs={'data-override': 'content'}), + initial=content, + required=False + ) + + return GenericContentForm() + + def get_name(self, view): + return view.get_view_name() + + def get_description(self, view, status_code): + if status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN): + return '' + return view.get_view_description(html=True) + + def get_breadcrumbs(self, request): + return get_breadcrumbs(request.path, request) + + def get_extra_actions(self, view, status_code): + if (status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN)): + return None + elif not hasattr(view, 'get_extra_action_url_map'): + return None + + return view.get_extra_action_url_map() + + def get_filter_form(self, data, view, request): + if not hasattr(view, 'get_queryset') or not hasattr(view, 'filter_backends'): + return + + # Infer if this is a list view or not. + paginator = getattr(view, 'paginator', None) + if isinstance(data, list): + pass + elif paginator is not None and data is not None: + try: + paginator.get_results(data) + except (TypeError, KeyError): + return + elif not isinstance(data, list): + return + + queryset = view.get_queryset() + elements = [] + for backend in view.filter_backends: + if hasattr(backend, 'to_html'): + html = backend().to_html(request, queryset, view) + if html: + elements.append(html) + + if not elements: + return + + template = loader.get_template(self.filter_template) + context = {'elements': elements} + return template.render(context) + + def get_context(self, data, accepted_media_type, renderer_context): + """ + Returns the context used to render. + """ + view = renderer_context['view'] + request = renderer_context['request'] + response = renderer_context['response'] + + renderer = self.get_default_renderer(view) + + raw_data_post_form = self.get_raw_data_form(data, view, 'POST', request) + raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request) + raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request) + raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form + + response_headers = OrderedDict(sorted(response.items())) + renderer_content_type = '' + if renderer: + renderer_content_type = '%s' % renderer.media_type + if renderer.charset: + renderer_content_type += ' ;%s' % renderer.charset + response_headers['Content-Type'] = renderer_content_type + + if getattr(view, 'paginator', None) and view.paginator.display_page_controls: + paginator = view.paginator + else: + paginator = None + + csrf_cookie_name = settings.CSRF_COOKIE_NAME + csrf_header_name = settings.CSRF_HEADER_NAME + if csrf_header_name.startswith('HTTP_'): + csrf_header_name = csrf_header_name[5:] + csrf_header_name = csrf_header_name.replace('_', '-') + + return { + 'content': self.get_content(renderer, data, accepted_media_type, renderer_context), + 'code_style': pygments_css(self.code_style), + 'view': view, + 'request': request, + 'response': response, + 'user': request.user, + 'description': self.get_description(view, response.status_code), + 'name': self.get_name(view), + 'version': VERSION, + 'paginator': paginator, + 'breadcrumblist': self.get_breadcrumbs(request), + 'allowed_methods': view.allowed_methods, + 'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes], + 'response_headers': response_headers, + + 'put_form': self.get_rendered_html_form(data, view, 'PUT', request), + 'post_form': self.get_rendered_html_form(data, view, 'POST', request), + 'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request), + 'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request), + + 'extra_actions': self.get_extra_actions(view, response.status_code), + + 'filter_form': self.get_filter_form(data, view, request), + + 'raw_data_put_form': raw_data_put_form, + 'raw_data_post_form': raw_data_post_form, + 'raw_data_patch_form': raw_data_patch_form, + 'raw_data_put_or_patch_form': raw_data_put_or_patch_form, + + 'display_edit_forms': bool(response.status_code != 403), + + 'api_settings': api_settings, + 'csrf_cookie_name': csrf_cookie_name, + 'csrf_header_name': csrf_header_name + } + + def render(self, data, accepted_media_type=None, renderer_context=None): + """ + Render the HTML for the browsable API representation. + """ + self.accepted_media_type = accepted_media_type or '' + self.renderer_context = renderer_context or {} + + template = loader.get_template(self.template) + context = self.get_context(data, accepted_media_type, renderer_context) + ret = template.render(context, request=renderer_context['request']) + + # Munge DELETE Response code to allow us to return content + # (Do this *after* we've rendered the template so that we include + # the normal deletion response code in the output) + response = renderer_context['response'] + if response.status_code == status.HTTP_204_NO_CONTENT: + response.status_code = status.HTTP_200_OK + + return ret + + +class AdminRenderer(BrowsableAPIRenderer): + template = 'rest_framework/admin.html' + format = 'admin' + + def render(self, data, accepted_media_type=None, renderer_context=None): + self.accepted_media_type = accepted_media_type or '' + self.renderer_context = renderer_context or {} + + response = renderer_context['response'] + request = renderer_context['request'] + view = self.renderer_context['view'] + + if response.status_code == status.HTTP_400_BAD_REQUEST: + # Errors still need to display the list or detail information. + # The only way we can get at that is to simulate a GET request. + self.error_form = self.get_rendered_html_form(data, view, request.method, request) + self.error_title = {'POST': 'Create', 'PUT': 'Edit'}.get(request.method, 'Errors') + + with override_method(view, request, 'GET') as request: + response = view.get(request, *view.args, **view.kwargs) + data = response.data + + template = loader.get_template(self.template) + context = self.get_context(data, accepted_media_type, renderer_context) + ret = template.render(context, request=renderer_context['request']) + + # Creation and deletion should use redirects in the admin style. + if response.status_code == status.HTTP_201_CREATED and 'Location' in response: + response.status_code = status.HTTP_303_SEE_OTHER + response['Location'] = request.build_absolute_uri() + ret = '' + + if response.status_code == status.HTTP_204_NO_CONTENT: + response.status_code = status.HTTP_303_SEE_OTHER + try: + # Attempt to get the parent breadcrumb URL. + response['Location'] = self.get_breadcrumbs(request)[-2][1] + except KeyError: + # Otherwise reload current URL to get a 'Not Found' page. + response['Location'] = request.full_path + ret = '' + + return ret + + def get_context(self, data, accepted_media_type, renderer_context): + """ + Render the HTML for the browsable API representation. + """ + context = super().get_context( + data, accepted_media_type, renderer_context + ) + + paginator = getattr(context['view'], 'paginator', None) + if paginator is not None and data is not None: + try: + results = paginator.get_results(data) + except (TypeError, KeyError): + results = data + else: + results = data + + if results is None: + header = {} + style = 'detail' + elif isinstance(results, list): + header = results[0] if results else {} + style = 'list' + else: + header = results + style = 'detail' + + columns = [key for key in header if key != 'url'] + details = [key for key in header if key != 'url'] + + if isinstance(results, list) and 'view' in renderer_context: + for result in results: + url = self.get_result_url(result, context['view']) + if url is not None: + result.setdefault('url', url) + + context['style'] = style + context['columns'] = columns + context['details'] = details + context['results'] = results + context['error_form'] = getattr(self, 'error_form', None) + context['error_title'] = getattr(self, 'error_title', None) + return context + + def get_result_url(self, result, view): + """ + Attempt to reverse the result's detail view URL. + + This only works with views that are generic-like (has `.lookup_field`) + and viewset-like (has `.basename` / `.reverse_action()`). + """ + if not hasattr(view, 'reverse_action') or \ + not hasattr(view, 'lookup_field'): + return + + lookup_field = view.lookup_field + lookup_url_kwarg = getattr(view, 'lookup_url_kwarg', None) or lookup_field + + try: + kwargs = {lookup_url_kwarg: result[lookup_field]} + return view.reverse_action('detail', kwargs=kwargs) + except (KeyError, NoReverseMatch): + return + + +class DocumentationRenderer(BaseRenderer): + media_type = 'text/html' + format = 'html' + charset = 'utf-8' + template = 'rest_framework/docs/index.html' + error_template = 'rest_framework/docs/error.html' + code_style = 'emacs' + languages = ['shell', 'javascript', 'python'] + + def get_context(self, data, request): + return { + 'document': data, + 'langs': self.languages, + 'lang_htmls': ["rest_framework/docs/langs/%s.html" % language for language in self.languages], + 'lang_intro_htmls': ["rest_framework/docs/langs/%s-intro.html" % language for language in self.languages], + 'code_style': pygments_css(self.code_style), + 'request': request + } + + def render(self, data, accepted_media_type=None, renderer_context=None): + if isinstance(data, coreapi.Document): + template = loader.get_template(self.template) + context = self.get_context(data, renderer_context['request']) + return template.render(context, request=renderer_context['request']) + else: + template = loader.get_template(self.error_template) + context = { + "data": data, + "request": renderer_context['request'], + "response": renderer_context['response'], + "debug": settings.DEBUG, + } + return template.render(context, request=renderer_context['request']) + + +class SchemaJSRenderer(BaseRenderer): + media_type = 'application/javascript' + format = 'javascript' + charset = 'utf-8' + template = 'rest_framework/schema.js' + + def render(self, data, accepted_media_type=None, renderer_context=None): + codec = coreapi.codecs.CoreJSONCodec() + schema = base64.b64encode(codec.encode(data)).decode('ascii') + + template = loader.get_template(self.template) + context = {'schema': mark_safe(schema)} + request = renderer_context['request'] + return template.render(context, request=request) + + +class MultiPartRenderer(BaseRenderer): + media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg' + format = 'multipart' + charset = 'utf-8' + BOUNDARY = 'BoUnDaRyStRiNg' + + def render(self, data, accepted_media_type=None, renderer_context=None): + from django.test.client import encode_multipart + + if hasattr(data, 'items'): + for key, value in data.items(): + assert not isinstance(value, dict), ( + "Test data contained a dictionary value for key '%s', " + "but multipart uploads do not support nested data. " + "You may want to consider using format='json' in this " + "test case." % key + ) + return encode_multipart(self.BOUNDARY, data) + + +class CoreJSONRenderer(BaseRenderer): + media_type = 'application/coreapi+json' + charset = None + format = 'corejson' + + def __init__(self): + assert coreapi, 'Using CoreJSONRenderer, but `coreapi` is not installed.' + + def render(self, data, media_type=None, renderer_context=None): + indent = bool(renderer_context.get('indent', 0)) + codec = coreapi.codecs.CoreJSONCodec() + return codec.dump(data, indent=indent) + + +class _BaseOpenAPIRenderer: + def get_schema(self, instance): + CLASS_TO_TYPENAME = { + coreschema.Object: 'object', + coreschema.Array: 'array', + coreschema.Number: 'number', + coreschema.Integer: 'integer', + coreschema.String: 'string', + coreschema.Boolean: 'boolean', + } + + schema = {} + if instance.__class__ in CLASS_TO_TYPENAME: + schema['type'] = CLASS_TO_TYPENAME[instance.__class__] + schema['title'] = instance.title + schema['description'] = instance.description + if hasattr(instance, 'enum'): + schema['enum'] = instance.enum + return schema + + def get_parameters(self, link): + parameters = [] + for field in link.fields: + if field.location not in ['path', 'query']: + continue + parameter = { + 'name': field.name, + 'in': field.location, + } + if field.required: + parameter['required'] = True + if field.description: + parameter['description'] = field.description + if field.schema: + parameter['schema'] = self.get_schema(field.schema) + parameters.append(parameter) + return parameters + + def get_operation(self, link, name, tag): + operation_id = "%s_%s" % (tag, name) if tag else name + parameters = self.get_parameters(link) + + operation = { + 'operationId': operation_id, + } + if link.title: + operation['summary'] = link.title + if link.description: + operation['description'] = link.description + if parameters: + operation['parameters'] = parameters + if tag: + operation['tags'] = [tag] + return operation + + def get_paths(self, document): + paths = {} + + tag = None + for name, link in document.links.items(): + path = parse.urlparse(link.url).path + method = link.action.lower() + paths.setdefault(path, {}) + paths[path][method] = self.get_operation(link, name, tag=tag) + + for tag, section in document.data.items(): + for name, link in section.links.items(): + path = parse.urlparse(link.url).path + method = link.action.lower() + paths.setdefault(path, {}) + paths[path][method] = self.get_operation(link, name, tag=tag) + + return paths + + def get_structure(self, data): + return { + 'openapi': '3.0.0', + 'info': { + 'version': '', + 'title': data.title, + 'description': data.description + }, + 'servers': [{ + 'url': data.url + }], + 'paths': self.get_paths(data) + } + + +class CoreAPIOpenAPIRenderer(_BaseOpenAPIRenderer): + media_type = 'application/vnd.oai.openapi' + charset = None + format = 'openapi' + + def __init__(self): + assert coreapi, 'Using CoreAPIOpenAPIRenderer, but `coreapi` is not installed.' + assert yaml, 'Using CoreAPIOpenAPIRenderer, but `pyyaml` is not installed.' + + def render(self, data, media_type=None, renderer_context=None): + structure = self.get_structure(data) + return yaml.dump(structure, default_flow_style=False).encode() + + +class CoreAPIJSONOpenAPIRenderer(_BaseOpenAPIRenderer): + media_type = 'application/vnd.oai.openapi+json' + charset = None + format = 'openapi-json' + ensure_ascii = not api_settings.UNICODE_JSON + + def __init__(self): + assert coreapi, 'Using CoreAPIJSONOpenAPIRenderer, but `coreapi` is not installed.' + + def render(self, data, media_type=None, renderer_context=None): + structure = self.get_structure(data) + return json.dumps( + structure, indent=4, + ensure_ascii=self.ensure_ascii).encode('utf-8') + + +class OpenAPIRenderer(BaseRenderer): + media_type = 'application/vnd.oai.openapi' + charset = None + format = 'openapi' + + def __init__(self): + assert yaml, 'Using OpenAPIRenderer, but `pyyaml` is not installed.' + + def render(self, data, media_type=None, renderer_context=None): + # disable yaml advanced feature 'alias' for clean, portable, and readable output + class Dumper(yaml.Dumper): + def ignore_aliases(self, data): + return True + return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8') + + +class JSONOpenAPIRenderer(BaseRenderer): + media_type = 'application/vnd.oai.openapi+json' + charset = None + encoder_class = encoders.JSONEncoder + format = 'openapi-json' + ensure_ascii = not api_settings.UNICODE_JSON + + def render(self, data, media_type=None, renderer_context=None): + return json.dumps( + data, cls=self.encoder_class, indent=2, + ensure_ascii=self.ensure_ascii).encode('utf-8') diff --git a/venv/Lib/site-packages/rest_framework/request.py b/venv/Lib/site-packages/rest_framework/request.py new file mode 100644 index 0000000..17ceadb --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/request.py @@ -0,0 +1,455 @@ +""" +The Request class is used as a wrapper around the standard request object. + +The wrapped request then offers a richer API, in particular : + + - content automatically parsed according to `Content-Type` header, + and available as `request.data` + - full support of PUT method, including support for file uploads + - form overloading of HTTP method, content type and content +""" +import io +import sys +from contextlib import contextmanager + +from django.conf import settings +from django.http import HttpRequest, QueryDict +from django.http.multipartparser import parse_header +from django.http.request import RawPostDataException +from django.utils.datastructures import MultiValueDict + +from rest_framework import HTTP_HEADER_ENCODING, exceptions +from rest_framework.settings import api_settings + + +def is_form_media_type(media_type): + """ + Return True if the media type is a valid form media type. + """ + base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING)) + return (base_media_type == 'application/x-www-form-urlencoded' or + base_media_type == 'multipart/form-data') + + +class override_method: + """ + A context manager that temporarily overrides the method on a request, + additionally setting the `view.request` attribute. + + Usage: + + with override_method(view, request, 'POST') as request: + ... # Do stuff with `view` and `request` + """ + + def __init__(self, view, request, method): + self.view = view + self.request = request + self.method = method + self.action = getattr(view, 'action', None) + + def __enter__(self): + self.view.request = clone_request(self.request, self.method) + # For viewsets we also set the `.action` attribute. + action_map = getattr(self.view, 'action_map', {}) + self.view.action = action_map.get(self.method.lower()) + return self.view.request + + def __exit__(self, *args, **kwarg): + self.view.request = self.request + self.view.action = self.action + + +class WrappedAttributeError(Exception): + pass + + +@contextmanager +def wrap_attributeerrors(): + """ + Used to re-raise AttributeErrors caught during authentication, preventing + these errors from otherwise being handled by the attribute access protocol. + """ + try: + yield + except AttributeError: + info = sys.exc_info() + exc = WrappedAttributeError(str(info[1])) + raise exc.with_traceback(info[2]) + + +class Empty: + """ + Placeholder for unset attributes. + Cannot use `None`, as that may be a valid value. + """ + pass + + +def _hasattr(obj, name): + return not getattr(obj, name) is Empty + + +def clone_request(request, method): + """ + Internal helper method to clone a request, replacing with a different + HTTP method. Used for checking permissions against other methods. + """ + ret = Request(request=request._request, + parsers=request.parsers, + authenticators=request.authenticators, + negotiator=request.negotiator, + parser_context=request.parser_context) + ret._data = request._data + ret._files = request._files + ret._full_data = request._full_data + ret._content_type = request._content_type + ret._stream = request._stream + ret.method = method + if hasattr(request, '_user'): + ret._user = request._user + if hasattr(request, '_auth'): + ret._auth = request._auth + if hasattr(request, '_authenticator'): + ret._authenticator = request._authenticator + if hasattr(request, 'accepted_renderer'): + ret.accepted_renderer = request.accepted_renderer + if hasattr(request, 'accepted_media_type'): + ret.accepted_media_type = request.accepted_media_type + if hasattr(request, 'version'): + ret.version = request.version + if hasattr(request, 'versioning_scheme'): + ret.versioning_scheme = request.versioning_scheme + return ret + + +class ForcedAuthentication: + """ + This authentication class is used if the test client or request factory + forcibly authenticated the request. + """ + + def __init__(self, force_user, force_token): + self.force_user = force_user + self.force_token = force_token + + def authenticate(self, request): + return (self.force_user, self.force_token) + + +class Request: + """ + Wrapper allowing to enhance a standard `HttpRequest` instance. + + Kwargs: + - request(HttpRequest). The original request instance. + - parsers(list/tuple). The parsers to use for parsing the + request content. + - authenticators(list/tuple). The authenticators used to try + authenticating the request's user. + """ + + def __init__(self, request, parsers=None, authenticators=None, + negotiator=None, parser_context=None): + assert isinstance(request, HttpRequest), ( + 'The `request` argument must be an instance of ' + '`django.http.HttpRequest`, not `{}.{}`.' + .format(request.__class__.__module__, request.__class__.__name__) + ) + + self._request = request + self.parsers = parsers or () + self.authenticators = authenticators or () + self.negotiator = negotiator or self._default_negotiator() + self.parser_context = parser_context + self._data = Empty + self._files = Empty + self._full_data = Empty + self._content_type = Empty + self._stream = Empty + + if self.parser_context is None: + self.parser_context = {} + self.parser_context['request'] = self + self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET + + force_user = getattr(request, '_force_auth_user', None) + force_token = getattr(request, '_force_auth_token', None) + if force_user is not None or force_token is not None: + forced_auth = ForcedAuthentication(force_user, force_token) + self.authenticators = (forced_auth,) + + def __repr__(self): + return '<%s.%s: %s %r>' % ( + self.__class__.__module__, + self.__class__.__name__, + self.method, + self.get_full_path()) + + def _default_negotiator(self): + return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS() + + @property + def content_type(self): + meta = self._request.META + return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', '')) + + @property + def stream(self): + """ + Returns an object that may be used to stream the request content. + """ + if not _hasattr(self, '_stream'): + self._load_stream() + return self._stream + + @property + def query_params(self): + """ + More semantically correct name for request.GET. + """ + return self._request.GET + + @property + def data(self): + if not _hasattr(self, '_full_data'): + self._load_data_and_files() + return self._full_data + + @property + def user(self): + """ + Returns the user associated with the current request, as authenticated + by the authentication classes provided to the request. + """ + if not hasattr(self, '_user'): + with wrap_attributeerrors(): + self._authenticate() + return self._user + + @user.setter + def user(self, value): + """ + Sets the user on the current request. This is necessary to maintain + compatibility with django.contrib.auth where the user property is + set in the login and logout functions. + + Note that we also set the user on Django's underlying `HttpRequest` + instance, ensuring that it is available to any middleware in the stack. + """ + self._user = value + self._request.user = value + + @property + def auth(self): + """ + Returns any non-user authentication information associated with the + request, such as an authentication token. + """ + if not hasattr(self, '_auth'): + with wrap_attributeerrors(): + self._authenticate() + return self._auth + + @auth.setter + def auth(self, value): + """ + Sets any non-user authentication information associated with the + request, such as an authentication token. + """ + self._auth = value + self._request.auth = value + + @property + def successful_authenticator(self): + """ + Return the instance of the authentication instance class that was used + to authenticate the request, or `None`. + """ + if not hasattr(self, '_authenticator'): + with wrap_attributeerrors(): + self._authenticate() + return self._authenticator + + def _load_data_and_files(self): + """ + Parses the request content into `self.data`. + """ + if not _hasattr(self, '_data'): + self._data, self._files = self._parse() + if self._files: + self._full_data = self._data.copy() + self._full_data.update(self._files) + else: + self._full_data = self._data + + # if a form media type, copy data & files refs to the underlying + # http request so that closable objects are handled appropriately. + if is_form_media_type(self.content_type): + self._request._post = self.POST + self._request._files = self.FILES + + def _load_stream(self): + """ + Return the content body of the request, as a stream. + """ + meta = self._request.META + try: + content_length = int( + meta.get('CONTENT_LENGTH', meta.get('HTTP_CONTENT_LENGTH', 0)) + ) + except (ValueError, TypeError): + content_length = 0 + + if content_length == 0: + self._stream = None + elif not self._request._read_started: + self._stream = self._request + else: + self._stream = io.BytesIO(self.body) + + def _supports_form_parsing(self): + """ + Return True if this requests supports parsing form data. + """ + form_media = ( + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ) + return any(parser.media_type in form_media for parser in self.parsers) + + def _parse(self): + """ + Parse the request content, returning a two-tuple of (data, files) + + May raise an `UnsupportedMediaType`, or `ParseError` exception. + """ + media_type = self.content_type + try: + stream = self.stream + except RawPostDataException: + if not hasattr(self._request, '_post'): + raise + # If request.POST has been accessed in middleware, and a method='POST' + # request was made with 'multipart/form-data', then the request stream + # will already have been exhausted. + if self._supports_form_parsing(): + return (self._request.POST, self._request.FILES) + stream = None + + if stream is None or media_type is None: + if media_type and is_form_media_type(media_type): + empty_data = QueryDict('', encoding=self._request._encoding) + else: + empty_data = {} + empty_files = MultiValueDict() + return (empty_data, empty_files) + + parser = self.negotiator.select_parser(self, self.parsers) + + if not parser: + raise exceptions.UnsupportedMediaType(media_type) + + try: + parsed = parser.parse(stream, media_type, self.parser_context) + except Exception: + # If we get an exception during parsing, fill in empty data and + # re-raise. Ensures we don't simply repeat the error when + # attempting to render the browsable renderer response, or when + # logging the request or similar. + self._data = QueryDict('', encoding=self._request._encoding) + self._files = MultiValueDict() + self._full_data = self._data + raise + + # Parser classes may return the raw data, or a + # DataAndFiles object. Unpack the result as required. + try: + return (parsed.data, parsed.files) + except AttributeError: + empty_files = MultiValueDict() + return (parsed, empty_files) + + def _authenticate(self): + """ + Attempt to authenticate the request using each authentication instance + in turn. + """ + for authenticator in self.authenticators: + try: + user_auth_tuple = authenticator.authenticate(self) + except exceptions.APIException: + self._not_authenticated() + raise + + if user_auth_tuple is not None: + self._authenticator = authenticator + self.user, self.auth = user_auth_tuple + return + + self._not_authenticated() + + def _not_authenticated(self): + """ + Set authenticator, user & authtoken representing an unauthenticated request. + + Defaults are None, AnonymousUser & None. + """ + self._authenticator = None + + if api_settings.UNAUTHENTICATED_USER: + self.user = api_settings.UNAUTHENTICATED_USER() + else: + self.user = None + + if api_settings.UNAUTHENTICATED_TOKEN: + self.auth = api_settings.UNAUTHENTICATED_TOKEN() + else: + self.auth = None + + def __getattr__(self, attr): + """ + If an attribute does not exist on this instance, then we also attempt + to proxy it to the underlying HttpRequest object. + """ + try: + return getattr(self._request, attr) + except AttributeError: + return self.__getattribute__(attr) + + @property + def DATA(self): + raise NotImplementedError( + '`request.DATA` has been deprecated in favor of `request.data` ' + 'since version 3.0, and has been fully removed as of version 3.2.' + ) + + @property + def POST(self): + # Ensure that request.POST uses our request parsing. + if not _hasattr(self, '_data'): + self._load_data_and_files() + if is_form_media_type(self.content_type): + return self._data + return QueryDict('', encoding=self._request._encoding) + + @property + def FILES(self): + # Leave this one alone for backwards compat with Django's request.FILES + # Different from the other two cases, which are not valid property + # names on the WSGIRequest class. + if not _hasattr(self, '_files'): + self._load_data_and_files() + return self._files + + @property + def QUERY_PARAMS(self): + raise NotImplementedError( + '`request.QUERY_PARAMS` has been deprecated in favor of `request.query_params` ' + 'since version 3.0, and has been fully removed as of version 3.2.' + ) + + def force_plaintext_errors(self, value): + # Hack to allow our exception handler to force choice of + # plaintext or html error responses. + self._request.is_ajax = lambda: value diff --git a/venv/Lib/site-packages/rest_framework/response.py b/venv/Lib/site-packages/rest_framework/response.py new file mode 100644 index 0000000..4954237 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/response.py @@ -0,0 +1,103 @@ +""" +The Response class in REST framework is similar to HTTPResponse, except that +it is initialized with unrendered data, instead of a pre-rendered string. + +The appropriate renderer is called during Django's template response rendering. +""" +from http.client import responses + +from django.template.response import SimpleTemplateResponse + +from rest_framework.serializers import Serializer + + +class Response(SimpleTemplateResponse): + """ + An HttpResponse that allows its data to be rendered into + arbitrary media types. + """ + + def __init__(self, data=None, status=None, + template_name=None, headers=None, + exception=False, content_type=None): + """ + Alters the init arguments slightly. + For example, drop 'template_name', and instead use 'data'. + + Setting 'renderer' and 'media_type' will typically be deferred, + For example being set automatically by the `APIView`. + """ + super().__init__(None, status=status) + + if isinstance(data, Serializer): + msg = ( + 'You passed a Serializer instance as data, but ' + 'probably meant to pass serialized `.data` or ' + '`.error`. representation.' + ) + raise AssertionError(msg) + + self.data = data + self.template_name = template_name + self.exception = exception + self.content_type = content_type + + if headers: + for name, value in headers.items(): + self[name] = value + + @property + def rendered_content(self): + renderer = getattr(self, 'accepted_renderer', None) + accepted_media_type = getattr(self, 'accepted_media_type', None) + context = getattr(self, 'renderer_context', None) + + assert renderer, ".accepted_renderer not set on Response" + assert accepted_media_type, ".accepted_media_type not set on Response" + assert context is not None, ".renderer_context not set on Response" + context['response'] = self + + media_type = renderer.media_type + charset = renderer.charset + content_type = self.content_type + + if content_type is None and charset is not None: + content_type = "{}; charset={}".format(media_type, charset) + elif content_type is None: + content_type = media_type + self['Content-Type'] = content_type + + ret = renderer.render(self.data, accepted_media_type, context) + if isinstance(ret, str): + assert charset, ( + 'renderer returned unicode, and did not specify ' + 'a charset value.' + ) + return ret.encode(charset) + + if not ret: + del self['Content-Type'] + + return ret + + @property + def status_text(self): + """ + Returns reason text corresponding to our HTTP response status code. + Provided for convenience. + """ + return responses.get(self.status_code, '') + + def __getstate__(self): + """ + Remove attributes from the response that shouldn't be cached. + """ + state = super().__getstate__() + for key in ( + 'accepted_renderer', 'renderer_context', 'resolver_match', + 'client', 'request', 'json', 'wsgi_request' + ): + if key in state: + del state[key] + state['_closable_objects'] = [] + return state diff --git a/venv/Lib/site-packages/rest_framework/reverse.py b/venv/Lib/site-packages/rest_framework/reverse.py new file mode 100644 index 0000000..55bf74a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/reverse.py @@ -0,0 +1,66 @@ +""" +Provide urlresolver functions that return fully qualified URLs or view names +""" +from django.urls import NoReverseMatch +from django.urls import reverse as django_reverse +from django.utils.functional import lazy + +from rest_framework.settings import api_settings +from rest_framework.utils.urls import replace_query_param + + +def preserve_builtin_query_params(url, request=None): + """ + Given an incoming request, and an outgoing URL representation, + append the value of any built-in query parameters. + """ + if request is None: + return url + + overrides = [ + api_settings.URL_FORMAT_OVERRIDE, + ] + + for param in overrides: + if param and (param in request.GET): + value = request.GET[param] + url = replace_query_param(url, param, value) + + return url + + +def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): + """ + If versioning is being used then we pass any `reverse` calls through + to the versioning scheme instance, so that the resulting URL + can be modified if needed. + """ + scheme = getattr(request, 'versioning_scheme', None) + if scheme is not None: + try: + url = scheme.reverse(viewname, args, kwargs, request, format, **extra) + except NoReverseMatch: + # In case the versioning scheme reversal fails, fallback to the + # default implementation + url = _reverse(viewname, args, kwargs, request, format, **extra) + else: + url = _reverse(viewname, args, kwargs, request, format, **extra) + + return preserve_builtin_query_params(url, request) + + +def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): + """ + Same as `django.urls.reverse`, but optionally takes a request + and returns a fully qualified URL, using the request to get the base URL. + """ + if format is not None: + kwargs = kwargs or {} + kwargs['format'] = format + url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) + if request: + return request.build_absolute_uri(url) + return url + + +reverse_lazy = lazy(reverse, str) diff --git a/venv/Lib/site-packages/rest_framework/routers.py b/venv/Lib/site-packages/rest_framework/routers.py new file mode 100644 index 0000000..e0ae24b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/routers.py @@ -0,0 +1,348 @@ +""" +Routers provide a convenient and consistent way of automatically +determining the URL conf for your API. + +They are used by simply instantiating a Router class, and then registering +all the required ViewSets with that router. + +For example, you might have a `urls.py` that looks something like this: + + router = routers.DefaultRouter() + router.register('users', UserViewSet, 'user') + router.register('accounts', AccountViewSet, 'account') + + urlpatterns = router.urls +""" +import itertools +from collections import OrderedDict, namedtuple + +from django.core.exceptions import ImproperlyConfigured +from django.urls import NoReverseMatch, re_path + +from rest_framework import views +from rest_framework.response import Response +from rest_framework.reverse import reverse +from rest_framework.schemas import SchemaGenerator +from rest_framework.schemas.views import SchemaView +from rest_framework.settings import api_settings +from rest_framework.urlpatterns import format_suffix_patterns + +Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs']) +DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs']) + + +def escape_curly_brackets(url_path): + """ + Double brackets in regex of url_path for escape string formatting + """ + return url_path.replace('{', '{{').replace('}', '}}') + + +def flatten(list_of_lists): + """ + Takes an iterable of iterables, returns a single iterable containing all items + """ + return itertools.chain(*list_of_lists) + + +class BaseRouter: + def __init__(self): + self.registry = [] + + def register(self, prefix, viewset, basename=None): + if basename is None: + basename = self.get_default_basename(viewset) + self.registry.append((prefix, viewset, basename)) + + # invalidate the urls cache + if hasattr(self, '_urls'): + del self._urls + + def get_default_basename(self, viewset): + """ + If `basename` is not specified, attempt to automatically determine + it from the viewset. + """ + raise NotImplementedError('get_default_basename must be overridden') + + def get_urls(self): + """ + Return a list of URL patterns, given the registered viewsets. + """ + raise NotImplementedError('get_urls must be overridden') + + @property + def urls(self): + if not hasattr(self, '_urls'): + self._urls = self.get_urls() + return self._urls + + +class SimpleRouter(BaseRouter): + + routes = [ + # List route. + Route( + url=r'^{prefix}{trailing_slash}$', + mapping={ + 'get': 'list', + 'post': 'create' + }, + name='{basename}-list', + detail=False, + initkwargs={'suffix': 'List'} + ), + # Dynamically generated list routes. Generated using + # @action(detail=False) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=False, + initkwargs={} + ), + # Detail route. + Route( + url=r'^{prefix}/{lookup}{trailing_slash}$', + mapping={ + 'get': 'retrieve', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy' + }, + name='{basename}-detail', + detail=True, + initkwargs={'suffix': 'Instance'} + ), + # Dynamically generated detail routes. Generated using + # @action(detail=True) decorator on methods of the viewset. + DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={} + ), + ] + + def __init__(self, trailing_slash=True): + self.trailing_slash = '/' if trailing_slash else '' + super().__init__() + + def get_default_basename(self, viewset): + """ + If `basename` is not specified, attempt to automatically determine + it from the viewset. + """ + queryset = getattr(viewset, 'queryset', None) + + assert queryset is not None, '`basename` argument not specified, and could ' \ + 'not automatically determine the name from the viewset, as ' \ + 'it does not have a `.queryset` attribute.' + + return queryset.model._meta.object_name.lower() + + def get_routes(self, viewset): + """ + Augment `self.routes` with any dynamically generated routes. + + Returns a list of the Route namedtuple. + """ + # converting to list as iterables are good for one pass, known host needs to be checked again and again for + # different functions. + known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)])) + extra_actions = viewset.get_extra_actions() + + # checking action names against the known actions list + not_allowed = [ + action.__name__ for action in extra_actions + if action.__name__ in known_actions + ] + if not_allowed: + msg = ('Cannot use the @action decorator on the following ' + 'methods, as they are existing routes: %s') + raise ImproperlyConfigured(msg % ', '.join(not_allowed)) + + # partition detail and list actions + detail_actions = [action for action in extra_actions if action.detail] + list_actions = [action for action in extra_actions if not action.detail] + + routes = [] + for route in self.routes: + if isinstance(route, DynamicRoute) and route.detail: + routes += [self._get_dynamic_route(route, action) for action in detail_actions] + elif isinstance(route, DynamicRoute) and not route.detail: + routes += [self._get_dynamic_route(route, action) for action in list_actions] + else: + routes.append(route) + + return routes + + def _get_dynamic_route(self, route, action): + initkwargs = route.initkwargs.copy() + initkwargs.update(action.kwargs) + + url_path = escape_curly_brackets(action.url_path) + + return Route( + url=route.url.replace('{url_path}', url_path), + mapping=action.mapping, + name=route.name.replace('{url_name}', action.url_name), + detail=route.detail, + initkwargs=initkwargs, + ) + + def get_method_map(self, viewset, method_map): + """ + Given a viewset, and a mapping of http methods to actions, + return a new mapping which only includes any mappings that + are actually implemented by the viewset. + """ + bound_methods = {} + for method, action in method_map.items(): + if hasattr(viewset, action): + bound_methods[method] = action + return bound_methods + + def get_lookup_regex(self, viewset, lookup_prefix=''): + """ + Given a viewset, return the portion of URL regex that is used + to match against a single instance. + + Note that lookup_prefix is not used directly inside REST rest_framework + itself, but is required in order to nicely support nested router + implementations, such as drf-nested-routers. + + https://github.com/alanjds/drf-nested-routers + """ + base_regex = '(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})' + # Use `pk` as default field, unset set. Default regex should not + # consume `.json` style suffixes and should break at '/' boundaries. + lookup_field = getattr(viewset, 'lookup_field', 'pk') + lookup_url_kwarg = getattr(viewset, 'lookup_url_kwarg', None) or lookup_field + lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+') + return base_regex.format( + lookup_prefix=lookup_prefix, + lookup_url_kwarg=lookup_url_kwarg, + lookup_value=lookup_value + ) + + def get_urls(self): + """ + Use the registered viewsets to generate a list of URL patterns. + """ + ret = [] + + for prefix, viewset, basename in self.registry: + lookup = self.get_lookup_regex(viewset) + routes = self.get_routes(viewset) + + for route in routes: + + # Only actions which actually exist on the viewset will be bound + mapping = self.get_method_map(viewset, route.mapping) + if not mapping: + continue + + # Build the url pattern + regex = route.url.format( + prefix=prefix, + lookup=lookup, + trailing_slash=self.trailing_slash + ) + + # If there is no prefix, the first part of the url is probably + # controlled by project's urls.py and the router is in an app, + # so a slash in the beginning will (A) cause Django to give + # warnings and (B) generate URLS that will require using '//'. + if not prefix and regex[:2] == '^/': + regex = '^' + regex[2:] + + initkwargs = route.initkwargs.copy() + initkwargs.update({ + 'basename': basename, + 'detail': route.detail, + }) + + view = viewset.as_view(mapping, **initkwargs) + name = route.name.format(basename=basename) + ret.append(re_path(regex, view, name=name)) + + return ret + + +class APIRootView(views.APIView): + """ + The default basic root view for DefaultRouter + """ + _ignore_model_permissions = True + schema = None # exclude from schema + api_root_dict = None + + def get(self, request, *args, **kwargs): + # Return a plain {"name": "hyperlink"} response. + ret = OrderedDict() + namespace = request.resolver_match.namespace + for key, url_name in self.api_root_dict.items(): + if namespace: + url_name = namespace + ':' + url_name + try: + ret[key] = reverse( + url_name, + args=args, + kwargs=kwargs, + request=request, + format=kwargs.get('format') + ) + except NoReverseMatch: + # Don't bail out if eg. no list routes exist, only detail routes. + continue + + return Response(ret) + + +class DefaultRouter(SimpleRouter): + """ + The default router extends the SimpleRouter, but also adds in a default + API root view, and adds format suffix patterns to the URLs. + """ + include_root_view = True + include_format_suffixes = True + root_view_name = 'api-root' + default_schema_renderers = None + APIRootView = APIRootView + APISchemaView = SchemaView + SchemaGenerator = SchemaGenerator + + def __init__(self, *args, **kwargs): + if 'root_renderers' in kwargs: + self.root_renderers = kwargs.pop('root_renderers') + else: + self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) + super().__init__(*args, **kwargs) + + def get_api_root_view(self, api_urls=None): + """ + Return a basic root view. + """ + api_root_dict = OrderedDict() + list_name = self.routes[0].name + for prefix, viewset, basename in self.registry: + api_root_dict[prefix] = list_name.format(basename=basename) + + return self.APIRootView.as_view(api_root_dict=api_root_dict) + + def get_urls(self): + """ + Generate the list of URL patterns, including a default root view + for the API, and appending `.json` style format suffixes. + """ + urls = super().get_urls() + + if self.include_root_view: + view = self.get_api_root_view(api_urls=urls) + root_url = re_path(r'^$', view, name=self.root_view_name) + urls.append(root_url) + + if self.include_format_suffixes: + urls = format_suffix_patterns(urls) + + return urls diff --git a/venv/Lib/site-packages/rest_framework/schemas/__init__.py b/venv/Lib/site-packages/rest_framework/schemas/__init__.py new file mode 100644 index 0000000..b63cb23 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/__init__.py @@ -0,0 +1,58 @@ +""" +rest_framework.schemas + +schemas: + __init__.py + generators.py # Top-down schema generation + inspectors.py # Per-endpoint view introspection + utils.py # Shared helper functions + views.py # Houses `SchemaView`, `APIView` subclass. + +We expose a minimal "public" API directly from `schemas`. This covers the +basic use-cases: + + from rest_framework.schemas import ( + AutoSchema, + ManualSchema, + get_schema_view, + SchemaGenerator, + ) + +Other access should target the submodules directly +""" +from rest_framework.settings import api_settings + +from . import coreapi, openapi +from .coreapi import AutoSchema, ManualSchema, SchemaGenerator # noqa +from .inspectors import DefaultSchema # noqa + + +def get_schema_view( + title=None, url=None, description=None, urlconf=None, renderer_classes=None, + public=False, patterns=None, generator_class=None, + authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES, + permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES, + version=None): + """ + Return a schema view. + """ + if generator_class is None: + if coreapi.is_enabled(): + generator_class = coreapi.SchemaGenerator + else: + generator_class = openapi.SchemaGenerator + + generator = generator_class( + title=title, url=url, description=description, + urlconf=urlconf, patterns=patterns, version=version + ) + + # Avoid import cycle on APIView + from .views import SchemaView + return SchemaView.as_view( + renderer_classes=renderer_classes, + schema_generator=generator, + public=public, + authentication_classes=authentication_classes, + permission_classes=permission_classes, + ) diff --git a/venv/Lib/site-packages/rest_framework/schemas/coreapi.py b/venv/Lib/site-packages/rest_framework/schemas/coreapi.py new file mode 100644 index 0000000..179f0fa --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/coreapi.py @@ -0,0 +1,612 @@ +import warnings +from collections import Counter, OrderedDict +from urllib import parse + +from django.db import models +from django.utils.encoding import force_str + +from rest_framework import exceptions, serializers +from rest_framework.compat import coreapi, coreschema, uritemplate +from rest_framework.settings import api_settings + +from .generators import BaseSchemaGenerator +from .inspectors import ViewInspector +from .utils import get_pk_description, is_list_view + + +def common_path(paths): + split_paths = [path.strip('/').split('/') for path in paths] + s1 = min(split_paths) + s2 = max(split_paths) + common = s1 + for i, c in enumerate(s1): + if c != s2[i]: + common = s1[:i] + break + return '/' + '/'.join(common) + + +def is_custom_action(action): + return action not in { + 'retrieve', 'list', 'create', 'update', 'partial_update', 'destroy' + } + + +def distribute_links(obj): + for key, value in obj.items(): + distribute_links(value) + + for preferred_key, link in obj.links: + key = obj.get_available_key(preferred_key) + obj[key] = link + + +INSERT_INTO_COLLISION_FMT = """ +Schema Naming Collision. + +coreapi.Link for URL path {value_url} cannot be inserted into schema. +Position conflicts with coreapi.Link for URL path {target_url}. + +Attempted to insert link with keys: {keys}. + +Adjust URLs to avoid naming collision or override `SchemaGenerator.get_keys()` +to customise schema structure. +""" + + +class LinkNode(OrderedDict): + def __init__(self): + self.links = [] + self.methods_counter = Counter() + super().__init__() + + def get_available_key(self, preferred_key): + if preferred_key not in self: + return preferred_key + + while True: + current_val = self.methods_counter[preferred_key] + self.methods_counter[preferred_key] += 1 + + key = '{}_{}'.format(preferred_key, current_val) + if key not in self: + return key + + +def insert_into(target, keys, value): + """ + Nested dictionary insertion. + + >>> example = {} + >>> insert_into(example, ['a', 'b', 'c'], 123) + >>> example + LinkNode({'a': LinkNode({'b': LinkNode({'c': LinkNode(links=[123])}}}))) + """ + for key in keys[:-1]: + if key not in target: + target[key] = LinkNode() + target = target[key] + + try: + target.links.append((keys[-1], value)) + except TypeError: + msg = INSERT_INTO_COLLISION_FMT.format( + value_url=value.url, + target_url=target.url, + keys=keys + ) + raise ValueError(msg) + + +class SchemaGenerator(BaseSchemaGenerator): + """ + Original CoreAPI version. + """ + # Map HTTP methods onto actions. + default_mapping = { + 'get': 'retrieve', + 'post': 'create', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy', + } + + # Map the method names we use for viewset actions onto external schema names. + # These give us names that are more suitable for the external representation. + # Set by 'SCHEMA_COERCE_METHOD_NAMES'. + coerce_method_names = None + + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=None): + assert coreapi, '`coreapi` must be installed for schema support.' + assert coreschema, '`coreschema` must be installed for schema support.' + + super().__init__(title, url, description, patterns, urlconf) + self.coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES + + def get_links(self, request=None): + """ + Return a dictionary containing all the links that should be + included in the API schema. + """ + links = LinkNode() + + paths, view_endpoints = self._get_paths_and_endpoints(request) + + # Only generate the path prefix for paths that will be included + if not paths: + return None + prefix = self.determine_path_prefix(paths) + + for path, method, view in view_endpoints: + if not self.has_view_permissions(path, method, view): + continue + link = view.schema.get_link(path, method, base_url=self.url) + subpath = path[len(prefix):] + keys = self.get_keys(subpath, method, view) + insert_into(links, keys, link) + + return links + + def get_schema(self, request=None, public=False): + """ + Generate a `coreapi.Document` representing the API schema. + """ + self._initialise_endpoints() + + links = self.get_links(None if public else request) + if not links: + return None + + url = self.url + if not url and request is not None: + url = request.build_absolute_uri() + + distribute_links(links) + return coreapi.Document( + title=self.title, description=self.description, + url=url, content=links + ) + + # Method for generating the link layout.... + def get_keys(self, subpath, method, view): + """ + Return a list of keys that should be used to layout a link within + the schema document. + + /users/ ("users", "list"), ("users", "create") + /users/{pk}/ ("users", "read"), ("users", "update"), ("users", "delete") + /users/enabled/ ("users", "enabled") # custom viewset list action + /users/{pk}/star/ ("users", "star") # custom viewset detail action + /users/{pk}/groups/ ("users", "groups", "list"), ("users", "groups", "create") + /users/{pk}/groups/{pk}/ ("users", "groups", "read"), ("users", "groups", "update"), ("users", "groups", "delete") + """ + if hasattr(view, 'action'): + # Viewsets have explicitly named actions. + action = view.action + else: + # Views have no associated action, so we determine one from the method. + if is_list_view(subpath, method, view): + action = 'list' + else: + action = self.default_mapping[method.lower()] + + named_path_components = [ + component for component + in subpath.strip('/').split('/') + if '{' not in component + ] + + if is_custom_action(action): + # Custom action, eg "/users/{pk}/activate/", "/users/active/" + if len(view.action_map) > 1: + action = self.default_mapping[method.lower()] + if action in self.coerce_method_names: + action = self.coerce_method_names[action] + return named_path_components + [action] + else: + return named_path_components[:-1] + [action] + + if action in self.coerce_method_names: + action = self.coerce_method_names[action] + + # Default action, eg "/users/", "/users/{pk}/" + return named_path_components + [action] + + def determine_path_prefix(self, paths): + """ + Given a list of all paths, return the common prefix which should be + discounted when generating a schema structure. + + This will be the longest common string that does not include that last + component of the URL, or the last component before a path parameter. + + For example: + + /api/v1/users/ + /api/v1/users/{pk}/ + + The path prefix is '/api/v1' + """ + prefixes = [] + for path in paths: + components = path.strip('/').split('/') + initial_components = [] + for component in components: + if '{' in component: + break + initial_components.append(component) + prefix = '/'.join(initial_components[:-1]) + if not prefix: + # We can just break early in the case that there's at least + # one URL that doesn't have a path prefix. + return '/' + prefixes.append('/' + prefix + '/') + return common_path(prefixes) + +# View Inspectors # + + +def field_to_schema(field): + title = force_str(field.label) if field.label else '' + description = force_str(field.help_text) if field.help_text else '' + + if isinstance(field, (serializers.ListSerializer, serializers.ListField)): + child_schema = field_to_schema(field.child) + return coreschema.Array( + items=child_schema, + title=title, + description=description + ) + elif isinstance(field, serializers.DictField): + return coreschema.Object( + title=title, + description=description + ) + elif isinstance(field, serializers.Serializer): + return coreschema.Object( + properties=OrderedDict([ + (key, field_to_schema(value)) + for key, value + in field.fields.items() + ]), + title=title, + description=description + ) + elif isinstance(field, serializers.ManyRelatedField): + related_field_schema = field_to_schema(field.child_relation) + + return coreschema.Array( + items=related_field_schema, + title=title, + description=description + ) + elif isinstance(field, serializers.PrimaryKeyRelatedField): + schema_cls = coreschema.String + model = getattr(field.queryset, 'model', None) + if model is not None: + model_field = model._meta.pk + if isinstance(model_field, models.AutoField): + schema_cls = coreschema.Integer + return schema_cls(title=title, description=description) + elif isinstance(field, serializers.RelatedField): + return coreschema.String(title=title, description=description) + elif isinstance(field, serializers.MultipleChoiceField): + return coreschema.Array( + items=coreschema.Enum(enum=list(field.choices)), + title=title, + description=description + ) + elif isinstance(field, serializers.ChoiceField): + return coreschema.Enum( + enum=list(field.choices), + title=title, + description=description + ) + elif isinstance(field, serializers.BooleanField): + return coreschema.Boolean(title=title, description=description) + elif isinstance(field, (serializers.DecimalField, serializers.FloatField)): + return coreschema.Number(title=title, description=description) + elif isinstance(field, serializers.IntegerField): + return coreschema.Integer(title=title, description=description) + elif isinstance(field, serializers.DateField): + return coreschema.String( + title=title, + description=description, + format='date' + ) + elif isinstance(field, serializers.DateTimeField): + return coreschema.String( + title=title, + description=description, + format='date-time' + ) + elif isinstance(field, serializers.JSONField): + return coreschema.Object(title=title, description=description) + + if field.style.get('base_template') == 'textarea.html': + return coreschema.String( + title=title, + description=description, + format='textarea' + ) + + return coreschema.String(title=title, description=description) + + +class AutoSchema(ViewInspector): + """ + Default inspector for APIView + + Responsible for per-view introspection and schema generation. + """ + def __init__(self, manual_fields=None): + """ + Parameters: + + * `manual_fields`: list of `coreapi.Field` instances that + will be added to auto-generated fields, overwriting on `Field.name` + """ + super().__init__() + if manual_fields is None: + manual_fields = [] + self._manual_fields = manual_fields + + def get_link(self, path, method, base_url): + """ + Generate `coreapi.Link` for self.view, path and method. + + This is the main _public_ access point. + + Parameters: + + * path: Route path for view from URLConf. + * method: The HTTP request method. + * base_url: The project "mount point" as given to SchemaGenerator + """ + fields = self.get_path_fields(path, method) + fields += self.get_serializer_fields(path, method) + fields += self.get_pagination_fields(path, method) + fields += self.get_filter_fields(path, method) + + manual_fields = self.get_manual_fields(path, method) + fields = self.update_fields(fields, manual_fields) + + if fields and any([field.location in ('form', 'body') for field in fields]): + encoding = self.get_encoding(path, method) + else: + encoding = None + + description = self.get_description(path, method) + + if base_url and path.startswith('/'): + path = path[1:] + + return coreapi.Link( + url=parse.urljoin(base_url, path), + action=method.lower(), + encoding=encoding, + fields=fields, + description=description + ) + + def get_path_fields(self, path, method): + """ + Return a list of `coreapi.Field` instances corresponding to any + templated path variables. + """ + view = self.view + model = getattr(getattr(view, 'queryset', None), 'model', None) + fields = [] + + for variable in uritemplate.variables(path): + title = '' + description = '' + schema_cls = coreschema.String + kwargs = {} + if model is not None: + # Attempt to infer a field description if possible. + try: + model_field = model._meta.get_field(variable) + except Exception: + model_field = None + + if model_field is not None and model_field.verbose_name: + title = force_str(model_field.verbose_name) + + if model_field is not None and model_field.help_text: + description = force_str(model_field.help_text) + elif model_field is not None and model_field.primary_key: + description = get_pk_description(model, model_field) + + if hasattr(view, 'lookup_value_regex') and view.lookup_field == variable: + kwargs['pattern'] = view.lookup_value_regex + elif isinstance(model_field, models.AutoField): + schema_cls = coreschema.Integer + + field = coreapi.Field( + name=variable, + location='path', + required=True, + schema=schema_cls(title=title, description=description, **kwargs) + ) + fields.append(field) + + return fields + + def get_serializer_fields(self, path, method): + """ + Return a list of `coreapi.Field` instances corresponding to any + request body input, as determined by the serializer class. + """ + view = self.view + + if method not in ('PUT', 'PATCH', 'POST'): + return [] + + if not hasattr(view, 'get_serializer'): + return [] + + try: + serializer = view.get_serializer() + except exceptions.APIException: + serializer = None + warnings.warn('{}.get_serializer() raised an exception during ' + 'schema generation. Serializer fields will not be ' + 'generated for {} {}.' + .format(view.__class__.__name__, method, path)) + + if isinstance(serializer, serializers.ListSerializer): + return [ + coreapi.Field( + name='data', + location='body', + required=True, + schema=coreschema.Array() + ) + ] + + if not isinstance(serializer, serializers.Serializer): + return [] + + fields = [] + for field in serializer.fields.values(): + if field.read_only or isinstance(field, serializers.HiddenField): + continue + + required = field.required and method != 'PATCH' + field = coreapi.Field( + name=field.field_name, + location='form', + required=required, + schema=field_to_schema(field) + ) + fields.append(field) + + return fields + + def get_pagination_fields(self, path, method): + view = self.view + + if not is_list_view(path, method, view): + return [] + + pagination = getattr(view, 'pagination_class', None) + if not pagination: + return [] + + paginator = view.pagination_class() + return paginator.get_schema_fields(view) + + def _allows_filters(self, path, method): + """ + Determine whether to include filter Fields in schema. + + Default implementation looks for ModelViewSet or GenericAPIView + actions/methods that cause filtering on the default implementation. + + Override to adjust behaviour for your view. + + Note: Introduced in v3.7: Initially "private" (i.e. with leading underscore) + to allow changes based on user experience. + """ + if getattr(self.view, 'filter_backends', None) is None: + return False + + if hasattr(self.view, 'action'): + return self.view.action in ["list", "retrieve", "update", "partial_update", "destroy"] + + return method.lower() in ["get", "put", "patch", "delete"] + + def get_filter_fields(self, path, method): + if not self._allows_filters(path, method): + return [] + + fields = [] + for filter_backend in self.view.filter_backends: + fields += filter_backend().get_schema_fields(self.view) + return fields + + def get_manual_fields(self, path, method): + return self._manual_fields + + @staticmethod + def update_fields(fields, update_with): + """ + Update list of coreapi.Field instances, overwriting on `Field.name`. + + Utility function to handle replacing coreapi.Field fields + from a list by name. Used to handle `manual_fields`. + + Parameters: + + * `fields`: list of `coreapi.Field` instances to update + * `update_with: list of `coreapi.Field` instances to add or replace. + """ + if not update_with: + return fields + + by_name = OrderedDict((f.name, f) for f in fields) + for f in update_with: + by_name[f.name] = f + fields = list(by_name.values()) + return fields + + def get_encoding(self, path, method): + """ + Return the 'encoding' parameter to use for a given endpoint. + """ + view = self.view + + # Core API supports the following request encodings over HTTP... + supported_media_types = { + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', + } + parser_classes = getattr(view, 'parser_classes', []) + for parser_class in parser_classes: + media_type = getattr(parser_class, 'media_type', None) + if media_type in supported_media_types: + return media_type + # Raw binary uploads are supported with "application/octet-stream" + if media_type == '*/*': + return 'application/octet-stream' + + return None + + +class ManualSchema(ViewInspector): + """ + Allows providing a list of coreapi.Fields, + plus an optional description. + """ + def __init__(self, fields, description='', encoding=None): + """ + Parameters: + + * `fields`: list of `coreapi.Field` instances. + * `description`: String description for view. Optional. + """ + super().__init__() + assert all(isinstance(f, coreapi.Field) for f in fields), "`fields` must be a list of coreapi.Field instances" + self._fields = fields + self._description = description + self._encoding = encoding + + def get_link(self, path, method, base_url): + + if base_url and path.startswith('/'): + path = path[1:] + + return coreapi.Link( + url=parse.urljoin(base_url, path), + action=method.lower(), + encoding=self._encoding, + fields=self._fields, + description=self._description + ) + + +def is_enabled(): + """Is CoreAPI Mode enabled?""" + return issubclass(api_settings.DEFAULT_SCHEMA_CLASS, AutoSchema) diff --git a/venv/Lib/site-packages/rest_framework/schemas/generators.py b/venv/Lib/site-packages/rest_framework/schemas/generators.py new file mode 100644 index 0000000..d3c6446 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/generators.py @@ -0,0 +1,239 @@ +""" +generators.py # Top-down schema generation + +See schemas.__init__.py for package overview. +""" +import re +from importlib import import_module + +from django.conf import settings +from django.contrib.admindocs.views import simplify_regex +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.urls import URLPattern, URLResolver + +from rest_framework import exceptions +from rest_framework.request import clone_request +from rest_framework.settings import api_settings +from rest_framework.utils.model_meta import _get_pk + + +def get_pk_name(model): + meta = model._meta.concrete_model._meta + return _get_pk(meta).name + + +def is_api_view(callback): + """ + Return `True` if the given view callback is a REST framework view/viewset. + """ + # Avoid import cycle on APIView + from rest_framework.views import APIView + cls = getattr(callback, 'cls', None) + return (cls is not None) and issubclass(cls, APIView) + + +def endpoint_ordering(endpoint): + path, method, callback = endpoint + method_priority = { + 'GET': 0, + 'POST': 1, + 'PUT': 2, + 'PATCH': 3, + 'DELETE': 4 + }.get(method, 5) + return (method_priority,) + + +_PATH_PARAMETER_COMPONENT_RE = re.compile( + r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>\w+)>' +) + + +class EndpointEnumerator: + """ + A class to determine the available API endpoints that a project exposes. + """ + def __init__(self, patterns=None, urlconf=None): + if patterns is None: + if urlconf is None: + # Use the default Django URL conf + urlconf = settings.ROOT_URLCONF + + # Load the given URLconf module + if isinstance(urlconf, str): + urls = import_module(urlconf) + else: + urls = urlconf + patterns = urls.urlpatterns + + self.patterns = patterns + + def get_api_endpoints(self, patterns=None, prefix=''): + """ + Return a list of all available API endpoints by inspecting the URL conf. + """ + if patterns is None: + patterns = self.patterns + + api_endpoints = [] + + for pattern in patterns: + path_regex = prefix + str(pattern.pattern) + if isinstance(pattern, URLPattern): + path = self.get_path_from_regex(path_regex) + callback = pattern.callback + if self.should_include_endpoint(path, callback): + for method in self.get_allowed_methods(callback): + endpoint = (path, method, callback) + api_endpoints.append(endpoint) + + elif isinstance(pattern, URLResolver): + nested_endpoints = self.get_api_endpoints( + patterns=pattern.url_patterns, + prefix=path_regex + ) + api_endpoints.extend(nested_endpoints) + + return sorted(api_endpoints, key=endpoint_ordering) + + def get_path_from_regex(self, path_regex): + """ + Given a URL conf regex, return a URI template string. + """ + # ???: Would it be feasible to adjust this such that we generate the + # path, plus the kwargs, plus the type from the convertor, such that we + # could feed that straight into the parameter schema object? + + path = simplify_regex(path_regex) + + # Strip Django 2.0 convertors as they are incompatible with uritemplate format + return re.sub(_PATH_PARAMETER_COMPONENT_RE, r'{\g<parameter>}', path) + + def should_include_endpoint(self, path, callback): + """ + Return `True` if the given endpoint should be included. + """ + if not is_api_view(callback): + return False # Ignore anything except REST framework views. + + if callback.cls.schema is None: + return False + + if 'schema' in callback.initkwargs: + if callback.initkwargs['schema'] is None: + return False + + if path.endswith('.{format}') or path.endswith('.{format}/'): + return False # Ignore .json style URLs. + + return True + + def get_allowed_methods(self, callback): + """ + Return a list of the valid HTTP methods for this endpoint. + """ + if hasattr(callback, 'actions'): + actions = set(callback.actions) + http_method_names = set(callback.cls.http_method_names) + methods = [method.upper() for method in actions & http_method_names] + else: + methods = callback.cls().allowed_methods + + return [method for method in methods if method not in ('OPTIONS', 'HEAD')] + + +class BaseSchemaGenerator: + endpoint_inspector_cls = EndpointEnumerator + + # 'pk' isn't great as an externally exposed name for an identifier, + # so by default we prefer to use the actual model field name for schemas. + # Set by 'SCHEMA_COERCE_PATH_PK'. + coerce_path_pk = None + + def __init__(self, title=None, url=None, description=None, patterns=None, urlconf=None, version=None): + if url and not url.endswith('/'): + url += '/' + + self.coerce_path_pk = api_settings.SCHEMA_COERCE_PATH_PK + + self.patterns = patterns + self.urlconf = urlconf + self.title = title + self.description = description + self.version = version + self.url = url + self.endpoints = None + + def _initialise_endpoints(self): + if self.endpoints is None: + inspector = self.endpoint_inspector_cls(self.patterns, self.urlconf) + self.endpoints = inspector.get_api_endpoints() + + def _get_paths_and_endpoints(self, request): + """ + Generate (path, method, view) given (path, method, callback) for paths. + """ + paths = [] + view_endpoints = [] + for path, method, callback in self.endpoints: + view = self.create_view(callback, method, request) + path = self.coerce_path(path, method, view) + paths.append(path) + view_endpoints.append((path, method, view)) + + return paths, view_endpoints + + def create_view(self, callback, method, request=None): + """ + Given a callback, return an actual view instance. + """ + view = callback.cls(**getattr(callback, 'initkwargs', {})) + view.args = () + view.kwargs = {} + view.format_kwarg = None + view.request = None + view.action_map = getattr(callback, 'actions', None) + + actions = getattr(callback, 'actions', None) + if actions is not None: + if method == 'OPTIONS': + view.action = 'metadata' + else: + view.action = actions.get(method.lower()) + + if request is not None: + view.request = clone_request(request, method) + + return view + + def coerce_path(self, path, method, view): + """ + Coerce {pk} path arguments into the name of the model field, + where possible. This is cleaner for an external representation. + (Ie. "this is an identifier", not "this is a database primary key") + """ + if not self.coerce_path_pk or '{pk}' not in path: + return path + model = getattr(getattr(view, 'queryset', None), 'model', None) + if model: + field_name = get_pk_name(model) + else: + field_name = 'id' + return path.replace('{pk}', '{%s}' % field_name) + + def get_schema(self, request=None, public=False): + raise NotImplementedError(".get_schema() must be implemented in subclasses.") + + def has_view_permissions(self, path, method, view): + """ + Return `True` if the incoming request has the correct view permissions. + """ + if view.request is None: + return True + + try: + view.check_permissions(view.request) + except (exceptions.APIException, Http404, PermissionDenied): + return False + return True diff --git a/venv/Lib/site-packages/rest_framework/schemas/inspectors.py b/venv/Lib/site-packages/rest_framework/schemas/inspectors.py new file mode 100644 index 0000000..027472d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/inspectors.py @@ -0,0 +1,125 @@ +""" +inspectors.py # Per-endpoint view introspection + +See schemas.__init__.py for package overview. +""" +import re +from weakref import WeakKeyDictionary + +from django.utils.encoding import smart_str + +from rest_framework.settings import api_settings +from rest_framework.utils import formatting + + +class ViewInspector: + """ + Descriptor class on APIView. + + Provide subclass for per-view schema generation + """ + + # Used in _get_description_section() + header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') + + def __init__(self): + self.instance_schemas = WeakKeyDictionary() + + def __get__(self, instance, owner): + """ + Enables `ViewInspector` as a Python _Descriptor_. + + This is how `view.schema` knows about `view`. + + `__get__` is called when the descriptor is accessed on the owner. + (That will be when view.schema is called in our case.) + + `owner` is always the owner class. (An APIView, or subclass for us.) + `instance` is the view instance or `None` if accessed from the class, + rather than an instance. + + See: https://docs.python.org/3/howto/descriptor.html for info on + descriptor usage. + """ + if instance in self.instance_schemas: + return self.instance_schemas[instance] + + self.view = instance + return self + + def __set__(self, instance, other): + self.instance_schemas[instance] = other + if other is not None: + other.view = instance + + @property + def view(self): + """View property.""" + assert self._view is not None, ( + "Schema generation REQUIRES a view instance. (Hint: you accessed " + "`schema` from the view class rather than an instance.)" + ) + return self._view + + @view.setter + def view(self, value): + self._view = value + + @view.deleter + def view(self): + self._view = None + + def get_description(self, path, method): + """ + Determine a path description. + + This will be based on the method docstring if one exists, + or else the class docstring. + """ + view = self.view + + method_name = getattr(view, 'action', method.lower()) + method_docstring = getattr(view, method_name, None).__doc__ + if method_docstring: + # An explicit docstring on the method or action. + return self._get_description_section(view, method.lower(), formatting.dedent(smart_str(method_docstring))) + else: + return self._get_description_section(view, getattr(view, 'action', method.lower()), + view.get_view_description()) + + def _get_description_section(self, view, header, description): + lines = [line for line in description.splitlines()] + current_section = '' + sections = {'': ''} + + for line in lines: + if self.header_regex.match(line): + current_section, separator, lead = line.partition(':') + sections[current_section] = lead.strip() + else: + sections[current_section] += '\n' + line + + # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys` + coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES + if header in sections: + return sections[header].strip() + if header in coerce_method_names: + if coerce_method_names[header] in sections: + return sections[coerce_method_names[header]].strip() + return sections[''].strip() + + +class DefaultSchema(ViewInspector): + """Allows overriding AutoSchema using DEFAULT_SCHEMA_CLASS setting""" + def __get__(self, instance, owner): + result = super().__get__(instance, owner) + if not isinstance(result, DefaultSchema): + return result + + inspector_class = api_settings.DEFAULT_SCHEMA_CLASS + assert issubclass(inspector_class, ViewInspector), ( + "DEFAULT_SCHEMA_CLASS must be set to a ViewInspector (usually an AutoSchema) subclass" + ) + inspector = inspector_class() + inspector.view = instance + return inspector diff --git a/venv/Lib/site-packages/rest_framework/schemas/openapi.py b/venv/Lib/site-packages/rest_framework/schemas/openapi.py new file mode 100644 index 0000000..5e9d59f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/openapi.py @@ -0,0 +1,810 @@ +import re +import warnings +from collections import OrderedDict +from decimal import Decimal +from operator import attrgetter +from urllib.parse import urljoin + +from django.core.validators import ( + DecimalValidator, EmailValidator, MaxLengthValidator, MaxValueValidator, + MinLengthValidator, MinValueValidator, RegexValidator, URLValidator +) +from django.db import models +from django.utils.encoding import force_str + +from rest_framework import ( + RemovedInDRF314Warning, exceptions, renderers, serializers +) +from rest_framework.compat import uritemplate +from rest_framework.fields import _UnvalidatedField, empty +from rest_framework.settings import api_settings + +from .generators import BaseSchemaGenerator +from .inspectors import ViewInspector +from .utils import get_pk_description, is_list_view + + +class SchemaGenerator(BaseSchemaGenerator): + + def get_info(self): + # Title and version are required by openapi specification 3.x + info = { + 'title': self.title or '', + 'version': self.version or '' + } + + if self.description is not None: + info['description'] = self.description + + return info + + def check_duplicate_operation_id(self, paths): + ids = {} + for route in paths: + for method in paths[route]: + if 'operationId' not in paths[route][method]: + continue + operation_id = paths[route][method]['operationId'] + if operation_id in ids: + warnings.warn( + 'You have a duplicated operationId in your OpenAPI schema: {operation_id}\n' + '\tRoute: {route1}, Method: {method1}\n' + '\tRoute: {route2}, Method: {method2}\n' + '\tAn operationId has to be unique across your schema. Your schema may not work in other tools.' + .format( + route1=ids[operation_id]['route'], + method1=ids[operation_id]['method'], + route2=route, + method2=method, + operation_id=operation_id + ) + ) + ids[operation_id] = { + 'route': route, + 'method': method + } + + def get_schema(self, request=None, public=False): + """ + Generate a OpenAPI schema. + """ + self._initialise_endpoints() + components_schemas = {} + + # Iterate endpoints generating per method path operations. + paths = {} + _, view_endpoints = self._get_paths_and_endpoints(None if public else request) + for path, method, view in view_endpoints: + if not self.has_view_permissions(path, method, view): + continue + + operation = view.schema.get_operation(path, method) + components = view.schema.get_components(path, method) + for k in components.keys(): + if k not in components_schemas: + continue + if components_schemas[k] == components[k]: + continue + warnings.warn('Schema component "{}" has been overriden with a different value.'.format(k)) + + components_schemas.update(components) + + # Normalise path for any provided mount url. + if path.startswith('/'): + path = path[1:] + path = urljoin(self.url or '/', path) + + paths.setdefault(path, {}) + paths[path][method.lower()] = operation + + self.check_duplicate_operation_id(paths) + + # Compile final schema. + schema = { + 'openapi': '3.0.2', + 'info': self.get_info(), + 'paths': paths, + } + + if len(components_schemas) > 0: + schema['components'] = { + 'schemas': components_schemas + } + + return schema + +# View Inspectors + + +class AutoSchema(ViewInspector): + + def __init__(self, tags=None, operation_id_base=None, component_name=None): + """ + :param operation_id_base: user-defined name in operationId. If empty, it will be deducted from the Model/Serializer/View name. + :param component_name: user-defined component's name. If empty, it will be deducted from the Serializer's class name. + """ + if tags and not all(isinstance(tag, str) for tag in tags): + raise ValueError('tags must be a list or tuple of string.') + self._tags = tags + self.operation_id_base = operation_id_base + self.component_name = component_name + super().__init__() + + request_media_types = [] + response_media_types = [] + + method_mapping = { + 'get': 'retrieve', + 'post': 'create', + 'put': 'update', + 'patch': 'partialUpdate', + 'delete': 'destroy', + } + + def get_operation(self, path, method): + operation = {} + + operation['operationId'] = self.get_operation_id(path, method) + operation['description'] = self.get_description(path, method) + + parameters = [] + parameters += self.get_path_parameters(path, method) + parameters += self.get_pagination_parameters(path, method) + parameters += self.get_filter_parameters(path, method) + operation['parameters'] = parameters + + request_body = self.get_request_body(path, method) + if request_body: + operation['requestBody'] = request_body + operation['responses'] = self.get_responses(path, method) + operation['tags'] = self.get_tags(path, method) + + return operation + + def get_component_name(self, serializer): + """ + Compute the component's name from the serializer. + Raise an exception if the serializer's class name is "Serializer" (case-insensitive). + """ + if self.component_name is not None: + return self.component_name + + # use the serializer's class name as the component name. + component_name = serializer.__class__.__name__ + # We remove the "serializer" string from the class name. + pattern = re.compile("serializer", re.IGNORECASE) + component_name = pattern.sub("", component_name) + + if component_name == "": + raise Exception( + '"{}" is an invalid class name for schema generation. ' + 'Serializer\'s class name should be unique and explicit. e.g. "ItemSerializer"' + .format(serializer.__class__.__name__) + ) + + return component_name + + def get_components(self, path, method): + """ + Return components with their properties from the serializer. + """ + + if method.lower() == 'delete': + return {} + + request_serializer = self.get_request_serializer(path, method) + response_serializer = self.get_response_serializer(path, method) + + components = {} + + if isinstance(request_serializer, serializers.Serializer): + component_name = self.get_component_name(request_serializer) + content = self.map_serializer(request_serializer) + components.setdefault(component_name, content) + + if isinstance(response_serializer, serializers.Serializer): + component_name = self.get_component_name(response_serializer) + content = self.map_serializer(response_serializer) + components.setdefault(component_name, content) + + return components + + def _to_camel_case(self, snake_str): + components = snake_str.split('_') + # We capitalize the first letter of each component except the first one + # with the 'title' method and join them together. + return components[0] + ''.join(x.title() for x in components[1:]) + + def get_operation_id_base(self, path, method, action): + """ + Compute the base part for operation ID from the model, serializer or view name. + """ + model = getattr(getattr(self.view, 'queryset', None), 'model', None) + + if self.operation_id_base is not None: + name = self.operation_id_base + + # Try to deduce the ID from the view's model + elif model is not None: + name = model.__name__ + + # Try with the serializer class name + elif self.get_serializer(path, method) is not None: + name = self.get_serializer(path, method).__class__.__name__ + if name.endswith('Serializer'): + name = name[:-10] + + # Fallback to the view name + else: + name = self.view.__class__.__name__ + if name.endswith('APIView'): + name = name[:-7] + elif name.endswith('View'): + name = name[:-4] + + # Due to camel-casing of classes and `action` being lowercase, apply title in order to find if action truly + # comes at the end of the name + if name.endswith(action.title()): # ListView, UpdateAPIView, ThingDelete ... + name = name[:-len(action)] + + if action == 'list' and not name.endswith('s'): # listThings instead of listThing + name += 's' + + return name + + def get_operation_id(self, path, method): + """ + Compute an operation ID from the view type and get_operation_id_base method. + """ + method_name = getattr(self.view, 'action', method.lower()) + if is_list_view(path, method, self.view): + action = 'list' + elif method_name not in self.method_mapping: + action = self._to_camel_case(method_name) + else: + action = self.method_mapping[method.lower()] + + name = self.get_operation_id_base(path, method, action) + + return action + name + + def get_path_parameters(self, path, method): + """ + Return a list of parameters from templated path variables. + """ + assert uritemplate, '`uritemplate` must be installed for OpenAPI schema support.' + + model = getattr(getattr(self.view, 'queryset', None), 'model', None) + parameters = [] + + for variable in uritemplate.variables(path): + description = '' + if model is not None: # TODO: test this. + # Attempt to infer a field description if possible. + try: + model_field = model._meta.get_field(variable) + except Exception: + model_field = None + + if model_field is not None and model_field.help_text: + description = force_str(model_field.help_text) + elif model_field is not None and model_field.primary_key: + description = get_pk_description(model, model_field) + + parameter = { + "name": variable, + "in": "path", + "required": True, + "description": description, + 'schema': { + 'type': 'string', # TODO: integer, pattern, ... + }, + } + parameters.append(parameter) + + return parameters + + def get_filter_parameters(self, path, method): + if not self.allows_filters(path, method): + return [] + parameters = [] + for filter_backend in self.view.filter_backends: + parameters += filter_backend().get_schema_operation_parameters(self.view) + return parameters + + def allows_filters(self, path, method): + """ + Determine whether to include filter Fields in schema. + + Default implementation looks for ModelViewSet or GenericAPIView + actions/methods that cause filtering on the default implementation. + """ + if getattr(self.view, 'filter_backends', None) is None: + return False + if hasattr(self.view, 'action'): + return self.view.action in ["list", "retrieve", "update", "partial_update", "destroy"] + return method.lower() in ["get", "put", "patch", "delete"] + + def get_pagination_parameters(self, path, method): + view = self.view + + if not is_list_view(path, method, view): + return [] + + paginator = self.get_paginator() + if not paginator: + return [] + + return paginator.get_schema_operation_parameters(view) + + def map_choicefield(self, field): + choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates + if all(isinstance(choice, bool) for choice in choices): + type = 'boolean' + elif all(isinstance(choice, int) for choice in choices): + type = 'integer' + elif all(isinstance(choice, (int, float, Decimal)) for choice in choices): # `number` includes `integer` + # Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21 + type = 'number' + elif all(isinstance(choice, str) for choice in choices): + type = 'string' + else: + type = None + + mapping = { + # The value of `enum` keyword MUST be an array and SHOULD be unique. + # Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.20 + 'enum': choices + } + + # If We figured out `type` then and only then we should set it. It must be a string. + # Ref: https://swagger.io/docs/specification/data-models/data-types/#mixed-type + # It is optional but it can not be null. + # Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21 + if type: + mapping['type'] = type + return mapping + + def map_field(self, field): + + # Nested Serializers, `many` or not. + if isinstance(field, serializers.ListSerializer): + return { + 'type': 'array', + 'items': self.map_serializer(field.child) + } + if isinstance(field, serializers.Serializer): + data = self.map_serializer(field) + data['type'] = 'object' + return data + + # Related fields. + if isinstance(field, serializers.ManyRelatedField): + return { + 'type': 'array', + 'items': self.map_field(field.child_relation) + } + if isinstance(field, serializers.PrimaryKeyRelatedField): + model = getattr(field.queryset, 'model', None) + if model is not None: + model_field = model._meta.pk + if isinstance(model_field, models.AutoField): + return {'type': 'integer'} + + # ChoiceFields (single and multiple). + # Q: + # - Is 'type' required? + # - can we determine the TYPE of a choicefield? + if isinstance(field, serializers.MultipleChoiceField): + return { + 'type': 'array', + 'items': self.map_choicefield(field) + } + + if isinstance(field, serializers.ChoiceField): + return self.map_choicefield(field) + + # ListField. + if isinstance(field, serializers.ListField): + mapping = { + 'type': 'array', + 'items': {}, + } + if not isinstance(field.child, _UnvalidatedField): + mapping['items'] = self.map_field(field.child) + return mapping + + # DateField and DateTimeField type is string + if isinstance(field, serializers.DateField): + return { + 'type': 'string', + 'format': 'date', + } + + if isinstance(field, serializers.DateTimeField): + return { + 'type': 'string', + 'format': 'date-time', + } + + # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification." + # see: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types + # see also: https://swagger.io/docs/specification/data-models/data-types/#string + if isinstance(field, serializers.EmailField): + return { + 'type': 'string', + 'format': 'email' + } + + if isinstance(field, serializers.URLField): + return { + 'type': 'string', + 'format': 'uri' + } + + if isinstance(field, serializers.UUIDField): + return { + 'type': 'string', + 'format': 'uuid' + } + + if isinstance(field, serializers.IPAddressField): + content = { + 'type': 'string', + } + if field.protocol != 'both': + content['format'] = field.protocol + return content + + if isinstance(field, serializers.DecimalField): + if getattr(field, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING): + content = { + 'type': 'string', + 'format': 'decimal', + } + else: + content = { + 'type': 'number' + } + + if field.decimal_places: + content['multipleOf'] = float('.' + (field.decimal_places - 1) * '0' + '1') + if field.max_whole_digits: + content['maximum'] = int(field.max_whole_digits * '9') + 1 + content['minimum'] = -content['maximum'] + self._map_min_max(field, content) + return content + + if isinstance(field, serializers.FloatField): + content = { + 'type': 'number', + } + self._map_min_max(field, content) + return content + + if isinstance(field, serializers.IntegerField): + content = { + 'type': 'integer' + } + self._map_min_max(field, content) + # 2147483647 is max for int32_size, so we use int64 for format + if int(content.get('maximum', 0)) > 2147483647 or int(content.get('minimum', 0)) > 2147483647: + content['format'] = 'int64' + return content + + if isinstance(field, serializers.FileField): + return { + 'type': 'string', + 'format': 'binary' + } + + # Simplest cases, default to 'string' type: + FIELD_CLASS_SCHEMA_TYPE = { + serializers.BooleanField: 'boolean', + serializers.JSONField: 'object', + serializers.DictField: 'object', + serializers.HStoreField: 'object', + } + return {'type': FIELD_CLASS_SCHEMA_TYPE.get(field.__class__, 'string')} + + def _map_min_max(self, field, content): + if field.max_value: + content['maximum'] = field.max_value + if field.min_value: + content['minimum'] = field.min_value + + def map_serializer(self, serializer): + # Assuming we have a valid serializer instance. + required = [] + properties = {} + + for field in serializer.fields.values(): + if isinstance(field, serializers.HiddenField): + continue + + if field.required: + required.append(field.field_name) + + schema = self.map_field(field) + if field.read_only: + schema['readOnly'] = True + if field.write_only: + schema['writeOnly'] = True + if field.allow_null: + schema['nullable'] = True + if field.default is not None and field.default != empty and not callable(field.default): + schema['default'] = field.default + if field.help_text: + schema['description'] = str(field.help_text) + self.map_field_validators(field, schema) + + properties[field.field_name] = schema + + result = { + 'type': 'object', + 'properties': properties + } + if required: + result['required'] = required + + return result + + def map_field_validators(self, field, schema): + """ + map field validators + """ + for v in field.validators: + # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification." + # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types + if isinstance(v, EmailValidator): + schema['format'] = 'email' + if isinstance(v, URLValidator): + schema['format'] = 'uri' + if isinstance(v, RegexValidator): + # In Python, the token \Z does what \z does in other engines. + # https://stackoverflow.com/questions/53283160 + schema['pattern'] = v.regex.pattern.replace('\\Z', '\\z') + elif isinstance(v, MaxLengthValidator): + attr_name = 'maxLength' + if isinstance(field, serializers.ListField): + attr_name = 'maxItems' + schema[attr_name] = v.limit_value + elif isinstance(v, MinLengthValidator): + attr_name = 'minLength' + if isinstance(field, serializers.ListField): + attr_name = 'minItems' + schema[attr_name] = v.limit_value + elif isinstance(v, MaxValueValidator): + schema['maximum'] = v.limit_value + elif isinstance(v, MinValueValidator): + schema['minimum'] = v.limit_value + elif isinstance(v, DecimalValidator) and \ + not getattr(field, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING): + if v.decimal_places: + schema['multipleOf'] = float('.' + (v.decimal_places - 1) * '0' + '1') + if v.max_digits: + digits = v.max_digits + if v.decimal_places is not None and v.decimal_places > 0: + digits -= v.decimal_places + schema['maximum'] = int(digits * '9') + 1 + schema['minimum'] = -schema['maximum'] + + def get_paginator(self): + pagination_class = getattr(self.view, 'pagination_class', None) + if pagination_class: + return pagination_class() + return None + + def map_parsers(self, path, method): + return list(map(attrgetter('media_type'), self.view.parser_classes)) + + def map_renderers(self, path, method): + media_types = [] + for renderer in self.view.renderer_classes: + # BrowsableAPIRenderer not relevant to OpenAPI spec + if issubclass(renderer, renderers.BrowsableAPIRenderer): + continue + media_types.append(renderer.media_type) + return media_types + + def get_serializer(self, path, method): + view = self.view + + if not hasattr(view, 'get_serializer'): + return None + + try: + return view.get_serializer() + except exceptions.APIException: + warnings.warn('{}.get_serializer() raised an exception during ' + 'schema generation. Serializer fields will not be ' + 'generated for {} {}.' + .format(view.__class__.__name__, method, path)) + return None + + def get_request_serializer(self, path, method): + """ + Override this method if your view uses a different serializer for + handling request body. + """ + return self.get_serializer(path, method) + + def get_response_serializer(self, path, method): + """ + Override this method if your view uses a different serializer for + populating response data. + """ + return self.get_serializer(path, method) + + def _get_reference(self, serializer): + return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(serializer))} + + def get_request_body(self, path, method): + if method not in ('PUT', 'PATCH', 'POST'): + return {} + + self.request_media_types = self.map_parsers(path, method) + + serializer = self.get_request_serializer(path, method) + + if not isinstance(serializer, serializers.Serializer): + item_schema = {} + else: + item_schema = self._get_reference(serializer) + + return { + 'content': { + ct: {'schema': item_schema} + for ct in self.request_media_types + } + } + + def get_responses(self, path, method): + if method == 'DELETE': + return { + '204': { + 'description': '' + } + } + + self.response_media_types = self.map_renderers(path, method) + + serializer = self.get_response_serializer(path, method) + + if not isinstance(serializer, serializers.Serializer): + item_schema = {} + else: + item_schema = self._get_reference(serializer) + + if is_list_view(path, method, self.view): + response_schema = { + 'type': 'array', + 'items': item_schema, + } + paginator = self.get_paginator() + if paginator: + response_schema = paginator.get_paginated_response_schema(response_schema) + else: + response_schema = item_schema + status_code = '201' if method == 'POST' else '200' + return { + status_code: { + 'content': { + ct: {'schema': response_schema} + for ct in self.response_media_types + }, + # description is a mandatory property, + # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject + # TODO: put something meaningful into it + 'description': "" + } + } + + def get_tags(self, path, method): + # If user have specified tags, use them. + if self._tags: + return self._tags + + # First element of a specific path could be valid tag. This is a fallback solution. + # PUT, PATCH, GET(Retrieve), DELETE: /user_profile/{id}/ tags = [user-profile] + # POST, GET(List): /user_profile/ tags = [user-profile] + if path.startswith('/'): + path = path[1:] + + return [path.split('/')[0].replace('_', '-')] + + def _get_path_parameters(self, path, method): + warnings.warn( + "Method `_get_path_parameters()` has been renamed to `get_path_parameters()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_path_parameters(path, method) + + def _get_filter_parameters(self, path, method): + warnings.warn( + "Method `_get_filter_parameters()` has been renamed to `get_filter_parameters()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_filter_parameters(path, method) + + def _get_responses(self, path, method): + warnings.warn( + "Method `_get_responses()` has been renamed to `get_responses()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_responses(path, method) + + def _get_request_body(self, path, method): + warnings.warn( + "Method `_get_request_body()` has been renamed to `get_request_body()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_request_body(path, method) + + def _get_serializer(self, path, method): + warnings.warn( + "Method `_get_serializer()` has been renamed to `get_serializer()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_serializer(path, method) + + def _get_paginator(self): + warnings.warn( + "Method `_get_paginator()` has been renamed to `get_paginator()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_paginator() + + def _map_field_validators(self, field, schema): + warnings.warn( + "Method `_map_field_validators()` has been renamed to `map_field_validators()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.map_field_validators(field, schema) + + def _map_serializer(self, serializer): + warnings.warn( + "Method `_map_serializer()` has been renamed to `map_serializer()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.map_serializer(serializer) + + def _map_field(self, field): + warnings.warn( + "Method `_map_field()` has been renamed to `map_field()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.map_field(field) + + def _map_choicefield(self, field): + warnings.warn( + "Method `_map_choicefield()` has been renamed to `map_choicefield()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.map_choicefield(field) + + def _get_pagination_parameters(self, path, method): + warnings.warn( + "Method `_get_pagination_parameters()` has been renamed to `get_pagination_parameters()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.get_pagination_parameters(path, method) + + def _allows_filters(self, path, method): + warnings.warn( + "Method `_allows_filters()` has been renamed to `allows_filters()`. " + "The old name will be removed in DRF v3.14.", + RemovedInDRF314Warning, stacklevel=2 + ) + return self.allows_filters(path, method) diff --git a/venv/Lib/site-packages/rest_framework/schemas/utils.py b/venv/Lib/site-packages/rest_framework/schemas/utils.py new file mode 100644 index 0000000..60ed698 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/utils.py @@ -0,0 +1,41 @@ +""" +utils.py # Shared helper functions + +See schemas.__init__.py for package overview. +""" +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from rest_framework.mixins import RetrieveModelMixin + + +def is_list_view(path, method, view): + """ + Return True if the given path/method appears to represent a list view. + """ + if hasattr(view, 'action'): + # Viewsets have an explicitly defined action, which we can inspect. + return view.action == 'list' + + if method.lower() != 'get': + return False + if isinstance(view, RetrieveModelMixin): + return False + path_components = path.strip('/').split('/') + if path_components and '{' in path_components[-1]: + return False + return True + + +def get_pk_description(model, model_field): + if isinstance(model_field, models.AutoField): + value_type = _('unique integer value') + elif isinstance(model_field, models.UUIDField): + value_type = _('UUID string') + else: + value_type = _('unique value') + + return _('A {value_type} identifying this {name}.').format( + value_type=value_type, + name=model._meta.verbose_name, + ) diff --git a/venv/Lib/site-packages/rest_framework/schemas/views.py b/venv/Lib/site-packages/rest_framework/schemas/views.py new file mode 100644 index 0000000..527a232 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/schemas/views.py @@ -0,0 +1,48 @@ +""" +views.py # Houses `SchemaView`, `APIView` subclass. + +See schemas.__init__.py for package overview. +""" +from rest_framework import exceptions, renderers +from rest_framework.response import Response +from rest_framework.schemas import coreapi +from rest_framework.settings import api_settings +from rest_framework.views import APIView + + +class SchemaView(APIView): + _ignore_model_permissions = True + schema = None # exclude from schema + renderer_classes = None + schema_generator = None + public = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.renderer_classes is None: + if coreapi.is_enabled(): + self.renderer_classes = [ + renderers.CoreAPIOpenAPIRenderer, + renderers.CoreJSONRenderer + ] + else: + self.renderer_classes = [ + renderers.OpenAPIRenderer, + renderers.JSONOpenAPIRenderer, + ] + if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: + self.renderer_classes += [renderers.BrowsableAPIRenderer] + + def get(self, request, *args, **kwargs): + schema = self.schema_generator.get_schema(request, self.public) + if schema is None: + raise exceptions.PermissionDenied() + return Response(schema) + + def handle_exception(self, exc): + # Schema renderers do not render exceptions, so re-perform content + # negotiation with default renderers. + self.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + neg = self.perform_content_negotiation(self.request, force=True) + self.request.accepted_renderer, self.request.accepted_media_type = neg + return super().handle_exception(exc) diff --git a/venv/Lib/site-packages/rest_framework/serializers.py b/venv/Lib/site-packages/rest_framework/serializers.py new file mode 100644 index 0000000..3896805 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/serializers.py @@ -0,0 +1,1666 @@ +""" +Serializers and ModelSerializers are similar to Forms and ModelForms. +Unlike forms, they are not constrained to dealing with HTML output, and +form encoded input. + +Serialization in REST framework is a two-phase process: + +1. Serializers marshal between complex types like model instances, and +python primitives. +2. The process of marshalling between python primitives and request and +response content is handled by parsers and renderers. +""" +import copy +import inspect +import traceback +from collections import OrderedDict, defaultdict +from collections.abc import Mapping + +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured +from django.core.exceptions import ValidationError as DjangoValidationError +from django.db import models +from django.db.models.fields import Field as DjangoModelField +from django.utils import timezone +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ + +from rest_framework.compat import postgres_fields +from rest_framework.exceptions import ErrorDetail, ValidationError +from rest_framework.fields import get_error_detail, set_value +from rest_framework.settings import api_settings +from rest_framework.utils import html, model_meta, representation +from rest_framework.utils.field_mapping import ( + ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs, + get_relation_kwargs, get_url_kwargs +) +from rest_framework.utils.serializer_helpers import ( + BindingDict, BoundField, JSONBoundField, NestedBoundField, ReturnDict, + ReturnList +) +from rest_framework.validators import ( + UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator, + UniqueTogetherValidator +) + +# Note: We do the following so that users of the framework can use this style: +# +# example_field = serializers.CharField(...) +# +# This helps keep the separation between model fields, form fields, and +# serializer fields more explicit. +from rest_framework.fields import ( # NOQA # isort:skip + BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, + DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, + HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField, + ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, + RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField, +) +from rest_framework.relations import ( # NOQA # isort:skip + HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField, + PrimaryKeyRelatedField, RelatedField, SlugRelatedField, StringRelatedField, +) + +# Non-field imports, but public API +from rest_framework.fields import ( # NOQA # isort:skip + CreateOnlyDefault, CurrentUserDefault, SkipField, empty +) +from rest_framework.relations import Hyperlink, PKOnlyObject # NOQA # isort:skip + +# We assume that 'validators' are intended for the child serializer, +# rather than the parent serializer. +LIST_SERIALIZER_KWARGS = ( + 'read_only', 'write_only', 'required', 'default', 'initial', 'source', + 'label', 'help_text', 'style', 'error_messages', 'allow_empty', + 'instance', 'data', 'partial', 'context', 'allow_null', + 'max_length', 'min_length' +) + +ALL_FIELDS = '__all__' + + +# BaseSerializer +# -------------- + +class BaseSerializer(Field): + """ + The BaseSerializer class provides a minimal class which may be used + for writing custom serializer implementations. + + Note that we strongly restrict the ordering of operations/properties + that may be used on the serializer in order to enforce correct usage. + + In particular, if a `data=` argument is passed then: + + .is_valid() - Available. + .initial_data - Available. + .validated_data - Only available after calling `is_valid()` + .errors - Only available after calling `is_valid()` + .data - Only available after calling `is_valid()` + + If a `data=` argument is not passed then: + + .is_valid() - Not available. + .initial_data - Not available. + .validated_data - Not available. + .errors - Not available. + .data - Available. + """ + + def __init__(self, instance=None, data=empty, **kwargs): + self.instance = instance + if data is not empty: + self.initial_data = data + self.partial = kwargs.pop('partial', False) + self._context = kwargs.pop('context', {}) + kwargs.pop('many', None) + super().__init__(**kwargs) + + def __new__(cls, *args, **kwargs): + # We override this method in order to automatically create + # `ListSerializer` classes instead when `many=True` is set. + if kwargs.pop('many', False): + return cls.many_init(*args, **kwargs) + return super().__new__(cls, *args, **kwargs) + + # Allow type checkers to make serializers generic. + def __class_getitem__(cls, *args, **kwargs): + return cls + + @classmethod + def many_init(cls, *args, **kwargs): + """ + This method implements the creation of a `ListSerializer` parent + class when `many=True` is used. You can customize it if you need to + control which keyword arguments are passed to the parent, and + which are passed to the child. + + Note that we're over-cautious in passing most arguments to both parent + and child classes in order to try to cover the general case. If you're + overriding this method you'll probably want something much simpler, eg: + + @classmethod + def many_init(cls, *args, **kwargs): + kwargs['child'] = cls() + return CustomListSerializer(*args, **kwargs) + """ + allow_empty = kwargs.pop('allow_empty', None) + max_length = kwargs.pop('max_length', None) + min_length = kwargs.pop('min_length', None) + child_serializer = cls(*args, **kwargs) + list_kwargs = { + 'child': child_serializer, + } + if allow_empty is not None: + list_kwargs['allow_empty'] = allow_empty + if max_length is not None: + list_kwargs['max_length'] = max_length + if min_length is not None: + list_kwargs['min_length'] = min_length + list_kwargs.update({ + key: value for key, value in kwargs.items() + if key in LIST_SERIALIZER_KWARGS + }) + meta = getattr(cls, 'Meta', None) + list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer) + return list_serializer_class(*args, **list_kwargs) + + def to_internal_value(self, data): + raise NotImplementedError('`to_internal_value()` must be implemented.') + + def to_representation(self, instance): + raise NotImplementedError('`to_representation()` must be implemented.') + + def update(self, instance, validated_data): + raise NotImplementedError('`update()` must be implemented.') + + def create(self, validated_data): + raise NotImplementedError('`create()` must be implemented.') + + def save(self, **kwargs): + assert hasattr(self, '_errors'), ( + 'You must call `.is_valid()` before calling `.save()`.' + ) + + assert not self.errors, ( + 'You cannot call `.save()` on a serializer with invalid data.' + ) + + # Guard against incorrect use of `serializer.save(commit=False)` + assert 'commit' not in kwargs, ( + "'commit' is not a valid keyword argument to the 'save()' method. " + "If you need to access data before committing to the database then " + "inspect 'serializer.validated_data' instead. " + "You can also pass additional keyword arguments to 'save()' if you " + "need to set extra attributes on the saved model instance. " + "For example: 'serializer.save(owner=request.user)'.'" + ) + + assert not hasattr(self, '_data'), ( + "You cannot call `.save()` after accessing `serializer.data`." + "If you need to access data before committing to the database then " + "inspect 'serializer.validated_data' instead. " + ) + + validated_data = {**self.validated_data, **kwargs} + + if self.instance is not None: + self.instance = self.update(self.instance, validated_data) + assert self.instance is not None, ( + '`update()` did not return an object instance.' + ) + else: + self.instance = self.create(validated_data) + assert self.instance is not None, ( + '`create()` did not return an object instance.' + ) + + return self.instance + + def is_valid(self, raise_exception=False): + assert hasattr(self, 'initial_data'), ( + 'Cannot call `.is_valid()` as no `data=` keyword argument was ' + 'passed when instantiating the serializer instance.' + ) + + if not hasattr(self, '_validated_data'): + try: + self._validated_data = self.run_validation(self.initial_data) + except ValidationError as exc: + self._validated_data = {} + self._errors = exc.detail + else: + self._errors = {} + + if self._errors and raise_exception: + raise ValidationError(self.errors) + + return not bool(self._errors) + + @property + def data(self): + if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): + msg = ( + 'When a serializer is passed a `data` keyword argument you ' + 'must call `.is_valid()` before attempting to access the ' + 'serialized `.data` representation.\n' + 'You should either call `.is_valid()` first, ' + 'or access `.initial_data` instead.' + ) + raise AssertionError(msg) + + if not hasattr(self, '_data'): + if self.instance is not None and not getattr(self, '_errors', None): + self._data = self.to_representation(self.instance) + elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): + self._data = self.to_representation(self.validated_data) + else: + self._data = self.get_initial() + return self._data + + @property + def errors(self): + if not hasattr(self, '_errors'): + msg = 'You must call `.is_valid()` before accessing `.errors`.' + raise AssertionError(msg) + return self._errors + + @property + def validated_data(self): + if not hasattr(self, '_validated_data'): + msg = 'You must call `.is_valid()` before accessing `.validated_data`.' + raise AssertionError(msg) + return self._validated_data + + +# Serializer & ListSerializer classes +# ----------------------------------- + +class SerializerMetaclass(type): + """ + This metaclass sets a dictionary named `_declared_fields` on the class. + + Any instances of `Field` included as attributes on either the class + or on any of its superclasses will be include in the + `_declared_fields` dictionary. + """ + + @classmethod + def _get_declared_fields(cls, bases, attrs): + fields = [(field_name, attrs.pop(field_name)) + for field_name, obj in list(attrs.items()) + if isinstance(obj, Field)] + fields.sort(key=lambda x: x[1]._creation_counter) + + # Ensures a base class field doesn't override cls attrs, and maintains + # field precedence when inheriting multiple parents. e.g. if there is a + # class C(A, B), and A and B both define 'field', use 'field' from A. + known = set(attrs) + + def visit(name): + known.add(name) + return name + + base_fields = [ + (visit(name), f) + for base in bases if hasattr(base, '_declared_fields') + for name, f in base._declared_fields.items() if name not in known + ] + + return OrderedDict(base_fields + fields) + + def __new__(cls, name, bases, attrs): + attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) + return super().__new__(cls, name, bases, attrs) + + +def as_serializer_error(exc): + assert isinstance(exc, (ValidationError, DjangoValidationError)) + + if isinstance(exc, DjangoValidationError): + detail = get_error_detail(exc) + else: + detail = exc.detail + + if isinstance(detail, Mapping): + # If errors may be a dict we use the standard {key: list of values}. + # Here we ensure that all the values are *lists* of errors. + return { + key: value if isinstance(value, (list, Mapping)) else [value] + for key, value in detail.items() + } + elif isinstance(detail, list): + # Errors raised as a list are non-field errors. + return { + api_settings.NON_FIELD_ERRORS_KEY: detail + } + # Errors raised as a string are non-field errors. + return { + api_settings.NON_FIELD_ERRORS_KEY: [detail] + } + + +class Serializer(BaseSerializer, metaclass=SerializerMetaclass): + default_error_messages = { + 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') + } + + @cached_property + def fields(self): + """ + A dictionary of {field_name: field_instance}. + """ + # `fields` is evaluated lazily. We do this to ensure that we don't + # have issues importing modules that use ModelSerializers as fields, + # even if Django's app-loading stage has not yet run. + fields = BindingDict(self) + for key, value in self.get_fields().items(): + fields[key] = value + return fields + + @property + def _writable_fields(self): + for field in self.fields.values(): + if not field.read_only: + yield field + + @property + def _readable_fields(self): + for field in self.fields.values(): + if not field.write_only: + yield field + + def get_fields(self): + """ + Returns a dictionary of {field_name: field_instance}. + """ + # Every new serializer is created with a clone of the field instances. + # This allows users to dynamically modify the fields on a serializer + # instance without affecting every other serializer instance. + return copy.deepcopy(self._declared_fields) + + def get_validators(self): + """ + Returns a list of validator callables. + """ + # Used by the lazily-evaluated `validators` property. + meta = getattr(self, 'Meta', None) + validators = getattr(meta, 'validators', None) + return list(validators) if validators else [] + + def get_initial(self): + if hasattr(self, 'initial_data'): + # initial_data may not be a valid type + if not isinstance(self.initial_data, Mapping): + return OrderedDict() + + return OrderedDict([ + (field_name, field.get_value(self.initial_data)) + for field_name, field in self.fields.items() + if (field.get_value(self.initial_data) is not empty) and + not field.read_only + ]) + + return OrderedDict([ + (field.field_name, field.get_initial()) + for field in self.fields.values() + if not field.read_only + ]) + + def get_value(self, dictionary): + # We override the default field access in order to support + # nested HTML forms. + if html.is_html_input(dictionary): + return html.parse_html_dict(dictionary, prefix=self.field_name) or empty + return dictionary.get(self.field_name, empty) + + def run_validation(self, data=empty): + """ + We override the default `run_validation`, because the validation + performed by validators and the `.validate()` method should + be coerced into an error dictionary with a 'non_fields_error' key. + """ + (is_empty_value, data) = self.validate_empty_values(data) + if is_empty_value: + return data + + value = self.to_internal_value(data) + try: + self.run_validators(value) + value = self.validate(value) + assert value is not None, '.validate() should return the validated data' + except (ValidationError, DjangoValidationError) as exc: + raise ValidationError(detail=as_serializer_error(exc)) + + return value + + def _read_only_defaults(self): + fields = [ + field for field in self.fields.values() + if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) + ] + + defaults = OrderedDict() + for field in fields: + try: + default = field.get_default() + except SkipField: + continue + defaults[field.source] = default + + return defaults + + def run_validators(self, value): + """ + Add read_only fields with defaults to value before running validators. + """ + if isinstance(value, dict): + to_validate = self._read_only_defaults() + to_validate.update(value) + else: + to_validate = value + super().run_validators(to_validate) + + def to_internal_value(self, data): + """ + Dict of native values <- Dict of primitive datatypes. + """ + if not isinstance(data, Mapping): + message = self.error_messages['invalid'].format( + datatype=type(data).__name__ + ) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='invalid') + + ret = OrderedDict() + errors = OrderedDict() + fields = self._writable_fields + + for field in fields: + validate_method = getattr(self, 'validate_' + field.field_name, None) + primitive_value = field.get_value(data) + try: + validated_value = field.run_validation(primitive_value) + if validate_method is not None: + validated_value = validate_method(validated_value) + except ValidationError as exc: + errors[field.field_name] = exc.detail + except DjangoValidationError as exc: + errors[field.field_name] = get_error_detail(exc) + except SkipField: + pass + else: + set_value(ret, field.source_attrs, validated_value) + + if errors: + raise ValidationError(errors) + + return ret + + def to_representation(self, instance): + """ + Object instance -> Dict of primitive datatypes. + """ + ret = OrderedDict() + fields = self._readable_fields + + for field in fields: + try: + attribute = field.get_attribute(instance) + except SkipField: + continue + + # We skip `to_representation` for `None` values so that fields do + # not have to explicitly deal with that case. + # + # For related fields with `use_pk_only_optimization` we need to + # resolve the pk value. + check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute + if check_for_none is None: + ret[field.field_name] = None + else: + ret[field.field_name] = field.to_representation(attribute) + + return ret + + def validate(self, attrs): + return attrs + + def __repr__(self): + return representation.serializer_repr(self, indent=1) + + # The following are used for accessing `BoundField` instances on the + # serializer, for the purposes of presenting a form-like API onto the + # field values and field errors. + + def __iter__(self): + for field in self.fields.values(): + yield self[field.field_name] + + def __getitem__(self, key): + field = self.fields[key] + value = self.data.get(key) + error = self.errors.get(key) if hasattr(self, '_errors') else None + if isinstance(field, Serializer): + return NestedBoundField(field, value, error) + if isinstance(field, JSONField): + return JSONBoundField(field, value, error) + return BoundField(field, value, error) + + # Include a backlink to the serializer class on return objects. + # Allows renderers such as HTMLFormRenderer to get the full field info. + + @property + def data(self): + ret = super().data + return ReturnDict(ret, serializer=self) + + @property + def errors(self): + ret = super().errors + if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null': + # Edge case. Provide a more descriptive error than + # "this field may not be null", when no data is passed. + detail = ErrorDetail('No data provided', code='null') + ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]} + return ReturnDict(ret, serializer=self) + + +# There's some replication of `ListField` here, +# but that's probably better than obfuscating the call hierarchy. + +class ListSerializer(BaseSerializer): + child = None + many = True + + default_error_messages = { + 'not_a_list': _('Expected a list of items but got type "{input_type}".'), + 'empty': _('This list may not be empty.'), + 'max_length': _('Ensure this field has no more than {max_length} elements.'), + 'min_length': _('Ensure this field has at least {min_length} elements.') + } + + def __init__(self, *args, **kwargs): + self.child = kwargs.pop('child', copy.deepcopy(self.child)) + self.allow_empty = kwargs.pop('allow_empty', True) + self.max_length = kwargs.pop('max_length', None) + self.min_length = kwargs.pop('min_length', None) + assert self.child is not None, '`child` is a required argument.' + assert not inspect.isclass(self.child), '`child` has not been instantiated.' + super().__init__(*args, **kwargs) + self.child.bind(field_name='', parent=self) + + def get_initial(self): + if hasattr(self, 'initial_data'): + return self.to_representation(self.initial_data) + return [] + + def get_value(self, dictionary): + """ + Given the input dictionary, return the field value. + """ + # We override the default field access in order to support + # lists in HTML forms. + if html.is_html_input(dictionary): + return html.parse_html_list(dictionary, prefix=self.field_name, default=empty) + return dictionary.get(self.field_name, empty) + + def run_validation(self, data=empty): + """ + We override the default `run_validation`, because the validation + performed by validators and the `.validate()` method should + be coerced into an error dictionary with a 'non_fields_error' key. + """ + (is_empty_value, data) = self.validate_empty_values(data) + if is_empty_value: + return data + + value = self.to_internal_value(data) + try: + self.run_validators(value) + value = self.validate(value) + assert value is not None, '.validate() should return the validated data' + except (ValidationError, DjangoValidationError) as exc: + raise ValidationError(detail=as_serializer_error(exc)) + + return value + + def to_internal_value(self, data): + """ + List of dicts of native values <- List of dicts of primitive datatypes. + """ + if html.is_html_input(data): + data = html.parse_html_list(data, default=[]) + + if not isinstance(data, list): + message = self.error_messages['not_a_list'].format( + input_type=type(data).__name__ + ) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='not_a_list') + + if not self.allow_empty and len(data) == 0: + message = self.error_messages['empty'] + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='empty') + + if self.max_length is not None and len(data) > self.max_length: + message = self.error_messages['max_length'].format(max_length=self.max_length) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='max_length') + + if self.min_length is not None and len(data) < self.min_length: + message = self.error_messages['min_length'].format(min_length=self.min_length) + raise ValidationError({ + api_settings.NON_FIELD_ERRORS_KEY: [message] + }, code='min_length') + + ret = [] + errors = [] + + for item in data: + try: + validated = self.child.run_validation(item) + except ValidationError as exc: + errors.append(exc.detail) + else: + ret.append(validated) + errors.append({}) + + if any(errors): + raise ValidationError(errors) + + return ret + + def to_representation(self, data): + """ + List of object instances -> List of dicts of primitive datatypes. + """ + # Dealing with nested relationships, data can be a Manager, + # so, first get a queryset from the Manager if needed + iterable = data.all() if isinstance(data, models.Manager) else data + + return [ + self.child.to_representation(item) for item in iterable + ] + + def validate(self, attrs): + return attrs + + def update(self, instance, validated_data): + raise NotImplementedError( + "Serializers with many=True do not support multiple update by " + "default, only multiple create. For updates it is unclear how to " + "deal with insertions and deletions. If you need to support " + "multiple update, use a `ListSerializer` class and override " + "`.update()` so you can specify the behavior exactly." + ) + + def create(self, validated_data): + return [ + self.child.create(attrs) for attrs in validated_data + ] + + def save(self, **kwargs): + """ + Save and return a list of object instances. + """ + # Guard against incorrect use of `serializer.save(commit=False)` + assert 'commit' not in kwargs, ( + "'commit' is not a valid keyword argument to the 'save()' method. " + "If you need to access data before committing to the database then " + "inspect 'serializer.validated_data' instead. " + "You can also pass additional keyword arguments to 'save()' if you " + "need to set extra attributes on the saved model instance. " + "For example: 'serializer.save(owner=request.user)'.'" + ) + + validated_data = [ + {**attrs, **kwargs} for attrs in self.validated_data + ] + + if self.instance is not None: + self.instance = self.update(self.instance, validated_data) + assert self.instance is not None, ( + '`update()` did not return an object instance.' + ) + else: + self.instance = self.create(validated_data) + assert self.instance is not None, ( + '`create()` did not return an object instance.' + ) + + return self.instance + + def is_valid(self, raise_exception=False): + # This implementation is the same as the default, + # except that we use lists, rather than dicts, as the empty case. + assert hasattr(self, 'initial_data'), ( + 'Cannot call `.is_valid()` as no `data=` keyword argument was ' + 'passed when instantiating the serializer instance.' + ) + + if not hasattr(self, '_validated_data'): + try: + self._validated_data = self.run_validation(self.initial_data) + except ValidationError as exc: + self._validated_data = [] + self._errors = exc.detail + else: + self._errors = [] + + if self._errors and raise_exception: + raise ValidationError(self.errors) + + return not bool(self._errors) + + def __repr__(self): + return representation.list_repr(self, indent=1) + + # Include a backlink to the serializer class on return objects. + # Allows renderers such as HTMLFormRenderer to get the full field info. + + @property + def data(self): + ret = super().data + return ReturnList(ret, serializer=self) + + @property + def errors(self): + ret = super().errors + if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null': + # Edge case. Provide a more descriptive error than + # "this field may not be null", when no data is passed. + detail = ErrorDetail('No data provided', code='null') + ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]} + if isinstance(ret, dict): + return ReturnDict(ret, serializer=self) + return ReturnList(ret, serializer=self) + + +# ModelSerializer & HyperlinkedModelSerializer +# -------------------------------------------- + +def raise_errors_on_nested_writes(method_name, serializer, validated_data): + """ + Give explicit errors when users attempt to pass writable nested data. + + If we don't do this explicitly they'd get a less helpful error when + calling `.save()` on the serializer. + + We don't *automatically* support these sorts of nested writes because + there are too many ambiguities to define a default behavior. + + Eg. Suppose we have a `UserSerializer` with a nested profile. How should + we handle the case of an update, where the `profile` relationship does + not exist? Any of the following might be valid: + + * Raise an application error. + * Silently ignore the nested part of the update. + * Automatically create a profile instance. + """ + ModelClass = serializer.Meta.model + model_field_info = model_meta.get_field_info(ModelClass) + + # Ensure we don't have a writable nested field. For example: + # + # class UserSerializer(ModelSerializer): + # ... + # profile = ProfileSerializer() + assert not any( + isinstance(field, BaseSerializer) and + (field.source in validated_data) and + (field.source in model_field_info.relations) and + isinstance(validated_data[field.source], (list, dict)) + for field in serializer._writable_fields + ), ( + 'The `.{method_name}()` method does not support writable nested ' + 'fields by default.\nWrite an explicit `.{method_name}()` method for ' + 'serializer `{module}.{class_name}`, or set `read_only=True` on ' + 'nested serializer fields.'.format( + method_name=method_name, + module=serializer.__class__.__module__, + class_name=serializer.__class__.__name__ + ) + ) + + # Ensure we don't have a writable dotted-source field. For example: + # + # class UserSerializer(ModelSerializer): + # ... + # address = serializer.CharField('profile.address') + # + # Though, non-relational fields (e.g., JSONField) are acceptable. For example: + # + # class NonRelationalPersonModel(models.Model): + # profile = JSONField() + # + # class UserSerializer(ModelSerializer): + # ... + # address = serializer.CharField('profile.address') + assert not any( + len(field.source_attrs) > 1 and + (field.source_attrs[0] in validated_data) and + (field.source_attrs[0] in model_field_info.relations) and + isinstance(validated_data[field.source_attrs[0]], (list, dict)) + for field in serializer._writable_fields + ), ( + 'The `.{method_name}()` method does not support writable dotted-source ' + 'fields by default.\nWrite an explicit `.{method_name}()` method for ' + 'serializer `{module}.{class_name}`, or set `read_only=True` on ' + 'dotted-source serializer fields.'.format( + method_name=method_name, + module=serializer.__class__.__module__, + class_name=serializer.__class__.__name__ + ) + ) + + +class ModelSerializer(Serializer): + """ + A `ModelSerializer` is just a regular `Serializer`, except that: + + * A set of default fields are automatically populated. + * A set of default validators are automatically populated. + * Default `.create()` and `.update()` implementations are provided. + + The process of automatically determining a set of serializer fields + based on the model fields is reasonably complex, but you almost certainly + don't need to dig into the implementation. + + If the `ModelSerializer` class *doesn't* generate the set of fields that + you need you should either declare the extra/differing fields explicitly on + the serializer class, or simply use a `Serializer` class. + """ + serializer_field_mapping = { + models.AutoField: IntegerField, + models.BigIntegerField: IntegerField, + models.BooleanField: BooleanField, + models.CharField: CharField, + models.CommaSeparatedIntegerField: CharField, + models.DateField: DateField, + models.DateTimeField: DateTimeField, + models.DecimalField: DecimalField, + models.DurationField: DurationField, + models.EmailField: EmailField, + models.Field: ModelField, + models.FileField: FileField, + models.FloatField: FloatField, + models.ImageField: ImageField, + models.IntegerField: IntegerField, + models.NullBooleanField: BooleanField, + models.PositiveIntegerField: IntegerField, + models.PositiveSmallIntegerField: IntegerField, + models.SlugField: SlugField, + models.SmallIntegerField: IntegerField, + models.TextField: CharField, + models.TimeField: TimeField, + models.URLField: URLField, + models.UUIDField: UUIDField, + models.GenericIPAddressField: IPAddressField, + models.FilePathField: FilePathField, + } + if hasattr(models, 'JSONField'): + serializer_field_mapping[models.JSONField] = JSONField + if postgres_fields: + serializer_field_mapping[postgres_fields.HStoreField] = HStoreField + serializer_field_mapping[postgres_fields.ArrayField] = ListField + serializer_field_mapping[postgres_fields.JSONField] = JSONField + serializer_related_field = PrimaryKeyRelatedField + serializer_related_to_field = SlugRelatedField + serializer_url_field = HyperlinkedIdentityField + serializer_choice_field = ChoiceField + + # The field name for hyperlinked identity fields. Defaults to 'url'. + # You can modify this using the API setting. + # + # Note that if you instead need modify this on a per-serializer basis, + # you'll also need to ensure you update the `create` method on any generic + # views, to correctly handle the 'Location' response header for + # "HTTP 201 Created" responses. + url_field_name = None + + # Default `create` and `update` behavior... + def create(self, validated_data): + """ + We have a bit of extra checking around this in order to provide + descriptive messages when something goes wrong, but this method is + essentially just: + + return ExampleModel.objects.create(**validated_data) + + If there are many to many fields present on the instance then they + cannot be set until the model is instantiated, in which case the + implementation is like so: + + example_relationship = validated_data.pop('example_relationship') + instance = ExampleModel.objects.create(**validated_data) + instance.example_relationship = example_relationship + return instance + + The default implementation also does not handle nested relationships. + If you want to support writable nested relationships you'll need + to write an explicit `.create()` method. + """ + raise_errors_on_nested_writes('create', self, validated_data) + + ModelClass = self.Meta.model + + # Remove many-to-many relationships from validated_data. + # They are not valid arguments to the default `.create()` method, + # as they require that the instance has already been saved. + info = model_meta.get_field_info(ModelClass) + many_to_many = {} + for field_name, relation_info in info.relations.items(): + if relation_info.to_many and (field_name in validated_data): + many_to_many[field_name] = validated_data.pop(field_name) + + try: + instance = ModelClass._default_manager.create(**validated_data) + except TypeError: + tb = traceback.format_exc() + msg = ( + 'Got a `TypeError` when calling `%s.%s.create()`. ' + 'This may be because you have a writable field on the ' + 'serializer class that is not a valid argument to ' + '`%s.%s.create()`. You may need to make the field ' + 'read-only, or override the %s.create() method to handle ' + 'this correctly.\nOriginal exception was:\n %s' % + ( + ModelClass.__name__, + ModelClass._default_manager.name, + ModelClass.__name__, + ModelClass._default_manager.name, + self.__class__.__name__, + tb + ) + ) + raise TypeError(msg) + + # Save many-to-many relationships after the instance is created. + if many_to_many: + for field_name, value in many_to_many.items(): + field = getattr(instance, field_name) + field.set(value) + + return instance + + def update(self, instance, validated_data): + raise_errors_on_nested_writes('update', self, validated_data) + info = model_meta.get_field_info(instance) + + # Simply set each attribute on the instance, and then save it. + # Note that unlike `.create()` we don't need to treat many-to-many + # relationships as being a special case. During updates we already + # have an instance pk for the relationships to be associated with. + m2m_fields = [] + for attr, value in validated_data.items(): + if attr in info.relations and info.relations[attr].to_many: + m2m_fields.append((attr, value)) + else: + setattr(instance, attr, value) + + instance.save() + + # Note that many-to-many fields are set after updating instance. + # Setting m2m fields triggers signals which could potentially change + # updated instance and we do not want it to collide with .update() + for attr, value in m2m_fields: + field = getattr(instance, attr) + field.set(value) + + return instance + + # Determine the fields to apply... + + def get_fields(self): + """ + Return the dict of field names -> field instances that should be + used for `self.fields` when instantiating the serializer. + """ + if self.url_field_name is None: + self.url_field_name = api_settings.URL_FIELD_NAME + + assert hasattr(self, 'Meta'), ( + 'Class {serializer_class} missing "Meta" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + assert hasattr(self.Meta, 'model'), ( + 'Class {serializer_class} missing "Meta.model" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + if model_meta.is_abstract_model(self.Meta.model): + raise ValueError( + 'Cannot use ModelSerializer with Abstract Models.' + ) + + declared_fields = copy.deepcopy(self._declared_fields) + model = getattr(self.Meta, 'model') + depth = getattr(self.Meta, 'depth', 0) + + if depth is not None: + assert depth >= 0, "'depth' may not be negative." + assert depth <= 10, "'depth' may not be greater than 10." + + # Retrieve metadata about fields & relationships on the model class. + info = model_meta.get_field_info(model) + field_names = self.get_field_names(declared_fields, info) + + # Determine any extra field arguments and hidden fields that + # should be included + extra_kwargs = self.get_extra_kwargs() + extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs( + field_names, declared_fields, extra_kwargs + ) + + # Determine the fields that should be included on the serializer. + fields = OrderedDict() + + for field_name in field_names: + # If the field is explicitly declared on the class then use that. + if field_name in declared_fields: + fields[field_name] = declared_fields[field_name] + continue + + extra_field_kwargs = extra_kwargs.get(field_name, {}) + source = extra_field_kwargs.get('source', '*') + if source == '*': + source = field_name + + # Determine the serializer field class and keyword arguments. + field_class, field_kwargs = self.build_field( + source, info, model, depth + ) + + # Include any kwargs defined in `Meta.extra_kwargs` + field_kwargs = self.include_extra_kwargs( + field_kwargs, extra_field_kwargs + ) + + # Create the serializer field. + fields[field_name] = field_class(**field_kwargs) + + # Add in any hidden fields. + fields.update(hidden_fields) + + return fields + + # Methods for determining the set of field names to include... + + def get_field_names(self, declared_fields, info): + """ + Returns the list of all field names that should be created when + instantiating this serializer class. This is based on the default + set of fields, but also takes into account the `Meta.fields` or + `Meta.exclude` options if they have been specified. + """ + fields = getattr(self.Meta, 'fields', None) + exclude = getattr(self.Meta, 'exclude', None) + + if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)): + raise TypeError( + 'The `fields` option must be a list or tuple or "__all__". ' + 'Got %s.' % type(fields).__name__ + ) + + if exclude and not isinstance(exclude, (list, tuple)): + raise TypeError( + 'The `exclude` option must be a list or tuple. Got %s.' % + type(exclude).__name__ + ) + + assert not (fields and exclude), ( + "Cannot set both 'fields' and 'exclude' options on " + "serializer {serializer_class}.".format( + serializer_class=self.__class__.__name__ + ) + ) + + assert not (fields is None and exclude is None), ( + "Creating a ModelSerializer without either the 'fields' attribute " + "or the 'exclude' attribute has been deprecated since 3.3.0, " + "and is now disallowed. Add an explicit fields = '__all__' to the " + "{serializer_class} serializer.".format( + serializer_class=self.__class__.__name__ + ), + ) + + if fields == ALL_FIELDS: + fields = None + + if fields is not None: + # Ensure that all declared fields have also been included in the + # `Meta.fields` option. + + # Do not require any fields that are declared in a parent class, + # in order to allow serializer subclasses to only include + # a subset of fields. + required_field_names = set(declared_fields) + for cls in self.__class__.__bases__: + required_field_names -= set(getattr(cls, '_declared_fields', [])) + + for field_name in required_field_names: + assert field_name in fields, ( + "The field '{field_name}' was declared on serializer " + "{serializer_class}, but has not been included in the " + "'fields' option.".format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + return fields + + # Use the default set of field names if `Meta.fields` is not specified. + fields = self.get_default_field_names(declared_fields, info) + + if exclude is not None: + # If `Meta.exclude` is included, then remove those fields. + for field_name in exclude: + assert field_name not in self._declared_fields, ( + "Cannot both declare the field '{field_name}' and include " + "it in the {serializer_class} 'exclude' option. Remove the " + "field or, if inherited from a parent serializer, disable " + "with `{field_name} = None`." + .format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + + assert field_name in fields, ( + "The field '{field_name}' was included on serializer " + "{serializer_class} in the 'exclude' option, but does " + "not match any model field.".format( + field_name=field_name, + serializer_class=self.__class__.__name__ + ) + ) + fields.remove(field_name) + + return fields + + def get_default_field_names(self, declared_fields, model_info): + """ + Return the default list of field names that will be used if the + `Meta.fields` option is not specified. + """ + return ( + [model_info.pk.name] + + list(declared_fields) + + list(model_info.fields) + + list(model_info.forward_relations) + ) + + # Methods for constructing serializer fields... + + def build_field(self, field_name, info, model_class, nested_depth): + """ + Return a two tuple of (cls, kwargs) to build a serializer field with. + """ + if field_name in info.fields_and_pk: + model_field = info.fields_and_pk[field_name] + return self.build_standard_field(field_name, model_field) + + elif field_name in info.relations: + relation_info = info.relations[field_name] + if not nested_depth: + return self.build_relational_field(field_name, relation_info) + else: + return self.build_nested_field(field_name, relation_info, nested_depth) + + elif hasattr(model_class, field_name): + return self.build_property_field(field_name, model_class) + + elif field_name == self.url_field_name: + return self.build_url_field(field_name, model_class) + + return self.build_unknown_field(field_name, model_class) + + def build_standard_field(self, field_name, model_field): + """ + Create regular model fields. + """ + field_mapping = ClassLookupDict(self.serializer_field_mapping) + + field_class = field_mapping[model_field] + field_kwargs = get_field_kwargs(field_name, model_field) + + # Special case to handle when a OneToOneField is also the primary key + if model_field.one_to_one and model_field.primary_key: + field_class = self.serializer_related_field + field_kwargs['queryset'] = model_field.related_model.objects + + if 'choices' in field_kwargs: + # Fields with choices get coerced into `ChoiceField` + # instead of using their regular typed field. + field_class = self.serializer_choice_field + # Some model fields may introduce kwargs that would not be valid + # for the choice field. We need to strip these out. + # Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES) + valid_kwargs = { + 'read_only', 'write_only', + 'required', 'default', 'initial', 'source', + 'label', 'help_text', 'style', + 'error_messages', 'validators', 'allow_null', 'allow_blank', + 'choices' + } + for key in list(field_kwargs): + if key not in valid_kwargs: + field_kwargs.pop(key) + + if not issubclass(field_class, ModelField): + # `model_field` is only valid for the fallback case of + # `ModelField`, which is used when no other typed field + # matched to the model field. + field_kwargs.pop('model_field', None) + + if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField): + # `allow_blank` is only valid for textual fields. + field_kwargs.pop('allow_blank', None) + + is_django_jsonfield = hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField) + if (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or is_django_jsonfield: + # Populate the `encoder` argument of `JSONField` instances generated + # for the model `JSONField`. + field_kwargs['encoder'] = getattr(model_field, 'encoder', None) + if is_django_jsonfield: + field_kwargs['decoder'] = getattr(model_field, 'decoder', None) + + if postgres_fields and isinstance(model_field, postgres_fields.ArrayField): + # Populate the `child` argument on `ListField` instances generated + # for the PostgreSQL specific `ArrayField`. + child_model_field = model_field.base_field + child_field_class, child_field_kwargs = self.build_standard_field( + 'child', child_model_field + ) + field_kwargs['child'] = child_field_class(**child_field_kwargs) + + return field_class, field_kwargs + + def build_relational_field(self, field_name, relation_info): + """ + Create fields for forward and reverse relationships. + """ + field_class = self.serializer_related_field + field_kwargs = get_relation_kwargs(field_name, relation_info) + + to_field = field_kwargs.pop('to_field', None) + if to_field and not relation_info.reverse and not relation_info.related_model._meta.get_field(to_field).primary_key: + field_kwargs['slug_field'] = to_field + field_class = self.serializer_related_to_field + + # `view_name` is only valid for hyperlinked relationships. + if not issubclass(field_class, HyperlinkedRelatedField): + field_kwargs.pop('view_name', None) + + return field_class, field_kwargs + + def build_nested_field(self, field_name, relation_info, nested_depth): + """ + Create nested fields for forward and reverse relationships. + """ + class NestedSerializer(ModelSerializer): + class Meta: + model = relation_info.related_model + depth = nested_depth - 1 + fields = '__all__' + + field_class = NestedSerializer + field_kwargs = get_nested_relation_kwargs(relation_info) + + return field_class, field_kwargs + + def build_property_field(self, field_name, model_class): + """ + Create a read only field for model methods and properties. + """ + field_class = ReadOnlyField + field_kwargs = {} + + return field_class, field_kwargs + + def build_url_field(self, field_name, model_class): + """ + Create a field representing the object's own URL. + """ + field_class = self.serializer_url_field + field_kwargs = get_url_kwargs(model_class) + + return field_class, field_kwargs + + def build_unknown_field(self, field_name, model_class): + """ + Raise an error on any unknown fields. + """ + raise ImproperlyConfigured( + 'Field name `%s` is not valid for model `%s`.' % + (field_name, model_class.__name__) + ) + + def include_extra_kwargs(self, kwargs, extra_kwargs): + """ + Include any 'extra_kwargs' that have been included for this field, + possibly removing any incompatible existing keyword arguments. + """ + if extra_kwargs.get('read_only', False): + for attr in [ + 'required', 'default', 'allow_blank', 'min_length', + 'max_length', 'min_value', 'max_value', 'validators', 'queryset' + ]: + kwargs.pop(attr, None) + + if extra_kwargs.get('default') and kwargs.get('required') is False: + kwargs.pop('required') + + if extra_kwargs.get('read_only', kwargs.get('read_only', False)): + extra_kwargs.pop('required', None) # Read only fields should always omit the 'required' argument. + + kwargs.update(extra_kwargs) + + return kwargs + + # Methods for determining additional keyword arguments to apply... + + def get_extra_kwargs(self): + """ + Return a dictionary mapping field names to a dictionary of + additional keyword arguments. + """ + extra_kwargs = copy.deepcopy(getattr(self.Meta, 'extra_kwargs', {})) + + read_only_fields = getattr(self.Meta, 'read_only_fields', None) + if read_only_fields is not None: + if not isinstance(read_only_fields, (list, tuple)): + raise TypeError( + 'The `read_only_fields` option must be a list or tuple. ' + 'Got %s.' % type(read_only_fields).__name__ + ) + for field_name in read_only_fields: + kwargs = extra_kwargs.get(field_name, {}) + kwargs['read_only'] = True + extra_kwargs[field_name] = kwargs + + else: + # Guard against the possible misspelling `readonly_fields` (used + # by the Django admin and others). + assert not hasattr(self.Meta, 'readonly_fields'), ( + 'Serializer `%s.%s` has field `readonly_fields`; ' + 'the correct spelling for the option is `read_only_fields`.' % + (self.__class__.__module__, self.__class__.__name__) + ) + + return extra_kwargs + + def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs): + """ + Return any additional field options that need to be included as a + result of uniqueness constraints on the model. This is returned as + a two-tuple of: + + ('dict of updated extra kwargs', 'mapping of hidden fields') + """ + if getattr(self.Meta, 'validators', None) is not None: + return (extra_kwargs, {}) + + model = getattr(self.Meta, 'model') + model_fields = self._get_model_fields( + field_names, declared_fields, extra_kwargs + ) + + # Determine if we need any additional `HiddenField` or extra keyword + # arguments to deal with `unique_for` dates that are required to + # be in the input data in order to validate it. + unique_constraint_names = set() + + for model_field in model_fields.values(): + # Include each of the `unique_for_*` field names. + unique_constraint_names |= {model_field.unique_for_date, model_field.unique_for_month, + model_field.unique_for_year} + + unique_constraint_names -= {None} + + # Include each of the `unique_together` field names, + # so long as all the field names are included on the serializer. + for parent_class in [model] + list(model._meta.parents): + for unique_together_list in parent_class._meta.unique_together: + if set(field_names).issuperset(unique_together_list): + unique_constraint_names |= set(unique_together_list) + + # Now we have all the field names that have uniqueness constraints + # applied, we can add the extra 'required=...' or 'default=...' + # arguments that are appropriate to these fields, or add a `HiddenField` for it. + hidden_fields = {} + uniqueness_extra_kwargs = {} + + for unique_constraint_name in unique_constraint_names: + # Get the model field that is referred too. + unique_constraint_field = model._meta.get_field(unique_constraint_name) + + if getattr(unique_constraint_field, 'auto_now_add', None): + default = CreateOnlyDefault(timezone.now) + elif getattr(unique_constraint_field, 'auto_now', None): + default = timezone.now + elif unique_constraint_field.has_default(): + default = unique_constraint_field.default + else: + default = empty + + if unique_constraint_name in model_fields: + # The corresponding field is present in the serializer + if default is empty: + uniqueness_extra_kwargs[unique_constraint_name] = {'required': True} + else: + uniqueness_extra_kwargs[unique_constraint_name] = {'default': default} + elif default is not empty: + # The corresponding field is not present in the + # serializer. We have a default to use for it, so + # add in a hidden field that populates it. + hidden_fields[unique_constraint_name] = HiddenField(default=default) + + # Update `extra_kwargs` with any new options. + for key, value in uniqueness_extra_kwargs.items(): + if key in extra_kwargs: + value.update(extra_kwargs[key]) + extra_kwargs[key] = value + + return extra_kwargs, hidden_fields + + def _get_model_fields(self, field_names, declared_fields, extra_kwargs): + """ + Returns all the model fields that are being mapped to by fields + on the serializer class. + Returned as a dict of 'model field name' -> 'model field'. + Used internally by `get_uniqueness_field_options`. + """ + model = getattr(self.Meta, 'model') + model_fields = {} + + for field_name in field_names: + if field_name in declared_fields: + # If the field is declared on the serializer + field = declared_fields[field_name] + source = field.source or field_name + else: + try: + source = extra_kwargs[field_name]['source'] + except KeyError: + source = field_name + + if '.' in source or source == '*': + # Model fields will always have a simple source mapping, + # they can't be nested attribute lookups. + continue + + try: + field = model._meta.get_field(source) + if isinstance(field, DjangoModelField): + model_fields[source] = field + except FieldDoesNotExist: + pass + + return model_fields + + # Determine the validators to apply... + + def get_validators(self): + """ + Determine the set of validators to use when instantiating serializer. + """ + # If the validators have been declared explicitly then use that. + validators = getattr(getattr(self, 'Meta', None), 'validators', None) + if validators is not None: + return list(validators) + + # Otherwise use the default set of validators. + return ( + self.get_unique_together_validators() + + self.get_unique_for_date_validators() + ) + + def get_unique_together_validators(self): + """ + Determine a default set of validators for any unique_together constraints. + """ + model_class_inheritance_tree = ( + [self.Meta.model] + + list(self.Meta.model._meta.parents) + ) + + # The field names we're passing though here only include fields + # which may map onto a model field. Any dotted field name lookups + # cannot map to a field, and must be a traversal, so we're not + # including those. + field_sources = OrderedDict( + (field.field_name, field.source) for field in self._writable_fields + if (field.source != '*') and ('.' not in field.source) + ) + + # Special Case: Add read_only fields with defaults. + field_sources.update(OrderedDict( + (field.field_name, field.source) for field in self.fields.values() + if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source) + )) + + # Invert so we can find the serializer field names that correspond to + # the model field names in the unique_together sets. This also allows + # us to check that multiple fields don't map to the same source. + source_map = defaultdict(list) + for name, source in field_sources.items(): + source_map[source].append(name) + + # Note that we make sure to check `unique_together` both on the + # base model class, but also on any parent classes. + validators = [] + for parent_class in model_class_inheritance_tree: + for unique_together in parent_class._meta.unique_together: + # Skip if serializer does not map to all unique together sources + if not set(source_map).issuperset(unique_together): + continue + + for source in unique_together: + assert len(source_map[source]) == 1, ( + "Unable to create `UniqueTogetherValidator` for " + "`{model}.{field}` as `{serializer}` has multiple " + "fields ({fields}) that map to this model field. " + "Either remove the extra fields, or override " + "`Meta.validators` with a `UniqueTogetherValidator` " + "using the desired field names." + .format( + model=self.Meta.model.__name__, + serializer=self.__class__.__name__, + field=source, + fields=', '.join(source_map[source]), + ) + ) + + field_names = tuple(source_map[f][0] for f in unique_together) + validator = UniqueTogetherValidator( + queryset=parent_class._default_manager, + fields=field_names + ) + validators.append(validator) + return validators + + def get_unique_for_date_validators(self): + """ + Determine a default set of validators for the following constraints: + + * unique_for_date + * unique_for_month + * unique_for_year + """ + info = model_meta.get_field_info(self.Meta.model) + default_manager = self.Meta.model._default_manager + field_names = [field.source for field in self.fields.values()] + + validators = [] + + for field_name, field in info.fields_and_pk.items(): + if field.unique_for_date and field_name in field_names: + validator = UniqueForDateValidator( + queryset=default_manager, + field=field_name, + date_field=field.unique_for_date + ) + validators.append(validator) + + if field.unique_for_month and field_name in field_names: + validator = UniqueForMonthValidator( + queryset=default_manager, + field=field_name, + date_field=field.unique_for_month + ) + validators.append(validator) + + if field.unique_for_year and field_name in field_names: + validator = UniqueForYearValidator( + queryset=default_manager, + field=field_name, + date_field=field.unique_for_year + ) + validators.append(validator) + + return validators + + +class HyperlinkedModelSerializer(ModelSerializer): + """ + A type of `ModelSerializer` that uses hyperlinked relationships instead + of primary key relationships. Specifically: + + * A 'url' field is included instead of the 'id' field. + * Relationships to other instances are hyperlinks, instead of primary keys. + """ + serializer_related_field = HyperlinkedRelatedField + + def get_default_field_names(self, declared_fields, model_info): + """ + Return the default list of field names that will be used if the + `Meta.fields` option is not specified. + """ + return ( + [self.url_field_name] + + list(declared_fields) + + list(model_info.fields) + + list(model_info.forward_relations) + ) + + def build_nested_field(self, field_name, relation_info, nested_depth): + """ + Create nested fields for forward and reverse relationships. + """ + class NestedSerializer(HyperlinkedModelSerializer): + class Meta: + model = relation_info.related_model + depth = nested_depth - 1 + fields = '__all__' + + field_class = NestedSerializer + field_kwargs = get_nested_relation_kwargs(relation_info) + + return field_class, field_kwargs diff --git a/venv/Lib/site-packages/rest_framework/settings.py b/venv/Lib/site-packages/rest_framework/settings.py new file mode 100644 index 0000000..9eb4c56 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/settings.py @@ -0,0 +1,256 @@ +""" +Settings for REST framework are all namespaced in the REST_FRAMEWORK setting. +For example your project's `settings.py` file might look like this: + +REST_FRAMEWORK = { + 'DEFAULT_RENDERER_CLASSES': [ + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.TemplateHTMLRenderer', + ], + 'DEFAULT_PARSER_CLASSES': [ + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser', + 'rest_framework.parsers.MultiPartParser', + ], +} + +This module provides the `api_setting` object, that is used to access +REST framework settings, checking for user settings first, then falling +back to the defaults. +""" +from django.conf import settings +from django.test.signals import setting_changed +from django.utils.module_loading import import_string + +from rest_framework import ISO_8601 + +DEFAULTS = { + # Base API policies + 'DEFAULT_RENDERER_CLASSES': [ + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer', + ], + 'DEFAULT_PARSER_CLASSES': [ + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser', + 'rest_framework.parsers.MultiPartParser' + ], + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.BasicAuthentication' + ], + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.AllowAny', + ], + 'DEFAULT_THROTTLE_CLASSES': [], + 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation', + 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata', + 'DEFAULT_VERSIONING_CLASS': None, + + # Generic view behavior + 'DEFAULT_PAGINATION_CLASS': None, + 'DEFAULT_FILTER_BACKENDS': [], + + # Schema + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema', + + # Throttling + 'DEFAULT_THROTTLE_RATES': { + 'user': None, + 'anon': None, + }, + 'NUM_PROXIES': None, + + # Pagination + 'PAGE_SIZE': None, + + # Filtering + 'SEARCH_PARAM': 'search', + 'ORDERING_PARAM': 'ordering', + + # Versioning + 'DEFAULT_VERSION': None, + 'ALLOWED_VERSIONS': None, + 'VERSION_PARAM': 'version', + + # Authentication + 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser', + 'UNAUTHENTICATED_TOKEN': None, + + # View configuration + 'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name', + 'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', + + # Exception handling + 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', + 'NON_FIELD_ERRORS_KEY': 'non_field_errors', + + # Testing + 'TEST_REQUEST_RENDERER_CLASSES': [ + 'rest_framework.renderers.MultiPartRenderer', + 'rest_framework.renderers.JSONRenderer' + ], + 'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', + + # Hyperlink settings + 'URL_FORMAT_OVERRIDE': 'format', + 'FORMAT_SUFFIX_KWARG': 'format', + 'URL_FIELD_NAME': 'url', + + # Input and output formats + 'DATE_FORMAT': ISO_8601, + 'DATE_INPUT_FORMATS': [ISO_8601], + + 'DATETIME_FORMAT': ISO_8601, + 'DATETIME_INPUT_FORMATS': [ISO_8601], + + 'TIME_FORMAT': ISO_8601, + 'TIME_INPUT_FORMATS': [ISO_8601], + + # Encoding + 'UNICODE_JSON': True, + 'COMPACT_JSON': True, + 'STRICT_JSON': True, + 'COERCE_DECIMAL_TO_STRING': True, + 'UPLOADED_FILES_USE_URL': True, + + # Browseable API + 'HTML_SELECT_CUTOFF': 1000, + 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", + + # Schemas + 'SCHEMA_COERCE_PATH_PK': True, + 'SCHEMA_COERCE_METHOD_NAMES': { + 'retrieve': 'read', + 'destroy': 'delete' + }, +} + + +# List of settings that may be in string import notation. +IMPORT_STRINGS = [ + 'DEFAULT_RENDERER_CLASSES', + 'DEFAULT_PARSER_CLASSES', + 'DEFAULT_AUTHENTICATION_CLASSES', + 'DEFAULT_PERMISSION_CLASSES', + 'DEFAULT_THROTTLE_CLASSES', + 'DEFAULT_CONTENT_NEGOTIATION_CLASS', + 'DEFAULT_METADATA_CLASS', + 'DEFAULT_VERSIONING_CLASS', + 'DEFAULT_PAGINATION_CLASS', + 'DEFAULT_FILTER_BACKENDS', + 'DEFAULT_SCHEMA_CLASS', + 'EXCEPTION_HANDLER', + 'TEST_REQUEST_RENDERER_CLASSES', + 'UNAUTHENTICATED_USER', + 'UNAUTHENTICATED_TOKEN', + 'VIEW_NAME_FUNCTION', + 'VIEW_DESCRIPTION_FUNCTION' +] + + +# List of settings that have been removed +REMOVED_SETTINGS = [ + 'PAGINATE_BY', 'PAGINATE_BY_PARAM', 'MAX_PAGINATE_BY', +] + + +def perform_import(val, setting_name): + """ + If the given setting is a string import notation, + then perform the necessary import or imports. + """ + if val is None: + return None + elif isinstance(val, str): + return import_from_string(val, setting_name) + elif isinstance(val, (list, tuple)): + return [import_from_string(item, setting_name) for item in val] + return val + + +def import_from_string(val, setting_name): + """ + Attempt to import a class from a string representation. + """ + try: + return import_string(val) + except ImportError as e: + msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e) + raise ImportError(msg) + + +class APISettings: + """ + A settings object that allows REST Framework settings to be accessed as + properties. For example: + + from rest_framework.settings import api_settings + print(api_settings.DEFAULT_RENDERER_CLASSES) + + Any setting with string import paths will be automatically resolved + and return the class, rather than the string literal. + + Note: + This is an internal class that is only compatible with settings namespaced + under the REST_FRAMEWORK name. It is not intended to be used by 3rd-party + apps, and test helpers like `override_settings` may not work as expected. + """ + def __init__(self, user_settings=None, defaults=None, import_strings=None): + if user_settings: + self._user_settings = self.__check_user_settings(user_settings) + self.defaults = defaults or DEFAULTS + self.import_strings = import_strings or IMPORT_STRINGS + self._cached_attrs = set() + + @property + def user_settings(self): + if not hasattr(self, '_user_settings'): + self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) + return self._user_settings + + def __getattr__(self, attr): + if attr not in self.defaults: + raise AttributeError("Invalid API setting: '%s'" % attr) + + try: + # Check if present in user settings + val = self.user_settings[attr] + except KeyError: + # Fall back to defaults + val = self.defaults[attr] + + # Coerce import strings into classes + if attr in self.import_strings: + val = perform_import(val, attr) + + # Cache the result + self._cached_attrs.add(attr) + setattr(self, attr, val) + return val + + def __check_user_settings(self, user_settings): + SETTINGS_DOC = "https://www.django-rest-framework.org/api-guide/settings/" + for setting in REMOVED_SETTINGS: + if setting in user_settings: + raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC)) + return user_settings + + def reload(self): + for attr in self._cached_attrs: + delattr(self, attr) + self._cached_attrs.clear() + if hasattr(self, '_user_settings'): + delattr(self, '_user_settings') + + +api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) + + +def reload_api_settings(*args, **kwargs): + setting = kwargs['setting'] + if setting == 'REST_FRAMEWORK': + api_settings.reload() + + +setting_changed.connect(reload_api_settings) diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-theme.min.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-theme.min.css new file mode 100644 index 0000000..30c85f6 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.4.0 (https://getbootstrap.com/) + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x;background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x;background-color:#2e6da4}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-tweaks.css new file mode 100644 index 0000000..c2fcb30 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -0,0 +1,233 @@ +/* + +This CSS file contains some tweaks specific to the included Bootstrap theme. +It's separate from `style.css` so that it can be easily overridden by replacing +a single block in the template. + +*/ + +.form-actions { + background: transparent; + border-top-color: transparent; + padding-top: 0; + text-align: right; +} + +#generic-content-form textarea { + font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; + font-size: 80%; +} + +.navbar-inverse .brand a { + color: #999999; +} +.navbar-inverse .brand:hover a { + color: white; + text-decoration: none; +} + +/* custom navigation styles */ +.navbar { + width: 100%; + position: fixed; + left: 0; + top: 0; +} + +.navbar { + background: #2C2C2C; + color: white; + border: none; + border-top: 5px solid #A30000; + border-radius: 0px; +} + +.navbar .nav li, .navbar .nav li a, .navbar .brand:hover { + color: white; +} + +.nav-list > .active > a, .nav-list > .active > a:hover { + background: #2C2C2C; +} + +.navbar .dropdown-menu li a, .navbar .dropdown-menu li { + color: #A30000; +} + +.navbar .dropdown-menu li a:hover { + background: #EEEEEE; + color: #C20000; +} + +ul.breadcrumb { + margin: 70px 0 0 0; +} + +.breadcrumb li.active a { + color: #777; +} + +.pagination>.disabled>a, +.pagination>.disabled>a:hover, +.pagination>.disabled>a:focus { + cursor: not-allowed; + pointer-events: none; +} + +.pager>.disabled>a, +.pager>.disabled>a:hover, +.pager>.disabled>a:focus { + pointer-events: none; +} + +.pager .next { + margin-left: 10px; +} + +/*=== dabapps bootstrap styles ====*/ + +html { + width:100%; + background: none; +} + +/*body, .navbar .container-fluid { + max-width: 1150px; + margin: 0 auto; +}*/ + +body { + background: url("../img/grid.png") repeat-x; + background-attachment: fixed; +} + +#content { + margin: 0; + padding-bottom: 60px; +} + +/* sticky footer and footer */ +html, body { + height: 100%; +} + +.wrapper { + position: relative; + top: 0; + left: 0; + padding-top: 60px; + margin: -60px 0; + min-height: 100%; +} + +.form-switcher { + margin-bottom: 0; +} + +.well { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.well .form-actions { + padding-bottom: 0; + margin-bottom: 0; +} + +.well form { + margin-bottom: 0; +} + +.nav-tabs { + border: 0; +} + +.nav-tabs > li { + float: right; +} + +.nav-tabs li a { + margin-right: 0; +} + +.nav-tabs > .active > a { + background: #F5F5F5; +} + +.nav-tabs > .active > a:hover { + background: #F5F5F5; +} + +.tabbable.first-tab-active .tab-content { + border-top-right-radius: 0; +} + +footer { + position: absolute; + bottom: 0; + left: 0; + clear: both; + z-index: 10; + height: 60px; + width: 95%; + margin: 0 2.5%; +} + +footer p { + text-align: center; + color: gray; + border-top: 1px solid #DDDDDD; + padding-top: 10px; +} + +footer a { + color: gray !important; + font-weight: bold; +} + +footer a:hover { + color: gray; +} + +.page-header { + border-bottom: none; + padding-bottom: 0px; + margin: 0; +} + +/* custom general page styles */ +.hero-unit h1, .hero-unit h2 { + color: #A30000; +} + +body a { + color: #A30000; +} + +body a:hover { + color: #c20000; +} + +.request-info { + clear:both; +} + +.horizontal-checkbox label { + padding-top: 0; +} + +.horizontal-checkbox label { + padding-top: 0 !important; +} + +.horizontal-checkbox input { + float: left; + width: 20px; + margin-top: 3px; +} + +.modal-footer form { + margin-left: 5px; + margin-right: 5px; +} diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap.min.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap.min.css new file mode 100644 index 0000000..7f3562e --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.4.0 (https://getbootstrap.com/) + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:"Glyphicons Halflings";src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"),url(../fonts/glyphicons-halflings-regular.woff2) format("woff2"),url(../fonts/glyphicons-halflings-regular.woff) format("woff"),url(../fonts/glyphicons-halflings-regular.ttf) format("truetype"),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:""}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*=col-]{padding-right:0;padding-left:0}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s,-webkit-box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;background-image:none;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out,-o-transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out,-o-transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);left:0}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);left:0}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/default.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/default.css new file mode 100644 index 0000000..51ca3ba --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/default.css @@ -0,0 +1,82 @@ +/* The navbar is fixed at >= 980px wide, so add padding to the body to prevent +content running up underneath it. */ + +h1 { + font-weight: 300; +} + +h2, h3 { + font-weight: 300; +} + +.resource-description, .response-info { + margin-bottom: 2em; +} + +.version:before { + content: "v"; + opacity: 0.6; + padding-right: 0.25em; +} + +.version { + font-size: 70%; +} + +.format-option { + font-family: Menlo, Consolas, "Andale Mono", "Lucida Console", monospace; +} + +.button-form { + float: right; + margin-right: 1em; +} + +td.nested { + padding: 0 !important; +} + +td.nested > table { + margin: 0; +} + +form select, form input:not([type=checkbox]), form textarea { + width: 90%; +} + +form select[multiple] { + height: 150px; +} + +/* To allow tooltips to work on disabled elements */ +.disabled-tooltip-shield { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.errorlist { + margin-top: 0.5em; +} + +pre { + overflow: auto; + word-wrap: normal; + white-space: pre; + font-size: 12px; +} + +.page-header { + border-bottom: none; + padding-bottom: 0px; +} + +#filtersModal form input[type=submit] { + width: auto; +} + +#filtersModal .modal-body h2 { + margin-top: 0 +} diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css new file mode 100644 index 0000000..048cff9 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css @@ -0,0 +1,1338 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.0.3'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.3333333333333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.2857142857142858em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.142857142857143em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.142857142857143em; + width: 2.142857142857143em; + top: 0.14285714285714285em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.8571428571428572em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: spin 2s infinite linear; + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + } + 100% { + -moz-transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -o-transform: rotate(0deg); + } + 100% { + -o-transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -ms-transform: rotate(0deg); + } + 100% { + -ms-transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -moz-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -moz-transform: scale(1, -1); + -ms-transform: scale(1, -1); + -o-transform: scale(1, -1); + transform: scale(1, -1); +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-asc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-desc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-reply-all:before { + content: "\f122"; +} +.fa-mail-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/css/prettify.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/prettify.css new file mode 100644 index 0000000..d437aff --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/css/prettify.css @@ -0,0 +1,30 @@ +.com { color: #93a1a1; } +.lit { color: #195f91; } +.pun, .opn, .clo { color: #93a1a1; } +.fun { color: #dc322f; } +.str, .atv { color: #D14; } +.kwd, .prettyprint .tag { color: #1e347b; } +.typ, .atn, .dec, .var { color: teal; } +.pln { color: #48484c; } + +.prettyprint { + padding: 8px; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} +.prettyprint.linenums { + -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; + -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; + box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin: 0 0 0 33px; /* IE indents via margin-left */ +} +ol.linenums li { + padding-left: 12px; + color: #bebec5; + line-height: 20px; + text-shadow: 0 1px 0 #fff; +} \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/base.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/base.css new file mode 100644 index 0000000..06b240c --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/base.css @@ -0,0 +1,359 @@ +h1 { + font-size: 45px; +} + +.intro-code { + margin-top: 20px; +} + +pre.highlight code * { + white-space: nowrap; /* this sets all children inside to nowrap */ +} + +pre.highlight { + overflow-x: auto; /* this sets the scrolling in x */ +} + +pre.highlight code { + white-space: pre; /* forces <code> to respect <pre> formatting */ +} + +.main-container { + padding-left: 30px; + padding-right: 30px; +} + +.btn:focus, +.btn:focus:active { + outline: none; +} + +.sidebar { + overflow: auto; + font-family: verdana, sans-serif; + font-size: 12px; + font-weight: 200; + background-color: #2e353d; + position: fixed; + top: 0px; + width: 225px; + height: 100%; + color: #FFF; +} + +.sidebar .brand { + background-color: #23282e; + display: block; + text-align: center; + padding: 25px 0; + margin-top: 0; + margin-bottom: 0; +} + +.sidebar .brand a { + color: #FFF; +} + +.sidebar .brand a:hover, +.sidebar .brand a:active, +.sidebar .brand a:focus { + text-decoration: none; +} + +.sidebar .toggle-btn { + display: none; +} + +.sidebar .menu-list { + width: inherit; +} + +.sidebar .menu-list ul, +.sidebar .menu-list li { + background: #2e353d; + list-style: none; + padding: 0px; + margin: 0px; + line-height: 35px; + cursor: pointer; +} + +.sidebar .menu-list ul :not(collapsed) .arrow:before, +.sidebar .menu-list li :not(collapsed) .arrow:before { + font-family: FontAwesome; + content: "\f078"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; + float: right; +} + +.sidebar .menu-list ul .active, +.sidebar .menu-list li .active { + border-left: 3px solid #d19b3d; + background-color: #4f5b69; +} + +.sidebar .menu-list ul .sub-menu li.active, +.sidebar .menu-list li .sub-menu li.active { + color: #d19b3d; +} + +.sidebar .menu-list ul .sub-menu li.active a, +.sidebar .menu-list li .sub-menu li.active a { + color: #d19b3d; +} + +.sidebar .menu-list ul .sub-menu li, +.sidebar .menu-list li .sub-menu li { + background-color: #181c20; + border: none; + border-bottom: 1px solid #23282e; + margin-left: 0px; + line-height: 1.4; + padding-top: 10px; + padding-bottom: 10px; + padding-right: 10px; + padding-left: 25px; +} + +.sidebar .menu-list ul .sub-menu li:hover, +.sidebar .menu-list li .sub-menu li:hover { + background-color: #020203; +} + + +.sidebar .menu-list ul .sub-menu li a, +.sidebar .menu-list li .sub-menu li a { + display: block; +} + +.sidebar .menu-list ul .sub-menu li a:before, +.sidebar .menu-list li .sub-menu li a:before { + font-family: FontAwesome; + font-size: 14px; + font-weight: bold; + content: "\f105"; + display: inline; + vertical-align: middle; + padding-left: 0; + padding-right: 7px; + margin-left: -12px; +} + +.sidebar .menu-list li { + padding-left: 0px; + border-left: 3px solid #2e353d; + border-bottom: 1px solid #23282e; +} + +.sidebar .menu-list li a { + text-decoration: none; + color: white; +} + +.sidebar .menu-list li a i { + padding-left: 10px; + width: 20px; + padding-right: 20px; +} + +.sidebar .menu-list li:hover { + border-left: 3px solid #d19b3d; + background-color: #4f5b69; + -webkit-transition: all 1s ease; + -moz-transition: all 1s ease; + -o-transition: all 1s ease; + -ms-transition: all 1s ease; + transition: all 1s ease; +} + +.sidebar #menu-content { + padding-bottom: 70px; +} + +body { + margin: 0px; + padding: 0px; +} + +.coredocs-section-title { + margin-top: 20px; + padding-bottom: 10px; + border-bottom: 1px solid lightgrey; +} + +.coredocs-link-title a, +.coredocs-section-title a { + display: none; +} + +.coredocs-link-title a, +.coredocs-section-title a { + text-decoration: none; +} + +.coredocs-link-title:hover a, +.coredocs-section-title:hover a { + display: inline; + font-size: 20px; +} + +.coredocs-section-title:last-child { + margin-top: 0; +} + + +/* @group Language Switcher */ + +.sidebar .menu-list.menu-list-bottom { + margin-bottom: 0; + position: fixed; + width: inherit; + bottom: 0; + left: 0; + right: 0; + border-top: 1px solid #23282e; +} + +.sidebar .menu-list-bottom li span { + float: right; + margin-right: 20px; + color: #d19b3d; +} + +/* @end Language Switcher */ + + +/* @group Docs Content */ + +.docs-content .meta .label { + vertical-align: middle; + font-size: 14px; + font-weight: normal; +} + +.docs-content .meta code { + vertical-align: middle; + padding: .2em .6em .3em; + font-size: 14px; +} + +.docs-content .btn { + font-size: inherit; +} + +.code-samples pre { + margin-top: 20px; +} + +/* @end Docs Content */ + + +@media (max-width: 767px) { + .main-container { + padding-left: 15px; + padding-right: 15px; + } + + .sidebar { + position: relative; + width: 100%; + margin-bottom: 10px; + overflow: visible; + } + + .sidebar .toggle-btn { + display: block; + cursor: pointer; + position: absolute; + right: 10px; + top: 10px; + z-index: 10 !important; + padding: 3px; + width: 40px; + text-align: center; + } + + .sidebar .menu-list.menu-list-bottom { + position: static; + } + + .sidebar .brand { + margin-top: 0; + margin-bottom: 0; + + text-align: left !important; + font-size: 22px; + padding: 0; + padding-left: 20px; + line-height: 50px !important; + } +} + +@media (min-width: 767px) { + .sidebar .menu-list .menu-content { + display: block; + } + #main { + width:calc(100% - 225px); + float: right; + } +} + +@media (min-width: 992px) { + .modal-lg { + width: 980px; + } +} + +.api-modal .modal-title .fa { + color: #93c54b; +} + +.api-modal .modal-body .request-awaiting { + padding: 35px 10px; + color: #7F8177; + text-align: center; +} + +.api-modal .modal-body .meta { + margin-bottom: 20px; +} + +.api-modal .modal-body .meta .label { + vertical-align: middle; + font-size: 14px; + font-weight: normal; +} + +.api-modal .modal-body .meta code { + vertical-align: middle; + padding: .2em .6em .3em; + font-size: 14px; +} + +.api-modal .modal-content .toggle-view { + text-align: right; + float: right; +} + +.api-modal .modal-content .response .well { + margin: 0; + max-height: 550px; +} + +.highlight { + background-color: #f7f7f9 +} + +.checkbox label.control-label { + font-weight: bold +} + +@media (min-width: 768px) { + .navbar-nav.navbar-right:last-child { + margin-right: 0 !important; + } +} diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/highlight.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/highlight.css new file mode 100644 index 0000000..0375453 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/highlight.css @@ -0,0 +1,125 @@ +/* +This is the GitHub theme for highlight.js + +github.com style (c) Vasily Polovnyov <vast@whiteants.net> + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + -webkit-text-size-adjust: none; +} + +.hljs-comment, +.diff .hljs-header, +.hljs-javadoc { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.css .rule .hljs-keyword, +.hljs-winutils, +.nginx .hljs-title, +.hljs-subst, +.hljs-request, +.hljs-status { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-hexcolor, +.ruby .hljs-constant { + color: #008080; +} + +.hljs-string, +.hljs-tag .hljs-value, +.hljs-phpdoc, +.hljs-dartdoc, +.tex .hljs-formula { + color: #d14; +} + +.hljs-title, +.hljs-id, +.scss .hljs-preprocessor { + color: #900; + font-weight: bold; +} + +.hljs-list .hljs-keyword, +.hljs-subst { + font-weight: normal; +} + +.hljs-class .hljs-title, +.hljs-type, +.vhdl .hljs-literal, +.tex .hljs-command { + color: #458; + font-weight: bold; +} + +.hljs-tag, +.hljs-tag .hljs-title, +.hljs-rule .hljs-property, +.django .hljs-tag .hljs-keyword { + color: #000080; + font-weight: normal; +} + +.hljs-attribute, +.hljs-variable, +.lisp .hljs-body, +.hljs-name { + color: #008080; +} + +.hljs-regexp { + color: #009926; +} + +.hljs-symbol, +.ruby .hljs-symbol .hljs-string, +.lisp .hljs-keyword, +.clojure .hljs-keyword, +.scheme .hljs-keyword, +.tex .hljs-special, +.hljs-prompt { + color: #990073; +} + +.hljs-built_in { + color: #0086b3; +} + +.hljs-preprocessor, +.hljs-pragma, +.hljs-pi, +.hljs-doctype, +.hljs-shebang, +.hljs-cdata { + color: #999; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.diff .hljs-change { + background: #0086b3; +} + +.hljs-chunk { + color: #aaa; +} diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css new file mode 100644 index 0000000..a075681 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css @@ -0,0 +1,11 @@ +.json-view{position:relative} +.json-view .collapser{width:20px;height:18px;display:block;position:absolute;left:-1.7em;top:-.2em;z-index:5;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYGBgOADE%2F3Hgw0DM4IRHgSsDFOzFInmMAQnY49ONzZRjDFiADT7dMLALiE8y4AGW6LoBAgwAuIkf%2F%2FB7O9sAAAAASUVORK5CYII%3D);background-repeat:no-repeat;background-position:center center;opacity:.5;cursor:pointer} +.json-view .collapsed{-ms-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-khtml-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)} +.json-view .bl{display:block;padding-left:20px;margin-left:-20px;position:relative} +.json-view{font-family:monospace} +.json-view ul{list-style-type:none;padding-left:2em;border-left:1px dotted;margin:.3em} +.json-view ul li{position:relative} +.json-view .comments,.json-view .dots{display:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none} +.json-view .comments{padding-left:.8em;font-style:italic;color:#888} +.json-view .bool,.json-view .null,.json-view .num,.json-view .undef{font-weight:700;color:#1A01CC} +.json-view .str{color:#800} \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/favicon.ico b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/favicon.ico new file mode 100644 index 0000000..17b2c5d Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/favicon.ico differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/grid.png b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/grid.png new file mode 100644 index 0000000..878c3ed Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/img/grid.png differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/api.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/api.js new file mode 100644 index 0000000..e6120cd --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/api.js @@ -0,0 +1,321 @@ +var responseDisplay = 'data' +var coreapi = window.coreapi +var schema = window.schema + +function normalizeKeys (arr) { + var _normarr = []; + for (var i = 0; i < arr.length; i++) { + _normarr = _normarr.concat(arr[i].split(' > ')); + } + return _normarr; +} + +function normalizeHTTPHeader (str) { + // Capitalize HTTP headers for display. + return (str.charAt(0).toUpperCase() + str.substring(1)) + .replace(/-(.)/g, function ($1) { + return $1.toUpperCase() + }) + .replace(/(Www)/g, function ($1) { + return 'WWW' + }) + .replace(/(Xss)/g, function ($1) { + return 'XSS' + }) + .replace(/(Md5)/g, function ($1) { + return 'MD5' + }) +} + +function formEntries (form) { + // Polyfill for new FormData(form).entries() + var formData = new FormData(form) + if (formData.entries !== undefined) { + return Array.from(formData.entries()) + } + + var entries = [] + + for (var i = 0; i < form.elements.length; i++) { + var element = form.elements[i] + + if (!element.name) { + continue + } + + if (element.type === 'file') { + for (var j = 0; j < element.files.length; j++) { + entries.push([element.name, element.files[j]]) + } + } else if (element.type === 'select-multiple' || element.type === 'select-one') { + for (var j = 0; j < element.selectedOptions.length; j++) { + entries.push([element.name, element.selectedOptions[j].value]) + } + } else if (element.type === 'checkbox') { + if (element.checked) { + entries.push([element.name, element.value]) + } + } else { + entries.push([element.name, element.value]) + } + } + + return entries +} + +$(function () { + var $selectedAuthentication = $('#selected-authentication') + var $authControl = $('#auth-control') + var $authTokenModal = $('#auth_token_modal') + var $authBasicModal = $('#auth_basic_modal') + var $authSessionModal = $('#auth_session_modal') + + // Language Control + $('#language-control li').click(function (event) { + event.preventDefault() + var $languageMenuItem = $(this).find('a') + var $languageControls = $(this).closest('ul').find('li') + var $languageControlLinks = $languageControls.find('a') + var language = $languageMenuItem.data('language') + + $languageControlLinks.not('[data-language="' + language + '"]').parent().removeClass('active') + $languageControlLinks.filter('[data-language="' + language + '"]').parent().addClass('active') + + $('#selected-language').text(language) + + var $codeBlocks = $('pre.highlight') + $codeBlocks.not('[data-language="' + language + '"]').addClass('hide') + $codeBlocks.filter('[data-language="' + language + '"]').removeClass('hide') + }) + + // API Explorer + $('form.api-interaction').submit(function (event) { + event.preventDefault() + + var $form = $(this).closest('form') + var $requestMethod = $form.find('.request-method') + var $requestUrl = $form.find('.request-url') + var $toggleView = $form.closest('.modal-content').find('.toggle-view') + var $responseStatusCode = $form.find('.response-status-code') + var $meta = $form.find('.meta') + var $responseRawResponse = $form.find('.response-raw-response') + var $requestAwaiting = $form.find('.request-awaiting') + var $responseRaw = $form.find('.response-raw') + var $responseData = $form.find('.response-data') + var key = normalizeKeys($form.data('key')) + var params = {} + var entries = formEntries($form.get()[0]) + + for (var i = 0; i < entries.length; i++) { + var entry = entries[i] + var paramKey = entry[0] + var paramValue = entry[1] + var $elem = $form.find('[name="' + paramKey + '"]') + var dataType = $elem.data('type') || 'string' + + if (dataType === 'integer' && paramValue) { + var value = parseInt(paramValue) + if (!isNaN(value)) { + params[paramKey] = value + } + } else if (dataType === 'number' && paramValue) { + var value = parseFloat(paramValue) + if (!isNaN(value)) { + params[paramKey] = value + } + } else if (dataType === 'boolean' && paramValue) { + var value = { + 'true': true, + 'false': false + }[paramValue.toLowerCase()] + if (value !== undefined) { + params[paramKey] = value + } + } else if (dataType === 'array' && paramValue) { + try { + params[paramKey] = JSON.parse(paramValue) + } catch (err) { + // Ignore malformed JSON + } + } else if (dataType === 'object' && paramValue) { + try { + params[paramKey] = JSON.parse(paramValue) + } catch (err) { + // Ignore malformed JSON + } + } else if (dataType === 'string' && paramValue) { + params[paramKey] = paramValue + } + } + + $form.find(':checkbox').each(function (index) { + // Handle unselected checkboxes + var name = $(this).attr('name') + if (!params.hasOwnProperty(name)) { + params[name] = false + } + }) + + function requestCallback (request) { + // Fill in the "GET /foo/" display. + var parser = document.createElement('a') + parser.href = request.url + var method = request.options.method + var path = parser.pathname + parser.hash + parser.search + + $requestMethod.text(method) + $requestUrl.text(path) + } + + function responseCallback (response, responseText) { + // Display the 'Data'/'Raw' control. + $toggleView.removeClass('hide') + + // Fill in the "200 OK" display. + $responseStatusCode.removeClass('label-success').removeClass('label-danger') + if (response.ok) { + $responseStatusCode.addClass('label-success') + } else { + $responseStatusCode.addClass('label-danger') + } + $responseStatusCode.text(response.status) + $meta.removeClass('hide') + + // Fill in the Raw HTTP response display. + var panelText = 'HTTP/1.1 ' + response.status + ' ' + response.statusText + '\n' + response.headers.forEach(function (header, key) { + panelText += normalizeHTTPHeader(key) + ': ' + header + '\n' + }) + if (responseText) { + panelText += '\n' + responseText + } + $responseRawResponse.text(panelText) + } + + // Instantiate a client to make the outgoing request. + var options = { + requestCallback: requestCallback, + responseCallback: responseCallback + } + + // Setup authentication options. + if (window.auth && window.auth.type === 'token') { + // Header authentication + options.auth = new coreapi.auth.TokenAuthentication({ + scheme: window.auth.scheme, + token: window.auth.token + }) + } else if (window.auth && window.auth.type === 'basic') { + // Basic authentication + options.auth = new coreapi.auth.BasicAuthentication({ + username: window.auth.username, + password: window.auth.password + }) + } else if (window.auth && window.auth.type === 'session') { + // Session authentication + options.auth = new coreapi.auth.SessionAuthentication({ + csrfCookieName: 'csrftoken', + csrfHeaderName: 'X-CSRFToken' + }) + } + + var client = new coreapi.Client(options) + client.action(schema, key, params).then(function (data) { + var response = JSON.stringify(data, null, 2) + $requestAwaiting.addClass('hide') + $responseRaw.addClass('hide') + $responseData.addClass('hide').text('').jsonView(response) + + if (responseDisplay === 'data') { + $responseData.removeClass('hide') + } else { + $responseRaw.removeClass('hide') + } + }).catch(function (error) { + var response = JSON.stringify(error.content, null, 2) + $requestAwaiting.addClass('hide') + $responseRaw.addClass('hide') + $responseData.addClass('hide').text('').jsonView(response) + + if (responseDisplay === 'data') { + $responseData.removeClass('hide') + } else { + $responseRaw.removeClass('hide') + } + }) + }) + + // 'Data'/'Raw' control + $('.toggle-view button').click(function () { + var $modalContent = $(this).closest('.modal-content') + var $modalResponseRaw = $modalContent.find('.response-raw') + var $modalResponseData = $modalContent.find('.response-data') + + responseDisplay = $(this).data('display-toggle') + + $(this).removeClass('btn-default').addClass('btn-info').siblings().removeClass('btn-info') + + if (responseDisplay === 'raw') { + $modalResponseRaw.removeClass('hide') + $modalResponseData.addClass('hide') + } else { + $modalResponseData.removeClass('hide') + $modalResponseRaw.addClass('hide') + } + }) + + // Authentication: none + $authControl.find("[data-auth='none']").click(function (event) { + event.preventDefault() + window.auth = null + $selectedAuthentication.text('none') + $authControl.find("[data-auth]").closest('li').removeClass('active') + $authControl.find("[data-auth='none']").closest('li').addClass('active') + }) + + // Authentication: token + $('form.authentication-token-form').submit(function (event) { + event.preventDefault() + var $form = $(this).closest('form') + var scheme = $form.find('input#scheme').val() + var token = $form.find('input#token').val() + window.auth = { + 'type': 'token', + 'scheme': scheme, + 'token': token + } + $selectedAuthentication.text('token') + $authControl.find("[data-auth]").closest('li').removeClass('active') + $authControl.find("[data-auth='token']").closest('li').addClass('active') + $authTokenModal.modal('hide') + }) + + // Authentication: basic + $('form.authentication-basic-form').submit(function (event) { + event.preventDefault() + var $form = $(this).closest('form') + var username = $form.find('input#username').val() + var password = $form.find('input#password').val() + window.auth = { + 'type': 'basic', + 'username': username, + 'password': password + } + $selectedAuthentication.text('basic') + $authControl.find("[data-auth]").closest('li').removeClass('active') + $authControl.find("[data-auth='basic']").closest('li').addClass('active') + $authBasicModal.modal('hide') + }) + + // Authentication: session + $('form.authentication-session-form').submit(function (event) { + event.preventDefault() + window.auth = { + 'type': 'session' + } + $selectedAuthentication.text('session') + $authControl.find("[data-auth]").closest('li').removeClass('active') + $authControl.find("[data-auth='session']").closest('li').addClass('active') + $authSessionModal.modal('hide') + }) +}) diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/highlight.pack.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/highlight.pack.js new file mode 100644 index 0000000..a5818df --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/highlight.pack.js @@ -0,0 +1,2 @@ +!function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){var n=(e.className+" "+(e.parentNode?e.parentNode.className:"")).split(/\s+/);return n=n.map(function(e){return e.replace(/^lang(uage)?-/,"")}),n.filter(function(e){return N(e)||/no(-?)highlight|plain|text/.test(e)})[0]}function i(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function o(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function u(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function c(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,o){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),o&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&o.tE&&(a.tE+=(a.e?"|":"")+o.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(i(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,o);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function s(e,t,a,i){function o(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function f(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=E.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":x.classPrefix,i='<span class="'+a,o=t?"":"</span>";return i+=e+'">',i+n+o}function d(){if(!L.k)return n(y);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=p(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(y)}return e+n(y.substr(t))}function h(){if(L.sL&&!w[L.sL])return n(y);var e=L.sL?s(L.sL,y,!0,M[L.sL]):l(y);return L.r>0&&(B+=e.r),"continuous"==L.subLanguageMode&&(M[L.sL]=e.top),p(e.language,e.value,!1,!0)}function b(){return void 0!==L.sL?h():d()}function v(e,t){var r=e.cN?p(e.cN,"",!0):"";e.rB?(k+=r,y=""):e.eB?(k+=n(t)+r,y=""):(k+=r,y=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(y+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(y+=t),k+=b();do L.cN&&(k+="</span>"),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),y="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(f(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"<unnamed>")+'"');return y+=t,t.length||1}var E=N(e);if(!E)throw new Error('Unknown language: "'+e+'"');c(E);var R,L=i||E,M={},k="";for(R=L;R!=E;R=R.parent)R.cN&&(k=p(R.cN,"",!0)+k);var y="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="</span>");return{r:B,value:k,language:e,top:L}}catch(S){if(-1!=S.message.indexOf("Illegal"))return{r:0,value:n(t)};throw S}}function l(e,t){t=t||x.languages||Object.keys(w);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(N(n)){var t=s(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function f(e){return x.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,x.tabReplace)})),x.useBR&&(e=e.replace(/\n/g,"<br>")),e}function g(e,n,t){var r=n?E[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=a(e);if(!/no(-?)highlight|plain|text/.test(n)){var t;x.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,i=n?s(n,r,!0):l(r),c=o(t);if(c.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=i.value,i.value=u(c,o(p),r)}i.value=f(i.value),e.innerHTML=i.value,e.className=g(e.className,n,i.language),e.result={language:i.language,re:i.r},i.second_best&&(e.second_best={language:i.second_best.language,re:i.second_best.r})}}function d(e){x=i(x,e)}function h(){if(!h.called){h.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function b(){addEventListener("DOMContentLoaded",h,!1),addEventListener("load",h,!1)}function v(n,t){var r=w[n]=t(e);r.aliases&&r.aliases.forEach(function(e){E[e]=n})}function m(){return Object.keys(w)}function N(e){return w[e]||w[E[e]]}var x={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},w={},E={};return e.highlight=s,e.highlightAuto=l,e.fixMarkup=f,e.highlightBlock=p,e.configure=d,e.initHighlighting=h,e.initHighlightingOnLoad=b,e.registerLanguage=v,e.listLanguages=m,e.getLanguage=N,e.inherit=i,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"(AV|CA|CF|CG|CI|MK|MP|NS|UI)\\w+"},i={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},o=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:i,l:o,i:"</",c:[t,e.CLCM,e.CBCM,e.CNM,e.QSM,{cN:"string",v:[{b:'@"',e:'"',i:"\\n",c:[e.BE]},{b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"}]},{cN:"preprocessor",b:"#",e:"$",c:[{cN:"title",v:[{b:'"',e:'"'},{b:"<",e:">"}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:o,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,v:[{b:/^\s*('|")use strict('|")/},{b:/^\s*('|")use asm('|")/}]},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",b:"\\b(0[xXbBoO][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}]}});hljs.registerLanguage("scss",function(e){{var t="[a-zA-Z-][a-zA-Z0-9_-]*",i={cN:"variable",b:"(\\$"+t+")\\b"},r={cN:"function",b:t+"\\(",rB:!0,eE:!0,e:"\\("},o={cN:"hexcolor",b:"#[0-9A-Fa-f]+"};({cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:!0,i:"[^\\s]",starts:{cN:"value",eW:!0,eE:!0,c:[r,o,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"important",b:"!important"}]}})}return{cI:!0,i:"[=/|']",c:[e.CLCM,e.CBCM,r,{cN:"id",b:"\\#[A-Za-z0-9_-]+",r:0},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"tag",b:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",r:0},{cN:"pseudo",b:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{cN:"pseudo",b:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},i,{cN:"attribute",b:"\\b(z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",i:"[^\\s]"},{cN:"value",b:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{cN:"value",b:":",e:";",c:[r,i,o,e.CSSNM,e.QSM,e.ASM,{cN:"important",b:"!important"}]},{cN:"at_rule",b:"@",e:"[{;]",k:"mixin include extend for if else each while charset import debug media page content font-face namespace warn",c:[r,i,e.QSM,e.ASM,o,e.CSSNM,{cN:"preprocessor",b:"\\s[A-Za-z0-9_.-]+",r:0}]}]}});hljs.registerLanguage("mel",function(e){return{k:"int float string vector matrix if else switch case default while do for in break continue global proc return about abs addAttr addAttributeEditorNodeHelp addDynamic addNewShelfTab addPP addPanelCategory addPrefixToName advanceToNextDrivenKey affectedNet affects aimConstraint air alias aliasAttr align alignCtx alignCurve alignSurface allViewFit ambientLight angle angleBetween animCone animCurveEditor animDisplay animView annotate appendStringArray applicationName applyAttrPreset applyTake arcLenDimContext arcLengthDimension arclen arrayMapper art3dPaintCtx artAttrCtx artAttrPaintVertexCtx artAttrSkinPaintCtx artAttrTool artBuildPaintMenu artFluidAttrCtx artPuttyCtx artSelectCtx artSetPaintCtx artUserPaintCtx assignCommand assignInputDevice assignViewportFactories attachCurve attachDeviceAttr attachSurface attrColorSliderGrp attrCompatibility attrControlGrp attrEnumOptionMenu attrEnumOptionMenuGrp attrFieldGrp attrFieldSliderGrp attrNavigationControlGrp attrPresetEditWin attributeExists attributeInfo attributeMenu attributeQuery autoKeyframe autoPlace bakeClip bakeFluidShading bakePartialHistory bakeResults bakeSimulation basename basenameEx batchRender bessel bevel bevelPlus binMembership bindSkin blend2 blendShape blendShapeEditor blendShapePanel blendTwoAttr blindDataType boneLattice boundary boxDollyCtx boxZoomCtx bufferCurve buildBookmarkMenu buildKeyframeMenu button buttonManip CBG cacheFile cacheFileCombine cacheFileMerge cacheFileTrack camera cameraView canCreateManip canvas capitalizeString catch catchQuiet ceil changeSubdivComponentDisplayLevel changeSubdivRegion channelBox character characterMap characterOutlineEditor characterize chdir checkBox checkBoxGrp checkDefaultRenderGlobals choice circle circularFillet clamp clear clearCache clip clipEditor clipEditorCurrentTimeCtx clipSchedule clipSchedulerOutliner clipTrimBefore closeCurve closeSurface cluster cmdFileOutput cmdScrollFieldExecuter cmdScrollFieldReporter cmdShell coarsenSubdivSelectionList collision color colorAtPoint colorEditor colorIndex colorIndexSliderGrp colorSliderButtonGrp colorSliderGrp columnLayout commandEcho commandLine commandPort compactHairSystem componentEditor compositingInterop computePolysetVolume condition cone confirmDialog connectAttr connectControl connectDynamic connectJoint connectionInfo constrain constrainValue constructionHistory container containsMultibyte contextInfo control convertFromOldLayers convertIffToPsd convertLightmap convertSolidTx convertTessellation convertUnit copyArray copyFlexor copyKey copySkinWeights cos cpButton cpCache cpClothSet cpCollision cpConstraint cpConvClothToMesh cpForces cpGetSolverAttr cpPanel cpProperty cpRigidCollisionFilter cpSeam cpSetEdit cpSetSolverAttr cpSolver cpSolverTypes cpTool cpUpdateClothUVs createDisplayLayer createDrawCtx createEditor createLayeredPsdFile createMotionField createNewShelf createNode createRenderLayer createSubdivRegion cross crossProduct ctxAbort ctxCompletion ctxEditMode ctxTraverse currentCtx currentTime currentTimeCtx currentUnit curve curveAddPtCtx curveCVCtx curveEPCtx curveEditorCtx curveIntersect curveMoveEPCtx curveOnSurface curveSketchCtx cutKey cycleCheck cylinder dagPose date defaultLightListCheckBox defaultNavigation defineDataServer defineVirtualDevice deformer deg_to_rad delete deleteAttr deleteShadingGroupsAndMaterials deleteShelfTab deleteUI deleteUnusedBrushes delrandstr detachCurve detachDeviceAttr detachSurface deviceEditor devicePanel dgInfo dgdirty dgeval dgtimer dimWhen directKeyCtx directionalLight dirmap dirname disable disconnectAttr disconnectJoint diskCache displacementToPoly displayAffected displayColor displayCull displayLevelOfDetail displayPref displayRGBColor displaySmoothness displayStats displayString displaySurface distanceDimContext distanceDimension doBlur dolly dollyCtx dopeSheetEditor dot dotProduct doubleProfileBirailSurface drag dragAttrContext draggerContext dropoffLocator duplicate duplicateCurve duplicateSurface dynCache dynControl dynExport dynExpression dynGlobals dynPaintEditor dynParticleCtx dynPref dynRelEdPanel dynRelEditor dynamicLoad editAttrLimits editDisplayLayerGlobals editDisplayLayerMembers editRenderLayerAdjustment editRenderLayerGlobals editRenderLayerMembers editor editorTemplate effector emit emitter enableDevice encodeString endString endsWith env equivalent equivalentTol erf error eval evalDeferred evalEcho event exactWorldBoundingBox exclusiveLightCheckBox exec executeForEachObject exists exp expression expressionEditorListen extendCurve extendSurface extrude fcheck fclose feof fflush fgetline fgetword file fileBrowserDialog fileDialog fileExtension fileInfo filetest filletCurve filter filterCurve filterExpand filterStudioImport findAllIntersections findAnimCurves findKeyframe findMenuItem findRelatedSkinCluster finder firstParentOf fitBspline flexor floatEq floatField floatFieldGrp floatScrollBar floatSlider floatSlider2 floatSliderButtonGrp floatSliderGrp floor flow fluidCacheInfo fluidEmitter fluidVoxelInfo flushUndo fmod fontDialog fopen formLayout format fprint frameLayout fread freeFormFillet frewind fromNativePath fwrite gamma gauss geometryConstraint getApplicationVersionAsFloat getAttr getClassification getDefaultBrush getFileList getFluidAttr getInputDeviceRange getMayaPanelTypes getModifiers getPanel getParticleAttr getPluginResource getenv getpid glRender glRenderEditor globalStitch gmatch goal gotoBindPose grabColor gradientControl gradientControlNoAttr graphDollyCtx graphSelectContext graphTrackCtx gravity grid gridLayout group groupObjectsByName HfAddAttractorToAS HfAssignAS HfBuildEqualMap HfBuildFurFiles HfBuildFurImages HfCancelAFR HfConnectASToHF HfCreateAttractor HfDeleteAS HfEditAS HfPerformCreateAS HfRemoveAttractorFromAS HfSelectAttached HfSelectAttractors HfUnAssignAS hardenPointCurve hardware hardwareRenderPanel headsUpDisplay headsUpMessage help helpLine hermite hide hilite hitTest hotBox hotkey hotkeyCheck hsv_to_rgb hudButton hudSlider hudSliderButton hwReflectionMap hwRender hwRenderLoad hyperGraph hyperPanel hyperShade hypot iconTextButton iconTextCheckBox iconTextRadioButton iconTextRadioCollection iconTextScrollList iconTextStaticLabel ikHandle ikHandleCtx ikHandleDisplayScale ikSolver ikSplineHandleCtx ikSystem ikSystemInfo ikfkDisplayMethod illustratorCurves image imfPlugins inheritTransform insertJoint insertJointCtx insertKeyCtx insertKnotCurve insertKnotSurface instance instanceable instancer intField intFieldGrp intScrollBar intSlider intSliderGrp interToUI internalVar intersect iprEngine isAnimCurve isConnected isDirty isParentOf isSameObject isTrue isValidObjectName isValidString isValidUiName isolateSelect itemFilter itemFilterAttr itemFilterRender itemFilterType joint jointCluster jointCtx jointDisplayScale jointLattice keyTangent keyframe keyframeOutliner keyframeRegionCurrentTimeCtx keyframeRegionDirectKeyCtx keyframeRegionDollyCtx keyframeRegionInsertKeyCtx keyframeRegionMoveKeyCtx keyframeRegionScaleKeyCtx keyframeRegionSelectKeyCtx keyframeRegionSetKeyCtx keyframeRegionTrackCtx keyframeStats lassoContext lattice latticeDeformKeyCtx launch launchImageEditor layerButton layeredShaderPort layeredTexturePort layout layoutDialog lightList lightListEditor lightListPanel lightlink lineIntersection linearPrecision linstep listAnimatable listAttr listCameras listConnections listDeviceAttachments listHistory listInputDeviceAxes listInputDeviceButtons listInputDevices listMenuAnnotation listNodeTypes listPanelCategories listRelatives listSets listTransforms listUnselected listerEditor loadFluid loadNewShelf loadPlugin loadPluginLanguageResources loadPrefObjects localizedPanelLabel lockNode loft log longNameOf lookThru ls lsThroughFilter lsType lsUI Mayatomr mag makeIdentity makeLive makePaintable makeRoll makeSingleSurface makeTubeOn makebot manipMoveContext manipMoveLimitsCtx manipOptions manipRotateContext manipRotateLimitsCtx manipScaleContext manipScaleLimitsCtx marker match max memory menu menuBarLayout menuEditor menuItem menuItemToShelf menuSet menuSetPref messageLine min minimizeApp mirrorJoint modelCurrentTimeCtx modelEditor modelPanel mouse movIn movOut move moveIKtoFK moveKeyCtx moveVertexAlongDirection multiProfileBirailSurface mute nParticle nameCommand nameField namespace namespaceInfo newPanelItems newton nodeCast nodeIconButton nodeOutliner nodePreset nodeType noise nonLinear normalConstraint normalize nurbsBoolean nurbsCopyUVSet nurbsCube nurbsEditUV nurbsPlane nurbsSelect nurbsSquare nurbsToPoly nurbsToPolygonsPref nurbsToSubdiv nurbsToSubdivPref nurbsUVSet nurbsViewDirectionVector objExists objectCenter objectLayer objectType objectTypeUI obsoleteProc oceanNurbsPreviewPlane offsetCurve offsetCurveOnSurface offsetSurface openGLExtension openMayaPref optionMenu optionMenuGrp optionVar orbit orbitCtx orientConstraint outlinerEditor outlinerPanel overrideModifier paintEffectsDisplay pairBlend palettePort paneLayout panel panelConfiguration panelHistory paramDimContext paramDimension paramLocator parent parentConstraint particle particleExists particleInstancer particleRenderInfo partition pasteKey pathAnimation pause pclose percent performanceOptions pfxstrokes pickWalk picture pixelMove planarSrf plane play playbackOptions playblast plugAttr plugNode pluginInfo pluginResourceUtil pointConstraint pointCurveConstraint pointLight pointMatrixMult pointOnCurve pointOnSurface pointPosition poleVectorConstraint polyAppend polyAppendFacetCtx polyAppendVertex polyAutoProjection polyAverageNormal polyAverageVertex polyBevel polyBlendColor polyBlindData polyBoolOp polyBridgeEdge polyCacheMonitor polyCheck polyChipOff polyClipboard polyCloseBorder polyCollapseEdge polyCollapseFacet polyColorBlindData polyColorDel polyColorPerVertex polyColorSet polyCompare polyCone polyCopyUV polyCrease polyCreaseCtx polyCreateFacet polyCreateFacetCtx polyCube polyCut polyCutCtx polyCylinder polyCylindricalProjection polyDelEdge polyDelFacet polyDelVertex polyDuplicateAndConnect polyDuplicateEdge polyEditUV polyEditUVShell polyEvaluate polyExtrudeEdge polyExtrudeFacet polyExtrudeVertex polyFlipEdge polyFlipUV polyForceUV polyGeoSampler polyHelix polyInfo polyInstallAction polyLayoutUV polyListComponentConversion polyMapCut polyMapDel polyMapSew polyMapSewMove polyMergeEdge polyMergeEdgeCtx polyMergeFacet polyMergeFacetCtx polyMergeUV polyMergeVertex polyMirrorFace polyMoveEdge polyMoveFacet polyMoveFacetUV polyMoveUV polyMoveVertex polyNormal polyNormalPerVertex polyNormalizeUV polyOptUvs polyOptions polyOutput polyPipe polyPlanarProjection polyPlane polyPlatonicSolid polyPoke polyPrimitive polyPrism polyProjection polyPyramid polyQuad polyQueryBlindData polyReduce polySelect polySelectConstraint polySelectConstraintMonitor polySelectCtx polySelectEditCtx polySeparate polySetToFaceNormal polySewEdge polyShortestPathCtx polySmooth polySoftEdge polySphere polySphericalProjection polySplit polySplitCtx polySplitEdge polySplitRing polySplitVertex polyStraightenUVBorder polySubdivideEdge polySubdivideFacet polyToSubdiv polyTorus polyTransfer polyTriangulate polyUVSet polyUnite polyWedgeFace popen popupMenu pose pow preloadRefEd print progressBar progressWindow projFileViewer projectCurve projectTangent projectionContext projectionManip promptDialog propModCtx propMove psdChannelOutliner psdEditTextureFile psdExport psdTextureFile putenv pwd python querySubdiv quit rad_to_deg radial radioButton radioButtonGrp radioCollection radioMenuItemCollection rampColorPort rand randomizeFollicles randstate rangeControl readTake rebuildCurve rebuildSurface recordAttr recordDevice redo reference referenceEdit referenceQuery refineSubdivSelectionList refresh refreshAE registerPluginResource rehash reloadImage removeJoint removeMultiInstance removePanelCategory rename renameAttr renameSelectionList renameUI render renderGlobalsNode renderInfo renderLayerButton renderLayerParent renderLayerPostProcess renderLayerUnparent renderManip renderPartition renderQualityNode renderSettings renderThumbnailUpdate renderWindowEditor renderWindowSelectContext renderer reorder reorderDeformers requires reroot resampleFluid resetAE resetPfxToPolyCamera resetTool resolutionNode retarget reverseCurve reverseSurface revolve rgb_to_hsv rigidBody rigidSolver roll rollCtx rootOf rot rotate rotationInterpolation roundConstantRadius rowColumnLayout rowLayout runTimeCommand runup sampleImage saveAllShelves saveAttrPreset saveFluid saveImage saveInitialState saveMenu savePrefObjects savePrefs saveShelf saveToolSettings scale scaleBrushBrightness scaleComponents scaleConstraint scaleKey scaleKeyCtx sceneEditor sceneUIReplacement scmh scriptCtx scriptEditorInfo scriptJob scriptNode scriptTable scriptToShelf scriptedPanel scriptedPanelType scrollField scrollLayout sculpt searchPathArray seed selLoadSettings select selectContext selectCurveCV selectKey selectKeyCtx selectKeyframeRegionCtx selectMode selectPref selectPriority selectType selectedNodes selectionConnection separator setAttr setAttrEnumResource setAttrMapping setAttrNiceNameResource setConstraintRestPosition setDefaultShadingGroup setDrivenKeyframe setDynamic setEditCtx setEditor setFluidAttr setFocus setInfinity setInputDeviceMapping setKeyCtx setKeyPath setKeyframe setKeyframeBlendshapeTargetWts setMenuMode setNodeNiceNameResource setNodeTypeFlag setParent setParticleAttr setPfxToPolyCamera setPluginResource setProject setStampDensity setStartupMessage setState setToolTo setUITemplate setXformManip sets shadingConnection shadingGeometryRelCtx shadingLightRelCtx shadingNetworkCompare shadingNode shapeCompare shelfButton shelfLayout shelfTabLayout shellField shortNameOf showHelp showHidden showManipCtx showSelectionInTitle showShadingGroupAttrEditor showWindow sign simplify sin singleProfileBirailSurface size sizeBytes skinCluster skinPercent smoothCurve smoothTangentSurface smoothstep snap2to2 snapKey snapMode snapTogetherCtx snapshot soft softMod softModCtx sort sound soundControl source spaceLocator sphere sphrand spotLight spotLightPreviewPort spreadSheetEditor spring sqrt squareSurface srtContext stackTrace startString startsWith stitchAndExplodeShell stitchSurface stitchSurfacePoints strcmp stringArrayCatenate stringArrayContains stringArrayCount stringArrayInsertAtIndex stringArrayIntersector stringArrayRemove stringArrayRemoveAtIndex stringArrayRemoveDuplicates stringArrayRemoveExact stringArrayToString stringToStringArray strip stripPrefixFromName stroke subdAutoProjection subdCleanTopology subdCollapse subdDuplicateAndConnect subdEditUV subdListComponentConversion subdMapCut subdMapSewMove subdMatchTopology subdMirror subdToBlind subdToPoly subdTransferUVsToCache subdiv subdivCrease subdivDisplaySmoothness substitute substituteAllString substituteGeometry substring surface surfaceSampler surfaceShaderList swatchDisplayPort switchTable symbolButton symbolCheckBox sysFile system tabLayout tan tangentConstraint texLatticeDeformContext texManipContext texMoveContext texMoveUVShellContext texRotateContext texScaleContext texSelectContext texSelectShortestPathCtx texSmudgeUVContext texWinToolCtx text textCurves textField textFieldButtonGrp textFieldGrp textManip textScrollList textToShelf textureDisplacePlane textureHairColor texturePlacementContext textureWindow threadCount threePointArcCtx timeControl timePort timerX toNativePath toggle toggleAxis toggleWindowVisibility tokenize tokenizeList tolerance tolower toolButton toolCollection toolDropped toolHasOptions toolPropertyWindow torus toupper trace track trackCtx transferAttributes transformCompare transformLimits translator trim trunc truncateFluidCache truncateHairCache tumble tumbleCtx turbulence twoPointArcCtx uiRes uiTemplate unassignInputDevice undo undoInfo ungroup uniform unit unloadPlugin untangleUV untitledFileName untrim upAxis updateAE userCtx uvLink uvSnapshot validateShelfName vectorize view2dToolCtx viewCamera viewClipPlane viewFit viewHeadOn viewLookAt viewManip viewPlace viewSet visor volumeAxis vortex waitCursor warning webBrowser webBrowserPrefs whatIs window windowPref wire wireContext workspace wrinkle wrinkleContext writeTake xbmLangPathList xform",i:"</",c:[e.CNM,e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE]},{cN:"variable",v:[{b:"\\$\\d"},{b:"[\\$\\%\\@](\\^\\w\\b|#\\w+|[^\\s\\w{]|{\\w+}|\\w+)"},{b:"\\*(\\^\\w\\b|#\\w+|[^\\s\\w{]|{\\w+}|\\w+)",r:0}]},e.CLCM,e.CBCM]}});hljs.registerLanguage("d",function(e){var r={keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},t="(0|[1-9][\\d_]*)",a="(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)",i="0[bB][01_]+",n="([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)",c="0[xX]"+n,_="([eE][+-]?"+a+")",d="("+a+"(\\.\\d*|"+_+")|\\d+\\."+a+a+"|\\."+t+_+"?)",o="(0[xX]("+n+"\\."+n+"|\\.?"+n+")[pP][+-]?"+a+")",s="("+t+"|"+i+"|"+c+")",l="("+o+"|"+d+")",u="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",b={cN:"number",b:"\\b"+s+"(L|u|U|Lu|LU|uL|UL)?",r:0},f={cN:"number",b:"\\b("+l+"([fF]|L|i|[fF]i|Li)?|"+s+"(i|[fF]i|Li))",r:0},g={cN:"string",b:"'("+u+"|.)",e:"'",i:"."},h={b:u,r:0},p={cN:"string",b:'"',c:[h],e:'"[cwd]?'},w={cN:"string",b:'[rq]"',e:'"[cwd]?',r:5},N={cN:"string",b:"`",e:"`[cwd]?"},A={cN:"string",b:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',r:10},F={cN:"string",b:'q"\\{',e:'\\}"'},m={cN:"shebang",b:"^#!",e:"$",r:5},y={cN:"preprocessor",b:"#(line)",e:"$",r:5},L={cN:"keyword",b:"@[a-zA-Z_][a-zA-Z_\\d]*"},v=e.C("\\/\\+","\\+\\/",{c:["self"],r:10});return{l:e.UIR,k:r,c:[e.CLCM,e.CBCM,v,A,p,w,N,F,f,b,g,m,y,L]}});hljs.registerLanguage("ruleslanguage",function(T){return{k:{keyword:"BILL_PERIOD BILL_START BILL_STOP RS_EFFECTIVE_START RS_EFFECTIVE_STOP RS_JURIS_CODE RS_OPCO_CODE INTDADDATTRIBUTE|5 INTDADDVMSG|5 INTDBLOCKOP|5 INTDBLOCKOPNA|5 INTDCLOSE|5 INTDCOUNT|5 INTDCOUNTSTATUSCODE|5 INTDCREATEMASK|5 INTDCREATEDAYMASK|5 INTDCREATEFACTORMASK|5 INTDCREATEHANDLE|5 INTDCREATEOVERRIDEDAYMASK|5 INTDCREATEOVERRIDEMASK|5 INTDCREATESTATUSCODEMASK|5 INTDCREATETOUPERIOD|5 INTDDELETE|5 INTDDIPTEST|5 INTDEXPORT|5 INTDGETERRORCODE|5 INTDGETERRORMESSAGE|5 INTDISEQUAL|5 INTDJOIN|5 INTDLOAD|5 INTDLOADACTUALCUT|5 INTDLOADDATES|5 INTDLOADHIST|5 INTDLOADLIST|5 INTDLOADLISTDATES|5 INTDLOADLISTENERGY|5 INTDLOADLISTHIST|5 INTDLOADRELATEDCHANNEL|5 INTDLOADSP|5 INTDLOADSTAGING|5 INTDLOADUOM|5 INTDLOADUOMDATES|5 INTDLOADUOMHIST|5 INTDLOADVERSION|5 INTDOPEN|5 INTDREADFIRST|5 INTDREADNEXT|5 INTDRECCOUNT|5 INTDRELEASE|5 INTDREPLACE|5 INTDROLLAVG|5 INTDROLLPEAK|5 INTDSCALAROP|5 INTDSCALE|5 INTDSETATTRIBUTE|5 INTDSETDSTPARTICIPANT|5 INTDSETSTRING|5 INTDSETVALUE|5 INTDSETVALUESTATUS|5 INTDSHIFTSTARTTIME|5 INTDSMOOTH|5 INTDSORT|5 INTDSPIKETEST|5 INTDSUBSET|5 INTDTOU|5 INTDTOURELEASE|5 INTDTOUVALUE|5 INTDUPDATESTATS|5 INTDVALUE|5 STDEV INTDDELETEEX|5 INTDLOADEXACTUAL|5 INTDLOADEXCUT|5 INTDLOADEXDATES|5 INTDLOADEX|5 INTDLOADEXRELATEDCHANNEL|5 INTDSAVEEX|5 MVLOAD|5 MVLOADACCT|5 MVLOADACCTDATES|5 MVLOADACCTHIST|5 MVLOADDATES|5 MVLOADHIST|5 MVLOADLIST|5 MVLOADLISTDATES|5 MVLOADLISTHIST|5 IF FOR NEXT DONE SELECT END CALL ABORT CLEAR CHANNEL FACTOR LIST NUMBER OVERRIDE SET WEEK DISTRIBUTIONNODE ELSE WHEN THEN OTHERWISE IENUM CSV INCLUDE LEAVE RIDER SAVE DELETE NOVALUE SECTION WARN SAVE_UPDATE DETERMINANT LABEL REPORT REVENUE EACH IN FROM TOTAL CHARGE BLOCK AND OR CSV_FILE RATE_CODE AUXILIARY_DEMAND UIDACCOUNT RS BILL_PERIOD_SELECT HOURS_PER_MONTH INTD_ERROR_STOP SEASON_SCHEDULE_NAME ACCOUNTFACTOR ARRAYUPPERBOUND CALLSTOREDPROC GETADOCONNECTION GETCONNECT GETDATASOURCE GETQUALIFIER GETUSERID HASVALUE LISTCOUNT LISTOP LISTUPDATE LISTVALUE PRORATEFACTOR RSPRORATE SETBINPATH SETDBMONITOR WQ_OPEN BILLINGHOURS DATE DATEFROMFLOAT DATETIMEFROMSTRING DATETIMETOSTRING DATETOFLOAT DAY DAYDIFF DAYNAME DBDATETIME HOUR MINUTE MONTH MONTHDIFF MONTHHOURS MONTHNAME ROUNDDATE SAMEWEEKDAYLASTYEAR SECOND WEEKDAY WEEKDIFF YEAR YEARDAY YEARSTR COMPSUM HISTCOUNT HISTMAX HISTMIN HISTMINNZ HISTVALUE MAXNRANGE MAXRANGE MINRANGE COMPIKVA COMPKVA COMPKVARFROMKQKW COMPLF IDATTR FLAG LF2KW LF2KWH MAXKW POWERFACTOR READING2USAGE AVGSEASON MAXSEASON MONTHLYMERGE SEASONVALUE SUMSEASON ACCTREADDATES ACCTTABLELOAD CONFIGADD CONFIGGET CREATEOBJECT CREATEREPORT EMAILCLIENT EXPBLKMDMUSAGE EXPMDMUSAGE EXPORT_USAGE FACTORINEFFECT GETUSERSPECIFIEDSTOP INEFFECT ISHOLIDAY RUNRATE SAVE_PROFILE SETREPORTTITLE USEREXIT WATFORRUNRATE TO TABLE ACOS ASIN ATAN ATAN2 BITAND CEIL COS COSECANT COSH COTANGENT DIVQUOT DIVREM EXP FABS FLOOR FMOD FREPM FREXPN LOG LOG10 MAX MAXN MIN MINNZ MODF POW ROUND ROUND2VALUE ROUNDINT SECANT SIN SINH SQROOT TAN TANH FLOAT2STRING FLOAT2STRINGNC INSTR LEFT LEN LTRIM MID RIGHT RTRIM STRING STRINGNC TOLOWER TOUPPER TRIM NUMDAYS READ_DATE STAGING",built_in:"IDENTIFIER OPTIONS XML_ELEMENT XML_OP XML_ELEMENT_OF DOMDOCCREATE DOMDOCLOADFILE DOMDOCLOADXML DOMDOCSAVEFILE DOMDOCGETROOT DOMDOCADDPI DOMNODEGETNAME DOMNODEGETTYPE DOMNODEGETVALUE DOMNODEGETCHILDCT DOMNODEGETFIRSTCHILD DOMNODEGETSIBLING DOMNODECREATECHILDELEMENT DOMNODESETATTRIBUTE DOMNODEGETCHILDELEMENTCT DOMNODEGETFIRSTCHILDELEMENT DOMNODEGETSIBLINGELEMENT DOMNODEGETATTRIBUTECT DOMNODEGETATTRIBUTEI DOMNODEGETATTRIBUTEBYNAME DOMNODEGETBYNAME"},c:[T.CLCM,T.CBCM,T.ASM,T.QSM,T.CNM,{cN:"array",b:"#[a-zA-Z .]+"}]}});hljs.registerLanguage("actionscript",function(e){var a="[a-zA-Z_$][a-zA-Z0-9_$]*",c="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)",t={cN:"rest_arg",b:"[.]{3}",e:a,r:10};return{aliases:["as"],k:{keyword:"as break case catch class const continue default delete do dynamic each else extends final finally for function get if implements import in include instanceof interface internal is namespace native new override package private protected public return set static super switch this throw try typeof use var void while with",literal:"true false null undefined"},c:[e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{cN:"package",bK:"package",e:"{",c:[e.TM]},{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.TM]},{cN:"preprocessor",bK:"import include",e:";"},{cN:"function",bK:"function",e:"[{;]",eE:!0,i:"\\S",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,e.CLCM,e.CBCM,t]},{cN:"type",b:":",e:c,r:10}]}]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",reserved:"case default function var void with const let enum export import native __hasProp __extends __slice __bind __indexOf",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",t={cN:"subst",b:/#\{/,e:/}/,k:c},r=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,t]},{b:/"/,e:/"/,c:[e.BE,t]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[t,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+n},{b:"`",e:"`",eB:!0,eE:!0,sL:"javascript"}];t.c=r;var i=e.inherit(e.TM,{b:n}),s="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(r)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:r.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("tex",function(c){var e={cN:"command",b:"\\\\[a-zA-Zа-яА-я]+[\\*]?"},m={cN:"command",b:"\\\\[^a-zA-Zа-яА-я0-9]"},r={cN:"special",b:"[{}\\[\\]\\&#~]",r:0};return{c:[{b:"\\\\[a-zA-Zа-яА-я]+[\\*]? *= *-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",rB:!0,c:[e,m,{cN:"number",b:" *=",e:"-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",eB:!0}],r:10},e,m,r,{cN:"formula",b:"\\$\\$",e:"\\$\\$",c:[e,m,r],r:0},{cN:"formula",b:"\\$",e:"\\$",c:[e,m,r],r:0},c.C("%","$",{r:0})]}});hljs.registerLanguage("go",function(e){var t={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer",constant:"true false iota nil",typename:"bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{aliases:["golang"],k:t,i:"</",c:[e.CLCM,e.CBCM,e.QSM,{cN:"string",b:"'",e:"[^\\\\]'"},{cN:"string",b:"`",e:"`"},{cN:"number",b:e.CNR+"[dflsi]?",r:0},e.CNM]}});hljs.registerLanguage("vbscript-html",function(s){return{sL:"xml",subLanguageMode:"continuous",c:[{b:"<%",e:"%>",sL:"vbscript"}]}});hljs.registerLanguage("haskell",function(e){var c=[e.C("--","$"),e.C("{-","-}",{c:["self"]})],a={cN:"pragma",b:"{-#",e:"#-}"},i={cN:"preprocessor",b:"^#",e:"$"},n={cN:"type",b:"\\b[A-Z][\\w']*",r:0},t={cN:"container",b:"\\(",e:"\\)",i:'"',c:[a,i,{cN:"type",b:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TM,{b:"[_a-z][\\w']*"})].concat(c)},l={cN:"container",b:"{",e:"}",c:t.c};return{aliases:["hs"],k:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",c:[{cN:"module",b:"\\bmodule\\b",e:"where",k:"module where",c:[t].concat(c),i:"\\W\\.|;"},{cN:"import",b:"\\bimport\\b",e:"$",k:"import|0 qualified as hiding",c:[t].concat(c),i:"\\W\\.|;"},{cN:"class",b:"^(\\s*)?(class|instance)\\b",e:"where",k:"class family instance where",c:[n,t].concat(c)},{cN:"typedef",b:"\\b(data|(new)?type)\\b",e:"$",k:"data family type newtype deriving",c:[a,n,t,l].concat(c)},{cN:"default",bK:"default",e:"$",c:[n,t].concat(c)},{cN:"infix",bK:"infix infixl infixr",e:"$",c:[e.CNM].concat(c)},{cN:"foreign",b:"\\bforeign\\b",e:"$",k:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",c:[n,e.QSM].concat(c)},{cN:"shebang",b:"#!\\/usr\\/bin\\/env runhaskell",e:"$"},a,i,e.QSM,e.CNM,n,e.inherit(e.TM,{b:"^[_a-z][\\w']*"}),{b:"->|<-"}].concat(c)}});hljs.registerLanguage("scilab",function(e){var n=[e.CNM,{cN:"string",b:"'|\"",e:"'|\"",c:[e.BE,{b:"''"}]}];return{aliases:["sci"],k:{keyword:"abort break case clear catch continue do elseif else endfunction end for functionglobal if pause return resume select try then while%f %F %t %T %pi %eps %inf %nan %e %i %z %s",built_in:"abs and acos asin atan ceil cd chdir clearglobal cosh cos cumprod deff disp errorexec execstr exists exp eye gettext floor fprintf fread fsolve imag isdef isemptyisinfisnan isvector lasterror length load linspace list listfiles log10 log2 logmax min msprintf mclose mopen ones or pathconvert poly printf prod pwd rand realround sinh sin size gsort sprintf sqrt strcat strcmps tring sum system tanh tantype typename warning zeros matrix"},i:'("|#|/\\*|\\s+/\\w+)',c:[{cN:"function",bK:"function endfunction",e:"$",k:"function endfunction|10",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)"}]},{cN:"transposed_variable",b:"[a-zA-Z_][a-zA-Z_0-9]*('+[\\.']*|[\\.']+)",e:"",r:0},{cN:"matrix",b:"\\[",e:"\\]'*[\\.']*",r:0,c:n},e.C("//","$")].concat(n)}});hljs.registerLanguage("profile",function(e){return{c:[e.CNM,{cN:"built_in",b:"{",e:"}$",eB:!0,eE:!0,c:[e.ASM,e.QSM],r:0},{cN:"filename",b:"[a-zA-Z_][\\da-zA-Z_]+\\.[\\da-zA-Z_]{1,3}",e:":",eE:!0},{cN:"header",b:"(ncalls|tottime|cumtime)",e:"$",k:"ncalls tottime|10 cumtime|10 filename",r:10},{cN:"summary",b:"function calls",e:"$",c:[e.CNM],r:10},e.ASM,e.QSM,{cN:"function",b:"\\(",e:"\\)$",c:[e.UTM],r:0}]}});hljs.registerLanguage("thrift",function(e){var t="bool byte i16 i32 i64 double string binary";return{k:{keyword:"namespace const typedef struct enum service exception void oneway set list map required optional",built_in:t,literal:"true false"},c:[e.QSM,e.NM,e.CLCM,e.CBCM,{cN:"class",bK:"struct enum service exception",e:/\{/,i:/\n/,c:[e.inherit(e.TM,{starts:{eW:!0,eE:!0}})]},{b:"\\b(set|list|map)\\s*<",e:">",k:t,c:["self"]}]}});hljs.registerLanguage("matlab",function(e){var a=[e.CNM,{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]}],s={r:0,c:[{cN:"operator",b:/'['\.]*/}]};return{k:{keyword:"break case catch classdef continue else elseif end enumerated events for function global if methods otherwise parfor persistent properties return spmd switch try while",built_in:"sin sind sinh asin asind asinh cos cosd cosh acos acosd acosh tan tand tanh atan atand atan2 atanh sec secd sech asec asecd asech csc cscd csch acsc acscd acsch cot cotd coth acot acotd acoth hypot exp expm1 log log1p log10 log2 pow2 realpow reallog realsqrt sqrt nthroot nextpow2 abs angle complex conj imag real unwrap isreal cplxpair fix floor ceil round mod rem sign airy besselj bessely besselh besseli besselk beta betainc betaln ellipj ellipke erf erfc erfcx erfinv expint gamma gammainc gammaln psi legendre cross dot factor isprime primes gcd lcm rat rats perms nchoosek factorial cart2sph cart2pol pol2cart sph2cart hsv2rgb rgb2hsv zeros ones eye repmat rand randn linspace logspace freqspace meshgrid accumarray size length ndims numel disp isempty isequal isequalwithequalnans cat reshape diag blkdiag tril triu fliplr flipud flipdim rot90 find sub2ind ind2sub bsxfun ndgrid permute ipermute shiftdim circshift squeeze isscalar isvector ans eps realmax realmin pi i inf nan isnan isinf isfinite j why compan gallery hadamard hankel hilb invhilb magic pascal rosser toeplitz vander wilkinson"},i:'(//|"|#|/\\*|\\s+/\\w+)',c:[{cN:"function",bK:"function",e:"$",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)"},{cN:"params",b:"\\[",e:"\\]"}]},{b:/[a-zA-Z_][a-zA-Z_0-9]*'['\.]*/,rB:!0,r:0,c:[{b:/[a-zA-Z_][a-zA-Z_0-9]*/,r:0},s.c[0]]},{cN:"matrix",b:"\\[",e:"\\]",c:a,r:0,starts:s},{cN:"cell",b:"\\{",e:/}/,c:a,r:0,starts:s},{b:/\)/,r:0,starts:s},e.C("^\\s*\\%\\{\\s*$","^\\s*\\%\\}\\s*$"),e.C("\\%","$")].concat(a)}});hljs.registerLanguage("vbscript",function(e){return{aliases:["vbs"],cI:!0,k:{keyword:"call class const dim do loop erase execute executeglobal exit for each next function if then else on error option explicit new private property let get public randomize redim rem select case set stop sub while wend with end to elseif is or xor and not class_initialize class_terminate default preserve in me byval byref step resume goto",built_in:"lcase month vartype instrrev ubound setlocale getobject rgb getref string weekdayname rnd dateadd monthname now day minute isarray cbool round formatcurrency conversions csng timevalue second year space abs clng timeserial fixs len asc isempty maths dateserial atn timer isobject filter weekday datevalue ccur isdate instr datediff formatdatetime replace isnull right sgn array snumeric log cdbl hex chr lbound msgbox ucase getlocale cos cdate cbyte rtrim join hour oct typename trim strcomp int createobject loadpicture tan formatnumber mid scriptenginebuildversion scriptengine split scriptengineminorversion cint sin datepart ltrim sqr scriptenginemajorversion time derived eval date formatpercent exp inputbox left ascw chrw regexp server response request cstr err",literal:"true false null nothing empty"},i:"//",c:[e.inherit(e.QSM,{c:[{b:'""'}]}),e.C(/'/,/$/,{r:0}),e.CNM]}});hljs.registerLanguage("capnproto",function(t){return{aliases:["capnp"],k:{keyword:"struct enum interface union group import using const annotation extends in of on as with from fixed",built_in:"Void Bool Int8 Int16 Int32 Int64 UInt8 UInt16 UInt32 UInt64 Float32 Float64 Text Data AnyPointer AnyStruct Capability List",literal:"true false"},c:[t.QSM,t.NM,t.HCM,{cN:"shebang",b:/@0x[\w\d]{16};/,i:/\n/},{cN:"number",b:/@\d+\b/},{cN:"class",bK:"struct enum",e:/\{/,i:/\n/,c:[t.inherit(t.TM,{starts:{eW:!0,eE:!0}})]},{cN:"class",bK:"interface",e:/\{/,i:/\n/,c:[t.inherit(t.TM,{starts:{eW:!0,eE:!0}})]}]}});hljs.registerLanguage("xl",function(e){var t="ObjectLoader Animate MovieCredits Slides Filters Shading Materials LensFlare Mapping VLCAudioVideo StereoDecoder PointCloud NetworkAccess RemoteControl RegExp ChromaKey Snowfall NodeJS Speech Charts",o={keyword:"if then else do while until for loop import with is as where when by data constant",literal:"true false nil",type:"integer real text name boolean symbol infix prefix postfix block tree",built_in:"in mod rem and or xor not abs sign floor ceil sqrt sin cos tan asin acos atan exp expm1 log log2 log10 log1p pi at",module:t,id:"text_length text_range text_find text_replace contains page slide basic_slide title_slide title subtitle fade_in fade_out fade_at clear_color color line_color line_width texture_wrap texture_transform texture scale_?x scale_?y scale_?z? translate_?x translate_?y translate_?z? rotate_?x rotate_?y rotate_?z? rectangle circle ellipse sphere path line_to move_to quad_to curve_to theme background contents locally time mouse_?x mouse_?y mouse_buttons"},a={cN:"constant",b:"[A-Z][A-Z_0-9]+",r:0},r={cN:"variable",b:"([A-Z][a-z_0-9]+)+",r:0},i={cN:"id",b:"[a-z][a-z_0-9]+",r:0},l={cN:"string",b:'"',e:'"',i:"\\n"},n={cN:"string",b:"'",e:"'",i:"\\n"},s={cN:"string",b:"<<",e:">>"},c={cN:"number",b:"[0-9]+#[0-9A-Z_]+(\\.[0-9-A-Z_]+)?#?([Ee][+-]?[0-9]+)?",r:10},_={cN:"import",bK:"import",e:"$",k:{keyword:"import",module:t},r:0,c:[l]},d={cN:"function",b:"[a-z].*->"};return{aliases:["tao"],l:/[a-zA-Z][a-zA-Z0-9_?]*/,k:o,c:[e.CLCM,e.CBCM,l,n,s,d,_,a,r,i,c,e.NM]}});hljs.registerLanguage("scala",function(e){var t={cN:"annotation",b:"@[A-Za-z]+"},a={cN:"string",b:'u?r?"""',e:'"""',r:10},r={cN:"symbol",b:"'\\w[\\w\\d_]*(?!')"},c={cN:"type",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},i={cN:"title",b:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,r:0},l={cN:"class",bK:"class object trait type",e:/[:={\[(\n;]/,c:[{cN:"keyword",bK:"extends with",r:10},i]},n={cN:"function",bK:"def val",e:/[:={\[(\n;]/,c:[i]};return{k:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},c:[e.CLCM,e.CBCM,a,e.QSM,r,c,n,l,e.CNM,t]}});hljs.registerLanguage("elixir",function(e){var n="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?",r="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",b="and false then defined module in return redo retry end for true self when next until do begin unless nil break not case cond alias while ensure or include use alias fn quote",c={cN:"subst",b:"#\\{",e:"}",l:n,k:b},a={cN:"string",c:[e.BE,c],v:[{b:/'/,e:/'/},{b:/"/,e:/"/}]},i={cN:"function",bK:"def defp defmacro",e:/\B\b/,c:[e.inherit(e.TM,{b:n,endsParent:!0})]},s=e.inherit(i,{cN:"class",bK:"defmodule defrecord",e:/\bdo\b|$|;/}),l=[a,e.HCM,s,i,{cN:"constant",b:"(\\b[A-Z_]\\w*(.)?)+",r:0},{cN:"symbol",b:":",c:[a,{b:r}],r:0},{cN:"symbol",b:n+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"->"},{b:"("+e.RSR+")\\s*",c:[e.HCM,{cN:"regexp",i:"\\n",c:[e.BE,c],v:[{b:"/",e:"/[a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];return c.c=l,{l:n,k:b,c:l}});hljs.registerLanguage("sml",function(e){return{aliases:["ml"],k:{keyword:"abstype and andalso as case datatype do else end eqtype exception fn fun functor handle if in include infix infixr let local nonfix of op open orelse raise rec sharing sig signature struct structure then type val with withtype where while",built_in:"array bool char exn int list option order real ref string substring vector unit word",literal:"true false NONE SOME LESS EQUAL GREATER nil"},i:/\/\/|>>/,l:"[a-z_]\\w*!?",c:[{cN:"literal",b:"\\[(\\|\\|)?\\]|\\(\\)"},e.C("\\(\\*","\\*\\)",{c:["self"]}),{cN:"symbol",b:"'[A-Za-z_](?!')[\\w']*"},{cN:"tag",b:"`[A-Z][\\w']*"},{cN:"type",b:"\\b[A-Z][\\w']*",r:0},{b:"[a-z_]\\w*'[\\w']*"},e.inherit(e.ASM,{cN:"char",r:0}),e.inherit(e.QSM,{i:null}),{cN:"number",b:"\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",r:0},{b:/[-=]>/}]}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"tag",b:"</?",e:">"},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("dockerfile",function(n){return{aliases:["docker"],cI:!0,k:{built_ins:"from maintainer cmd expose add copy entrypoint volume user workdir onbuild run env"},c:[n.HCM,{k:{built_in:"run cmd entrypoint volume add copy workdir onbuild"},b:/^ *(onbuild +)?(run|cmd|entrypoint|volume|add|copy|workdir) +/,starts:{e:/[^\\]\n/,sL:"bash",subLanguageMode:"continuous"}},{k:{built_in:"from maintainer expose env user onbuild"},b:/^ *(onbuild +)?(from|maintainer|expose|env|user|onbuild) +/,e:/[^\\]\n/,c:[n.ASM,n.QSM,n.NM,n.HCM]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("haml",function(s){return{cI:!0,c:[{cN:"doctype",b:"^!!!( (5|1\\.1|Strict|Frameset|Basic|Mobile|RDFa|XML\\b.*))?$",r:10},s.C("^\\s*(!=#|=#|-#|/).*$",!1,{r:0}),{b:"^\\s*(-|=|!=)(?!#)",starts:{e:"\\n",sL:"ruby"}},{cN:"tag",b:"^\\s*%",c:[{cN:"title",b:"\\w+"},{cN:"value",b:"[#\\.]\\w+"},{b:"{\\s*",e:"\\s*}",eE:!0,c:[{b:":\\w+\\s*=>",e:",\\s+",rB:!0,eW:!0,c:[{cN:"symbol",b:":\\w+"},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]},{b:"\\(\\s*",e:"\\s*\\)",eE:!0,c:[{b:"\\w+\\s*=",e:"\\s+",rB:!0,eW:!0,c:[{cN:"attribute",b:"\\w+",r:0},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]}]},{cN:"bullet",b:"^\\s*[=~]\\s*",r:0},{b:"#{",starts:{e:"}",sL:"ruby"}}]}});hljs.registerLanguage("fortran",function(e){var t={cN:"params",b:"\\(",e:"\\)"},n={constant:".False. .True.",type:"integer real character complex logical dimension allocatable|10 parameter external implicit|10 none double precision assign intent optional pointer target in out common equivalence data",keyword:"kind do while private call intrinsic where elsewhere type endtype endmodule endselect endinterface end enddo endif if forall endforall only contains default return stop then public subroutine|10 function program .and. .or. .not. .le. .eq. .ge. .gt. .lt. goto save else use module select case access blank direct exist file fmt form formatted iostat name named nextrec number opened rec recl sequential status unformatted unit continue format pause cycle exit c_null_char c_alert c_backspace c_form_feed flush wait decimal round iomsg synchronous nopass non_overridable pass protected volatile abstract extends import non_intrinsic value deferred generic final enumerator class associate bind enum c_int c_short c_long c_long_long c_signed_char c_size_t c_int8_t c_int16_t c_int32_t c_int64_t c_int_least8_t c_int_least16_t c_int_least32_t c_int_least64_t c_int_fast8_t c_int_fast16_t c_int_fast32_t c_int_fast64_t c_intmax_t C_intptr_t c_float c_double c_long_double c_float_complex c_double_complex c_long_double_complex c_bool c_char c_null_ptr c_null_funptr c_new_line c_carriage_return c_horizontal_tab c_vertical_tab iso_c_binding c_loc c_funloc c_associated c_f_pointer c_ptr c_funptr iso_fortran_env character_storage_size error_unit file_storage_size input_unit iostat_end iostat_eor numeric_storage_size output_unit c_f_procpointer ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode newunit contiguous pad position action delim readwrite eor advance nml interface procedure namelist include sequence elemental pure",built_in:"alog alog10 amax0 amax1 amin0 amin1 amod cabs ccos cexp clog csin csqrt dabs dacos dasin datan datan2 dcos dcosh ddim dexp dint dlog dlog10 dmax1 dmin1 dmod dnint dsign dsin dsinh dsqrt dtan dtanh float iabs idim idint idnint ifix isign max0 max1 min0 min1 sngl algama cdabs cdcos cdexp cdlog cdsin cdsqrt cqabs cqcos cqexp cqlog cqsin cqsqrt dcmplx dconjg derf derfc dfloat dgamma dimag dlgama iqint qabs qacos qasin qatan qatan2 qcmplx qconjg qcos qcosh qdim qerf qerfc qexp qgamma qimag qlgama qlog qlog10 qmax1 qmin1 qmod qnint qsign qsin qsinh qsqrt qtan qtanh abs acos aimag aint anint asin atan atan2 char cmplx conjg cos cosh exp ichar index int log log10 max min nint sign sin sinh sqrt tan tanh print write dim lge lgt lle llt mod nullify allocate deallocate adjustl adjustr all allocated any associated bit_size btest ceiling count cshift date_and_time digits dot_product eoshift epsilon exponent floor fraction huge iand ibclr ibits ibset ieor ior ishft ishftc lbound len_trim matmul maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits nearest pack present product radix random_number random_seed range repeat reshape rrspacing scale scan selected_int_kind selected_real_kind set_exponent shape size spacing spread sum system_clock tiny transpose trim ubound unpack verify achar iachar transfer dble entry dprod cpu_time command_argument_count get_command get_command_argument get_environment_variable is_iostat_end ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode is_iostat_eor move_alloc new_line selected_char_kind same_type_as extends_type_ofacosh asinh atanh bessel_j0 bessel_j1 bessel_jn bessel_y0 bessel_y1 bessel_yn erf erfc erfc_scaled gamma log_gamma hypot norm2 atomic_define atomic_ref execute_command_line leadz trailz storage_size merge_bits bge bgt ble blt dshiftl dshiftr findloc iall iany iparity image_index lcobound ucobound maskl maskr num_images parity popcnt poppar shifta shiftl shiftr this_image"};return{cI:!0,aliases:["f90","f95"],k:n,c:[e.inherit(e.ASM,{cN:"string",r:0}),e.inherit(e.QSM,{cN:"string",r:0}),{cN:"function",bK:"subroutine function program",i:"[${=\\n]",c:[e.UTM,t]},e.C("!","$",{r:0}),{cN:"number",b:"(?=\\b|\\+|\\-|\\.)(?=\\.\\d|\\d)(?:\\d+)?(?:\\.?\\d*)(?:[de][+-]?\\d+)?\\b\\.?",r:0}]}});hljs.registerLanguage("smali",function(r){var t=["add","and","cmp","cmpg","cmpl","const","div","double","float","goto","if","int","long","move","mul","neg","new","nop","not","or","rem","return","shl","shr","sput","sub","throw","ushr","xor"],n=["aget","aput","array","check","execute","fill","filled","goto/16","goto/32","iget","instance","invoke","iput","monitor","packed","sget","sparse"],s=["transient","constructor","abstract","final","synthetic","public","private","protected","static","bridge","system"];return{aliases:["smali"],c:[{cN:"string",b:'"',e:'"',r:0},r.C("#","$",{r:0}),{cN:"keyword",b:"\\s*\\.end\\s[a-zA-Z0-9]*",r:1},{cN:"keyword",b:"^[ ]*\\.[a-zA-Z]*",r:0},{cN:"keyword",b:"\\s:[a-zA-Z_0-9]*",r:0},{cN:"keyword",b:"\\s("+s.join("|")+")",r:1},{cN:"keyword",b:"\\[",r:0},{cN:"instruction",b:"\\s("+t.join("|")+")\\s",r:1},{cN:"instruction",b:"\\s("+t.join("|")+")((\\-|/)[a-zA-Z0-9]+)+\\s",r:10},{cN:"instruction",b:"\\s("+n.join("|")+")((\\-|/)[a-zA-Z0-9]+)*\\s",r:10},{cN:"class",b:"L[^(;:\n]*;",r:0},{cN:"function",b:'( |->)[^(\n ;"]*\\(',r:0},{cN:"function",b:"\\)",r:0},{cN:"variable",b:"[vp][0-9]+",r:0}]}});hljs.registerLanguage("julia",function(r){var e={keyword:"in abstract baremodule begin bitstype break catch ccall const continue do else elseif end export finally for function global if immutable import importall let local macro module quote return try type typealias using while",literal:"true false ANY ARGS CPU_CORES C_NULL DL_LOAD_PATH DevNull ENDIAN_BOM ENV I|0 Inf Inf16 Inf32 InsertionSort JULIA_HOME LOAD_PATH MS_ASYNC MS_INVALIDATE MS_SYNC MergeSort NaN NaN16 NaN32 OS_NAME QuickSort RTLD_DEEPBIND RTLD_FIRST RTLD_GLOBAL RTLD_LAZY RTLD_LOCAL RTLD_NODELETE RTLD_NOLOAD RTLD_NOW RoundDown RoundFromZero RoundNearest RoundToZero RoundUp STDERR STDIN STDOUT VERSION WORD_SIZE catalan cglobal e eu eulergamma golden im nothing pi γ π φ",built_in:"ASCIIString AbstractArray AbstractRNG AbstractSparseArray Any ArgumentError Array Associative Base64Pipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError Box CFILE Cchar Cdouble Cfloat Char CharString Cint Clong Clonglong ClusterManager Cmd Coff_t Colon Complex Complex128 Complex32 Complex64 Condition Cptrdiff_t Cshort Csize_t Cssize_t Cuchar Cuint Culong Culonglong Cushort Cwchar_t DArray DataType DenseArray Diagonal Dict DimensionMismatch DirectIndexString Display DivideError DomainError EOFError EachLine Enumerate ErrorException Exception Expr Factorization FileMonitor FileOffset Filter Float16 Float32 Float64 FloatRange FloatingPoint Function GetfieldNode GotoNode Hermitian IO IOBuffer IOStream IPv4 IPv6 InexactError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException IntrinsicFunction KeyError LabelNode LambdaStaticData LineNumberNode LoadError LocalProcess MIME MathConst MemoryError MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode Nothing Number ObjectIdDict OrdinalRange OverflowError ParseError PollingFileWatcher ProcessExitedException ProcessGroup Ptr QuoteNode Range Range1 Ranges Rational RawFD Real Regex RegexMatch RemoteRef RepString RevString RopeString RoundingMode Set SharedArray Signed SparseMatrixCSC StackOverflowError Stat StatStruct StepRange String SubArray SubString SymTridiagonal Symbol SymbolNode Symmetric SystemError Task TextDisplay Timer TmStruct TopNode Triangular Tridiagonal Type TypeConstructor TypeError TypeName TypeVar UTF16String UTF32String UTF8String UdpSocket Uint Uint128 Uint16 Uint32 Uint64 Uint8 UndefRefError UndefVarError UniformScaling UnionType UnitRange Unsigned Vararg VersionNumber WString WeakKeyDict WeakRef Woodbury Zip"},t="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",o={l:t,k:e},n={cN:"type-annotation",b:/::/},a={cN:"subtype",b:/<:/},i={cN:"number",b:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,r:0},l={cN:"char",b:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},c={cN:"subst",b:/\$\(/,e:/\)/,k:e},u={cN:"variable",b:"\\$"+t},d={cN:"string",c:[r.BE,c,u],v:[{b:/\w*"/,e:/"\w*/},{b:/\w*"""/,e:/"""\w*/}]},g={cN:"string",c:[r.BE,c,u],b:"`",e:"`"},s={cN:"macrocall",b:"@"+t},S={cN:"comment",v:[{b:"#=",e:"=#",r:10},{b:"#",e:"$"}]};return o.c=[i,l,n,a,d,g,s,S,r.HCM],c.c=o.c,o});hljs.registerLanguage("delphi",function(e){var r="exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure",t=[e.CLCM,e.C(/\{/,/\}/,{r:0}),e.C(/\(\*/,/\*\)/,{r:10})],i={cN:"string",b:/'/,e:/'/,c:[{b:/''/}]},c={cN:"string",b:/(#\d+)+/},o={b:e.IR+"\\s*=\\s*class\\s*\\(",rB:!0,c:[e.TM]},n={cN:"function",bK:"function constructor destructor procedure",e:/[:;]/,k:"function constructor|10 destructor|10 procedure|10",c:[e.TM,{cN:"params",b:/\(/,e:/\)/,k:r,c:[i,c]}].concat(t)};return{cI:!0,k:r,i:/"|\$[G-Zg-z]|\/\*|<\/|\|/,c:[i,c,e.NM,o,n].concat(t)}});hljs.registerLanguage("brainfuck",function(r){var n={cN:"literal",b:"[\\+\\-]",r:0};return{aliases:["bf"],c:[r.C("[^\\[\\]\\.,\\+\\-<> \r\n]","[\\[\\]\\.,\\+\\-<> \r\n]",{rE:!0,r:0}),{cN:"title",b:"[\\[\\]]",r:0},{cN:"string",b:"[\\.,]",r:0},{b:/\+\+|\-\-/,rB:!0,c:[n]},n]}});hljs.registerLanguage("ini",function(e){return{cI:!0,i:/\S/,c:[e.C(";","$"),{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[e.QSM,e.NM],r:0}]}]}});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],l={cN:"value",e:",",eW:!0,eE:!0,c:i,k:t},c={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:l}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(l,{cN:null})],i:"\\S"};return i.splice(i.length,0,c,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("powershell",function(e){var t={b:"`[\\s\\S]",r:0},r={cN:"variable",v:[{b:/\$[\w\d][\w\d_:]*/}]},o={cN:"string",b:/"/,e:/"/,c:[t,r,{cN:"variable",b:/\$[A-z]/,e:/[^A-z]/}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["ps"],l:/-?[A-z\.\-]+/,cI:!0,k:{keyword:"if else foreach return function do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch",literal:"$null $true $false",built_in:"Add-Content Add-History Add-Member Add-PSSnapin Clear-Content Clear-Item Clear-Item Property Clear-Variable Compare-Object ConvertFrom-SecureString Convert-Path ConvertTo-Html ConvertTo-SecureString Copy-Item Copy-ItemProperty Export-Alias Export-Clixml Export-Console Export-Csv ForEach-Object Format-Custom Format-List Format-Table Format-Wide Get-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem Get-Command Get-Content Get-Credential Get-Culture Get-Date Get-EventLog Get-ExecutionPolicy Get-Help Get-History Get-Host Get-Item Get-ItemProperty Get-Location Get-Member Get-PfxCertificate Get-Process Get-PSDrive Get-PSProvider Get-PSSnapin Get-Service Get-TraceSource Get-UICulture Get-Unique Get-Variable Get-WmiObject Group-Object Import-Alias Import-Clixml Import-Csv Invoke-Expression Invoke-History Invoke-Item Join-Path Measure-Command Measure-Object Move-Item Move-ItemProperty New-Alias New-Item New-ItemProperty New-Object New-PSDrive New-Service New-TimeSpan New-Variable Out-Default Out-File Out-Host Out-Null Out-Printer Out-String Pop-Location Push-Location Read-Host Remove-Item Remove-ItemProperty Remove-PSDrive Remove-PSSnapin Remove-Variable Rename-Item Rename-ItemProperty Resolve-Path Restart-Service Resume-Service Select-Object Select-String Set-Acl Set-Alias Set-AuthenticodeSignature Set-Content Set-Date Set-ExecutionPolicy Set-Item Set-ItemProperty Set-Location Set-PSDebug Set-Service Set-TraceSource Set-Variable Sort-Object Split-Path Start-Service Start-Sleep Start-Transcript Stop-Process Stop-Service Stop-Transcript Suspend-Service Tee-Object Test-Path Trace-Command Update-FormatData Update-TypeData Where-Object Write-Debug Write-Error Write-Host Write-Output Write-Progress Write-Verbose Write-Warning",operator:"-ne -eq -lt -gt -ge -le -not -like -notlike -match -notmatch -contains -notcontains -in -notin -replace"},c:[e.HCM,e.NM,o,a,r]}});hljs.registerLanguage("gradle",function(e){return{cI:!0,k:{keyword:"task project allprojects subprojects artifacts buildscript configurations dependencies repositories sourceSets description delete from into include exclude source classpath destinationDir includes options sourceCompatibility targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant def abstract break case catch continue default do else extends final finally for if implements instanceof native new private protected public return static switch synchronized throw throws transient try volatile while strictfp package import false null super this true antlrtask checkstyle codenarc copy boolean byte char class double float int interface long short void compile runTime file fileTree abs any append asList asWritable call collect compareTo count div dump each eachByte eachFile eachLine every find findAll flatten getAt getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter newReader newWriter next plus pop power previous print println push putAt read readBytes readLines reverse reverseEach round size sort splitEachLine step subMap times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader withStream withWriter withWriterAppend write writeLine"},c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.NM,e.RM]}});hljs.registerLanguage("erb",function(e){return{sL:"xml",subLanguageMode:"continuous",c:[e.C("<%#","%>"),{b:"<%[%=-]?",e:"[%-]?%>",sL:"ruby",eB:!0,eE:!0}]}});hljs.registerLanguage("swift",function(e){var i={keyword:"class deinit enum extension func import init let protocol static struct subscript typealias var break case continue default do else fallthrough if in for return switch where while as dynamicType is new super self Self Type __COLUMN__ __FILE__ __FUNCTION__ __LINE__ associativity didSet get infix inout left mutating none nonmutating operator override postfix precedence prefix right set unowned unowned safe unsafe weak willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue assert bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal false filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced join lexicographicalCompare map max maxElement min minElement nil numericCast partition posix print println quickSort reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith strideof strideofValue swap swift toString transcode true underestimateCount unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafePointers withVaList"},t={cN:"type",b:"\\b[A-Z][\\w']*",r:0},n=e.C("/\\*","\\*/",{c:["self"]}),r={cN:"subst",b:/\\\(/,e:"\\)",k:i,c:[]},s={cN:"number",b:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",r:0},o=e.inherit(e.QSM,{c:[r,e.BE]});return r.c=[s],{k:i,c:[o,e.CLCM,n,t,s,{cN:"func",bK:"func",e:"{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/,i:/\(/}),{cN:"generics",b:/</,e:/>/,i:/>/},{cN:"params",b:/\(/,e:/\)/,endsParent:!0,k:i,c:["self",s,o,e.CBCM,{b:":"}],i:/["']/}],i:/\[|%/},{cN:"class",bK:"struct protocol class extension enum",k:i,e:"\\{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/})]},{cN:"preprocessor",b:"(@assignment|@class_protocol|@exported|@final|@lazy|@noreturn|@NSCopying|@NSManaged|@objc|@optional|@required|@auto_closure|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix)"}]}});hljs.registerLanguage("lisp",function(b){var e="[a-zA-Z_\\-\\+\\*\\/\\<\\=\\>\\&\\#][a-zA-Z0-9_\\-\\+\\*\\/\\<\\=\\>\\&\\#!]*",c="\\|[^]*?\\|",r="(\\-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|\\-)?\\d+)?",a={cN:"shebang",b:"^#!",e:"$"},i={cN:"literal",b:"\\b(t{1}|nil)\\b"},l={cN:"number",v:[{b:r,r:0},{b:"#(b|B)[0-1]+(/[0-1]+)?"},{b:"#(o|O)[0-7]+(/[0-7]+)?"},{b:"#(x|X)[0-9a-fA-F]+(/[0-9a-fA-F]+)?"},{b:"#(c|C)\\("+r+" +"+r,e:"\\)"}]},t=b.inherit(b.QSM,{i:null}),d=b.C(";","$",{r:0}),n={cN:"variable",b:"\\*",e:"\\*"},u={cN:"keyword",b:"[:&]"+e},N={b:e,r:0},o={b:c},s={b:"\\(",e:"\\)",c:["self",i,t,l,N]},v={cN:"quoted",c:[l,t,n,u,s,N],v:[{b:"['`]\\(",e:"\\)"},{b:"\\(quote ",e:"\\)",k:"quote"},{b:"'"+c}]},f={cN:"quoted",v:[{b:"'"+e},{b:"#'"+e+"(::"+e+")*"}]},g={cN:"list",b:"\\(\\s*",e:"\\)"},q={eW:!0,r:0};return g.c=[{cN:"keyword",v:[{b:e},{b:c}]},q],q.c=[v,f,g,i,l,t,d,n,u,o,N],{i:/\S/,c:[l,a,i,t,d,v,f,g,N]}});hljs.registerLanguage("rsl",function(e){return{k:{keyword:"float color point normal vector matrix while for if do return else break extern continue",built_in:"abs acos ambient area asin atan atmosphere attribute calculatenormal ceil cellnoise clamp comp concat cos degrees depth Deriv diffuse distance Du Dv environment exp faceforward filterstep floor format fresnel incident length lightsource log match max min mod noise normalize ntransform opposite option phong pnoise pow printf ptlined radians random reflect refract renderinfo round setcomp setxcomp setycomp setzcomp shadow sign sin smoothstep specular specularbrdf spline sqrt step tan texture textureinfo trace transform vtransform xcomp ycomp zcomp"},i:"</",c:[e.CLCM,e.CBCM,e.QSM,e.ASM,e.CNM,{cN:"preprocessor",b:"#",e:"$"},{cN:"shader",bK:"surface displacement light volume imager",e:"\\("},{cN:"shading",bK:"illuminate illuminance gather",e:"\\("}]}});hljs.registerLanguage("scheme",function(e){var t="[^\\(\\)\\[\\]\\{\\}\",'`;#|\\\\\\s]+",r="(\\-|\\+)?\\d+([./]\\d+)?",i=r+"[+\\-]"+r+"i",a={built_in:"case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules ' * + , ,@ - ... / ; < <= = => > >= ` abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci<? char-ci=? char-ci>=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char<? char=? char>=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string<? string=? string>=? string>? string? substring symbol->string symbol? tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero?"},n={cN:"shebang",b:"^#!",e:"$"},c={cN:"literal",b:"(#t|#f|#\\\\"+t+"|#\\\\.)"},l={cN:"number",v:[{b:r,r:0},{b:i,r:0},{b:"#b[0-1]+(/[0-1]+)?"},{b:"#o[0-7]+(/[0-7]+)?"},{b:"#x[0-9a-f]+(/[0-9a-f]+)?"}]},s=e.QSM,o=[e.C(";","$",{r:0}),e.C("#\\|","\\|#")],u={b:t,r:0},p={cN:"variable",b:"'"+t},d={eW:!0,r:0},g={cN:"list",v:[{b:"\\(",e:"\\)"},{b:"\\[",e:"\\]"}],c:[{cN:"keyword",b:t,l:t,k:a},d]};return d.c=[c,l,s,u,p,g].concat(o),{i:/\S/,c:[n,l,s,p,g].concat(o)}});hljs.registerLanguage("stata",function(e){return{aliases:["do","ado"],cI:!0,k:"if else in foreach for forv forva forval forvalu forvalue forvalues by bys bysort xi quietly qui capture about ac ac_7 acprplot acprplot_7 adjust ado adopath adoupdate alpha ameans an ano anov anova anova_estat anova_terms anovadef aorder ap app appe appen append arch arch_dr arch_estat arch_p archlm areg areg_p args arima arima_dr arima_estat arima_p as asmprobit asmprobit_estat asmprobit_lf asmprobit_mfx__dlg asmprobit_p ass asse asser assert avplot avplot_7 avplots avplots_7 bcskew0 bgodfrey binreg bip0_lf biplot bipp_lf bipr_lf bipr_p biprobit bitest bitesti bitowt blogit bmemsize boot bootsamp bootstrap bootstrap_8 boxco_l boxco_p boxcox boxcox_6 boxcox_p bprobit br break brier bro brow brows browse brr brrstat bs bs_7 bsampl_w bsample bsample_7 bsqreg bstat bstat_7 bstat_8 bstrap bstrap_7 ca ca_estat ca_p cabiplot camat canon canon_8 canon_8_p canon_estat canon_p cap caprojection capt captu captur capture cat cc cchart cchart_7 cci cd censobs_table centile cf char chdir checkdlgfiles checkestimationsample checkhlpfiles checksum chelp ci cii cl class classutil clear cli clis clist clo clog clog_lf clog_p clogi clogi_sw clogit clogit_lf clogit_p clogitp clogl_sw cloglog clonevar clslistarray cluster cluster_measures cluster_stop cluster_tree cluster_tree_8 clustermat cmdlog cnr cnre cnreg cnreg_p cnreg_sw cnsreg codebook collaps4 collapse colormult_nb colormult_nw compare compress conf confi confir confirm conren cons const constr constra constrai constrain constraint continue contract copy copyright copysource cor corc corr corr2data corr_anti corr_kmo corr_smc corre correl correla correlat correlate corrgram cou coun count cox cox_p cox_sw coxbase coxhaz coxvar cprplot cprplot_7 crc cret cretu cretur creturn cross cs cscript cscript_log csi ct ct_is ctset ctst_5 ctst_st cttost cumsp cumsp_7 cumul cusum cusum_7 cutil d datasig datasign datasigna datasignat datasignatu datasignatur datasignature datetof db dbeta de dec deco decod decode deff des desc descr descri describ describe destring dfbeta dfgls dfuller di di_g dir dirstats dis discard disp disp_res disp_s displ displa display distinct do doe doed doedi doedit dotplot dotplot_7 dprobit drawnorm drop ds ds_util dstdize duplicates durbina dwstat dydx e ed edi edit egen eivreg emdef en enc enco encod encode eq erase ereg ereg_lf ereg_p ereg_sw ereghet ereghet_glf ereghet_glf_sh ereghet_gp ereghet_ilf ereghet_ilf_sh ereghet_ip eret eretu eretur ereturn err erro error est est_cfexist est_cfname est_clickable est_expand est_hold est_table est_unhold est_unholdok estat estat_default estat_summ estat_vce_only esti estimates etodow etof etomdy ex exi exit expand expandcl fac fact facto factor factor_estat factor_p factor_pca_rotated factor_rotate factormat fcast fcast_compute fcast_graph fdades fdadesc fdadescr fdadescri fdadescrib fdadescribe fdasav fdasave fdause fh_st file open file read file close file filefilter fillin find_hlp_file findfile findit findit_7 fit fl fli flis flist for5_0 form forma format fpredict frac_154 frac_adj frac_chk frac_cox frac_ddp frac_dis frac_dv frac_in frac_mun frac_pp frac_pq frac_pv frac_wgt frac_xo fracgen fracplot fracplot_7 fracpoly fracpred fron_ex fron_hn fron_p fron_tn fron_tn2 frontier ftodate ftoe ftomdy ftowdate g gamhet_glf gamhet_gp gamhet_ilf gamhet_ip gamma gamma_d2 gamma_p gamma_sw gammahet gdi_hexagon gdi_spokes ge gen gene gener genera generat generate genrank genstd genvmean gettoken gl gladder gladder_7 glim_l01 glim_l02 glim_l03 glim_l04 glim_l05 glim_l06 glim_l07 glim_l08 glim_l09 glim_l10 glim_l11 glim_l12 glim_lf glim_mu glim_nw1 glim_nw2 glim_nw3 glim_p glim_v1 glim_v2 glim_v3 glim_v4 glim_v5 glim_v6 glim_v7 glm glm_6 glm_p glm_sw glmpred glo glob globa global glogit glogit_8 glogit_p gmeans gnbre_lf gnbreg gnbreg_5 gnbreg_p gomp_lf gompe_sw gomper_p gompertz gompertzhet gomphet_glf gomphet_glf_sh gomphet_gp gomphet_ilf gomphet_ilf_sh gomphet_ip gphdot gphpen gphprint gprefs gprobi_p gprobit gprobit_8 gr gr7 gr_copy gr_current gr_db gr_describe gr_dir gr_draw gr_draw_replay gr_drop gr_edit gr_editviewopts gr_example gr_example2 gr_export gr_print gr_qscheme gr_query gr_read gr_rename gr_replay gr_save gr_set gr_setscheme gr_table gr_undo gr_use graph graph7 grebar greigen greigen_7 greigen_8 grmeanby grmeanby_7 gs_fileinfo gs_filetype gs_graphinfo gs_stat gsort gwood h hadimvo hareg hausman haver he heck_d2 heckma_p heckman heckp_lf heckpr_p heckprob hel help hereg hetpr_lf hetpr_p hetprob hettest hexdump hilite hist hist_7 histogram hlogit hlu hmeans hotel hotelling hprobit hreg hsearch icd9 icd9_ff icd9p iis impute imtest inbase include inf infi infil infile infix inp inpu input ins insheet insp inspe inspec inspect integ inten intreg intreg_7 intreg_p intrg2_ll intrg_ll intrg_ll2 ipolate iqreg ir irf irf_create irfm iri is_svy is_svysum isid istdize ivprob_1_lf ivprob_lf ivprobit ivprobit_p ivreg ivreg_footnote ivtob_1_lf ivtob_lf ivtobit ivtobit_p jackknife jacknife jknife jknife_6 jknife_8 jkstat joinby kalarma1 kap kap_3 kapmeier kappa kapwgt kdensity kdensity_7 keep ksm ksmirnov ktau kwallis l la lab labe label labelbook ladder levels levelsof leverage lfit lfit_p li lincom line linktest lis list lloghet_glf lloghet_glf_sh lloghet_gp lloghet_ilf lloghet_ilf_sh lloghet_ip llogi_sw llogis_p llogist llogistic llogistichet lnorm_lf lnorm_sw lnorma_p lnormal lnormalhet lnormhet_glf lnormhet_glf_sh lnormhet_gp lnormhet_ilf lnormhet_ilf_sh lnormhet_ip lnskew0 loadingplot loc loca local log logi logis_lf logistic logistic_p logit logit_estat logit_p loglogs logrank loneway lookfor lookup lowess lowess_7 lpredict lrecomp lroc lroc_7 lrtest ls lsens lsens_7 lsens_x lstat ltable ltable_7 ltriang lv lvr2plot lvr2plot_7 m ma mac macr macro makecns man manova manova_estat manova_p manovatest mantel mark markin markout marksample mat mat_capp mat_order mat_put_rr mat_rapp mata mata_clear mata_describe mata_drop mata_matdescribe mata_matsave mata_matuse mata_memory mata_mlib mata_mosave mata_rename mata_which matalabel matcproc matlist matname matr matri matrix matrix_input__dlg matstrik mcc mcci md0_ md1_ md1debug_ md2_ md2debug_ mds mds_estat mds_p mdsconfig mdslong mdsmat mdsshepard mdytoe mdytof me_derd mean means median memory memsize meqparse mer merg merge mfp mfx mhelp mhodds minbound mixed_ll mixed_ll_reparm mkassert mkdir mkmat mkspline ml ml_5 ml_adjs ml_bhhhs ml_c_d ml_check ml_clear ml_cnt ml_debug ml_defd ml_e0 ml_e0_bfgs ml_e0_cycle ml_e0_dfp ml_e0i ml_e1 ml_e1_bfgs ml_e1_bhhh ml_e1_cycle ml_e1_dfp ml_e2 ml_e2_cycle ml_ebfg0 ml_ebfr0 ml_ebfr1 ml_ebh0q ml_ebhh0 ml_ebhr0 ml_ebr0i ml_ecr0i ml_edfp0 ml_edfr0 ml_edfr1 ml_edr0i ml_eds ml_eer0i ml_egr0i ml_elf ml_elf_bfgs ml_elf_bhhh ml_elf_cycle ml_elf_dfp ml_elfi ml_elfs ml_enr0i ml_enrr0 ml_erdu0 ml_erdu0_bfgs ml_erdu0_bhhh ml_erdu0_bhhhq ml_erdu0_cycle ml_erdu0_dfp ml_erdu0_nrbfgs ml_exde ml_footnote ml_geqnr ml_grad0 ml_graph ml_hbhhh ml_hd0 ml_hold ml_init ml_inv ml_log ml_max ml_mlout ml_mlout_8 ml_model ml_nb0 ml_opt ml_p ml_plot ml_query ml_rdgrd ml_repor ml_s_e ml_score ml_searc ml_technique ml_unhold mleval mlf_ mlmatbysum mlmatsum mlog mlogi mlogit mlogit_footnote mlogit_p mlopts mlsum mlvecsum mnl0_ mor more mov move mprobit mprobit_lf mprobit_p mrdu0_ mrdu1_ mvdecode mvencode mvreg mvreg_estat n nbreg nbreg_al nbreg_lf nbreg_p nbreg_sw nestreg net newey newey_7 newey_p news nl nl_7 nl_9 nl_9_p nl_p nl_p_7 nlcom nlcom_p nlexp2 nlexp2_7 nlexp2a nlexp2a_7 nlexp3 nlexp3_7 nlgom3 nlgom3_7 nlgom4 nlgom4_7 nlinit nllog3 nllog3_7 nllog4 nllog4_7 nlog_rd nlogit nlogit_p nlogitgen nlogittree nlpred no nobreak noi nois noisi noisil noisily note notes notes_dlg nptrend numlabel numlist odbc old_ver olo olog ologi ologi_sw ologit ologit_p ologitp on one onew onewa oneway op_colnm op_comp op_diff op_inv op_str opr opro oprob oprob_sw oprobi oprobi_p oprobit oprobitp opts_exclusive order orthog orthpoly ou out outf outfi outfil outfile outs outsh outshe outshee outsheet ovtest pac pac_7 palette parse parse_dissim pause pca pca_8 pca_display pca_estat pca_p pca_rotate pcamat pchart pchart_7 pchi pchi_7 pcorr pctile pentium pergram pergram_7 permute permute_8 personal peto_st pkcollapse pkcross pkequiv pkexamine pkexamine_7 pkshape pksumm pksumm_7 pl plo plot plugin pnorm pnorm_7 poisgof poiss_lf poiss_sw poisso_p poisson poisson_estat post postclose postfile postutil pperron pr prais prais_e prais_e2 prais_p predict predictnl preserve print pro prob probi probit probit_estat probit_p proc_time procoverlay procrustes procrustes_estat procrustes_p profiler prog progr progra program prop proportion prtest prtesti pwcorr pwd q\\s qby qbys qchi qchi_7 qladder qladder_7 qnorm qnorm_7 qqplot qqplot_7 qreg qreg_c qreg_p qreg_sw qu quadchk quantile quantile_7 que quer query range ranksum ratio rchart rchart_7 rcof recast reclink recode reg reg3 reg3_p regdw regr regre regre_p2 regres regres_p regress regress_estat regriv_p remap ren rena renam rename renpfix repeat replace report reshape restore ret retu retur return rm rmdir robvar roccomp roccomp_7 roccomp_8 rocf_lf rocfit rocfit_8 rocgold rocplot rocplot_7 roctab roctab_7 rolling rologit rologit_p rot rota rotat rotate rotatemat rreg rreg_p ru run runtest rvfplot rvfplot_7 rvpplot rvpplot_7 sa safesum sample sampsi sav save savedresults saveold sc sca scal scala scalar scatter scm_mine sco scob_lf scob_p scobi_sw scobit scor score scoreplot scoreplot_help scree screeplot screeplot_help sdtest sdtesti se search separate seperate serrbar serrbar_7 serset set set_defaults sfrancia sh she shel shell shewhart shewhart_7 signestimationsample signrank signtest simul simul_7 simulate simulate_8 sktest sleep slogit slogit_d2 slogit_p smooth snapspan so sor sort spearman spikeplot spikeplot_7 spikeplt spline_x split sqreg sqreg_p sret sretu sretur sreturn ssc st st_ct st_hc st_hcd st_hcd_sh st_is st_issys st_note st_promo st_set st_show st_smpl st_subid stack statsby statsby_8 stbase stci stci_7 stcox stcox_estat stcox_fr stcox_fr_ll stcox_p stcox_sw stcoxkm stcoxkm_7 stcstat stcurv stcurve stcurve_7 stdes stem stepwise stereg stfill stgen stir stjoin stmc stmh stphplot stphplot_7 stphtest stphtest_7 stptime strate strate_7 streg streg_sw streset sts sts_7 stset stsplit stsum sttocc sttoct stvary stweib su suest suest_8 sum summ summa summar summari summariz summarize sunflower sureg survcurv survsum svar svar_p svmat svy svy_disp svy_dreg svy_est svy_est_7 svy_estat svy_get svy_gnbreg_p svy_head svy_header svy_heckman_p svy_heckprob_p svy_intreg_p svy_ivreg_p svy_logistic_p svy_logit_p svy_mlogit_p svy_nbreg_p svy_ologit_p svy_oprobit_p svy_poisson_p svy_probit_p svy_regress_p svy_sub svy_sub_7 svy_x svy_x_7 svy_x_p svydes svydes_8 svygen svygnbreg svyheckman svyheckprob svyintreg svyintreg_7 svyintrg svyivreg svylc svylog_p svylogit svymarkout svymarkout_8 svymean svymlog svymlogit svynbreg svyolog svyologit svyoprob svyoprobit svyopts svypois svypois_7 svypoisson svyprobit svyprobt svyprop svyprop_7 svyratio svyreg svyreg_p svyregress svyset svyset_7 svyset_8 svytab svytab_7 svytest svytotal sw sw_8 swcnreg swcox swereg swilk swlogis swlogit swologit swoprbt swpois swprobit swqreg swtobit swweib symmetry symmi symplot symplot_7 syntax sysdescribe sysdir sysuse szroeter ta tab tab1 tab2 tab_or tabd tabdi tabdis tabdisp tabi table tabodds tabodds_7 tabstat tabu tabul tabula tabulat tabulate te tempfile tempname tempvar tes test testnl testparm teststd tetrachoric time_it timer tis tob tobi tobit tobit_p tobit_sw token tokeni tokeniz tokenize tostring total translate translator transmap treat_ll treatr_p treatreg trim trnb_cons trnb_mean trpoiss_d2 trunc_ll truncr_p truncreg tsappend tset tsfill tsline tsline_ex tsreport tsrevar tsrline tsset tssmooth tsunab ttest ttesti tut_chk tut_wait tutorial tw tware_st two twoway twoway__fpfit_serset twoway__function_gen twoway__histogram_gen twoway__ipoint_serset twoway__ipoints_serset twoway__kdensity_gen twoway__lfit_serset twoway__normgen_gen twoway__pci_serset twoway__qfit_serset twoway__scatteri_serset twoway__sunflower_gen twoway_ksm_serset ty typ type typeof u unab unabbrev unabcmd update us use uselabel var var_mkcompanion var_p varbasic varfcast vargranger varirf varirf_add varirf_cgraph varirf_create varirf_ctable varirf_describe varirf_dir varirf_drop varirf_erase varirf_graph varirf_ograph varirf_rename varirf_set varirf_table varlist varlmar varnorm varsoc varstable varstable_w varstable_w2 varwle vce vec vec_fevd vec_mkphi vec_p vec_p_w vecirf_create veclmar veclmar_w vecnorm vecnorm_w vecrank vecstable verinst vers versi versio version view viewsource vif vwls wdatetof webdescribe webseek webuse weib1_lf weib2_lf weib_lf weib_lf0 weibhet_glf weibhet_glf_sh weibhet_glfa weibhet_glfa_sh weibhet_gp weibhet_ilf weibhet_ilf_sh weibhet_ilfa weibhet_ilfa_sh weibhet_ip weibu_sw weibul_p weibull weibull_c weibull_s weibullhet wh whelp whi which whil while wilc_st wilcoxon win wind windo window winexec wntestb wntestb_7 wntestq xchart xchart_7 xcorr xcorr_7 xi xi_6 xmlsav xmlsave xmluse xpose xsh xshe xshel xshell xt_iis xt_tis xtab_p xtabond xtbin_p xtclog xtcloglog xtcloglog_8 xtcloglog_d2 xtcloglog_pa_p xtcloglog_re_p xtcnt_p xtcorr xtdata xtdes xtfront_p xtfrontier xtgee xtgee_elink xtgee_estat xtgee_makeivar xtgee_p xtgee_plink xtgls xtgls_p xthaus xthausman xtht_p xthtaylor xtile xtint_p xtintreg xtintreg_8 xtintreg_d2 xtintreg_p xtivp_1 xtivp_2 xtivreg xtline xtline_ex xtlogit xtlogit_8 xtlogit_d2 xtlogit_fe_p xtlogit_pa_p xtlogit_re_p xtmixed xtmixed_estat xtmixed_p xtnb_fe xtnb_lf xtnbreg xtnbreg_pa_p xtnbreg_refe_p xtpcse xtpcse_p xtpois xtpoisson xtpoisson_d2 xtpoisson_pa_p xtpoisson_refe_p xtpred xtprobit xtprobit_8 xtprobit_d2 xtprobit_re_p xtps_fe xtps_lf xtps_ren xtps_ren_8 xtrar_p xtrc xtrc_p xtrchh xtrefe_p xtreg xtreg_be xtreg_fe xtreg_ml xtreg_pa_p xtreg_re xtregar xtrere_p xtset xtsf_ll xtsf_llti xtsum xttab xttest0 xttobit xttobit_8 xttobit_p xttrans yx yxview__barlike_draw yxview_area_draw yxview_bar_draw yxview_dot_draw yxview_dropline_draw yxview_function_draw yxview_iarrow_draw yxview_ilabels_draw yxview_normal_draw yxview_pcarrow_draw yxview_pcbarrow_draw yxview_pccapsym_draw yxview_pcscatter_draw yxview_pcspike_draw yxview_rarea_draw yxview_rbar_draw yxview_rbarm_draw yxview_rcap_draw yxview_rcapsym_draw yxview_rconnected_draw yxview_rline_draw yxview_rscatter_draw yxview_rspike_draw yxview_spike_draw yxview_sunflower_draw zap_s zinb zinb_llf zinb_plf zip zip_llf zip_p zip_plf zt_ct_5 zt_hc_5 zt_hcd_5 zt_is_5 zt_iss_5 zt_sho_5 zt_smp_5 ztbase_5 ztcox_5 ztdes_5 ztereg_5 ztfill_5 ztgen_5 ztir_5 ztjoin_5 ztnb ztnb_p ztp ztp_p zts_5 ztset_5 ztspli_5 ztsum_5 zttoct_5 ztvary_5 ztweib_5",c:[{cN:"label",v:[{b:"\\$\\{?[a-zA-Z0-9_]+\\}?"},{b:"`[a-zA-Z0-9_]+'"}]},{cN:"string",v:[{b:'`"[^\r\n]*?"\''},{b:'"[^\r\n"]*"'}]},{cN:"literal",v:[{b:"\\b(abs|acos|asin|atan|atan2|atanh|ceil|cloglog|comb|cos|digamma|exp|floor|invcloglog|invlogit|ln|lnfact|lnfactorial|lngamma|log|log10|max|min|mod|reldif|round|sign|sin|sqrt|sum|tan|tanh|trigamma|trunc|betaden|Binomial|binorm|binormal|chi2|chi2tail|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|F|Fden|Ftail|gammaden|gammap|ibeta|invbinomial|invchi2|invchi2tail|invF|invFtail|invgammap|invibeta|invnchi2|invnFtail|invnibeta|invnorm|invnormal|invttail|nbetaden|nchi2|nFden|nFtail|nibeta|norm|normal|normalden|normd|npnchi2|tden|ttail|uniform|abbrev|char|index|indexnot|length|lower|ltrim|match|plural|proper|real|regexm|regexr|regexs|reverse|rtrim|string|strlen|strlower|strltrim|strmatch|strofreal|strpos|strproper|strreverse|strrtrim|strtrim|strupper|subinstr|subinword|substr|trim|upper|word|wordcount|_caller|autocode|byteorder|chop|clip|cond|e|epsdouble|epsfloat|group|inlist|inrange|irecode|matrix|maxbyte|maxdouble|maxfloat|maxint|maxlong|mi|minbyte|mindouble|minfloat|minint|minlong|missing|r|recode|replay|return|s|scalar|d|date|day|dow|doy|halfyear|mdy|month|quarter|week|year|d|daily|dofd|dofh|dofm|dofq|dofw|dofy|h|halfyearly|hofd|m|mofd|monthly|q|qofd|quarterly|tin|twithin|w|weekly|wofd|y|yearly|yh|ym|yofd|yq|yw|cholesky|colnumb|colsof|corr|det|diag|diag0cnt|el|get|hadamard|I|inv|invsym|issym|issymmetric|J|matmissing|matuniform|mreldif|nullmat|rownumb|rowsof|sweep|syminv|trace|vec|vecdiag)(?=\\(|$)"}]},e.C("^[ ]*\\*.*$",!1),e.CLCM,e.CBCM]}});hljs.registerLanguage("asciidoc",function(e){return{aliases:["adoc"],c:[e.C("^/{4,}\\n","\\n/{4,}$",{r:10}),e.C("^//","$",{r:0}),{cN:"title",b:"^\\.\\w.*$"},{b:"^[=\\*]{4,}\\n",e:"\\n^[=\\*]{4,}$",r:10},{cN:"header",b:"^(={1,5}) .+?( \\1)?$",r:10},{cN:"header",b:"^[^\\[\\]\\n]+?\\n[=\\-~\\^\\+]{2,}$",r:10},{cN:"attribute",b:"^:.+?:",e:"\\s",eE:!0,r:10},{cN:"attribute",b:"^\\[.+?\\]$",r:0},{cN:"blockquote",b:"^_{4,}\\n",e:"\\n_{4,}$",r:10},{cN:"code",b:"^[\\-\\.]{4,}\\n",e:"\\n[\\-\\.]{4,}$",r:10},{b:"^\\+{4,}\\n",e:"\\n\\+{4,}$",c:[{b:"<",e:">",sL:"xml",r:0}],r:10},{cN:"bullet",b:"^(\\*+|\\-+|\\.+|[^\\n]+?::)\\s+"},{cN:"label",b:"^(NOTE|TIP|IMPORTANT|WARNING|CAUTION):\\s+",r:10},{cN:"strong",b:"\\B\\*(?![\\*\\s])",e:"(\\n{2}|\\*)",c:[{b:"\\\\*\\w",r:0}]},{cN:"emphasis",b:"\\B'(?!['\\s])",e:"(\\n{2}|')",c:[{b:"\\\\'\\w",r:0}],r:0},{cN:"emphasis",b:"_(?![_\\s])",e:"(\\n{2}|_)",r:0},{cN:"smartquote",v:[{b:"``.+?''"},{b:"`.+?'"}]},{cN:"code",b:"(`.+?`|\\+.+?\\+)",r:0},{cN:"code",b:"^[ \\t]",e:"$",r:0},{cN:"horizontal_rule",b:"^'{3,}[ \\t]*$",r:10},{b:"(link:)?(http|https|ftp|file|irc|image:?):\\S+\\[.*?\\]",rB:!0,c:[{b:"(link|image:?):",r:0},{cN:"link_url",b:"\\w",e:"[^\\[]+",r:0},{cN:"link_label",b:"\\[",e:"\\]",eB:!0,eE:!0,r:0}],r:10}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"preprocessor",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},i]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},i,c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}});hljs.registerLanguage("java",function(e){var a=e.UIR+"(<"+e.UIR+">)?",t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",c="(\\b(0b[01_]+)|\\b0[xX][a-fA-F0-9_]+|(\\b[\\d_]+(\\.[\\d_]*)?|\\.[\\d_]+)([eE][-+]?\\d+)?)[lLfF]?",r={cN:"number",b:c,r:0};return{aliases:["jsp"],k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return",r:0},{cN:"function",b:"("+a+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},r,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("glsl",function(e){return{k:{keyword:"atomic_uint attribute bool break bvec2 bvec3 bvec4 case centroid coherent const continue default discard dmat2 dmat2x2 dmat2x3 dmat2x4 dmat3 dmat3x2 dmat3x3 dmat3x4 dmat4 dmat4x2 dmat4x3 dmat4x4 do double dvec2 dvec3 dvec4 else flat float for highp if iimage1D iimage1DArray iimage2D iimage2DArray iimage2DMS iimage2DMSArray iimage2DRect iimage3D iimageBuffer iimageCube iimageCubeArray image1D image1DArray image2D image2DArray image2DMS image2DMSArray image2DRect image3D imageBuffer imageCube imageCubeArray in inout int invariant isampler1D isampler1DArray isampler2D isampler2DArray isampler2DMS isampler2DMSArray isampler2DRect isampler3D isamplerBuffer isamplerCube isamplerCubeArray ivec2 ivec3 ivec4 layout lowp mat2 mat2x2 mat2x3 mat2x4 mat3 mat3x2 mat3x3 mat3x4 mat4 mat4x2 mat4x3 mat4x4 mediump noperspective out patch precision readonly restrict return sample sampler1D sampler1DArray sampler1DArrayShadow sampler1DShadow sampler2D sampler2DArray sampler2DArrayShadow sampler2DMS sampler2DMSArray sampler2DRect sampler2DRectShadow sampler2DShadow sampler3D samplerBuffer samplerCube samplerCubeArray samplerCubeArrayShadow samplerCubeShadow smooth struct subroutine switch uimage1D uimage1DArray uimage2D uimage2DArray uimage2DMS uimage2DMSArray uimage2DRect uimage3D uimageBuffer uimageCube uimageCubeArray uint uniform usampler1D usampler1DArray usampler2D usampler2DArray usampler2DMS usampler2DMSArray usampler2DRect usampler3D usamplerBuffer usamplerCube usamplerCubeArray uvec2 uvec3 uvec4 varying vec2 vec3 vec4 void volatile while writeonly",built_in:"gl_BackColor gl_BackLightModelProduct gl_BackLightProduct gl_BackMaterial gl_BackSecondaryColor gl_ClipDistance gl_ClipPlane gl_ClipVertex gl_Color gl_DepthRange gl_EyePlaneQ gl_EyePlaneR gl_EyePlaneS gl_EyePlaneT gl_Fog gl_FogCoord gl_FogFragCoord gl_FragColor gl_FragCoord gl_FragData gl_FragDepth gl_FrontColor gl_FrontFacing gl_FrontLightModelProduct gl_FrontLightProduct gl_FrontMaterial gl_FrontSecondaryColor gl_InstanceID gl_InvocationID gl_Layer gl_LightModel gl_LightSource gl_MaxAtomicCounterBindings gl_MaxAtomicCounterBufferSize gl_MaxClipDistances gl_MaxClipPlanes gl_MaxCombinedAtomicCounterBuffers gl_MaxCombinedAtomicCounters gl_MaxCombinedImageUniforms gl_MaxCombinedImageUnitsAndFragmentOutputs gl_MaxCombinedTextureImageUnits gl_MaxDrawBuffers gl_MaxFragmentAtomicCounterBuffers gl_MaxFragmentAtomicCounters gl_MaxFragmentImageUniforms gl_MaxFragmentInputComponents gl_MaxFragmentUniformComponents gl_MaxFragmentUniformVectors gl_MaxGeometryAtomicCounterBuffers gl_MaxGeometryAtomicCounters gl_MaxGeometryImageUniforms gl_MaxGeometryInputComponents gl_MaxGeometryOutputComponents gl_MaxGeometryOutputVertices gl_MaxGeometryTextureImageUnits gl_MaxGeometryTotalOutputComponents gl_MaxGeometryUniformComponents gl_MaxGeometryVaryingComponents gl_MaxImageSamples gl_MaxImageUnits gl_MaxLights gl_MaxPatchVertices gl_MaxProgramTexelOffset gl_MaxTessControlAtomicCounterBuffers gl_MaxTessControlAtomicCounters gl_MaxTessControlImageUniforms gl_MaxTessControlInputComponents gl_MaxTessControlOutputComponents gl_MaxTessControlTextureImageUnits gl_MaxTessControlTotalOutputComponents gl_MaxTessControlUniformComponents gl_MaxTessEvaluationAtomicCounterBuffers gl_MaxTessEvaluationAtomicCounters gl_MaxTessEvaluationImageUniforms gl_MaxTessEvaluationInputComponents gl_MaxTessEvaluationOutputComponents gl_MaxTessEvaluationTextureImageUnits gl_MaxTessEvaluationUniformComponents gl_MaxTessGenLevel gl_MaxTessPatchComponents gl_MaxTextureCoords gl_MaxTextureImageUnits gl_MaxTextureUnits gl_MaxVaryingComponents gl_MaxVaryingFloats gl_MaxVaryingVectors gl_MaxVertexAtomicCounterBuffers gl_MaxVertexAtomicCounters gl_MaxVertexAttribs gl_MaxVertexImageUniforms gl_MaxVertexOutputComponents gl_MaxVertexTextureImageUnits gl_MaxVertexUniformComponents gl_MaxVertexUniformVectors gl_MaxViewports gl_MinProgramTexelOffsetgl_ModelViewMatrix gl_ModelViewMatrixInverse gl_ModelViewMatrixInverseTranspose gl_ModelViewMatrixTranspose gl_ModelViewProjectionMatrix gl_ModelViewProjectionMatrixInverse gl_ModelViewProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixTranspose gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_Normal gl_NormalMatrix gl_NormalScale gl_ObjectPlaneQ gl_ObjectPlaneR gl_ObjectPlaneS gl_ObjectPlaneT gl_PatchVerticesIn gl_PerVertex gl_Point gl_PointCoord gl_PointSize gl_Position gl_PrimitiveID gl_PrimitiveIDIn gl_ProjectionMatrix gl_ProjectionMatrixInverse gl_ProjectionMatrixInverseTranspose gl_ProjectionMatrixTranspose gl_SampleID gl_SampleMask gl_SampleMaskIn gl_SamplePosition gl_SecondaryColor gl_TessCoord gl_TessLevelInner gl_TessLevelOuter gl_TexCoord gl_TextureEnvColor gl_TextureMatrixInverseTranspose gl_TextureMatrixTranspose gl_Vertex gl_VertexID gl_ViewportIndex gl_in gl_out EmitStreamVertex EmitVertex EndPrimitive EndStreamPrimitive abs acos acosh all any asin asinh atan atanh atomicCounter atomicCounterDecrement atomicCounterIncrement barrier bitCount bitfieldExtract bitfieldInsert bitfieldReverse ceil clamp cos cosh cross dFdx dFdy degrees determinant distance dot equal exp exp2 faceforward findLSB findMSB floatBitsToInt floatBitsToUint floor fma fract frexp ftransform fwidth greaterThan greaterThanEqual imageAtomicAdd imageAtomicAnd imageAtomicCompSwap imageAtomicExchange imageAtomicMax imageAtomicMin imageAtomicOr imageAtomicXor imageLoad imageStore imulExtended intBitsToFloat interpolateAtCentroid interpolateAtOffset interpolateAtSample inverse inversesqrt isinf isnan ldexp length lessThan lessThanEqual log log2 matrixCompMult max memoryBarrier min mix mod modf noise1 noise2 noise3 noise4 normalize not notEqual outerProduct packDouble2x32 packHalf2x16 packSnorm2x16 packSnorm4x8 packUnorm2x16 packUnorm4x8 pow radians reflect refract round roundEven shadow1D shadow1DLod shadow1DProj shadow1DProjLod shadow2D shadow2DLod shadow2DProj shadow2DProjLod sign sin sinh smoothstep sqrt step tan tanh texelFetch texelFetchOffset texture texture1D texture1DLod texture1DProj texture1DProjLod texture2D texture2DLod texture2DProj texture2DProjLod texture3D texture3DLod texture3DProj texture3DProjLod textureCube textureCubeLod textureGather textureGatherOffset textureGatherOffsets textureGrad textureGradOffset textureLod textureLodOffset textureOffset textureProj textureProjGrad textureProjGradOffset textureProjLod textureProjLodOffset textureProjOffset textureQueryLod textureSize transpose trunc uaddCarry uintBitsToFloat umulExtended unpackDouble2x32 unpackHalf2x16 unpackSnorm2x16 unpackSnorm4x8 unpackUnorm2x16 unpackUnorm4x8 usubBorrow gl_TextureMatrix gl_TextureMatrixInverse",literal:"true false"},i:'"',c:[e.CLCM,e.CBCM,e.CNM,{cN:"preprocessor",b:"#",e:"$"}]}});hljs.registerLanguage("lua",function(e){var t="\\[=*\\[",a="\\]=*\\]",r={b:t,e:a,c:["self"]},n=[e.C("--(?!"+t+")","$"),e.C("--"+t,a,{c:[r],r:10})];return{l:e.UIR,k:{keyword:"and break do else elseif end false for if in local nil not or repeat return then true until while",built_in:"_G _VERSION assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall coroutine debug io math os package string table"},c:n.concat([{cN:"function",bK:"function",e:"\\)",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{cN:"params",b:"\\(",eW:!0,c:n}].concat(n)},e.CNM,e.ASM,e.QSM,{cN:"string",b:t,e:a,c:[r],r:5}])}});hljs.registerLanguage("protobuf",function(e){return{k:{keyword:"package import option optional required repeated group",built_in:"double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes",literal:"true false"},c:[e.QSM,e.NM,e.CLCM,{cN:"class",bK:"message enum service",e:/\{/,i:/\n/,c:[e.inherit(e.TM,{starts:{eW:!0,eE:!0}})]},{cN:"function",bK:"rpc",e:/;/,eE:!0,k:"rpc returns"},{cN:"constant",b:/^\s*[A-Z_]+/,e:/\s*=/,eE:!0}]}});hljs.registerLanguage("gcode",function(e){var N="[A-Z_][A-Z0-9_.]*",i="\\%",c={literal:"",built_in:"",keyword:"IF DO WHILE ENDWHILE CALL ENDIF SUB ENDSUB GOTO REPEAT ENDREPEAT EQ LT GT NE GE LE OR XOR"},r={cN:"preprocessor",b:"([O])([0-9]+)"},l=[e.CLCM,e.CBCM,e.C(/\(/,/\)/),e.inherit(e.CNM,{b:"([-+]?([0-9]*\\.?[0-9]+\\.?))|"+e.CNR}),e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"keyword",b:"([G])([0-9]+\\.?[0-9]?)"},{cN:"title",b:"([M])([0-9]+\\.?[0-9]?)"},{cN:"title",b:"(VC|VS|#)",e:"(\\d+)"},{cN:"title",b:"(VZOFX|VZOFY|VZOFZ)"},{cN:"built_in",b:"(ATAN|ABS|ACOS|ASIN|SIN|COS|EXP|FIX|FUP|ROUND|LN|TAN)(\\[)",e:"([-+]?([0-9]*\\.?[0-9]+\\.?))(\\])"},{cN:"label",v:[{b:"N",e:"\\d+",i:"\\W"}]}];return{aliases:["nc"],cI:!0,l:N,k:c,c:[{cN:"preprocessor",b:i},r].concat(l)}});hljs.registerLanguage("vim",function(e){return{l:/[!#@\w]+/,k:{keyword:"N|0 P|0 X|0 a|0 ab abc abo al am an|0 ar arga argd arge argdo argg argl argu as au aug aun b|0 bN ba bad bd be bel bf bl bm bn bo bp br brea breaka breakd breakl bro bufdo buffers bun bw c|0 cN cNf ca cabc caddb cad caddf cal cat cb cc ccl cd ce cex cf cfir cgetb cgete cg changes chd che checkt cl cla clo cm cmapc cme cn cnew cnf cno cnorea cnoreme co col colo com comc comp con conf cope cp cpf cq cr cs cst cu cuna cunme cw d|0 delm deb debugg delc delf dif diffg diffo diffp diffpu diffs diffthis dig di dl dell dj dli do doautoa dp dr ds dsp e|0 ea ec echoe echoh echom echon el elsei em en endfo endf endt endw ene ex exe exi exu f|0 files filet fin fina fini fir fix fo foldc foldd folddoc foldo for fu g|0 go gr grepa gu gv ha h|0 helpf helpg helpt hi hid his i|0 ia iabc if ij il im imapc ime ino inorea inoreme int is isp iu iuna iunme j|0 ju k|0 keepa kee keepj lN lNf l|0 lad laddb laddf la lan lat lb lc lch lcl lcs le lefta let lex lf lfir lgetb lgete lg lgr lgrepa lh ll lla lli lmak lm lmapc lne lnew lnf ln loadk lo loc lockv lol lope lp lpf lr ls lt lu lua luad luaf lv lvimgrepa lw m|0 ma mak map mapc marks mat me menut mes mk mks mksp mkv mkvie mod mz mzf nbc nb nbs n|0 new nm nmapc nme nn nnoreme noa no noh norea noreme norm nu nun nunme ol o|0 om omapc ome on ono onoreme opt ou ounme ow p|0 profd prof pro promptr pc ped pe perld po popu pp pre prev ps pt ptN ptf ptj ptl ptn ptp ptr pts pu pw py3 python3 py3d py3f py pyd pyf q|0 quita qa r|0 rec red redi redr redraws reg res ret retu rew ri rightb rub rubyd rubyf rund ru rv s|0 sN san sa sal sav sb sbN sba sbf sbl sbm sbn sbp sbr scrip scripte scs se setf setg setl sf sfir sh sim sig sil sl sla sm smap smapc sme sn sni sno snor snoreme sor so spelld spe spelli spellr spellu spellw sp spr sre st sta startg startr star stopi stj sts sun sunm sunme sus sv sw sy synti sync t|0 tN tabN tabc tabdo tabe tabf tabfir tabl tabm tabnew tabn tabo tabp tabr tabs tab ta tags tc tcld tclf te tf th tj tl tm tn to tp tr try ts tu u|0 undoj undol una unh unl unlo unm unme uns up v|0 ve verb vert vim vimgrepa vi viu vie vm vmapc vme vne vn vnoreme vs vu vunme windo w|0 wN wa wh wi winc winp wn wp wq wqa ws wu wv x|0 xa xmapc xm xme xn xnoreme xu xunme y|0 z|0 ~ Next Print append abbreviate abclear aboveleft all amenu anoremenu args argadd argdelete argedit argglobal arglocal argument ascii autocmd augroup aunmenu buffer bNext ball badd bdelete behave belowright bfirst blast bmodified bnext botright bprevious brewind break breakadd breakdel breaklist browse bunload bwipeout change cNext cNfile cabbrev cabclear caddbuffer caddexpr caddfile call catch cbuffer cclose center cexpr cfile cfirst cgetbuffer cgetexpr cgetfile chdir checkpath checktime clist clast close cmap cmapclear cmenu cnext cnewer cnfile cnoremap cnoreabbrev cnoremenu copy colder colorscheme command comclear compiler continue confirm copen cprevious cpfile cquit crewind cscope cstag cunmap cunabbrev cunmenu cwindow delete delmarks debug debuggreedy delcommand delfunction diffupdate diffget diffoff diffpatch diffput diffsplit digraphs display deletel djump dlist doautocmd doautoall deletep drop dsearch dsplit edit earlier echo echoerr echohl echomsg else elseif emenu endif endfor endfunction endtry endwhile enew execute exit exusage file filetype find finally finish first fixdel fold foldclose folddoopen folddoclosed foldopen function global goto grep grepadd gui gvim hardcopy help helpfind helpgrep helptags highlight hide history insert iabbrev iabclear ijump ilist imap imapclear imenu inoremap inoreabbrev inoremenu intro isearch isplit iunmap iunabbrev iunmenu join jumps keepalt keepmarks keepjumps lNext lNfile list laddexpr laddbuffer laddfile last language later lbuffer lcd lchdir lclose lcscope left leftabove lexpr lfile lfirst lgetbuffer lgetexpr lgetfile lgrep lgrepadd lhelpgrep llast llist lmake lmap lmapclear lnext lnewer lnfile lnoremap loadkeymap loadview lockmarks lockvar lolder lopen lprevious lpfile lrewind ltag lunmap luado luafile lvimgrep lvimgrepadd lwindow move mark make mapclear match menu menutranslate messages mkexrc mksession mkspell mkvimrc mkview mode mzscheme mzfile nbclose nbkey nbsart next nmap nmapclear nmenu nnoremap nnoremenu noautocmd noremap nohlsearch noreabbrev noremenu normal number nunmap nunmenu oldfiles open omap omapclear omenu only onoremap onoremenu options ounmap ounmenu ownsyntax print profdel profile promptfind promptrepl pclose pedit perl perldo pop popup ppop preserve previous psearch ptag ptNext ptfirst ptjump ptlast ptnext ptprevious ptrewind ptselect put pwd py3do py3file python pydo pyfile quit quitall qall read recover redo redir redraw redrawstatus registers resize retab return rewind right rightbelow ruby rubydo rubyfile rundo runtime rviminfo substitute sNext sandbox sargument sall saveas sbuffer sbNext sball sbfirst sblast sbmodified sbnext sbprevious sbrewind scriptnames scriptencoding scscope set setfiletype setglobal setlocal sfind sfirst shell simalt sign silent sleep slast smagic smapclear smenu snext sniff snomagic snoremap snoremenu sort source spelldump spellgood spellinfo spellrepall spellundo spellwrong split sprevious srewind stop stag startgreplace startreplace startinsert stopinsert stjump stselect sunhide sunmap sunmenu suspend sview swapname syntax syntime syncbind tNext tabNext tabclose tabedit tabfind tabfirst tablast tabmove tabnext tabonly tabprevious tabrewind tag tcl tcldo tclfile tearoff tfirst throw tjump tlast tmenu tnext topleft tprevious trewind tselect tunmenu undo undojoin undolist unabbreviate unhide unlet unlockvar unmap unmenu unsilent update vglobal version verbose vertical vimgrep vimgrepadd visual viusage view vmap vmapclear vmenu vnew vnoremap vnoremenu vsplit vunmap vunmenu write wNext wall while winsize wincmd winpos wnext wprevious wqall wsverb wundo wviminfo xit xall xmapclear xmap xmenu xnoremap xnoremenu xunmap xunmenu yank",built_in:"abs acos add and append argc argidx argv asin atan atan2 browse browsedir bufexists buflisted bufloaded bufname bufnr bufwinnr byte2line byteidx call ceil changenr char2nr cindent clearmatches col complete complete_add complete_check confirm copy cos cosh count cscope_connection cursor deepcopy delete did_filetype diff_filler diff_hlID empty escape eval eventhandler executable exists exp expand extend feedkeys filereadable filewritable filter finddir findfile float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreground function garbagecollect get getbufline getbufvar getchar getcharmod getcmdline getcmdpos getcmdtype getcwd getfontname getfperm getfsize getftime getftype getline getloclist getmatches getpid getpos getqflist getreg getregtype gettabvar gettabwinvar getwinposx getwinposy getwinvar glob globpath has has_key haslocaldir hasmapto histadd histdel histget histnr hlexists hlID hostname iconv indent index input inputdialog inputlist inputrestore inputsave inputsecret insert invert isdirectory islocked items join keys len libcall libcallnr line line2byte lispindent localtime log log10 luaeval map maparg mapcheck match matchadd matcharg matchdelete matchend matchlist matchstr max min mkdir mode mzeval nextnonblank nr2char or pathshorten pow prevnonblank printf pumvisible py3eval pyeval range readfile reltime reltimestr remote_expr remote_foreground remote_peek remote_read remote_send remove rename repeat resolve reverse round screenattr screenchar screencol screenrow search searchdecl searchpair searchpairpos searchpos server2client serverlist setbufvar setcmdpos setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar setwinvar sha256 shellescape shiftwidth simplify sin sinh sort soundfold spellbadword spellsuggest split sqrt str2float str2nr strchars strdisplaywidth strftime stridx string strlen strpart strridx strtrans strwidth submatch substitute synconcealed synID synIDattr synIDtrans synstack system tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname tolower toupper tr trunc type undofile undotree values virtcol visualmode wildmenumode winbufnr wincol winheight winline winnr winrestcmd winrestview winsaveview winwidth writefile xor"},i:/[{:]/,c:[e.NM,e.ASM,{cN:"string",b:/"((\\")|[^"\n])*("|\n)/},{cN:"variable",b:/[bwtglsav]:[\w\d_]*/},{cN:"function",bK:"function function!",e:"$",r:0,c:[e.TM,{cN:"params",b:"\\(",e:"\\)"}]}]}});hljs.registerLanguage("processing",function(e){return{k:{keyword:"BufferedReader PVector PFont PImage PGraphics HashMap boolean byte char color double float int long String Array FloatDict FloatList IntDict IntList JSONArray JSONObject Object StringDict StringList Table TableRow XML false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",constant:"P2D P3D HALF_PI PI QUARTER_PI TAU TWO_PI",variable:"displayHeight displayWidth mouseY mouseX mousePressed pmouseX pmouseY key keyCode pixels focused frameCount frameRate height width",title:"setup draw",built_in:"size createGraphics beginDraw createShape loadShape PShape arc ellipse line point quad rect triangle bezier bezierDetail bezierPoint bezierTangent curve curveDetail curvePoint curveTangent curveTightness shape shapeMode beginContour beginShape bezierVertex curveVertex endContour endShape quadraticVertex vertex ellipseMode noSmooth rectMode smooth strokeCap strokeJoin strokeWeight mouseClicked mouseDragged mouseMoved mousePressed mouseReleased mouseWheel keyPressed keyPressedkeyReleased keyTyped print println save saveFrame day hour millis minute month second year background clear colorMode fill noFill noStroke stroke alpha blue brightness color green hue lerpColor red saturation modelX modelY modelZ screenX screenY screenZ ambient emissive shininess specular add createImage beginCamera camera endCamera frustum ortho perspective printCamera printProjection cursor frameRate noCursor exit loop noLoop popStyle pushStyle redraw binary boolean byte char float hex int str unbinary unhex join match matchAll nf nfc nfp nfs split splitTokens trim append arrayCopy concat expand reverse shorten sort splice subset box sphere sphereDetail createInput createReader loadBytes loadJSONArray loadJSONObject loadStrings loadTable loadXML open parseXML saveTable selectFolder selectInput beginRaw beginRecord createOutput createWriter endRaw endRecord PrintWritersaveBytes saveJSONArray saveJSONObject saveStream saveStrings saveXML selectOutput popMatrix printMatrix pushMatrix resetMatrix rotate rotateX rotateY rotateZ scale shearX shearY translate ambientLight directionalLight lightFalloff lights lightSpecular noLights normal pointLight spotLight image imageMode loadImage noTint requestImage tint texture textureMode textureWrap blend copy filter get loadPixels set updatePixels blendMode loadShader PShaderresetShader shader createFont loadFont text textFont textAlign textLeading textMode textSize textWidth textAscent textDescent abs ceil constrain dist exp floor lerp log mag map max min norm pow round sq sqrt acos asin atan atan2 cos degrees radians sin tan noise noiseDetail noiseSeed random randomGaussian randomSeed"},c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.CNM]}});hljs.registerLanguage("mizar",function(e){return{k:"environ vocabularies notations constructors definitions registrations theorems schemes requirements begin end definition registration cluster existence pred func defpred deffunc theorem proof let take assume then thus hence ex for st holds consider reconsider such that and in provided of as from be being by means equals implies iff redefine define now not or attr is mode suppose per cases set thesis contradiction scheme reserve struct correctness compatibility coherence symmetry assymetry reflexivity irreflexivity connectedness uniqueness commutativity idempotence involutiveness projectivity",c:[e.C("::","$")]}});hljs.registerLanguage("vbnet",function(e){return{aliases:["vb"],cI:!0,k:{keyword:"addhandler addressof alias and andalso aggregate ansi as assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into is isfalse isnot istrue join key let lib like loop me mid mod module mustinherit mustoverride mybase myclass namespace narrowing new next not notinheritable notoverridable of off on operator option optional or order orelse overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim rem removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly xor",built_in:"boolean byte cbool cbyte cchar cdate cdec cdbl char cint clng cobj csbyte cshort csng cstr ctype date decimal directcast double gettype getxmlnamespace iif integer long object sbyte short single string trycast typeof uinteger ulong ushort",literal:"true false nothing"},i:"//|{|}|endif|gosub|variant|wend",c:[e.inherit(e.QSM,{c:[{b:'""'}]}),e.C("'","$",{rB:!0,c:[{cN:"xmlDocTag",b:"'''|<!--|-->",c:[e.PWM]},{cN:"xmlDocTag",b:"</?",e:">",c:[e.PWM]}]}),e.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elseif end region externalsource"}]}});hljs.registerLanguage("q",function(e){var s={keyword:"do while select delete by update from",constant:"0b 1b",built_in:"neg not null string reciprocal floor ceiling signum mod xbar xlog and or each scan over prior mmu lsq inv md5 ltime gtime count first var dev med cov cor all any rand sums prds mins maxs fills deltas ratios avgs differ prev next rank reverse iasc idesc asc desc msum mcount mavg mdev xrank mmin mmax xprev rotate distinct group where flip type key til get value attr cut set upsert raze union inter except cross sv vs sublist enlist read0 read1 hopen hclose hdel hsym hcount peach system ltrim rtrim trim lower upper ssr view tables views cols xcols keys xkey xcol xasc xdesc fkeys meta lj aj aj0 ij pj asof uj ww wj wj1 fby xgroup ungroup ej save load rsave rload show csv parse eval min max avg wavg wsum sin cos tan sum",typename:"`float `double int `timestamp `timespan `datetime `time `boolean `symbol `char `byte `short `long `real `month `date `minute `second `guid"};return{aliases:["k","kdb"],k:s,l:/\b(`?)[A-Za-z0-9_]+\b/,c:[e.CLCM,e.QSM,e.CNM]}});hljs.registerLanguage("livescript",function(e){var t={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger case default function var with then unless until loop of by when and or is isnt not it that otherwise from to til fallthrough super case default function var void const let enum export import native __hasProp __extends __slice __bind __indexOf",literal:"true false null undefined yes no on off it that void",built_in:"npm require console print module global window document"},s="[A-Za-z$_](?:-[0-9A-Za-z$_]|[0-9A-Za-z$_])*",i=e.inherit(e.TM,{b:s}),n={cN:"subst",b:/#\{/,e:/}/,k:t},r={cN:"subst",b:/#[A-Za-z$_]/,e:/(?:\-[0-9A-Za-z$_]|[0-9A-Za-z$_])*/,k:t},c=[e.BNM,{cN:"number",b:"(\\b0[xX][a-fA-F0-9_]+)|(\\b\\d(\\d|_\\d)*(\\.(\\d(\\d|_\\d)*)?)?(_*[eE]([-+]\\d(_\\d|\\d)*)?)?[_a-z]*)",r:0,starts:{e:"(\\s*/)?",r:0}},{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,n,r]},{b:/"/,e:/"/,c:[e.BE,n,r]},{b:/\\/,e:/(\s|$)/,eE:!0}]},{cN:"pi",v:[{b:"//",e:"//[gim]*",c:[n,e.HCM]},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+s},{b:"``",e:"``",eB:!0,eE:!0,sL:"javascript"}];n.c=c;var a={cN:"params",b:"\\(",rB:!0,c:[{b:/\(/,e:/\)/,k:t,c:["self"].concat(c)}]};return{aliases:["ls"],k:t,i:/\/\*/,c:c.concat([e.C("\\/\\*","\\*\\/"),e.HCM,{cN:"function",c:[i,a],rB:!0,v:[{b:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\))?\\s*\\B\\->\\*?",e:"\\->\\*?"},{b:"("+s+"\\s*(?:=|:=)\\s*)?!?(\\(.*\\))?\\s*\\B[-~]{1,2}>\\*?",e:"[-~]{1,2}>\\*?"},{b:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\))?\\s*\\B!?[-~]{1,2}>\\*?",e:"!?[-~]{1,2}>\\*?"}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:s+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("haxe",function(e){var r="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)";return{aliases:["hx"],k:{keyword:"break callback case cast catch class continue default do dynamic else enum extends extern for function here if implements import in inline interface never new override package private public return static super switch this throw trace try typedef untyped using var while",literal:"true false null"},c:[e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.TM]},{cN:"preprocessor",b:"#",e:"$",k:"if else elseif end error"},{cN:"function",bK:"function",e:"[{;]",eE:!0,i:"\\S",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,e.CLCM,e.CBCM]},{cN:"type",b:":",e:r,r:10}]}]}});hljs.registerLanguage("monkey",function(e){var n={cN:"number",r:0,v:[{b:"[$][a-fA-F0-9]+"},e.NM]};return{cI:!0,k:{keyword:"public private property continue exit extern new try catch eachin not abstract final select case default const local global field end if then else elseif endif while wend repeat until forever for to step next return module inline throw",built_in:"DebugLog DebugStop Error Print ACos ACosr ASin ASinr ATan ATan2 ATan2r ATanr Abs Abs Ceil Clamp Clamp Cos Cosr Exp Floor Log Max Max Min Min Pow Sgn Sgn Sin Sinr Sqrt Tan Tanr Seed PI HALFPI TWOPI",literal:"true false null and or shl shr mod"},c:[e.C("#rem","#end"),e.C("'","$",{r:0}),{cN:"function",bK:"function method",e:"[(=:]|$",i:/\n/,c:[e.UTM]},{cN:"class",bK:"class interface",e:"$",c:[{bK:"extends implements"},e.UTM]},{cN:"variable",b:"\\b(self|super)\\b"},{cN:"preprocessor",bK:"import",e:"$"},{cN:"preprocessor",b:"\\s*#",e:"$",k:"if else elseif endif end then"},{cN:"pi",b:"^\\s*strict\\b"},{bK:"alias",e:"=",c:[e.UTM]},e.QSM,n]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\.]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",operator:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"shebang",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,e.NM,s,a,t]}});hljs.registerLanguage("erlang",function(e){var r="[a-z'][a-zA-Z0-9_']*",c="("+r+":"+r+"|"+r+")",a={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"},n=e.C("%","$"),i={cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},b={b:"fun\\s+"+r+"/\\d+"},d={b:c+"\\(",e:"\\)",rB:!0,r:0,c:[{cN:"function_name",b:c,r:0},{b:"\\(",e:"\\)",eW:!0,rE:!0,r:0}]},o={cN:"tuple",b:"{",e:"}",r:0},t={cN:"variable",b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0},l={cN:"variable",b:"[A-Z][a-zA-Z0-9_]*",r:0},f={b:"#"+e.UIR,r:0,rB:!0,c:[{cN:"record_name",b:"#"+e.UIR,r:0},{b:"{",e:"}",r:0}]},s={bK:"fun receive if try case",e:"end",k:a};s.c=[n,b,e.inherit(e.ASM,{cN:""}),s,d,e.QSM,i,o,t,l,f];var u=[n,b,s,d,e.QSM,i,o,t,l,f];d.c[1].c=u,o.c=u,f.c[1].c=u;var v={cN:"params",b:"\\(",e:"\\)",c:u};return{aliases:["erl"],k:a,i:"(</|\\*=|\\+=|-=|/\\*|\\*/|\\(\\*|\\*\\))",c:[{cN:"function",b:"^"+r+"\\s*\\(",e:"->",rB:!0,i:"\\(|#|//|/\\*|\\\\|:|;",c:[v,e.inherit(e.TM,{b:r})],starts:{e:";|\\.",k:a,c:u}},n,{cN:"pp",b:"^-",e:"\\.",r:0,eE:!0,rB:!0,l:"-"+e.IR,k:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec",c:[v]},i,e.QSM,f,t,l,o,{b:/\.$/}]}});hljs.registerLanguage("kotlin",function(e){var a="val var get set class trait object public open private protected final enum if else do while for when break continue throw try catch finally import package is as in return fun override default companion reified inline volatile transient native";return{k:{typename:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null",keyword:a},c:[e.CLCM,{cN:"javadoc",b:"/\\*\\*",e:"\\*//*",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CBCM,{cN:"type",b:/</,e:/>/,rB:!0,eE:!1,r:0},{cN:"function",bK:"fun",e:"[(]|$",rB:!0,eE:!0,k:a,i:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,r:5,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"type",b:/</,e:/>/,k:"reified",r:0},{cN:"params",b:/\(/,e:/\)/,k:a,r:0,i:/\([^\(,\s:]+,/,c:[{cN:"typename",b:/:\s*/,e:/\s*[=\)]/,eB:!0,rE:!0,r:0}]},e.CLCM,e.CBCM]},{cN:"class",bK:"class trait",e:/[:\{(]|$/,eE:!0,i:"extends implements",c:[e.UTM,{cN:"type",b:/</,e:/>/,eB:!0,eE:!0,r:0},{cN:"typename",b:/[,:]\s*/,e:/[<\(,]|$/,eB:!0,rE:!0}]},{cN:"variable",bK:"var val",e:/\s*[=:$]/,eE:!0},e.QSM,{cN:"shebang",b:"^#!/usr/bin/env",e:"$",i:"\n"},e.CNM]}});hljs.registerLanguage("stylus",function(t){var e={cN:"variable",b:"\\$"+t.IR},o={cN:"hexcolor",b:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})",r:10},i=["charset","css","debug","extend","font-face","for","import","include","media","mixin","page","warn","while"],r=["after","before","first-letter","first-line","active","first-child","focus","hover","lang","link","visited"],n=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],a="[\\.\\s\\n\\[\\:,]",l=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"],d=["\\{","\\}","\\?","(\\bReturn\\b)","(\\bEnd\\b)","(\\bend\\b)",";","#\\s","\\*\\s","===\\s","\\|","%"];return{aliases:["styl"],cI:!1,i:"("+d.join("|")+")",k:"if else for in",c:[t.QSM,t.ASM,t.CLCM,t.CBCM,o,{b:"\\.[a-zA-Z][a-zA-Z0-9_-]*"+a,rB:!0,c:[{cN:"class",b:"\\.[a-zA-Z][a-zA-Z0-9_-]*"}]},{b:"\\#[a-zA-Z][a-zA-Z0-9_-]*"+a,rB:!0,c:[{cN:"id",b:"\\#[a-zA-Z][a-zA-Z0-9_-]*"}]},{b:"\\b("+n.join("|")+")"+a,rB:!0,c:[{cN:"tag",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*"}]},{cN:"pseudo",b:"&?:?:\\b("+r.join("|")+")"+a},{cN:"at_rule",b:"@("+i.join("|")+")\\b"},e,t.CSSNM,t.NM,{cN:"function",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*\\(.*\\)",i:"[\\n]",rB:!0,c:[{cN:"title",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*"},{cN:"params",b:/\(/,e:/\)/,c:[o,e,t.ASM,t.CSSNM,t.NM,t.QSM]}]},{cN:"attribute",b:"\\b("+l.reverse().join("|")+")\\b"}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("},r={cN:"rule",b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]};return{cI:!0,i:/[=\/|']/,c:[e.CBCM,r,{cN:"id",b:/\#[A-Za-z0-9_-]+/},{cN:"class",b:/\.[A-Za-z0-9_-]+/,r:0},{cN:"attr_selector",b:/\[/,e:/\]/,i:"$"},{cN:"pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"']+/},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:/\S/,r:0,c:[e.CBCM,r]}]}});hljs.registerLanguage("puppet",function(e){var s="augeas computer cron exec file filebucket host interface k5login macauthorization mailalias maillist mcx mount nagios_command nagios_contact nagios_contactgroup nagios_host nagios_hostdependency nagios_hostescalation nagios_hostextinfo nagios_hostgroup nagios_service firewall nagios_servicedependency nagios_serviceescalation nagios_serviceextinfo nagios_servicegroup nagios_timeperiod notify package resources router schedule scheduled_task selboolean selmodule service ssh_authorized_key sshkey stage tidy user vlan yumrepo zfs zone zpool",r="alias audit before loglevel noop require subscribe tag owner ensure group mode name|0 changes context force incl lens load_path onlyif provider returns root show_diff type_check en_address ip_address realname command environment hour monute month monthday special target weekday creates cwd ogoutput refresh refreshonly tries try_sleep umask backup checksum content ctime force ignore links mtime purge recurse recurselimit replace selinux_ignore_defaults selrange selrole seltype seluser source souirce_permissions sourceselect validate_cmd validate_replacement allowdupe attribute_membership auth_membership forcelocal gid ia_load_module members system host_aliases ip allowed_trunk_vlans description device_url duplex encapsulation etherchannel native_vlan speed principals allow_root auth_class auth_type authenticate_user k_of_n mechanisms rule session_owner shared options device fstype enable hasrestart directory present absent link atboot blockdevice device dump pass remounts poller_tag use message withpath adminfile allow_virtual allowcdrom category configfiles flavor install_options instance package_settings platform responsefile status uninstall_options vendor unless_system_user unless_uid binary control flags hasstatus manifest pattern restart running start stop allowdupe auths expiry gid groups home iterations key_membership keys managehome membership password password_max_age password_min_age profile_membership profiles project purge_ssh_keys role_membership roles salt shell uid baseurl cost descr enabled enablegroups exclude failovermethod gpgcheck gpgkey http_caching include includepkgs keepalive metadata_expire metalink mirrorlist priority protect proxy proxy_password proxy_username repo_gpgcheck s3_enabled skip_if_unavailable sslcacert sslclientcert sslclientkey sslverify mounted",a={keyword:"and case class default define else elsif false if in import enherits node or true undef unless main settings $string "+s,literal:r,built_in:"architecture augeasversion blockdevices boardmanufacturer boardproductname boardserialnumber cfkey dhcp_servers domain ec2_ ec2_userdata facterversion filesystems ldom fqdn gid hardwareisa hardwaremodel hostname id|0 interfaces ipaddress ipaddress_ ipaddress6 ipaddress6_ iphostnumber is_virtual kernel kernelmajversion kernelrelease kernelversion kernelrelease kernelversion lsbdistcodename lsbdistdescription lsbdistid lsbdistrelease lsbmajdistrelease lsbminordistrelease lsbrelease macaddress macaddress_ macosx_buildversion macosx_productname macosx_productversion macosx_productverson_major macosx_productversion_minor manufacturer memoryfree memorysize netmask metmask_ network_ operatingsystem operatingsystemmajrelease operatingsystemrelease osfamily partitions path physicalprocessorcount processor processorcount productname ps puppetversion rubysitedir rubyversion selinux selinux_config_mode selinux_config_policy selinux_current_mode selinux_current_mode selinux_enforced selinux_policyversion serialnumber sp_ sshdsakey sshecdsakey sshrsakey swapencrypted swapfree swapsize timezone type uniqueid uptime uptime_days uptime_hours uptime_seconds uuid virtual vlans xendomains zfs_version zonenae zones zpool_version"},i=e.C("#","$"),o={cN:"string",c:[e.BE],v:[{b:/'/,e:/'/},{b:/"/,e:/"/}]},n=[o,i,{cN:"keyword",bK:"class",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"(::)?[A-Za-z_]\\w*(::\\w+)*"}),i,o]},{cN:"keyword",b:"([a-zA-Z_(::)]+ *\\{)",c:[o,i],r:0},{cN:"keyword",b:"(\\}|\\{)",r:0},{cN:"function",b:"[a-zA-Z_]+\\s*=>"},{cN:"constant",b:"(::)?(\\b[A-Z][a-z_]*(::)?)+",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0}];return{aliases:["pp"],k:a,c:n}});hljs.registerLanguage("nimrod",function(t){return{aliases:["nim"],k:{keyword:"addr and as asm bind block break|0 case|0 cast const|0 continue|0 converter discard distinct|10 div do elif else|0 end|0 enum|0 except export finally for from generic if|0 import|0 in include|0 interface is isnot|10 iterator|10 let|0 macro method|10 mixin mod nil not notin|10 object|0 of or out proc|10 ptr raise ref|10 return shl shr static template|10 try|0 tuple type|0 using|0 var|0 when while|0 with without xor yield",literal:"shared guarded stdin stdout stderr result|10 true false"},c:[{cN:"decorator",b:/{\./,e:/\.}/,r:10},{cN:"string",b:/[a-zA-Z]\w*"/,e:/"/,c:[{b:/""/}]},{cN:"string",b:/([a-zA-Z]\w*)?"""/,e:/"""/},t.QSM,{cN:"type",b:/\b[A-Z]\w+\b/,r:0},{cN:"type",b:/\b(int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float|float32|float64|bool|char|string|cstring|pointer|expr|stmt|void|auto|any|range|array|openarray|varargs|seq|set|clong|culong|cchar|cschar|cshort|cint|csize|clonglong|cfloat|cdouble|clongdouble|cuchar|cushort|cuint|culonglong|cstringarray|semistatic)\b/},{cN:"number",b:/\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(\d[_\d]*)('?[iIuUfF](8|16|32|64))?/,r:0},t.HCM]}});hljs.registerLanguage("smalltalk",function(a){var r="[a-z][a-zA-Z0-9_]*",s={cN:"char",b:"\\$.{1}"},c={cN:"symbol",b:"#"+a.UIR};return{aliases:["st"],k:"self super nil true false thisContext",c:[a.C('"','"'),a.ASM,{cN:"class",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},{cN:"method",b:r+":",r:0},a.CNM,c,s,{cN:"localvars",b:"\\|[ ]*"+r+"([ ]+"+r+")*[ ]*\\|",rB:!0,e:/\|/,i:/\S/,c:[{b:"(\\|[ ]*)?"+r}]},{cN:"array",b:"\\#\\(",e:"\\)",c:[a.ASM,s,a.CNM,c]}]}});hljs.registerLanguage("x86asm",function(s){return{cI:!0,l:"\\.?"+s.IR,k:{keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",literal:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l",pseudo:"db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times",preprocessor:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public ",built_in:"bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},c:[s.C(";","$",{r:0}),{cN:"number",b:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",r:0},{cN:"number",b:"\\$[0-9][0-9A-Fa-f]*",r:0},{cN:"number",b:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[HhXx]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{cN:"number",b:"\\b(?:0[HhXx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"},s.QSM,{cN:"string",b:"'",e:"[^\\\\]'",r:0},{cN:"string",b:"`",e:"[^\\\\]`",r:0},{cN:"string",b:"\\.[A-Za-z0-9]+",r:0},{cN:"label",b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)",r:0},{cN:"label",b:"^\\s*%%[A-Za-z0-9_$#@~.?]*:",r:0},{cN:"argument",b:"%[0-9]+",r:0},{cN:"built_in",b:"%!S+",r:0}]}});hljs.registerLanguage("roboconf",function(e){var n="[a-zA-Z-_][^\n{\r\n]+\\{";return{aliases:["graph","instances"],cI:!0,k:"import",c:[{cN:"facet",b:"^facet "+n,e:"}",k:"facet installer exports children extends",c:[e.HCM]},{cN:"instance-of",b:"^instance of "+n,e:"}",k:"name count channels instance-data instance-state instance of",c:[{cN:"keyword",b:"[a-zA-Z-_]+( | )*:"},e.HCM]},{cN:"component",b:"^"+n,e:"}",l:"\\(?[a-zA-Z]+\\)?",k:"installer exports children extends imports facets alias (optional)",c:[{cN:"string",b:"\\.[a-zA-Z-_]+",e:"\\s|,|;",eE:!0},e.HCM]},e.HCM]}});hljs.registerLanguage("ruby",function(e){var c="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",b={cN:"yardoctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},n=[e.C("#","$",{c:[b]}),e.C("^\\=begin","^\\=end",{c:[b],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},i={cN:"params",b:"\\(",e:"\\)",k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(n)},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:c}),i].concat(n)},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[t,{b:c}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(n),r:0}].concat(n);s.c=d,i.c=d;var o="[>?]>",l="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",N=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:d}},{cN:"prompt",b:"^("+o+"|"+l+"|"+u+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,c:n.concat(N).concat(d)}});hljs.registerLanguage("typescript",function(e){return{aliases:["ts"],k:{keyword:"in if for while finally var new function|0 do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class public private get set super interface extendsstatic constructor implements enum export import declare type protected",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document any number boolean string void"},c:[{cN:"pi",b:/^\s*('|")use strict('|")/,r:0},e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/,r:0},{cN:"constructor",bK:"constructor",e:/\{/,eE:!0,r:10},{cN:"module",bK:"module",e:/\{/,eE:!0},{cN:"interface",bK:"interface",e:/\{/,eE:!0},{b:/\$[(.]/},{b:"\\."+e.IR,r:0}]}});hljs.registerLanguage("handlebars",function(e){var a="each in with if else unless bindattr action collection debugger log outlet template unbound view yield";return{aliases:["hbs","html.hbs","html.handlebars"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[{cN:"expression",b:"{{",e:"}}",c:[{cN:"begin-block",b:"#[a-zA-Z- .]+",k:a},{cN:"string",b:'"',e:'"'},{cN:"end-block",b:"\\/[a-zA-Z- .]+",k:a},{cN:"variable",b:"[a-zA-Z-.]+",k:a}]}]}});hljs.registerLanguage("mercury",function(e){var i={keyword:"module use_module import_module include_module end_module initialise mutable initialize finalize finalise interface implementation pred mode func type inst solver any_pred any_func is semidet det nondet multi erroneous failure cc_nondet cc_multi typeclass instance where pragma promise external trace atomic or_else require_complete_switch require_det require_semidet require_multi require_nondet require_cc_multi require_cc_nondet require_erroneous require_failure",pragma:"inline no_inline type_spec source_file fact_table obsolete memo loop_check minimal_model terminates does_not_terminate check_termination promise_equivalent_clauses",preprocessor:"foreign_proc foreign_decl foreign_code foreign_type foreign_import_module foreign_export_enum foreign_export foreign_enum may_call_mercury will_not_call_mercury thread_safe not_thread_safe maybe_thread_safe promise_pure promise_semipure tabled_for_io local untrailed trailed attach_to_io_state can_pass_as_mercury_type stable will_not_throw_exception may_modify_trail will_not_modify_trail may_duplicate may_not_duplicate affects_liveness does_not_affect_liveness doesnt_affect_liveness no_sharing unknown_sharing sharing",built_in:"some all not if then else true fail false try catch catch_any semidet_true semidet_false semidet_fail impure_true impure semipure"},r={cN:"label",b:"XXX",e:"$",eW:!0,r:0},t=e.inherit(e.CLCM,{b:"%"}),_=e.inherit(e.CBCM,{r:0});t.c.push(r),_.c.push(r);var n={cN:"number",b:"0'.\\|0[box][0-9a-fA-F]*"},a=e.inherit(e.ASM,{r:0}),o=e.inherit(e.QSM,{r:0}),l={cN:"constant",b:"\\\\[abfnrtv]\\|\\\\x[0-9a-fA-F]*\\\\\\|%[-+# *.0-9]*[dioxXucsfeEgGp]",r:0};o.c.push(l);var s={cN:"built_in",v:[{b:"<=>"},{b:"<=",r:0},{b:"=>",r:0},{b:"/\\\\"},{b:"\\\\/"}]},c={cN:"built_in",v:[{b:":-\\|-->"},{b:"=",r:0}]};return{aliases:["m","moo"],k:i,c:[s,c,t,_,n,e.NM,a,o,{b:/:-/}]}});hljs.registerLanguage("fix",function(u){return{c:[{b:/[^\u2401\u0001]+/,e:/[\u2401\u0001]/,eE:!0,rB:!0,rE:!1,c:[{b:/([^\u2401\u0001=]+)/,e:/=([^\u2401\u0001=]+)/,rE:!0,rB:!1,cN:"attribute"},{b:/=/,e:/([\u2401\u0001])/,eE:!0,eB:!0,cN:"string"}]}],cI:!0}});hljs.registerLanguage("clojure",function(e){var t={built_in:"def cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={cN:"collection",b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"attribute",b:"[:]"+n},f={cN:"list",b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"keyword",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={cN:"variable",v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=e.C("^(__END__|__DATA__)","\\n$",{r:5}),o=[e.BE,r,n],a=[n,e.HCM,i,e.C("^\\=\\w","\\=cut",{eW:!0}),s,{cN:"string",c:o,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,i,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return r.c=a,s.c=a,{aliases:["pl"],k:t,c:a}});hljs.registerLanguage("twig",function(e){var t={cN:"params",b:"\\(",e:"\\)"},a="attribute block constant cycle date dump include max min parent random range source template_from_string",r={cN:"function",bK:a,r:0,c:[t]},c={cN:"filter",b:/\|[A-Za-z_]+:?/,k:"abs batch capitalize convert_encoding date date_modify default escape first format join json_encode keys last length lower merge nl2br number_format raw replace reverse round slice sort split striptags title trim upper url_encode",c:[r]},n="autoescape block do embed extends filter flush for if import include macro sandbox set spaceless use verbatim";return n=n+" "+n.split(" ").map(function(e){return"end"+e}).join(" "),{aliases:["craftcms"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[e.C(/\{#/,/#}/),{cN:"template_tag",b:/\{%/,e:/%}/,k:n,c:[c,r]},{cN:"variable",b:/\{\{/,e:/}}/,c:[c,r]}]}});hljs.registerLanguage("livecodeserver",function(e){var r={cN:"variable",b:"\\b[gtps][A-Z]+[A-Za-z0-9_\\-]*\\b|\\$_[A-Z]+",r:0},t=[e.CBCM,e.HCM,e.C("--","$"),e.C("[^:]//","$")],a=e.inherit(e.TM,{v:[{b:"\\b_*rig[A-Z]+[A-Za-z0-9_\\-]*"},{b:"\\b_[a-z0-9\\-]+"}]}),o=e.inherit(e.TM,{b:"\\b([A-Za-z0-9_\\-]+)\\b"});return{cI:!1,k:{keyword:"$_COOKIE $_FILES $_GET $_GET_BINARY $_GET_RAW $_POST $_POST_BINARY $_POST_RAW $_SESSION $_SERVER codepoint codepoints segment segments codeunit codeunits sentence sentences trueWord trueWords paragraph after byte bytes english the until http forever descending using line real8 with seventh for stdout finally element word words fourth before black ninth sixth characters chars stderr uInt1 uInt1s uInt2 uInt2s stdin string lines relative rel any fifth items from middle mid at else of catch then third it file milliseconds seconds second secs sec int1 int1s int4 int4s internet int2 int2s normal text item last long detailed effective uInt4 uInt4s repeat end repeat URL in try into switch to words https token binfile each tenth as ticks tick system real4 by dateItems without char character ascending eighth whole dateTime numeric short first ftp integer abbreviated abbr abbrev private case while if",constant:"SIX TEN FORMFEED NINE ZERO NONE SPACE FOUR FALSE COLON CRLF PI COMMA ENDOFFILE EOF EIGHT FIVE QUOTE EMPTY ONE TRUE RETURN CR LINEFEED RIGHT BACKSLASH NULL SEVEN TAB THREE TWO six ten formfeed nine zero none space four false colon crlf pi comma endoffile eof eight five quote empty one true return cr linefeed right backslash null seven tab three two RIVERSION RISTATE FILE_READ_MODE FILE_WRITE_MODE FILE_WRITE_MODE DIR_WRITE_MODE FILE_READ_UMASK FILE_WRITE_UMASK DIR_READ_UMASK DIR_WRITE_UMASK",operator:"div mod wrap and or bitAnd bitNot bitOr bitXor among not in a an within contains ends with begins the keys of keys",built_in:"put abs acos aliasReference annuity arrayDecode arrayEncode asin atan atan2 average avg avgDev base64Decode base64Encode baseConvert binaryDecode binaryEncode byteOffset byteToNum cachedURL cachedURLs charToNum cipherNames codepointOffset codepointProperty codepointToNum codeunitOffset commandNames compound compress constantNames cos date dateFormat decompress directories diskSpace DNSServers exp exp1 exp2 exp10 extents files flushEvents folders format functionNames geometricMean global globals hasMemory harmonicMean hostAddress hostAddressToName hostName hostNameToAddress isNumber ISOToMac itemOffset keys len length libURLErrorData libUrlFormData libURLftpCommand libURLLastHTTPHeaders libURLLastRHHeaders libUrlMultipartFormAddPart libUrlMultipartFormData libURLVersion lineOffset ln ln1 localNames log log2 log10 longFilePath lower macToISO matchChunk matchText matrixMultiply max md5Digest median merge millisec millisecs millisecond milliseconds min monthNames nativeCharToNum normalizeText num number numToByte numToChar numToCodepoint numToNativeChar offset open openfiles openProcesses openProcessIDs openSockets paragraphOffset paramCount param params peerAddress pendingMessages platform popStdDev populationStandardDeviation populationVariance popVariance processID random randomBytes replaceText result revCreateXMLTree revCreateXMLTreeFromFile revCurrentRecord revCurrentRecordIsFirst revCurrentRecordIsLast revDatabaseColumnCount revDatabaseColumnIsNull revDatabaseColumnLengths revDatabaseColumnNames revDatabaseColumnNamed revDatabaseColumnNumbered revDatabaseColumnTypes revDatabaseConnectResult revDatabaseCursors revDatabaseID revDatabaseTableNames revDatabaseType revDataFromQuery revdb_closeCursor revdb_columnbynumber revdb_columncount revdb_columnisnull revdb_columnlengths revdb_columnnames revdb_columntypes revdb_commit revdb_connect revdb_connections revdb_connectionerr revdb_currentrecord revdb_cursorconnection revdb_cursorerr revdb_cursors revdb_dbtype revdb_disconnect revdb_execute revdb_iseof revdb_isbof revdb_movefirst revdb_movelast revdb_movenext revdb_moveprev revdb_query revdb_querylist revdb_recordcount revdb_rollback revdb_tablenames revGetDatabaseDriverPath revNumberOfRecords revOpenDatabase revOpenDatabases revQueryDatabase revQueryDatabaseBlob revQueryResult revQueryIsAtStart revQueryIsAtEnd revUnixFromMacPath revXMLAttribute revXMLAttributes revXMLAttributeValues revXMLChildContents revXMLChildNames revXMLCreateTreeFromFileWithNamespaces revXMLCreateTreeWithNamespaces revXMLDataFromXPathQuery revXMLEvaluateXPath revXMLFirstChild revXMLMatchingNode revXMLNextSibling revXMLNodeContents revXMLNumberOfChildren revXMLParent revXMLPreviousSibling revXMLRootNode revXMLRPC_CreateRequest revXMLRPC_Documents revXMLRPC_Error revXMLRPC_GetHost revXMLRPC_GetMethod revXMLRPC_GetParam revXMLText revXMLRPC_Execute revXMLRPC_GetParamCount revXMLRPC_GetParamNode revXMLRPC_GetParamType revXMLRPC_GetPath revXMLRPC_GetPort revXMLRPC_GetProtocol revXMLRPC_GetRequest revXMLRPC_GetResponse revXMLRPC_GetSocket revXMLTree revXMLTrees revXMLValidateDTD revZipDescribeItem revZipEnumerateItems revZipOpenArchives round sampVariance sec secs seconds sentenceOffset sha1Digest shell shortFilePath sin specialFolderPath sqrt standardDeviation statRound stdDev sum sysError systemVersion tan tempName textDecode textEncode tick ticks time to tokenOffset toLower toUpper transpose truewordOffset trunc uniDecode uniEncode upper URLDecode URLEncode URLStatus uuid value variableNames variance version waitDepth weekdayNames wordOffset xsltApplyStylesheet xsltApplyStylesheetFromFile xsltLoadStylesheet xsltLoadStylesheetFromFile add breakpoint cancel clear local variable file word line folder directory URL close socket process combine constant convert create new alias folder directory decrypt delete variable word line folder directory URL dispatch divide do encrypt filter get include intersect kill libURLDownloadToFile libURLFollowHttpRedirects libURLftpUpload libURLftpUploadFile libURLresetAll libUrlSetAuthCallback libURLSetCustomHTTPHeaders libUrlSetExpect100 libURLSetFTPListCommand libURLSetFTPMode libURLSetFTPStopTime libURLSetStatusCallback load multiply socket prepare process post seek rel relative read from process rename replace require resetAll resolve revAddXMLNode revAppendXML revCloseCursor revCloseDatabase revCommitDatabase revCopyFile revCopyFolder revCopyXMLNode revDeleteFolder revDeleteXMLNode revDeleteAllXMLTrees revDeleteXMLTree revExecuteSQL revGoURL revInsertXMLNode revMoveFolder revMoveToFirstRecord revMoveToLastRecord revMoveToNextRecord revMoveToPreviousRecord revMoveToRecord revMoveXMLNode revPutIntoXMLNode revRollBackDatabase revSetDatabaseDriverPath revSetXMLAttribute revXMLRPC_AddParam revXMLRPC_DeleteAllDocuments revXMLAddDTD revXMLRPC_Free revXMLRPC_FreeAll revXMLRPC_DeleteDocument revXMLRPC_DeleteParam revXMLRPC_SetHost revXMLRPC_SetMethod revXMLRPC_SetPort revXMLRPC_SetProtocol revXMLRPC_SetSocket revZipAddItemWithData revZipAddItemWithFile revZipAddUncompressedItemWithData revZipAddUncompressedItemWithFile revZipCancel revZipCloseArchive revZipDeleteItem revZipExtractItemToFile revZipExtractItemToVariable revZipSetProgressCallback revZipRenameItem revZipReplaceItemWithData revZipReplaceItemWithFile revZipOpenArchive send set sort split start stop subtract union unload wait write"},c:[r,{cN:"keyword",b:"\\bend\\sif\\b"},{cN:"function",bK:"function",e:"$",c:[r,o,e.ASM,e.QSM,e.BNM,e.CNM,a]},{cN:"function",bK:"end",e:"$",c:[o,a]},{cN:"command",bK:"command on",e:"$",c:[r,o,e.ASM,e.QSM,e.BNM,e.CNM,a]},{cN:"command",bK:"end",e:"$",c:[o,a]},{cN:"preprocessor",b:"<\\?rev|<\\?lc|<\\?livecode",r:10},{cN:"preprocessor",b:"<\\?"},{cN:"preprocessor",b:"\\?>"},e.ASM,e.QSM,e.BNM,e.CNM,a].concat(t),i:";$|^\\[|^="}});hljs.registerLanguage("step21",function(e){var r="[A-Z_][A-Z0-9_.]*",i="END-ISO-10303-21;",l={literal:"",built_in:"",keyword:"HEADER ENDSEC DATA"},s={cN:"preprocessor",b:"ISO-10303-21;",r:10},t=[e.CLCM,e.CBCM,e.C("/\\*\\*!","\\*/"),e.CNM,e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"string",b:"'",e:"'"},{cN:"label",v:[{b:"#",e:"\\d+",i:"\\W"}]}];return{aliases:["p21","step","stp"],cI:!0,l:r,k:l,c:[{cN:"preprocessor",b:i,r:10},s].concat(t)}});hljs.registerLanguage("cpp",function(t){var i={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginary intmax_t uintmax_t int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t int_least8_t uint_least8_t int_least16_t uint_least16_t int_least32_t uint_least32_t int_least64_t uint_least64_t int_fast8_t uint_fast8_t int_fast16_t uint_fast16_t int_fast32_t uint_fast32_t int_fast64_t uint_fast64_t intptr_t uintptr_t atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong atomic_wchar_t atomic_char16_t atomic_char32_t atomic_intmax_t atomic_uintmax_t atomic_intptr_t atomic_uintptr_t atomic_size_t atomic_ptrdiff_t atomic_int_least8_t atomic_int_least16_t atomic_int_least32_t atomic_int_least64_t atomic_uint_least8_t atomic_uint_least16_t atomic_uint_least32_t atomic_uint_least64_t atomic_int_fast8_t atomic_int_fast16_t atomic_int_fast32_t atomic_int_fast64_t atomic_uint_fast8_t atomic_uint_fast16_t atomic_uint_fast32_t atomic_uint_fast64_t",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c","cc","h","c++","h++","hpp"],k:i,i:"</",c:[t.CLCM,t.CBCM,t.QSM,{cN:"string",b:"'\\\\?.",e:"'",i:"."},{cN:"number",b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},t.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line pragma",c:[{b:/\\\n/,r:0},{b:'include\\s*[<"]',e:'[>"]',k:"include",i:"\\n"},t.CLCM]},{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:i,c:["self"]},{b:t.IR+"::",k:i},{bK:"new throw return else",r:0},{cN:"function",b:"("+t.IR+"\\s+)+"+t.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:t.IR+"\\s*\\(",rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:i,r:0,c:[t.CBCM]},t.CLCM,t.CBCM]}]}});hljs.registerLanguage("vala",function(e){return{k:{keyword:"char uchar unichar int uint long ulong short ushort int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double bool struct enum string void weak unowned owned async signal static abstract interface override while do for foreach else switch case break default return try catch public private protected internal using new this get set const stdout stdin stderr var",built_in:"DBus GLib CCode Gee Object",literal:"false true null"},c:[{cN:"class",bK:"class interface delegate namespace",e:"{",eE:!0,i:"[^,:\\n\\s\\.]",c:[e.UTM]},e.CLCM,e.CBCM,{cN:"string",b:'"""',e:'"""',r:5},e.ASM,e.QSM,e.CNM,{cN:"preprocessor",b:"^#",e:"$",r:2},{cN:"constant",b:" [A-Z_]+ ",r:0}]}});hljs.registerLanguage("http",function(t){return{aliases:["https"],i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:!0}}]}});hljs.registerLanguage("avrasm",function(r){return{cI:!0,l:"\\.?"+r.IR,k:{keyword:"adc add adiw and andi asr bclr bld brbc brbs brcc brcs break breq brge brhc brhs brid brie brlo brlt brmi brne brpl brsh brtc brts brvc brvs bset bst call cbi cbr clc clh cli cln clr cls clt clv clz com cp cpc cpi cpse dec eicall eijmp elpm eor fmul fmuls fmulsu icall ijmp in inc jmp ld ldd ldi lds lpm lsl lsr mov movw mul muls mulsu neg nop or ori out pop push rcall ret reti rjmp rol ror sbc sbr sbrc sbrs sec seh sbi sbci sbic sbis sbiw sei sen ser ses set sev sez sleep spm st std sts sub subi swap tst wdr",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r30 r31 x|0 xh xl y|0 yh yl z|0 zh zl ucsr1c udr1 ucsr1a ucsr1b ubrr1l ubrr1h ucsr0c ubrr0h tccr3c tccr3a tccr3b tcnt3h tcnt3l ocr3ah ocr3al ocr3bh ocr3bl ocr3ch ocr3cl icr3h icr3l etimsk etifr tccr1c ocr1ch ocr1cl twcr twdr twar twsr twbr osccal xmcra xmcrb eicra spmcsr spmcr portg ddrg ping portf ddrf sreg sph spl xdiv rampz eicrb eimsk gimsk gicr eifr gifr timsk tifr mcucr mcucsr tccr0 tcnt0 ocr0 assr tccr1a tccr1b tcnt1h tcnt1l ocr1ah ocr1al ocr1bh ocr1bl icr1h icr1l tccr2 tcnt2 ocr2 ocdr wdtcr sfior eearh eearl eedr eecr porta ddra pina portb ddrb pinb portc ddrc pinc portd ddrd pind spdr spsr spcr udr0 ucsr0a ucsr0b ubrr0l acsr admux adcsr adch adcl porte ddre pine pinf",preprocessor:".byte .cseg .db .def .device .dseg .dw .endmacro .equ .eseg .exit .include .list .listmac .macro .nolist .org .set"},c:[r.CBCM,r.C(";","$",{r:0}),r.CNM,r.BNM,{cN:"number",b:"\\b(\\$[a-zA-Z0-9]+|0o[0-7]+)"},r.QSM,{cN:"string",b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"},{cN:"label",b:"^[A-Za-z0-9_.$]+:"},{cN:"preprocessor",b:"#",e:"$"},{cN:"localvars",b:"@[0-9]+"}]}});hljs.registerLanguage("aspectj",function(e){var t="false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else extends implements break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws privileged aspectOf adviceexecution proceed cflowbelow cflow initialization preinitialization staticinitialization withincode target within execution getWithinTypeName handler thisJoinPoint thisJoinPointStaticPart thisEnclosingJoinPointStaticPart declare parents warning error soft precedence thisAspectInstance",i="get set args call";return{k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"aspect",bK:"aspect",e:/[{;=]/,eE:!0,i:/[:;"\[\]]/,c:[{bK:"extends implements pertypewithin perthis pertarget percflowbelow percflow issingleton"},e.UTM,{b:/\([^\)]*/,e:/[)]+/,k:t+" "+i,eE:!1}]},{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,r:0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"pointcut after before around throwing returning",e:/[)]/,eE:!1,i:/["\[\]]/,c:[{b:e.UIR+"\\s*\\(",rB:!0,c:[e.UTM]}]},{b:/[:]/,rB:!0,e:/[{;]/,r:0,eE:!1,k:t,i:/["\[\]]/,c:[{b:e.UIR+"\\s*\\(",k:t+" "+i},e.QSM]},{bK:"new throw",r:0},{cN:"function",b:/\w+ +\w+(\.)?\w+\s*\([^\)]*\)\s*((throws)[\w\s,]+)?[\{;]/,rB:!0,e:/[{;=]/,k:t,eE:!0,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,r:0,k:t,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("rib",function(e){return{k:"ArchiveRecord AreaLightSource Atmosphere Attribute AttributeBegin AttributeEnd Basis Begin Blobby Bound Clipping ClippingPlane Color ColorSamples ConcatTransform Cone CoordinateSystem CoordSysTransform CropWindow Curves Cylinder DepthOfField Detail DetailRange Disk Displacement Display End ErrorHandler Exposure Exterior Format FrameAspectRatio FrameBegin FrameEnd GeneralPolygon GeometricApproximation Geometry Hider Hyperboloid Identity Illuminate Imager Interior LightSource MakeCubeFaceEnvironment MakeLatLongEnvironment MakeShadow MakeTexture Matte MotionBegin MotionEnd NuPatch ObjectBegin ObjectEnd ObjectInstance Opacity Option Orientation Paraboloid Patch PatchMesh Perspective PixelFilter PixelSamples PixelVariance Points PointsGeneralPolygons PointsPolygons Polygon Procedural Projection Quantize ReadArchive RelativeDetail ReverseOrientation Rotate Scale ScreenWindow ShadingInterpolation ShadingRate Shutter Sides Skew SolidBegin SolidEnd Sphere SubdivisionMesh Surface TextureCoordinates Torus Transform TransformBegin TransformEnd TransformPoints Translate TrimCurve WorldBegin WorldEnd",i:"</",c:[e.HCM,e.CNM,e.ASM,e.QSM]}});hljs.registerLanguage("python",function(e){var r={cN:"prompt",b:/^(>>>|\.\.\.) /},b={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},l={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},c={cN:"params",b:/\(/,e:/\)/,c:["self",r,l,b]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[r,l,b,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,c]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("axapta",function(e){return{k:"false int abstract private char boolean static null if for true while long throw finally protected final return void enum else break new catch byte super case short default double public try this switch continue reverse firstfast firstonly forupdate nofetch sum avg minof maxof count order group by asc desc index hint like dispaly edit client server ttsbegin ttscommit str real date container anytype common div mod",c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.CNM,{cN:"preprocessor",b:"#",e:"$"},{cN:"class",bK:"class interface",e:"{",eE:!0,i:":",c:[{bK:"extends implements"},e.UTM]}]}});hljs.registerLanguage("nix",function(e){var t={keyword:"rec with let in inherit assert if else then",constant:"true false or and null",built_in:"import abort baseNameOf dirOf isNull builtins map removeAttrs throw toString derivation"},i={cN:"subst",b:/\$\{/,e:/}/,k:t},r={cN:"variable",b:/[a-zA-Z0-9-_]+(\s*=)/},n={cN:"string",b:"''",e:"''",c:[i]},s={cN:"string",b:'"',e:'"',c:[i]},a=[e.NM,e.HCM,e.CBCM,n,s,r];return i.c=a,{aliases:["nixos"],k:t,c:a}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("parser3",function(r){var e=r.C("{","}",{c:["self"]});return{sL:"xml",r:0,c:[r.C("^#","$"),r.C("\\^rem{","}",{r:10,c:[e]}),{cN:"preprocessor",b:"^@(?:BASE|USE|CLASS|OPTIONS)$",r:10},{cN:"title",b:"@[\\w\\-]+\\[[\\w^;\\-]*\\](?:\\[[\\w^;\\-]*\\])?(?:.*)$"},{cN:"variable",b:"\\$\\{?[\\w\\-\\.\\:]+\\}?"},{cN:"keyword",b:"\\^[\\w\\-\\.\\:]+"},{cN:"number",b:"\\^#[0-9a-fA-F]+"},r.CNM]}});hljs.registerLanguage("django",function(e){var t={cN:"filter",b:/\|[A-Za-z]+:?/,k:"truncatewords removetags linebreaksbr yesno get_digit timesince random striptags filesizeformat escape linebreaks length_is ljust rjust cut urlize fix_ampersands title floatformat capfirst pprint divisibleby add make_list unordered_list urlencode timeuntil urlizetrunc wordcount stringformat linenumbers slice date dictsort dictsortreversed default_if_none pluralize lower join center default truncatewords_html upper length phone2numeric wordwrap time addslashes slugify first escapejs force_escape iriencode last safe safeseq truncatechars localize unlocalize localtime utc timezone",c:[{cN:"argument",b:/"/,e:/"/},{cN:"argument",b:/'/,e:/'/}]};return{aliases:["jinja"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[e.C(/\{%\s*comment\s*%}/,/\{%\s*endcomment\s*%}/),e.C(/\{#/,/#}/),{cN:"template_tag",b:/\{%/,e:/%}/,k:"comment endcomment load templatetag ifchanged endifchanged if endif firstof for endfor in ifnotequal endifnotequal widthratio extends include spaceless endspaceless regroup by as ifequal endifequal ssi now with cycle url filter endfilter debug block endblock else autoescape endautoescape csrf_token empty elif endwith static trans blocktrans endblocktrans get_static_prefix get_media_prefix plural get_current_language language get_available_languages get_current_language_bidi get_language_info get_language_info_list localize endlocalize localtime endlocaltime timezone endtimezone get_current_timezone verbatim",c:[t]},{cN:"variable",b:/\{\{/,e:/}}/,c:[t]}]}});hljs.registerLanguage("rust",function(e){var t=e.inherit(e.CBCM);return t.c.push("self"),{aliases:["rs"],k:{keyword:"alignof as be box break const continue crate do else enum extern false fn for if impl in let loop match mod mut offsetof once priv proc pub pure ref return self sizeof static struct super trait true type typeof unsafe unsized use virtual while yield int i8 i16 i32 i64 uint u8 u32 u64 float f32 f64 str char bool",built_in:"assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln!"},l:e.IR+"!?",i:"</",c:[e.CLCM,t,e.inherit(e.QSM,{i:null}),{cN:"string",b:/r(#*)".*?"\1(?!#)/},{cN:"string",b:/'\\?(x\w{2}|u\w{4}|U\w{8}|.)'/},{b:/'[a-zA-Z_][a-zA-Z0-9_]*/},{cN:"number",b:/\b(0[xbo][A-Fa-f0-9_]+|\d[\d_]*(\.[0-9_]+)?([eE][+-]?[0-9_]+)?)([uif](8|16|32|64|size))?/,r:0},{cN:"function",bK:"fn",e:"(\\(|<)",eE:!0,c:[e.UTM]},{cN:"preprocessor",b:"#\\!?\\[",e:"\\]"},{bK:"type",e:"(=|<)",c:[e.UTM],i:"\\S"},{bK:"trait enum",e:"({|<)",c:[e.UTM],i:"\\S"},{b:e.IR+"::"},{b:"->"}]}});hljs.registerLanguage("vhdl",function(e){var t="\\d(_|\\d)*",r="[eE][-+]?"+t,n=t+"(\\."+t+")?("+r+")?",o="\\w+",i=t+"#"+o+"(\\."+o+")?#("+r+")?",a="\\b("+i+"|"+n+")";return{cI:!0,k:{keyword:"abs access after alias all and architecture array assert attribute begin block body buffer bus case component configuration constant context cover disconnect downto default else elsif end entity exit fairness file for force function generate generic group guarded if impure in inertial inout is label library linkage literal loop map mod nand new next nor not null of on open or others out package port postponed procedure process property protected pure range record register reject release rem report restrict restrict_guarantee return rol ror select sequence severity shared signal sla sll sra srl strong subtype then to transport type unaffected units until use variable vmode vprop vunit wait when while with xnor xor",typename:"boolean bit character severity_level integer time delay_length natural positive string bit_vector file_open_kind file_open_status std_ulogic std_ulogic_vector std_logic std_logic_vector unsigned signed boolean_vector integer_vector real_vector time_vector"},i:"{",c:[e.CBCM,e.C("--","$"),e.QSM,{cN:"number",b:a,r:0},{cN:"literal",b:"'(U|X|0|1|Z|W|L|H|-)'",c:[e.BE]},{cN:"attribute",b:"'[A-Za-z](_?[A-Za-z0-9])*",c:[e.BE]}]}});hljs.registerLanguage("ocaml",function(e){return{aliases:["ml"],k:{keyword:"and as assert asr begin class constraint do done downto else end exception external for fun function functor if in include inherit! inherit initializer land lazy let lor lsl lsr lxor match method!|10 method mod module mutable new object of open! open or private rec sig struct then to try type val! val virtual when while with parser value",built_in:"array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 string unit in_channel out_channel ref",literal:"true false"},i:/\/\/|>>/,l:"[a-z_]\\w*!?",c:[{cN:"literal",b:"\\[(\\|\\|)?\\]|\\(\\)"},e.C("\\(\\*","\\*\\)",{c:["self"]}),{cN:"symbol",b:"'[A-Za-z_](?!')[\\w']*"},{cN:"tag",b:"`[A-Z][\\w']*"},{cN:"type",b:"\\b[A-Z][\\w']*",r:0},{b:"[a-z_]\\w*'[\\w']*"},e.inherit(e.ASM,{cN:"char",r:0}),e.inherit(e.QSM,{i:null}),{cN:"number",b:"\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",r:0},{b:/[-=]>/}]}});hljs.registerLanguage("cmake",function(e){return{aliases:["cmake.in"],cI:!0,k:{keyword:"add_custom_command add_custom_target add_definitions add_dependencies add_executable add_library add_subdirectory add_test aux_source_directory break build_command cmake_minimum_required cmake_policy configure_file create_test_sourcelist define_property else elseif enable_language enable_testing endforeach endfunction endif endmacro endwhile execute_process export find_file find_library find_package find_path find_program fltk_wrap_ui foreach function get_cmake_property get_directory_property get_filename_component get_property get_source_file_property get_target_property get_test_property if include include_directories include_external_msproject include_regular_expression install link_directories load_cache load_command macro mark_as_advanced message option output_required_files project qt_wrap_cpp qt_wrap_ui remove_definitions return separate_arguments set set_directory_properties set_property set_source_files_properties set_target_properties set_tests_properties site_name source_group string target_link_libraries try_compile try_run unset variable_watch while build_name exec_program export_library_dependencies install_files install_programs install_targets link_libraries make_directory remove subdir_depends subdirs use_mangled_mesa utility_source variable_requires write_file qt5_use_modules qt5_use_package qt5_wrap_cpp on off true false and or",operator:"equal less greater strless strgreater strequal matches"},c:[{cN:"envvar",b:"\\${",e:"}"},e.HCM,e.QSM,e.NM]}});hljs.registerLanguage("1c",function(c){var e="[a-zA-Zа-яА-Я][a-zA-Z0-9_а-яА-Я]*",r="возврат дата для если и или иначе иначеесли исключение конецесли конецпопытки конецпроцедуры конецфункции конеццикла константа не перейти перем перечисление по пока попытка прервать продолжить процедура строка тогда фс функция цикл число экспорт",t="ansitooem oemtoansi ввестивидсубконто ввестидату ввестизначение ввестиперечисление ввестипериод ввестиплансчетов ввестистроку ввестичисло вопрос восстановитьзначение врег выбранныйплансчетов вызватьисключение датагод датамесяц датачисло добавитьмесяц завершитьработусистемы заголовоксистемы записьжурналарегистрации запуститьприложение зафиксироватьтранзакцию значениевстроку значениевстрокувнутр значениевфайл значениеизстроки значениеизстрокивнутр значениеизфайла имякомпьютера имяпользователя каталогвременныхфайлов каталогиб каталогпользователя каталогпрограммы кодсимв командасистемы конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лев лог лог10 макс максимальноеколичествосубконто мин монопольныйрежим названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найти найтипомеченныенаудаление найтиссылки началопериодаби началостандартногоинтервала начатьтранзакцию начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода нрег обработкаожидания окр описаниеошибки основнойжурналрасчетов основнойплансчетов основнойязык открытьформу открытьформумодально отменитьтранзакцию очиститьокносообщений периодстр полноеимяпользователя получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта прав праводоступа предупреждение префиксавтонумерации пустаястрока пустоезначение рабочаядаттьпустоезначение рабочаядата разделительстраниц разделительстрок разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо сигнал симв символтабуляции создатьобъект сокрл сокрлп сокрп сообщить состояние сохранитьзначение сред статусвозврата стрдлина стрзаменить стрколичествострок стрполучитьстроку стрчисловхождений сформироватьпозициюдокумента счетпокоду текущаядата текущеевремя типзначения типзначениястр удалитьобъекты установитьтана установитьтапо фиксшаблон формат цел шаблон",i={cN:"dquote",b:'""'},n={cN:"string",b:'"',e:'"|$',c:[i]},a={cN:"string",b:"\\|",e:'"|$',c:[i]};return{cI:!0,l:e,k:{keyword:r,built_in:t},c:[c.CLCM,c.NM,n,a,{cN:"function",b:"(процедура|функция)",e:"$",l:e,k:"процедура функция",c:[c.inherit(c.TM,{b:e}),{cN:"tail",eW:!0,c:[{cN:"params",b:"\\(",e:"\\)",l:e,k:"знач",c:[n,a]},{cN:"export",b:"экспорт",eW:!0,l:e,k:"экспорт",c:[c.CLCM]}]},c.CLCM]},{cN:"preprocessor",b:"#",e:"$"},{cN:"date",b:"'\\d{2}\\.\\d{2}\\.(\\d{2}|\\d{4})'"}]}});hljs.registerLanguage("tcl",function(e){return{aliases:["tk"],k:"after append apply array auto_execok auto_import auto_load auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror binary break catch cd chan clock close concat continue dde dict encoding eof error eval exec exit expr fblocked fconfigure fcopy file fileevent filename flush for foreach format gets glob global history http if incr info interp join lappend|10 lassign|10 lindex|10 linsert|10 list llength|10 load lrange|10 lrepeat|10 lreplace|10 lreverse|10 lsearch|10 lset|10 lsort|10 mathfunc mathop memory msgcat namespace open package parray pid pkg::create pkg_mkIndex platform platform::shell proc puts pwd read refchan regexp registry regsub|10 rename return safe scan seek set socket source split string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord tcl_startOfPreviousWord tcl_wordBreakAfter tcl_wordBreakBefore tcltest tclvars tell time tm trace unknown unload unset update uplevel upvar variable vwait while",c:[e.C(";[ \\t]*#","$"),e.C("^[ \\t]*#","$"),{bK:"proc",e:"[\\{]",eE:!0,c:[{cN:"symbol",b:"[ \\t\\n\\r]+(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",e:"[ \\t\\n\\r]",eW:!0,eE:!0}]},{cN:"variable",eE:!0,v:[{b:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*\\(([a-zA-Z0-9_])*\\)",e:"[^a-zA-Z0-9_\\}\\$]"},{b:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",e:"(\\))?[^a-zA-Z0-9_\\}\\$]"}]},{cN:"string",c:[e.BE],v:[e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},{cN:"number",v:[e.BNM,e.CNM]}]}});hljs.registerLanguage("groovy",function(e){return{k:{typename:"byte short char int long boolean float double void",literal:"true false null",keyword:"def as in assert trait super this abstract static volatile transient public private protected synchronized final class interface enum if else for while switch case break default continue throw throws try catch finally implements extends new import package return instanceof"},c:[e.CLCM,{cN:"javadoc",b:"/\\*\\*",e:"\\*//*",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CBCM,{cN:"string",b:'"""',e:'"""'},{cN:"string",b:"'''",e:"'''"},{cN:"string",b:"\\$/",e:"/\\$",r:10},e.ASM,{cN:"regexp",b:/~?\/[^\/\n]+\//,c:[e.BE]},e.QSM,{cN:"shebang",b:"^#!/usr/bin/env",e:"$",i:"\n"},e.BNM,{cN:"class",bK:"class interface trait enum",e:"{",i:":",c:[{bK:"extends implements"},e.UTM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"},{cN:"string",b:/[^\?]{0}[A-Za-z0-9_$]+ *:/},{b:/\?/,e:/\:/},{cN:"label",b:"^\\s*[A-Za-z0-9_$]+:",r:0}]}});hljs.registerLanguage("erlang-repl",function(r){return{k:{special_functions:"spawn spawn_link self",reserved:"after and andalso|10 band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse|10 query receive rem try when xor"},c:[{cN:"prompt",b:"^[0-9]+> ",r:10},r.C("%","$"),{cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},r.ASM,r.QSM,{cN:"constant",b:"\\?(::)?([A-Z]\\w*(::)?)+"},{cN:"arrow",b:"->"},{cN:"ok",b:"ok"},{cN:"exclamation_mark",b:"!"},{cN:"function_or_atom",b:"(\\b[a-z'][a-zA-Z0-9_']*:[a-z'][a-zA-Z0-9_']*)|(\\b[a-z'][a-zA-Z0-9_']*)",r:0},{cN:"variable",b:"[A-Z][a-zA-Z0-9_']*",r:0}]}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("mathematica",function(e){return{aliases:["mma"],l:"(\\$|\\b)"+e.IR+"\\b",k:"AbelianGroup Abort AbortKernels AbortProtect Above Abs Absolute AbsoluteCorrelation AbsoluteCorrelationFunction AbsoluteCurrentValue AbsoluteDashing AbsoluteFileName AbsoluteOptions AbsolutePointSize AbsoluteThickness AbsoluteTime AbsoluteTiming AccountingForm Accumulate Accuracy AccuracyGoal ActionDelay ActionMenu ActionMenuBox ActionMenuBoxOptions Active ActiveItem ActiveStyle AcyclicGraphQ AddOnHelpPath AddTo AdjacencyGraph AdjacencyList AdjacencyMatrix AdjustmentBox AdjustmentBoxOptions AdjustTimeSeriesForecast AffineTransform After AiryAi AiryAiPrime AiryAiZero AiryBi AiryBiPrime AiryBiZero AlgebraicIntegerQ AlgebraicNumber AlgebraicNumberDenominator AlgebraicNumberNorm AlgebraicNumberPolynomial AlgebraicNumberTrace AlgebraicRules AlgebraicRulesData Algebraics AlgebraicUnitQ Alignment AlignmentMarker AlignmentPoint All AllowedDimensions AllowGroupClose AllowInlineCells AllowKernelInitialization AllowReverseGroupClose AllowScriptLevelChange AlphaChannel AlternatingGroup AlternativeHypothesis Alternatives AmbientLight Analytic AnchoredSearch And AndersonDarlingTest AngerJ AngleBracket AngularGauge Animate AnimationCycleOffset AnimationCycleRepetitions AnimationDirection AnimationDisplayTime AnimationRate AnimationRepetitions AnimationRunning Animator AnimatorBox AnimatorBoxOptions AnimatorElements Annotation Annuity AnnuityDue Antialiasing Antisymmetric Apart ApartSquareFree Appearance AppearanceElements AppellF1 Append AppendTo Apply ArcCos ArcCosh ArcCot ArcCoth ArcCsc ArcCsch ArcSec ArcSech ArcSin ArcSinDistribution ArcSinh ArcTan ArcTanh Arg ArgMax ArgMin ArgumentCountQ ARIMAProcess ArithmeticGeometricMean ARMAProcess ARProcess Array ArrayComponents ArrayDepth ArrayFlatten ArrayPad ArrayPlot ArrayQ ArrayReshape ArrayRules Arrays Arrow Arrow3DBox ArrowBox Arrowheads AspectRatio AspectRatioFixed Assert Assuming Assumptions AstronomicalData Asynchronous AsynchronousTaskObject AsynchronousTasks AtomQ Attributes AugmentedSymmetricPolynomial AutoAction AutoDelete AutoEvaluateEvents AutoGeneratedPackage AutoIndent AutoIndentSpacings AutoItalicWords AutoloadPath AutoMatch Automatic AutomaticImageSize AutoMultiplicationSymbol AutoNumberFormatting AutoOpenNotebooks AutoOpenPalettes AutorunSequencing AutoScaling AutoScroll AutoSpacing AutoStyleOptions AutoStyleWords Axes AxesEdge AxesLabel AxesOrigin AxesStyle Axis BabyMonsterGroupB Back Background BackgroundTasksSettings Backslash Backsubstitution Backward Band BandpassFilter BandstopFilter BarabasiAlbertGraphDistribution BarChart BarChart3D BarLegend BarlowProschanImportance BarnesG BarOrigin BarSpacing BartlettHannWindow BartlettWindow BaseForm Baseline BaselinePosition BaseStyle BatesDistribution BattleLemarieWavelet Because BeckmannDistribution Beep Before Begin BeginDialogPacket BeginFrontEndInteractionPacket BeginPackage BellB BellY Below BenfordDistribution BeniniDistribution BenktanderGibratDistribution BenktanderWeibullDistribution BernoulliB BernoulliDistribution BernoulliGraphDistribution BernoulliProcess BernsteinBasis BesselFilterModel BesselI BesselJ BesselJZero BesselK BesselY BesselYZero Beta BetaBinomialDistribution BetaDistribution BetaNegativeBinomialDistribution BetaPrimeDistribution BetaRegularized BetweennessCentrality BezierCurve BezierCurve3DBox BezierCurve3DBoxOptions BezierCurveBox BezierCurveBoxOptions BezierFunction BilateralFilter Binarize BinaryFormat BinaryImageQ BinaryRead BinaryReadList BinaryWrite BinCounts BinLists Binomial BinomialDistribution BinomialProcess BinormalDistribution BiorthogonalSplineWavelet BipartiteGraphQ BirnbaumImportance BirnbaumSaundersDistribution BitAnd BitClear BitGet BitLength BitNot BitOr BitSet BitShiftLeft BitShiftRight BitXor Black BlackmanHarrisWindow BlackmanNuttallWindow BlackmanWindow Blank BlankForm BlankNullSequence BlankSequence Blend Block BlockRandom BlomqvistBeta BlomqvistBetaTest Blue Blur BodePlot BohmanWindow Bold Bookmarks Boole BooleanConsecutiveFunction BooleanConvert BooleanCountingFunction BooleanFunction BooleanGraph BooleanMaxterms BooleanMinimize BooleanMinterms Booleans BooleanTable BooleanVariables BorderDimensions BorelTannerDistribution Bottom BottomHatTransform BoundaryStyle Bounds Box BoxBaselineShift BoxData BoxDimensions Boxed Boxes BoxForm BoxFormFormatTypes BoxFrame BoxID BoxMargins BoxMatrix BoxRatios BoxRotation BoxRotationPoint BoxStyle BoxWhiskerChart Bra BracketingBar BraKet BrayCurtisDistance BreadthFirstScan Break Brown BrownForsytheTest BrownianBridgeProcess BrowserCategory BSplineBasis BSplineCurve BSplineCurve3DBox BSplineCurveBox BSplineCurveBoxOptions BSplineFunction BSplineSurface BSplineSurface3DBox BubbleChart BubbleChart3D BubbleScale BubbleSizes BulletGauge BusinessDayQ ButterflyGraph ButterworthFilterModel Button ButtonBar ButtonBox ButtonBoxOptions ButtonCell ButtonContents ButtonData ButtonEvaluator ButtonExpandable ButtonFrame ButtonFunction ButtonMargins ButtonMinHeight ButtonNote ButtonNotebook ButtonSource ButtonStyle ButtonStyleMenuListing Byte ByteCount ByteOrdering C CachedValue CacheGraphics CalendarData CalendarType CallPacket CanberraDistance Cancel CancelButton CandlestickChart Cap CapForm CapitalDifferentialD CardinalBSplineBasis CarmichaelLambda Cases Cashflow Casoratian Catalan CatalanNumber Catch CauchyDistribution CauchyWindow CayleyGraph CDF CDFDeploy CDFInformation CDFWavelet Ceiling Cell CellAutoOverwrite CellBaseline CellBoundingBox CellBracketOptions CellChangeTimes CellContents CellContext CellDingbat CellDynamicExpression CellEditDuplicate CellElementsBoundingBox CellElementSpacings CellEpilog CellEvaluationDuplicate CellEvaluationFunction CellEventActions CellFrame CellFrameColor CellFrameLabelMargins CellFrameLabels CellFrameMargins CellGroup CellGroupData CellGrouping CellGroupingRules CellHorizontalScrolling CellID CellLabel CellLabelAutoDelete CellLabelMargins CellLabelPositioning CellMargins CellObject CellOpen CellPrint CellProlog Cells CellSize CellStyle CellTags CellularAutomaton CensoredDistribution Censoring Center CenterDot CentralMoment CentralMomentGeneratingFunction CForm ChampernowneNumber ChanVeseBinarize Character CharacterEncoding CharacterEncodingsPath CharacteristicFunction CharacteristicPolynomial CharacterRange Characters ChartBaseStyle ChartElementData ChartElementDataFunction ChartElementFunction ChartElements ChartLabels ChartLayout ChartLegends ChartStyle Chebyshev1FilterModel Chebyshev2FilterModel ChebyshevDistance ChebyshevT ChebyshevU Check CheckAbort CheckAll Checkbox CheckboxBar CheckboxBox CheckboxBoxOptions ChemicalData ChessboardDistance ChiDistribution ChineseRemainder ChiSquareDistribution ChoiceButtons ChoiceDialog CholeskyDecomposition Chop Circle CircleBox CircleDot CircleMinus CirclePlus CircleTimes CirculantGraph CityData Clear ClearAll ClearAttributes ClearSystemCache ClebschGordan ClickPane Clip ClipboardNotebook ClipFill ClippingStyle ClipPlanes ClipRange Clock ClockGauge ClockwiseContourIntegral Close Closed CloseKernels ClosenessCentrality Closing ClosingAutoSave ClosingEvent ClusteringComponents CMYKColor Coarse Coefficient CoefficientArrays CoefficientDomain CoefficientList CoefficientRules CoifletWavelet Collect Colon ColonForm ColorCombine ColorConvert ColorData ColorDataFunction ColorFunction ColorFunctionScaling Colorize ColorNegate ColorOutput ColorProfileData ColorQuantize ColorReplace ColorRules ColorSelectorSettings ColorSeparate ColorSetter ColorSetterBox ColorSetterBoxOptions ColorSlider ColorSpace Column ColumnAlignments ColumnBackgrounds ColumnForm ColumnLines ColumnsEqual ColumnSpacings ColumnWidths CommonDefaultFormatTypes Commonest CommonestFilter CommonUnits CommunityBoundaryStyle CommunityGraphPlot CommunityLabels CommunityRegionStyle CompatibleUnitQ CompilationOptions CompilationTarget Compile Compiled CompiledFunction Complement CompleteGraph CompleteGraphQ CompleteKaryTree CompletionsListPacket Complex Complexes ComplexExpand ComplexInfinity ComplexityFunction ComponentMeasurements ComponentwiseContextMenu Compose ComposeList ComposeSeries Composition CompoundExpression CompoundPoissonDistribution CompoundPoissonProcess CompoundRenewalProcess Compress CompressedData Condition ConditionalExpression Conditioned Cone ConeBox ConfidenceLevel ConfidenceRange ConfidenceTransform ConfigurationPath Congruent Conjugate ConjugateTranspose Conjunction Connect ConnectedComponents ConnectedGraphQ ConnesWindow ConoverTest ConsoleMessage ConsoleMessagePacket ConsolePrint Constant ConstantArray Constants ConstrainedMax ConstrainedMin ContentPadding ContentsBoundingBox ContentSelectable ContentSize Context ContextMenu Contexts ContextToFilename ContextToFileName Continuation Continue ContinuedFraction ContinuedFractionK ContinuousAction ContinuousMarkovProcess ContinuousTimeModelQ ContinuousWaveletData ContinuousWaveletTransform ContourDetect ContourGraphics ContourIntegral ContourLabels ContourLines ContourPlot ContourPlot3D Contours ContourShading ContourSmoothing ContourStyle ContraharmonicMean Control ControlActive ControlAlignment ControllabilityGramian ControllabilityMatrix ControllableDecomposition ControllableModelQ ControllerDuration ControllerInformation ControllerInformationData ControllerLinking ControllerManipulate ControllerMethod ControllerPath ControllerState ControlPlacement ControlsRendering ControlType Convergents ConversionOptions ConversionRules ConvertToBitmapPacket ConvertToPostScript ConvertToPostScriptPacket Convolve ConwayGroupCo1 ConwayGroupCo2 ConwayGroupCo3 CoordinateChartData CoordinatesToolOptions CoordinateTransform CoordinateTransformData CoprimeQ Coproduct CopulaDistribution Copyable CopyDirectory CopyFile CopyTag CopyToClipboard CornerFilter CornerNeighbors Correlation CorrelationDistance CorrelationFunction CorrelationTest Cos Cosh CoshIntegral CosineDistance CosineWindow CosIntegral Cot Coth Count CounterAssignments CounterBox CounterBoxOptions CounterClockwiseContourIntegral CounterEvaluator CounterFunction CounterIncrements CounterStyle CounterStyleMenuListing CountRoots CountryData Covariance CovarianceEstimatorFunction CovarianceFunction CoxianDistribution CoxIngersollRossProcess CoxModel CoxModelFit CramerVonMisesTest CreateArchive CreateDialog CreateDirectory CreateDocument CreateIntermediateDirectories CreatePalette CreatePalettePacket CreateScheduledTask CreateTemporary CreateWindow CriticalityFailureImportance CriticalitySuccessImportance CriticalSection Cross CrossingDetect CrossMatrix Csc Csch CubeRoot Cubics Cuboid CuboidBox Cumulant CumulantGeneratingFunction Cup CupCap Curl CurlyDoubleQuote CurlyQuote CurrentImage CurrentlySpeakingPacket CurrentValue CurvatureFlowFilter CurveClosed Cyan CycleGraph CycleIndexPolynomial Cycles CyclicGroup Cyclotomic Cylinder CylinderBox CylindricalDecomposition D DagumDistribution DamerauLevenshteinDistance DampingFactor Darker Dashed Dashing DataCompression DataDistribution DataRange DataReversed Date DateDelimiters DateDifference DateFunction DateList DateListLogPlot DateListPlot DatePattern DatePlus DateRange DateString DateTicksFormat DaubechiesWavelet DavisDistribution DawsonF DayCount DayCountConvention DayMatchQ DayName DayPlus DayRange DayRound DeBruijnGraph Debug DebugTag Decimal DeclareKnownSymbols DeclarePackage Decompose Decrement DedekindEta Default DefaultAxesStyle DefaultBaseStyle DefaultBoxStyle DefaultButton DefaultColor DefaultControlPlacement DefaultDuplicateCellStyle DefaultDuration DefaultElement DefaultFaceGridsStyle DefaultFieldHintStyle DefaultFont DefaultFontProperties DefaultFormatType DefaultFormatTypeForStyle DefaultFrameStyle DefaultFrameTicksStyle DefaultGridLinesStyle DefaultInlineFormatType DefaultInputFormatType DefaultLabelStyle DefaultMenuStyle DefaultNaturalLanguage DefaultNewCellStyle DefaultNewInlineCellStyle DefaultNotebook DefaultOptions DefaultOutputFormatType DefaultStyle DefaultStyleDefinitions DefaultTextFormatType DefaultTextInlineFormatType DefaultTicksStyle DefaultTooltipStyle DefaultValues Defer DefineExternal DefineInputStreamMethod DefineOutputStreamMethod Definition Degree DegreeCentrality DegreeGraphDistribution DegreeLexicographic DegreeReverseLexicographic Deinitialization Del Deletable Delete DeleteBorderComponents DeleteCases DeleteContents DeleteDirectory DeleteDuplicates DeleteFile DeleteSmallComponents DeleteWithContents DeletionWarning Delimiter DelimiterFlashTime DelimiterMatching Delimiters Denominator DensityGraphics DensityHistogram DensityPlot DependentVariables Deploy Deployed Depth DepthFirstScan Derivative DerivativeFilter DescriptorStateSpace DesignMatrix Det DGaussianWavelet DiacriticalPositioning Diagonal DiagonalMatrix Dialog DialogIndent DialogInput DialogLevel DialogNotebook DialogProlog DialogReturn DialogSymbols Diamond DiamondMatrix DiceDissimilarity DictionaryLookup DifferenceDelta DifferenceOrder DifferenceRoot DifferenceRootReduce Differences DifferentialD DifferentialRoot DifferentialRootReduce DifferentiatorFilter DigitBlock DigitBlockMinimum DigitCharacter DigitCount DigitQ DihedralGroup Dilation Dimensions DiracComb DiracDelta DirectedEdge DirectedEdges DirectedGraph DirectedGraphQ DirectedInfinity Direction Directive Directory DirectoryName DirectoryQ DirectoryStack DirichletCharacter DirichletConvolve DirichletDistribution DirichletL DirichletTransform DirichletWindow DisableConsolePrintPacket DiscreteChirpZTransform DiscreteConvolve DiscreteDelta DiscreteHadamardTransform DiscreteIndicator DiscreteLQEstimatorGains DiscreteLQRegulatorGains DiscreteLyapunovSolve DiscreteMarkovProcess DiscretePlot DiscretePlot3D DiscreteRatio DiscreteRiccatiSolve DiscreteShift DiscreteTimeModelQ DiscreteUniformDistribution DiscreteVariables DiscreteWaveletData DiscreteWaveletPacketTransform DiscreteWaveletTransform Discriminant Disjunction Disk DiskBox DiskMatrix Dispatch DispersionEstimatorFunction Display DisplayAllSteps DisplayEndPacket DisplayFlushImagePacket DisplayForm DisplayFunction DisplayPacket DisplayRules DisplaySetSizePacket DisplayString DisplayTemporary DisplayWith DisplayWithRef DisplayWithVariable DistanceFunction DistanceTransform Distribute Distributed DistributedContexts DistributeDefinitions DistributionChart DistributionDomain DistributionFitTest DistributionParameterAssumptions DistributionParameterQ Dithering Div Divergence Divide DivideBy Dividers Divisible Divisors DivisorSigma DivisorSum DMSList DMSString Do DockedCells DocumentNotebook DominantColors DOSTextFormat Dot DotDashed DotEqual Dotted DoubleBracketingBar DoubleContourIntegral DoubleDownArrow DoubleLeftArrow DoubleLeftRightArrow DoubleLeftTee DoubleLongLeftArrow DoubleLongLeftRightArrow DoubleLongRightArrow DoubleRightArrow DoubleRightTee DoubleUpArrow DoubleUpDownArrow DoubleVerticalBar DoublyInfinite Down DownArrow DownArrowBar DownArrowUpArrow DownLeftRightVector DownLeftTeeVector DownLeftVector DownLeftVectorBar DownRightTeeVector DownRightVector DownRightVectorBar Downsample DownTee DownTeeArrow DownValues DragAndDrop DrawEdges DrawFrontFaces DrawHighlighted Drop DSolve Dt DualLinearProgramming DualSystemsModel DumpGet DumpSave DuplicateFreeQ Dynamic DynamicBox DynamicBoxOptions DynamicEvaluationTimeout DynamicLocation DynamicModule DynamicModuleBox DynamicModuleBoxOptions DynamicModuleParent DynamicModuleValues DynamicName DynamicNamespace DynamicReference DynamicSetting DynamicUpdating DynamicWrapper DynamicWrapperBox DynamicWrapperBoxOptions E EccentricityCentrality EdgeAdd EdgeBetweennessCentrality EdgeCapacity EdgeCapForm EdgeColor EdgeConnectivity EdgeCost EdgeCount EdgeCoverQ EdgeDashing EdgeDelete EdgeDetect EdgeForm EdgeIndex EdgeJoinForm EdgeLabeling EdgeLabels EdgeLabelStyle EdgeList EdgeOpacity EdgeQ EdgeRenderingFunction EdgeRules EdgeShapeFunction EdgeStyle EdgeThickness EdgeWeight Editable EditButtonSettings EditCellTagsSettings EditDistance EffectiveInterest Eigensystem Eigenvalues EigenvectorCentrality Eigenvectors Element ElementData Eliminate EliminationOrder EllipticE EllipticExp EllipticExpPrime EllipticF EllipticFilterModel EllipticK EllipticLog EllipticNomeQ EllipticPi EllipticReducedHalfPeriods EllipticTheta EllipticThetaPrime EmitSound EmphasizeSyntaxErrors EmpiricalDistribution Empty EmptyGraphQ EnableConsolePrintPacket Enabled Encode End EndAdd EndDialogPacket EndFrontEndInteractionPacket EndOfFile EndOfLine EndOfString EndPackage EngineeringForm Enter EnterExpressionPacket EnterTextPacket Entropy EntropyFilter Environment Epilog Equal EqualColumns EqualRows EqualTilde EquatedTo Equilibrium EquirippleFilterKernel Equivalent Erf Erfc Erfi ErlangB ErlangC ErlangDistribution Erosion ErrorBox ErrorBoxOptions ErrorNorm ErrorPacket ErrorsDialogSettings EstimatedDistribution EstimatedProcess EstimatorGains EstimatorRegulator EuclideanDistance EulerE EulerGamma EulerianGraphQ EulerPhi Evaluatable Evaluate Evaluated EvaluatePacket EvaluationCell EvaluationCompletionAction EvaluationElements EvaluationMode EvaluationMonitor EvaluationNotebook EvaluationObject EvaluationOrder Evaluator EvaluatorNames EvenQ EventData EventEvaluator EventHandler EventHandlerTag EventLabels ExactBlackmanWindow ExactNumberQ ExactRootIsolation ExampleData Except ExcludedForms ExcludePods Exclusions ExclusionsStyle Exists Exit ExitDialog Exp Expand ExpandAll ExpandDenominator ExpandFileName ExpandNumerator Expectation ExpectationE ExpectedValue ExpGammaDistribution ExpIntegralE ExpIntegralEi Exponent ExponentFunction ExponentialDistribution ExponentialFamily ExponentialGeneratingFunction ExponentialMovingAverage ExponentialPowerDistribution ExponentPosition ExponentStep Export ExportAutoReplacements ExportPacket ExportString Expression ExpressionCell ExpressionPacket ExpToTrig ExtendedGCD Extension ExtentElementFunction ExtentMarkers ExtentSize ExternalCall ExternalDataCharacterEncoding Extract ExtractArchive ExtremeValueDistribution FaceForm FaceGrids FaceGridsStyle Factor FactorComplete Factorial Factorial2 FactorialMoment FactorialMomentGeneratingFunction FactorialPower FactorInteger FactorList FactorSquareFree FactorSquareFreeList FactorTerms FactorTermsList Fail FailureDistribution False FARIMAProcess FEDisableConsolePrintPacket FeedbackSector FeedbackSectorStyle FeedbackType FEEnableConsolePrintPacket Fibonacci FieldHint FieldHintStyle FieldMasked FieldSize File FileBaseName FileByteCount FileDate FileExistsQ FileExtension FileFormat FileHash FileInformation FileName FileNameDepth FileNameDialogSettings FileNameDrop FileNameJoin FileNames FileNameSetter FileNameSplit FileNameTake FilePrint FileType FilledCurve FilledCurveBox Filling FillingStyle FillingTransform FilterRules FinancialBond FinancialData FinancialDerivative FinancialIndicator Find FindArgMax FindArgMin FindClique FindClusters FindCurvePath FindDistributionParameters FindDivisions FindEdgeCover FindEdgeCut FindEulerianCycle FindFaces FindFile FindFit FindGeneratingFunction FindGeoLocation FindGeometricTransform FindGraphCommunities FindGraphIsomorphism FindGraphPartition FindHamiltonianCycle FindIndependentEdgeSet FindIndependentVertexSet FindInstance FindIntegerNullVector FindKClan FindKClique FindKClub FindKPlex FindLibrary FindLinearRecurrence FindList FindMaximum FindMaximumFlow FindMaxValue FindMinimum FindMinimumCostFlow FindMinimumCut FindMinValue FindPermutation FindPostmanTour FindProcessParameters FindRoot FindSequenceFunction FindSettings FindShortestPath FindShortestTour FindThreshold FindVertexCover FindVertexCut Fine FinishDynamic FiniteAbelianGroupCount FiniteGroupCount FiniteGroupData First FirstPassageTimeDistribution FischerGroupFi22 FischerGroupFi23 FischerGroupFi24Prime FisherHypergeometricDistribution FisherRatioTest FisherZDistribution Fit FitAll FittedModel FixedPoint FixedPointList FlashSelection Flat Flatten FlattenAt FlatTopWindow FlipView Floor FlushPrintOutputPacket Fold FoldList Font FontColor FontFamily FontForm FontName FontOpacity FontPostScriptName FontProperties FontReencoding FontSize FontSlant FontSubstitutions FontTracking FontVariations FontWeight For ForAll Format FormatRules FormatType FormatTypeAutoConvert FormatValues FormBox FormBoxOptions FortranForm Forward ForwardBackward Fourier FourierCoefficient FourierCosCoefficient FourierCosSeries FourierCosTransform FourierDCT FourierDCTFilter FourierDCTMatrix FourierDST FourierDSTMatrix FourierMatrix FourierParameters FourierSequenceTransform FourierSeries FourierSinCoefficient FourierSinSeries FourierSinTransform FourierTransform FourierTrigSeries FractionalBrownianMotionProcess FractionalPart FractionBox FractionBoxOptions FractionLine Frame FrameBox FrameBoxOptions Framed FrameInset FrameLabel Frameless FrameMargins FrameStyle FrameTicks FrameTicksStyle FRatioDistribution FrechetDistribution FreeQ FrequencySamplingFilterKernel FresnelC FresnelS Friday FrobeniusNumber FrobeniusSolve FromCharacterCode FromCoefficientRules FromContinuedFraction FromDate FromDigits FromDMS Front FrontEndDynamicExpression FrontEndEventActions FrontEndExecute FrontEndObject FrontEndResource FrontEndResourceString FrontEndStackSize FrontEndToken FrontEndTokenExecute FrontEndValueCache FrontEndVersion FrontFaceColor FrontFaceOpacity Full FullAxes FullDefinition FullForm FullGraphics FullOptions FullSimplify Function FunctionExpand FunctionInterpolation FunctionSpace FussellVeselyImportance GaborFilter GaborMatrix GaborWavelet GainMargins GainPhaseMargins Gamma GammaDistribution GammaRegularized GapPenalty Gather GatherBy GaugeFaceElementFunction GaugeFaceStyle GaugeFrameElementFunction GaugeFrameSize GaugeFrameStyle GaugeLabels GaugeMarkers GaugeStyle GaussianFilter GaussianIntegers GaussianMatrix GaussianWindow GCD GegenbauerC General GeneralizedLinearModelFit GenerateConditions GeneratedCell GeneratedParameters GeneratingFunction Generic GenericCylindricalDecomposition GenomeData GenomeLookup GeodesicClosing GeodesicDilation GeodesicErosion GeodesicOpening GeoDestination GeodesyData GeoDirection GeoDistance GeoGridPosition GeometricBrownianMotionProcess GeometricDistribution GeometricMean GeometricMeanFilter GeometricTransformation GeometricTransformation3DBox GeometricTransformation3DBoxOptions GeometricTransformationBox GeometricTransformationBoxOptions GeoPosition GeoPositionENU GeoPositionXYZ GeoProjectionData GestureHandler GestureHandlerTag Get GetBoundingBoxSizePacket GetContext GetEnvironment GetFileName GetFrontEndOptionsDataPacket GetLinebreakInformationPacket GetMenusPacket GetPageBreakInformationPacket Glaisher GlobalClusteringCoefficient GlobalPreferences GlobalSession Glow GoldenRatio GompertzMakehamDistribution GoodmanKruskalGamma GoodmanKruskalGammaTest Goto Grad Gradient GradientFilter GradientOrientationFilter Graph GraphAssortativity GraphCenter GraphComplement GraphData GraphDensity GraphDiameter GraphDifference GraphDisjointUnion GraphDistance GraphDistanceMatrix GraphElementData GraphEmbedding GraphHighlight GraphHighlightStyle GraphHub Graphics Graphics3D Graphics3DBox Graphics3DBoxOptions GraphicsArray GraphicsBaseline GraphicsBox GraphicsBoxOptions GraphicsColor GraphicsColumn GraphicsComplex GraphicsComplex3DBox GraphicsComplex3DBoxOptions GraphicsComplexBox GraphicsComplexBoxOptions GraphicsContents GraphicsData GraphicsGrid GraphicsGridBox GraphicsGroup GraphicsGroup3DBox GraphicsGroup3DBoxOptions GraphicsGroupBox GraphicsGroupBoxOptions GraphicsGrouping GraphicsHighlightColor GraphicsRow GraphicsSpacing GraphicsStyle GraphIntersection GraphLayout GraphLinkEfficiency GraphPeriphery GraphPlot GraphPlot3D GraphPower GraphPropertyDistribution GraphQ GraphRadius GraphReciprocity GraphRoot GraphStyle GraphUnion Gray GrayLevel GreatCircleDistance Greater GreaterEqual GreaterEqualLess GreaterFullEqual GreaterGreater GreaterLess GreaterSlantEqual GreaterTilde Green Grid GridBaseline GridBox GridBoxAlignment GridBoxBackground GridBoxDividers GridBoxFrame GridBoxItemSize GridBoxItemStyle GridBoxOptions GridBoxSpacings GridCreationSettings GridDefaultElement GridElementStyleOptions GridFrame GridFrameMargins GridGraph GridLines GridLinesStyle GroebnerBasis GroupActionBase GroupCentralizer GroupElementFromWord GroupElementPosition GroupElementQ GroupElements GroupElementToWord GroupGenerators GroupMultiplicationTable GroupOrbits GroupOrder GroupPageBreakWithin GroupSetwiseStabilizer GroupStabilizer GroupStabilizerChain Gudermannian GumbelDistribution HaarWavelet HadamardMatrix HalfNormalDistribution HamiltonianGraphQ HammingDistance HammingWindow HankelH1 HankelH2 HankelMatrix HannPoissonWindow HannWindow HaradaNortonGroupHN HararyGraph HarmonicMean HarmonicMeanFilter HarmonicNumber Hash HashTable Haversine HazardFunction Head HeadCompose Heads HeavisideLambda HeavisidePi HeavisideTheta HeldGroupHe HeldPart HelpBrowserLookup HelpBrowserNotebook HelpBrowserSettings HermiteDecomposition HermiteH HermitianMatrixQ HessenbergDecomposition Hessian HexadecimalCharacter Hexahedron HexahedronBox HexahedronBoxOptions HiddenSurface HighlightGraph HighlightImage HighpassFilter HigmanSimsGroupHS HilbertFilter HilbertMatrix Histogram Histogram3D HistogramDistribution HistogramList HistogramTransform HistogramTransformInterpolation HitMissTransform HITSCentrality HodgeDual HoeffdingD HoeffdingDTest Hold HoldAll HoldAllComplete HoldComplete HoldFirst HoldForm HoldPattern HoldRest HolidayCalendar HomeDirectory HomePage Horizontal HorizontalForm HorizontalGauge HorizontalScrollPosition HornerForm HotellingTSquareDistribution HoytDistribution HTMLSave Hue HumpDownHump HumpEqual HurwitzLerchPhi HurwitzZeta HyperbolicDistribution HypercubeGraph HyperexponentialDistribution Hyperfactorial Hypergeometric0F1 Hypergeometric0F1Regularized Hypergeometric1F1 Hypergeometric1F1Regularized Hypergeometric2F1 Hypergeometric2F1Regularized HypergeometricDistribution HypergeometricPFQ HypergeometricPFQRegularized HypergeometricU Hyperlink HyperlinkCreationSettings Hyphenation HyphenationOptions HypoexponentialDistribution HypothesisTestData I Identity IdentityMatrix If IgnoreCase Im Image Image3D Image3DSlices ImageAccumulate ImageAdd ImageAdjust ImageAlign ImageApply ImageAspectRatio ImageAssemble ImageCache ImageCacheValid ImageCapture ImageChannels ImageClip ImageColorSpace ImageCompose ImageConvolve ImageCooccurrence ImageCorners ImageCorrelate ImageCorrespondingPoints ImageCrop ImageData ImageDataPacket ImageDeconvolve ImageDemosaic ImageDifference ImageDimensions ImageDistance ImageEffect ImageFeatureTrack ImageFileApply ImageFileFilter ImageFileScan ImageFilter ImageForestingComponents ImageForwardTransformation ImageHistogram ImageKeypoints ImageLevels ImageLines ImageMargins ImageMarkers ImageMeasurements ImageMultiply ImageOffset ImagePad ImagePadding ImagePartition ImagePeriodogram ImagePerspectiveTransformation ImageQ ImageRangeCache ImageReflect ImageRegion ImageResize ImageResolution ImageRotate ImageRotated ImageScaled ImageScan ImageSize ImageSizeAction ImageSizeCache ImageSizeMultipliers ImageSizeRaw ImageSubtract ImageTake ImageTransformation ImageTrim ImageType ImageValue ImageValuePositions Implies Import ImportAutoReplacements ImportString ImprovementImportance In IncidenceGraph IncidenceList IncidenceMatrix IncludeConstantBasis IncludeFileExtension IncludePods IncludeSingularTerm Increment Indent IndentingNewlineSpacings IndentMaxFraction IndependenceTest IndependentEdgeSetQ IndependentUnit IndependentVertexSetQ Indeterminate IndexCreationOptions Indexed IndexGraph IndexTag Inequality InexactNumberQ InexactNumbers Infinity Infix Information Inherited InheritScope Initialization InitializationCell InitializationCellEvaluation InitializationCellWarning InlineCounterAssignments InlineCounterIncrements InlineRules Inner Inpaint Input InputAliases InputAssumptions InputAutoReplacements InputField InputFieldBox InputFieldBoxOptions InputForm InputGrouping InputNamePacket InputNotebook InputPacket InputSettings InputStream InputString InputStringPacket InputToBoxFormPacket Insert InsertionPointObject InsertResults Inset Inset3DBox Inset3DBoxOptions InsetBox InsetBoxOptions Install InstallService InString Integer IntegerDigits IntegerExponent IntegerLength IntegerPart IntegerPartitions IntegerQ Integers IntegerString Integral Integrate Interactive InteractiveTradingChart Interlaced Interleaving InternallyBalancedDecomposition InterpolatingFunction InterpolatingPolynomial Interpolation InterpolationOrder InterpolationPoints InterpolationPrecision Interpretation InterpretationBox InterpretationBoxOptions InterpretationFunction InterpretTemplate InterquartileRange Interrupt InterruptSettings Intersection Interval IntervalIntersection IntervalMemberQ IntervalUnion Inverse InverseBetaRegularized InverseCDF InverseChiSquareDistribution InverseContinuousWaveletTransform InverseDistanceTransform InverseEllipticNomeQ InverseErf InverseErfc InverseFourier InverseFourierCosTransform InverseFourierSequenceTransform InverseFourierSinTransform InverseFourierTransform InverseFunction InverseFunctions InverseGammaDistribution InverseGammaRegularized InverseGaussianDistribution InverseGudermannian InverseHaversine InverseJacobiCD InverseJacobiCN InverseJacobiCS InverseJacobiDC InverseJacobiDN InverseJacobiDS InverseJacobiNC InverseJacobiND InverseJacobiNS InverseJacobiSC InverseJacobiSD InverseJacobiSN InverseLaplaceTransform InversePermutation InverseRadon InverseSeries InverseSurvivalFunction InverseWaveletTransform InverseWeierstrassP InverseZTransform Invisible InvisibleApplication InvisibleTimes IrreduciblePolynomialQ IsolatingInterval IsomorphicGraphQ IsotopeData Italic Item ItemBox ItemBoxOptions ItemSize ItemStyle ItoProcess JaccardDissimilarity JacobiAmplitude Jacobian JacobiCD JacobiCN JacobiCS JacobiDC JacobiDN JacobiDS JacobiNC JacobiND JacobiNS JacobiP JacobiSC JacobiSD JacobiSN JacobiSymbol JacobiZeta JankoGroupJ1 JankoGroupJ2 JankoGroupJ3 JankoGroupJ4 JarqueBeraALMTest JohnsonDistribution Join Joined JoinedCurve JoinedCurveBox JoinForm JordanDecomposition JordanModelDecomposition K KagiChart KaiserBesselWindow KaiserWindow KalmanEstimator KalmanFilter KarhunenLoeveDecomposition KaryTree KatzCentrality KCoreComponents KDistribution KelvinBei KelvinBer KelvinKei KelvinKer KendallTau KendallTauTest KernelExecute KernelMixtureDistribution KernelObject Kernels Ket Khinchin KirchhoffGraph KirchhoffMatrix KleinInvariantJ KnightTourGraph KnotData KnownUnitQ KolmogorovSmirnovTest KroneckerDelta KroneckerModelDecomposition KroneckerProduct KroneckerSymbol KuiperTest KumaraswamyDistribution Kurtosis KuwaharaFilter Label Labeled LabeledSlider LabelingFunction LabelStyle LaguerreL LambdaComponents LambertW LanczosWindow LandauDistribution Language LanguageCategory LaplaceDistribution LaplaceTransform Laplacian LaplacianFilter LaplacianGaussianFilter Large Larger Last Latitude LatitudeLongitude LatticeData LatticeReduce Launch LaunchKernels LayeredGraphPlot LayerSizeFunction LayoutInformation LCM LeafCount LeapYearQ LeastSquares LeastSquaresFilterKernel Left LeftArrow LeftArrowBar LeftArrowRightArrow LeftDownTeeVector LeftDownVector LeftDownVectorBar LeftRightArrow LeftRightVector LeftTee LeftTeeArrow LeftTeeVector LeftTriangle LeftTriangleBar LeftTriangleEqual LeftUpDownVector LeftUpTeeVector LeftUpVector LeftUpVectorBar LeftVector LeftVectorBar LegendAppearance Legended LegendFunction LegendLabel LegendLayout LegendMargins LegendMarkers LegendMarkerSize LegendreP LegendreQ LegendreType Length LengthWhile LerchPhi Less LessEqual LessEqualGreater LessFullEqual LessGreater LessLess LessSlantEqual LessTilde LetterCharacter LetterQ Level LeveneTest LeviCivitaTensor LevyDistribution Lexicographic LibraryFunction LibraryFunctionError LibraryFunctionInformation LibraryFunctionLoad LibraryFunctionUnload LibraryLoad LibraryUnload LicenseID LiftingFilterData LiftingWaveletTransform LightBlue LightBrown LightCyan Lighter LightGray LightGreen Lighting LightingAngle LightMagenta LightOrange LightPink LightPurple LightRed LightSources LightYellow Likelihood Limit LimitsPositioning LimitsPositioningTokens LindleyDistribution Line Line3DBox LinearFilter LinearFractionalTransform LinearModelFit LinearOffsetFunction LinearProgramming LinearRecurrence LinearSolve LinearSolveFunction LineBox LineBreak LinebreakAdjustments LineBreakChart LineBreakWithin LineColor LineForm LineGraph LineIndent LineIndentMaxFraction LineIntegralConvolutionPlot LineIntegralConvolutionScale LineLegend LineOpacity LineSpacing LineWrapParts LinkActivate LinkClose LinkConnect LinkConnectedQ LinkCreate LinkError LinkFlush LinkFunction LinkHost LinkInterrupt LinkLaunch LinkMode LinkObject LinkOpen LinkOptions LinkPatterns LinkProtocol LinkRead LinkReadHeld LinkReadyQ Links LinkWrite LinkWriteHeld LiouvilleLambda List Listable ListAnimate ListContourPlot ListContourPlot3D ListConvolve ListCorrelate ListCurvePathPlot ListDeconvolve ListDensityPlot Listen ListFourierSequenceTransform ListInterpolation ListLineIntegralConvolutionPlot ListLinePlot ListLogLinearPlot ListLogLogPlot ListLogPlot ListPicker ListPickerBox ListPickerBoxBackground ListPickerBoxOptions ListPlay ListPlot ListPlot3D ListPointPlot3D ListPolarPlot ListQ ListStreamDensityPlot ListStreamPlot ListSurfacePlot3D ListVectorDensityPlot ListVectorPlot ListVectorPlot3D ListZTransform Literal LiteralSearch LocalClusteringCoefficient LocalizeVariables LocationEquivalenceTest LocationTest Locator LocatorAutoCreate LocatorBox LocatorBoxOptions LocatorCentering LocatorPane LocatorPaneBox LocatorPaneBoxOptions LocatorRegion Locked Log Log10 Log2 LogBarnesG LogGamma LogGammaDistribution LogicalExpand LogIntegral LogisticDistribution LogitModelFit LogLikelihood LogLinearPlot LogLogisticDistribution LogLogPlot LogMultinormalDistribution LogNormalDistribution LogPlot LogRankTest LogSeriesDistribution LongEqual Longest LongestAscendingSequence LongestCommonSequence LongestCommonSequencePositions LongestCommonSubsequence LongestCommonSubsequencePositions LongestMatch LongForm Longitude LongLeftArrow LongLeftRightArrow LongRightArrow Loopback LoopFreeGraphQ LowerCaseQ LowerLeftArrow LowerRightArrow LowerTriangularize LowpassFilter LQEstimatorGains LQGRegulator LQOutputRegulatorGains LQRegulatorGains LUBackSubstitution LucasL LuccioSamiComponents LUDecomposition LyapunovSolve LyonsGroupLy MachineID MachineName MachineNumberQ MachinePrecision MacintoshSystemPageSetup Magenta Magnification Magnify MainSolve MaintainDynamicCaches Majority MakeBoxes MakeExpression MakeRules MangoldtLambda ManhattanDistance Manipulate Manipulator MannWhitneyTest MantissaExponent Manual Map MapAll MapAt MapIndexed MAProcess MapThread MarcumQ MardiaCombinedTest MardiaKurtosisTest MardiaSkewnessTest MarginalDistribution MarkovProcessProperties Masking MatchingDissimilarity MatchLocalNameQ MatchLocalNames MatchQ Material MathematicaNotation MathieuC MathieuCharacteristicA MathieuCharacteristicB MathieuCharacteristicExponent MathieuCPrime MathieuGroupM11 MathieuGroupM12 MathieuGroupM22 MathieuGroupM23 MathieuGroupM24 MathieuS MathieuSPrime MathMLForm MathMLText Matrices MatrixExp MatrixForm MatrixFunction MatrixLog MatrixPlot MatrixPower MatrixQ MatrixRank Max MaxBend MaxDetect MaxExtraBandwidths MaxExtraConditions MaxFeatures MaxFilter Maximize MaxIterations MaxMemoryUsed MaxMixtureKernels MaxPlotPoints MaxPoints MaxRecursion MaxStableDistribution MaxStepFraction MaxSteps MaxStepSize MaxValue MaxwellDistribution McLaughlinGroupMcL Mean MeanClusteringCoefficient MeanDegreeConnectivity MeanDeviation MeanFilter MeanGraphDistance MeanNeighborDegree MeanShift MeanShiftFilter Median MedianDeviation MedianFilter Medium MeijerG MeixnerDistribution MemberQ MemoryConstrained MemoryInUse Menu MenuAppearance MenuCommandKey MenuEvaluator MenuItem MenuPacket MenuSortingValue MenuStyle MenuView MergeDifferences Mesh MeshFunctions MeshRange MeshShading MeshStyle Message MessageDialog MessageList MessageName MessageOptions MessagePacket Messages MessagesNotebook MetaCharacters MetaInformation Method MethodOptions MexicanHatWavelet MeyerWavelet Min MinDetect MinFilter MinimalPolynomial MinimalStateSpaceModel Minimize Minors MinRecursion MinSize MinStableDistribution Minus MinusPlus MinValue Missing MissingDataMethod MittagLefflerE MixedRadix MixedRadixQuantity MixtureDistribution Mod Modal Mode Modular ModularLambda Module Modulus MoebiusMu Moment Momentary MomentConvert MomentEvaluate MomentGeneratingFunction Monday Monitor MonomialList MonomialOrder MonsterGroupM MorletWavelet MorphologicalBinarize MorphologicalBranchPoints MorphologicalComponents MorphologicalEulerNumber MorphologicalGraph MorphologicalPerimeter MorphologicalTransform Most MouseAnnotation MouseAppearance MouseAppearanceTag MouseButtons Mouseover MousePointerNote MousePosition MovingAverage MovingMedian MoyalDistribution MultiedgeStyle MultilaunchWarning MultiLetterItalics MultiLetterStyle MultilineFunction Multinomial MultinomialDistribution MultinormalDistribution MultiplicativeOrder Multiplicity Multiselection MultivariateHypergeometricDistribution MultivariatePoissonDistribution MultivariateTDistribution N NakagamiDistribution NameQ Names NamespaceBox Nand NArgMax NArgMin NBernoulliB NCache NDSolve NDSolveValue Nearest NearestFunction NeedCurrentFrontEndPackagePacket NeedCurrentFrontEndSymbolsPacket NeedlemanWunschSimilarity Needs Negative NegativeBinomialDistribution NegativeMultinomialDistribution NeighborhoodGraph Nest NestedGreaterGreater NestedLessLess NestedScriptRules NestList NestWhile NestWhileList NevilleThetaC NevilleThetaD NevilleThetaN NevilleThetaS NewPrimitiveStyle NExpectation Next NextPrime NHoldAll NHoldFirst NHoldRest NicholsGridLines NicholsPlot NIntegrate NMaximize NMaxValue NMinimize NMinValue NominalVariables NonAssociative NoncentralBetaDistribution NoncentralChiSquareDistribution NoncentralFRatioDistribution NoncentralStudentTDistribution NonCommutativeMultiply NonConstants None NonlinearModelFit NonlocalMeansFilter NonNegative NonPositive Nor NorlundB Norm Normal NormalDistribution NormalGrouping Normalize NormalizedSquaredEuclideanDistance NormalsFunction NormFunction Not NotCongruent NotCupCap NotDoubleVerticalBar Notebook NotebookApply NotebookAutoSave NotebookClose NotebookConvertSettings NotebookCreate NotebookCreateReturnObject NotebookDefault NotebookDelete NotebookDirectory NotebookDynamicExpression NotebookEvaluate NotebookEventActions NotebookFileName NotebookFind NotebookFindReturnObject NotebookGet NotebookGetLayoutInformationPacket NotebookGetMisspellingsPacket NotebookInformation NotebookInterfaceObject NotebookLocate NotebookObject NotebookOpen NotebookOpenReturnObject NotebookPath NotebookPrint NotebookPut NotebookPutReturnObject NotebookRead NotebookResetGeneratedCells Notebooks NotebookSave NotebookSaveAs NotebookSelection NotebookSetupLayoutInformationPacket NotebooksMenu NotebookWrite NotElement NotEqualTilde NotExists NotGreater NotGreaterEqual NotGreaterFullEqual NotGreaterGreater NotGreaterLess NotGreaterSlantEqual NotGreaterTilde NotHumpDownHump NotHumpEqual NotLeftTriangle NotLeftTriangleBar NotLeftTriangleEqual NotLess NotLessEqual NotLessFullEqual NotLessGreater NotLessLess NotLessSlantEqual NotLessTilde NotNestedGreaterGreater NotNestedLessLess NotPrecedes NotPrecedesEqual NotPrecedesSlantEqual NotPrecedesTilde NotReverseElement NotRightTriangle NotRightTriangleBar NotRightTriangleEqual NotSquareSubset NotSquareSubsetEqual NotSquareSuperset NotSquareSupersetEqual NotSubset NotSubsetEqual NotSucceeds NotSucceedsEqual NotSucceedsSlantEqual NotSucceedsTilde NotSuperset NotSupersetEqual NotTilde NotTildeEqual NotTildeFullEqual NotTildeTilde NotVerticalBar NProbability NProduct NProductFactors NRoots NSolve NSum NSumTerms Null NullRecords NullSpace NullWords Number NumberFieldClassNumber NumberFieldDiscriminant NumberFieldFundamentalUnits NumberFieldIntegralBasis NumberFieldNormRepresentatives NumberFieldRegulator NumberFieldRootsOfUnity NumberFieldSignature NumberForm NumberFormat NumberMarks NumberMultiplier NumberPadding NumberPoint NumberQ NumberSeparator NumberSigns NumberString Numerator NumericFunction NumericQ NuttallWindow NValues NyquistGridLines NyquistPlot O ObservabilityGramian ObservabilityMatrix ObservableDecomposition ObservableModelQ OddQ Off Offset OLEData On ONanGroupON OneIdentity Opacity Open OpenAppend Opener OpenerBox OpenerBoxOptions OpenerView OpenFunctionInspectorPacket Opening OpenRead OpenSpecialOptions OpenTemporary OpenWrite Operate OperatingSystem OptimumFlowData Optional OptionInspectorSettings OptionQ Options OptionsPacket OptionsPattern OptionValue OptionValueBox OptionValueBoxOptions Or Orange Order OrderDistribution OrderedQ Ordering Orderless OrnsteinUhlenbeckProcess Orthogonalize Out Outer OutputAutoOverwrite OutputControllabilityMatrix OutputControllableModelQ OutputForm OutputFormData OutputGrouping OutputMathEditExpression OutputNamePacket OutputResponse OutputSizeLimit OutputStream Over OverBar OverDot Overflow OverHat Overlaps Overlay OverlayBox OverlayBoxOptions Overscript OverscriptBox OverscriptBoxOptions OverTilde OverVector OwenT OwnValues PackingMethod PaddedForm Padding PadeApproximant PadLeft PadRight PageBreakAbove PageBreakBelow PageBreakWithin PageFooterLines PageFooters PageHeaderLines PageHeaders PageHeight PageRankCentrality PageWidth PairedBarChart PairedHistogram PairedSmoothHistogram PairedTTest PairedZTest PaletteNotebook PalettePath Pane PaneBox PaneBoxOptions Panel PanelBox PanelBoxOptions Paneled PaneSelector PaneSelectorBox PaneSelectorBoxOptions PaperWidth ParabolicCylinderD ParagraphIndent ParagraphSpacing ParallelArray ParallelCombine ParallelDo ParallelEvaluate Parallelization Parallelize ParallelMap ParallelNeeds ParallelProduct ParallelSubmit ParallelSum ParallelTable ParallelTry Parameter ParameterEstimator ParameterMixtureDistribution ParameterVariables ParametricFunction ParametricNDSolve ParametricNDSolveValue ParametricPlot ParametricPlot3D ParentConnect ParentDirectory ParentForm Parenthesize ParentList ParetoDistribution Part PartialCorrelationFunction PartialD ParticleData Partition PartitionsP PartitionsQ ParzenWindow PascalDistribution PassEventsDown PassEventsUp Paste PasteBoxFormInlineCells PasteButton Path PathGraph PathGraphQ Pattern PatternSequence PatternTest PauliMatrix PaulWavelet Pause PausedTime PDF PearsonChiSquareTest PearsonCorrelationTest PearsonDistribution PerformanceGoal PeriodicInterpolation Periodogram PeriodogramArray PermutationCycles PermutationCyclesQ PermutationGroup PermutationLength PermutationList PermutationListQ PermutationMax PermutationMin PermutationOrder PermutationPower PermutationProduct PermutationReplace Permutations PermutationSupport Permute PeronaMalikFilter Perpendicular PERTDistribution PetersenGraph PhaseMargins Pi Pick PIDData PIDDerivativeFilter PIDFeedforward PIDTune Piecewise PiecewiseExpand PieChart PieChart3D PillaiTrace PillaiTraceTest Pink Pivoting PixelConstrained PixelValue PixelValuePositions Placed Placeholder PlaceholderReplace Plain PlanarGraphQ Play PlayRange Plot Plot3D Plot3Matrix PlotDivision PlotJoined PlotLabel PlotLayout PlotLegends PlotMarkers PlotPoints PlotRange PlotRangeClipping PlotRangePadding PlotRegion PlotStyle Plus PlusMinus Pochhammer PodStates PodWidth Point Point3DBox PointBox PointFigureChart PointForm PointLegend PointSize PoissonConsulDistribution PoissonDistribution PoissonProcess PoissonWindow PolarAxes PolarAxesOrigin PolarGridLines PolarPlot PolarTicks PoleZeroMarkers PolyaAeppliDistribution PolyGamma Polygon Polygon3DBox Polygon3DBoxOptions PolygonBox PolygonBoxOptions PolygonHoleScale PolygonIntersections PolygonScale PolyhedronData PolyLog PolynomialExtendedGCD PolynomialForm PolynomialGCD PolynomialLCM PolynomialMod PolynomialQ PolynomialQuotient PolynomialQuotientRemainder PolynomialReduce PolynomialRemainder Polynomials PopupMenu PopupMenuBox PopupMenuBoxOptions PopupView PopupWindow Position Positive PositiveDefiniteMatrixQ PossibleZeroQ Postfix PostScript Power PowerDistribution PowerExpand PowerMod PowerModList PowerSpectralDensity PowersRepresentations PowerSymmetricPolynomial Precedence PrecedenceForm Precedes PrecedesEqual PrecedesSlantEqual PrecedesTilde Precision PrecisionGoal PreDecrement PredictionRoot PreemptProtect PreferencesPath Prefix PreIncrement Prepend PrependTo PreserveImageOptions Previous PriceGraphDistribution PrimaryPlaceholder Prime PrimeNu PrimeOmega PrimePi PrimePowerQ PrimeQ Primes PrimeZetaP PrimitiveRoot PrincipalComponents PrincipalValue Print PrintAction PrintForm PrintingCopies PrintingOptions PrintingPageRange PrintingStartingPageNumber PrintingStyleEnvironment PrintPrecision PrintTemporary Prism PrismBox PrismBoxOptions PrivateCellOptions PrivateEvaluationOptions PrivateFontOptions PrivateFrontEndOptions PrivateNotebookOptions PrivatePaths Probability ProbabilityDistribution ProbabilityPlot ProbabilityPr ProbabilityScalePlot ProbitModelFit ProcessEstimator ProcessParameterAssumptions ProcessParameterQ ProcessStateDomain ProcessTimeDomain Product ProductDistribution ProductLog ProgressIndicator ProgressIndicatorBox ProgressIndicatorBoxOptions Projection Prolog PromptForm Properties Property PropertyList PropertyValue Proportion Proportional Protect Protected ProteinData Pruning PseudoInverse Purple Put PutAppend Pyramid PyramidBox PyramidBoxOptions QBinomial QFactorial QGamma QHypergeometricPFQ QPochhammer QPolyGamma QRDecomposition QuadraticIrrationalQ Quantile QuantilePlot Quantity QuantityForm QuantityMagnitude QuantityQ QuantityUnit Quartics QuartileDeviation Quartiles QuartileSkewness QueueingNetworkProcess QueueingProcess QueueProperties Quiet Quit Quotient QuotientRemainder RadialityCentrality RadicalBox RadicalBoxOptions RadioButton RadioButtonBar RadioButtonBox RadioButtonBoxOptions Radon RamanujanTau RamanujanTauL RamanujanTauTheta RamanujanTauZ Random RandomChoice RandomComplex RandomFunction RandomGraph RandomImage RandomInteger RandomPermutation RandomPrime RandomReal RandomSample RandomSeed RandomVariate RandomWalkProcess Range RangeFilter RangeSpecification RankedMax RankedMin Raster Raster3D Raster3DBox Raster3DBoxOptions RasterArray RasterBox RasterBoxOptions Rasterize RasterSize Rational RationalFunctions Rationalize Rationals Ratios Raw RawArray RawBoxes RawData RawMedium RayleighDistribution Re Read ReadList ReadProtected Real RealBlockDiagonalForm RealDigits RealExponent Reals Reap Record RecordLists RecordSeparators Rectangle RectangleBox RectangleBoxOptions RectangleChart RectangleChart3D RecurrenceFilter RecurrenceTable RecurringDigitsForm Red Reduce RefBox ReferenceLineStyle ReferenceMarkers ReferenceMarkerStyle Refine ReflectionMatrix ReflectionTransform Refresh RefreshRate RegionBinarize RegionFunction RegionPlot RegionPlot3D RegularExpression Regularization Reinstall Release ReleaseHold ReliabilityDistribution ReliefImage ReliefPlot Remove RemoveAlphaChannel RemoveAsynchronousTask Removed RemoveInputStreamMethod RemoveOutputStreamMethod RemoveProperty RemoveScheduledTask RenameDirectory RenameFile RenderAll RenderingOptions RenewalProcess RenkoChart Repeated RepeatedNull RepeatedString Replace ReplaceAll ReplaceHeldPart ReplaceImageValue ReplaceList ReplacePart ReplacePixelValue ReplaceRepeated Resampling Rescale RescalingTransform ResetDirectory ResetMenusPacket ResetScheduledTask Residue Resolve Rest Resultant ResumePacket Return ReturnExpressionPacket ReturnInputFormPacket ReturnPacket ReturnTextPacket Reverse ReverseBiorthogonalSplineWavelet ReverseElement ReverseEquilibrium ReverseGraph ReverseUpEquilibrium RevolutionAxis RevolutionPlot3D RGBColor RiccatiSolve RiceDistribution RidgeFilter RiemannR RiemannSiegelTheta RiemannSiegelZ Riffle Right RightArrow RightArrowBar RightArrowLeftArrow RightCosetRepresentative RightDownTeeVector RightDownVector RightDownVectorBar RightTee RightTeeArrow RightTeeVector RightTriangle RightTriangleBar RightTriangleEqual RightUpDownVector RightUpTeeVector RightUpVector RightUpVectorBar RightVector RightVectorBar RiskAchievementImportance RiskReductionImportance RogersTanimotoDissimilarity Root RootApproximant RootIntervals RootLocusPlot RootMeanSquare RootOfUnityQ RootReduce Roots RootSum Rotate RotateLabel RotateLeft RotateRight RotationAction RotationBox RotationBoxOptions RotationMatrix RotationTransform Round RoundImplies RoundingRadius Row RowAlignments RowBackgrounds RowBox RowHeights RowLines RowMinHeight RowReduce RowsEqual RowSpacings RSolve RudvalisGroupRu Rule RuleCondition RuleDelayed RuleForm RulerUnits Run RunScheduledTask RunThrough RuntimeAttributes RuntimeOptions RussellRaoDissimilarity SameQ SameTest SampleDepth SampledSoundFunction SampledSoundList SampleRate SamplingPeriod SARIMAProcess SARMAProcess SatisfiabilityCount SatisfiabilityInstances SatisfiableQ Saturday Save Saveable SaveAutoDelete SaveDefinitions SawtoothWave Scale Scaled ScaleDivisions ScaledMousePosition ScaleOrigin ScalePadding ScaleRanges ScaleRangeStyle ScalingFunctions ScalingMatrix ScalingTransform Scan ScheduledTaskActiveQ ScheduledTaskData ScheduledTaskObject ScheduledTasks SchurDecomposition ScientificForm ScreenRectangle ScreenStyleEnvironment ScriptBaselineShifts ScriptLevel ScriptMinSize ScriptRules ScriptSizeMultipliers Scrollbars ScrollingOptions ScrollPosition Sec Sech SechDistribution SectionGrouping SectorChart SectorChart3D SectorOrigin SectorSpacing SeedRandom Select Selectable SelectComponents SelectedCells SelectedNotebook Selection SelectionAnimate SelectionCell SelectionCellCreateCell SelectionCellDefaultStyle SelectionCellParentStyle SelectionCreateCell SelectionDebuggerTag SelectionDuplicateCell SelectionEvaluate SelectionEvaluateCreateCell SelectionMove SelectionPlaceholder SelectionSetStyle SelectWithContents SelfLoops SelfLoopStyle SemialgebraicComponentInstances SendMail Sequence SequenceAlignment SequenceForm SequenceHold SequenceLimit Series SeriesCoefficient SeriesData SessionTime Set SetAccuracy SetAlphaChannel SetAttributes Setbacks SetBoxFormNamesPacket SetDelayed SetDirectory SetEnvironment SetEvaluationNotebook SetFileDate SetFileLoadingContext SetNotebookStatusLine SetOptions SetOptionsPacket SetPrecision SetProperty SetSelectedNotebook SetSharedFunction SetSharedVariable SetSpeechParametersPacket SetStreamPosition SetSystemOptions Setter SetterBar SetterBox SetterBoxOptions Setting SetValue Shading Shallow ShannonWavelet ShapiroWilkTest Share Sharpen ShearingMatrix ShearingTransform ShenCastanMatrix Short ShortDownArrow Shortest ShortestMatch ShortestPathFunction ShortLeftArrow ShortRightArrow ShortUpArrow Show ShowAutoStyles ShowCellBracket ShowCellLabel ShowCellTags ShowClosedCellArea ShowContents ShowControls ShowCursorTracker ShowGroupOpenCloseIcon ShowGroupOpener ShowInvisibleCharacters ShowPageBreaks ShowPredictiveInterface ShowSelection ShowShortBoxForm ShowSpecialCharacters ShowStringCharacters ShowSyntaxStyles ShrinkingDelay ShrinkWrapBoundingBox SiegelTheta SiegelTukeyTest Sign Signature SignedRankTest SignificanceLevel SignPadding SignTest SimilarityRules SimpleGraph SimpleGraphQ Simplify Sin Sinc SinghMaddalaDistribution SingleEvaluation SingleLetterItalics SingleLetterStyle SingularValueDecomposition SingularValueList SingularValuePlot SingularValues Sinh SinhIntegral SinIntegral SixJSymbol Skeleton SkeletonTransform SkellamDistribution Skewness SkewNormalDistribution Skip SliceDistribution Slider Slider2D Slider2DBox Slider2DBoxOptions SliderBox SliderBoxOptions SlideView Slot SlotSequence Small SmallCircle Smaller SmithDelayCompensator SmithWatermanSimilarity SmoothDensityHistogram SmoothHistogram SmoothHistogram3D SmoothKernelDistribution SocialMediaData Socket SokalSneathDissimilarity Solve SolveAlways SolveDelayed Sort SortBy Sound SoundAndGraphics SoundNote SoundVolume Sow Space SpaceForm Spacer Spacings Span SpanAdjustments SpanCharacterRounding SpanFromAbove SpanFromBoth SpanFromLeft SpanLineThickness SpanMaxSize SpanMinSize SpanningCharacters SpanSymmetric SparseArray SpatialGraphDistribution Speak SpeakTextPacket SpearmanRankTest SpearmanRho Spectrogram SpectrogramArray Specularity SpellingCorrection SpellingDictionaries SpellingDictionariesPath SpellingOptions SpellingSuggestionsPacket Sphere SphereBox SphericalBesselJ SphericalBesselY SphericalHankelH1 SphericalHankelH2 SphericalHarmonicY SphericalPlot3D SphericalRegion SpheroidalEigenvalue SpheroidalJoiningFactor SpheroidalPS SpheroidalPSPrime SpheroidalQS SpheroidalQSPrime SpheroidalRadialFactor SpheroidalS1 SpheroidalS1Prime SpheroidalS2 SpheroidalS2Prime Splice SplicedDistribution SplineClosed SplineDegree SplineKnots SplineWeights Split SplitBy SpokenString Sqrt SqrtBox SqrtBoxOptions Square SquaredEuclideanDistance SquareFreeQ SquareIntersection SquaresR SquareSubset SquareSubsetEqual SquareSuperset SquareSupersetEqual SquareUnion SquareWave StabilityMargins StabilityMarginsStyle StableDistribution Stack StackBegin StackComplete StackInhibit StandardDeviation StandardDeviationFilter StandardForm Standardize StandbyDistribution Star StarGraph StartAsynchronousTask StartingStepSize StartOfLine StartOfString StartScheduledTask StartupSound StateDimensions StateFeedbackGains StateOutputEstimator StateResponse StateSpaceModel StateSpaceRealization StateSpaceTransform StationaryDistribution StationaryWaveletPacketTransform StationaryWaveletTransform StatusArea StatusCentrality StepMonitor StieltjesGamma StirlingS1 StirlingS2 StopAsynchronousTask StopScheduledTask StrataVariables StratonovichProcess StreamColorFunction StreamColorFunctionScaling StreamDensityPlot StreamPlot StreamPoints StreamPosition Streams StreamScale StreamStyle String StringBreak StringByteCount StringCases StringCount StringDrop StringExpression StringForm StringFormat StringFreeQ StringInsert StringJoin StringLength StringMatchQ StringPosition StringQ StringReplace StringReplaceList StringReplacePart StringReverse StringRotateLeft StringRotateRight StringSkeleton StringSplit StringTake StringToStream StringTrim StripBoxes StripOnInput StripWrapperBoxes StrokeForm StructuralImportance StructuredArray StructuredSelection StruveH StruveL Stub StudentTDistribution Style StyleBox StyleBoxAutoDelete StyleBoxOptions StyleData StyleDefinitions StyleForm StyleKeyMapping StyleMenuListing StyleNameDialogSettings StyleNames StylePrint StyleSheetPath Subfactorial Subgraph SubMinus SubPlus SubresultantPolynomialRemainders SubresultantPolynomials Subresultants Subscript SubscriptBox SubscriptBoxOptions Subscripted Subset SubsetEqual Subsets SubStar Subsuperscript SubsuperscriptBox SubsuperscriptBoxOptions Subtract SubtractFrom SubValues Succeeds SucceedsEqual SucceedsSlantEqual SucceedsTilde SuchThat Sum SumConvergence Sunday SuperDagger SuperMinus SuperPlus Superscript SuperscriptBox SuperscriptBoxOptions Superset SupersetEqual SuperStar Surd SurdForm SurfaceColor SurfaceGraphics SurvivalDistribution SurvivalFunction SurvivalModel SurvivalModelFit SuspendPacket SuzukiDistribution SuzukiGroupSuz SwatchLegend Switch Symbol SymbolName SymletWavelet Symmetric SymmetricGroup SymmetricMatrixQ SymmetricPolynomial SymmetricReduction Symmetrize SymmetrizedArray SymmetrizedArrayRules SymmetrizedDependentComponents SymmetrizedIndependentComponents SymmetrizedReplacePart SynchronousInitialization SynchronousUpdating Syntax SyntaxForm SyntaxInformation SyntaxLength SyntaxPacket SyntaxQ SystemDialogInput SystemException SystemHelpPath SystemInformation SystemInformationData SystemOpen SystemOptions SystemsModelDelay SystemsModelDelayApproximate SystemsModelDelete SystemsModelDimensions SystemsModelExtract SystemsModelFeedbackConnect SystemsModelLabels SystemsModelOrder SystemsModelParallelConnect SystemsModelSeriesConnect SystemsModelStateFeedbackConnect SystemStub Tab TabFilling Table TableAlignments TableDepth TableDirections TableForm TableHeadings TableSpacing TableView TableViewBox TabSpacings TabView TabViewBox TabViewBoxOptions TagBox TagBoxNote TagBoxOptions TaggingRules TagSet TagSetDelayed TagStyle TagUnset Take TakeWhile Tally Tan Tanh TargetFunctions TargetUnits TautologyQ TelegraphProcess TemplateBox TemplateBoxOptions TemplateSlotSequence TemporalData Temporary TemporaryVariable TensorContract TensorDimensions TensorExpand TensorProduct TensorQ TensorRank TensorReduce TensorSymmetry TensorTranspose TensorWedge Tetrahedron TetrahedronBox TetrahedronBoxOptions TeXForm TeXSave Text Text3DBox Text3DBoxOptions TextAlignment TextBand TextBoundingBox TextBox TextCell TextClipboardType TextData TextForm TextJustification TextLine TextPacket TextParagraph TextRecognize TextRendering TextStyle Texture TextureCoordinateFunction TextureCoordinateScaling Therefore ThermometerGauge Thick Thickness Thin Thinning ThisLink ThompsonGroupTh Thread ThreeJSymbol Threshold Through Throw Thumbnail Thursday Ticks TicksStyle Tilde TildeEqual TildeFullEqual TildeTilde TimeConstrained TimeConstraint Times TimesBy TimeSeriesForecast TimeSeriesInvertibility TimeUsed TimeValue TimeZone Timing Tiny TitleGrouping TitsGroupT ToBoxes ToCharacterCode ToColor ToContinuousTimeModel ToDate ToDiscreteTimeModel ToeplitzMatrix ToExpression ToFileName Together Toggle ToggleFalse Toggler TogglerBar TogglerBox TogglerBoxOptions ToHeldExpression ToInvertibleTimeSeries TokenWords Tolerance ToLowerCase ToNumberField TooBig Tooltip TooltipBox TooltipBoxOptions TooltipDelay TooltipStyle Top TopHatTransform TopologicalSort ToRadicals ToRules ToString Total TotalHeight TotalVariationFilter TotalWidth TouchscreenAutoZoom TouchscreenControlPlacement ToUpperCase Tr Trace TraceAbove TraceAction TraceBackward TraceDepth TraceDialog TraceForward TraceInternal TraceLevel TraceOff TraceOn TraceOriginal TracePrint TraceScan TrackedSymbols TradingChart TraditionalForm TraditionalFunctionNotation TraditionalNotation TraditionalOrder TransferFunctionCancel TransferFunctionExpand TransferFunctionFactor TransferFunctionModel TransferFunctionPoles TransferFunctionTransform TransferFunctionZeros TransformationFunction TransformationFunctions TransformationMatrix TransformedDistribution TransformedField Translate TranslationTransform TransparentColor Transpose TreeForm TreeGraph TreeGraphQ TreePlot TrendStyle TriangleWave TriangularDistribution Trig TrigExpand TrigFactor TrigFactorList Trigger TrigReduce TrigToExp TrimmedMean True TrueQ TruncatedDistribution TsallisQExponentialDistribution TsallisQGaussianDistribution TTest Tube TubeBezierCurveBox TubeBezierCurveBoxOptions TubeBox TubeBSplineCurveBox TubeBSplineCurveBoxOptions Tuesday TukeyLambdaDistribution TukeyWindow Tuples TuranGraph TuringMachine Transparent UnateQ Uncompress Undefined UnderBar Underflow Underlined Underoverscript UnderoverscriptBox UnderoverscriptBoxOptions Underscript UnderscriptBox UnderscriptBoxOptions UndirectedEdge UndirectedGraph UndirectedGraphQ UndocumentedTestFEParserPacket UndocumentedTestGetSelectionPacket Unequal Unevaluated UniformDistribution UniformGraphDistribution UniformSumDistribution Uninstall Union UnionPlus Unique UnitBox UnitConvert UnitDimensions Unitize UnitRootTest UnitSimplify UnitStep UnitTriangle UnitVector Unprotect UnsameQ UnsavedVariables Unset UnsetShared UntrackedVariables Up UpArrow UpArrowBar UpArrowDownArrow Update UpdateDynamicObjects UpdateDynamicObjectsSynchronous UpdateInterval UpDownArrow UpEquilibrium UpperCaseQ UpperLeftArrow UpperRightArrow UpperTriangularize Upsample UpSet UpSetDelayed UpTee UpTeeArrow UpValues URL URLFetch URLFetchAsynchronous URLSave URLSaveAsynchronous UseGraphicsRange Using UsingFrontEnd V2Get ValidationLength Value ValueBox ValueBoxOptions ValueForm ValueQ ValuesData Variables Variance VarianceEquivalenceTest VarianceEstimatorFunction VarianceGammaDistribution VarianceTest VectorAngle VectorColorFunction VectorColorFunctionScaling VectorDensityPlot VectorGlyphData VectorPlot VectorPlot3D VectorPoints VectorQ Vectors VectorScale VectorStyle Vee Verbatim Verbose VerboseConvertToPostScriptPacket VerifyConvergence VerifySolutions VerifyTestAssumptions Version VersionNumber VertexAdd VertexCapacity VertexColors VertexComponent VertexConnectivity VertexCoordinateRules VertexCoordinates VertexCorrelationSimilarity VertexCosineSimilarity VertexCount VertexCoverQ VertexDataCoordinates VertexDegree VertexDelete VertexDiceSimilarity VertexEccentricity VertexInComponent VertexInDegree VertexIndex VertexJaccardSimilarity VertexLabeling VertexLabels VertexLabelStyle VertexList VertexNormals VertexOutComponent VertexOutDegree VertexQ VertexRenderingFunction VertexReplace VertexShape VertexShapeFunction VertexSize VertexStyle VertexTextureCoordinates VertexWeight Vertical VerticalBar VerticalForm VerticalGauge VerticalSeparator VerticalSlider VerticalTilde ViewAngle ViewCenter ViewMatrix ViewPoint ViewPointSelectorSettings ViewPort ViewRange ViewVector ViewVertical VirtualGroupData Visible VisibleCell VoigtDistribution VonMisesDistribution WaitAll WaitAsynchronousTask WaitNext WaitUntil WakebyDistribution WalleniusHypergeometricDistribution WaringYuleDistribution WatershedComponents WatsonUSquareTest WattsStrogatzGraphDistribution WaveletBestBasis WaveletFilterCoefficients WaveletImagePlot WaveletListPlot WaveletMapIndexed WaveletMatrixPlot WaveletPhi WaveletPsi WaveletScale WaveletScalogram WaveletThreshold WeaklyConnectedComponents WeaklyConnectedGraphQ WeakStationarity WeatherData WeberE Wedge Wednesday WeibullDistribution WeierstrassHalfPeriods WeierstrassInvariants WeierstrassP WeierstrassPPrime WeierstrassSigma WeierstrassZeta WeightedAdjacencyGraph WeightedAdjacencyMatrix WeightedData WeightedGraphQ Weights WelchWindow WheelGraph WhenEvent Which While White Whitespace WhitespaceCharacter WhittakerM WhittakerW WienerFilter WienerProcess WignerD WignerSemicircleDistribution WilksW WilksWTest WindowClickSelect WindowElements WindowFloating WindowFrame WindowFrameElements WindowMargins WindowMovable WindowOpacity WindowSelected WindowSize WindowStatusArea WindowTitle WindowToolbars WindowWidth With WolframAlpha WolframAlphaDate WolframAlphaQuantity WolframAlphaResult Word WordBoundary WordCharacter WordData WordSearch WordSeparators WorkingPrecision Write WriteString Wronskian XMLElement XMLObject Xnor Xor Yellow YuleDissimilarity ZernikeR ZeroSymmetric ZeroTest ZeroWidthTimes Zeta ZetaZero ZipfDistribution ZTest ZTransform $Aborted $ActivationGroupID $ActivationKey $ActivationUserRegistered $AddOnsDirectory $AssertFunction $Assumptions $AsynchronousTask $BaseDirectory $BatchInput $BatchOutput $BoxForms $ByteOrdering $Canceled $CharacterEncoding $CharacterEncodings $CommandLine $CompilationTarget $ConditionHold $ConfiguredKernels $Context $ContextPath $ControlActiveSetting $CreationDate $CurrentLink $DateStringFormat $DefaultFont $DefaultFrontEnd $DefaultImagingDevice $DefaultPath $Display $DisplayFunction $DistributedContexts $DynamicEvaluation $Echo $Epilog $ExportFormats $Failed $FinancialDataSource $FormatType $FrontEnd $FrontEndSession $GeoLocation $HistoryLength $HomeDirectory $HTTPCookies $IgnoreEOF $ImagingDevices $ImportFormats $InitialDirectory $Input $InputFileName $InputStreamMethods $Inspector $InstallationDate $InstallationDirectory $InterfaceEnvironment $IterationLimit $KernelCount $KernelID $Language $LaunchDirectory $LibraryPath $LicenseExpirationDate $LicenseID $LicenseProcesses $LicenseServer $LicenseSubprocesses $LicenseType $Line $Linked $LinkSupported $LoadedFiles $MachineAddresses $MachineDomain $MachineDomains $MachineEpsilon $MachineID $MachineName $MachinePrecision $MachineType $MaxExtraPrecision $MaxLicenseProcesses $MaxLicenseSubprocesses $MaxMachineNumber $MaxNumber $MaxPiecewiseCases $MaxPrecision $MaxRootDegree $MessageGroups $MessageList $MessagePrePrint $Messages $MinMachineNumber $MinNumber $MinorReleaseNumber $MinPrecision $ModuleNumber $NetworkLicense $NewMessage $NewSymbol $Notebooks $NumberMarks $Off $OperatingSystem $Output $OutputForms $OutputSizeLimit $OutputStreamMethods $Packages $ParentLink $ParentProcessID $PasswordFile $PatchLevelID $Path $PathnameSeparator $PerformanceGoal $PipeSupported $Post $Pre $PreferencesDirectory $PrePrint $PreRead $PrintForms $PrintLiteral $ProcessID $ProcessorCount $ProcessorType $ProductInformation $ProgramName $RandomState $RecursionLimit $ReleaseNumber $RootDirectory $ScheduledTask $ScriptCommandLine $SessionID $SetParentLink $SharedFunctions $SharedVariables $SoundDisplay $SoundDisplayFunction $SuppressInputFormHeads $SynchronousEvaluation $SyntaxHandler $System $SystemCharacterEncoding $SystemID $SystemWordLength $TemporaryDirectory $TemporaryPrefix $TextStyle $TimedOut $TimeUnit $TimeZone $TopDirectory $TraceOff $TraceOn $TracePattern $TracePostAction $TracePreAction $Urgent $UserAddOnsDirectory $UserBaseDirectory $UserDocumentsDirectory $UserName $Version $VersionNumber", +c:[{cN:"comment",b:/\(\*/,e:/\*\)/},e.ASM,e.QSM,e.CNM,{cN:"list",b:/\{/,e:/\}/,i:/:/}]}});hljs.registerLanguage("fsharp",function(e){var t={b:"<",e:">",c:[e.inherit(e.TM,{b:/'[a-zA-Z0-9_]+/})]};return{aliases:["fs"],k:"yield! return! let! do!abstract and as assert base begin class default delegate do done downcast downto elif else end exception extern false finally for fun function global if in inherit inline interface internal lazy let match member module mutable namespace new null of open or override private public rec return sig static struct then to true try type upcast use val void when while with yield",c:[{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},{cN:"string",b:'"""',e:'"""'},e.C("\\(\\*","\\*\\)"),{cN:"class",bK:"type",e:"\\(|=|$",eE:!0,c:[e.UTM,t]},{cN:"annotation",b:"\\[<",e:">\\]",r:10},{cN:"attribute",b:"\\B('[A-Za-z])\\b",c:[e.BE]},e.CLCM,e.inherit(e.QSM,{i:null}),e.CNM]}});hljs.registerLanguage("verilog",function(e){return{aliases:["v"],cI:!0,k:{keyword:"always and assign begin buf bufif0 bufif1 case casex casez cmos deassign default defparam disable edge else end endcase endfunction endmodule endprimitive endspecify endtable endtask event for force forever fork function if ifnone initial inout input join macromodule module nand negedge nmos nor not notif0 notif1 or output parameter pmos posedge primitive pulldown pullup rcmos release repeat rnmos rpmos rtran rtranif0 rtranif1 specify specparam table task timescale tran tranif0 tranif1 wait while xnor xor",typename:"highz0 highz1 integer large medium pull0 pull1 real realtime reg scalared signed small strong0 strong1 supply0 supply0 supply1 supply1 time tri tri0 tri1 triand trior trireg vectored wand weak0 weak1 wire wor"},c:[e.CBCM,e.CLCM,e.QSM,{cN:"number",b:"\\b(\\d+'(b|h|o|d|B|H|O|D))?[0-9xzXZ]+",c:[e.BE],r:0},{cN:"typename",b:"\\.\\w+",r:0},{cN:"value",b:"#\\((?!parameter).+\\)"},{cN:"keyword",b:"\\+|-|\\*|/|%|<|>|=|#|`|\\!|&|\\||@|:|\\^|~|\\{|\\}",r:0}]}});hljs.registerLanguage("dos",function(e){var r=e.C(/@?rem\b/,/$/,{r:10}),t={cN:"label",b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)",r:0};return{aliases:["bat","cmd"],cI:!0,k:{flow:"if else goto for in do call exit not exist errorlevel defined",operator:"equ neq lss leq gtr geq",keyword:"shift cd dir echo setlocal endlocal set pause copy",stream:"prn nul lpt3 lpt2 lpt1 con com4 com3 com2 com1 aux",winutils:"ping net ipconfig taskkill xcopy ren del",built_in:"append assoc at attrib break cacls cd chcp chdir chkdsk chkntfs cls cmd color comp compact convert date dir diskcomp diskcopy doskey erase fs find findstr format ftype graftabl help keyb label md mkdir mode more move path pause print popd pushd promt rd recover rem rename replace restore rmdir shiftsort start subst time title tree type ver verify vol"},c:[{cN:"envvar",b:/%%[^ ]|%[^ ]+?%|![^ ]+?!/},{cN:"function",b:t.b,e:"goto:eof",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),r]},{cN:"number",b:"\\b\\d+",r:0},r]}});hljs.registerLanguage("gherkin",function(e){return{aliases:["feature"],k:"Feature Background Ability Business Need Scenario Scenarios Scenario Outline Scenario Template Examples Given And Then But When",c:[{cN:"keyword",b:"\\*"},e.C("@[^@\r\n ]+","$"),{cN:"string",b:"\\|",e:"\\$"},{cN:"variable",b:"<",e:">"},e.HCM,{cN:"string",b:'"""',e:'"""'},e.QSM]}});hljs.registerLanguage("xml",function(t){var e="[A-Za-z0-9\\._:-]+",s={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},c={eW:!0,i:/</,r:0,c:[s,{cN:"attribute",b:e,r:0},{b:"=",r:0,c:[{cN:"value",c:[s],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},t.C("<!--","-->",{r:10}),{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[c],starts:{e:"</style>",rE:!0,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[c],starts:{e:"</script>",rE:!0,sL:""}},s,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},c]}]}});hljs.registerLanguage("autohotkey",function(e){var r={cN:"escape",b:"`[\\s\\S]"},c=e.C(";","$",{r:0}),n=[{cN:"built_in",b:"A_[a-zA-Z0-9]+"},{cN:"built_in",bK:"ComSpec Clipboard ClipboardAll ErrorLevel"}];return{cI:!0,k:{keyword:"Break Continue Else Gosub If Loop Return While",literal:"A true false NOT AND OR"},c:n.concat([r,e.inherit(e.QSM,{c:[r]}),c,{cN:"number",b:e.NR,r:0},{cN:"var_expand",b:"%",e:"%",i:"\\n",c:[r]},{cN:"label",c:[r],v:[{b:'^[^\\n";]+::(?!=)'},{b:'^[^\\n";]+:(?!=)',r:0}]},{b:",\\s*,",r:10}])}});hljs.registerLanguage("r",function(e){var r="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{c:[e.HCM,{b:r,l:r,k:{keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},r:0},{cN:"number",b:"0[xX][0-9a-fA-F]+[Li]?\\b",r:0},{cN:"number",b:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",r:0},{cN:"number",b:"\\d+\\.(?!\\d)(?:i\\b)?",r:0},{cN:"number",b:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",r:0},{cN:"number",b:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",r:0},{b:"`",e:"`",r:0},{cN:"string",c:[e.BE],v:[{b:'"',e:'"'},{b:"'",e:"'"}]}]}});hljs.registerLanguage("cs",function(e){var r="abstract as base bool break byte case catch char checked const continue decimal dynamic default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long null when object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async protected public private internal ascending descending from get group into join let orderby partial select set value var where yield",t=e.IR+"(<"+e.IR+">)?";return{aliases:["csharp"],k:r,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:"<!--|-->"},{b:"</?",e:">"}]}]}),e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("nsis",function(e){var t={cN:"symbol",b:"\\$(ADMINTOOLS|APPDATA|CDBURN_AREA|CMDLINE|COMMONFILES32|COMMONFILES64|COMMONFILES|COOKIES|DESKTOP|DOCUMENTS|EXEDIR|EXEFILE|EXEPATH|FAVORITES|FONTS|HISTORY|HWNDPARENT|INSTDIR|INTERNET_CACHE|LANGUAGE|LOCALAPPDATA|MUSIC|NETHOOD|OUTDIR|PICTURES|PLUGINSDIR|PRINTHOOD|PROFILE|PROGRAMFILES32|PROGRAMFILES64|PROGRAMFILES|QUICKLAUNCH|RECENT|RESOURCES_LOCALIZED|RESOURCES|SENDTO|SMPROGRAMS|SMSTARTUP|STARTMENU|SYSDIR|TEMP|TEMPLATES|VIDEOS|WINDIR)"},n={cN:"constant",b:"\\$+{[a-zA-Z0-9_]+}"},i={cN:"variable",b:"\\$+[a-zA-Z0-9_]+",i:"\\(\\){}"},r={cN:"constant",b:"\\$+\\([a-zA-Z0-9_]+\\)"},o={cN:"params",b:"(ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SYSTEM|TEMPORARY)"},l={cN:"constant",b:"\\!(addincludedir|addplugindir|appendfile|cd|define|delfile|echo|else|endif|error|execute|finalize|getdllversionsystem|ifdef|ifmacrodef|ifmacrondef|ifndef|if|include|insertmacro|macroend|macro|makensis|packhdr|searchparse|searchreplace|tempfile|undef|verbose|warning)"};return{cI:!1,k:{keyword:"Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ChangeUI CheckBitmap ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exch Exec ExecShell ExecWait ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileReadUTF16LE FileReadWord FileSeek FileWrite FileWriteByte FileWriteUTF16LE FileWriteWord FindClose FindFirst FindNext FindWindow FlushINI FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LockWindow LogSet LogText ManifestDPIAware ManifestSupportedOS MessageBox MiscButtonText Name Nop OutFile Page PageCallbacks PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename RequestExecutionLevel ReserveFile Return RMDir SearchPath SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionGroupEnd SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetRegView SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCmpS StrCpy StrLen SubCaption SubSectionEnd Unicode UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIFileVersion VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle",literal:"admin all auto both colored current false force hide highest lastused leave listonly none normal notset off on open print show silent silentlog smooth textonly true user "},c:[e.HCM,e.CBCM,{cN:"string",b:'"',e:'"',i:"\\n",c:[{cN:"symbol",b:"\\$(\\\\(n|r|t)|\\$)"},t,n,i,r]},e.C(";","$",{r:0}),{cN:"function",bK:"Function PageEx Section SectionGroup SubSection",e:"$"},l,n,i,r,o,e.NM,{cN:"literal",b:e.IR+"::"+e.IR}]}});hljs.registerLanguage("less",function(e){var r="[\\w-]+",t="("+r+"|@{"+r+"})",a=[],c=[],n=function(e){return{cN:"string",b:"~?"+e+".*?"+e}},i=function(e,r,t){return{cN:e,b:r,r:t}},s=function(r,t,a){return e.inherit({cN:r,b:t+"\\(",e:"\\(",rB:!0,eE:!0,r:0},a)},b={b:"\\(",e:"\\)",c:c,r:0};c.push(e.CLCM,e.CBCM,n("'"),n('"'),e.CSSNM,i("hexcolor","#[0-9A-Fa-f]+\\b"),s("function","(url|data-uri)",{starts:{cN:"string",e:"[\\)\\n]",eE:!0}}),s("function",r),b,i("variable","@@?"+r,10),i("variable","@{"+r+"}"),i("built_in","~?`[^`]*?`"),{cN:"attribute",b:r+"\\s*:",e:":",rB:!0,eE:!0});var o=c.concat({b:"{",e:"}",c:a}),u={bK:"when",eW:!0,c:[{bK:"and not"}].concat(c)},C={cN:"attribute",b:t,e:":",eE:!0,c:[e.CLCM,e.CBCM],i:/\S/,starts:{e:"[;}]",rE:!0,c:c,i:"[<=$]"}},l={cN:"at_rule",b:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{e:"[;{}]",rE:!0,c:c,r:0}},d={cN:"variable",v:[{b:"@"+r+"\\s*:",r:15},{b:"@"+r}],starts:{e:"[;}]",rE:!0,c:o}},p={v:[{b:"[\\.#:&\\[]",e:"[;{}]"},{b:t+"[^;]*{",e:"{"}],rB:!0,rE:!0,i:"[<='$\"]",c:[e.CLCM,e.CBCM,u,i("keyword","all\\b"),i("variable","@{"+r+"}"),i("tag",t+"%?",0),i("id","#"+t),i("class","\\."+t,0),i("keyword","&",0),s("pseudo",":not"),s("keyword",":extend"),i("pseudo","::?"+t),{cN:"attr_selector",b:"\\[",e:"\\]"},{b:"\\(",e:"\\)",c:o},{b:"!important"}]};return a.push(e.CLCM,e.CBCM,l,d,p,C),{cI:!0,i:"[=>'/<($\"]",c:a}});hljs.registerLanguage("pf",function(t){var o={cN:"variable",b:/\$[\w\d#@][\w\d_]*/},e={cN:"variable",b:/</,e:/>/};return{aliases:["pf.conf"],l:/[a-z0-9_<>-]+/,k:{built_in:"block match pass load anchor|5 antispoof|10 set table",keyword:"in out log quick on rdomain inet inet6 proto from port os to routeallow-opts divert-packet divert-reply divert-to flags group icmp-typeicmp6-type label once probability recieved-on rtable prio queuetos tag tagged user keep fragment for os dropaf-to|10 binat-to|10 nat-to|10 rdr-to|10 bitmask least-stats random round-robinsource-hash static-portdup-to reply-to route-toparent bandwidth default min max qlimitblock-policy debug fingerprints hostid limit loginterface optimizationreassemble ruleset-optimization basic none profile skip state-defaultsstate-policy timeoutconst counters persistno modulate synproxy state|5 floating if-bound no-sync pflow|10 sloppysource-track global rule max-src-nodes max-src-states max-src-connmax-src-conn-rate overload flushscrub|5 max-mss min-ttl no-df|10 random-id",literal:"all any no-route self urpf-failed egress|5 unknown"},c:[t.HCM,t.NM,t.QSM,o,e]}});hljs.registerLanguage("lasso",function(e){var r="[a-zA-Z_][a-zA-Z0-9_.]*",a="<\\?(lasso(script)?|=)",t="\\]|\\?>",s={literal:"true false none minimal full all void and or not bw nbw ew new cn ncn lt lte gt gte eq neq rx nrx ft",built_in:"array date decimal duration integer map pair string tag xml null boolean bytes keyword list locale queue set stack staticarray local var variable global data self inherited",keyword:"error_code error_msg error_pop error_push error_reset cache database_names database_schemanames database_tablenames define_tag define_type email_batch encode_set html_comment handle handle_error header if inline iterate ljax_target link link_currentaction link_currentgroup link_currentrecord link_detail link_firstgroup link_firstrecord link_lastgroup link_lastrecord link_nextgroup link_nextrecord link_prevgroup link_prevrecord log loop namespace_using output_none portal private protect records referer referrer repeating resultset rows search_args search_arguments select sort_args sort_arguments thread_atomic value_list while abort case else if_empty if_false if_null if_true loop_abort loop_continue loop_count params params_up return return_value run_children soap_definetag soap_lastrequest soap_lastresponse tag_name ascending average by define descending do equals frozen group handle_failure import in into join let match max min on order parent protected provide public require returnhome skip split_thread sum take thread to trait type where with yield yieldhome"},n=e.C("<!--","-->",{r:0}),o={cN:"preprocessor",b:"\\[noprocess\\]",starts:{cN:"markup",e:"\\[/noprocess\\]",rE:!0,c:[n]}},i={cN:"preprocessor",b:"\\[/noprocess|"+a},l={cN:"variable",b:"'"+r+"'"},c=[e.CLCM,{cN:"javadoc",b:"/\\*\\*!",e:"\\*/",c:[e.PWM]},e.CBCM,e.inherit(e.CNM,{b:e.CNR+"|(-?infinity|nan)\\b"}),e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"string",b:"`",e:"`"},{cN:"variable",v:[{b:"[#$]"+r},{b:"#",e:"\\d+",i:"\\W"}]},{cN:"tag",b:"::\\s*",e:r,i:"\\W"},{cN:"attribute",v:[{b:"-"+e.UIR,r:0},{b:"(\\.\\.\\.)"}]},{cN:"subst",v:[{b:"->\\s*",c:[l]},{b:":=|/(?!\\w)=?|[-+*%=<>&|!?\\\\]+",r:0}]},{cN:"built_in",b:"\\.\\.?\\s*",r:0,c:[l]},{cN:"class",bK:"define",rE:!0,e:"\\(|=>",c:[e.inherit(e.TM,{b:e.UIR+"(=(?!>))?"})]}];return{aliases:["ls","lassoscript"],cI:!0,l:r+"|&[lg]t;",k:s,c:[{cN:"preprocessor",b:t,r:0,starts:{cN:"markup",e:"\\[|"+a,rE:!0,r:0,c:[n]}},o,i,{cN:"preprocessor",b:"\\[no_square_brackets",starts:{e:"\\[/no_square_brackets\\]",l:r+"|&[lg]t;",k:s,c:[{cN:"preprocessor",b:t,r:0,starts:{cN:"markup",e:"\\[noprocess\\]|"+a,rE:!0,c:[n]}},o,i].concat(c)}},{cN:"preprocessor",b:"\\[",r:0},{cN:"shebang",b:"^#!.+lasso9\\b",r:10}].concat(c)}});hljs.registerLanguage("prolog",function(c){var r={cN:"atom",b:/[a-z][A-Za-z0-9_]*/,r:0},b={cN:"name",v:[{b:/[A-Z][a-zA-Z0-9_]*/},{b:/_[A-Za-z0-9_]*/}],r:0},a={b:/\(/,e:/\)/,r:0},e={b:/\[/,e:/\]/},n={cN:"comment",b:/%/,e:/$/,c:[c.PWM]},t={cN:"string",b:/`/,e:/`/,c:[c.BE]},g={cN:"string",b:/0\'(\\\'|.)/},N={cN:"string",b:/0\'\\s/},o={b:/:-/},s=[r,b,a,o,e,n,c.CBCM,c.QSM,c.ASM,t,g,N,c.CNM];return a.c=s,e.c=s,{c:s.concat([{b:/\.$/}])}});hljs.registerLanguage("oxygene",function(e){var r="abstract add and array as asc aspect assembly async begin break block by case class concat const copy constructor continue create default delegate desc distinct div do downto dynamic each else empty end ensure enum equals event except exit extension external false final finalize finalizer finally flags for forward from function future global group has if implementation implements implies in index inherited inline interface into invariants is iterator join locked locking loop matching method mod module namespace nested new nil not notify nullable of old on operator or order out override parallel params partial pinned private procedure property protected public queryable raise read readonly record reintroduce remove repeat require result reverse sealed select self sequence set shl shr skip static step soft take then to true try tuple type union unit unsafe until uses using var virtual raises volatile where while with write xor yield await mapped deprecated stdcall cdecl pascal register safecall overload library platform reference packed strict published autoreleasepool selector strong weak unretained",t=e.C("{","}",{r:0}),a=e.C("\\(\\*","\\*\\)",{r:10}),n={cN:"string",b:"'",e:"'",c:[{b:"''"}]},o={cN:"string",b:"(#\\d+)+"},i={cN:"function",bK:"function constructor destructor procedure method",e:"[:;]",k:"function constructor|10 destructor|10 procedure|10 method|10",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",k:r,c:[n,o]},t,a]};return{cI:!0,k:r,i:'("|\\$[G-Zg-z]|\\/\\*|</|=>|->)',c:[t,a,e.CLCM,n,o,e.NM,i,{cN:"class",b:"=\\bclass\\b",e:"end;",k:r,c:[n,o,t,a,e.CLCM,i]}]}});hljs.registerLanguage("applescript",function(e){var t=e.inherit(e.QSM,{i:""}),r={cN:"params",b:"\\(",e:"\\)",c:["self",e.CNM,t]},o=e.C("--","$"),n=e.C("\\(\\*","\\*\\)",{c:["self",o]}),a=[o,n,e.HCM];return{aliases:["osascript"],k:{keyword:"about above after against and around as at back before beginning behind below beneath beside between but by considering contain contains continue copy div does eighth else end equal equals error every exit fifth first for fourth from front get given global if ignoring in into is it its last local me middle mod my ninth not of on onto or over prop property put ref reference repeat returning script second set seventh since sixth some tell tenth that the|0 then third through thru timeout times to transaction try until where while whose with without",constant:"AppleScript false linefeed return pi quote result space tab true",type:"alias application boolean class constant date file integer list number real record string text",command:"activate beep count delay launch log offset read round run say summarize write",property:"character characters contents day frontmost id item length month name paragraph paragraphs rest reverse running time version weekday word words year"},c:[t,e.CNM,{cN:"type",b:"\\bPOSIX file\\b"},{cN:"command",b:"\\b(clipboard info|the clipboard|info for|list (disks|folder)|mount volume|path to|(close|open for) access|(get|set) eof|current date|do shell script|get volume settings|random number|set volume|system attribute|system info|time to GMT|(load|run|store) script|scripting components|ASCII (character|number)|localized string|choose (application|color|file|file name|folder|from list|remote application|URL)|display (alert|dialog))\\b|^\\s*return\\b"},{cN:"constant",b:"\\b(text item delimiters|current application|missing value)\\b"},{cN:"keyword",b:"\\b(apart from|aside from|instead of|out of|greater than|isn't|(doesn't|does not) (equal|come before|come after|contain)|(greater|less) than( or equal)?|(starts?|ends|begins?) with|contained by|comes (before|after)|a (ref|reference))\\b"},{cN:"property",b:"\\b(POSIX path|(date|time) string|quoted form)\\b"},{cN:"function_start",bK:"on",i:"[${=;\\n]",c:[e.UTM,r]}].concat(a),i:"//|->|=>"}});hljs.registerLanguage("makefile",function(e){var a={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[a]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,a]}]}});hljs.registerLanguage("dust",function(e){var a="if eq ne lt lte gt gte select default math sep";return{aliases:["dst"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[{cN:"expression",b:"{",e:"}",r:0,c:[{cN:"begin-block",b:"#[a-zA-Z- .]+",k:a},{cN:"string",b:'"',e:'"'},{cN:"end-block",b:"\\/[a-zA-Z- .]+",k:a},{cN:"variable",b:"[a-zA-Z-.]+",k:a,r:0}]}]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"prompt",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure",subLanguageMode:"continuous"}}]}});hljs.registerLanguage("dart",function(e){var t={cN:"subst",b:"\\$\\{",e:"}",k:"true false null this is new super"},r={cN:"string",v:[{b:"r'''",e:"'''"},{b:'r"""',e:'"""'},{b:"r'",e:"'",i:"\\n"},{b:'r"',e:'"',i:"\\n"},{b:"'''",e:"'''",c:[e.BE,t]},{b:'"""',e:'"""',c:[e.BE,t]},{b:"'",e:"'",i:"\\n",c:[e.BE,t]},{b:'"',e:'"',i:"\\n",c:[e.BE,t]}]};t.c=[e.CNM,r];var n={keyword:"assert break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch this throw true try var void while with",literal:"abstract as dynamic export external factory get implements import library operator part set static typedef",built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"};return{k:n,c:[r,{cN:"dartdoc",b:"/\\*\\*",e:"\\*/",sL:"markdown",subLanguageMode:"continuous"},{cN:"dartdoc",b:"///",e:"$",sL:"markdown",subLanguageMode:"continuous"},e.CLCM,e.CBCM,{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.UTM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"},{b:"=>"}]}}); \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/jquery.json-view.min.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/jquery.json-view.min.js new file mode 100644 index 0000000..ce3a604 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/docs/js/jquery.json-view.min.js @@ -0,0 +1,7 @@ +/** + * jquery.json-view - jQuery collapsible JSON plugin + * @version v1.0.0 + * @link http://github.com/bazh/jquery.json-view + * @license MIT + */ +!function(e){"use strict";var n=function(n){var a=e("<span />",{"class":"collapser",on:{click:function(){var n=e(this);n.toggleClass("collapsed");var a=n.parent().children(".block"),p=a.children("ul");n.hasClass("collapsed")?(p.hide(),a.children(".dots, .comments").show()):(p.show(),a.children(".dots, .comments").hide())}}});return n&&a.addClass("collapsed"),a},a=function(a,p){var t=e.extend({},{nl2br:!0},p),r=function(e){return e.toString()?e.toString().replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"):""},s=function(n,a){return e("<span />",{"class":a,html:r(n)})},l=function(a,p){switch(e.type(a)){case"object":p||(p=0);var c=e("<span />",{"class":"block"}),d=Object.keys(a).length;if(!d)return c.append(s("{","b")).append(" ").append(s("}","b"));c.append(s("{","b"));var i=e("<ul />",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("<li />").append(s('"',"q")).append(a).append(s('"',"q")).append(": ").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("}","b")),c.append(1===Object.keys(a).length?s("// 1 item","comments"):s("// "+Object.keys(a).length+" items","comments")),c;case"array":p||(p=0);var d=a.length,c=e("<span />",{"class":"block"});if(!d)return c.append(s("[","b")).append(" ").append(s("]","b"));c.append(s("[","b"));var i=e("<ul />",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("<li />").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("]","b")),c.append(1===a.length?s("// 1 item","comments"):s("// "+a.length+" items","comments")),c;case"string":if(a=r(a),/^(http|https|file):\/\/[^\s]+$/i.test(a))return e("<span />").append(s('"',"q")).append(e("<a />",{href:a,text:a})).append(s('"',"q"));if(t.nl2br){var o=/\n/g;o.test(a)&&(a=(a+"").replace(o,"<br />"))}var u=e("<span />",{"class":"str"}).html(a);return e("<span />").append(s('"',"q")).append(u).append(s('"',"q"));case"number":return s(a.toString(),"num");case"undefined":return s("undefined","undef");case"null":return s("null","null");case"boolean":return s(a?"true":"false","bool")}};return l(a)};return e.fn.jsonView=function(n,p){var t=e(this);if(p=e.extend({},{nl2br:!0},p),"string"==typeof n)try{n=JSON.parse(n)}catch(r){}return t.append(e("<div />",{"class":"json-view"}).append(a(n,p))),t}}(jQuery); \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..7c79c6a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.svg b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..4b2226d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.svg @@ -0,0 +1,414 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata></metadata> +<defs> +<font id="fontawesomeregular" horiz-adv-x="1536" > +<font-face units-per-em="1792" ascent="1536" descent="-256" /> +<missing-glyph horiz-adv-x="448" /> +<glyph unicode=" " horiz-adv-x="448" /> +<glyph unicode=" " horiz-adv-x="448" /> +<glyph unicode=" " horiz-adv-x="448" /> +<glyph unicode="¨" horiz-adv-x="1792" /> +<glyph unicode="©" horiz-adv-x="1792" /> +<glyph unicode="®" horiz-adv-x="1792" /> +<glyph unicode="´" horiz-adv-x="1792" /> +<glyph unicode="Æ" horiz-adv-x="1792" /> +<glyph unicode=" " horiz-adv-x="768" /> +<glyph unicode=" " /> +<glyph unicode=" " horiz-adv-x="768" /> +<glyph unicode=" " /> +<glyph unicode=" " horiz-adv-x="512" /> +<glyph unicode=" " horiz-adv-x="384" /> +<glyph unicode=" " horiz-adv-x="256" /> +<glyph unicode=" " horiz-adv-x="256" /> +<glyph unicode=" " horiz-adv-x="192" /> +<glyph unicode=" " horiz-adv-x="307" /> +<glyph unicode=" " horiz-adv-x="85" /> +<glyph unicode=" " horiz-adv-x="307" /> +<glyph unicode=" " horiz-adv-x="384" /> +<glyph unicode="™" horiz-adv-x="1792" /> +<glyph unicode="∞" horiz-adv-x="1792" /> +<glyph unicode="≠" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="500" d="M0 0z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" /> +<glyph unicode="" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" /> +<glyph unicode="" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> +<glyph unicode="" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" /> +<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " /> +<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" /> +<glyph unicode="" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" /> +<glyph unicode="" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" /> +<glyph unicode="" horiz-adv-x="1280" d="M128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280zM768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z " /> +<glyph unicode="" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" /> +<glyph unicode="" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" /> +<glyph unicode="" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" /> +<glyph unicode="" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" /> +<glyph unicode="" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" /> +<glyph unicode="" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" /> +<glyph unicode="" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" /> +<glyph unicode="" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" /> +<glyph unicode="" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" /> +<glyph unicode="" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M725 977l-170 -450q73 -1 153.5 -2t119 -1.5t52.5 -0.5l29 2q-32 95 -92 241q-53 132 -92 211zM21 -128h-21l2 79q22 7 80 18q89 16 110 31q20 16 48 68l237 616l280 724h75h53l11 -21l205 -480q103 -242 124 -297q39 -102 96 -235q26 -58 65 -164q24 -67 65 -149 q22 -49 35 -57q22 -19 69 -23q47 -6 103 -27q6 -39 6 -57q0 -14 -1 -26q-80 0 -192 8q-93 8 -189 8q-79 0 -135 -2l-200 -11l-58 -2q0 45 4 78l131 28q56 13 68 23q12 12 12 27t-6 32l-47 114l-92 228l-450 2q-29 -65 -104 -274q-23 -64 -23 -84q0 -31 17 -43 q26 -21 103 -32q3 0 13.5 -2t30 -5t40.5 -6q1 -28 1 -58q0 -17 -2 -27q-66 0 -349 20l-48 -8q-81 -14 -167 -14z" /> +<glyph unicode="" horiz-adv-x="1408" d="M555 15q76 -32 140 -32q131 0 216 41t122 113q38 70 38 181q0 114 -41 180q-58 94 -141 126q-80 32 -247 32q-74 0 -101 -10v-144l-1 -173l3 -270q0 -15 12 -44zM541 761q43 -7 109 -7q175 0 264 65t89 224q0 112 -85 187q-84 75 -255 75q-52 0 -130 -13q0 -44 2 -77 q7 -122 6 -279l-1 -98q0 -43 1 -77zM0 -128l2 94q45 9 68 12q77 12 123 31q17 27 21 51q9 66 9 194l-2 497q-5 256 -9 404q-1 87 -11 109q-1 4 -12 12q-18 12 -69 15q-30 2 -114 13l-4 83l260 6l380 13l45 1q5 0 14 0.5t14 0.5q1 0 21.5 -0.5t40.5 -0.5h74q88 0 191 -27 q43 -13 96 -39q57 -29 102 -76q44 -47 65 -104t21 -122q0 -70 -32 -128t-95 -105q-26 -20 -150 -77q177 -41 267 -146q92 -106 92 -236q0 -76 -29 -161q-21 -62 -71 -117q-66 -72 -140 -108q-73 -36 -203 -60q-82 -15 -198 -11l-197 4q-84 2 -298 -11q-33 -3 -272 -11z" /> +<glyph unicode="" horiz-adv-x="1024" d="M0 -126l17 85q4 1 77 20q76 19 116 39q29 37 41 101l27 139l56 268l12 64q8 44 17 84.5t16 67t12.5 46.5t9 30.5t3.5 11.5l29 157l16 63l22 135l8 50v38q-41 22 -144 28q-28 2 -38 4l19 103l317 -14q39 -2 73 -2q66 0 214 9q33 2 68 4.5t36 2.5q-2 -19 -6 -38 q-7 -29 -13 -51q-55 -19 -109 -31q-64 -16 -101 -31q-12 -31 -24 -88q-9 -44 -13 -82q-44 -199 -66 -306l-61 -311l-38 -158l-43 -235l-12 -45q-2 -7 1 -27q64 -15 119 -21q36 -5 66 -10q-1 -29 -7 -58q-7 -31 -9 -41q-18 0 -23 -1q-24 -2 -42 -2q-9 0 -28 3q-19 4 -145 17 l-198 2q-41 1 -174 -11q-74 -7 -98 -9z" /> +<glyph unicode="" horiz-adv-x="1792" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l215 -1h293l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -42.5 2t-103.5 -1t-111 -1 q-34 0 -67 -5q-10 -97 -8 -136l1 -152v-332l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-88 0 -233 -14q-48 -4 -70 -4q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q8 192 6 433l-5 428q-1 62 -0.5 118.5t0.5 102.5t-2 57t-6 15q-6 5 -14 6q-38 6 -148 6q-43 0 -100 -13.5t-73 -24.5q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1744 128q33 0 42 -18.5t-11 -44.5 l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80z" /> +<glyph unicode="" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l446 -1h318l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -58.5 2t-138.5 -1t-128 -1 q-94 0 -127 -5q-10 -97 -8 -136l1 -152v52l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-82 0 -233 -13q-45 -5 -70 -5q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q6 137 6 433l-5 44q0 265 -2 278q-2 11 -6 15q-6 5 -14 6q-38 6 -148 6q-50 0 -168.5 -14t-132.5 -24q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1505 113q26 -20 26 -49t-26 -49l-162 -126 q-26 -20 -44.5 -11t-18.5 42v80h-1024v-80q0 -33 -18.5 -42t-44.5 11l-162 126q-26 20 -26 49t26 49l162 126q26 20 44.5 11t18.5 -42v-80h1024v80q0 33 18.5 42t44.5 -11z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" /> +<glyph unicode="" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> +<glyph unicode="" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" /> +<glyph unicode="" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" /> +<glyph unicode="" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" /> +<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" /> +<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" /> +<glyph unicode="" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" /> +<glyph unicode="" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" /> +<glyph unicode="" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" /> +<glyph unicode="" horiz-adv-x="1152" d="M742 -37l-652 651q-37 37 -37 90.5t37 90.5l652 651q37 37 90.5 37t90.5 -37l75 -75q37 -37 37 -90.5t-37 -90.5l-486 -486l486 -485q37 -38 37 -91t-37 -90l-75 -75q-37 -37 -90.5 -37t-90.5 37z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1099 704q0 -52 -37 -91l-652 -651q-37 -37 -90 -37t-90 37l-76 75q-37 39 -37 91q0 53 37 90l486 486l-486 485q-37 39 -37 91q0 53 37 90l76 75q36 38 90 38t90 -38l652 -651q37 -37 37 -90z" /> +<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" /> +<glyph unicode="" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" /> +<glyph unicode="" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" /> +<glyph unicode="" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" /> +<glyph unicode="" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" /> +<glyph unicode="" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" /> +<glyph unicode="" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" /> +<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" /> +<glyph unicode="" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" /> +<glyph unicode="" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " /> +<glyph unicode="" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" /> +<glyph unicode="" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> +<glyph unicode="" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" /> +<glyph unicode="" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1611 320q0 -53 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-486 485l-486 -485q-36 -38 -90 -38t-90 38l-75 75q-38 36 -38 90q0 53 38 91l651 651q37 37 90 37q52 0 91 -37l650 -651q38 -38 38 -91z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1611 832q0 -53 -37 -90l-651 -651q-38 -38 -91 -38q-54 0 -90 38l-651 651q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l486 -486l486 486q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " /> +<glyph unicode="" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" /> +<glyph unicode="" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1920" d="M512 512v-384h-256v384h256zM896 1024v-896h-256v896h256zM1280 768v-640h-256v640h256zM1664 1152v-1024h-256v1024h256zM1792 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5z M1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> +<glyph unicode="" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1307 618l23 219h-198v109q0 49 15.5 68.5t71.5 19.5h110v219h-175q-152 0 -218 -72t-66 -213v-131h-131v-219h131v-635h262v635h175zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" /> +<glyph unicode="" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" /> +<glyph unicode="" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" /> +<glyph unicode="" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" /> +<glyph unicode="" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" /> +<glyph unicode="" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" /> +<glyph unicode="" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" /> +<glyph unicode="" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" /> +<glyph unicode="" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" /> +<glyph unicode="" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" /> +<glyph unicode="" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" /> +<glyph unicode="" horiz-adv-x="768" d="M511 980h257l-30 -284h-227v-824h-341v824h-170v284h170v171q0 182 86 275.5t283 93.5h227v-284h-142q-39 0 -62.5 -6.5t-34 -23.5t-13.5 -34.5t-3 -49.5v-142z" /> +<glyph unicode="" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" /> +<glyph unicode="" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" /> +<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM183 128h1298q-164 181 -246.5 411.5t-82.5 484.5q0 256 -320 256t-320 -256q0 -254 -82.5 -484.5t-246.5 -411.5zM1664 128q0 -52 -38 -90t-90 -38 h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" /> +<glyph unicode="" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" /> +<glyph unicode="" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" /> +<glyph unicode="" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" /> +<glyph unicode="" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" /> +<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" /> +<glyph unicode="" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" /> +<glyph unicode="" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" /> +<glyph unicode="" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " /> +<glyph unicode="" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " /> +<glyph unicode="" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" /> +<glyph unicode="" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" /> +<glyph unicode="" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" /> +<glyph unicode="" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" /> +<glyph unicode="" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" /> +<glyph unicode="" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" /> +<glyph unicode="" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" /> +<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" /> +<glyph unicode="" d="M678 -57q0 -38 -10 -71h-380q-95 0 -171.5 56.5t-103.5 147.5q24 45 69 77.5t100 49.5t107 24t107 7q32 0 49 -2q6 -4 30.5 -21t33 -23t31 -23t32 -25.5t27.5 -25.5t26.5 -29.5t21 -30.5t17.5 -34.5t9.5 -36t4.5 -40.5zM385 294q-234 -7 -385 -85v433q103 -118 273 -118 q32 0 70 5q-21 -61 -21 -86q0 -67 63 -149zM558 805q0 -100 -43.5 -160.5t-140.5 -60.5q-51 0 -97 26t-78 67.5t-56 93.5t-35.5 104t-11.5 99q0 96 51.5 165t144.5 69q66 0 119 -41t84 -104t47 -130t16 -128zM1536 896v-736q0 -119 -84.5 -203.5t-203.5 -84.5h-468 q39 73 39 157q0 66 -22 122.5t-55.5 93t-72 71t-72 59.5t-55.5 54.5t-22 59.5q0 36 23 68t56 61.5t65.5 64.5t55.5 93t23 131t-26.5 145.5t-75.5 118.5q-6 6 -14 11t-12.5 7.5t-10 9.5t-10.5 17h135l135 64h-437q-138 0 -244.5 -38.5t-182.5 -133.5q0 126 81 213t207 87h960 q119 0 203.5 -84.5t84.5 -203.5v-96h-256v256h-128v-256h-256v-128h256v-256h128v256h256z" /> +<glyph unicode="" horiz-adv-x="1664" d="M876 71q0 21 -4.5 40.5t-9.5 36t-17.5 34.5t-21 30.5t-26.5 29.5t-27.5 25.5t-32 25.5t-31 23t-33 23t-30.5 21q-17 2 -50 2q-54 0 -106 -7t-108 -25t-98 -46t-69 -75t-27 -107q0 -68 35.5 -121.5t93 -84t120.5 -45.5t127 -15q59 0 112.5 12.5t100.5 39t74.5 73.5 t27.5 110zM756 933q0 60 -16.5 127.5t-47 130.5t-84 104t-119.5 41q-93 0 -144 -69t-51 -165q0 -47 11.5 -99t35.5 -104t56 -93.5t78 -67.5t97 -26q97 0 140.5 60.5t43.5 160.5zM625 1408h437l-135 -79h-135q71 -45 110 -126t39 -169q0 -74 -23 -131.5t-56 -92.5t-66 -64.5 t-56 -61t-23 -67.5q0 -26 16.5 -51t43 -48t58.5 -48t64 -55.5t58.5 -66t43 -85t16.5 -106.5q0 -160 -140 -282q-152 -131 -420 -131q-59 0 -119.5 10t-122 33.5t-108.5 58t-77 89t-30 121.5q0 61 37 135q32 64 96 110.5t145 71t155 36t150 13.5q-64 83 -64 149q0 12 2 23.5 t5 19.5t8 21.5t7 21.5q-40 -5 -70 -5q-149 0 -255.5 98t-106.5 246q0 140 95 250.5t234 141.5q94 20 187 20zM1664 1152v-128h-256v-256h-128v256h-256v128h256v256h128v-256h256z" /> +<glyph unicode="" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" /> +<glyph unicode="" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" /> +<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" /> +<glyph unicode="" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" /> +<glyph unicode="" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" /> +<glyph unicode="" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" /> +<glyph unicode="" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" /> +<glyph unicode="" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" /> +<glyph unicode="" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" /> +<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1664 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5 q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1024 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1024 608v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280z M768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" /> +<glyph unicode="" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" /> +<glyph unicode="" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" /> +<glyph unicode="" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" /> +<glyph unicode="" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" /> +<glyph unicode="" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" /> +<glyph unicode="" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" /> +<glyph unicode="" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" /> +<glyph unicode="" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" /> +<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" /> +<glyph unicode="" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" /> +<glyph unicode="" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" /> +<glyph unicode="" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" /> +<glyph unicode="" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" /> +<glyph unicode="" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" /> +<glyph unicode="" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" /> +<glyph unicode="" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" /> +<glyph unicode="" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" /> +<glyph unicode="" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" /> +<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" /> +<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" /> +<glyph unicode="" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" /> +<glyph unicode="" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" /> +<glyph unicode="" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" /> +<glyph unicode="" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" /> +<glyph unicode="" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" /> +<glyph unicode="" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" /> +<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" /> +<glyph unicode="" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" /> +<glyph unicode="" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" /> +<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" /> +<glyph unicode="" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" /> +<glyph unicode="" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" /> +<glyph unicode="" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" /> +<glyph unicode="" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1024 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1024 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28 t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" /> +<glyph unicode="" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" /> +<glyph unicode="" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" /> +<glyph unicode="" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" /> +<glyph unicode="" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" /> +<glyph unicode="" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" /> +<glyph unicode="" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" /> +<glyph unicode="" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" /> +<glyph unicode="" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" /> +<glyph unicode="" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" /> +<glyph unicode="" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" /> +<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" /> +<glyph unicode="" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" /> +<glyph unicode="" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M390 1408h219v-388h364v-241h-364v-394q0 -136 14 -172q13 -37 52 -60q50 -31 117 -31q117 0 232 76v-242q-102 -48 -178 -65q-77 -19 -173 -19q-105 0 -186 27q-78 25 -138 75q-58 51 -79 105q-22 54 -22 161v539h-170v217q91 30 155 84q64 55 103 132q39 78 54 196z " /> +<glyph unicode="" d="M1123 127v181q-88 -56 -174 -56q-51 0 -88 23q-29 17 -39 45q-11 30 -11 129v295h274v181h-274v291h-164q-11 -90 -40 -147t-78 -99q-48 -40 -116 -63v-163h127v-404q0 -78 17 -121q17 -42 59 -78q43 -37 104 -57q62 -20 140 -20q67 0 129 14q57 13 134 49zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" /> +<glyph unicode="" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" /> +<glyph unicode="" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" /> +<glyph unicode="" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" /> +<glyph unicode="" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" /> +<glyph unicode="" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1483 512l-587 -587q-52 -53 -127.5 -53t-128.5 53l-587 587q-53 53 -53 128t53 128l587 587q53 53 128 53t128 -53l265 -265l-398 -399l-188 188q-42 42 -99 42q-59 0 -100 -41l-120 -121q-42 -40 -42 -99q0 -58 42 -100l406 -408q30 -28 67 -37l6 -4h28q60 0 99 41 l619 619l2 -3q53 -53 53 -128t-53 -128zM1406 1138l120 -120q14 -15 14 -36t-14 -36l-730 -730q-17 -15 -37 -15v0q-4 0 -6 1q-18 2 -30 14l-407 408q-14 15 -14 36t14 35l121 120q13 15 35 15t36 -15l252 -252l574 575q15 15 36 15t36 -15z" /> +<glyph unicode="" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" /> +<glyph unicode="" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" /> +<glyph unicode="" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" /> +<glyph unicode="" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" /> +<glyph unicode="" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" /> +<glyph unicode="" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" /> +<glyph unicode="" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" /> +<glyph unicode="" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" /> +<glyph unicode="" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " /> +<glyph unicode="" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" /> +<glyph unicode="" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" /> +<glyph unicode="" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" /> +<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +<glyph unicode="" horiz-adv-x="1792" /> +</font> +</defs></svg> diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..e89738d Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..8c1748a Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..b93a495 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.svg b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..187805a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata></metadata> +<defs> +<font id="glyphicons_halflingsregular" horiz-adv-x="1200" > +<font-face units-per-em="1200" ascent="960" descent="-240" /> +<missing-glyph horiz-adv-x="500" /> +<glyph horiz-adv-x="0" /> +<glyph horiz-adv-x="400" /> +<glyph unicode=" " /> +<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" /> +<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode=" " /> +<glyph unicode="¥" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" /> +<glyph unicode=" " horiz-adv-x="650" /> +<glyph unicode=" " horiz-adv-x="1300" /> +<glyph unicode=" " horiz-adv-x="650" /> +<glyph unicode=" " horiz-adv-x="1300" /> +<glyph unicode=" " horiz-adv-x="433" /> +<glyph unicode=" " horiz-adv-x="325" /> +<glyph unicode=" " horiz-adv-x="216" /> +<glyph unicode=" " horiz-adv-x="216" /> +<glyph unicode=" " horiz-adv-x="162" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="72" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="325" /> +<glyph unicode="€" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" /> +<glyph unicode="₽" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" /> +<glyph unicode="−" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="⌛" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" /> +<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" /> +<glyph unicode="☁" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" /> +<glyph unicode="⛺" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " /> +<glyph unicode="✉" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" /> +<glyph unicode="✏" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" /> +<glyph unicode="" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" /> +<glyph unicode="" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" /> +<glyph unicode="" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" /> +<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" /> +<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" /> +<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" /> +<glyph unicode="" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" /> +<glyph unicode="" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" /> +<glyph unicode="" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" /> +<glyph unicode="" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" /> +<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" /> +<glyph unicode="" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" /> +<glyph unicode="" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" /> +<glyph unicode="" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" /> +<glyph unicode="" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" /> +<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" /> +<glyph unicode="" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" /> +<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" /> +<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" /> +<glyph unicode="" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" /> +<glyph unicode="" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" /> +<glyph unicode="" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" /> +<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" /> +<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" /> +<glyph unicode="" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" /> +<glyph unicode="" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" /> +<glyph unicode="" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" /> +<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" /> +<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" /> +<glyph unicode="" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" /> +<glyph unicode="" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" /> +<glyph unicode="" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" /> +<glyph unicode="" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" /> +<glyph unicode="" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" /> +<glyph unicode="" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" /> +<glyph unicode="" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" /> +<glyph unicode="" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" /> +<glyph unicode="" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" /> +<glyph unicode="" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" /> +<glyph unicode="" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" /> +<glyph unicode="" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" /> +<glyph unicode="" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" /> +<glyph unicode="" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" /> +<glyph unicode="" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" /> +<glyph unicode="" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" /> +<glyph unicode="" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" /> +<glyph unicode="" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" /> +<glyph unicode="" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" /> +<glyph unicode="" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" /> +<glyph unicode="" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" /> +<glyph unicode="" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" /> +<glyph unicode="" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" /> +<glyph unicode="" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" /> +<glyph unicode="" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" /> +<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" /> +<glyph unicode="" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" /> +<glyph unicode="" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" /> +<glyph unicode="" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" /> +<glyph unicode="" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" /> +<glyph unicode="" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" /> +<glyph unicode="" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" /> +<glyph unicode="" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" /> +<glyph unicode="" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" /> +<glyph unicode="" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" /> +<glyph unicode="" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" /> +<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" /> +<glyph unicode="" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" /> +<glyph unicode="" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" /> +<glyph unicode="" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" /> +<glyph unicode="" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" /> +<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" /> +<glyph unicode="" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" /> +<glyph unicode="" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" /> +<glyph unicode="" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" /> +<glyph unicode="" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" /> +<glyph unicode="" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" /> +<glyph unicode="" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" /> +<glyph unicode="" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" /> +<glyph unicode="" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" /> +<glyph unicode="" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" /> +<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " /> +<glyph unicode="" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" /> +<glyph unicode="" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" /> +<glyph unicode="" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" /> +<glyph unicode="" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" /> +<glyph unicode="" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" /> +<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" /> +<glyph unicode="" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" /> +<glyph unicode="" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" /> +<glyph unicode="" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" /> +<glyph unicode="" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" /> +<glyph unicode="" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" /> +<glyph unicode="" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" /> +<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" /> +<glyph unicode="" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" /> +<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" /> +<glyph unicode="" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" /> +<glyph unicode="" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" /> +<glyph unicode="" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" /> +<glyph unicode="" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" /> +<glyph unicode="" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" /> +<glyph unicode="" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" /> +<glyph unicode="" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" /> +<glyph unicode="" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" /> +<glyph unicode="" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" /> +<glyph unicode="" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" /> +<glyph unicode="" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" /> +<glyph unicode="" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" /> +<glyph unicode="" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" /> +<glyph unicode="" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" /> +<glyph unicode="" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" /> +<glyph unicode="" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" /> +<glyph unicode="" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" /> +<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" /> +<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" /> +<glyph unicode="" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" /> +<glyph unicode="" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" /> +<glyph unicode="" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" /> +<glyph unicode="" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" /> +<glyph unicode="" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" /> +<glyph unicode="" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" /> +<glyph unicode="" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" /> +<glyph unicode="" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" /> +<glyph unicode="" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" /> +<glyph unicode="" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" /> +<glyph unicode="" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" /> +<glyph unicode="" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" /> +<glyph unicode="" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" /> +<glyph unicode="" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" /> +<glyph unicode="" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " /> +<glyph unicode="" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" /> +<glyph unicode="" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" /> +<glyph unicode="" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" /> +<glyph unicode="" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" /> +<glyph unicode="" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" /> +<glyph unicode="" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" /> +<glyph unicode="" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" /> +<glyph unicode="" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" /> +<glyph unicode="" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" /> +<glyph unicode="" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" /> +<glyph unicode="" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" /> +<glyph unicode="" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" /> +<glyph unicode="" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" /> +<glyph unicode="" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" /> +<glyph unicode="" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" /> +<glyph unicode="" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" /> +<glyph unicode="" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" /> +<glyph unicode="" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" /> +<glyph unicode="" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" /> +<glyph unicode="" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" /> +<glyph unicode="" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" /> +<glyph unicode="" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" /> +<glyph unicode="" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" /> +<glyph unicode="" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" /> +<glyph unicode="" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" /> +<glyph unicode="" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" /> +<glyph unicode="" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" /> +<glyph unicode="" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" /> +<glyph unicode="" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" /> +<glyph unicode="" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" /> +<glyph unicode="🔑" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" /> +<glyph unicode="🚪" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" /> +</font> +</defs></svg> diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..1413fc6 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..9e61285 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2 b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000..64539b5 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png new file mode 100644 index 0000000..3bf6484 Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings.png b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings.png new file mode 100644 index 0000000..36c3b1e Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/glyphicons-halflings.png differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/img/grid.png b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/grid.png new file mode 100644 index 0000000..878c3ed Binary files /dev/null and b/venv/Lib/site-packages/rest_framework/static/rest_framework/img/grid.png differ diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/ajax-form.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/ajax-form.js new file mode 100644 index 0000000..1483305 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/ajax-form.js @@ -0,0 +1,127 @@ +function replaceDocument(docString) { + var doc = document.open("text/html"); + + doc.write(docString); + doc.close(); +} + +function doAjaxSubmit(e) { + var form = $(this); + var btn = $(this.clk); + var method = ( + btn.data('method') || + form.data('method') || + form.attr('method') || 'GET' + ).toUpperCase(); + + if (method === 'GET') { + // GET requests can always use standard form submits. + return; + } + + var contentType = + form.find('input[data-override="content-type"]').val() || + form.find('select[data-override="content-type"] option:selected').text(); + + if (method === 'POST' && !contentType) { + // POST requests can use standard form submits, unless we have + // overridden the content type. + return; + } + + // At this point we need to make an AJAX form submission. + e.preventDefault(); + + var url = form.attr('action'); + var data; + + if (contentType) { + data = form.find('[data-override="content"]').val() || '' + + if (contentType === 'multipart/form-data') { + // We need to add a boundary parameter to the header + // We assume the first valid-looking boundary line in the body is correct + // regex is from RFC 2046 appendix A + var boundaryCharNoSpace = "0-9A-Z'()+_,-./:=?"; + var boundaryChar = boundaryCharNoSpace + ' '; + var re = new RegExp('^--([' + boundaryChar + ']{0,69}[' + boundaryCharNoSpace + '])[\\s]*?$', 'im'); + var boundary = data.match(re); + if (boundary !== null) { + contentType += '; boundary="' + boundary[1] + '"'; + } + // Fix textarea.value EOL normalisation (multipart/form-data should use CR+NL, not NL) + data = data.replace(/\n/g, '\r\n'); + } + } else { + contentType = form.attr('enctype') || form.attr('encoding') + + if (contentType === 'multipart/form-data') { + if (!window.FormData) { + alert('Your browser does not support AJAX multipart form submissions'); + return; + } + + // Use the FormData API and allow the content type to be set automatically, + // so it includes the boundary string. + // See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects + contentType = false; + data = new FormData(form[0]); + } else { + contentType = 'application/x-www-form-urlencoded; charset=UTF-8' + data = form.serialize(); + } + } + + var ret = $.ajax({ + url: url, + method: method, + data: data, + contentType: contentType, + processData: false, + headers: { + 'Accept': 'text/html; q=1.0, */*' + }, + }); + + ret.always(function(data, textStatus, jqXHR) { + if (textStatus != 'success') { + jqXHR = data; + } + + var responseContentType = jqXHR.getResponseHeader("content-type") || ""; + + if (responseContentType.toLowerCase().indexOf('text/html') === 0) { + replaceDocument(jqXHR.responseText); + + try { + // Modify the location and scroll to top, as if after page load. + history.replaceState({}, '', url); + scroll(0, 0); + } catch (err) { + // History API not supported, so redirect. + window.location = url; + } + } else { + // Not HTML content. We can't open this directly, so redirect. + window.location = url; + } + }); + + return ret; +} + +function captureSubmittingElement(e) { + var target = e.target; + var form = this; + + form.clk = target; +} + +$.fn.ajaxForm = function() { + var options = {} + + return this + .unbind('submit.form-plugin click.form-plugin') + .bind('submit.form-plugin', options, doAjaxSubmit) + .bind('click.form-plugin', options, captureSubmittingElement); +}; diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/bootstrap.min.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/bootstrap.min.js new file mode 100644 index 0000000..eb0a8b4 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/bootstrap.min.js @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(t){"use strict";var e=jQuery.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||3<e[0])throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(),function(n){"use strict";n.fn.emulateTransitionEnd=function(t){var e=!1,i=this;n(this).one("bsTransitionEnd",function(){e=!0});return setTimeout(function(){e||n(i).trigger(n.support.transition.end)},t),this},n(function(){n.support.transition=function o(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var i in e)if(t.style[i]!==undefined)return{end:e[i]};return!1}(),n.support.transition&&(n.event.special.bsTransitionEnd={bindType:n.support.transition.end,delegateType:n.support.transition.end,handle:function(t){if(n(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}})})}(jQuery),function(s){"use strict";var e='[data-dismiss="alert"]',a=function(t){s(t).on("click",e,this.close)};a.VERSION="3.4.1",a.TRANSITION_DURATION=150,a.prototype.close=function(t){var e=s(this),i=e.attr("data-target");i||(i=(i=e.attr("href"))&&i.replace(/.*(?=#[^\s]*$)/,"")),i="#"===i?[]:i;var o=s(document).find(i);function n(){o.detach().trigger("closed.bs.alert").remove()}t&&t.preventDefault(),o.length||(o=e.closest(".alert")),o.trigger(t=s.Event("close.bs.alert")),t.isDefaultPrevented()||(o.removeClass("in"),s.support.transition&&o.hasClass("fade")?o.one("bsTransitionEnd",n).emulateTransitionEnd(a.TRANSITION_DURATION):n())};var t=s.fn.alert;s.fn.alert=function o(i){return this.each(function(){var t=s(this),e=t.data("bs.alert");e||t.data("bs.alert",e=new a(this)),"string"==typeof i&&e[i].call(t)})},s.fn.alert.Constructor=a,s.fn.alert.noConflict=function(){return s.fn.alert=t,this},s(document).on("click.bs.alert.data-api",e,a.prototype.close)}(jQuery),function(s){"use strict";var n=function(t,e){this.$element=s(t),this.options=s.extend({},n.DEFAULTS,e),this.isLoading=!1};function i(o){return this.each(function(){var t=s(this),e=t.data("bs.button"),i="object"==typeof o&&o;e||t.data("bs.button",e=new n(this,i)),"toggle"==o?e.toggle():o&&e.setState(o)})}n.VERSION="3.4.1",n.DEFAULTS={loadingText:"loading..."},n.prototype.setState=function(t){var e="disabled",i=this.$element,o=i.is("input")?"val":"html",n=i.data();t+="Text",null==n.resetText&&i.data("resetText",i[o]()),setTimeout(s.proxy(function(){i[o](null==n[t]?this.options[t]:n[t]),"loadingText"==t?(this.isLoading=!0,i.addClass(e).attr(e,e).prop(e,!0)):this.isLoading&&(this.isLoading=!1,i.removeClass(e).removeAttr(e).prop(e,!1))},this),0)},n.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")?(i.prop("checked")&&(t=!1),e.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==i.prop("type")&&(i.prop("checked")!==this.$element.hasClass("active")&&(t=!1),this.$element.toggleClass("active")),i.prop("checked",this.$element.hasClass("active")),t&&i.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var t=s.fn.button;s.fn.button=i,s.fn.button.Constructor=n,s.fn.button.noConflict=function(){return s.fn.button=t,this},s(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(t){var e=s(t.target).closest(".btn");i.call(e,"toggle"),s(t.target).is('input[type="radio"], input[type="checkbox"]')||(t.preventDefault(),e.is("input,button")?e.trigger("focus"):e.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(t){s(t.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(t.type))})}(jQuery),function(p){"use strict";var c=function(t,e){this.$element=p(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=e,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",p.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",p.proxy(this.pause,this)).on("mouseleave.bs.carousel",p.proxy(this.cycle,this))};function r(n){return this.each(function(){var t=p(this),e=t.data("bs.carousel"),i=p.extend({},c.DEFAULTS,t.data(),"object"==typeof n&&n),o="string"==typeof n?n:i.slide;e||t.data("bs.carousel",e=new c(this,i)),"number"==typeof n?e.to(n):o?e[o]():i.interval&&e.pause().cycle()})}c.VERSION="3.4.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(t){if(!/input|textarea/i.test(t.target.tagName)){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()}},c.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(p.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},c.prototype.getItemForDirection=function(t,e){var i=this.getItemIndex(e);if(("prev"==t&&0===i||"next"==t&&i==this.$items.length-1)&&!this.options.wrap)return e;var o=(i+("prev"==t?-1:1))%this.$items.length;return this.$items.eq(o)},c.prototype.to=function(t){var e=this,i=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(t>this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(i<t?"next":"prev",this.$items.eq(t))},c.prototype.pause=function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&p.support.transition&&(this.$element.trigger(p.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(t,e){var i=this.$element.find(".item.active"),o=e||this.getItemForDirection(t,i),n=this.interval,s="next"==t?"left":"right",a=this;if(o.hasClass("active"))return this.sliding=!1;var r=o[0],l=p.Event("slide.bs.carousel",{relatedTarget:r,direction:s});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,n&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var h=p(this.$indicators.children()[this.getItemIndex(o)]);h&&h.addClass("active")}var d=p.Event("slid.bs.carousel",{relatedTarget:r,direction:s});return p.support.transition&&this.$element.hasClass("slide")?(o.addClass(t),"object"==typeof o&&o.length&&o[0].offsetWidth,i.addClass(s),o.addClass(s),i.one("bsTransitionEnd",function(){o.removeClass([t,s].join(" ")).addClass("active"),i.removeClass(["active",s].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger(d)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(i.removeClass("active"),o.addClass("active"),this.sliding=!1,this.$element.trigger(d)),n&&this.cycle(),this}};var t=p.fn.carousel;p.fn.carousel=r,p.fn.carousel.Constructor=c,p.fn.carousel.noConflict=function(){return p.fn.carousel=t,this};var e=function(t){var e=p(this),i=e.attr("href");i&&(i=i.replace(/.*(?=#[^\s]+$)/,""));var o=e.attr("data-target")||i,n=p(document).find(o);if(n.hasClass("carousel")){var s=p.extend({},n.data(),e.data()),a=e.attr("data-slide-to");a&&(s.interval=!1),r.call(n,s),a&&n.data("bs.carousel").to(a),t.preventDefault()}};p(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),p(window).on("load",function(){p('[data-ride="carousel"]').each(function(){var t=p(this);r.call(t,t.data())})})}(jQuery),function(a){"use strict";var r=function(t,e){this.$element=a(t),this.options=a.extend({},r.DEFAULTS,e),this.$trigger=a('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};function n(t){var e,i=t.attr("data-target")||(e=t.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"");return a(document).find(i)}function l(o){return this.each(function(){var t=a(this),e=t.data("bs.collapse"),i=a.extend({},r.DEFAULTS,t.data(),"object"==typeof o&&o);!e&&i.toggle&&/show|hide/.test(o)&&(i.toggle=!1),e||t.data("bs.collapse",e=new r(this,i)),"string"==typeof o&&e[o]()})}r.VERSION="3.4.1",r.TRANSITION_DURATION=350,r.DEFAULTS={toggle:!0},r.prototype.dimension=function(){return this.$element.hasClass("width")?"width":"height"},r.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var t,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(t=e.data("bs.collapse"))&&t.transitioning)){var i=a.Event("show.bs.collapse");if(this.$element.trigger(i),!i.isDefaultPrevented()){e&&e.length&&(l.call(e,"hide"),t||e.data("bs.collapse",null));var o=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[o](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var n=function(){this.$element.removeClass("collapsing").addClass("collapse in")[o](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return n.call(this);var s=a.camelCase(["scroll",o].join("-"));this.$element.one("bsTransitionEnd",a.proxy(n,this)).emulateTransitionEnd(r.TRANSITION_DURATION)[o](this.$element[0][s])}}}},r.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var t=a.Event("hide.bs.collapse");if(this.$element.trigger(t),!t.isDefaultPrevented()){var e=this.dimension();this.$element[e](this.$element[e]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var i=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};if(!a.support.transition)return i.call(this);this.$element[e](0).one("bsTransitionEnd",a.proxy(i,this)).emulateTransitionEnd(r.TRANSITION_DURATION)}}},r.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},r.prototype.getParent=function(){return a(document).find(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(t,e){var i=a(e);this.addAriaAndCollapsedClass(n(i),i)},this)).end()},r.prototype.addAriaAndCollapsedClass=function(t,e){var i=t.hasClass("in");t.attr("aria-expanded",i),e.toggleClass("collapsed",!i).attr("aria-expanded",i)};var t=a.fn.collapse;a.fn.collapse=l,a.fn.collapse.Constructor=r,a.fn.collapse.noConflict=function(){return a.fn.collapse=t,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(t){var e=a(this);e.attr("data-target")||t.preventDefault();var i=n(e),o=i.data("bs.collapse")?"toggle":e.data();l.call(i,o)})}(jQuery),function(a){"use strict";var r='[data-toggle="dropdown"]',o=function(t){a(t).on("click.bs.dropdown",this.toggle)};function l(t){var e=t.attr("data-target");e||(e=(e=t.attr("href"))&&/#[A-Za-z]/.test(e)&&e.replace(/.*(?=#[^\s]*$)/,""));var i="#"!==e?a(document).find(e):null;return i&&i.length?i:t.parent()}function s(o){o&&3===o.which||(a(".dropdown-backdrop").remove(),a(r).each(function(){var t=a(this),e=l(t),i={relatedTarget:this};e.hasClass("open")&&(o&&"click"==o.type&&/input|textarea/i.test(o.target.tagName)&&a.contains(e[0],o.target)||(e.trigger(o=a.Event("hide.bs.dropdown",i)),o.isDefaultPrevented()||(t.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",i)))))}))}o.VERSION="3.4.1",o.prototype.toggle=function(t){var e=a(this);if(!e.is(".disabled, :disabled")){var i=l(e),o=i.hasClass("open");if(s(),!o){"ontouchstart"in document.documentElement&&!i.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",s);var n={relatedTarget:this};if(i.trigger(t=a.Event("show.bs.dropdown",n)),t.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),i.toggleClass("open").trigger(a.Event("shown.bs.dropdown",n))}return!1}},o.prototype.keydown=function(t){if(/(38|40|27|32)/.test(t.which)&&!/input|textarea/i.test(t.target.tagName)){var e=a(this);if(t.preventDefault(),t.stopPropagation(),!e.is(".disabled, :disabled")){var i=l(e),o=i.hasClass("open");if(!o&&27!=t.which||o&&27==t.which)return 27==t.which&&i.find(r).trigger("focus"),e.trigger("click");var n=i.find(".dropdown-menu li:not(.disabled):visible a");if(n.length){var s=n.index(t.target);38==t.which&&0<s&&s--,40==t.which&&s<n.length-1&&s++,~s||(s=0),n.eq(s).trigger("focus")}}}};var t=a.fn.dropdown;a.fn.dropdown=function e(i){return this.each(function(){var t=a(this),e=t.data("bs.dropdown");e||t.data("bs.dropdown",e=new o(this)),"string"==typeof i&&e[i].call(t)})},a.fn.dropdown.Constructor=o,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=t,this},a(document).on("click.bs.dropdown.data-api",s).on("click.bs.dropdown.data-api",".dropdown form",function(t){t.stopPropagation()}).on("click.bs.dropdown.data-api",r,o.prototype.toggle).on("keydown.bs.dropdown.data-api",r,o.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",o.prototype.keydown)}(jQuery),function(a){"use strict";var s=function(t,e){this.options=e,this.$body=a(document.body),this.$element=a(t),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.fixedContent=".navbar-fixed-top, .navbar-fixed-bottom",this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};function r(o,n){return this.each(function(){var t=a(this),e=t.data("bs.modal"),i=a.extend({},s.DEFAULTS,t.data(),"object"==typeof o&&o);e||t.data("bs.modal",e=new s(this,i)),"string"==typeof o?e[o](n):i.show&&e.show(n)})}s.VERSION="3.4.1",s.TRANSITION_DURATION=300,s.BACKDROP_TRANSITION_DURATION=150,s.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},s.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},s.prototype.show=function(i){var o=this,t=a.Event("show.bs.modal",{relatedTarget:i});this.$element.trigger(t),this.isShown||t.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){o.$element.one("mouseup.dismiss.bs.modal",function(t){a(t.target).is(o.$element)&&(o.ignoreBackdropClick=!0)})}),this.backdrop(function(){var t=a.support.transition&&o.$element.hasClass("fade");o.$element.parent().length||o.$element.appendTo(o.$body),o.$element.show().scrollTop(0),o.adjustDialog(),t&&o.$element[0].offsetWidth,o.$element.addClass("in"),o.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:i});t?o.$dialog.one("bsTransitionEnd",function(){o.$element.trigger("focus").trigger(e)}).emulateTransitionEnd(s.TRANSITION_DURATION):o.$element.trigger("focus").trigger(e)}))},s.prototype.hide=function(t){t&&t.preventDefault(),t=a.Event("hide.bs.modal"),this.$element.trigger(t),this.isShown&&!t.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(s.TRANSITION_DURATION):this.hideModal())},s.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(t){document===t.target||this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},s.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},s.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},s.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},s.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},s.prototype.backdrop=function(t){var e=this,i=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var o=a.support.transition&&i;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+i).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(t){this.ignoreBackdropClick?this.ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide())},this)),o&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!t)return;o?this.$backdrop.one("bsTransitionEnd",t).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):t()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var n=function(){e.removeBackdrop(),t&&t()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",n).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):n()}else t&&t()},s.prototype.handleUpdate=function(){this.adjustDialog()},s.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},s.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"";var n=this.scrollbarWidth;this.bodyIsOverflowing&&(this.$body.css("padding-right",t+n),a(this.fixedContent).each(function(t,e){var i=e.style.paddingRight,o=a(e).css("padding-right");a(e).data("padding-right",i).css("padding-right",parseFloat(o)+n+"px")}))},s.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad),a(this.fixedContent).each(function(t,e){var i=a(e).data("padding-right");a(e).removeData("padding-right"),e.style.paddingRight=i||""})},s.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var t=a.fn.modal;a.fn.modal=r,a.fn.modal.Constructor=s,a.fn.modal.noConflict=function(){return a.fn.modal=t,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(t){var e=a(this),i=e.attr("href"),o=e.attr("data-target")||i&&i.replace(/.*(?=#[^\s]+$)/,""),n=a(document).find(o),s=n.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(i)&&i},n.data(),e.data());e.is("a")&&t.preventDefault(),n.one("show.bs.modal",function(t){t.isDefaultPrevented()||n.one("hidden.bs.modal",function(){e.is(":visible")&&e.trigger("focus")})}),r.call(n,s,this)})}(jQuery),function(g){"use strict";var o=["sanitize","whiteList","sanitizeFn"],a=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],t={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},r=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,l=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function u(t,e){var i=t.nodeName.toLowerCase();if(-1!==g.inArray(i,e))return-1===g.inArray(i,a)||Boolean(t.nodeValue.match(r)||t.nodeValue.match(l));for(var o=g(e).filter(function(t,e){return e instanceof RegExp}),n=0,s=o.length;n<s;n++)if(i.match(o[n]))return!0;return!1}function n(t,e,i){if(0===t.length)return t;if(i&&"function"==typeof i)return i(t);if(!document.implementation||!document.implementation.createHTMLDocument)return t;var o=document.implementation.createHTMLDocument("sanitization");o.body.innerHTML=t;for(var n=g.map(e,function(t,e){return e}),s=g(o.body).find("*"),a=0,r=s.length;a<r;a++){var l=s[a],h=l.nodeName.toLowerCase();if(-1!==g.inArray(h,n))for(var d=g.map(l.attributes,function(t){return t}),p=[].concat(e["*"]||[],e[h]||[]),c=0,f=d.length;c<f;c++)u(d[c],p)||l.removeAttribute(d[c].nodeName);else l.parentNode.removeChild(l)}return o.body.innerHTML}var m=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};m.VERSION="3.4.1",m.TRANSITION_DURATION=150,m.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0},sanitize:!0,sanitizeFn:null,whiteList:t},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){var e=this.$element.data();for(var i in e)e.hasOwnProperty(i)&&-1!==g.inArray(i,o)&&delete e[i];return(t=g.extend({},this.getDefaults(),e,t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t.sanitize&&(t.template=n(t.template,t.whiteList,t.sanitizeFn)),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-d<c.top?"bottom":"right"==s&&l.right+h>c.width?"left":"left"==s&&l.left-h<c.left?"right":s,o.removeClass(p).addClass(s)}var f=this.getCalculatedOffset(s,l,h,d);this.applyPlacement(f,s);var u=function(){var t=i.hoverState;i.$element.trigger("shown.bs."+i.type),i.hoverState=null,"out"==t&&i.leave(i)};g.support.transition&&this.$tip.hasClass("fade")?o.one("bsTransitionEnd",u).emulateTransitionEnd(m.TRANSITION_DURATION):u()}},m.prototype.applyPlacement=function(t,e){var i=this.tip(),o=i[0].offsetWidth,n=i[0].offsetHeight,s=parseInt(i.css("margin-top"),10),a=parseInt(i.css("margin-left"),10);isNaN(s)&&(s=0),isNaN(a)&&(a=0),t.top+=s,t.left+=a,g.offset.setOffset(i[0],g.extend({using:function(t){i.css({top:Math.round(t.top),left:Math.round(t.left)})}},t),0),i.addClass("in");var r=i[0].offsetWidth,l=i[0].offsetHeight;"top"==e&&l!=n&&(t.top=t.top+n-l);var h=this.getViewportAdjustedDelta(e,t,r,l);h.left?t.left+=h.left:t.top+=h.top;var d=/top|bottom/.test(e),p=d?2*h.left-o+r:2*h.top-n+l,c=d?"offsetWidth":"offsetHeight";i.offset(t),this.replaceArrow(p,i[0][c],d)},m.prototype.replaceArrow=function(t,e,i){this.arrow().css(i?"left":"top",50*(1-t/e)+"%").css(i?"top":"left","")},m.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();this.options.html?(this.options.sanitize&&(e=n(e,this.options.whiteList,this.options.sanitizeFn)),t.find(".tooltip-inner").html(e)):t.find(".tooltip-inner").text(e),t.removeClass("fade in top bottom left right")},m.prototype.hide=function(t){var e=this,i=g(this.$tip),o=g.Event("hide.bs."+this.type);function n(){"in"!=e.hoverState&&i.detach(),e.$element&&e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),t&&t()}if(this.$element.trigger(o),!o.isDefaultPrevented())return i.removeClass("in"),g.support.transition&&i.hasClass("fade")?i.one("bsTransitionEnd",n).emulateTransitionEnd(m.TRANSITION_DURATION):n(),this.hoverState=null,this},m.prototype.fixTitle=function(){var t=this.$element;(t.attr("title")||"string"!=typeof t.attr("data-original-title"))&&t.attr("data-original-title",t.attr("title")||"").attr("title","")},m.prototype.hasContent=function(){return this.getTitle()},m.prototype.getPosition=function(t){var e=(t=t||this.$element)[0],i="BODY"==e.tagName,o=e.getBoundingClientRect();null==o.width&&(o=g.extend({},o,{width:o.right-o.left,height:o.bottom-o.top}));var n=window.SVGElement&&e instanceof window.SVGElement,s=i?{top:0,left:0}:n?null:t.offset(),a={scroll:i?document.documentElement.scrollTop||document.body.scrollTop:t.scrollTop()},r=i?{width:g(window).width(),height:g(window).height()}:null;return g.extend({},o,a,r,s)},m.prototype.getCalculatedOffset=function(t,e,i,o){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-i/2}:"top"==t?{top:e.top-o,left:e.left+e.width/2-i/2}:"left"==t?{top:e.top+e.height/2-o/2,left:e.left-i}:{top:e.top+e.height/2-o/2,left:e.left+e.width}},m.prototype.getViewportAdjustedDelta=function(t,e,i,o){var n={top:0,left:0};if(!this.$viewport)return n;var s=this.options.viewport&&this.options.viewport.padding||0,a=this.getPosition(this.$viewport);if(/right|left/.test(t)){var r=e.top-s-a.scroll,l=e.top+s-a.scroll+o;r<a.top?n.top=a.top-r:l>a.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;h<a.left?n.left=a.left-h:d>a.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})},m.prototype.sanitizeHtml=function(t){return n(t,this.options.whiteList,this.options.sanitizeFn)};var e=g.fn.tooltip;g.fn.tooltip=function i(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=e,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.1",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();if(this.options.html){var o=typeof i;this.options.sanitize&&(e=this.sanitizeHtml(e),"string"===o&&(i=this.sanitizeHtml(i))),t.find(".popover-title").html(e),t.find(".popover-content").children().detach().end()["string"===o?"html":"append"](i)}else t.find(".popover-title").text(e),t.find(".popover-content").children().detach().end().text(i);t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.1",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e<n[0])return this.activeTarget=null,this.clear();for(t=n.length;t--;)a!=s[t]&&e>=n[t]&&(n[t+1]===undefined||e<n[t+1])&&this.activate(s[t])},n.prototype.activate=function(t){this.activeTarget=t,this.clear();var e=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',i=s(e).parents("li").addClass("active");i.parent(".dropdown-menu").length&&(i=i.closest("li.dropdown").addClass("active")),i.trigger("activate.bs.scrollspy")},n.prototype.clear=function(){s(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=s.fn.scrollspy;s.fn.scrollspy=e,s.fn.scrollspy.Constructor=n,s.fn.scrollspy.noConflict=function(){return s.fn.scrollspy=t,this},s(window).on("load.bs.scrollspy.data-api",function(){s('[data-spy="scroll"]').each(function(){var t=s(this);e.call(t,t.data())})})}(jQuery),function(r){"use strict";var a=function(t){this.element=r(t)};function e(i){return this.each(function(){var t=r(this),e=t.data("bs.tab");e||t.data("bs.tab",e=new a(this)),"string"==typeof i&&e[i]()})}a.VERSION="3.4.1",a.TRANSITION_DURATION=150,a.prototype.show=function(){var t=this.element,e=t.closest("ul:not(.dropdown-menu)"),i=t.data("target");if(i||(i=(i=t.attr("href"))&&i.replace(/.*(?=#[^\s]*$)/,"")),!t.parent("li").hasClass("active")){var o=e.find(".active:last a"),n=r.Event("hide.bs.tab",{relatedTarget:t[0]}),s=r.Event("show.bs.tab",{relatedTarget:o[0]});if(o.trigger(n),t.trigger(s),!s.isDefaultPrevented()&&!n.isDefaultPrevented()){var a=r(document).find(i);this.activate(t.closest("li"),e),this.activate(a,a.parent(),function(){o.trigger({type:"hidden.bs.tab",relatedTarget:t[0]}),t.trigger({type:"shown.bs.tab",relatedTarget:o[0]})})}}},a.prototype.activate=function(t,e,i){var o=e.find("> .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.1",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n<i&&"top";if("bottom"==this.affixed)return null!=i?!(n+this.unpin<=s.top)&&"bottom":!(n+a<=t-o)&&"bottom";var r=null==this.affixed,l=r?n:s.top;return null!=i&&n<=i?"top":null!=o&&t-o<=l+(r?a:e)&&"bottom"},h.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(h.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},h.prototype.checkPositionWithEventLoop=function(){setTimeout(l.proxy(this.checkPosition,this),1)},h.prototype.checkPosition=function(){if(this.$element.is(":visible")){var t=this.$element.height(),e=this.options.offset,i=e.top,o=e.bottom,n=Math.max(l(document).height(),l(document.body).height());"object"!=typeof e&&(o=i=e),"function"==typeof i&&(i=e.top(this.$element)),"function"==typeof o&&(o=e.bottom(this.$element));var s=this.getState(n,t,i,o);if(this.affixed!=s){null!=this.unpin&&this.$element.css("top","");var a="affix"+(s?"-"+s:""),r=l.Event(a+".bs.affix");if(this.$element.trigger(r),r.isDefaultPrevented())return;this.affixed=s,this.unpin="bottom"==s?this.getPinnedOffset():null,this.$element.removeClass(h.RESET).addClass(a).trigger(a.replace("affix","affixed")+".bs.affix")}"bottom"==s&&this.$element.offset({top:n-t-o})}};var t=l.fn.affix;l.fn.affix=i,l.fn.affix.Constructor=h,l.fn.affix.noConflict=function(){return l.fn.affix=t,this},l(window).on("load",function(){l('[data-spy="affix"]').each(function(){var t=l(this),e=t.data();e.offset=e.offset||{},null!=e.offsetBottom&&(e.offset.bottom=e.offsetBottom),null!=e.offsetTop&&(e.offset.top=e.offsetTop),i.call(t,e)})})}(jQuery); \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/coreapi-0.1.1.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/coreapi-0.1.1.js new file mode 100644 index 0000000..3c5a2be --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/coreapi-0.1.1.js @@ -0,0 +1,2043 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.coreapi = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var BasicAuthentication = function () { + function BasicAuthentication() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, BasicAuthentication); + + var username = options.username; + var password = options.password; + var hash = window.btoa(username + ':' + password); + this.auth = 'Basic ' + hash; + } + + _createClass(BasicAuthentication, [{ + key: 'authenticate', + value: function authenticate(options) { + options.headers['Authorization'] = this.auth; + return options; + } + }]); + + return BasicAuthentication; +}(); + +module.exports = { + BasicAuthentication: BasicAuthentication +}; + +},{}],2:[function(require,module,exports){ +'use strict'; + +var basic = require('./basic'); +var session = require('./session'); +var token = require('./token'); + +module.exports = { + BasicAuthentication: basic.BasicAuthentication, + SessionAuthentication: session.SessionAuthentication, + TokenAuthentication: token.TokenAuthentication +}; + +},{"./basic":1,"./session":3,"./token":4}],3:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var utils = require('../utils'); + +function trim(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); +} + +function getCookie(cookieName, cookieString) { + cookieString = cookieString || window.document.cookie; + if (cookieString && cookieString !== '') { + var cookies = cookieString.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, cookieName.length + 1) === cookieName + '=') { + return decodeURIComponent(cookie.substring(cookieName.length + 1)); + } + } + } + return null; +} + +var SessionAuthentication = function () { + function SessionAuthentication() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, SessionAuthentication); + + this.csrfToken = getCookie(options.csrfCookieName, options.cookieString); + this.csrfHeaderName = options.csrfHeaderName; + } + + _createClass(SessionAuthentication, [{ + key: 'authenticate', + value: function authenticate(options) { + options.credentials = 'same-origin'; + if (this.csrfToken && !utils.csrfSafeMethod(options.method)) { + options.headers[this.csrfHeaderName] = this.csrfToken; + } + return options; + } + }]); + + return SessionAuthentication; +}(); + +module.exports = { + SessionAuthentication: SessionAuthentication +}; + +},{"../utils":15}],4:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var TokenAuthentication = function () { + function TokenAuthentication() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, TokenAuthentication); + + this.token = options.token; + this.scheme = options.scheme || 'Bearer'; + } + + _createClass(TokenAuthentication, [{ + key: 'authenticate', + value: function authenticate(options) { + options.headers['Authorization'] = this.scheme + ' ' + this.token; + return options; + } + }]); + + return TokenAuthentication; +}(); + +module.exports = { + TokenAuthentication: TokenAuthentication +}; + +},{}],5:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var document = require('./document'); +var codecs = require('./codecs'); +var errors = require('./errors'); +var transports = require('./transports'); +var utils = require('./utils'); + +function lookupLink(node, keys) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var key = _step.value; + + if (node instanceof document.Document) { + node = node.content[key]; + } else { + node = node[key]; + } + if (node === undefined) { + throw new errors.LinkLookupError('Invalid link lookup: ' + JSON.stringify(keys)); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + if (!(node instanceof document.Link)) { + throw new errors.LinkLookupError('Invalid link lookup: ' + JSON.stringify(keys)); + } + return node; +} + +var Client = function () { + function Client() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, Client); + + var transportOptions = { + auth: options.auth || null, + headers: options.headers || {}, + requestCallback: options.requestCallback, + responseCallback: options.responseCallback + }; + + this.decoders = options.decoders || [new codecs.CoreJSONCodec(), new codecs.JSONCodec(), new codecs.TextCodec()]; + this.transports = options.transports || [new transports.HTTPTransport(transportOptions)]; + } + + _createClass(Client, [{ + key: 'action', + value: function action(document, keys) { + var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var link = lookupLink(document, keys); + var transport = utils.determineTransport(this.transports, link.url); + return transport.action(link, this.decoders, params); + } + }, { + key: 'get', + value: function get(url) { + var link = new document.Link(url, 'get'); + var transport = utils.determineTransport(this.transports, url); + return transport.action(link, this.decoders); + } + }]); + + return Client; +}(); + +module.exports = { + Client: Client +}; + +},{"./codecs":7,"./document":10,"./errors":11,"./transports":14,"./utils":15}],6:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var document = require('../document'); +var URL = require('url-parse'); + +function unescapeKey(key) { + if (key.match(/__(type|meta)$/)) { + return key.substring(1); + } + return key; +} + +function getString(obj, key) { + var value = obj[key]; + if (typeof value === 'string') { + return value; + } + return ''; +} + +function getBoolean(obj, key) { + var value = obj[key]; + if (typeof value === 'boolean') { + return value; + } + return false; +} + +function getObject(obj, key) { + var value = obj[key]; + if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') { + return value; + } + return {}; +} + +function getArray(obj, key) { + var value = obj[key]; + if (value instanceof Array) { + return value; + } + return []; +} + +function getContent(data, baseUrl) { + var excluded = ['_type', '_meta']; + var content = {}; + for (var property in data) { + if (data.hasOwnProperty(property) && !excluded.includes(property)) { + var key = unescapeKey(property); + var value = primitiveToNode(data[property], baseUrl); + content[key] = value; + } + } + return content; +} + +function primitiveToNode(data, baseUrl) { + var isObject = data instanceof Object && !(data instanceof Array); + + if (isObject && data._type === 'document') { + // Document + var meta = getObject(data, '_meta'); + var relativeUrl = getString(meta, 'url'); + var url = relativeUrl ? URL(relativeUrl, baseUrl).toString() : ''; + var title = getString(meta, 'title'); + var description = getString(meta, 'description'); + var content = getContent(data, url); + return new document.Document(url, title, description, content); + } else if (isObject && data._type === 'link') { + // Link + var _relativeUrl = getString(data, 'url'); + var _url = _relativeUrl ? URL(_relativeUrl, baseUrl).toString() : ''; + var method = getString(data, 'action') || 'get'; + var _title = getString(data, 'title'); + var _description = getString(data, 'description'); + var fieldsData = getArray(data, 'fields'); + var fields = []; + for (var idx = 0, len = fieldsData.length; idx < len; idx++) { + var value = fieldsData[idx]; + var name = getString(value, 'name'); + var required = getBoolean(value, 'required'); + var location = getString(value, 'location'); + var fieldDescription = getString(value, 'fieldDescription'); + var field = new document.Field(name, required, location, fieldDescription); + fields.push(field); + } + return new document.Link(_url, method, 'application/json', fields, _title, _description); + } else if (isObject) { + // Object + var _content = {}; + for (var key in data) { + if (data.hasOwnProperty(key)) { + _content[key] = primitiveToNode(data[key], baseUrl); + } + } + return _content; + } else if (data instanceof Array) { + // Object + var _content2 = []; + for (var _idx = 0, _len = data.length; _idx < _len; _idx++) { + _content2.push(primitiveToNode(data[_idx], baseUrl)); + } + return _content2; + } + // Primitive + return data; +} + +var CoreJSONCodec = function () { + function CoreJSONCodec() { + _classCallCheck(this, CoreJSONCodec); + + this.mediaType = 'application/coreapi+json'; + } + + _createClass(CoreJSONCodec, [{ + key: 'decode', + value: function decode(text) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var data = text; + if (options.preloaded === undefined || !options.preloaded) { + data = JSON.parse(text); + } + return primitiveToNode(data, options.url); + } + }]); + + return CoreJSONCodec; +}(); + +module.exports = { + CoreJSONCodec: CoreJSONCodec +}; + +},{"../document":10,"url-parse":19}],7:[function(require,module,exports){ +'use strict'; + +var corejson = require('./corejson'); +var json = require('./json'); +var text = require('./text'); + +module.exports = { + CoreJSONCodec: corejson.CoreJSONCodec, + JSONCodec: json.JSONCodec, + TextCodec: text.TextCodec +}; + +},{"./corejson":6,"./json":8,"./text":9}],8:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var JSONCodec = function () { + function JSONCodec() { + _classCallCheck(this, JSONCodec); + + this.mediaType = 'application/json'; + } + + _createClass(JSONCodec, [{ + key: 'decode', + value: function decode(text) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return JSON.parse(text); + } + }]); + + return JSONCodec; +}(); + +module.exports = { + JSONCodec: JSONCodec +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var TextCodec = function () { + function TextCodec() { + _classCallCheck(this, TextCodec); + + this.mediaType = 'text/*'; + } + + _createClass(TextCodec, [{ + key: 'decode', + value: function decode(text) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + return text; + } + }]); + + return TextCodec; +}(); + +module.exports = { + TextCodec: TextCodec +}; + +},{}],10:[function(require,module,exports){ +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Document = function Document() { + var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var description = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var content = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + _classCallCheck(this, Document); + + this.url = url; + this.title = title; + this.description = description; + this.content = content; +}; + +var Link = function Link(url, method) { + var encoding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'application/json'; + var fields = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; + var title = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : ''; + var description = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : ''; + + _classCallCheck(this, Link); + + if (url === undefined) { + throw new Error('url argument is required'); + } + + if (method === undefined) { + throw new Error('method argument is required'); + } + + this.url = url; + this.method = method; + this.encoding = encoding; + this.fields = fields; + this.title = title; + this.description = description; +}; + +var Field = function Field(name) { + var required = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var location = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var description = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; + + _classCallCheck(this, Field); + + if (name === undefined) { + throw new Error('name argument is required'); + } + + this.name = name; + this.required = required; + this.location = location; + this.description = description; +}; + +module.exports = { + Document: Document, + Link: Link, + Field: Field +}; + +},{}],11:[function(require,module,exports){ +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ParameterError = function (_Error) { + _inherits(ParameterError, _Error); + + function ParameterError(message) { + _classCallCheck(this, ParameterError); + + var _this = _possibleConstructorReturn(this, (ParameterError.__proto__ || Object.getPrototypeOf(ParameterError)).call(this, message)); + + _this.message = message; + _this.name = 'ParameterError'; + return _this; + } + + return ParameterError; +}(Error); + +var LinkLookupError = function (_Error2) { + _inherits(LinkLookupError, _Error2); + + function LinkLookupError(message) { + _classCallCheck(this, LinkLookupError); + + var _this2 = _possibleConstructorReturn(this, (LinkLookupError.__proto__ || Object.getPrototypeOf(LinkLookupError)).call(this, message)); + + _this2.message = message; + _this2.name = 'LinkLookupError'; + return _this2; + } + + return LinkLookupError; +}(Error); + +var ErrorMessage = function (_Error3) { + _inherits(ErrorMessage, _Error3); + + function ErrorMessage(message, content) { + _classCallCheck(this, ErrorMessage); + + var _this3 = _possibleConstructorReturn(this, (ErrorMessage.__proto__ || Object.getPrototypeOf(ErrorMessage)).call(this, message)); + + _this3.message = message; + _this3.content = content; + _this3.name = 'ErrorMessage'; + return _this3; + } + + return ErrorMessage; +}(Error); + +module.exports = { + ParameterError: ParameterError, + LinkLookupError: LinkLookupError, + ErrorMessage: ErrorMessage +}; + +},{}],12:[function(require,module,exports){ +'use strict'; + +var auth = require('./auth'); +var client = require('./client'); +var codecs = require('./codecs'); +var document = require('./document'); +var errors = require('./errors'); +var transports = require('./transports'); +var utils = require('./utils'); + +var coreapi = { + Client: client.Client, + Document: document.Document, + Link: document.Link, + auth: auth, + codecs: codecs, + errors: errors, + transports: transports, + utils: utils +}; + +module.exports = coreapi; + +},{"./auth":2,"./client":5,"./codecs":7,"./document":10,"./errors":11,"./transports":14,"./utils":15}],13:[function(require,module,exports){ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var fetch = require('isomorphic-fetch'); +var errors = require('../errors'); +var utils = require('../utils'); +var URL = require('url-parse'); +var urlTemplate = require('url-template'); + +var parseResponse = function parseResponse(response, decoders, responseCallback) { + return response.text().then(function (text) { + if (responseCallback) { + responseCallback(response, text); + } + var contentType = response.headers.get('Content-Type'); + var decoder = utils.negotiateDecoder(decoders, contentType); + var options = { url: response.url }; + return decoder.decode(text, options); + }); +}; + +var HTTPTransport = function () { + function HTTPTransport() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, HTTPTransport); + + this.schemes = ['http', 'https']; + this.auth = options.auth || null; + this.headers = options.headers || {}; + this.fetch = options.fetch || fetch; + this.FormData = options.FormData || window.FormData; + this.requestCallback = options.requestCallback; + this.responseCallback = options.responseCallback; + } + + _createClass(HTTPTransport, [{ + key: 'buildRequest', + value: function buildRequest(link, decoders) { + var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var fields = link.fields; + var method = link.method.toUpperCase(); + var queryParams = {}; + var pathParams = {}; + var formParams = {}; + var fieldNames = []; + var hasBody = false; + + for (var idx = 0, len = fields.length; idx < len; idx++) { + var field = fields[idx]; + + // Ensure any required fields are included + if (!params.hasOwnProperty(field.name)) { + if (field.required) { + throw new errors.ParameterError('Missing required field: "' + field.name + '"'); + } else { + continue; + } + } + + fieldNames.push(field.name); + if (field.location === 'query') { + queryParams[field.name] = params[field.name]; + } else if (field.location === 'path') { + pathParams[field.name] = params[field.name]; + } else if (field.location === 'form') { + formParams[field.name] = params[field.name]; + hasBody = true; + } else if (field.location === 'body') { + formParams = params[field.name]; + hasBody = true; + } + } + + // Check for any parameters that did not have a matching field + for (var property in params) { + if (params.hasOwnProperty(property) && !fieldNames.includes(property)) { + throw new errors.ParameterError('Unknown parameter: "' + property + '"'); + } + } + + var requestOptions = { method: method, headers: {} }; + + Object.assign(requestOptions.headers, this.headers); + + if (hasBody) { + if (link.encoding === 'application/json') { + requestOptions.body = JSON.stringify(formParams); + requestOptions.headers['Content-Type'] = 'application/json'; + } else if (link.encoding === 'multipart/form-data') { + var form = new this.FormData(); + + for (var paramKey in formParams) { + form.append(paramKey, formParams[paramKey]); + } + requestOptions.body = form; + } else if (link.encoding === 'application/x-www-form-urlencoded') { + var formBody = []; + for (var _paramKey in formParams) { + var encodedKey = encodeURIComponent(_paramKey); + var encodedValue = encodeURIComponent(formParams[_paramKey]); + formBody.push(encodedKey + '=' + encodedValue); + } + formBody = formBody.join('&'); + + requestOptions.body = formBody; + requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + } + + if (this.auth) { + requestOptions = this.auth.authenticate(requestOptions); + } + + var parsedUrl = urlTemplate.parse(link.url); + parsedUrl = parsedUrl.expand(pathParams); + parsedUrl = new URL(parsedUrl); + parsedUrl.set('query', queryParams); + + return { + url: parsedUrl.toString(), + options: requestOptions + }; + } + }, { + key: 'action', + value: function action(link, decoders) { + var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var responseCallback = this.responseCallback; + var request = this.buildRequest(link, decoders, params); + + if (this.requestCallback) { + this.requestCallback(request); + } + + return this.fetch(request.url, request.options).then(function (response) { + if (response.status === 204) { + return; + } + return parseResponse(response, decoders, responseCallback).then(function (data) { + if (response.ok) { + return data; + } else { + var title = response.status + ' ' + response.statusText; + var error = new errors.ErrorMessage(title, data); + return Promise.reject(error); + } + }); + }); + } + }]); + + return HTTPTransport; +}(); + +module.exports = { + HTTPTransport: HTTPTransport +}; + +},{"../errors":11,"../utils":15,"isomorphic-fetch":16,"url-parse":19,"url-template":21}],14:[function(require,module,exports){ +'use strict'; + +var http = require('./http'); + +module.exports = { + HTTPTransport: http.HTTPTransport +}; + +},{"./http":13}],15:[function(require,module,exports){ +'use strict'; + +var URL = require('url-parse'); + +var determineTransport = function determineTransport(transports, url) { + var parsedUrl = new URL(url); + var scheme = parsedUrl.protocol.replace(':', ''); + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = transports[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var transport = _step.value; + + if (transport.schemes.includes(scheme)) { + return transport; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + throw Error('Unsupported scheme in URL: ' + url); +}; + +var negotiateDecoder = function negotiateDecoder(decoders, contentType) { + if (contentType === undefined || contentType === null) { + return decoders[0]; + } + + var fullType = contentType.toLowerCase().split(';')[0].trim(); + var mainType = fullType.split('/')[0] + '/*'; + var wildcardType = '*/*'; + var acceptableTypes = [fullType, mainType, wildcardType]; + + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = decoders[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var decoder = _step2.value; + + if (acceptableTypes.includes(decoder.mediaType)) { + return decoder; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + throw Error('Unsupported media in Content-Type header: ' + contentType); +}; + +var csrfSafeMethod = function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method) + ); +}; + +module.exports = { + determineTransport: determineTransport, + negotiateDecoder: negotiateDecoder, + csrfSafeMethod: csrfSafeMethod +}; + +},{"url-parse":19}],16:[function(require,module,exports){ +// the whatwg-fetch polyfill installs the fetch() function +// on the global object (window or self) +// +// Return that as the export for use in Webpack, Browserify etc. +require('whatwg-fetch'); +module.exports = self.fetch.bind(self); + +},{"whatwg-fetch":22}],17:[function(require,module,exports){ +'use strict'; + +var has = Object.prototype.hasOwnProperty; + +/** + * Simple query string parser. + * + * @param {String} query The query string that needs to be parsed. + * @returns {Object} + * @api public + */ +function querystring(query) { + var parser = /([^=?&]+)=?([^&]*)/g + , result = {} + , part; + + // + // Little nifty parsing hack, leverage the fact that RegExp.exec increments + // the lastIndex property so we can continue executing this loop until we've + // parsed all results. + // + for (; + part = parser.exec(query); + result[decodeURIComponent(part[1])] = decodeURIComponent(part[2]) + ); + + return result; +} + +/** + * Transform a query string to an object. + * + * @param {Object} obj Object that should be transformed. + * @param {String} prefix Optional prefix. + * @returns {String} + * @api public + */ +function querystringify(obj, prefix) { + prefix = prefix || ''; + + var pairs = []; + + // + // Optionally prefix with a '?' if needed + // + if ('string' !== typeof prefix) prefix = '?'; + + for (var key in obj) { + if (has.call(obj, key)) { + pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key])); + } + } + + return pairs.length ? prefix + pairs.join('&') : ''; +} + +// +// Expose the module. +// +exports.stringify = querystringify; +exports.parse = querystring; + +},{}],18:[function(require,module,exports){ +'use strict'; + +/** + * Check if we're required to add a port number. + * + * @see https://url.spec.whatwg.org/#default-port + * @param {Number|String} port Port number we need to check + * @param {String} protocol Protocol we need to check against. + * @returns {Boolean} Is it a default port for the given protocol + * @api private + */ +module.exports = function required(port, protocol) { + protocol = protocol.split(':')[0]; + port = +port; + + if (!port) return false; + + switch (protocol) { + case 'http': + case 'ws': + return port !== 80; + + case 'https': + case 'wss': + return port !== 443; + + case 'ftp': + return port !== 21; + + case 'gopher': + return port !== 70; + + case 'file': + return false; + } + + return port !== 0; +}; + +},{}],19:[function(require,module,exports){ +'use strict'; + +var required = require('requires-port') + , lolcation = require('./lolcation') + , qs = require('querystringify') + , protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i; + +/** + * These are the parse rules for the URL parser, it informs the parser + * about: + * + * 0. The char it Needs to parse, if it's a string it should be done using + * indexOf, RegExp using exec and NaN means set as current value. + * 1. The property we should set when parsing this value. + * 2. Indication if it's backwards or forward parsing, when set as number it's + * the value of extra chars that should be split off. + * 3. Inherit from location if non existing in the parser. + * 4. `toLowerCase` the resulting value. + */ +var rules = [ + ['#', 'hash'], // Extract from the back. + ['?', 'query'], // Extract from the back. + ['/', 'pathname'], // Extract from the back. + ['@', 'auth', 1], // Extract from the front. + [NaN, 'host', undefined, 1, 1], // Set left over value. + [/:(\d+)$/, 'port', undefined, 1], // RegExp the back. + [NaN, 'hostname', undefined, 1, 1] // Set left over. +]; + +/** + * @typedef ProtocolExtract + * @type Object + * @property {String} protocol Protocol matched in the URL, in lowercase. + * @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. + * @property {String} rest Rest of the URL that is not part of the protocol. + */ + +/** + * Extract protocol information from a URL with/without double slash ("//"). + * + * @param {String} address URL we want to extract from. + * @return {ProtocolExtract} Extracted information. + * @api private + */ +function extractProtocol(address) { + var match = protocolre.exec(address); + + return { + protocol: match[1] ? match[1].toLowerCase() : '', + slashes: !!match[2], + rest: match[3] + }; +} + +/** + * Resolve a relative URL pathname against a base URL pathname. + * + * @param {String} relative Pathname of the relative URL. + * @param {String} base Pathname of the base URL. + * @return {String} Resolved pathname. + * @api private + */ +function resolve(relative, base) { + var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) + , i = path.length + , last = path[i - 1] + , unshift = false + , up = 0; + + while (i--) { + if (path[i] === '.') { + path.splice(i, 1); + } else if (path[i] === '..') { + path.splice(i, 1); + up++; + } else if (up) { + if (i === 0) unshift = true; + path.splice(i, 1); + up--; + } + } + + if (unshift) path.unshift(''); + if (last === '.' || last === '..') path.push(''); + + return path.join('/'); +} + +/** + * The actual URL instance. Instead of returning an object we've opted-in to + * create an actual constructor as it's much more memory efficient and + * faster and it pleases my OCD. + * + * @constructor + * @param {String} address URL we want to parse. + * @param {Object|String} location Location defaults for relative paths. + * @param {Boolean|Function} parser Parser for the query string. + * @api public + */ +function URL(address, location, parser) { + if (!(this instanceof URL)) { + return new URL(address, location, parser); + } + + var relative, extracted, parse, instruction, index, key + , instructions = rules.slice() + , type = typeof location + , url = this + , i = 0; + + // + // The following if statements allows this module two have compatibility with + // 2 different API: + // + // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments + // where the boolean indicates that the query string should also be parsed. + // + // 2. The `URL` interface of the browser which accepts a URL, object as + // arguments. The supplied object will be used as default values / fall-back + // for relative paths. + // + if ('object' !== type && 'string' !== type) { + parser = location; + location = null; + } + + if (parser && 'function' !== typeof parser) parser = qs.parse; + + location = lolcation(location); + + // + // Extract protocol information before running the instructions. + // + extracted = extractProtocol(address || ''); + relative = !extracted.protocol && !extracted.slashes; + url.slashes = extracted.slashes || relative && location.slashes; + url.protocol = extracted.protocol || location.protocol || ''; + address = extracted.rest; + + // + // When the authority component is absent the URL starts with a path + // component. + // + if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname']; + + for (; i < instructions.length; i++) { + instruction = instructions[i]; + parse = instruction[0]; + key = instruction[1]; + + if (parse !== parse) { + url[key] = address; + } else if ('string' === typeof parse) { + if (~(index = address.indexOf(parse))) { + if ('number' === typeof instruction[2]) { + url[key] = address.slice(0, index); + address = address.slice(index + instruction[2]); + } else { + url[key] = address.slice(index); + address = address.slice(0, index); + } + } + } else if (index = parse.exec(address)) { + url[key] = index[1]; + address = address.slice(0, index.index); + } + + url[key] = url[key] || ( + relative && instruction[3] ? location[key] || '' : '' + ); + + // + // Hostname, host and protocol should be lowercased so they can be used to + // create a proper `origin`. + // + if (instruction[4]) url[key] = url[key].toLowerCase(); + } + + // + // Also parse the supplied query string in to an object. If we're supplied + // with a custom parser as function use that instead of the default build-in + // parser. + // + if (parser) url.query = parser(url.query); + + // + // If the URL is relative, resolve the pathname against the base URL. + // + if ( + relative + && location.slashes + && url.pathname.charAt(0) !== '/' + && (url.pathname !== '' || location.pathname !== '') + ) { + url.pathname = resolve(url.pathname, location.pathname); + } + + // + // We should not add port numbers if they are already the default port number + // for a given protocol. As the host also contains the port number we're going + // override it with the hostname which contains no port number. + // + if (!required(url.port, url.protocol)) { + url.host = url.hostname; + url.port = ''; + } + + // + // Parse down the `auth` for the username and password. + // + url.username = url.password = ''; + if (url.auth) { + instruction = url.auth.split(':'); + url.username = instruction[0] || ''; + url.password = instruction[1] || ''; + } + + url.origin = url.protocol && url.host && url.protocol !== 'file:' + ? url.protocol +'//'+ url.host + : 'null'; + + // + // The href is just the compiled result. + // + url.href = url.toString(); +} + +/** + * This is convenience method for changing properties in the URL instance to + * insure that they all propagate correctly. + * + * @param {String} part Property we need to adjust. + * @param {Mixed} value The newly assigned value. + * @param {Boolean|Function} fn When setting the query, it will be the function + * used to parse the query. + * When setting the protocol, double slash will be + * removed from the final url if it is true. + * @returns {URL} + * @api public + */ +URL.prototype.set = function set(part, value, fn) { + var url = this; + + switch (part) { + case 'query': + if ('string' === typeof value && value.length) { + value = (fn || qs.parse)(value); + } + + url[part] = value; + break; + + case 'port': + url[part] = value; + + if (!required(value, url.protocol)) { + url.host = url.hostname; + url[part] = ''; + } else if (value) { + url.host = url.hostname +':'+ value; + } + + break; + + case 'hostname': + url[part] = value; + + if (url.port) value += ':'+ url.port; + url.host = value; + break; + + case 'host': + url[part] = value; + + if (/:\d+$/.test(value)) { + value = value.split(':'); + url.port = value.pop(); + url.hostname = value.join(':'); + } else { + url.hostname = value; + url.port = ''; + } + + break; + + case 'protocol': + url.protocol = value.toLowerCase(); + url.slashes = !fn; + break; + + case 'pathname': + url.pathname = value.length && value.charAt(0) !== '/' ? '/' + value : value; + + break; + + default: + url[part] = value; + } + + for (var i = 0; i < rules.length; i++) { + var ins = rules[i]; + + if (ins[4]) url[ins[1]] = url[ins[1]].toLowerCase(); + } + + url.origin = url.protocol && url.host && url.protocol !== 'file:' + ? url.protocol +'//'+ url.host + : 'null'; + + url.href = url.toString(); + + return url; +}; + +/** + * Transform the properties back in to a valid and full URL string. + * + * @param {Function} stringify Optional query stringify function. + * @returns {String} + * @api public + */ +URL.prototype.toString = function toString(stringify) { + if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify; + + var query + , url = this + , protocol = url.protocol; + + if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':'; + + var result = protocol + (url.slashes ? '//' : ''); + + if (url.username) { + result += url.username; + if (url.password) result += ':'+ url.password; + result += '@'; + } + + result += url.host + url.pathname; + + query = 'object' === typeof url.query ? stringify(url.query) : url.query; + if (query) result += '?' !== query.charAt(0) ? '?'+ query : query; + + if (url.hash) result += url.hash; + + return result; +}; + +// +// Expose the URL parser and some additional properties that might be useful for +// others or testing. +// +URL.extractProtocol = extractProtocol; +URL.location = lolcation; +URL.qs = qs; + +module.exports = URL; + +},{"./lolcation":20,"querystringify":17,"requires-port":18}],20:[function(require,module,exports){ +(function (global){ +'use strict'; + +var slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//; + +/** + * These properties should not be copied or inherited from. This is only needed + * for all non blob URL's as a blob URL does not include a hash, only the + * origin. + * + * @type {Object} + * @private + */ +var ignore = { hash: 1, query: 1 } + , URL; + +/** + * The location object differs when your code is loaded through a normal page, + * Worker or through a worker using a blob. And with the blobble begins the + * trouble as the location object will contain the URL of the blob, not the + * location of the page where our code is loaded in. The actual origin is + * encoded in the `pathname` so we can thankfully generate a good "default" + * location from it so we can generate proper relative URL's again. + * + * @param {Object|String} loc Optional default location object. + * @returns {Object} lolcation object. + * @api public + */ +module.exports = function lolcation(loc) { + loc = loc || global.location || {}; + URL = URL || require('./'); + + var finaldestination = {} + , type = typeof loc + , key; + + if ('blob:' === loc.protocol) { + finaldestination = new URL(unescape(loc.pathname), {}); + } else if ('string' === type) { + finaldestination = new URL(loc, {}); + for (key in ignore) delete finaldestination[key]; + } else if ('object' === type) { + for (key in loc) { + if (key in ignore) continue; + finaldestination[key] = loc[key]; + } + + if (finaldestination.slashes === undefined) { + finaldestination.slashes = slashes.test(loc.href); + } + } + + return finaldestination; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + +},{"./":19}],21:[function(require,module,exports){ +(function (root, factory) { + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define([], factory); + } else { + root.urltemplate = factory(); + } +}(this, function () { + /** + * @constructor + */ + function UrlTemplate() { + } + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeReserved = function (str) { + return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) { + if (!/%[0-9A-Fa-f]/.test(part)) { + part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']'); + } + return part; + }).join(''); + }; + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeUnreserved = function (str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase(); + }); + } + + /** + * @private + * @param {string} operator + * @param {string} value + * @param {string} key + * @return {string} + */ + UrlTemplate.prototype.encodeValue = function (operator, value, key) { + value = (operator === '+' || operator === '#') ? this.encodeReserved(value) : this.encodeUnreserved(value); + + if (key) { + return this.encodeUnreserved(key) + '=' + value; + } else { + return value; + } + }; + + /** + * @private + * @param {*} value + * @return {boolean} + */ + UrlTemplate.prototype.isDefined = function (value) { + return value !== undefined && value !== null; + }; + + /** + * @private + * @param {string} + * @return {boolean} + */ + UrlTemplate.prototype.isKeyOperator = function (operator) { + return operator === ';' || operator === '&' || operator === '?'; + }; + + /** + * @private + * @param {Object} context + * @param {string} operator + * @param {string} key + * @param {string} modifier + */ + UrlTemplate.prototype.getValues = function (context, operator, key, modifier) { + var value = context[key], + result = []; + + if (this.isDefined(value) && value !== '') { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + value = value.toString(); + + if (modifier && modifier !== '*') { + value = value.substring(0, parseInt(modifier, 10)); + } + + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + } else { + if (modifier === '*') { + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + result.push(this.encodeValue(operator, value[k], k)); + } + }, this); + } + } else { + var tmp = []; + + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + tmp.push(this.encodeValue(operator, value)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + tmp.push(this.encodeUnreserved(k)); + tmp.push(this.encodeValue(operator, value[k].toString())); + } + }, this); + } + + if (this.isKeyOperator(operator)) { + result.push(this.encodeUnreserved(key) + '=' + tmp.join(',')); + } else if (tmp.length !== 0) { + result.push(tmp.join(',')); + } + } + } + } else { + if (operator === ';') { + if (this.isDefined(value)) { + result.push(this.encodeUnreserved(key)); + } + } else if (value === '' && (operator === '&' || operator === '?')) { + result.push(this.encodeUnreserved(key) + '='); + } else if (value === '') { + result.push(''); + } + } + return result; + }; + + /** + * @param {string} template + * @return {function(Object):string} + */ + UrlTemplate.prototype.parse = function (template) { + var that = this; + var operators = ['+', '#', '.', '/', ';', '?', '&']; + + return { + expand: function (context) { + return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { + if (expression) { + var operator = null, + values = []; + + if (operators.indexOf(expression.charAt(0)) !== -1) { + operator = expression.charAt(0); + expression = expression.substr(1); + } + + expression.split(/,/g).forEach(function (variable) { + var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); + values.push.apply(values, that.getValues(context, operator, tmp[1], tmp[2] || tmp[3])); + }); + + if (operator && operator !== '+') { + var separator = ','; + + if (operator === '?') { + separator = '&'; + } else if (operator !== '#') { + separator = operator; + } + return (values.length !== 0 ? operator : '') + values.join(separator); + } else { + return values.join(','); + } + } else { + return that.encodeReserved(literal); + } + }); + } + }; + }; + + return new UrlTemplate(); +})); + +},{}],22:[function(require,module,exports){ +(function(self) { + 'use strict'; + + if (self.fetch) { + return + } + + var support = { + searchParams: 'URLSearchParams' in self, + iterable: 'Symbol' in self && 'iterator' in Symbol, + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob() + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self, + arrayBuffer: 'ArrayBuffer' in self + } + + if (support.arrayBuffer) { + var viewClasses = [ + '[object Int8Array]', + '[object Uint8Array]', + '[object Uint8ClampedArray]', + '[object Int16Array]', + '[object Uint16Array]', + '[object Int32Array]', + '[object Uint32Array]', + '[object Float32Array]', + '[object Float64Array]' + ] + + var isDataView = function(obj) { + return obj && DataView.prototype.isPrototypeOf(obj) + } + + var isArrayBufferView = ArrayBuffer.isView || function(obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 + } + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name) + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value) + } + return value + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function() { + var value = items.shift() + return {done: value === undefined, value: value} + } + } + + if (support.iterable) { + iterator[Symbol.iterator] = function() { + return iterator + } + } + + return iterator + } + + function Headers(headers) { + this.map = {} + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value) + }, this) + + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]) + }, this) + } + } + + Headers.prototype.append = function(name, value) { + name = normalizeName(name) + value = normalizeValue(value) + var oldValue = this.map[name] + this.map[name] = oldValue ? oldValue+','+value : value + } + + Headers.prototype['delete'] = function(name) { + delete this.map[normalizeName(name)] + } + + Headers.prototype.get = function(name) { + name = normalizeName(name) + return this.has(name) ? this.map[name] : null + } + + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)) + } + + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = normalizeValue(value) + } + + Headers.prototype.forEach = function(callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this) + } + } + } + + Headers.prototype.keys = function() { + var items = [] + this.forEach(function(value, name) { items.push(name) }) + return iteratorFor(items) + } + + Headers.prototype.values = function() { + var items = [] + this.forEach(function(value) { items.push(value) }) + return iteratorFor(items) + } + + Headers.prototype.entries = function() { + var items = [] + this.forEach(function(value, name) { items.push([name, value]) }) + return iteratorFor(items) + } + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')) + } + body.bodyUsed = true + } + + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result) + } + reader.onerror = function() { + reject(reader.error) + } + }) + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsArrayBuffer(blob) + return promise + } + + function readBlobAsText(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsText(blob) + return promise + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0) + } else { + var view = new Uint8Array(buf.byteLength) + view.set(new Uint8Array(buf)) + return view.buffer + } + } + + function Body() { + this.bodyUsed = false + + this._initBody = function(body) { + this._bodyInit = body + if (!body) { + this._bodyText = '' + } else if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString() + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer) + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]) + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body) + } else { + throw new Error('unsupported BodyInit type') + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8') + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type) + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') + } + } + } + + if (support.blob) { + this.blob = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } + } + } + + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + var view = new Uint8Array(this._bodyArrayBuffer) + var str = String.fromCharCode.apply(null, view) + return Promise.resolve(str) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } + } + + if (support.arrayBuffer) { + this.arrayBuffer = function() { + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer) + } else { + return this.blob().then(readBlobAsArrayBuffer) + } + } + } + + if (support.formData) { + this.formData = function() { + return this.text().then(decode) + } + } + + this.json = function() { + return this.text().then(JSON.parse) + } + + return this + } + + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + + function normalizeMethod(method) { + var upcased = method.toUpperCase() + return (methods.indexOf(upcased) > -1) ? upcased : method + } + + function Request(input, options) { + options = options || {} + var body = options.body + + if (typeof input === 'string') { + this.url = input + } else { + if (input.bodyUsed) { + throw new TypeError('Already read') + } + this.url = input.url + this.credentials = input.credentials + if (!options.headers) { + this.headers = new Headers(input.headers) + } + this.method = input.method + this.mode = input.mode + if (!body && input._bodyInit != null) { + body = input._bodyInit + input.bodyUsed = true + } + } + + this.credentials = options.credentials || this.credentials || 'omit' + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers) + } + this.method = normalizeMethod(options.method || this.method || 'GET') + this.mode = options.mode || this.mode || null + this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(body) + } + + Request.prototype.clone = function() { + return new Request(this, { body: this._bodyInit }) + } + + function decode(body) { + var form = new FormData() + body.trim().split('&').forEach(function(bytes) { + if (bytes) { + var split = bytes.split('=') + var name = split.shift().replace(/\+/g, ' ') + var value = split.join('=').replace(/\+/g, ' ') + form.append(decodeURIComponent(name), decodeURIComponent(value)) + } + }) + return form + } + + function parseHeaders(rawHeaders) { + var headers = new Headers() + rawHeaders.split('\r\n').forEach(function(line) { + var parts = line.split(':') + var key = parts.shift().trim() + if (key) { + var value = parts.join(':').trim() + headers.append(key, value) + } + }) + return headers + } + + Body.call(Request.prototype) + + function Response(bodyInit, options) { + if (!options) { + options = {} + } + + this.type = 'default' + this.status = 'status' in options ? options.status : 200 + this.ok = this.status >= 200 && this.status < 300 + this.statusText = 'statusText' in options ? options.statusText : 'OK' + this.headers = new Headers(options.headers) + this.url = options.url || '' + this._initBody(bodyInit) + } + + Body.call(Response.prototype) + + Response.prototype.clone = function() { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }) + } + + Response.error = function() { + var response = new Response(null, {status: 0, statusText: ''}) + response.type = 'error' + return response + } + + var redirectStatuses = [301, 302, 303, 307, 308] + + Response.redirect = function(url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code') + } + + return new Response(null, {status: status, headers: {location: url}}) + } + + self.Headers = Headers + self.Request = Request + self.Response = Response + + self.fetch = function(input, init) { + return new Promise(function(resolve, reject) { + var request = new Request(input, init) + var xhr = new XMLHttpRequest() + + xhr.onload = function() { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || '') + } + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') + var body = 'response' in xhr ? xhr.response : xhr.responseText + resolve(new Response(body, options)) + } + + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + + xhr.ontimeout = function() { + reject(new TypeError('Network request failed')) + } + + xhr.open(request.method, request.url, true) + + if (request.credentials === 'include') { + xhr.withCredentials = true + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob' + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value) + }) + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) + }) + } + self.fetch.polyfill = true +})(typeof self !== 'undefined' ? self : this); + +},{}]},{},[12])(12) +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWIvYXV0aC9iYXNpYy5qcyIsImxpYi9hdXRoL2luZGV4LmpzIiwibGliL2F1dGgvc2Vzc2lvbi5qcyIsImxpYi9hdXRoL3Rva2VuLmpzIiwibGliL2NsaWVudC5qcyIsImxpYi9jb2RlY3MvY29yZWpzb24uanMiLCJsaWIvY29kZWNzL2luZGV4LmpzIiwibGliL2NvZGVjcy9qc29uLmpzIiwibGliL2NvZGVjcy90ZXh0LmpzIiwibGliL2RvY3VtZW50LmpzIiwibGliL2Vycm9ycy5qcyIsImxpYi9pbmRleC5qcyIsImxpYi90cmFuc3BvcnRzL2h0dHAuanMiLCJsaWIvdHJhbnNwb3J0cy9pbmRleC5qcyIsImxpYi91dGlscy5qcyIsIm5vZGVfbW9kdWxlcy9pc29tb3JwaGljLWZldGNoL2ZldGNoLW5wbS1icm93c2VyaWZ5LmpzIiwibm9kZV9tb2R1bGVzL3F1ZXJ5c3RyaW5naWZ5L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3JlcXVpcmVzLXBvcnQvaW5kZXguanMiLCJub2RlX21vZHVsZXMvdXJsLXBhcnNlL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3VybC1wYXJzZS9sb2xjYXRpb24uanMiLCJub2RlX21vZHVsZXMvdXJsLXRlbXBsYXRlL2xpYi91cmwtdGVtcGxhdGUuanMiLCJub2RlX21vZHVsZXMvd2hhdHdnLWZldGNoL2ZldGNoLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0lDQU0sbUI7QUFDSixpQ0FBMkI7QUFBQSxRQUFkLE9BQWMsdUVBQUosRUFBSTs7QUFBQTs7QUFDekIsUUFBTSxXQUFXLFFBQVEsUUFBekI7QUFDQSxRQUFNLFdBQVcsUUFBUSxRQUF6QjtBQUNBLFFBQU0sT0FBTyxPQUFPLElBQVAsQ0FBWSxXQUFXLEdBQVgsR0FBaUIsUUFBN0IsQ0FBYjtBQUNBLFNBQUssSUFBTCxHQUFZLFdBQVcsSUFBdkI7QUFDRDs7OztpQ0FFYSxPLEVBQVM7QUFDckIsY0FBUSxPQUFSLENBQWdCLGVBQWhCLElBQW1DLEtBQUssSUFBeEM7QUFDQSxhQUFPLE9BQVA7QUFDRDs7Ozs7O0FBR0gsT0FBTyxPQUFQLEdBQWlCO0FBQ2YsdUJBQXFCO0FBRE4sQ0FBakI7Ozs7O0FDZEEsSUFBTSxRQUFRLFFBQVEsU0FBUixDQUFkO0FBQ0EsSUFBTSxVQUFVLFFBQVEsV0FBUixDQUFoQjtBQUNBLElBQU0sUUFBUSxRQUFRLFNBQVIsQ0FBZDs7QUFFQSxPQUFPLE9BQVAsR0FBaUI7QUFDZix1QkFBcUIsTUFBTSxtQkFEWjtBQUVmLHlCQUF1QixRQUFRLHFCQUZoQjtBQUdmLHVCQUFxQixNQUFNO0FBSFosQ0FBakI7Ozs7Ozs7OztBQ0pBLElBQU0sUUFBUSxRQUFRLFVBQVIsQ0FBZDs7QUFFQSxTQUFTLElBQVQsQ0FBZSxHQUFmLEVBQW9CO0FBQ2xCLFNBQU8sSUFBSSxPQUFKLENBQVksUUFBWixFQUFzQixFQUF0QixFQUEwQixPQUExQixDQUFrQyxRQUFsQyxFQUE0QyxFQUE1QyxDQUFQO0FBQ0Q7O0FBRUQsU0FBUyxTQUFULENBQW9CLFVBQXBCLEVBQWdDLFlBQWhDLEVBQThDO0FBQzVDLGlCQUFlLGdCQUFnQixPQUFPLFFBQVAsQ0FBZ0IsTUFBL0M7QUFDQSxNQUFJLGdCQUFnQixpQkFBaUIsRUFBckMsRUFBeUM7QUFDdkMsUUFBTSxVQUFVLGFBQWEsS0FBYixDQUFtQixHQUFuQixDQUFoQjtBQUNBLFNBQUssSUFBSSxJQUFJLENBQWIsRUFBZ0IsSUFBSSxRQUFRLE1BQTVCLEVBQW9DLEdBQXBDLEVBQXlDO0FBQ3ZDLFVBQU0sU0FBUyxLQUFLLFFBQVEsQ0FBUixDQUFMLENBQWY7QUFDQTtBQUNBLFVBQUksT0FBTyxTQUFQLENBQWlCLENBQWpCLEVBQW9CLFdBQVcsTUFBWCxHQUFvQixDQUF4QyxNQUFnRCxhQUFhLEdBQWpFLEVBQXVFO0FBQ3JFLGVBQU8sbUJBQW1CLE9BQU8sU0FBUCxDQUFpQixXQUFXLE1BQVgsR0FBb0IsQ0FBckMsQ0FBbkIsQ0FBUDtBQUNEO0FBQ0Y7QUFDRjtBQUNELFNBQU8sSUFBUDtBQUNEOztJQUVLLHFCO0FBQ0osbUNBQTJCO0FBQUEsUUFBZCxPQUFjLHVFQUFKLEVBQUk7O0FBQUE7O0FBQ3pCLFNBQUssU0FBTCxHQUFpQixVQUFVLFFBQVEsY0FBbEIsRUFBa0MsUUFBUSxZQUExQyxDQUFqQjtBQUNBLFNBQUssY0FBTCxHQUFzQixRQUFRLGNBQTlCO0FBQ0Q7Ozs7aUNBRWEsTyxFQUFTO0FBQ3JCLGNBQVEsV0FBUixHQUFzQixhQUF0QjtBQUNBLFVBQUksS0FBSyxTQUFMLElBQWtCLENBQUMsTUFBTSxjQUFOLENBQXFCLFFBQVEsTUFBN0IsQ0FBdkIsRUFBNkQ7QUFDM0QsZ0JBQVEsT0FBUixDQUFnQixLQUFLLGNBQXJCLElBQXVDLEtBQUssU0FBNUM7QUFDRDtBQUNELGFBQU8sT0FBUDtBQUNEOzs7Ozs7QUFHSCxPQUFPLE9BQVAsR0FBaUI7QUFDZix5QkFBdUI7QUFEUixDQUFqQjs7Ozs7Ozs7O0lDcENNLG1CO0FBQ0osaUNBQTJCO0FBQUEsUUFBZCxPQUFjLHVFQUFKLEVBQUk7O0FBQUE7O0FBQ3pCLFNBQUssS0FBTCxHQUFhLFFBQVEsS0FBckI7QUFDQSxTQUFLLE1BQUwsR0FBYyxRQUFRLE1BQVIsSUFBa0IsUUFBaEM7QUFDRDs7OztpQ0FFYSxPLEVBQVM7QUFDckIsY0FBUSxPQUFSLENBQWdCLGVBQWhCLElBQW1DLEtBQUssTUFBTCxHQUFjLEdBQWQsR0FBb0IsS0FBSyxLQUE1RDtBQUNBLGFBQU8sT0FBUDtBQUNEOzs7Ozs7QUFHSCxPQUFPLE9BQVAsR0FBaUI7QUFDZix1QkFBcUI7QUFETixDQUFqQjs7Ozs7Ozs7O0FDWkEsSUFBTSxXQUFXLFFBQVEsWUFBUixDQUFqQjtBQUNBLElBQU0sU0FBUyxRQUFRLFVBQVIsQ0FBZjtBQUNBLElBQU0sU0FBUyxRQUFRLFVBQVIsQ0FBZjtBQUNBLElBQU0sYUFBYSxRQUFRLGNBQVIsQ0FBbkI7QUFDQSxJQUFNLFFBQVEsUUFBUSxTQUFSLENBQWQ7O0FBRUEsU0FBUyxVQUFULENBQXFCLElBQXJCLEVBQTJCLElBQTNCLEVBQWlDO0FBQUE7QUFBQTtBQUFBOztBQUFBO0FBQy9CLHlCQUFnQixJQUFoQiw4SEFBc0I7QUFBQSxVQUFiLEdBQWE7O0FBQ3BCLFVBQUksZ0JBQWdCLFNBQVMsUUFBN0IsRUFBdUM7QUFDckMsZUFBTyxLQUFLLE9BQUwsQ0FBYSxHQUFiLENBQVA7QUFDRCxPQUZELE1BRU87QUFDTCxlQUFPLEtBQUssR0FBTCxDQUFQO0FBQ0Q7QUFDRCxVQUFJLFNBQVMsU0FBYixFQUF3QjtBQUN0QixjQUFNLElBQUksT0FBTyxlQUFYLDJCQUFtRCxLQUFLLFNBQUwsQ0FBZSxJQUFmLENBQW5ELENBQU47QUFDRDtBQUNGO0FBVjhCO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBVy9CLE1BQUksRUFBRSxnQkFBZ0IsU0FBUyxJQUEzQixDQUFKLEVBQXNDO0FBQ3BDLFVBQU0sSUFBSSxPQUFPLGVBQVgsMkJBQW1ELEtBQUssU0FBTCxDQUFlLElBQWYsQ0FBbkQsQ0FBTjtBQUNEO0FBQ0QsU0FBTyxJQUFQO0FBQ0Q7O0lBRUssTTtBQUNKLG9CQUEyQjtBQUFBLFFBQWQsT0FBYyx1RUFBSixFQUFJOztBQUFBOztBQUN6QixRQUFNLG1CQUFtQjtBQUN2QixZQUFNLFFBQVEsSUFBUixJQUFnQixJQURDO0FBRXZCLGVBQVMsUUFBUSxPQUFSLElBQW1CLEVBRkw7QUFHdkIsdUJBQWlCLFFBQVEsZUFIRjtBQUl2Qix3QkFBa0IsUUFBUTtBQUpILEtBQXpCOztBQU9BLFNBQUssUUFBTCxHQUFnQixRQUFRLFFBQVIsSUFBb0IsQ0FBQyxJQUFJLE9BQU8sYUFBWCxFQUFELEVBQTZCLElBQUksT0FBTyxTQUFYLEVBQTdCLEVBQXFELElBQUksT0FBTyxTQUFYLEVBQXJELENBQXBDO0FBQ0EsU0FBSyxVQUFMLEdBQWtCLFFBQVEsVUFBUixJQUFzQixDQUFDLElBQUksV0FBVyxhQUFmLENBQTZCLGdCQUE3QixDQUFELENBQXhDO0FBQ0Q7Ozs7MkJBRU8sUSxFQUFVLEksRUFBbUI7QUFBQSxVQUFiLE1BQWEsdUVBQUosRUFBSTs7QUFDbkMsVUFBTSxPQUFPLFdBQVcsUUFBWCxFQUFxQixJQUFyQixDQUFiO0FBQ0EsVUFBTSxZQUFZLE1BQU0sa0JBQU4sQ0FBeUIsS0FBSyxVQUE5QixFQUEwQyxLQUFLLEdBQS9DLENBQWxCO0FBQ0EsYUFBTyxVQUFVLE1BQVYsQ0FBaUIsSUFBakIsRUFBdUIsS0FBSyxRQUE1QixFQUFzQyxNQUF0QyxDQUFQO0FBQ0Q7Ozt3QkFFSSxHLEVBQUs7QUFDUixVQUFNLE9BQU8sSUFBSSxTQUFTLElBQWIsQ0FBa0IsR0FBbEIsRUFBdUIsS0FBdkIsQ0FBYjtBQUNBLFVBQU0sWUFBWSxNQUFNLGtCQUFOLENBQXlCLEtBQUssVUFBOUIsRUFBMEMsR0FBMUMsQ0FBbEI7QUFDQSxhQUFPLFVBQVUsTUFBVixDQUFpQixJQUFqQixFQUF1QixLQUFLLFFBQTVCLENBQVA7QUFDRDs7Ozs7O0FBR0gsT0FBTyxPQUFQLEdBQWlCO0FBQ2YsVUFBUTtBQURPLENBQWpCOzs7Ozs7Ozs7OztBQ2pEQSxJQUFNLFdBQVcsUUFBUSxhQUFSLENBQWpCO0FBQ0EsSUFBTSxNQUFNLFFBQVEsV0FBUixDQUFaOztBQUVBLFNBQVMsV0FBVCxDQUFzQixHQUF0QixFQUEyQjtBQUN6QixNQUFJLElBQUksS0FBSixDQUFVLGdCQUFWLENBQUosRUFBaUM7QUFDL0IsV0FBTyxJQUFJLFNBQUosQ0FBYyxDQUFkLENBQVA7QUFDRDtBQUNELFNBQU8sR0FBUDtBQUNEOztBQUVELFNBQVMsU0FBVCxDQUFvQixHQUFwQixFQUF5QixHQUF6QixFQUE4QjtBQUM1QixNQUFNLFFBQVEsSUFBSSxHQUFKLENBQWQ7QUFDQSxNQUFJLE9BQVEsS0FBUixLQUFtQixRQUF2QixFQUFpQztBQUMvQixXQUFPLEtBQVA7QUFDRDtBQUNELFNBQU8sRUFBUDtBQUNEOztBQUVELFNBQVMsVUFBVCxDQUFxQixHQUFyQixFQUEwQixHQUExQixFQUErQjtBQUM3QixNQUFNLFFBQVEsSUFBSSxHQUFKLENBQWQ7QUFDQSxNQUFJLE9BQVEsS0FBUixLQUFtQixTQUF2QixFQUFrQztBQUNoQyxXQUFPLEtBQVA7QUFDRDtBQUNELFNBQU8sS0FBUDtBQUNEOztBQUVELFNBQVMsU0FBVCxDQUFvQixHQUFwQixFQUF5QixHQUF6QixFQUE4QjtBQUM1QixNQUFNLFFBQVEsSUFBSSxHQUFKLENBQWQ7QUFDQSxNQUFJLFFBQVEsS0FBUix5Q0FBUSxLQUFSLE9BQW1CLFFBQXZCLEVBQWlDO0FBQy9CLFdBQU8sS0FBUDtBQUNEO0FBQ0QsU0FBTyxFQUFQO0FBQ0Q7O0FBRUQsU0FBUyxRQUFULENBQW1CLEdBQW5CLEVBQXdCLEdBQXhCLEVBQTZCO0FBQzNCLE1BQU0sUUFBUSxJQUFJLEdBQUosQ0FBZDtBQUNBLE1BQUksaUJBQWlCLEtBQXJCLEVBQTRCO0FBQzFCLFdBQU8sS0FBUDtBQUNEO0FBQ0QsU0FBTyxFQUFQO0FBQ0Q7O0FBRUQsU0FBUyxVQUFULENBQXFCLElBQXJCLEVBQTJCLE9BQTNCLEVBQW9DO0FBQ2xDLE1BQU0sV0FBVyxDQUFDLE9BQUQsRUFBVSxPQUFWLENBQWpCO0FBQ0EsTUFBSSxVQUFVLEVBQWQ7QUFDQSxPQUFLLElBQUksUUFBVCxJQUFxQixJQUFyQixFQUEyQjtBQUN6QixRQUFJLEtBQUssY0FBTCxDQUFvQixRQUFwQixLQUFpQyxDQUFDLFNBQVMsUUFBVCxDQUFrQixRQUFsQixDQUF0QyxFQUFtRTtBQUNqRSxVQUFNLE1BQU0sWUFBWSxRQUFaLENBQVo7QUFDQSxVQUFNLFFBQVEsZ0JBQWdCLEtBQUssUUFBTCxDQUFoQixFQUFnQyxPQUFoQyxDQUFkO0FBQ0EsY0FBUSxHQUFSLElBQWUsS0FBZjtBQUNEO0FBQ0Y7QUFDRCxTQUFPLE9BQVA7QUFDRDs7QUFFRCxTQUFTLGVBQVQsQ0FBMEIsSUFBMUIsRUFBZ0MsT0FBaEMsRUFBeUM7QUFDdkMsTUFBTSxXQUFXLGdCQUFnQixNQUFoQixJQUEwQixFQUFFLGdCQUFnQixLQUFsQixDQUEzQzs7QUFFQSxNQUFJLFlBQVksS0FBSyxLQUFMLEtBQWUsVUFBL0IsRUFBMkM7QUFDekM7QUFDQSxRQUFNLE9BQU8sVUFBVSxJQUFWLEVBQWdCLE9BQWhCLENBQWI7QUFDQSxRQUFNLGNBQWMsVUFBVSxJQUFWLEVBQWdCLEtBQWhCLENBQXBCO0FBQ0EsUUFBTSxNQUFNLGNBQWMsSUFBSSxXQUFKLEVBQWlCLE9BQWpCLEVBQTBCLFFBQTFCLEVBQWQsR0FBcUQsRUFBakU7QUFDQSxRQUFNLFFBQVEsVUFBVSxJQUFWLEVBQWdCLE9BQWhCLENBQWQ7QUFDQSxRQUFNLGNBQWMsVUFBVSxJQUFWLEVBQWdCLGFBQWhCLENBQXBCO0FBQ0EsUUFBTSxVQUFVLFdBQVcsSUFBWCxFQUFpQixHQUFqQixDQUFoQjtBQUNBLFdBQU8sSUFBSSxTQUFTLFFBQWIsQ0FBc0IsR0FBdEIsRUFBMkIsS0FBM0IsRUFBa0MsV0FBbEMsRUFBK0MsT0FBL0MsQ0FBUDtBQUNELEdBVEQsTUFTTyxJQUFJLFlBQVksS0FBSyxLQUFMLEtBQWUsTUFBL0IsRUFBdUM7QUFDNUM7QUFDQSxRQUFNLGVBQWMsVUFBVSxJQUFWLEVBQWdCLEtBQWhCLENBQXBCO0FBQ0EsUUFBTSxPQUFNLGVBQWMsSUFBSSxZQUFKLEVBQWlCLE9BQWpCLEVBQTBCLFFBQTFCLEVBQWQsR0FBcUQsRUFBakU7QUFDQSxRQUFNLFNBQVMsVUFBVSxJQUFWLEVBQWdCLFFBQWhCLEtBQTZCLEtBQTVDO0FBQ0EsUUFBTSxTQUFRLFVBQVUsSUFBVixFQUFnQixPQUFoQixDQUFkO0FBQ0EsUUFBTSxlQUFjLFVBQVUsSUFBVixFQUFnQixhQUFoQixDQUFwQjtBQUNBLFFBQU0sYUFBYSxTQUFTLElBQVQsRUFBZSxRQUFmLENBQW5CO0FBQ0EsUUFBSSxTQUFTLEVBQWI7QUFDQSxTQUFLLElBQUksTUFBTSxDQUFWLEVBQWEsTUFBTSxXQUFXLE1BQW5DLEVBQTJDLE1BQU0sR0FBakQsRUFBc0QsS0FBdEQsRUFBNkQ7QUFDM0QsVUFBSSxRQUFRLFdBQVcsR0FBWCxDQUFaO0FBQ0EsVUFBSSxPQUFPLFVBQVUsS0FBVixFQUFpQixNQUFqQixDQUFYO0FBQ0EsVUFBSSxXQUFXLFdBQVcsS0FBWCxFQUFrQixVQUFsQixDQUFmO0FBQ0EsVUFBSSxXQUFXLFVBQVUsS0FBVixFQUFpQixVQUFqQixDQUFmO0FBQ0EsVUFBSSxtQkFBbUIsVUFBVSxLQUFWLEVBQWlCLGtCQUFqQixDQUF2QjtBQUNBLFVBQUksUUFBUSxJQUFJLFNBQVMsS0FBYixDQUFtQixJQUFuQixFQUF5QixRQUF6QixFQUFtQyxRQUFuQyxFQUE2QyxnQkFBN0MsQ0FBWjtBQUNBLGFBQU8sSUFBUCxDQUFZLEtBQVo7QUFDRDtBQUNELFdBQU8sSUFBSSxTQUFTLElBQWIsQ0FBa0IsSUFBbEIsRUFBdUIsTUFBdkIsRUFBK0Isa0JBQS9CLEVBQW1ELE1BQW5ELEVBQTJELE1BQTNELEVBQWtFLFlBQWxFLENBQVA7QUFDRCxHQW5CTSxNQW1CQSxJQUFJLFFBQUosRUFBYztBQUNuQjtBQUNBLFFBQUksV0FBVSxFQUFkO0FBQ0EsU0FBSyxJQUFJLEdBQVQsSUFBZ0IsSUFBaEIsRUFBc0I7QUFDcEIsVUFBSSxLQUFLLGNBQUwsQ0FBb0IsR0FBcEIsQ0FBSixFQUE4QjtBQUM1QixpQkFBUSxHQUFSLElBQWUsZ0JBQWdCLEtBQUssR0FBTCxDQUFoQixFQUEyQixPQUEzQixDQUFmO0FBQ0Q7QUFDRjtBQUNELFdBQU8sUUFBUDtBQUNELEdBVE0sTUFTQSxJQUFJLGdCQUFnQixLQUFwQixFQUEyQjtBQUNoQztBQUNBLFFBQUksWUFBVSxFQUFkO0FBQ0EsU0FBSyxJQUFJLE9BQU0sQ0FBVixFQUFhLE9BQU0sS0FBSyxNQUE3QixFQUFxQyxPQUFNLElBQTNDLEVBQWdELE1BQWhELEVBQXVEO0FBQ3JELGdCQUFRLElBQVIsQ0FBYSxnQkFBZ0IsS0FBSyxJQUFMLENBQWhCLEVBQTJCLE9BQTNCLENBQWI7QUFDRDtBQUNELFdBQU8sU0FBUDtBQUNEO0FBQ0Q7QUFDQSxTQUFPLElBQVA7QUFDRDs7SUFFSyxhO0FBQ0osMkJBQWU7QUFBQTs7QUFDYixTQUFLLFNBQUwsR0FBaUIsMEJBQWpCO0FBQ0Q7Ozs7MkJBRU8sSSxFQUFvQjtBQUFBLFVBQWQsT0FBYyx1RUFBSixFQUFJOztBQUMxQixVQUFJLE9BQU8sSUFBWDtBQUNBLFVBQUksUUFBUSxTQUFSLEtBQXNCLFNBQXRCLElBQW1DLENBQUMsUUFBUSxTQUFoRCxFQUEyRDtBQUN6RCxlQUFPLEtBQUssS0FBTCxDQUFXLElBQVgsQ0FBUDtBQUNEO0FBQ0QsYUFBTyxnQkFBZ0IsSUFBaEIsRUFBc0IsUUFBUSxHQUE5QixDQUFQO0FBQ0Q7Ozs7OztBQUdILE9BQU8sT0FBUCxHQUFpQjtBQUNmLGlCQUFlO0FBREEsQ0FBakI7Ozs7O0FDekhBLElBQU0sV0FBVyxRQUFRLFlBQVIsQ0FBakI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7O0FBRUEsT0FBTyxPQUFQLEdBQWlCO0FBQ2YsaUJBQWUsU0FBUyxhQURUO0FBRWYsYUFBVyxLQUFLLFNBRkQ7QUFHZixhQUFXLEtBQUs7QUFIRCxDQUFqQjs7Ozs7Ozs7O0lDSk0sUztBQUNKLHVCQUFlO0FBQUE7O0FBQ2IsU0FBSyxTQUFMLEdBQWlCLGtCQUFqQjtBQUNEOzs7OzJCQUVPLEksRUFBb0I7QUFBQSxVQUFkLE9BQWMsdUVBQUosRUFBSTs7QUFDMUIsYUFBTyxLQUFLLEtBQUwsQ0FBVyxJQUFYLENBQVA7QUFDRDs7Ozs7O0FBR0gsT0FBTyxPQUFQLEdBQWlCO0FBQ2YsYUFBVztBQURJLENBQWpCOzs7Ozs7Ozs7SUNWTSxTO0FBQ0osdUJBQWU7QUFBQTs7QUFDYixTQUFLLFNBQUwsR0FBaUIsUUFBakI7QUFDRDs7OzsyQkFFTyxJLEVBQW9CO0FBQUEsVUFBZCxPQUFjLHVFQUFKLEVBQUk7O0FBQzFCLGFBQU8sSUFBUDtBQUNEOzs7Ozs7QUFHSCxPQUFPLE9BQVAsR0FBaUI7QUFDZixhQUFXO0FBREksQ0FBakI7Ozs7Ozs7SUNWTSxRLEdBQ0osb0JBQW1FO0FBQUEsTUFBdEQsR0FBc0QsdUVBQWhELEVBQWdEO0FBQUEsTUFBNUMsS0FBNEMsdUVBQXBDLEVBQW9DO0FBQUEsTUFBaEMsV0FBZ0MsdUVBQWxCLEVBQWtCO0FBQUEsTUFBZCxPQUFjLHVFQUFKLEVBQUk7O0FBQUE7O0FBQ2pFLE9BQUssR0FBTCxHQUFXLEdBQVg7QUFDQSxPQUFLLEtBQUwsR0FBYSxLQUFiO0FBQ0EsT0FBSyxXQUFMLEdBQW1CLFdBQW5CO0FBQ0EsT0FBSyxPQUFMLEdBQWUsT0FBZjtBQUNELEM7O0lBR0csSSxHQUNKLGNBQWEsR0FBYixFQUFrQixNQUFsQixFQUFvRztBQUFBLE1BQTFFLFFBQTBFLHVFQUEvRCxrQkFBK0Q7QUFBQSxNQUEzQyxNQUEyQyx1RUFBbEMsRUFBa0M7QUFBQSxNQUE5QixLQUE4Qix1RUFBdEIsRUFBc0I7QUFBQSxNQUFsQixXQUFrQix1RUFBSixFQUFJOztBQUFBOztBQUNsRyxNQUFJLFFBQVEsU0FBWixFQUF1QjtBQUNyQixVQUFNLElBQUksS0FBSixDQUFVLDBCQUFWLENBQU47QUFDRDs7QUFFRCxNQUFJLFdBQVcsU0FBZixFQUEwQjtBQUN4QixVQUFNLElBQUksS0FBSixDQUFVLDZCQUFWLENBQU47QUFDRDs7QUFFRCxPQUFLLEdBQUwsR0FBVyxHQUFYO0FBQ0EsT0FBSyxNQUFMLEdBQWMsTUFBZDtBQUNBLE9BQUssUUFBTCxHQUFnQixRQUFoQjtBQUNBLE9BQUssTUFBTCxHQUFjLE1BQWQ7QUFDQSxPQUFLLEtBQUwsR0FBYSxLQUFiO0FBQ0EsT0FBSyxXQUFMLEdBQW1CLFdBQW5CO0FBQ0QsQzs7SUFHRyxLLEdBQ0osZUFBYSxJQUFiLEVBQXNFO0FBQUEsTUFBbkQsUUFBbUQsdUVBQXhDLEtBQXdDO0FBQUEsTUFBakMsUUFBaUMsdUVBQXRCLEVBQXNCO0FBQUEsTUFBbEIsV0FBa0IsdUVBQUosRUFBSTs7QUFBQTs7QUFDcEUsTUFBSSxTQUFTLFNBQWIsRUFBd0I7QUFDdEIsVUFBTSxJQUFJLEtBQUosQ0FBVSwyQkFBVixDQUFOO0FBQ0Q7O0FBRUQsT0FBSyxJQUFMLEdBQVksSUFBWjtBQUNBLE9BQUssUUFBTCxHQUFnQixRQUFoQjtBQUNBLE9BQUssUUFBTCxHQUFnQixRQUFoQjtBQUNBLE9BQUssV0FBTCxHQUFtQixXQUFuQjtBQUNELEM7O0FBR0gsT0FBTyxPQUFQLEdBQWlCO0FBQ2YsWUFBVSxRQURLO0FBRWYsUUFBTSxJQUZTO0FBR2YsU0FBTztBQUhRLENBQWpCOzs7Ozs7Ozs7OztJQ3pDTSxjOzs7QUFDSiwwQkFBYSxPQUFiLEVBQXNCO0FBQUE7O0FBQUEsZ0lBQ2QsT0FEYzs7QUFFcEIsVUFBSyxPQUFMLEdBQWUsT0FBZjtBQUNBLFVBQUssSUFBTCxHQUFZLGdCQUFaO0FBSG9CO0FBSXJCOzs7RUFMMEIsSzs7SUFRdkIsZTs7O0FBQ0osMkJBQWEsT0FBYixFQUFzQjtBQUFBOztBQUFBLG1JQUNkLE9BRGM7O0FBRXBCLFdBQUssT0FBTCxHQUFlLE9BQWY7QUFDQSxXQUFLLElBQUwsR0FBWSxpQkFBWjtBQUhvQjtBQUlyQjs7O0VBTDJCLEs7O0lBUXhCLFk7OztBQUNKLHdCQUFhLE9BQWIsRUFBc0IsT0FBdEIsRUFBK0I7QUFBQTs7QUFBQSw2SEFDdkIsT0FEdUI7O0FBRTdCLFdBQUssT0FBTCxHQUFlLE9BQWY7QUFDQSxXQUFLLE9BQUwsR0FBZSxPQUFmO0FBQ0EsV0FBSyxJQUFMLEdBQVksY0FBWjtBQUo2QjtBQUs5Qjs7O0VBTndCLEs7O0FBUzNCLE9BQU8sT0FBUCxHQUFpQjtBQUNmLGtCQUFnQixjQUREO0FBRWYsbUJBQWlCLGVBRkY7QUFHZixnQkFBYztBQUhDLENBQWpCOzs7OztBQ3pCQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLFNBQVMsUUFBUSxVQUFSLENBQWY7QUFDQSxJQUFNLFNBQVMsUUFBUSxVQUFSLENBQWY7QUFDQSxJQUFNLFdBQVcsUUFBUSxZQUFSLENBQWpCO0FBQ0EsSUFBTSxTQUFTLFFBQVEsVUFBUixDQUFmO0FBQ0EsSUFBTSxhQUFhLFFBQVEsY0FBUixDQUFuQjtBQUNBLElBQU0sUUFBUSxRQUFRLFNBQVIsQ0FBZDs7QUFFQSxJQUFNLFVBQVU7QUFDZCxVQUFRLE9BQU8sTUFERDtBQUVkLFlBQVUsU0FBUyxRQUZMO0FBR2QsUUFBTSxTQUFTLElBSEQ7QUFJZCxRQUFNLElBSlE7QUFLZCxVQUFRLE1BTE07QUFNZCxVQUFRLE1BTk07QUFPZCxjQUFZLFVBUEU7QUFRZCxTQUFPO0FBUk8sQ0FBaEI7O0FBV0EsT0FBTyxPQUFQLEdBQWlCLE9BQWpCOzs7Ozs7Ozs7QUNuQkEsSUFBTSxRQUFRLFFBQVEsa0JBQVIsQ0FBZDtBQUNBLElBQU0sU0FBUyxRQUFRLFdBQVIsQ0FBZjtBQUNBLElBQU0sUUFBUSxRQUFRLFVBQVIsQ0FBZDtBQUNBLElBQU0sTUFBTSxRQUFRLFdBQVIsQ0FBWjtBQUNBLElBQU0sY0FBYyxRQUFRLGNBQVIsQ0FBcEI7O0FBRUEsSUFBTSxnQkFBZ0IsU0FBaEIsYUFBZ0IsQ0FBQyxRQUFELEVBQVcsUUFBWCxFQUFxQixnQkFBckIsRUFBMEM7QUFDOUQsU0FBTyxTQUFTLElBQVQsR0FBZ0IsSUFBaEIsQ0FBcUIsZ0JBQVE7QUFDbEMsUUFBSSxnQkFBSixFQUFzQjtBQUNwQix1QkFBaUIsUUFBakIsRUFBMkIsSUFBM0I7QUFDRDtBQUNELFFBQU0sY0FBYyxTQUFTLE9BQVQsQ0FBaUIsR0FBakIsQ0FBcUIsY0FBckIsQ0FBcEI7QUFDQSxRQUFNLFVBQVUsTUFBTSxnQkFBTixDQUF1QixRQUF2QixFQUFpQyxXQUFqQyxDQUFoQjtBQUNBLFFBQU0sVUFBVSxFQUFDLEtBQUssU0FBUyxHQUFmLEVBQWhCO0FBQ0EsV0FBTyxRQUFRLE1BQVIsQ0FBZSxJQUFmLEVBQXFCLE9BQXJCLENBQVA7QUFDRCxHQVJNLENBQVA7QUFTRCxDQVZEOztJQVlNLGE7QUFDSiwyQkFBMkI7QUFBQSxRQUFkLE9BQWMsdUVBQUosRUFBSTs7QUFBQTs7QUFDekIsU0FBSyxPQUFMLEdBQWUsQ0FBQyxNQUFELEVBQVMsT0FBVCxDQUFmO0FBQ0EsU0FBSyxJQUFMLEdBQVksUUFBUSxJQUFSLElBQWdCLElBQTVCO0FBQ0EsU0FBSyxPQUFMLEdBQWUsUUFBUSxPQUFSLElBQW1CLEVBQWxDO0FBQ0EsU0FBSyxLQUFMLEdBQWEsUUFBUSxLQUFSLElBQWlCLEtBQTlCO0FBQ0EsU0FBSyxRQUFMLEdBQWdCLFFBQVEsUUFBUixJQUFvQixPQUFPLFFBQTNDO0FBQ0EsU0FBSyxlQUFMLEdBQXVCLFFBQVEsZUFBL0I7QUFDQSxTQUFLLGdCQUFMLEdBQXdCLFFBQVEsZ0JBQWhDO0FBQ0Q7Ozs7aUNBRWEsSSxFQUFNLFEsRUFBdUI7QUFBQSxVQUFiLE1BQWEsdUVBQUosRUFBSTs7QUFDekMsVUFBTSxTQUFTLEtBQUssTUFBcEI7QUFDQSxVQUFNLFNBQVMsS0FBSyxNQUFMLENBQVksV0FBWixFQUFmO0FBQ0EsVUFBSSxjQUFjLEVBQWxCO0FBQ0EsVUFBSSxhQUFhLEVBQWpCO0FBQ0EsVUFBSSxhQUFhLEVBQWpCO0FBQ0EsVUFBSSxhQUFhLEVBQWpCO0FBQ0EsVUFBSSxVQUFVLEtBQWQ7O0FBRUEsV0FBSyxJQUFJLE1BQU0sQ0FBVixFQUFhLE1BQU0sT0FBTyxNQUEvQixFQUF1QyxNQUFNLEdBQTdDLEVBQWtELEtBQWxELEVBQXlEO0FBQ3ZELFlBQU0sUUFBUSxPQUFPLEdBQVAsQ0FBZDs7QUFFQTtBQUNBLFlBQUksQ0FBQyxPQUFPLGNBQVAsQ0FBc0IsTUFBTSxJQUE1QixDQUFMLEVBQXdDO0FBQ3RDLGNBQUksTUFBTSxRQUFWLEVBQW9CO0FBQ2xCLGtCQUFNLElBQUksT0FBTyxjQUFYLCtCQUFzRCxNQUFNLElBQTVELE9BQU47QUFDRCxXQUZELE1BRU87QUFDTDtBQUNEO0FBQ0Y7O0FBRUQsbUJBQVcsSUFBWCxDQUFnQixNQUFNLElBQXRCO0FBQ0EsWUFBSSxNQUFNLFFBQU4sS0FBbUIsT0FBdkIsRUFBZ0M7QUFDOUIsc0JBQVksTUFBTSxJQUFsQixJQUEwQixPQUFPLE1BQU0sSUFBYixDQUExQjtBQUNELFNBRkQsTUFFTyxJQUFJLE1BQU0sUUFBTixLQUFtQixNQUF2QixFQUErQjtBQUNwQyxxQkFBVyxNQUFNLElBQWpCLElBQXlCLE9BQU8sTUFBTSxJQUFiLENBQXpCO0FBQ0QsU0FGTSxNQUVBLElBQUksTUFBTSxRQUFOLEtBQW1CLE1BQXZCLEVBQStCO0FBQ3BDLHFCQUFXLE1BQU0sSUFBakIsSUFBeUIsT0FBTyxNQUFNLElBQWIsQ0FBekI7QUFDQSxvQkFBVSxJQUFWO0FBQ0QsU0FITSxNQUdBLElBQUksTUFBTSxRQUFOLEtBQW1CLE1BQXZCLEVBQStCO0FBQ3BDLHVCQUFhLE9BQU8sTUFBTSxJQUFiLENBQWI7QUFDQSxvQkFBVSxJQUFWO0FBQ0Q7QUFDRjs7QUFFRDtBQUNBLFdBQUssSUFBSSxRQUFULElBQXFCLE1BQXJCLEVBQTZCO0FBQzNCLFlBQUksT0FBTyxjQUFQLENBQXNCLFFBQXRCLEtBQW1DLENBQUMsV0FBVyxRQUFYLENBQW9CLFFBQXBCLENBQXhDLEVBQXVFO0FBQ3JFLGdCQUFNLElBQUksT0FBTyxjQUFYLDBCQUFpRCxRQUFqRCxPQUFOO0FBQ0Q7QUFDRjs7QUFFRCxVQUFJLGlCQUFpQixFQUFDLFFBQVEsTUFBVCxFQUFpQixTQUFTLEVBQTFCLEVBQXJCOztBQUVBLGFBQU8sTUFBUCxDQUFjLGVBQWUsT0FBN0IsRUFBc0MsS0FBSyxPQUEzQzs7QUFFQSxVQUFJLE9BQUosRUFBYTtBQUNYLFlBQUksS0FBSyxRQUFMLEtBQWtCLGtCQUF0QixFQUEwQztBQUN4Qyx5QkFBZSxJQUFmLEdBQXNCLEtBQUssU0FBTCxDQUFlLFVBQWYsQ0FBdEI7QUFDQSx5QkFBZSxPQUFmLENBQXVCLGNBQXZCLElBQXlDLGtCQUF6QztBQUNELFNBSEQsTUFHTyxJQUFJLEtBQUssUUFBTCxLQUFrQixxQkFBdEIsRUFBNkM7QUFDbEQsY0FBSSxPQUFPLElBQUksS0FBSyxRQUFULEVBQVg7O0FBRUEsZUFBSyxJQUFJLFFBQVQsSUFBcUIsVUFBckIsRUFBaUM7QUFDL0IsaUJBQUssTUFBTCxDQUFZLFFBQVosRUFBc0IsV0FBVyxRQUFYLENBQXRCO0FBQ0Q7QUFDRCx5QkFBZSxJQUFmLEdBQXNCLElBQXRCO0FBQ0QsU0FQTSxNQU9BLElBQUksS0FBSyxRQUFMLEtBQWtCLG1DQUF0QixFQUEyRDtBQUNoRSxjQUFJLFdBQVcsRUFBZjtBQUNBLGVBQUssSUFBSSxTQUFULElBQXFCLFVBQXJCLEVBQWlDO0FBQy9CLGdCQUFNLGFBQWEsbUJBQW1CLFNBQW5CLENBQW5CO0FBQ0EsZ0JBQU0sZUFBZSxtQkFBbUIsV0FBVyxTQUFYLENBQW5CLENBQXJCO0FBQ0EscUJBQVMsSUFBVCxDQUFjLGFBQWEsR0FBYixHQUFtQixZQUFqQztBQUNEO0FBQ0QscUJBQVcsU0FBUyxJQUFULENBQWMsR0FBZCxDQUFYOztBQUVBLHlCQUFlLElBQWYsR0FBc0IsUUFBdEI7QUFDQSx5QkFBZSxPQUFmLENBQXVCLGNBQXZCLElBQXlDLG1DQUF6QztBQUNEO0FBQ0Y7O0FBRUQsVUFBSSxLQUFLLElBQVQsRUFBZTtBQUNiLHlCQUFpQixLQUFLLElBQUwsQ0FBVSxZQUFWLENBQXVCLGNBQXZCLENBQWpCO0FBQ0Q7O0FBRUQsVUFBSSxZQUFZLFlBQVksS0FBWixDQUFrQixLQUFLLEdBQXZCLENBQWhCO0FBQ0Esa0JBQVksVUFBVSxNQUFWLENBQWlCLFVBQWpCLENBQVo7QUFDQSxrQkFBWSxJQUFJLEdBQUosQ0FBUSxTQUFSLENBQVo7QUFDQSxnQkFBVSxHQUFWLENBQWMsT0FBZCxFQUF1QixXQUF2Qjs7QUFFQSxhQUFPO0FBQ0wsYUFBSyxVQUFVLFFBQVYsRUFEQTtBQUVMLGlCQUFTO0FBRkosT0FBUDtBQUlEOzs7MkJBRU8sSSxFQUFNLFEsRUFBdUI7QUFBQSxVQUFiLE1BQWEsdUVBQUosRUFBSTs7QUFDbkMsVUFBTSxtQkFBbUIsS0FBSyxnQkFBOUI7QUFDQSxVQUFNLFVBQVUsS0FBSyxZQUFMLENBQWtCLElBQWxCLEVBQXdCLFFBQXhCLEVBQWtDLE1BQWxDLENBQWhCOztBQUVBLFVBQUksS0FBSyxlQUFULEVBQTBCO0FBQ3hCLGFBQUssZUFBTCxDQUFxQixPQUFyQjtBQUNEOztBQUVELGFBQU8sS0FBSyxLQUFMLENBQVcsUUFBUSxHQUFuQixFQUF3QixRQUFRLE9BQWhDLEVBQ0osSUFESSxDQUNDLFVBQVUsUUFBVixFQUFvQjtBQUN4QixZQUFJLFNBQVMsTUFBVCxLQUFvQixHQUF4QixFQUE2QjtBQUMzQjtBQUNEO0FBQ0QsZUFBTyxjQUFjLFFBQWQsRUFBd0IsUUFBeEIsRUFBa0MsZ0JBQWxDLEVBQ0osSUFESSxDQUNDLFVBQVUsSUFBVixFQUFnQjtBQUNwQixjQUFJLFNBQVMsRUFBYixFQUFpQjtBQUNmLG1CQUFPLElBQVA7QUFDRCxXQUZELE1BRU87QUFDTCxnQkFBTSxRQUFRLFNBQVMsTUFBVCxHQUFrQixHQUFsQixHQUF3QixTQUFTLFVBQS9DO0FBQ0EsZ0JBQU0sUUFBUSxJQUFJLE9BQU8sWUFBWCxDQUF3QixLQUF4QixFQUErQixJQUEvQixDQUFkO0FBQ0EsbUJBQU8sUUFBUSxNQUFSLENBQWUsS0FBZixDQUFQO0FBQ0Q7QUFDRixTQVRJLENBQVA7QUFVRCxPQWZJLENBQVA7QUFnQkQ7Ozs7OztBQUdILE9BQU8sT0FBUCxHQUFpQjtBQUNmLGlCQUFlO0FBREEsQ0FBakI7Ozs7O0FDOUlBLElBQU0sT0FBTyxRQUFRLFFBQVIsQ0FBYjs7QUFFQSxPQUFPLE9BQVAsR0FBaUI7QUFDZixpQkFBZSxLQUFLO0FBREwsQ0FBakI7Ozs7O0FDRkEsSUFBTSxNQUFNLFFBQVEsV0FBUixDQUFaOztBQUVBLElBQU0scUJBQXFCLFNBQXJCLGtCQUFxQixDQUFVLFVBQVYsRUFBc0IsR0FBdEIsRUFBMkI7QUFDcEQsTUFBTSxZQUFZLElBQUksR0FBSixDQUFRLEdBQVIsQ0FBbEI7QUFDQSxNQUFNLFNBQVMsVUFBVSxRQUFWLENBQW1CLE9BQW5CLENBQTJCLEdBQTNCLEVBQWdDLEVBQWhDLENBQWY7O0FBRm9EO0FBQUE7QUFBQTs7QUFBQTtBQUlwRCx5QkFBc0IsVUFBdEIsOEhBQWtDO0FBQUEsVUFBekIsU0FBeUI7O0FBQ2hDLFVBQUksVUFBVSxPQUFWLENBQWtCLFFBQWxCLENBQTJCLE1BQTNCLENBQUosRUFBd0M7QUFDdEMsZUFBTyxTQUFQO0FBQ0Q7QUFDRjtBQVJtRDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQVVwRCxRQUFNLHNDQUFvQyxHQUFwQyxDQUFOO0FBQ0QsQ0FYRDs7QUFhQSxJQUFNLG1CQUFtQixTQUFuQixnQkFBbUIsQ0FBVSxRQUFWLEVBQW9CLFdBQXBCLEVBQWlDO0FBQ3hELE1BQUksZ0JBQWdCLFNBQWhCLElBQTZCLGdCQUFnQixJQUFqRCxFQUF1RDtBQUNyRCxXQUFPLFNBQVMsQ0FBVCxDQUFQO0FBQ0Q7O0FBRUQsTUFBTSxXQUFXLFlBQVksV0FBWixHQUEwQixLQUExQixDQUFnQyxHQUFoQyxFQUFxQyxDQUFyQyxFQUF3QyxJQUF4QyxFQUFqQjtBQUNBLE1BQU0sV0FBVyxTQUFTLEtBQVQsQ0FBZSxHQUFmLEVBQW9CLENBQXBCLElBQXlCLElBQTFDO0FBQ0EsTUFBTSxlQUFlLEtBQXJCO0FBQ0EsTUFBTSxrQkFBa0IsQ0FBQyxRQUFELEVBQVcsUUFBWCxFQUFxQixZQUFyQixDQUF4Qjs7QUFSd0Q7QUFBQTtBQUFBOztBQUFBO0FBVXhELDBCQUFvQixRQUFwQixtSUFBOEI7QUFBQSxVQUFyQixPQUFxQjs7QUFDNUIsVUFBSSxnQkFBZ0IsUUFBaEIsQ0FBeUIsUUFBUSxTQUFqQyxDQUFKLEVBQWlEO0FBQy9DLGVBQU8sT0FBUDtBQUNEO0FBQ0Y7QUFkdUQ7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFnQnhELFFBQU0scURBQW1ELFdBQW5ELENBQU47QUFDRCxDQWpCRDs7QUFtQkEsSUFBTSxpQkFBaUIsU0FBakIsY0FBaUIsQ0FBVSxNQUFWLEVBQWtCO0FBQ3ZDO0FBQ0EsU0FBUSw4QkFBNkIsSUFBN0IsQ0FBa0MsTUFBbEM7QUFBUjtBQUNELENBSEQ7O0FBS0EsT0FBTyxPQUFQLEdBQWlCO0FBQ2Ysc0JBQW9CLGtCQURMO0FBRWYsb0JBQWtCLGdCQUZIO0FBR2Ysa0JBQWdCO0FBSEQsQ0FBakI7OztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUNyV0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDckRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiY2xhc3MgQmFzaWNBdXRoZW50aWNhdGlvbiB7XG4gIGNvbnN0cnVjdG9yIChvcHRpb25zID0ge30pIHtcbiAgICBjb25zdCB1c2VybmFtZSA9IG9wdGlvbnMudXNlcm5hbWVcbiAgICBjb25zdCBwYXNzd29yZCA9IG9wdGlvbnMucGFzc3dvcmRcbiAgICBjb25zdCBoYXNoID0gd2luZG93LmJ0b2EodXNlcm5hbWUgKyAnOicgKyBwYXNzd29yZClcbiAgICB0aGlzLmF1dGggPSAnQmFzaWMgJyArIGhhc2hcbiAgfVxuXG4gIGF1dGhlbnRpY2F0ZSAob3B0aW9ucykge1xuICAgIG9wdGlvbnMuaGVhZGVyc1snQXV0aG9yaXphdGlvbiddID0gdGhpcy5hdXRoXG4gICAgcmV0dXJuIG9wdGlvbnNcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgQmFzaWNBdXRoZW50aWNhdGlvbjogQmFzaWNBdXRoZW50aWNhdGlvblxufVxuIiwiY29uc3QgYmFzaWMgPSByZXF1aXJlKCcuL2Jhc2ljJylcbmNvbnN0IHNlc3Npb24gPSByZXF1aXJlKCcuL3Nlc3Npb24nKVxuY29uc3QgdG9rZW4gPSByZXF1aXJlKCcuL3Rva2VuJylcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIEJhc2ljQXV0aGVudGljYXRpb246IGJhc2ljLkJhc2ljQXV0aGVudGljYXRpb24sXG4gIFNlc3Npb25BdXRoZW50aWNhdGlvbjogc2Vzc2lvbi5TZXNzaW9uQXV0aGVudGljYXRpb24sXG4gIFRva2VuQXV0aGVudGljYXRpb246IHRva2VuLlRva2VuQXV0aGVudGljYXRpb25cbn1cbiIsImNvbnN0IHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKVxuXG5mdW5jdGlvbiB0cmltIChzdHIpIHtcbiAgcmV0dXJuIHN0ci5yZXBsYWNlKC9eXFxzXFxzKi8sICcnKS5yZXBsYWNlKC9cXHNcXHMqJC8sICcnKVxufVxuXG5mdW5jdGlvbiBnZXRDb29raWUgKGNvb2tpZU5hbWUsIGNvb2tpZVN0cmluZykge1xuICBjb29raWVTdHJpbmcgPSBjb29raWVTdHJpbmcgfHwgd2luZG93LmRvY3VtZW50LmNvb2tpZVxuICBpZiAoY29va2llU3RyaW5nICYmIGNvb2tpZVN0cmluZyAhPT0gJycpIHtcbiAgICBjb25zdCBjb29raWVzID0gY29va2llU3RyaW5nLnNwbGl0KCc7JylcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNvb2tpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGNvb2tpZSA9IHRyaW0oY29va2llc1tpXSlcbiAgICAgIC8vIERvZXMgdGhpcyBjb29raWUgc3RyaW5nIGJlZ2luIHdpdGggdGhlIG5hbWUgd2Ugd2FudD9cbiAgICAgIGlmIChjb29raWUuc3Vic3RyaW5nKDAsIGNvb2tpZU5hbWUubGVuZ3RoICsgMSkgPT09IChjb29raWVOYW1lICsgJz0nKSkge1xuICAgICAgICByZXR1cm4gZGVjb2RlVVJJQ29tcG9uZW50KGNvb2tpZS5zdWJzdHJpbmcoY29va2llTmFtZS5sZW5ndGggKyAxKSlcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGxcbn1cblxuY2xhc3MgU2Vzc2lvbkF1dGhlbnRpY2F0aW9uIHtcbiAgY29uc3RydWN0b3IgKG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuY3NyZlRva2VuID0gZ2V0Q29va2llKG9wdGlvbnMuY3NyZkNvb2tpZU5hbWUsIG9wdGlvbnMuY29va2llU3RyaW5nKVxuICAgIHRoaXMuY3NyZkhlYWRlck5hbWUgPSBvcHRpb25zLmNzcmZIZWFkZXJOYW1lXG4gIH1cblxuICBhdXRoZW50aWNhdGUgKG9wdGlvbnMpIHtcbiAgICBvcHRpb25zLmNyZWRlbnRpYWxzID0gJ3NhbWUtb3JpZ2luJ1xuICAgIGlmICh0aGlzLmNzcmZUb2tlbiAmJiAhdXRpbHMuY3NyZlNhZmVNZXRob2Qob3B0aW9ucy5tZXRob2QpKSB7XG4gICAgICBvcHRpb25zLmhlYWRlcnNbdGhpcy5jc3JmSGVhZGVyTmFtZV0gPSB0aGlzLmNzcmZUb2tlblxuICAgIH1cbiAgICByZXR1cm4gb3B0aW9uc1xuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBTZXNzaW9uQXV0aGVudGljYXRpb246IFNlc3Npb25BdXRoZW50aWNhdGlvblxufVxuIiwiY2xhc3MgVG9rZW5BdXRoZW50aWNhdGlvbiB7XG4gIGNvbnN0cnVjdG9yIChvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLnRva2VuID0gb3B0aW9ucy50b2tlblxuICAgIHRoaXMuc2NoZW1lID0gb3B0aW9ucy5zY2hlbWUgfHwgJ0JlYXJlcidcbiAgfVxuXG4gIGF1dGhlbnRpY2F0ZSAob3B0aW9ucykge1xuICAgIG9wdGlvbnMuaGVhZGVyc1snQXV0aG9yaXphdGlvbiddID0gdGhpcy5zY2hlbWUgKyAnICcgKyB0aGlzLnRva2VuXG4gICAgcmV0dXJuIG9wdGlvbnNcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgVG9rZW5BdXRoZW50aWNhdGlvbjogVG9rZW5BdXRoZW50aWNhdGlvblxufVxuIiwiY29uc3QgZG9jdW1lbnQgPSByZXF1aXJlKCcuL2RvY3VtZW50JylcbmNvbnN0IGNvZGVjcyA9IHJlcXVpcmUoJy4vY29kZWNzJylcbmNvbnN0IGVycm9ycyA9IHJlcXVpcmUoJy4vZXJyb3JzJylcbmNvbnN0IHRyYW5zcG9ydHMgPSByZXF1aXJlKCcuL3RyYW5zcG9ydHMnKVxuY29uc3QgdXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzJylcblxuZnVuY3Rpb24gbG9va3VwTGluayAobm9kZSwga2V5cykge1xuICBmb3IgKGxldCBrZXkgb2Yga2V5cykge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgZG9jdW1lbnQuRG9jdW1lbnQpIHtcbiAgICAgIG5vZGUgPSBub2RlLmNvbnRlbnRba2V5XVxuICAgIH0gZWxzZSB7XG4gICAgICBub2RlID0gbm9kZVtrZXldXG4gICAgfVxuICAgIGlmIChub2RlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBlcnJvcnMuTGlua0xvb2t1cEVycm9yKGBJbnZhbGlkIGxpbmsgbG9va3VwOiAke0pTT04uc3RyaW5naWZ5KGtleXMpfWApXG4gICAgfVxuICB9XG4gIGlmICghKG5vZGUgaW5zdGFuY2VvZiBkb2N1bWVudC5MaW5rKSkge1xuICAgIHRocm93IG5ldyBlcnJvcnMuTGlua0xvb2t1cEVycm9yKGBJbnZhbGlkIGxpbmsgbG9va3VwOiAke0pTT04uc3RyaW5naWZ5KGtleXMpfWApXG4gIH1cbiAgcmV0dXJuIG5vZGVcbn1cblxuY2xhc3MgQ2xpZW50IHtcbiAgY29uc3RydWN0b3IgKG9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHRyYW5zcG9ydE9wdGlvbnMgPSB7XG4gICAgICBhdXRoOiBvcHRpb25zLmF1dGggfHwgbnVsbCxcbiAgICAgIGhlYWRlcnM6IG9wdGlvbnMuaGVhZGVycyB8fCB7fSxcbiAgICAgIHJlcXVlc3RDYWxsYmFjazogb3B0aW9ucy5yZXF1ZXN0Q2FsbGJhY2ssXG4gICAgICByZXNwb25zZUNhbGxiYWNrOiBvcHRpb25zLnJlc3BvbnNlQ2FsbGJhY2tcbiAgICB9XG5cbiAgICB0aGlzLmRlY29kZXJzID0gb3B0aW9ucy5kZWNvZGVycyB8fCBbbmV3IGNvZGVjcy5Db3JlSlNPTkNvZGVjKCksIG5ldyBjb2RlY3MuSlNPTkNvZGVjKCksIG5ldyBjb2RlY3MuVGV4dENvZGVjKCldXG4gICAgdGhpcy50cmFuc3BvcnRzID0gb3B0aW9ucy50cmFuc3BvcnRzIHx8IFtuZXcgdHJhbnNwb3J0cy5IVFRQVHJhbnNwb3J0KHRyYW5zcG9ydE9wdGlvbnMpXVxuICB9XG5cbiAgYWN0aW9uIChkb2N1bWVudCwga2V5cywgcGFyYW1zID0ge30pIHtcbiAgICBjb25zdCBsaW5rID0gbG9va3VwTGluayhkb2N1bWVudCwga2V5cylcbiAgICBjb25zdCB0cmFuc3BvcnQgPSB1dGlscy5kZXRlcm1pbmVUcmFuc3BvcnQodGhpcy50cmFuc3BvcnRzLCBsaW5rLnVybClcbiAgICByZXR1cm4gdHJhbnNwb3J0LmFjdGlvbihsaW5rLCB0aGlzLmRlY29kZXJzLCBwYXJhbXMpXG4gIH1cblxuICBnZXQgKHVybCkge1xuICAgIGNvbnN0IGxpbmsgPSBuZXcgZG9jdW1lbnQuTGluayh1cmwsICdnZXQnKVxuICAgIGNvbnN0IHRyYW5zcG9ydCA9IHV0aWxzLmRldGVybWluZVRyYW5zcG9ydCh0aGlzLnRyYW5zcG9ydHMsIHVybClcbiAgICByZXR1cm4gdHJhbnNwb3J0LmFjdGlvbihsaW5rLCB0aGlzLmRlY29kZXJzKVxuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBDbGllbnQ6IENsaWVudFxufVxuIiwiY29uc3QgZG9jdW1lbnQgPSByZXF1aXJlKCcuLi9kb2N1bWVudCcpXG5jb25zdCBVUkwgPSByZXF1aXJlKCd1cmwtcGFyc2UnKVxuXG5mdW5jdGlvbiB1bmVzY2FwZUtleSAoa2V5KSB7XG4gIGlmIChrZXkubWF0Y2goL19fKHR5cGV8bWV0YSkkLykpIHtcbiAgICByZXR1cm4ga2V5LnN1YnN0cmluZygxKVxuICB9XG4gIHJldHVybiBrZXlcbn1cblxuZnVuY3Rpb24gZ2V0U3RyaW5nIChvYmosIGtleSkge1xuICBjb25zdCB2YWx1ZSA9IG9ialtrZXldXG4gIGlmICh0eXBlb2YgKHZhbHVlKSA9PT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWVcbiAgfVxuICByZXR1cm4gJydcbn1cblxuZnVuY3Rpb24gZ2V0Qm9vbGVhbiAob2JqLCBrZXkpIHtcbiAgY29uc3QgdmFsdWUgPSBvYmpba2V5XVxuICBpZiAodHlwZW9mICh2YWx1ZSkgPT09ICdib29sZWFuJykge1xuICAgIHJldHVybiB2YWx1ZVxuICB9XG4gIHJldHVybiBmYWxzZVxufVxuXG5mdW5jdGlvbiBnZXRPYmplY3QgKG9iaiwga2V5KSB7XG4gIGNvbnN0IHZhbHVlID0gb2JqW2tleV1cbiAgaWYgKHR5cGVvZiAodmFsdWUpID09PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiB2YWx1ZVxuICB9XG4gIHJldHVybiB7fVxufVxuXG5mdW5jdGlvbiBnZXRBcnJheSAob2JqLCBrZXkpIHtcbiAgY29uc3QgdmFsdWUgPSBvYmpba2V5XVxuICBpZiAodmFsdWUgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgIHJldHVybiB2YWx1ZVxuICB9XG4gIHJldHVybiBbXVxufVxuXG5mdW5jdGlvbiBnZXRDb250ZW50IChkYXRhLCBiYXNlVXJsKSB7XG4gIGNvbnN0IGV4Y2x1ZGVkID0gWydfdHlwZScsICdfbWV0YSddXG4gIHZhciBjb250ZW50ID0ge31cbiAgZm9yICh2YXIgcHJvcGVydHkgaW4gZGF0YSkge1xuICAgIGlmIChkYXRhLmhhc093blByb3BlcnR5KHByb3BlcnR5KSAmJiAhZXhjbHVkZWQuaW5jbHVkZXMocHJvcGVydHkpKSB7XG4gICAgICBjb25zdCBrZXkgPSB1bmVzY2FwZUtleShwcm9wZXJ0eSlcbiAgICAgIGNvbnN0IHZhbHVlID0gcHJpbWl0aXZlVG9Ob2RlKGRhdGFbcHJvcGVydHldLCBiYXNlVXJsKVxuICAgICAgY29udGVudFtrZXldID0gdmFsdWVcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGNvbnRlbnRcbn1cblxuZnVuY3Rpb24gcHJpbWl0aXZlVG9Ob2RlIChkYXRhLCBiYXNlVXJsKSB7XG4gIGNvbnN0IGlzT2JqZWN0ID0gZGF0YSBpbnN0YW5jZW9mIE9iamVjdCAmJiAhKGRhdGEgaW5zdGFuY2VvZiBBcnJheSlcblxuICBpZiAoaXNPYmplY3QgJiYgZGF0YS5fdHlwZSA9PT0gJ2RvY3VtZW50Jykge1xuICAgIC8vIERvY3VtZW50XG4gICAgY29uc3QgbWV0YSA9IGdldE9iamVjdChkYXRhLCAnX21ldGEnKVxuICAgIGNvbnN0IHJlbGF0aXZlVXJsID0gZ2V0U3RyaW5nKG1ldGEsICd1cmwnKVxuICAgIGNvbnN0IHVybCA9IHJlbGF0aXZlVXJsID8gVVJMKHJlbGF0aXZlVXJsLCBiYXNlVXJsKS50b1N0cmluZygpIDogJydcbiAgICBjb25zdCB0aXRsZSA9IGdldFN0cmluZyhtZXRhLCAndGl0bGUnKVxuICAgIGNvbnN0IGRlc2NyaXB0aW9uID0gZ2V0U3RyaW5nKG1ldGEsICdkZXNjcmlwdGlvbicpXG4gICAgY29uc3QgY29udGVudCA9IGdldENvbnRlbnQoZGF0YSwgdXJsKVxuICAgIHJldHVybiBuZXcgZG9jdW1lbnQuRG9jdW1lbnQodXJsLCB0aXRsZSwgZGVzY3JpcHRpb24sIGNvbnRlbnQpXG4gIH0gZWxzZSBpZiAoaXNPYmplY3QgJiYgZGF0YS5fdHlwZSA9PT0gJ2xpbmsnKSB7XG4gICAgLy8gTGlua1xuICAgIGNvbnN0IHJlbGF0aXZlVXJsID0gZ2V0U3RyaW5nKGRhdGEsICd1cmwnKVxuICAgIGNvbnN0IHVybCA9IHJlbGF0aXZlVXJsID8gVVJMKHJlbGF0aXZlVXJsLCBiYXNlVXJsKS50b1N0cmluZygpIDogJydcbiAgICBjb25zdCBtZXRob2QgPSBnZXRTdHJpbmcoZGF0YSwgJ2FjdGlvbicpIHx8ICdnZXQnXG4gICAgY29uc3QgdGl0bGUgPSBnZXRTdHJpbmcoZGF0YSwgJ3RpdGxlJylcbiAgICBjb25zdCBkZXNjcmlwdGlvbiA9IGdldFN0cmluZyhkYXRhLCAnZGVzY3JpcHRpb24nKVxuICAgIGNvbnN0IGZpZWxkc0RhdGEgPSBnZXRBcnJheShkYXRhLCAnZmllbGRzJylcbiAgICB2YXIgZmllbGRzID0gW11cbiAgICBmb3IgKGxldCBpZHggPSAwLCBsZW4gPSBmaWVsZHNEYXRhLmxlbmd0aDsgaWR4IDwgbGVuOyBpZHgrKykge1xuICAgICAgbGV0IHZhbHVlID0gZmllbGRzRGF0YVtpZHhdXG4gICAgICBsZXQgbmFtZSA9IGdldFN0cmluZyh2YWx1ZSwgJ25hbWUnKVxuICAgICAgbGV0IHJlcXVpcmVkID0gZ2V0Qm9vbGVhbih2YWx1ZSwgJ3JlcXVpcmVkJylcbiAgICAgIGxldCBsb2NhdGlvbiA9IGdldFN0cmluZyh2YWx1ZSwgJ2xvY2F0aW9uJylcbiAgICAgIGxldCBmaWVsZERlc2NyaXB0aW9uID0gZ2V0U3RyaW5nKHZhbHVlLCAnZmllbGREZXNjcmlwdGlvbicpXG4gICAgICBsZXQgZmllbGQgPSBuZXcgZG9jdW1lbnQuRmllbGQobmFtZSwgcmVxdWlyZWQsIGxvY2F0aW9uLCBmaWVsZERlc2NyaXB0aW9uKVxuICAgICAgZmllbGRzLnB1c2goZmllbGQpXG4gICAgfVxuICAgIHJldHVybiBuZXcgZG9jdW1lbnQuTGluayh1cmwsIG1ldGhvZCwgJ2FwcGxpY2F0aW9uL2pzb24nLCBmaWVsZHMsIHRpdGxlLCBkZXNjcmlwdGlvbilcbiAgfSBlbHNlIGlmIChpc09iamVjdCkge1xuICAgIC8vIE9iamVjdFxuICAgIGxldCBjb250ZW50ID0ge31cbiAgICBmb3IgKGxldCBrZXkgaW4gZGF0YSkge1xuICAgICAgaWYgKGRhdGEuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICBjb250ZW50W2tleV0gPSBwcmltaXRpdmVUb05vZGUoZGF0YVtrZXldLCBiYXNlVXJsKVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY29udGVudFxuICB9IGVsc2UgaWYgKGRhdGEgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgIC8vIE9iamVjdFxuICAgIGxldCBjb250ZW50ID0gW11cbiAgICBmb3IgKGxldCBpZHggPSAwLCBsZW4gPSBkYXRhLmxlbmd0aDsgaWR4IDwgbGVuOyBpZHgrKykge1xuICAgICAgY29udGVudC5wdXNoKHByaW1pdGl2ZVRvTm9kZShkYXRhW2lkeF0sIGJhc2VVcmwpKVxuICAgIH1cbiAgICByZXR1cm4gY29udGVudFxuICB9XG4gIC8vIFByaW1pdGl2ZVxuICByZXR1cm4gZGF0YVxufVxuXG5jbGFzcyBDb3JlSlNPTkNvZGVjIHtcbiAgY29uc3RydWN0b3IgKCkge1xuICAgIHRoaXMubWVkaWFUeXBlID0gJ2FwcGxpY2F0aW9uL2NvcmVhcGkranNvbidcbiAgfVxuXG4gIGRlY29kZSAodGV4dCwgb3B0aW9ucyA9IHt9KSB7XG4gICAgbGV0IGRhdGEgPSB0ZXh0XG4gICAgaWYgKG9wdGlvbnMucHJlbG9hZGVkID09PSB1bmRlZmluZWQgfHwgIW9wdGlvbnMucHJlbG9hZGVkKSB7XG4gICAgICBkYXRhID0gSlNPTi5wYXJzZSh0ZXh0KVxuICAgIH1cbiAgICByZXR1cm4gcHJpbWl0aXZlVG9Ob2RlKGRhdGEsIG9wdGlvbnMudXJsKVxuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBDb3JlSlNPTkNvZGVjOiBDb3JlSlNPTkNvZGVjXG59XG4iLCJjb25zdCBjb3JlanNvbiA9IHJlcXVpcmUoJy4vY29yZWpzb24nKVxuY29uc3QganNvbiA9IHJlcXVpcmUoJy4vanNvbicpXG5jb25zdCB0ZXh0ID0gcmVxdWlyZSgnLi90ZXh0JylcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIENvcmVKU09OQ29kZWM6IGNvcmVqc29uLkNvcmVKU09OQ29kZWMsXG4gIEpTT05Db2RlYzoganNvbi5KU09OQ29kZWMsXG4gIFRleHRDb2RlYzogdGV4dC5UZXh0Q29kZWNcbn1cbiIsImNsYXNzIEpTT05Db2RlYyB7XG4gIGNvbnN0cnVjdG9yICgpIHtcbiAgICB0aGlzLm1lZGlhVHlwZSA9ICdhcHBsaWNhdGlvbi9qc29uJ1xuICB9XG5cbiAgZGVjb2RlICh0ZXh0LCBvcHRpb25zID0ge30pIHtcbiAgICByZXR1cm4gSlNPTi5wYXJzZSh0ZXh0KVxuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBKU09OQ29kZWM6IEpTT05Db2RlY1xufVxuIiwiY2xhc3MgVGV4dENvZGVjIHtcbiAgY29uc3RydWN0b3IgKCkge1xuICAgIHRoaXMubWVkaWFUeXBlID0gJ3RleHQvKidcbiAgfVxuXG4gIGRlY29kZSAodGV4dCwgb3B0aW9ucyA9IHt9KSB7XG4gICAgcmV0dXJuIHRleHRcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgVGV4dENvZGVjOiBUZXh0Q29kZWNcbn1cbiIsImNsYXNzIERvY3VtZW50IHtcbiAgY29uc3RydWN0b3IgKHVybCA9ICcnLCB0aXRsZSA9ICcnLCBkZXNjcmlwdGlvbiA9ICcnLCBjb250ZW50ID0ge30pIHtcbiAgICB0aGlzLnVybCA9IHVybFxuICAgIHRoaXMudGl0bGUgPSB0aXRsZVxuICAgIHRoaXMuZGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvblxuICAgIHRoaXMuY29udGVudCA9IGNvbnRlbnRcbiAgfVxufVxuXG5jbGFzcyBMaW5rIHtcbiAgY29uc3RydWN0b3IgKHVybCwgbWV0aG9kLCBlbmNvZGluZyA9ICdhcHBsaWNhdGlvbi9qc29uJywgZmllbGRzID0gW10sIHRpdGxlID0gJycsIGRlc2NyaXB0aW9uID0gJycpIHtcbiAgICBpZiAodXJsID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndXJsIGFyZ3VtZW50IGlzIHJlcXVpcmVkJylcbiAgICB9XG5cbiAgICBpZiAobWV0aG9kID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbWV0aG9kIGFyZ3VtZW50IGlzIHJlcXVpcmVkJylcbiAgICB9XG5cbiAgICB0aGlzLnVybCA9IHVybFxuICAgIHRoaXMubWV0aG9kID0gbWV0aG9kXG4gICAgdGhpcy5lbmNvZGluZyA9IGVuY29kaW5nXG4gICAgdGhpcy5maWVsZHMgPSBmaWVsZHNcbiAgICB0aGlzLnRpdGxlID0gdGl0bGVcbiAgICB0aGlzLmRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb25cbiAgfVxufVxuXG5jbGFzcyBGaWVsZCB7XG4gIGNvbnN0cnVjdG9yIChuYW1lLCByZXF1aXJlZCA9IGZhbHNlLCBsb2NhdGlvbiA9ICcnLCBkZXNjcmlwdGlvbiA9ICcnKSB7XG4gICAgaWYgKG5hbWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCduYW1lIGFyZ3VtZW50IGlzIHJlcXVpcmVkJylcbiAgICB9XG5cbiAgICB0aGlzLm5hbWUgPSBuYW1lXG4gICAgdGhpcy5yZXF1aXJlZCA9IHJlcXVpcmVkXG4gICAgdGhpcy5sb2NhdGlvbiA9IGxvY2F0aW9uXG4gICAgdGhpcy5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uXG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIERvY3VtZW50OiBEb2N1bWVudCxcbiAgTGluazogTGluayxcbiAgRmllbGQ6IEZpZWxkXG59XG4iLCJjbGFzcyBQYXJhbWV0ZXJFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3IgKG1lc3NhZ2UpIHtcbiAgICBzdXBlcihtZXNzYWdlKVxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2VcbiAgICB0aGlzLm5hbWUgPSAnUGFyYW1ldGVyRXJyb3InXG4gIH1cbn1cblxuY2xhc3MgTGlua0xvb2t1cEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvciAobWVzc2FnZSkge1xuICAgIHN1cGVyKG1lc3NhZ2UpXG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZVxuICAgIHRoaXMubmFtZSA9ICdMaW5rTG9va3VwRXJyb3InXG4gIH1cbn1cblxuY2xhc3MgRXJyb3JNZXNzYWdlIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvciAobWVzc2FnZSwgY29udGVudCkge1xuICAgIHN1cGVyKG1lc3NhZ2UpXG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZVxuICAgIHRoaXMuY29udGVudCA9IGNvbnRlbnRcbiAgICB0aGlzLm5hbWUgPSAnRXJyb3JNZXNzYWdlJ1xuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBQYXJhbWV0ZXJFcnJvcjogUGFyYW1ldGVyRXJyb3IsXG4gIExpbmtMb29rdXBFcnJvcjogTGlua0xvb2t1cEVycm9yLFxuICBFcnJvck1lc3NhZ2U6IEVycm9yTWVzc2FnZVxufVxuIiwiY29uc3QgYXV0aCA9IHJlcXVpcmUoJy4vYXV0aCcpXG5jb25zdCBjbGllbnQgPSByZXF1aXJlKCcuL2NsaWVudCcpXG5jb25zdCBjb2RlY3MgPSByZXF1aXJlKCcuL2NvZGVjcycpXG5jb25zdCBkb2N1bWVudCA9IHJlcXVpcmUoJy4vZG9jdW1lbnQnKVxuY29uc3QgZXJyb3JzID0gcmVxdWlyZSgnLi9lcnJvcnMnKVxuY29uc3QgdHJhbnNwb3J0cyA9IHJlcXVpcmUoJy4vdHJhbnNwb3J0cycpXG5jb25zdCB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMnKVxuXG5jb25zdCBjb3JlYXBpID0ge1xuICBDbGllbnQ6IGNsaWVudC5DbGllbnQsXG4gIERvY3VtZW50OiBkb2N1bWVudC5Eb2N1bWVudCxcbiAgTGluazogZG9jdW1lbnQuTGluayxcbiAgYXV0aDogYXV0aCxcbiAgY29kZWNzOiBjb2RlY3MsXG4gIGVycm9yczogZXJyb3JzLFxuICB0cmFuc3BvcnRzOiB0cmFuc3BvcnRzLFxuICB1dGlsczogdXRpbHNcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBjb3JlYXBpXG4iLCJjb25zdCBmZXRjaCA9IHJlcXVpcmUoJ2lzb21vcnBoaWMtZmV0Y2gnKVxuY29uc3QgZXJyb3JzID0gcmVxdWlyZSgnLi4vZXJyb3JzJylcbmNvbnN0IHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKVxuY29uc3QgVVJMID0gcmVxdWlyZSgndXJsLXBhcnNlJylcbmNvbnN0IHVybFRlbXBsYXRlID0gcmVxdWlyZSgndXJsLXRlbXBsYXRlJylcblxuY29uc3QgcGFyc2VSZXNwb25zZSA9IChyZXNwb25zZSwgZGVjb2RlcnMsIHJlc3BvbnNlQ2FsbGJhY2spID0+IHtcbiAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKS50aGVuKHRleHQgPT4ge1xuICAgIGlmIChyZXNwb25zZUNhbGxiYWNrKSB7XG4gICAgICByZXNwb25zZUNhbGxiYWNrKHJlc3BvbnNlLCB0ZXh0KVxuICAgIH1cbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdDb250ZW50LVR5cGUnKVxuICAgIGNvbnN0IGRlY29kZXIgPSB1dGlscy5uZWdvdGlhdGVEZWNvZGVyKGRlY29kZXJzLCBjb250ZW50VHlwZSlcbiAgICBjb25zdCBvcHRpb25zID0ge3VybDogcmVzcG9uc2UudXJsfVxuICAgIHJldHVybiBkZWNvZGVyLmRlY29kZSh0ZXh0LCBvcHRpb25zKVxuICB9KVxufVxuXG5jbGFzcyBIVFRQVHJhbnNwb3J0IHtcbiAgY29uc3RydWN0b3IgKG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuc2NoZW1lcyA9IFsnaHR0cCcsICdodHRwcyddXG4gICAgdGhpcy5hdXRoID0gb3B0aW9ucy5hdXRoIHx8IG51bGxcbiAgICB0aGlzLmhlYWRlcnMgPSBvcHRpb25zLmhlYWRlcnMgfHwge31cbiAgICB0aGlzLmZldGNoID0gb3B0aW9ucy5mZXRjaCB8fCBmZXRjaFxuICAgIHRoaXMuRm9ybURhdGEgPSBvcHRpb25zLkZvcm1EYXRhIHx8IHdpbmRvdy5Gb3JtRGF0YVxuICAgIHRoaXMucmVxdWVzdENhbGxiYWNrID0gb3B0aW9ucy5yZXF1ZXN0Q2FsbGJhY2tcbiAgICB0aGlzLnJlc3BvbnNlQ2FsbGJhY2sgPSBvcHRpb25zLnJlc3BvbnNlQ2FsbGJhY2tcbiAgfVxuXG4gIGJ1aWxkUmVxdWVzdCAobGluaywgZGVjb2RlcnMsIHBhcmFtcyA9IHt9KSB7XG4gICAgY29uc3QgZmllbGRzID0gbGluay5maWVsZHNcbiAgICBjb25zdCBtZXRob2QgPSBsaW5rLm1ldGhvZC50b1VwcGVyQ2FzZSgpXG4gICAgbGV0IHF1ZXJ5UGFyYW1zID0ge31cbiAgICBsZXQgcGF0aFBhcmFtcyA9IHt9XG4gICAgbGV0IGZvcm1QYXJhbXMgPSB7fVxuICAgIGxldCBmaWVsZE5hbWVzID0gW11cbiAgICBsZXQgaGFzQm9keSA9IGZhbHNlXG5cbiAgICBmb3IgKGxldCBpZHggPSAwLCBsZW4gPSBmaWVsZHMubGVuZ3RoOyBpZHggPCBsZW47IGlkeCsrKSB7XG4gICAgICBjb25zdCBmaWVsZCA9IGZpZWxkc1tpZHhdXG5cbiAgICAgIC8vIEVuc3VyZSBhbnkgcmVxdWlyZWQgZmllbGRzIGFyZSBpbmNsdWRlZFxuICAgICAgaWYgKCFwYXJhbXMuaGFzT3duUHJvcGVydHkoZmllbGQubmFtZSkpIHtcbiAgICAgICAgaWYgKGZpZWxkLnJlcXVpcmVkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IGVycm9ycy5QYXJhbWV0ZXJFcnJvcihgTWlzc2luZyByZXF1aXJlZCBmaWVsZDogXCIke2ZpZWxkLm5hbWV9XCJgKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZmllbGROYW1lcy5wdXNoKGZpZWxkLm5hbWUpXG4gICAgICBpZiAoZmllbGQubG9jYXRpb24gPT09ICdxdWVyeScpIHtcbiAgICAgICAgcXVlcnlQYXJhbXNbZmllbGQubmFtZV0gPSBwYXJhbXNbZmllbGQubmFtZV1cbiAgICAgIH0gZWxzZSBpZiAoZmllbGQubG9jYXRpb24gPT09ICdwYXRoJykge1xuICAgICAgICBwYXRoUGFyYW1zW2ZpZWxkLm5hbWVdID0gcGFyYW1zW2ZpZWxkLm5hbWVdXG4gICAgICB9IGVsc2UgaWYgKGZpZWxkLmxvY2F0aW9uID09PSAnZm9ybScpIHtcbiAgICAgICAgZm9ybVBhcmFtc1tmaWVsZC5uYW1lXSA9IHBhcmFtc1tmaWVsZC5uYW1lXVxuICAgICAgICBoYXNCb2R5ID0gdHJ1ZVxuICAgICAgfSBlbHNlIGlmIChmaWVsZC5sb2NhdGlvbiA9PT0gJ2JvZHknKSB7XG4gICAgICAgIGZvcm1QYXJhbXMgPSBwYXJhbXNbZmllbGQubmFtZV1cbiAgICAgICAgaGFzQm9keSA9IHRydWVcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDaGVjayBmb3IgYW55IHBhcmFtZXRlcnMgdGhhdCBkaWQgbm90IGhhdmUgYSBtYXRjaGluZyBmaWVsZFxuICAgIGZvciAodmFyIHByb3BlcnR5IGluIHBhcmFtcykge1xuICAgICAgaWYgKHBhcmFtcy5oYXNPd25Qcm9wZXJ0eShwcm9wZXJ0eSkgJiYgIWZpZWxkTmFtZXMuaW5jbHVkZXMocHJvcGVydHkpKSB7XG4gICAgICAgIHRocm93IG5ldyBlcnJvcnMuUGFyYW1ldGVyRXJyb3IoYFVua25vd24gcGFyYW1ldGVyOiBcIiR7cHJvcGVydHl9XCJgKVxuICAgICAgfVxuICAgIH1cblxuICAgIGxldCByZXF1ZXN0T3B0aW9ucyA9IHttZXRob2Q6IG1ldGhvZCwgaGVhZGVyczoge319XG5cbiAgICBPYmplY3QuYXNzaWduKHJlcXVlc3RPcHRpb25zLmhlYWRlcnMsIHRoaXMuaGVhZGVycylcblxuICAgIGlmIChoYXNCb2R5KSB7XG4gICAgICBpZiAobGluay5lbmNvZGluZyA9PT0gJ2FwcGxpY2F0aW9uL2pzb24nKSB7XG4gICAgICAgIHJlcXVlc3RPcHRpb25zLmJvZHkgPSBKU09OLnN0cmluZ2lmeShmb3JtUGFyYW1zKVxuICAgICAgICByZXF1ZXN0T3B0aW9ucy5oZWFkZXJzWydDb250ZW50LVR5cGUnXSA9ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgfSBlbHNlIGlmIChsaW5rLmVuY29kaW5nID09PSAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpIHtcbiAgICAgICAgbGV0IGZvcm0gPSBuZXcgdGhpcy5Gb3JtRGF0YSgpXG5cbiAgICAgICAgZm9yIChsZXQgcGFyYW1LZXkgaW4gZm9ybVBhcmFtcykge1xuICAgICAgICAgIGZvcm0uYXBwZW5kKHBhcmFtS2V5LCBmb3JtUGFyYW1zW3BhcmFtS2V5XSlcbiAgICAgICAgfVxuICAgICAgICByZXF1ZXN0T3B0aW9ucy5ib2R5ID0gZm9ybVxuICAgICAgfSBlbHNlIGlmIChsaW5rLmVuY29kaW5nID09PSAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykge1xuICAgICAgICBsZXQgZm9ybUJvZHkgPSBbXVxuICAgICAgICBmb3IgKGxldCBwYXJhbUtleSBpbiBmb3JtUGFyYW1zKSB7XG4gICAgICAgICAgY29uc3QgZW5jb2RlZEtleSA9IGVuY29kZVVSSUNvbXBvbmVudChwYXJhbUtleSlcbiAgICAgICAgICBjb25zdCBlbmNvZGVkVmFsdWUgPSBlbmNvZGVVUklDb21wb25lbnQoZm9ybVBhcmFtc1twYXJhbUtleV0pXG4gICAgICAgICAgZm9ybUJvZHkucHVzaChlbmNvZGVkS2V5ICsgJz0nICsgZW5jb2RlZFZhbHVlKVxuICAgICAgICB9XG4gICAgICAgIGZvcm1Cb2R5ID0gZm9ybUJvZHkuam9pbignJicpXG5cbiAgICAgICAgcmVxdWVzdE9wdGlvbnMuYm9keSA9IGZvcm1Cb2R5XG4gICAgICAgIHJlcXVlc3RPcHRpb25zLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddID0gJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCdcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodGhpcy5hdXRoKSB7XG4gICAgICByZXF1ZXN0T3B0aW9ucyA9IHRoaXMuYXV0aC5hdXRoZW50aWNhdGUocmVxdWVzdE9wdGlvbnMpXG4gICAgfVxuXG4gICAgbGV0IHBhcnNlZFVybCA9IHVybFRlbXBsYXRlLnBhcnNlKGxpbmsudXJsKVxuICAgIHBhcnNlZFVybCA9IHBhcnNlZFVybC5leHBhbmQocGF0aFBhcmFtcylcbiAgICBwYXJzZWRVcmwgPSBuZXcgVVJMKHBhcnNlZFVybClcbiAgICBwYXJzZWRVcmwuc2V0KCdxdWVyeScsIHF1ZXJ5UGFyYW1zKVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHVybDogcGFyc2VkVXJsLnRvU3RyaW5nKCksXG4gICAgICBvcHRpb25zOiByZXF1ZXN0T3B0aW9uc1xuICAgIH1cbiAgfVxuXG4gIGFjdGlvbiAobGluaywgZGVjb2RlcnMsIHBhcmFtcyA9IHt9KSB7XG4gICAgY29uc3QgcmVzcG9uc2VDYWxsYmFjayA9IHRoaXMucmVzcG9uc2VDYWxsYmFja1xuICAgIGNvbnN0IHJlcXVlc3QgPSB0aGlzLmJ1aWxkUmVxdWVzdChsaW5rLCBkZWNvZGVycywgcGFyYW1zKVxuXG4gICAgaWYgKHRoaXMucmVxdWVzdENhbGxiYWNrKSB7XG4gICAgICB0aGlzLnJlcXVlc3RDYWxsYmFjayhyZXF1ZXN0KVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmZldGNoKHJlcXVlc3QudXJsLCByZXF1ZXN0Lm9wdGlvbnMpXG4gICAgICAudGhlbihmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICAgICAgaWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gMjA0KSB7XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhcnNlUmVzcG9uc2UocmVzcG9uc2UsIGRlY29kZXJzLCByZXNwb25zZUNhbGxiYWNrKVxuICAgICAgICAgIC50aGVuKGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgICAgICBpZiAocmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGRhdGFcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGNvbnN0IHRpdGxlID0gcmVzcG9uc2Uuc3RhdHVzICsgJyAnICsgcmVzcG9uc2Uuc3RhdHVzVGV4dFxuICAgICAgICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBlcnJvcnMuRXJyb3JNZXNzYWdlKHRpdGxlLCBkYXRhKVxuICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSlcbiAgICAgIH0pXG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIEhUVFBUcmFuc3BvcnQ6IEhUVFBUcmFuc3BvcnRcbn1cbiIsImNvbnN0IGh0dHAgPSByZXF1aXJlKCcuL2h0dHAnKVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgSFRUUFRyYW5zcG9ydDogaHR0cC5IVFRQVHJhbnNwb3J0XG59XG4iLCJjb25zdCBVUkwgPSByZXF1aXJlKCd1cmwtcGFyc2UnKVxuXG5jb25zdCBkZXRlcm1pbmVUcmFuc3BvcnQgPSBmdW5jdGlvbiAodHJhbnNwb3J0cywgdXJsKSB7XG4gIGNvbnN0IHBhcnNlZFVybCA9IG5ldyBVUkwodXJsKVxuICBjb25zdCBzY2hlbWUgPSBwYXJzZWRVcmwucHJvdG9jb2wucmVwbGFjZSgnOicsICcnKVxuXG4gIGZvciAobGV0IHRyYW5zcG9ydCBvZiB0cmFuc3BvcnRzKSB7XG4gICAgaWYgKHRyYW5zcG9ydC5zY2hlbWVzLmluY2x1ZGVzKHNjaGVtZSkpIHtcbiAgICAgIHJldHVybiB0cmFuc3BvcnRcbiAgICB9XG4gIH1cblxuICB0aHJvdyBFcnJvcihgVW5zdXBwb3J0ZWQgc2NoZW1lIGluIFVSTDogJHt1cmx9YClcbn1cblxuY29uc3QgbmVnb3RpYXRlRGVjb2RlciA9IGZ1bmN0aW9uIChkZWNvZGVycywgY29udGVudFR5cGUpIHtcbiAgaWYgKGNvbnRlbnRUeXBlID09PSB1bmRlZmluZWQgfHwgY29udGVudFR5cGUgPT09IG51bGwpIHtcbiAgICByZXR1cm4gZGVjb2RlcnNbMF1cbiAgfVxuXG4gIGNvbnN0IGZ1bGxUeXBlID0gY29udGVudFR5cGUudG9Mb3dlckNhc2UoKS5zcGxpdCgnOycpWzBdLnRyaW0oKVxuICBjb25zdCBtYWluVHlwZSA9IGZ1bGxUeXBlLnNwbGl0KCcvJylbMF0gKyAnLyonXG4gIGNvbnN0IHdpbGRjYXJkVHlwZSA9ICcqLyonXG4gIGNvbnN0IGFjY2VwdGFibGVUeXBlcyA9IFtmdWxsVHlwZSwgbWFpblR5cGUsIHdpbGRjYXJkVHlwZV1cblxuICBmb3IgKGxldCBkZWNvZGVyIG9mIGRlY29kZXJzKSB7XG4gICAgaWYgKGFjY2VwdGFibGVUeXBlcy5pbmNsdWRlcyhkZWNvZGVyLm1lZGlhVHlwZSkpIHtcbiAgICAgIHJldHVybiBkZWNvZGVyXG4gICAgfVxuICB9XG5cbiAgdGhyb3cgRXJyb3IoYFVuc3VwcG9ydGVkIG1lZGlhIGluIENvbnRlbnQtVHlwZSBoZWFkZXI6ICR7Y29udGVudFR5cGV9YClcbn1cblxuY29uc3QgY3NyZlNhZmVNZXRob2QgPSBmdW5jdGlvbiAobWV0aG9kKSB7XG4gIC8vIHRoZXNlIEhUVFAgbWV0aG9kcyBkbyBub3QgcmVxdWlyZSBDU1JGIHByb3RlY3Rpb25cbiAgcmV0dXJuICgvXihHRVR8SEVBRHxPUFRJT05TfFRSQUNFKSQvLnRlc3QobWV0aG9kKSlcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIGRldGVybWluZVRyYW5zcG9ydDogZGV0ZXJtaW5lVHJhbnNwb3J0LFxuICBuZWdvdGlhdGVEZWNvZGVyOiBuZWdvdGlhdGVEZWNvZGVyLFxuICBjc3JmU2FmZU1ldGhvZDogY3NyZlNhZmVNZXRob2Rcbn1cbiIsIi8vIHRoZSB3aGF0d2ctZmV0Y2ggcG9seWZpbGwgaW5zdGFsbHMgdGhlIGZldGNoKCkgZnVuY3Rpb25cbi8vIG9uIHRoZSBnbG9iYWwgb2JqZWN0ICh3aW5kb3cgb3Igc2VsZilcbi8vXG4vLyBSZXR1cm4gdGhhdCBhcyB0aGUgZXhwb3J0IGZvciB1c2UgaW4gV2VicGFjaywgQnJvd3NlcmlmeSBldGMuXG5yZXF1aXJlKCd3aGF0d2ctZmV0Y2gnKTtcbm1vZHVsZS5leHBvcnRzID0gc2VsZi5mZXRjaC5iaW5kKHNlbGYpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgaGFzID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eTtcblxuLyoqXG4gKiBTaW1wbGUgcXVlcnkgc3RyaW5nIHBhcnNlci5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gcXVlcnkgVGhlIHF1ZXJ5IHN0cmluZyB0aGF0IG5lZWRzIHRvIGJlIHBhcnNlZC5cbiAqIEByZXR1cm5zIHtPYmplY3R9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5mdW5jdGlvbiBxdWVyeXN0cmluZyhxdWVyeSkge1xuICB2YXIgcGFyc2VyID0gLyhbXj0/Jl0rKT0/KFteJl0qKS9nXG4gICAgLCByZXN1bHQgPSB7fVxuICAgICwgcGFydDtcblxuICAvL1xuICAvLyBMaXR0bGUgbmlmdHkgcGFyc2luZyBoYWNrLCBsZXZlcmFnZSB0aGUgZmFjdCB0aGF0IFJlZ0V4cC5leGVjIGluY3JlbWVudHNcbiAgLy8gdGhlIGxhc3RJbmRleCBwcm9wZXJ0eSBzbyB3ZSBjYW4gY29udGludWUgZXhlY3V0aW5nIHRoaXMgbG9vcCB1bnRpbCB3ZSd2ZVxuICAvLyBwYXJzZWQgYWxsIHJlc3VsdHMuXG4gIC8vXG4gIGZvciAoO1xuICAgIHBhcnQgPSBwYXJzZXIuZXhlYyhxdWVyeSk7XG4gICAgcmVzdWx0W2RlY29kZVVSSUNvbXBvbmVudChwYXJ0WzFdKV0gPSBkZWNvZGVVUklDb21wb25lbnQocGFydFsyXSlcbiAgKTtcblxuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIFRyYW5zZm9ybSBhIHF1ZXJ5IHN0cmluZyB0byBhbiBvYmplY3QuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9iaiBPYmplY3QgdGhhdCBzaG91bGQgYmUgdHJhbnNmb3JtZWQuXG4gKiBAcGFyYW0ge1N0cmluZ30gcHJlZml4IE9wdGlvbmFsIHByZWZpeC5cbiAqIEByZXR1cm5zIHtTdHJpbmd9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5mdW5jdGlvbiBxdWVyeXN0cmluZ2lmeShvYmosIHByZWZpeCkge1xuICBwcmVmaXggPSBwcmVmaXggfHwgJyc7XG5cbiAgdmFyIHBhaXJzID0gW107XG5cbiAgLy9cbiAgLy8gT3B0aW9uYWxseSBwcmVmaXggd2l0aCBhICc/JyBpZiBuZWVkZWRcbiAgLy9cbiAgaWYgKCdzdHJpbmcnICE9PSB0eXBlb2YgcHJlZml4KSBwcmVmaXggPSAnPyc7XG5cbiAgZm9yICh2YXIga2V5IGluIG9iaikge1xuICAgIGlmIChoYXMuY2FsbChvYmosIGtleSkpIHtcbiAgICAgIHBhaXJzLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KGtleSkgKyc9JysgZW5jb2RlVVJJQ29tcG9uZW50KG9ialtrZXldKSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHBhaXJzLmxlbmd0aCA/IHByZWZpeCArIHBhaXJzLmpvaW4oJyYnKSA6ICcnO1xufVxuXG4vL1xuLy8gRXhwb3NlIHRoZSBtb2R1bGUuXG4vL1xuZXhwb3J0cy5zdHJpbmdpZnkgPSBxdWVyeXN0cmluZ2lmeTtcbmV4cG9ydHMucGFyc2UgPSBxdWVyeXN0cmluZztcbiIsIid1c2Ugc3RyaWN0JztcblxuLyoqXG4gKiBDaGVjayBpZiB3ZSdyZSByZXF1aXJlZCB0byBhZGQgYSBwb3J0IG51bWJlci5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vdXJsLnNwZWMud2hhdHdnLm9yZy8jZGVmYXVsdC1wb3J0XG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd9IHBvcnQgUG9ydCBudW1iZXIgd2UgbmVlZCB0byBjaGVja1xuICogQHBhcmFtIHtTdHJpbmd9IHByb3RvY29sIFByb3RvY29sIHdlIG5lZWQgdG8gY2hlY2sgYWdhaW5zdC5cbiAqIEByZXR1cm5zIHtCb29sZWFufSBJcyBpdCBhIGRlZmF1bHQgcG9ydCBmb3IgdGhlIGdpdmVuIHByb3RvY29sXG4gKiBAYXBpIHByaXZhdGVcbiAqL1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiByZXF1aXJlZChwb3J0LCBwcm90b2NvbCkge1xuICBwcm90b2NvbCA9IHByb3RvY29sLnNwbGl0KCc6JylbMF07XG4gIHBvcnQgPSArcG9ydDtcblxuICBpZiAoIXBvcnQpIHJldHVybiBmYWxzZTtcblxuICBzd2l0Y2ggKHByb3RvY29sKSB7XG4gICAgY2FzZSAnaHR0cCc6XG4gICAgY2FzZSAnd3MnOlxuICAgIHJldHVybiBwb3J0ICE9PSA4MDtcblxuICAgIGNhc2UgJ2h0dHBzJzpcbiAgICBjYXNlICd3c3MnOlxuICAgIHJldHVybiBwb3J0ICE9PSA0NDM7XG5cbiAgICBjYXNlICdmdHAnOlxuICAgIHJldHVybiBwb3J0ICE9PSAyMTtcblxuICAgIGNhc2UgJ2dvcGhlcic6XG4gICAgcmV0dXJuIHBvcnQgIT09IDcwO1xuXG4gICAgY2FzZSAnZmlsZSc6XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIHBvcnQgIT09IDA7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgcmVxdWlyZWQgPSByZXF1aXJlKCdyZXF1aXJlcy1wb3J0JylcbiAgLCBsb2xjYXRpb24gPSByZXF1aXJlKCcuL2xvbGNhdGlvbicpXG4gICwgcXMgPSByZXF1aXJlKCdxdWVyeXN0cmluZ2lmeScpXG4gICwgcHJvdG9jb2xyZSA9IC9eKFthLXpdW2EtejAtOS4rLV0qOik/KFxcL1xcLyk/KFtcXFNcXHNdKikvaTtcblxuLyoqXG4gKiBUaGVzZSBhcmUgdGhlIHBhcnNlIHJ1bGVzIGZvciB0aGUgVVJMIHBhcnNlciwgaXQgaW5mb3JtcyB0aGUgcGFyc2VyXG4gKiBhYm91dDpcbiAqXG4gKiAwLiBUaGUgY2hhciBpdCBOZWVkcyB0byBwYXJzZSwgaWYgaXQncyBhIHN0cmluZyBpdCBzaG91bGQgYmUgZG9uZSB1c2luZ1xuICogICAgaW5kZXhPZiwgUmVnRXhwIHVzaW5nIGV4ZWMgYW5kIE5hTiBtZWFucyBzZXQgYXMgY3VycmVudCB2YWx1ZS5cbiAqIDEuIFRoZSBwcm9wZXJ0eSB3ZSBzaG91bGQgc2V0IHdoZW4gcGFyc2luZyB0aGlzIHZhbHVlLlxuICogMi4gSW5kaWNhdGlvbiBpZiBpdCdzIGJhY2t3YXJkcyBvciBmb3J3YXJkIHBhcnNpbmcsIHdoZW4gc2V0IGFzIG51bWJlciBpdCdzXG4gKiAgICB0aGUgdmFsdWUgb2YgZXh0cmEgY2hhcnMgdGhhdCBzaG91bGQgYmUgc3BsaXQgb2ZmLlxuICogMy4gSW5oZXJpdCBmcm9tIGxvY2F0aW9uIGlmIG5vbiBleGlzdGluZyBpbiB0aGUgcGFyc2VyLlxuICogNC4gYHRvTG93ZXJDYXNlYCB0aGUgcmVzdWx0aW5nIHZhbHVlLlxuICovXG52YXIgcnVsZXMgPSBbXG4gIFsnIycsICdoYXNoJ10sICAgICAgICAgICAgICAgICAgICAgICAgLy8gRXh0cmFjdCBmcm9tIHRoZSBiYWNrLlxuICBbJz8nLCAncXVlcnknXSwgICAgICAgICAgICAgICAgICAgICAgIC8vIEV4dHJhY3QgZnJvbSB0aGUgYmFjay5cbiAgWycvJywgJ3BhdGhuYW1lJ10sICAgICAgICAgICAgICAgICAgICAvLyBFeHRyYWN0IGZyb20gdGhlIGJhY2suXG4gIFsnQCcsICdhdXRoJywgMV0sICAgICAgICAgICAgICAgICAgICAgLy8gRXh0cmFjdCBmcm9tIHRoZSBmcm9udC5cbiAgW05hTiwgJ2hvc3QnLCB1bmRlZmluZWQsIDEsIDFdLCAgICAgICAvLyBTZXQgbGVmdCBvdmVyIHZhbHVlLlxuICBbLzooXFxkKykkLywgJ3BvcnQnLCB1bmRlZmluZWQsIDFdLCAgICAvLyBSZWdFeHAgdGhlIGJhY2suXG4gIFtOYU4sICdob3N0bmFtZScsIHVuZGVmaW5lZCwgMSwgMV0gICAgLy8gU2V0IGxlZnQgb3Zlci5cbl07XG5cbi8qKlxuICogQHR5cGVkZWYgUHJvdG9jb2xFeHRyYWN0XG4gKiBAdHlwZSBPYmplY3RcbiAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBwcm90b2NvbCBQcm90b2NvbCBtYXRjaGVkIGluIHRoZSBVUkwsIGluIGxvd2VyY2FzZS5cbiAqIEBwcm9wZXJ0eSB7Qm9vbGVhbn0gc2xhc2hlcyBgdHJ1ZWAgaWYgcHJvdG9jb2wgaXMgZm9sbG93ZWQgYnkgXCIvL1wiLCBlbHNlIGBmYWxzZWAuXG4gKiBAcHJvcGVydHkge1N0cmluZ30gcmVzdCBSZXN0IG9mIHRoZSBVUkwgdGhhdCBpcyBub3QgcGFydCBvZiB0aGUgcHJvdG9jb2wuXG4gKi9cblxuLyoqXG4gKiBFeHRyYWN0IHByb3RvY29sIGluZm9ybWF0aW9uIGZyb20gYSBVUkwgd2l0aC93aXRob3V0IGRvdWJsZSBzbGFzaCAoXCIvL1wiKS5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzcyBVUkwgd2Ugd2FudCB0byBleHRyYWN0IGZyb20uXG4gKiBAcmV0dXJuIHtQcm90b2NvbEV4dHJhY3R9IEV4dHJhY3RlZCBpbmZvcm1hdGlvbi5cbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBleHRyYWN0UHJvdG9jb2woYWRkcmVzcykge1xuICB2YXIgbWF0Y2ggPSBwcm90b2NvbHJlLmV4ZWMoYWRkcmVzcyk7XG5cbiAgcmV0dXJuIHtcbiAgICBwcm90b2NvbDogbWF0Y2hbMV0gPyBtYXRjaFsxXS50b0xvd2VyQ2FzZSgpIDogJycsXG4gICAgc2xhc2hlczogISFtYXRjaFsyXSxcbiAgICByZXN0OiBtYXRjaFszXVxuICB9O1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSByZWxhdGl2ZSBVUkwgcGF0aG5hbWUgYWdhaW5zdCBhIGJhc2UgVVJMIHBhdGhuYW1lLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSByZWxhdGl2ZSBQYXRobmFtZSBvZiB0aGUgcmVsYXRpdmUgVVJMLlxuICogQHBhcmFtIHtTdHJpbmd9IGJhc2UgUGF0aG5hbWUgb2YgdGhlIGJhc2UgVVJMLlxuICogQHJldHVybiB7U3RyaW5nfSBSZXNvbHZlZCBwYXRobmFtZS5cbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5mdW5jdGlvbiByZXNvbHZlKHJlbGF0aXZlLCBiYXNlKSB7XG4gIHZhciBwYXRoID0gKGJhc2UgfHwgJy8nKS5zcGxpdCgnLycpLnNsaWNlKDAsIC0xKS5jb25jYXQocmVsYXRpdmUuc3BsaXQoJy8nKSlcbiAgICAsIGkgPSBwYXRoLmxlbmd0aFxuICAgICwgbGFzdCA9IHBhdGhbaSAtIDFdXG4gICAgLCB1bnNoaWZ0ID0gZmFsc2VcbiAgICAsIHVwID0gMDtcblxuICB3aGlsZSAoaS0tKSB7XG4gICAgaWYgKHBhdGhbaV0gPT09ICcuJykge1xuICAgICAgcGF0aC5zcGxpY2UoaSwgMSk7XG4gICAgfSBlbHNlIGlmIChwYXRoW2ldID09PSAnLi4nKSB7XG4gICAgICBwYXRoLnNwbGljZShpLCAxKTtcbiAgICAgIHVwKys7XG4gICAgfSBlbHNlIGlmICh1cCkge1xuICAgICAgaWYgKGkgPT09IDApIHVuc2hpZnQgPSB0cnVlO1xuICAgICAgcGF0aC5zcGxpY2UoaSwgMSk7XG4gICAgICB1cC0tO1xuICAgIH1cbiAgfVxuXG4gIGlmICh1bnNoaWZ0KSBwYXRoLnVuc2hpZnQoJycpO1xuICBpZiAobGFzdCA9PT0gJy4nIHx8IGxhc3QgPT09ICcuLicpIHBhdGgucHVzaCgnJyk7XG5cbiAgcmV0dXJuIHBhdGguam9pbignLycpO1xufVxuXG4vKipcbiAqIFRoZSBhY3R1YWwgVVJMIGluc3RhbmNlLiBJbnN0ZWFkIG9mIHJldHVybmluZyBhbiBvYmplY3Qgd2UndmUgb3B0ZWQtaW4gdG9cbiAqIGNyZWF0ZSBhbiBhY3R1YWwgY29uc3RydWN0b3IgYXMgaXQncyBtdWNoIG1vcmUgbWVtb3J5IGVmZmljaWVudCBhbmRcbiAqIGZhc3RlciBhbmQgaXQgcGxlYXNlcyBteSBPQ0QuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzcyBVUkwgd2Ugd2FudCB0byBwYXJzZS5cbiAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gbG9jYXRpb24gTG9jYXRpb24gZGVmYXVsdHMgZm9yIHJlbGF0aXZlIHBhdGhzLlxuICogQHBhcmFtIHtCb29sZWFufEZ1bmN0aW9ufSBwYXJzZXIgUGFyc2VyIGZvciB0aGUgcXVlcnkgc3RyaW5nLlxuICogQGFwaSBwdWJsaWNcbiAqL1xuZnVuY3Rpb24gVVJMKGFkZHJlc3MsIGxvY2F0aW9uLCBwYXJzZXIpIHtcbiAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIFVSTCkpIHtcbiAgICByZXR1cm4gbmV3IFVSTChhZGRyZXNzLCBsb2NhdGlvbiwgcGFyc2VyKTtcbiAgfVxuXG4gIHZhciByZWxhdGl2ZSwgZXh0cmFjdGVkLCBwYXJzZSwgaW5zdHJ1Y3Rpb24sIGluZGV4LCBrZXlcbiAgICAsIGluc3RydWN0aW9ucyA9IHJ1bGVzLnNsaWNlKClcbiAgICAsIHR5cGUgPSB0eXBlb2YgbG9jYXRpb25cbiAgICAsIHVybCA9IHRoaXNcbiAgICAsIGkgPSAwO1xuXG4gIC8vXG4gIC8vIFRoZSBmb2xsb3dpbmcgaWYgc3RhdGVtZW50cyBhbGxvd3MgdGhpcyBtb2R1bGUgdHdvIGhhdmUgY29tcGF0aWJpbGl0eSB3aXRoXG4gIC8vIDIgZGlmZmVyZW50IEFQSTpcbiAgLy9cbiAgLy8gMS4gTm9kZS5qcydzIGB1cmwucGFyc2VgIGFwaSB3aGljaCBhY2NlcHRzIGEgVVJMLCBib29sZWFuIGFzIGFyZ3VtZW50c1xuICAvLyAgICB3aGVyZSB0aGUgYm9vbGVhbiBpbmRpY2F0ZXMgdGhhdCB0aGUgcXVlcnkgc3RyaW5nIHNob3VsZCBhbHNvIGJlIHBhcnNlZC5cbiAgLy9cbiAgLy8gMi4gVGhlIGBVUkxgIGludGVyZmFjZSBvZiB0aGUgYnJvd3NlciB3aGljaCBhY2NlcHRzIGEgVVJMLCBvYmplY3QgYXNcbiAgLy8gICAgYXJndW1lbnRzLiBUaGUgc3VwcGxpZWQgb2JqZWN0IHdpbGwgYmUgdXNlZCBhcyBkZWZhdWx0IHZhbHVlcyAvIGZhbGwtYmFja1xuICAvLyAgICBmb3IgcmVsYXRpdmUgcGF0aHMuXG4gIC8vXG4gIGlmICgnb2JqZWN0JyAhPT0gdHlwZSAmJiAnc3RyaW5nJyAhPT0gdHlwZSkge1xuICAgIHBhcnNlciA9IGxvY2F0aW9uO1xuICAgIGxvY2F0aW9uID0gbnVsbDtcbiAgfVxuXG4gIGlmIChwYXJzZXIgJiYgJ2Z1bmN0aW9uJyAhPT0gdHlwZW9mIHBhcnNlcikgcGFyc2VyID0gcXMucGFyc2U7XG5cbiAgbG9jYXRpb24gPSBsb2xjYXRpb24obG9jYXRpb24pO1xuXG4gIC8vXG4gIC8vIEV4dHJhY3QgcHJvdG9jb2wgaW5mb3JtYXRpb24gYmVmb3JlIHJ1bm5pbmcgdGhlIGluc3RydWN0aW9ucy5cbiAgLy9cbiAgZXh0cmFjdGVkID0gZXh0cmFjdFByb3RvY29sKGFkZHJlc3MgfHwgJycpO1xuICByZWxhdGl2ZSA9ICFleHRyYWN0ZWQucHJvdG9jb2wgJiYgIWV4dHJhY3RlZC5zbGFzaGVzO1xuICB1cmwuc2xhc2hlcyA9IGV4dHJhY3RlZC5zbGFzaGVzIHx8IHJlbGF0aXZlICYmIGxvY2F0aW9uLnNsYXNoZXM7XG4gIHVybC5wcm90b2NvbCA9IGV4dHJhY3RlZC5wcm90b2NvbCB8fCBsb2NhdGlvbi5wcm90b2NvbCB8fCAnJztcbiAgYWRkcmVzcyA9IGV4dHJhY3RlZC5yZXN0O1xuXG4gIC8vXG4gIC8vIFdoZW4gdGhlIGF1dGhvcml0eSBjb21wb25lbnQgaXMgYWJzZW50IHRoZSBVUkwgc3RhcnRzIHdpdGggYSBwYXRoXG4gIC8vIGNvbXBvbmVudC5cbiAgLy9cbiAgaWYgKCFleHRyYWN0ZWQuc2xhc2hlcykgaW5zdHJ1Y3Rpb25zWzJdID0gWy8oLiopLywgJ3BhdGhuYW1lJ107XG5cbiAgZm9yICg7IGkgPCBpbnN0cnVjdGlvbnMubGVuZ3RoOyBpKyspIHtcbiAgICBpbnN0cnVjdGlvbiA9IGluc3RydWN0aW9uc1tpXTtcbiAgICBwYXJzZSA9IGluc3RydWN0aW9uWzBdO1xuICAgIGtleSA9IGluc3RydWN0aW9uWzFdO1xuXG4gICAgaWYgKHBhcnNlICE9PSBwYXJzZSkge1xuICAgICAgdXJsW2tleV0gPSBhZGRyZXNzO1xuICAgIH0gZWxzZSBpZiAoJ3N0cmluZycgPT09IHR5cGVvZiBwYXJzZSkge1xuICAgICAgaWYgKH4oaW5kZXggPSBhZGRyZXNzLmluZGV4T2YocGFyc2UpKSkge1xuICAgICAgICBpZiAoJ251bWJlcicgPT09IHR5cGVvZiBpbnN0cnVjdGlvblsyXSkge1xuICAgICAgICAgIHVybFtrZXldID0gYWRkcmVzcy5zbGljZSgwLCBpbmRleCk7XG4gICAgICAgICAgYWRkcmVzcyA9IGFkZHJlc3Muc2xpY2UoaW5kZXggKyBpbnN0cnVjdGlvblsyXSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdXJsW2tleV0gPSBhZGRyZXNzLnNsaWNlKGluZGV4KTtcbiAgICAgICAgICBhZGRyZXNzID0gYWRkcmVzcy5zbGljZSgwLCBpbmRleCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGluZGV4ID0gcGFyc2UuZXhlYyhhZGRyZXNzKSkge1xuICAgICAgdXJsW2tleV0gPSBpbmRleFsxXTtcbiAgICAgIGFkZHJlc3MgPSBhZGRyZXNzLnNsaWNlKDAsIGluZGV4LmluZGV4KTtcbiAgICB9XG5cbiAgICB1cmxba2V5XSA9IHVybFtrZXldIHx8IChcbiAgICAgIHJlbGF0aXZlICYmIGluc3RydWN0aW9uWzNdID8gbG9jYXRpb25ba2V5XSB8fCAnJyA6ICcnXG4gICAgKTtcblxuICAgIC8vXG4gICAgLy8gSG9zdG5hbWUsIGhvc3QgYW5kIHByb3RvY29sIHNob3VsZCBiZSBsb3dlcmNhc2VkIHNvIHRoZXkgY2FuIGJlIHVzZWQgdG9cbiAgICAvLyBjcmVhdGUgYSBwcm9wZXIgYG9yaWdpbmAuXG4gICAgLy9cbiAgICBpZiAoaW5zdHJ1Y3Rpb25bNF0pIHVybFtrZXldID0gdXJsW2tleV0udG9Mb3dlckNhc2UoKTtcbiAgfVxuXG4gIC8vXG4gIC8vIEFsc28gcGFyc2UgdGhlIHN1cHBsaWVkIHF1ZXJ5IHN0cmluZyBpbiB0byBhbiBvYmplY3QuIElmIHdlJ3JlIHN1cHBsaWVkXG4gIC8vIHdpdGggYSBjdXN0b20gcGFyc2VyIGFzIGZ1bmN0aW9uIHVzZSB0aGF0IGluc3RlYWQgb2YgdGhlIGRlZmF1bHQgYnVpbGQtaW5cbiAgLy8gcGFyc2VyLlxuICAvL1xuICBpZiAocGFyc2VyKSB1cmwucXVlcnkgPSBwYXJzZXIodXJsLnF1ZXJ5KTtcblxuICAvL1xuICAvLyBJZiB0aGUgVVJMIGlzIHJlbGF0aXZlLCByZXNvbHZlIHRoZSBwYXRobmFtZSBhZ2FpbnN0IHRoZSBiYXNlIFVSTC5cbiAgLy9cbiAgaWYgKFxuICAgICAgcmVsYXRpdmVcbiAgICAmJiBsb2NhdGlvbi5zbGFzaGVzXG4gICAgJiYgdXJsLnBhdGhuYW1lLmNoYXJBdCgwKSAhPT0gJy8nXG4gICAgJiYgKHVybC5wYXRobmFtZSAhPT0gJycgfHwgbG9jYXRpb24ucGF0aG5hbWUgIT09ICcnKVxuICApIHtcbiAgICB1cmwucGF0aG5hbWUgPSByZXNvbHZlKHVybC5wYXRobmFtZSwgbG9jYXRpb24ucGF0aG5hbWUpO1xuICB9XG5cbiAgLy9cbiAgLy8gV2Ugc2hvdWxkIG5vdCBhZGQgcG9ydCBudW1iZXJzIGlmIHRoZXkgYXJlIGFscmVhZHkgdGhlIGRlZmF1bHQgcG9ydCBudW1iZXJcbiAgLy8gZm9yIGEgZ2l2ZW4gcHJvdG9jb2wuIEFzIHRoZSBob3N0IGFsc28gY29udGFpbnMgdGhlIHBvcnQgbnVtYmVyIHdlJ3JlIGdvaW5nXG4gIC8vIG92ZXJyaWRlIGl0IHdpdGggdGhlIGhvc3RuYW1lIHdoaWNoIGNvbnRhaW5zIG5vIHBvcnQgbnVtYmVyLlxuICAvL1xuICBpZiAoIXJlcXVpcmVkKHVybC5wb3J0LCB1cmwucHJvdG9jb2wpKSB7XG4gICAgdXJsLmhvc3QgPSB1cmwuaG9zdG5hbWU7XG4gICAgdXJsLnBvcnQgPSAnJztcbiAgfVxuXG4gIC8vXG4gIC8vIFBhcnNlIGRvd24gdGhlIGBhdXRoYCBmb3IgdGhlIHVzZXJuYW1lIGFuZCBwYXNzd29yZC5cbiAgLy9cbiAgdXJsLnVzZXJuYW1lID0gdXJsLnBhc3N3b3JkID0gJyc7XG4gIGlmICh1cmwuYXV0aCkge1xuICAgIGluc3RydWN0aW9uID0gdXJsLmF1dGguc3BsaXQoJzonKTtcbiAgICB1cmwudXNlcm5hbWUgPSBpbnN0cnVjdGlvblswXSB8fCAnJztcbiAgICB1cmwucGFzc3dvcmQgPSBpbnN0cnVjdGlvblsxXSB8fCAnJztcbiAgfVxuXG4gIHVybC5vcmlnaW4gPSB1cmwucHJvdG9jb2wgJiYgdXJsLmhvc3QgJiYgdXJsLnByb3RvY29sICE9PSAnZmlsZTonXG4gICAgPyB1cmwucHJvdG9jb2wgKycvLycrIHVybC5ob3N0XG4gICAgOiAnbnVsbCc7XG5cbiAgLy9cbiAgLy8gVGhlIGhyZWYgaXMganVzdCB0aGUgY29tcGlsZWQgcmVzdWx0LlxuICAvL1xuICB1cmwuaHJlZiA9IHVybC50b1N0cmluZygpO1xufVxuXG4vKipcbiAqIFRoaXMgaXMgY29udmVuaWVuY2UgbWV0aG9kIGZvciBjaGFuZ2luZyBwcm9wZXJ0aWVzIGluIHRoZSBVUkwgaW5zdGFuY2UgdG9cbiAqIGluc3VyZSB0aGF0IHRoZXkgYWxsIHByb3BhZ2F0ZSBjb3JyZWN0bHkuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHBhcnQgICAgICAgICAgUHJvcGVydHkgd2UgbmVlZCB0byBhZGp1c3QuXG4gKiBAcGFyYW0ge01peGVkfSB2YWx1ZSAgICAgICAgICBUaGUgbmV3bHkgYXNzaWduZWQgdmFsdWUuXG4gKiBAcGFyYW0ge0Jvb2xlYW58RnVuY3Rpb259IGZuICBXaGVuIHNldHRpbmcgdGhlIHF1ZXJ5LCBpdCB3aWxsIGJlIHRoZSBmdW5jdGlvblxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlZCB0byBwYXJzZSB0aGUgcXVlcnkuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXaGVuIHNldHRpbmcgdGhlIHByb3RvY29sLCBkb3VibGUgc2xhc2ggd2lsbCBiZVxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlZCBmcm9tIHRoZSBmaW5hbCB1cmwgaWYgaXQgaXMgdHJ1ZS5cbiAqIEByZXR1cm5zIHtVUkx9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5VUkwucHJvdG90eXBlLnNldCA9IGZ1bmN0aW9uIHNldChwYXJ0LCB2YWx1ZSwgZm4pIHtcbiAgdmFyIHVybCA9IHRoaXM7XG5cbiAgc3dpdGNoIChwYXJ0KSB7XG4gICAgY2FzZSAncXVlcnknOlxuICAgICAgaWYgKCdzdHJpbmcnID09PSB0eXBlb2YgdmFsdWUgJiYgdmFsdWUubGVuZ3RoKSB7XG4gICAgICAgIHZhbHVlID0gKGZuIHx8IHFzLnBhcnNlKSh2YWx1ZSk7XG4gICAgICB9XG5cbiAgICAgIHVybFtwYXJ0XSA9IHZhbHVlO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICdwb3J0JzpcbiAgICAgIHVybFtwYXJ0XSA9IHZhbHVlO1xuXG4gICAgICBpZiAoIXJlcXVpcmVkKHZhbHVlLCB1cmwucHJvdG9jb2wpKSB7XG4gICAgICAgIHVybC5ob3N0ID0gdXJsLmhvc3RuYW1lO1xuICAgICAgICB1cmxbcGFydF0gPSAnJztcbiAgICAgIH0gZWxzZSBpZiAodmFsdWUpIHtcbiAgICAgICAgdXJsLmhvc3QgPSB1cmwuaG9zdG5hbWUgKyc6JysgdmFsdWU7XG4gICAgICB9XG5cbiAgICAgIGJyZWFrO1xuXG4gICAgY2FzZSAnaG9zdG5hbWUnOlxuICAgICAgdXJsW3BhcnRdID0gdmFsdWU7XG5cbiAgICAgIGlmICh1cmwucG9ydCkgdmFsdWUgKz0gJzonKyB1cmwucG9ydDtcbiAgICAgIHVybC5ob3N0ID0gdmFsdWU7XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ2hvc3QnOlxuICAgICAgdXJsW3BhcnRdID0gdmFsdWU7XG5cbiAgICAgIGlmICgvOlxcZCskLy50ZXN0KHZhbHVlKSkge1xuICAgICAgICB2YWx1ZSA9IHZhbHVlLnNwbGl0KCc6Jyk7XG4gICAgICAgIHVybC5wb3J0ID0gdmFsdWUucG9wKCk7XG4gICAgICAgIHVybC5ob3N0bmFtZSA9IHZhbHVlLmpvaW4oJzonKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHVybC5ob3N0bmFtZSA9IHZhbHVlO1xuICAgICAgICB1cmwucG9ydCA9ICcnO1xuICAgICAgfVxuXG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ3Byb3RvY29sJzpcbiAgICAgIHVybC5wcm90b2NvbCA9IHZhbHVlLnRvTG93ZXJDYXNlKCk7XG4gICAgICB1cmwuc2xhc2hlcyA9ICFmbjtcbiAgICAgIGJyZWFrO1xuXG4gICAgY2FzZSAncGF0aG5hbWUnOlxuICAgICAgdXJsLnBhdGhuYW1lID0gdmFsdWUubGVuZ3RoICYmIHZhbHVlLmNoYXJBdCgwKSAhPT0gJy8nID8gJy8nICsgdmFsdWUgOiB2YWx1ZTtcblxuICAgICAgYnJlYWs7XG5cbiAgICBkZWZhdWx0OlxuICAgICAgdXJsW3BhcnRdID0gdmFsdWU7XG4gIH1cblxuICBmb3IgKHZhciBpID0gMDsgaSA8IHJ1bGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIGlucyA9IHJ1bGVzW2ldO1xuXG4gICAgaWYgKGluc1s0XSkgdXJsW2luc1sxXV0gPSB1cmxbaW5zWzFdXS50b0xvd2VyQ2FzZSgpO1xuICB9XG5cbiAgdXJsLm9yaWdpbiA9IHVybC5wcm90b2NvbCAmJiB1cmwuaG9zdCAmJiB1cmwucHJvdG9jb2wgIT09ICdmaWxlOidcbiAgICA/IHVybC5wcm90b2NvbCArJy8vJysgdXJsLmhvc3RcbiAgICA6ICdudWxsJztcblxuICB1cmwuaHJlZiA9IHVybC50b1N0cmluZygpO1xuXG4gIHJldHVybiB1cmw7XG59O1xuXG4vKipcbiAqIFRyYW5zZm9ybSB0aGUgcHJvcGVydGllcyBiYWNrIGluIHRvIGEgdmFsaWQgYW5kIGZ1bGwgVVJMIHN0cmluZy5cbiAqXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBzdHJpbmdpZnkgT3B0aW9uYWwgcXVlcnkgc3RyaW5naWZ5IGZ1bmN0aW9uLlxuICogQHJldHVybnMge1N0cmluZ31cbiAqIEBhcGkgcHVibGljXG4gKi9cblVSTC5wcm90b3R5cGUudG9TdHJpbmcgPSBmdW5jdGlvbiB0b1N0cmluZyhzdHJpbmdpZnkpIHtcbiAgaWYgKCFzdHJpbmdpZnkgfHwgJ2Z1bmN0aW9uJyAhPT0gdHlwZW9mIHN0cmluZ2lmeSkgc3RyaW5naWZ5ID0gcXMuc3RyaW5naWZ5O1xuXG4gIHZhciBxdWVyeVxuICAgICwgdXJsID0gdGhpc1xuICAgICwgcHJvdG9jb2wgPSB1cmwucHJvdG9jb2w7XG5cbiAgaWYgKHByb3RvY29sICYmIHByb3RvY29sLmNoYXJBdChwcm90b2NvbC5sZW5ndGggLSAxKSAhPT0gJzonKSBwcm90b2NvbCArPSAnOic7XG5cbiAgdmFyIHJlc3VsdCA9IHByb3RvY29sICsgKHVybC5zbGFzaGVzID8gJy8vJyA6ICcnKTtcblxuICBpZiAodXJsLnVzZXJuYW1lKSB7XG4gICAgcmVzdWx0ICs9IHVybC51c2VybmFtZTtcbiAgICBpZiAodXJsLnBhc3N3b3JkKSByZXN1bHQgKz0gJzonKyB1cmwucGFzc3dvcmQ7XG4gICAgcmVzdWx0ICs9ICdAJztcbiAgfVxuXG4gIHJlc3VsdCArPSB1cmwuaG9zdCArIHVybC5wYXRobmFtZTtcblxuICBxdWVyeSA9ICdvYmplY3QnID09PSB0eXBlb2YgdXJsLnF1ZXJ5ID8gc3RyaW5naWZ5KHVybC5xdWVyeSkgOiB1cmwucXVlcnk7XG4gIGlmIChxdWVyeSkgcmVzdWx0ICs9ICc/JyAhPT0gcXVlcnkuY2hhckF0KDApID8gJz8nKyBxdWVyeSA6IHF1ZXJ5O1xuXG4gIGlmICh1cmwuaGFzaCkgcmVzdWx0ICs9IHVybC5oYXNoO1xuXG4gIHJldHVybiByZXN1bHQ7XG59O1xuXG4vL1xuLy8gRXhwb3NlIHRoZSBVUkwgcGFyc2VyIGFuZCBzb21lIGFkZGl0aW9uYWwgcHJvcGVydGllcyB0aGF0IG1pZ2h0IGJlIHVzZWZ1bCBmb3Jcbi8vIG90aGVycyBvciB0ZXN0aW5nLlxuLy9cblVSTC5leHRyYWN0UHJvdG9jb2wgPSBleHRyYWN0UHJvdG9jb2w7XG5VUkwubG9jYXRpb24gPSBsb2xjYXRpb247XG5VUkwucXMgPSBxcztcblxubW9kdWxlLmV4cG9ydHMgPSBVUkw7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBzbGFzaGVzID0gL15bQS1aYS16XVtBLVphLXowLTkrLS5dKjpcXC9cXC8vO1xuXG4vKipcbiAqIFRoZXNlIHByb3BlcnRpZXMgc2hvdWxkIG5vdCBiZSBjb3BpZWQgb3IgaW5oZXJpdGVkIGZyb20uIFRoaXMgaXMgb25seSBuZWVkZWRcbiAqIGZvciBhbGwgbm9uIGJsb2IgVVJMJ3MgYXMgYSBibG9iIFVSTCBkb2VzIG5vdCBpbmNsdWRlIGEgaGFzaCwgb25seSB0aGVcbiAqIG9yaWdpbi5cbiAqXG4gKiBAdHlwZSB7T2JqZWN0fVxuICogQHByaXZhdGVcbiAqL1xudmFyIGlnbm9yZSA9IHsgaGFzaDogMSwgcXVlcnk6IDEgfVxuICAsIFVSTDtcblxuLyoqXG4gKiBUaGUgbG9jYXRpb24gb2JqZWN0IGRpZmZlcnMgd2hlbiB5b3VyIGNvZGUgaXMgbG9hZGVkIHRocm91Z2ggYSBub3JtYWwgcGFnZSxcbiAqIFdvcmtlciBvciB0aHJvdWdoIGEgd29ya2VyIHVzaW5nIGEgYmxvYi4gQW5kIHdpdGggdGhlIGJsb2JibGUgYmVnaW5zIHRoZVxuICogdHJvdWJsZSBhcyB0aGUgbG9jYXRpb24gb2JqZWN0IHdpbGwgY29udGFpbiB0aGUgVVJMIG9mIHRoZSBibG9iLCBub3QgdGhlXG4gKiBsb2NhdGlvbiBvZiB0aGUgcGFnZSB3aGVyZSBvdXIgY29kZSBpcyBsb2FkZWQgaW4uIFRoZSBhY3R1YWwgb3JpZ2luIGlzXG4gKiBlbmNvZGVkIGluIHRoZSBgcGF0aG5hbWVgIHNvIHdlIGNhbiB0aGFua2Z1bGx5IGdlbmVyYXRlIGEgZ29vZCBcImRlZmF1bHRcIlxuICogbG9jYXRpb24gZnJvbSBpdCBzbyB3ZSBjYW4gZ2VuZXJhdGUgcHJvcGVyIHJlbGF0aXZlIFVSTCdzIGFnYWluLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fFN0cmluZ30gbG9jIE9wdGlvbmFsIGRlZmF1bHQgbG9jYXRpb24gb2JqZWN0LlxuICogQHJldHVybnMge09iamVjdH0gbG9sY2F0aW9uIG9iamVjdC5cbiAqIEBhcGkgcHVibGljXG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gbG9sY2F0aW9uKGxvYykge1xuICBsb2MgPSBsb2MgfHwgZ2xvYmFsLmxvY2F0aW9uIHx8IHt9O1xuICBVUkwgPSBVUkwgfHwgcmVxdWlyZSgnLi8nKTtcblxuICB2YXIgZmluYWxkZXN0aW5hdGlvbiA9IHt9XG4gICAgLCB0eXBlID0gdHlwZW9mIGxvY1xuICAgICwga2V5O1xuXG4gIGlmICgnYmxvYjonID09PSBsb2MucHJvdG9jb2wpIHtcbiAgICBmaW5hbGRlc3RpbmF0aW9uID0gbmV3IFVSTCh1bmVzY2FwZShsb2MucGF0aG5hbWUpLCB7fSk7XG4gIH0gZWxzZSBpZiAoJ3N0cmluZycgPT09IHR5cGUpIHtcbiAgICBmaW5hbGRlc3RpbmF0aW9uID0gbmV3IFVSTChsb2MsIHt9KTtcbiAgICBmb3IgKGtleSBpbiBpZ25vcmUpIGRlbGV0ZSBmaW5hbGRlc3RpbmF0aW9uW2tleV07XG4gIH0gZWxzZSBpZiAoJ29iamVjdCcgPT09IHR5cGUpIHtcbiAgICBmb3IgKGtleSBpbiBsb2MpIHtcbiAgICAgIGlmIChrZXkgaW4gaWdub3JlKSBjb250aW51ZTtcbiAgICAgIGZpbmFsZGVzdGluYXRpb25ba2V5XSA9IGxvY1trZXldO1xuICAgIH1cblxuICAgIGlmIChmaW5hbGRlc3RpbmF0aW9uLnNsYXNoZXMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgZmluYWxkZXN0aW5hdGlvbi5zbGFzaGVzID0gc2xhc2hlcy50ZXN0KGxvYy5ocmVmKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gZmluYWxkZXN0aW5hdGlvbjtcbn07XG4iLCIoZnVuY3Rpb24gKHJvb3QsIGZhY3RvcnkpIHtcbiAgICBpZiAodHlwZW9mIGV4cG9ydHMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeSgpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XG4gICAgICAgIGRlZmluZShbXSwgZmFjdG9yeSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgcm9vdC51cmx0ZW1wbGF0ZSA9IGZhY3RvcnkoKTtcbiAgICB9XG59KHRoaXMsIGZ1bmN0aW9uICgpIHtcbiAgLyoqXG4gICAqIEBjb25zdHJ1Y3RvclxuICAgKi9cbiAgZnVuY3Rpb24gVXJsVGVtcGxhdGUoKSB7XG4gIH1cblxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN0clxuICAgKiBAcmV0dXJuIHtzdHJpbmd9XG4gICAqL1xuICBVcmxUZW1wbGF0ZS5wcm90b3R5cGUuZW5jb2RlUmVzZXJ2ZWQgPSBmdW5jdGlvbiAoc3RyKSB7XG4gICAgcmV0dXJuIHN0ci5zcGxpdCgvKCVbMC05QS1GYS1mXXsyfSkvZykubWFwKGZ1bmN0aW9uIChwYXJ0KSB7XG4gICAgICBpZiAoIS8lWzAtOUEtRmEtZl0vLnRlc3QocGFydCkpIHtcbiAgICAgICAgcGFydCA9IGVuY29kZVVSSShwYXJ0KS5yZXBsYWNlKC8lNUIvZywgJ1snKS5yZXBsYWNlKC8lNUQvZywgJ10nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBwYXJ0O1xuICAgIH0pLmpvaW4oJycpO1xuICB9O1xuXG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3RyXG4gICAqIEByZXR1cm4ge3N0cmluZ31cbiAgICovXG4gIFVybFRlbXBsYXRlLnByb3RvdHlwZS5lbmNvZGVVbnJlc2VydmVkID0gZnVuY3Rpb24gKHN0cikge1xuICAgIHJldHVybiBlbmNvZGVVUklDb21wb25lbnQoc3RyKS5yZXBsYWNlKC9bIScoKSpdL2csIGZ1bmN0aW9uIChjKSB7XG4gICAgICByZXR1cm4gJyUnICsgYy5jaGFyQ29kZUF0KDApLnRvU3RyaW5nKDE2KS50b1VwcGVyQ2FzZSgpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwcml2YXRlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBvcGVyYXRvclxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcbiAgICogQHBhcmFtIHtzdHJpbmd9IGtleVxuICAgKiBAcmV0dXJuIHtzdHJpbmd9XG4gICAqL1xuICBVcmxUZW1wbGF0ZS5wcm90b3R5cGUuZW5jb2RlVmFsdWUgPSBmdW5jdGlvbiAob3BlcmF0b3IsIHZhbHVlLCBrZXkpIHtcbiAgICB2YWx1ZSA9IChvcGVyYXRvciA9PT0gJysnIHx8IG9wZXJhdG9yID09PSAnIycpID8gdGhpcy5lbmNvZGVSZXNlcnZlZCh2YWx1ZSkgOiB0aGlzLmVuY29kZVVucmVzZXJ2ZWQodmFsdWUpO1xuXG4gICAgaWYgKGtleSkge1xuICAgICAgcmV0dXJuIHRoaXMuZW5jb2RlVW5yZXNlcnZlZChrZXkpICsgJz0nICsgdmFsdWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB2YWx1ZTtcbiAgICB9XG4gIH07XG5cbiAgLyoqXG4gICAqIEBwcml2YXRlXG4gICAqIEBwYXJhbSB7Kn0gdmFsdWVcbiAgICogQHJldHVybiB7Ym9vbGVhbn1cbiAgICovXG4gIFVybFRlbXBsYXRlLnByb3RvdHlwZS5pc0RlZmluZWQgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICByZXR1cm4gdmFsdWUgIT09IHVuZGVmaW5lZCAmJiB2YWx1ZSAhPT0gbnVsbDtcbiAgfTtcblxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtzdHJpbmd9XG4gICAqIEByZXR1cm4ge2Jvb2xlYW59XG4gICAqL1xuICBVcmxUZW1wbGF0ZS5wcm90b3R5cGUuaXNLZXlPcGVyYXRvciA9IGZ1bmN0aW9uIChvcGVyYXRvcikge1xuICAgIHJldHVybiBvcGVyYXRvciA9PT0gJzsnIHx8IG9wZXJhdG9yID09PSAnJicgfHwgb3BlcmF0b3IgPT09ICc/JztcbiAgfTtcblxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IG9wZXJhdG9yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IG1vZGlmaWVyXG4gICAqL1xuICBVcmxUZW1wbGF0ZS5wcm90b3R5cGUuZ2V0VmFsdWVzID0gZnVuY3Rpb24gKGNvbnRleHQsIG9wZXJhdG9yLCBrZXksIG1vZGlmaWVyKSB7XG4gICAgdmFyIHZhbHVlID0gY29udGV4dFtrZXldLFxuICAgICAgICByZXN1bHQgPSBbXTtcblxuICAgIGlmICh0aGlzLmlzRGVmaW5lZCh2YWx1ZSkgJiYgdmFsdWUgIT09ICcnKSB7XG4gICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyB8fCB0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInIHx8IHR5cGVvZiB2YWx1ZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgICAgIHZhbHVlID0gdmFsdWUudG9TdHJpbmcoKTtcblxuICAgICAgICBpZiAobW9kaWZpZXIgJiYgbW9kaWZpZXIgIT09ICcqJykge1xuICAgICAgICAgIHZhbHVlID0gdmFsdWUuc3Vic3RyaW5nKDAsIHBhcnNlSW50KG1vZGlmaWVyLCAxMCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmVzdWx0LnB1c2godGhpcy5lbmNvZGVWYWx1ZShvcGVyYXRvciwgdmFsdWUsIHRoaXMuaXNLZXlPcGVyYXRvcihvcGVyYXRvcikgPyBrZXkgOiBudWxsKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAobW9kaWZpZXIgPT09ICcqJykge1xuICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgICAgICAgdmFsdWUuZmlsdGVyKHRoaXMuaXNEZWZpbmVkKS5mb3JFYWNoKGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICByZXN1bHQucHVzaCh0aGlzLmVuY29kZVZhbHVlKG9wZXJhdG9yLCB2YWx1ZSwgdGhpcy5pc0tleU9wZXJhdG9yKG9wZXJhdG9yKSA/IGtleSA6IG51bGwpKTtcbiAgICAgICAgICAgIH0sIHRoaXMpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBPYmplY3Qua2V5cyh2YWx1ZSkuZm9yRWFjaChmdW5jdGlvbiAoaykge1xuICAgICAgICAgICAgICBpZiAodGhpcy5pc0RlZmluZWQodmFsdWVba10pKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2godGhpcy5lbmNvZGVWYWx1ZShvcGVyYXRvciwgdmFsdWVba10sIGspKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgdGhpcyk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhciB0bXAgPSBbXTtcblxuICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgICAgICAgdmFsdWUuZmlsdGVyKHRoaXMuaXNEZWZpbmVkKS5mb3JFYWNoKGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICB0bXAucHVzaCh0aGlzLmVuY29kZVZhbHVlKG9wZXJhdG9yLCB2YWx1ZSkpO1xuICAgICAgICAgICAgfSwgdGhpcyk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHZhbHVlKS5mb3JFYWNoKGZ1bmN0aW9uIChrKSB7XG4gICAgICAgICAgICAgIGlmICh0aGlzLmlzRGVmaW5lZCh2YWx1ZVtrXSkpIHtcbiAgICAgICAgICAgICAgICB0bXAucHVzaCh0aGlzLmVuY29kZVVucmVzZXJ2ZWQoaykpO1xuICAgICAgICAgICAgICAgIHRtcC5wdXNoKHRoaXMuZW5jb2RlVmFsdWUob3BlcmF0b3IsIHZhbHVlW2tdLnRvU3RyaW5nKCkpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgdGhpcyk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKHRoaXMuaXNLZXlPcGVyYXRvcihvcGVyYXRvcikpIHtcbiAgICAgICAgICAgIHJlc3VsdC5wdXNoKHRoaXMuZW5jb2RlVW5yZXNlcnZlZChrZXkpICsgJz0nICsgdG1wLmpvaW4oJywnKSk7XG4gICAgICAgICAgfSBlbHNlIGlmICh0bXAubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICByZXN1bHQucHVzaCh0bXAuam9pbignLCcpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKG9wZXJhdG9yID09PSAnOycpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNEZWZpbmVkKHZhbHVlKSkge1xuICAgICAgICAgIHJlc3VsdC5wdXNoKHRoaXMuZW5jb2RlVW5yZXNlcnZlZChrZXkpKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh2YWx1ZSA9PT0gJycgJiYgKG9wZXJhdG9yID09PSAnJicgfHwgb3BlcmF0b3IgPT09ICc/JykpIHtcbiAgICAgICAgcmVzdWx0LnB1c2godGhpcy5lbmNvZGVVbnJlc2VydmVkKGtleSkgKyAnPScpO1xuICAgICAgfSBlbHNlIGlmICh2YWx1ZSA9PT0gJycpIHtcbiAgICAgICAgcmVzdWx0LnB1c2goJycpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9O1xuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGVtcGxhdGVcbiAgICogQHJldHVybiB7ZnVuY3Rpb24oT2JqZWN0KTpzdHJpbmd9XG4gICAqL1xuICBVcmxUZW1wbGF0ZS5wcm90b3R5cGUucGFyc2UgPSBmdW5jdGlvbiAodGVtcGxhdGUpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgdmFyIG9wZXJhdG9ycyA9IFsnKycsICcjJywgJy4nLCAnLycsICc7JywgJz8nLCAnJiddO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGV4cGFuZDogZnVuY3Rpb24gKGNvbnRleHQpIHtcbiAgICAgICAgcmV0dXJuIHRlbXBsYXRlLnJlcGxhY2UoL1xceyhbXlxce1xcfV0rKVxcfXwoW15cXHtcXH1dKykvZywgZnVuY3Rpb24gKF8sIGV4cHJlc3Npb24sIGxpdGVyYWwpIHtcbiAgICAgICAgICBpZiAoZXhwcmVzc2lvbikge1xuICAgICAgICAgICAgdmFyIG9wZXJhdG9yID0gbnVsbCxcbiAgICAgICAgICAgICAgICB2YWx1ZXMgPSBbXTtcblxuICAgICAgICAgICAgaWYgKG9wZXJhdG9ycy5pbmRleE9mKGV4cHJlc3Npb24uY2hhckF0KDApKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgb3BlcmF0b3IgPSBleHByZXNzaW9uLmNoYXJBdCgwKTtcbiAgICAgICAgICAgICAgZXhwcmVzc2lvbiA9IGV4cHJlc3Npb24uc3Vic3RyKDEpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBleHByZXNzaW9uLnNwbGl0KC8sL2cpLmZvckVhY2goZnVuY3Rpb24gKHZhcmlhYmxlKSB7XG4gICAgICAgICAgICAgIHZhciB0bXAgPSAvKFteOlxcKl0qKSg/OjooXFxkKyl8KFxcKikpPy8uZXhlYyh2YXJpYWJsZSk7XG4gICAgICAgICAgICAgIHZhbHVlcy5wdXNoLmFwcGx5KHZhbHVlcywgdGhhdC5nZXRWYWx1ZXMoY29udGV4dCwgb3BlcmF0b3IsIHRtcFsxXSwgdG1wWzJdIHx8IHRtcFszXSkpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGlmIChvcGVyYXRvciAmJiBvcGVyYXRvciAhPT0gJysnKSB7XG4gICAgICAgICAgICAgIHZhciBzZXBhcmF0b3IgPSAnLCc7XG5cbiAgICAgICAgICAgICAgaWYgKG9wZXJhdG9yID09PSAnPycpIHtcbiAgICAgICAgICAgICAgICBzZXBhcmF0b3IgPSAnJic7XG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAob3BlcmF0b3IgIT09ICcjJykge1xuICAgICAgICAgICAgICAgIHNlcGFyYXRvciA9IG9wZXJhdG9yO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVybiAodmFsdWVzLmxlbmd0aCAhPT0gMCA/IG9wZXJhdG9yIDogJycpICsgdmFsdWVzLmpvaW4oc2VwYXJhdG9yKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHJldHVybiB2YWx1ZXMuam9pbignLCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGhhdC5lbmNvZGVSZXNlcnZlZChsaXRlcmFsKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gIH07XG5cbiAgcmV0dXJuIG5ldyBVcmxUZW1wbGF0ZSgpO1xufSkpO1xuIiwiKGZ1bmN0aW9uKHNlbGYpIHtcbiAgJ3VzZSBzdHJpY3QnO1xuXG4gIGlmIChzZWxmLmZldGNoKSB7XG4gICAgcmV0dXJuXG4gIH1cblxuICB2YXIgc3VwcG9ydCA9IHtcbiAgICBzZWFyY2hQYXJhbXM6ICdVUkxTZWFyY2hQYXJhbXMnIGluIHNlbGYsXG4gICAgaXRlcmFibGU6ICdTeW1ib2wnIGluIHNlbGYgJiYgJ2l0ZXJhdG9yJyBpbiBTeW1ib2wsXG4gICAgYmxvYjogJ0ZpbGVSZWFkZXInIGluIHNlbGYgJiYgJ0Jsb2InIGluIHNlbGYgJiYgKGZ1bmN0aW9uKCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgbmV3IEJsb2IoKVxuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfSBjYXRjaChlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuICAgIH0pKCksXG4gICAgZm9ybURhdGE6ICdGb3JtRGF0YScgaW4gc2VsZixcbiAgICBhcnJheUJ1ZmZlcjogJ0FycmF5QnVmZmVyJyBpbiBzZWxmXG4gIH1cblxuICBpZiAoc3VwcG9ydC5hcnJheUJ1ZmZlcikge1xuICAgIHZhciB2aWV3Q2xhc3NlcyA9IFtcbiAgICAgICdbb2JqZWN0IEludDhBcnJheV0nLFxuICAgICAgJ1tvYmplY3QgVWludDhBcnJheV0nLFxuICAgICAgJ1tvYmplY3QgVWludDhDbGFtcGVkQXJyYXldJyxcbiAgICAgICdbb2JqZWN0IEludDE2QXJyYXldJyxcbiAgICAgICdbb2JqZWN0IFVpbnQxNkFycmF5XScsXG4gICAgICAnW29iamVjdCBJbnQzMkFycmF5XScsXG4gICAgICAnW29iamVjdCBVaW50MzJBcnJheV0nLFxuICAgICAgJ1tvYmplY3QgRmxvYXQzMkFycmF5XScsXG4gICAgICAnW29iamVjdCBGbG9hdDY0QXJyYXldJ1xuICAgIF1cblxuICAgIHZhciBpc0RhdGFWaWV3ID0gZnVuY3Rpb24ob2JqKSB7XG4gICAgICByZXR1cm4gb2JqICYmIERhdGFWaWV3LnByb3RvdHlwZS5pc1Byb3RvdHlwZU9mKG9iailcbiAgICB9XG5cbiAgICB2YXIgaXNBcnJheUJ1ZmZlclZpZXcgPSBBcnJheUJ1ZmZlci5pc1ZpZXcgfHwgZnVuY3Rpb24ob2JqKSB7XG4gICAgICByZXR1cm4gb2JqICYmIHZpZXdDbGFzc2VzLmluZGV4T2YoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikpID4gLTFcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBub3JtYWxpemVOYW1lKG5hbWUpIHtcbiAgICBpZiAodHlwZW9mIG5hbWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICBuYW1lID0gU3RyaW5nKG5hbWUpXG4gICAgfVxuICAgIGlmICgvW15hLXowLTlcXC0jJCUmJyorLlxcXl9gfH5dL2kudGVzdChuYW1lKSkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSW52YWxpZCBjaGFyYWN0ZXIgaW4gaGVhZGVyIGZpZWxkIG5hbWUnKVxuICAgIH1cbiAgICByZXR1cm4gbmFtZS50b0xvd2VyQ2FzZSgpXG4gIH1cblxuICBmdW5jdGlvbiBub3JtYWxpemVWYWx1ZSh2YWx1ZSkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICB2YWx1ZSA9IFN0cmluZyh2YWx1ZSlcbiAgICB9XG4gICAgcmV0dXJuIHZhbHVlXG4gIH1cblxuICAvLyBCdWlsZCBhIGRlc3RydWN0aXZlIGl0ZXJhdG9yIGZvciB0aGUgdmFsdWUgbGlzdFxuICBmdW5jdGlvbiBpdGVyYXRvckZvcihpdGVtcykge1xuICAgIHZhciBpdGVyYXRvciA9IHtcbiAgICAgIG5leHQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdmFsdWUgPSBpdGVtcy5zaGlmdCgpXG4gICAgICAgIHJldHVybiB7ZG9uZTogdmFsdWUgPT09IHVuZGVmaW5lZCwgdmFsdWU6IHZhbHVlfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChzdXBwb3J0Lml0ZXJhYmxlKSB7XG4gICAgICBpdGVyYXRvcltTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBpdGVyYXRvclxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBpdGVyYXRvclxuICB9XG5cbiAgZnVuY3Rpb24gSGVhZGVycyhoZWFkZXJzKSB7XG4gICAgdGhpcy5tYXAgPSB7fVxuXG4gICAgaWYgKGhlYWRlcnMgaW5zdGFuY2VvZiBIZWFkZXJzKSB7XG4gICAgICBoZWFkZXJzLmZvckVhY2goZnVuY3Rpb24odmFsdWUsIG5hbWUpIHtcbiAgICAgICAgdGhpcy5hcHBlbmQobmFtZSwgdmFsdWUpXG4gICAgICB9LCB0aGlzKVxuXG4gICAgfSBlbHNlIGlmIChoZWFkZXJzKSB7XG4gICAgICBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhoZWFkZXJzKS5mb3JFYWNoKGZ1bmN0aW9uKG5hbWUpIHtcbiAgICAgICAgdGhpcy5hcHBlbmQobmFtZSwgaGVhZGVyc1tuYW1lXSlcbiAgICAgIH0sIHRoaXMpXG4gICAgfVxuICB9XG5cbiAgSGVhZGVycy5wcm90b3R5cGUuYXBwZW5kID0gZnVuY3Rpb24obmFtZSwgdmFsdWUpIHtcbiAgICBuYW1lID0gbm9ybWFsaXplTmFtZShuYW1lKVxuICAgIHZhbHVlID0gbm9ybWFsaXplVmFsdWUodmFsdWUpXG4gICAgdmFyIG9sZFZhbHVlID0gdGhpcy5tYXBbbmFtZV1cbiAgICB0aGlzLm1hcFtuYW1lXSA9IG9sZFZhbHVlID8gb2xkVmFsdWUrJywnK3ZhbHVlIDogdmFsdWVcbiAgfVxuXG4gIEhlYWRlcnMucHJvdG90eXBlWydkZWxldGUnXSA9IGZ1bmN0aW9uKG5hbWUpIHtcbiAgICBkZWxldGUgdGhpcy5tYXBbbm9ybWFsaXplTmFtZShuYW1lKV1cbiAgfVxuXG4gIEhlYWRlcnMucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uKG5hbWUpIHtcbiAgICBuYW1lID0gbm9ybWFsaXplTmFtZShuYW1lKVxuICAgIHJldHVybiB0aGlzLmhhcyhuYW1lKSA/IHRoaXMubWFwW25hbWVdIDogbnVsbFxuICB9XG5cbiAgSGVhZGVycy5wcm90b3R5cGUuaGFzID0gZnVuY3Rpb24obmFtZSkge1xuICAgIHJldHVybiB0aGlzLm1hcC5oYXNPd25Qcm9wZXJ0eShub3JtYWxpemVOYW1lKG5hbWUpKVxuICB9XG5cbiAgSGVhZGVycy5wcm90b3R5cGUuc2V0ID0gZnVuY3Rpb24obmFtZSwgdmFsdWUpIHtcbiAgICB0aGlzLm1hcFtub3JtYWxpemVOYW1lKG5hbWUpXSA9IG5vcm1hbGl6ZVZhbHVlKHZhbHVlKVxuICB9XG5cbiAgSGVhZGVycy5wcm90b3R5cGUuZm9yRWFjaCA9IGZ1bmN0aW9uKGNhbGxiYWNrLCB0aGlzQXJnKSB7XG4gICAgZm9yICh2YXIgbmFtZSBpbiB0aGlzLm1hcCkge1xuICAgICAgaWYgKHRoaXMubWFwLmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICAgIGNhbGxiYWNrLmNhbGwodGhpc0FyZywgdGhpcy5tYXBbbmFtZV0sIG5hbWUsIHRoaXMpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgSGVhZGVycy5wcm90b3R5cGUua2V5cyA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBpdGVtcyA9IFtdXG4gICAgdGhpcy5mb3JFYWNoKGZ1bmN0aW9uKHZhbHVlLCBuYW1lKSB7IGl0ZW1zLnB1c2gobmFtZSkgfSlcbiAgICByZXR1cm4gaXRlcmF0b3JGb3IoaXRlbXMpXG4gIH1cblxuICBIZWFkZXJzLnByb3RvdHlwZS52YWx1ZXMgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgaXRlbXMgPSBbXVxuICAgIHRoaXMuZm9yRWFjaChmdW5jdGlvbih2YWx1ZSkgeyBpdGVtcy5wdXNoKHZhbHVlKSB9KVxuICAgIHJldHVybiBpdGVyYXRvckZvcihpdGVtcylcbiAgfVxuXG4gIEhlYWRlcnMucHJvdG90eXBlLmVudHJpZXMgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgaXRlbXMgPSBbXVxuICAgIHRoaXMuZm9yRWFjaChmdW5jdGlvbih2YWx1ZSwgbmFtZSkgeyBpdGVtcy5wdXNoKFtuYW1lLCB2YWx1ZV0pIH0pXG4gICAgcmV0dXJuIGl0ZXJhdG9yRm9yKGl0ZW1zKVxuICB9XG5cbiAgaWYgKHN1cHBvcnQuaXRlcmFibGUpIHtcbiAgICBIZWFkZXJzLnByb3RvdHlwZVtTeW1ib2wuaXRlcmF0b3JdID0gSGVhZGVycy5wcm90b3R5cGUuZW50cmllc1xuICB9XG5cbiAgZnVuY3Rpb24gY29uc3VtZWQoYm9keSkge1xuICAgIGlmIChib2R5LmJvZHlVc2VkKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IFR5cGVFcnJvcignQWxyZWFkeSByZWFkJykpXG4gICAgfVxuICAgIGJvZHkuYm9keVVzZWQgPSB0cnVlXG4gIH1cblxuICBmdW5jdGlvbiBmaWxlUmVhZGVyUmVhZHkocmVhZGVyKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgICAgcmVhZGVyLm9ubG9hZCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXNvbHZlKHJlYWRlci5yZXN1bHQpXG4gICAgICB9XG4gICAgICByZWFkZXIub25lcnJvciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZWplY3QocmVhZGVyLmVycm9yKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiByZWFkQmxvYkFzQXJyYXlCdWZmZXIoYmxvYikge1xuICAgIHZhciByZWFkZXIgPSBuZXcgRmlsZVJlYWRlcigpXG4gICAgdmFyIHByb21pc2UgPSBmaWxlUmVhZGVyUmVhZHkocmVhZGVyKVxuICAgIHJlYWRlci5yZWFkQXNBcnJheUJ1ZmZlcihibG9iKVxuICAgIHJldHVybiBwcm9taXNlXG4gIH1cblxuICBmdW5jdGlvbiByZWFkQmxvYkFzVGV4dChibG9iKSB7XG4gICAgdmFyIHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKClcbiAgICB2YXIgcHJvbWlzZSA9IGZpbGVSZWFkZXJSZWFkeShyZWFkZXIpXG4gICAgcmVhZGVyLnJlYWRBc1RleHQoYmxvYilcbiAgICByZXR1cm4gcHJvbWlzZVxuICB9XG5cbiAgZnVuY3Rpb24gYnVmZmVyQ2xvbmUoYnVmKSB7XG4gICAgaWYgKGJ1Zi5zbGljZSkge1xuICAgICAgcmV0dXJuIGJ1Zi5zbGljZSgwKVxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdmlldyA9IG5ldyBVaW50OEFycmF5KGJ1Zi5ieXRlTGVuZ3RoKVxuICAgICAgdmlldy5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmKSlcbiAgICAgIHJldHVybiB2aWV3LmJ1ZmZlclxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIEJvZHkoKSB7XG4gICAgdGhpcy5ib2R5VXNlZCA9IGZhbHNlXG5cbiAgICB0aGlzLl9pbml0Qm9keSA9IGZ1bmN0aW9uKGJvZHkpIHtcbiAgICAgIHRoaXMuX2JvZHlJbml0ID0gYm9keVxuICAgICAgaWYgKCFib2R5KSB7XG4gICAgICAgIHRoaXMuX2JvZHlUZXh0ID0gJydcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGJvZHkgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHRoaXMuX2JvZHlUZXh0ID0gYm9keVxuICAgICAgfSBlbHNlIGlmIChzdXBwb3J0LmJsb2IgJiYgQmxvYi5wcm90b3R5cGUuaXNQcm90b3R5cGVPZihib2R5KSkge1xuICAgICAgICB0aGlzLl9ib2R5QmxvYiA9IGJvZHlcbiAgICAgIH0gZWxzZSBpZiAoc3VwcG9ydC5mb3JtRGF0YSAmJiBGb3JtRGF0YS5wcm90b3R5cGUuaXNQcm90b3R5cGVPZihib2R5KSkge1xuICAgICAgICB0aGlzLl9ib2R5Rm9ybURhdGEgPSBib2R5XG4gICAgICB9IGVsc2UgaWYgKHN1cHBvcnQuc2VhcmNoUGFyYW1zICYmIFVSTFNlYXJjaFBhcmFtcy5wcm90b3R5cGUuaXNQcm90b3R5cGVPZihib2R5KSkge1xuICAgICAgICB0aGlzLl9ib2R5VGV4dCA9IGJvZHkudG9TdHJpbmcoKVxuICAgICAgfSBlbHNlIGlmIChzdXBwb3J0LmFycmF5QnVmZmVyICYmIHN1cHBvcnQuYmxvYiAmJiBpc0RhdGFWaWV3KGJvZHkpKSB7XG4gICAgICAgIHRoaXMuX2JvZHlBcnJheUJ1ZmZlciA9IGJ1ZmZlckNsb25lKGJvZHkuYnVmZmVyKVxuICAgICAgICAvLyBJRSAxMC0xMSBjYW4ndCBoYW5kbGUgYSBEYXRhVmlldyBib2R5LlxuICAgICAgICB0aGlzLl9ib2R5SW5pdCA9IG5ldyBCbG9iKFt0aGlzLl9ib2R5QXJyYXlCdWZmZXJdKVxuICAgICAgfSBlbHNlIGlmIChzdXBwb3J0LmFycmF5QnVmZmVyICYmIChBcnJheUJ1ZmZlci5wcm90b3R5cGUuaXNQcm90b3R5cGVPZihib2R5KSB8fCBpc0FycmF5QnVmZmVyVmlldyhib2R5KSkpIHtcbiAgICAgICAgdGhpcy5fYm9keUFycmF5QnVmZmVyID0gYnVmZmVyQ2xvbmUoYm9keSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigndW5zdXBwb3J0ZWQgQm9keUluaXQgdHlwZScpXG4gICAgICB9XG5cbiAgICAgIGlmICghdGhpcy5oZWFkZXJzLmdldCgnY29udGVudC10eXBlJykpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBib2R5ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgIHRoaXMuaGVhZGVycy5zZXQoJ2NvbnRlbnQtdHlwZScsICd0ZXh0L3BsYWluO2NoYXJzZXQ9VVRGLTgnKVxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuX2JvZHlCbG9iICYmIHRoaXMuX2JvZHlCbG9iLnR5cGUpIHtcbiAgICAgICAgICB0aGlzLmhlYWRlcnMuc2V0KCdjb250ZW50LXR5cGUnLCB0aGlzLl9ib2R5QmxvYi50eXBlKVxuICAgICAgICB9IGVsc2UgaWYgKHN1cHBvcnQuc2VhcmNoUGFyYW1zICYmIFVSTFNlYXJjaFBhcmFtcy5wcm90b3R5cGUuaXNQcm90b3R5cGVPZihib2R5KSkge1xuICAgICAgICAgIHRoaXMuaGVhZGVycy5zZXQoJ2NvbnRlbnQtdHlwZScsICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7Y2hhcnNldD1VVEYtOCcpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoc3VwcG9ydC5ibG9iKSB7XG4gICAgICB0aGlzLmJsb2IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHJlamVjdGVkID0gY29uc3VtZWQodGhpcylcbiAgICAgICAgaWYgKHJlamVjdGVkKSB7XG4gICAgICAgICAgcmV0dXJuIHJlamVjdGVkXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5fYm9keUJsb2IpIHtcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHRoaXMuX2JvZHlCbG9iKVxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuX2JvZHlBcnJheUJ1ZmZlcikge1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobmV3IEJsb2IoW3RoaXMuX2JvZHlBcnJheUJ1ZmZlcl0pKVxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuX2JvZHlGb3JtRGF0YSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignY291bGQgbm90IHJlYWQgRm9ybURhdGEgYm9keSBhcyBibG9iJylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG5ldyBCbG9iKFt0aGlzLl9ib2R5VGV4dF0pKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy50ZXh0ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgcmVqZWN0ZWQgPSBjb25zdW1lZCh0aGlzKVxuICAgICAgaWYgKHJlamVjdGVkKSB7XG4gICAgICAgIHJldHVybiByZWplY3RlZFxuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5fYm9keUJsb2IpIHtcbiAgICAgICAgcmV0dXJuIHJlYWRCbG9iQXNUZXh0KHRoaXMuX2JvZHlCbG9iKVxuICAgICAgfSBlbHNlIGlmICh0aGlzLl9ib2R5QXJyYXlCdWZmZXIpIHtcbiAgICAgICAgdmFyIHZpZXcgPSBuZXcgVWludDhBcnJheSh0aGlzLl9ib2R5QXJyYXlCdWZmZXIpXG4gICAgICAgIHZhciBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIHZpZXcpXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoc3RyKVxuICAgICAgfSBlbHNlIGlmICh0aGlzLl9ib2R5Rm9ybURhdGEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjb3VsZCBub3QgcmVhZCBGb3JtRGF0YSBib2R5IGFzIHRleHQnKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0aGlzLl9ib2R5VGV4dClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoc3VwcG9ydC5hcnJheUJ1ZmZlcikge1xuICAgICAgdGhpcy5hcnJheUJ1ZmZlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAodGhpcy5fYm9keUFycmF5QnVmZmVyKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbnN1bWVkKHRoaXMpIHx8IFByb21pc2UucmVzb2x2ZSh0aGlzLl9ib2R5QXJyYXlCdWZmZXIpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuYmxvYigpLnRoZW4ocmVhZEJsb2JBc0FycmF5QnVmZmVyKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHN1cHBvcnQuZm9ybURhdGEpIHtcbiAgICAgIHRoaXMuZm9ybURhdGEgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudGV4dCgpLnRoZW4oZGVjb2RlKVxuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuanNvbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMudGV4dCgpLnRoZW4oSlNPTi5wYXJzZSlcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpc1xuICB9XG5cbiAgLy8gSFRUUCBtZXRob2RzIHdob3NlIGNhcGl0YWxpemF0aW9uIHNob3VsZCBiZSBub3JtYWxpemVkXG4gIHZhciBtZXRob2RzID0gWydERUxFVEUnLCAnR0VUJywgJ0hFQUQnLCAnT1BUSU9OUycsICdQT1NUJywgJ1BVVCddXG5cbiAgZnVuY3Rpb24gbm9ybWFsaXplTWV0aG9kKG1ldGhvZCkge1xuICAgIHZhciB1cGNhc2VkID0gbWV0aG9kLnRvVXBwZXJDYXNlKClcbiAgICByZXR1cm4gKG1ldGhvZHMuaW5kZXhPZih1cGNhc2VkKSA+IC0xKSA/IHVwY2FzZWQgOiBtZXRob2RcbiAgfVxuXG4gIGZ1bmN0aW9uIFJlcXVlc3QoaW5wdXQsIG9wdGlvbnMpIHtcbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fVxuICAgIHZhciBib2R5ID0gb3B0aW9ucy5ib2R5XG5cbiAgICBpZiAodHlwZW9mIGlucHV0ID09PSAnc3RyaW5nJykge1xuICAgICAgdGhpcy51cmwgPSBpbnB1dFxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoaW5wdXQuYm9keVVzZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQWxyZWFkeSByZWFkJylcbiAgICAgIH1cbiAgICAgIHRoaXMudXJsID0gaW5wdXQudXJsXG4gICAgICB0aGlzLmNyZWRlbnRpYWxzID0gaW5wdXQuY3JlZGVudGlhbHNcbiAgICAgIGlmICghb3B0aW9ucy5oZWFkZXJzKSB7XG4gICAgICAgIHRoaXMuaGVhZGVycyA9IG5ldyBIZWFkZXJzKGlucHV0LmhlYWRlcnMpXG4gICAgICB9XG4gICAgICB0aGlzLm1ldGhvZCA9IGlucHV0Lm1ldGhvZFxuICAgICAgdGhpcy5tb2RlID0gaW5wdXQubW9kZVxuICAgICAgaWYgKCFib2R5ICYmIGlucHV0Ll9ib2R5SW5pdCAhPSBudWxsKSB7XG4gICAgICAgIGJvZHkgPSBpbnB1dC5fYm9keUluaXRcbiAgICAgICAgaW5wdXQuYm9keVVzZWQgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5jcmVkZW50aWFscyA9IG9wdGlvbnMuY3JlZGVudGlhbHMgfHwgdGhpcy5jcmVkZW50aWFscyB8fCAnb21pdCdcbiAgICBpZiAob3B0aW9ucy5oZWFkZXJzIHx8ICF0aGlzLmhlYWRlcnMpIHtcbiAgICAgIHRoaXMuaGVhZGVycyA9IG5ldyBIZWFkZXJzKG9wdGlvbnMuaGVhZGVycylcbiAgICB9XG4gICAgdGhpcy5tZXRob2QgPSBub3JtYWxpemVNZXRob2Qob3B0aW9ucy5tZXRob2QgfHwgdGhpcy5tZXRob2QgfHwgJ0dFVCcpXG4gICAgdGhpcy5tb2RlID0gb3B0aW9ucy5tb2RlIHx8IHRoaXMubW9kZSB8fCBudWxsXG4gICAgdGhpcy5yZWZlcnJlciA9IG51bGxcblxuICAgIGlmICgodGhpcy5tZXRob2QgPT09ICdHRVQnIHx8IHRoaXMubWV0aG9kID09PSAnSEVBRCcpICYmIGJvZHkpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0JvZHkgbm90IGFsbG93ZWQgZm9yIEdFVCBvciBIRUFEIHJlcXVlc3RzJylcbiAgICB9XG4gICAgdGhpcy5faW5pdEJvZHkoYm9keSlcbiAgfVxuXG4gIFJlcXVlc3QucHJvdG90eXBlLmNsb25lID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIG5ldyBSZXF1ZXN0KHRoaXMsIHsgYm9keTogdGhpcy5fYm9keUluaXQgfSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlY29kZShib2R5KSB7XG4gICAgdmFyIGZvcm0gPSBuZXcgRm9ybURhdGEoKVxuICAgIGJvZHkudHJpbSgpLnNwbGl0KCcmJykuZm9yRWFjaChmdW5jdGlvbihieXRlcykge1xuICAgICAgaWYgKGJ5dGVzKSB7XG4gICAgICAgIHZhciBzcGxpdCA9IGJ5dGVzLnNwbGl0KCc9JylcbiAgICAgICAgdmFyIG5hbWUgPSBzcGxpdC5zaGlmdCgpLnJlcGxhY2UoL1xcKy9nLCAnICcpXG4gICAgICAgIHZhciB2YWx1ZSA9IHNwbGl0LmpvaW4oJz0nKS5yZXBsYWNlKC9cXCsvZywgJyAnKVxuICAgICAgICBmb3JtLmFwcGVuZChkZWNvZGVVUklDb21wb25lbnQobmFtZSksIGRlY29kZVVSSUNvbXBvbmVudCh2YWx1ZSkpXG4gICAgICB9XG4gICAgfSlcbiAgICByZXR1cm4gZm9ybVxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VIZWFkZXJzKHJhd0hlYWRlcnMpIHtcbiAgICB2YXIgaGVhZGVycyA9IG5ldyBIZWFkZXJzKClcbiAgICByYXdIZWFkZXJzLnNwbGl0KCdcXHJcXG4nKS5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3BsaXQoJzonKVxuICAgICAgdmFyIGtleSA9IHBhcnRzLnNoaWZ0KCkudHJpbSgpXG4gICAgICBpZiAoa2V5KSB7XG4gICAgICAgIHZhciB2YWx1ZSA9IHBhcnRzLmpvaW4oJzonKS50cmltKClcbiAgICAgICAgaGVhZGVycy5hcHBlbmQoa2V5LCB2YWx1ZSlcbiAgICAgIH1cbiAgICB9KVxuICAgIHJldHVybiBoZWFkZXJzXG4gIH1cblxuICBCb2R5LmNhbGwoUmVxdWVzdC5wcm90b3R5cGUpXG5cbiAgZnVuY3Rpb24gUmVzcG9uc2UoYm9keUluaXQsIG9wdGlvbnMpIHtcbiAgICBpZiAoIW9wdGlvbnMpIHtcbiAgICAgIG9wdGlvbnMgPSB7fVxuICAgIH1cblxuICAgIHRoaXMudHlwZSA9ICdkZWZhdWx0J1xuICAgIHRoaXMuc3RhdHVzID0gJ3N0YXR1cycgaW4gb3B0aW9ucyA/IG9wdGlvbnMuc3RhdHVzIDogMjAwXG4gICAgdGhpcy5vayA9IHRoaXMuc3RhdHVzID49IDIwMCAmJiB0aGlzLnN0YXR1cyA8IDMwMFxuICAgIHRoaXMuc3RhdHVzVGV4dCA9ICdzdGF0dXNUZXh0JyBpbiBvcHRpb25zID8gb3B0aW9ucy5zdGF0dXNUZXh0IDogJ09LJ1xuICAgIHRoaXMuaGVhZGVycyA9IG5ldyBIZWFkZXJzKG9wdGlvbnMuaGVhZGVycylcbiAgICB0aGlzLnVybCA9IG9wdGlvbnMudXJsIHx8ICcnXG4gICAgdGhpcy5faW5pdEJvZHkoYm9keUluaXQpXG4gIH1cblxuICBCb2R5LmNhbGwoUmVzcG9uc2UucHJvdG90eXBlKVxuXG4gIFJlc3BvbnNlLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBuZXcgUmVzcG9uc2UodGhpcy5fYm9keUluaXQsIHtcbiAgICAgIHN0YXR1czogdGhpcy5zdGF0dXMsXG4gICAgICBzdGF0dXNUZXh0OiB0aGlzLnN0YXR1c1RleHQsXG4gICAgICBoZWFkZXJzOiBuZXcgSGVhZGVycyh0aGlzLmhlYWRlcnMpLFxuICAgICAgdXJsOiB0aGlzLnVybFxuICAgIH0pXG4gIH1cblxuICBSZXNwb25zZS5lcnJvciA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciByZXNwb25zZSA9IG5ldyBSZXNwb25zZShudWxsLCB7c3RhdHVzOiAwLCBzdGF0dXNUZXh0OiAnJ30pXG4gICAgcmVzcG9uc2UudHlwZSA9ICdlcnJvcidcbiAgICByZXR1cm4gcmVzcG9uc2VcbiAgfVxuXG4gIHZhciByZWRpcmVjdFN0YXR1c2VzID0gWzMwMSwgMzAyLCAzMDMsIDMwNywgMzA4XVxuXG4gIFJlc3BvbnNlLnJlZGlyZWN0ID0gZnVuY3Rpb24odXJsLCBzdGF0dXMpIHtcbiAgICBpZiAocmVkaXJlY3RTdGF0dXNlcy5pbmRleE9mKHN0YXR1cykgPT09IC0xKSB7XG4gICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignSW52YWxpZCBzdGF0dXMgY29kZScpXG4gICAgfVxuXG4gICAgcmV0dXJuIG5ldyBSZXNwb25zZShudWxsLCB7c3RhdHVzOiBzdGF0dXMsIGhlYWRlcnM6IHtsb2NhdGlvbjogdXJsfX0pXG4gIH1cblxuICBzZWxmLkhlYWRlcnMgPSBIZWFkZXJzXG4gIHNlbGYuUmVxdWVzdCA9IFJlcXVlc3RcbiAgc2VsZi5SZXNwb25zZSA9IFJlc3BvbnNlXG5cbiAgc2VsZi5mZXRjaCA9IGZ1bmN0aW9uKGlucHV0LCBpbml0KSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgICAgdmFyIHJlcXVlc3QgPSBuZXcgUmVxdWVzdChpbnB1dCwgaW5pdClcbiAgICAgIHZhciB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKVxuXG4gICAgICB4aHIub25sb2FkID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBvcHRpb25zID0ge1xuICAgICAgICAgIHN0YXR1czogeGhyLnN0YXR1cyxcbiAgICAgICAgICBzdGF0dXNUZXh0OiB4aHIuc3RhdHVzVGV4dCxcbiAgICAgICAgICBoZWFkZXJzOiBwYXJzZUhlYWRlcnMoeGhyLmdldEFsbFJlc3BvbnNlSGVhZGVycygpIHx8ICcnKVxuICAgICAgICB9XG4gICAgICAgIG9wdGlvbnMudXJsID0gJ3Jlc3BvbnNlVVJMJyBpbiB4aHIgPyB4aHIucmVzcG9uc2VVUkwgOiBvcHRpb25zLmhlYWRlcnMuZ2V0KCdYLVJlcXVlc3QtVVJMJylcbiAgICAgICAgdmFyIGJvZHkgPSAncmVzcG9uc2UnIGluIHhociA/IHhoci5yZXNwb25zZSA6IHhoci5yZXNwb25zZVRleHRcbiAgICAgICAgcmVzb2x2ZShuZXcgUmVzcG9uc2UoYm9keSwgb3B0aW9ucykpXG4gICAgICB9XG5cbiAgICAgIHhoci5vbmVycm9yID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJlamVjdChuZXcgVHlwZUVycm9yKCdOZXR3b3JrIHJlcXVlc3QgZmFpbGVkJykpXG4gICAgICB9XG5cbiAgICAgIHhoci5vbnRpbWVvdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmVqZWN0KG5ldyBUeXBlRXJyb3IoJ05ldHdvcmsgcmVxdWVzdCBmYWlsZWQnKSlcbiAgICAgIH1cblxuICAgICAgeGhyLm9wZW4ocmVxdWVzdC5tZXRob2QsIHJlcXVlc3QudXJsLCB0cnVlKVxuXG4gICAgICBpZiAocmVxdWVzdC5jcmVkZW50aWFscyA9PT0gJ2luY2x1ZGUnKSB7XG4gICAgICAgIHhoci53aXRoQ3JlZGVudGlhbHMgPSB0cnVlXG4gICAgICB9XG5cbiAgICAgIGlmICgncmVzcG9uc2VUeXBlJyBpbiB4aHIgJiYgc3VwcG9ydC5ibG9iKSB7XG4gICAgICAgIHhoci5yZXNwb25zZVR5cGUgPSAnYmxvYidcbiAgICAgIH1cblxuICAgICAgcmVxdWVzdC5oZWFkZXJzLmZvckVhY2goZnVuY3Rpb24odmFsdWUsIG5hbWUpIHtcbiAgICAgICAgeGhyLnNldFJlcXVlc3RIZWFkZXIobmFtZSwgdmFsdWUpXG4gICAgICB9KVxuXG4gICAgICB4aHIuc2VuZCh0eXBlb2YgcmVxdWVzdC5fYm9keUluaXQgPT09ICd1bmRlZmluZWQnID8gbnVsbCA6IHJlcXVlc3QuX2JvZHlJbml0KVxuICAgIH0pXG4gIH1cbiAgc2VsZi5mZXRjaC5wb2x5ZmlsbCA9IHRydWVcbn0pKHR5cGVvZiBzZWxmICE9PSAndW5kZWZpbmVkJyA/IHNlbGYgOiB0aGlzKTtcbiJdfQ== \ No newline at end of file diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/csrf.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/csrf.js new file mode 100644 index 0000000..6e4bf39 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/csrf.js @@ -0,0 +1,52 @@ +function getCookie(name) { + var cookieValue = null; + + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + + return cookieValue; +} + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} + +function sameOrigin(url) { + // test that a given url is a same-origin URL + // url could be relative or scheme relative or absolute + var host = document.location.host; // host + port + var protocol = document.location.protocol; + var sr_origin = '//' + host; + var origin = protocol + sr_origin; + + // Allow absolute or scheme relative URLs to same origin + return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || + (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || + // or any other URL that isn't scheme relative or absolute i.e relative. + !(/^(\/\/|http:|https:).*/.test(url)); +} + +var csrftoken = window.drf.csrfToken; + +$.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { + // Send the token to same-origin, relative URLs only. + // Send the token only if the method warrants CSRF protection + // Using the CSRFToken value acquired earlier + xhr.setRequestHeader(window.drf.csrfHeaderName, csrftoken); + } + } +}); diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/default.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/default.js new file mode 100644 index 0000000..bec2e4f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/default.js @@ -0,0 +1,47 @@ +$(document).ready(function() { + // JSON highlighting. + prettyPrint(); + + // Bootstrap tooltips. + $('.js-tooltip').tooltip({ + delay: 1000, + container: 'body' + }); + + // Deal with rounded tab styling after tab clicks. + $('a[data-toggle="tab"]:first').on('shown', function(e) { + $(e.target).parents('.tabbable').addClass('first-tab-active'); + }); + + $('a[data-toggle="tab"]:not(:first)').on('shown', function(e) { + $(e.target).parents('.tabbable').removeClass('first-tab-active'); + }); + + $('a[data-toggle="tab"]').click(function() { + document.cookie = "tabstyle=" + this.name + "; path=/"; + }); + + // Store tab preference in cookies & display appropriate tab on load. + var selectedTab = null; + var selectedTabName = getCookie('tabstyle'); + + if (selectedTabName) { + selectedTabName = selectedTabName.replace(/[^a-z-]/g, ''); + } + + if (selectedTabName) { + selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); + } + + if (selectedTab && selectedTab.length > 0) { + // Display whichever tab is selected. + selectedTab.tab('show'); + } else { + // If no tab selected, display rightmost tab. + $('.form-switcher a:first').tab('show'); + } + + $(window).on('load', function() { + $('#errorModal').modal('show'); + }); +}); diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/jquery-3.5.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/venv/Lib/site-packages/rest_framework/static/rest_framework/js/prettify-min.js b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/prettify-min.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/static/rest_framework/js/prettify-min.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c< +f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&& +(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r= +{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length, +t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b=== +"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value", +m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m= +a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue= +j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m, +250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit", +PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})(); diff --git a/venv/Lib/site-packages/rest_framework/status.py b/venv/Lib/site-packages/rest_framework/status.py new file mode 100644 index 0000000..2561d76 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/status.py @@ -0,0 +1,89 @@ +""" +Descriptive HTTP status codes, for code readability. + +See RFC 2616 - https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +And RFC 6585 - https://tools.ietf.org/html/rfc6585 +And RFC 4918 - https://tools.ietf.org/html/rfc4918 +""" + + +def is_informational(code): + return 100 <= code <= 199 + + +def is_success(code): + return 200 <= code <= 299 + + +def is_redirect(code): + return 300 <= code <= 399 + + +def is_client_error(code): + return 400 <= code <= 499 + + +def is_server_error(code): + return 500 <= code <= 599 + + +HTTP_100_CONTINUE = 100 +HTTP_101_SWITCHING_PROTOCOLS = 101 +HTTP_200_OK = 200 +HTTP_201_CREATED = 201 +HTTP_202_ACCEPTED = 202 +HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203 +HTTP_204_NO_CONTENT = 204 +HTTP_205_RESET_CONTENT = 205 +HTTP_206_PARTIAL_CONTENT = 206 +HTTP_207_MULTI_STATUS = 207 +HTTP_208_ALREADY_REPORTED = 208 +HTTP_226_IM_USED = 226 +HTTP_300_MULTIPLE_CHOICES = 300 +HTTP_301_MOVED_PERMANENTLY = 301 +HTTP_302_FOUND = 302 +HTTP_303_SEE_OTHER = 303 +HTTP_304_NOT_MODIFIED = 304 +HTTP_305_USE_PROXY = 305 +HTTP_306_RESERVED = 306 +HTTP_307_TEMPORARY_REDIRECT = 307 +HTTP_308_PERMANENT_REDIRECT = 308 +HTTP_400_BAD_REQUEST = 400 +HTTP_401_UNAUTHORIZED = 401 +HTTP_402_PAYMENT_REQUIRED = 402 +HTTP_403_FORBIDDEN = 403 +HTTP_404_NOT_FOUND = 404 +HTTP_405_METHOD_NOT_ALLOWED = 405 +HTTP_406_NOT_ACCEPTABLE = 406 +HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407 +HTTP_408_REQUEST_TIMEOUT = 408 +HTTP_409_CONFLICT = 409 +HTTP_410_GONE = 410 +HTTP_411_LENGTH_REQUIRED = 411 +HTTP_412_PRECONDITION_FAILED = 412 +HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413 +HTTP_414_REQUEST_URI_TOO_LONG = 414 +HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 +HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 +HTTP_417_EXPECTATION_FAILED = 417 +HTTP_418_IM_A_TEAPOT = 418 +HTTP_422_UNPROCESSABLE_ENTITY = 422 +HTTP_423_LOCKED = 423 +HTTP_424_FAILED_DEPENDENCY = 424 +HTTP_426_UPGRADE_REQUIRED = 426 +HTTP_428_PRECONDITION_REQUIRED = 428 +HTTP_429_TOO_MANY_REQUESTS = 429 +HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 +HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451 +HTTP_500_INTERNAL_SERVER_ERROR = 500 +HTTP_501_NOT_IMPLEMENTED = 501 +HTTP_502_BAD_GATEWAY = 502 +HTTP_503_SERVICE_UNAVAILABLE = 503 +HTTP_504_GATEWAY_TIMEOUT = 504 +HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505 +HTTP_506_VARIANT_ALSO_NEGOTIATES = 506 +HTTP_507_INSUFFICIENT_STORAGE = 507 +HTTP_508_LOOP_DETECTED = 508 +HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509 +HTTP_510_NOT_EXTENDED = 510 +HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin.html new file mode 100644 index 0000000..1281220 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin.html @@ -0,0 +1,267 @@ +{% load static %} +{% load i18n %} +{% load rest_framework %} + +<!DOCTYPE html> +<html> + <head> + {% block head %} + + {% block meta %} + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <meta name="robots" content="NONE,NOARCHIVE" /> + {% endblock %} + + <title>{% block title %}Django REST framework{% endblock %} + + {% block style %} + {% block bootstrap_theme %} + + + {% endblock %} + + + {% endblock %} + + {% endblock %} + + + {% block body %} + +
+ {% block navbar %} + + {% endblock %} + +
+ {% block breadcrumbs %} + + {% endblock %} + + +
+ {% if 'GET' in allowed_methods %} +
+
+
+ + +
+
+
+ {% endif %} + + {% if post_form %} + + {% endif %} + + {% if put_form %} + + {% endif %} + + {% if delete_form %} +
+ +
+ {% endif %} + + {% if extra_actions %} + + {% endif %} + + {% if filter_form %} + + {% endif %} + +
+ + +
+ {% block description %} + {{ description }} + {% endblock %} +
+ + {% if paginator %} + + {% endif %} + +
+ {% if style == 'list' %} + {% include "rest_framework/admin/list.html" %} + {% else %} + {% include "rest_framework/admin/detail.html" %} + {% endif %} +
+ + {% if paginator %} + + {% endif %} +
+
+ +
+
+ + + + + + + + {% if error_form %} + + + {% endif %} + + {% if filter_form %} + {{ filter_form }} + {% endif %} + + {% block script %} + + + + + + + + + {% endblock %} + + {% endblock %} + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/detail.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/detail.html new file mode 100644 index 0000000..42cd1a3 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/detail.html @@ -0,0 +1,10 @@ +{% load rest_framework %} + + + {% for key, value in results|items %} + {% if key in details %} + + {% endif %} + {% endfor %} + +
{{ key|capfirst }}{{ value|format_value }}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/dict_value.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/dict_value.html new file mode 100644 index 0000000..ef47b72 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/dict_value.html @@ -0,0 +1,11 @@ +{% load rest_framework %} + + + {% for k, v in value|items %} + + + + + {% endfor %} + +
{{ k|format_value }}{{ v|format_value }}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list.html new file mode 100644 index 0000000..ab3e84d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list.html @@ -0,0 +1,26 @@ +{% load rest_framework %} + + + {% for column in columns%}{% endfor %} + + + {% for row in results %} + + {% for key, value in row|items %} + {% if key in columns %} + + {% endif %} + {% endfor %} + + + {% endfor %} + +
{{ column|capfirst }}
+ {{ value|format_value }} + + {% if row.url %} + + {% else %} + + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list_value.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list_value.html new file mode 100644 index 0000000..5bab6b5 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/list_value.html @@ -0,0 +1,11 @@ +{% load rest_framework %} + + + {% for item in value %} + + + + + {% endfor %} + +
{{ forloop.counter0 }}{{ item|format_value }}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/simple_list_value.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/simple_list_value.html new file mode 100644 index 0000000..e378a36 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/admin/simple_list_value.html @@ -0,0 +1,2 @@ +{% load rest_framework %} +{% for item in value %}{% if not forloop.first%},{% endif %} {{item|format_value}}{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/api.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/api.html new file mode 100644 index 0000000..81d277e --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/api.html @@ -0,0 +1,3 @@ +{% extends "rest_framework/base.html" %} + +{# Override this template in your own templates directory to customize #} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/base.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/base.html new file mode 100644 index 0000000..a88e159 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/base.html @@ -0,0 +1,311 @@ +{% load static %} +{% load i18n %} +{% load rest_framework %} + + + + + {% block head %} + + {% block meta %} + + + {% endblock %} + + {% block title %}{% if name %}{{ name }} – {% endif %}Django REST framework{% endblock %} + + {% block style %} + {% block bootstrap_theme %} + + + {% endblock %} + + + + {% if code_style %}{% endif %} + {% endblock %} + + {% endblock %} + + + {% block body %} + + +
+ {% block navbar %} + + {% endblock %} + +
+ {% block breadcrumbs %} + + {% endblock %} + + +
+ {% block content %} + +
+ {% block request_forms %} + + {% if 'GET' in allowed_methods %} +
+
+ {% if api_settings.URL_FORMAT_OVERRIDE %} +
+ GET + + + +
+ {% else %} + GET + {% endif %} +
+
+ {% endif %} + + {% if options_form %} +
+ +
+ {% endif %} + + {% if delete_form %} + + + + + {% endif %} + + {% if extra_actions %} + + {% endif %} + + {% if filter_form %} + + {% endif %} + + {% endblock request_forms %} +
+ +
+ +
+ {% block description %} + {{ description }} + {% endblock %} +
+ + {% if paginator %} + + {% endif %} + +
+
{{ request.method }} {{ request.get_full_path }}
+
+ +
+
HTTP {{ response.status_code }} {{ response.status_text }}{% for key, val in response_headers|items %}
+{{ key }}: {{ val|break_long_headers|urlize }}{% endfor %}
+
+{{ content|urlize }}
+
+
+ + {% if display_edit_forms %} + {% if post_form or raw_data_post_form %} +
+ {% if post_form %} + + {% endif %} + +
+ {% if post_form %} +
+ {% with form=post_form %} +
+
+ {% csrf_token %} + {{ post_form }} +
+ +
+
+
+ {% endwith %} +
+ {% endif %} + +
+ {% with form=raw_data_post_form %} +
+
+ {% include "rest_framework/raw_data_form.html" %} +
+ +
+
+
+ {% endwith %} +
+
+
+ {% endif %} + + {% if put_form or raw_data_put_form or raw_data_patch_form %} +
+ {% if put_form %} + + {% endif %} + +
+ {% if put_form %} +
+
+
+ {{ put_form }} +
+ +
+
+
+
+ {% endif %} + +
+ {% with form=raw_data_put_or_patch_form %} +
+
+ {% include "rest_framework/raw_data_form.html" %} +
+ {% if raw_data_put_form %} + + {% endif %} + {% if raw_data_patch_form %} + + {% endif %} +
+
+
+ {% endwith %} +
+
+
+ {% endif %} + {% endif %} + {% endblock content %} +
+
+
+ + {% if filter_form %} + {{ filter_form }} + {% endif %} + + {% block script %} + + + + + + + + + {% endblock %} + + + {% endblock %} + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/basic.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/basic.html new file mode 100644 index 0000000..16fc672 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/basic.html @@ -0,0 +1,38 @@ +{% load rest_framework %} + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/session.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/session.html new file mode 100644 index 0000000..59430d9 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/session.html @@ -0,0 +1,35 @@ +{% load rest_framework %} + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/token.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/token.html new file mode 100644 index 0000000..ce781b8 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/auth/token.html @@ -0,0 +1,36 @@ +{% load rest_framework %} + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/document.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/document.html new file mode 100644 index 0000000..9aecb40 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/document.html @@ -0,0 +1,31 @@ +{% load rest_framework %} + +
+
+

{{ document.title }}

+ {% if document.description %} +

{% render_markdown document.description %}

+ {% endif %} +
+
+ {% for html in lang_intro_htmls %} + {% include html %} + {% endfor %} +
+
+{% if document|data %} +{% for section_key, section in document|data|items %} +{% if section_key %} +

{{ section_key }} +

+{% endif %} + + {% for link_key, link in section|schema_links|items %} + {% include "rest_framework/docs/link.html" %} + {% endfor %} +{% endfor %} + +{% for link_key, link in document.links|items %} + {% include "rest_framework/docs/link.html" %} +{% endfor %} +{% endif %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/error.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/error.html new file mode 100644 index 0000000..694f88a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/error.html @@ -0,0 +1,71 @@ +{% load static %} + + + + + + + + Error Rendering Schema + + + + + +

Error

+ +
+{{ data }}
+
+ + +{% if debug is True %} +
+

Additional Information

+

Note: You are seeing this message because DEBUG==True.

+ +

Seeing this page is usually a configuration error: are your +DEFAULT_AUTHENTICATION_CLASSES or DEFAULT_PERMISSION_CLASSES +being applied unexpectedly?

+ +

Your response status code is: {{ response.status_code }}

+ +

401 Unauthorised.

+
    +
  • Do you have SessionAuthentication enabled?
  • +
  • Are you logged in?
  • +
+ + +

403 Forbidden.

+
    +
  • Do you have sufficient permissions to access this view?
  • +
  • Is you schema non-empty? (An empty schema will lead to a permission denied error being raised.)
  • +
+ + +

Most commonly the intended solution is to disable authentication and permissions +when including the docs urls:

+ +
+   path('docs/', include_docs_urls(title='Your API',
+                                    authentication_classes=[],
+                                    permission_classes=[])),
+
+ + +

Overriding this template

+ +

If you wish access to your docs to be authenticated you may override this template +at rest_framework/docs/error.html.

+ +

The available context is: data the error dict above, request, +response and the debug flag.

+ +{% endif %} + + + + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/index.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/index.html new file mode 100644 index 0000000..dfd3637 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/index.html @@ -0,0 +1,57 @@ +{% load static %} + + + + + + + + {{ document.title }} + + + + + + + + + + {% if code_style %}{% endif %} + + + + + + + + {% include "rest_framework/docs/sidebar.html" %} + +
+
+
+ {% include "rest_framework/docs/document.html" %} +
+
+
+ + {% include "rest_framework/docs/auth/token.html" %} + {% include "rest_framework/docs/auth/basic.html" %} + {% include "rest_framework/docs/auth/session.html" %} + + + + + + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/interact.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/interact.html new file mode 100644 index 0000000..60771ba --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/interact.html @@ -0,0 +1,51 @@ +{% load rest_framework %} + + + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html new file mode 100644 index 0000000..a6938fc --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript-intro.html @@ -0,0 +1,5 @@ +{% load rest_framework %} +{% load static %} +
{% code html %}
+
+{% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript.html new file mode 100644 index 0000000..4a51a32 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/javascript.html @@ -0,0 +1,15 @@ +{% load rest_framework %} +
{% code javascript %}var coreapi = window.coreapi  // Loaded by `coreapi.js`
+var schema = window.schema    // Loaded by `schema.js`
+
+// Initialize a client
+var client = new coreapi.Client()
+
+// Interact with the API endpoint
+var action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
+{% if link.fields %}var params = {
+{% for field in link.fields %}    {{ field.name }}: ...{% if not loop.last %},{% endif %}
+{% endfor %}}
+{% endif %}client.action(schema, action{% if link.fields %}, params{% endif %}).then(function(result) {
+    // Return value is in 'result'
+}){% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python-intro.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python-intro.html new file mode 100644 index 0000000..c2e0bcb --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python-intro.html @@ -0,0 +1,3 @@ +{% load rest_framework %} +
{% code bash %}# Install the Python client library
+$ pip install coreapi{% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python.html new file mode 100644 index 0000000..f944daa --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/python.html @@ -0,0 +1,13 @@ +{% load rest_framework %} +
{% code python %}import coreapi
+
+# Initialize a client & load the schema document
+client = coreapi.Client()
+schema = client.get("{{ document.url }}"{% if schema_format %}, format="{{ schema_format }}"{% endif %})
+
+# Interact with the API endpoint
+action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
+{% if link.fields %}params = {
+{% for field in link.fields %}    "{{ field.name }}": ...{% if not loop.last %},{% endif %}
+{% endfor %}}
+{% endif %}result = client.action(schema, action{% if link.fields %}, params=params{% endif %}){% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell-intro.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell-intro.html new file mode 100644 index 0000000..2320ddf --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell-intro.html @@ -0,0 +1,3 @@ +{% load rest_framework %} +
{% code bash %}# Install the command line client
+$ pip install coreapi-cli{% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell.html new file mode 100644 index 0000000..e5f2a03 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/langs/shell.html @@ -0,0 +1,6 @@ +{% load rest_framework %} +
{% code bash %}# Load the schema document
+$ coreapi get {{ document.url }}{% if schema_format %} --format {{ schema_format }}{% endif %}
+
+# Interact with the API endpoint
+$ coreapi action {% if section_key %}{{ section_key }} {% endif %}{{ link_key|cut:"> " }}{% for field in link.fields %} -p {{ field.name }}=...{% endfor %}{% endcode %}
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/link.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/link.html new file mode 100644 index 0000000..0f92a88 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/link.html @@ -0,0 +1,102 @@ +{% load rest_framework %} + + +{% include "rest_framework/docs/interact.html" with link=link %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/sidebar.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/sidebar.html new file mode 100644 index 0000000..c318789 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/docs/sidebar.html @@ -0,0 +1,44 @@ +{% load rest_framework %} + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/base.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/base.html new file mode 100644 index 0000000..f830e16 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/base.html @@ -0,0 +1,16 @@ + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/ordering.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/ordering.html new file mode 100644 index 0000000..b71b2a5 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/ordering.html @@ -0,0 +1,14 @@ +{% load rest_framework %} +{% load i18n %} +

{% trans "Ordering" %}

+
+ {% for key, label in options %} + {% if key == current %} + + {{ label }} + + {% else %} + {{ label }} + {% endif %} + {% endfor %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/search.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/search.html new file mode 100644 index 0000000..065c388 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/filters/search.html @@ -0,0 +1,12 @@ +{% load i18n %} +

{% trans "Search" %}

+
+
+
+ + + + +
+
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox.html new file mode 100644 index 0000000..faaebb8 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox.html @@ -0,0 +1,21 @@ +
+ {% if field.label %} + + {% endif %} + +
+ + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html new file mode 100644 index 0000000..42b47a4 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html @@ -0,0 +1,39 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + +
+ {% if style.inline %} + {% for key, text in field.choices|items %} + + {% endfor %} + {% else %} + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} + {% endif %} + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/dict_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/dict_field.html new file mode 100644 index 0000000..7c7414b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/dict_field.html @@ -0,0 +1,11 @@ +
+ {% if field.label %} + + {% endif %} + +
+

Dictionaries are not currently supported in HTML input.

+
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/fieldset.html new file mode 100644 index 0000000..1747d25 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/fieldset.html @@ -0,0 +1,16 @@ +{% load rest_framework %} +
+ {% if field.label %} +
+ + {{ field.label }} + +
+ {% endif %} + + {% for nested_field in field %} + {% if not nested_field.read_only %} + {% render_field nested_field style=style %} + {% endif %} + {% endfor %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/form.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/form.html new file mode 100644 index 0000000..13fc807 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/form.html @@ -0,0 +1,6 @@ +{% load rest_framework %} +{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/input.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/input.html new file mode 100644 index 0000000..b908100 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/input.html @@ -0,0 +1,21 @@ +
+ {% if field.label %} + + {% endif %} + +
+ + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_field.html new file mode 100644 index 0000000..46a9b7e --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_field.html @@ -0,0 +1,11 @@ +
+ {% if field.label %} + + {% endif %} + +
+

Lists are not currently supported in HTML input.

+
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_fieldset.html new file mode 100644 index 0000000..962c333 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/list_fieldset.html @@ -0,0 +1,13 @@ +{% load rest_framework %} + +
+ {% if field.label %} +
+ + {{ field.label }} + +
+ {% endif %} + +

Lists are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/radio.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/radio.html new file mode 100644 index 0000000..1710473 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/radio.html @@ -0,0 +1,57 @@ +{% load i18n %} +{% load rest_framework %} + +{% trans "None" as none_choice %} + +
+ {% if field.label %} + + {% endif %} + +
+ {% if style.inline %} + {% if field.allow_null or field.allow_blank %} + + {% endif %} + + {% for key, text in field.choices|items %} + + {% endfor %} + {% else %} + {% if field.allow_null or field.allow_blank %} +
+ +
+ {% endif %} + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} + {% endif %} + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select.html new file mode 100644 index 0000000..7a3db2d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select.html @@ -0,0 +1,36 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + +
+ + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select_multiple.html new file mode 100644 index 0000000..36ff9fd --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/select_multiple.html @@ -0,0 +1,38 @@ +{% load i18n %} +{% load rest_framework %} + +{% trans "No items to select." as no_items %} + +
+ {% if field.label %} + + {% endif %} + +
+ + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/textarea.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/textarea.html new file mode 100644 index 0000000..b279a2f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/horizontal/textarea.html @@ -0,0 +1,21 @@ +
+ {% if field.label %} + + {% endif %} + +
+ + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox.html new file mode 100644 index 0000000..665249a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox.html @@ -0,0 +1,8 @@ +
+
+ +
+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox_multiple.html new file mode 100644 index 0000000..e95c46d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/checkbox_multiple.html @@ -0,0 +1,16 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/dict_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/dict_field.html new file mode 100644 index 0000000..1301452 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/dict_field.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + +

Dictionaries are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/fieldset.html new file mode 100644 index 0000000..44feef8 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/fieldset.html @@ -0,0 +1,6 @@ +{% load rest_framework %} +{% for nested_field in field %} + {% if not nested_field.read_only %} + {% render_field nested_field style=style %} + {% endif %} +{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/form.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/form.html new file mode 100644 index 0000000..13fc807 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/form.html @@ -0,0 +1,6 @@ +{% load rest_framework %} +{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/input.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/input.html new file mode 100644 index 0000000..26cdcb7 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/input.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + + +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_field.html new file mode 100644 index 0000000..321d01b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_field.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + +

Lists are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_fieldset.html new file mode 100644 index 0000000..2ae56d7 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/list_fieldset.html @@ -0,0 +1 @@ +Lists are not currently supported in HTML input. diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/radio.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/radio.html new file mode 100644 index 0000000..38fac44 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/radio.html @@ -0,0 +1,29 @@ +{% load i18n %} +{% load rest_framework %} +{% trans "None" as none_choice %} + +
+ {% if field.label %} + + {% endif %} + + {% if field.allow_null or field.allow_blank %} +
+ +
+ {% endif %} + + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select.html new file mode 100644 index 0000000..5023c22 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select.html @@ -0,0 +1,24 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + + +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select_multiple.html new file mode 100644 index 0000000..b5fd46f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/select_multiple.html @@ -0,0 +1,25 @@ +{% load i18n %} +{% load rest_framework %} +{% trans "No items to select." as no_items %} + +
+ {% if field.label %} + + {% endif %} + + +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/textarea.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/textarea.html new file mode 100644 index 0000000..bce427f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/inline/textarea.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {% endif %} + + +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/login.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/login.html new file mode 100644 index 0000000..b762932 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/login.html @@ -0,0 +1,3 @@ +{% extends "rest_framework/login_base.html" %} + +{# Override this template in your own templates directory to customize #} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/login_base.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/login_base.html new file mode 100644 index 0000000..ba48917 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/login_base.html @@ -0,0 +1,65 @@ +{% extends "rest_framework/base.html" %} +{% load rest_framework %} + +{% block body %} + +
+
+
+
+
+ {% block branding %}

Django REST framework

{% endblock %} +
+
+ +
+
+
+ {% csrf_token %} + + +
+
+ + + {% if form.username.errors %} +

+ {{ form.username.errors|striptags }} +

+ {% endif %} +
+
+ +
+
+ + + {% if form.password.errors %} +

+ {{ form.password.errors|striptags }} +

+ {% endif %} +
+
+ + {% if form.non_field_errors %} + {% for error in form.non_field_errors %} +
{{ error }}
+ {% endfor %} + {% endif %} + +
+ +
+
+
+
+
+
+
+ +{% endblock %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/numbers.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/numbers.html new file mode 100644 index 0000000..4ff9385 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/numbers.html @@ -0,0 +1,47 @@ + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/previous_and_next.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/previous_and_next.html new file mode 100644 index 0000000..5563c4b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/pagination/previous_and_next.html @@ -0,0 +1,21 @@ + diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/raw_data_form.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/raw_data_form.html new file mode 100644 index 0000000..d279832 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/raw_data_form.html @@ -0,0 +1,11 @@ +{% load rest_framework %} +{{ form.non_field_errors }} +{% for field in form %} +
+ {{ field.label_tag|add_class:"col-sm-2 control-label" }} +
+ {{ field|add_class:"form-control" }} + {{ field.help_text|safe }} +
+
+{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/schema.js b/venv/Lib/site-packages/rest_framework/templates/rest_framework/schema.js new file mode 100644 index 0000000..692cb90 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/schema.js @@ -0,0 +1,3 @@ +var codec = new window.coreapi.codecs.CoreJSONCodec() +var coreJSON = window.atob('{{ schema }}') +window.schema = codec.decode(coreJSON) diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox.html new file mode 100644 index 0000000..827ad8a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox.html @@ -0,0 +1,18 @@ +
+
+ +
+ + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html new file mode 100644 index 0000000..1bc13df --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html @@ -0,0 +1,37 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + + {% if style.inline %} +
+ {% for key, text in field.choices|items %} + + {% endfor %} +
+ {% else %} + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} + {% endif %} + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/dict_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/dict_field.html new file mode 100644 index 0000000..dde803b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/dict_field.html @@ -0,0 +1,7 @@ +
+ {% if field.label %} + + {% endif %} + +

Dictionaries are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/fieldset.html new file mode 100644 index 0000000..4ec2a5d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/fieldset.html @@ -0,0 +1,15 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {{ field.label }} + + {% endif %} + + {% for nested_field in field %} + {% if not nested_field.read_only %} + {% render_field nested_field style=style %} + {% endif %} + {% endfor %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/form.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/form.html new file mode 100644 index 0000000..13fc807 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/form.html @@ -0,0 +1,6 @@ +{% load rest_framework %} +{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/input.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/input.html new file mode 100644 index 0000000..29c4370 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/input.html @@ -0,0 +1,17 @@ +
+ {% if field.label %} + + {% endif %} + + + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_field.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_field.html new file mode 100644 index 0000000..47a60c5 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_field.html @@ -0,0 +1,7 @@ +
+ {% if field.label %} + + {% endif %} + +

Lists are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_fieldset.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_fieldset.html new file mode 100644 index 0000000..f2b615f --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/list_fieldset.html @@ -0,0 +1,9 @@ +
+ {% if field.label %} + + {{ field.label }} + + {% endif %} + +

Lists are not currently supported in HTML input.

+
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/radio.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/radio.html new file mode 100644 index 0000000..39b3c71 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/radio.html @@ -0,0 +1,57 @@ +{% load i18n %} +{% load rest_framework %} +{% trans "None" as none_choice %} + +
+ {% if field.label %} + + {% endif %} + + {% if style.inline %} +
+ {% if field.allow_null or field.allow_blank %} + + {% endif %} + + {% for key, text in field.choices|items %} + + {% endfor %} +
+ {% else %} + {% if field.allow_null or field.allow_blank %} +
+ +
+ {% endif %} + + {% for key, text in field.choices|items %} +
+ +
+ {% endfor %} + {% endif %} + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select.html new file mode 100644 index 0000000..6ccaaf2 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select.html @@ -0,0 +1,34 @@ +{% load rest_framework %} + +
+ {% if field.label %} + + {% endif %} + + + + {% if field.errors %} + {% for error in field.errors %} + {{ error }} + {% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select_multiple.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select_multiple.html new file mode 100644 index 0000000..b77c4be --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/select_multiple.html @@ -0,0 +1,33 @@ +{% load i18n %} +{% load rest_framework %} +{% trans "No items to select." as no_items %} + +
+ {% if field.label %} + + {% endif %} + + + + {% if field.errors %} + {% for error in field.errors %}{{ error }}{% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/textarea.html b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/textarea.html new file mode 100644 index 0000000..fea94cd --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templates/rest_framework/vertical/textarea.html @@ -0,0 +1,17 @@ +
+ {% if field.label %} + + {% endif %} + + + + {% if field.errors %} + {% for error in field.errors %}{{ error }}{% endfor %} + {% endif %} + + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} +
diff --git a/venv/Lib/site-packages/rest_framework/templatetags/__init__.py b/venv/Lib/site-packages/rest_framework/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/templatetags/rest_framework.py b/venv/Lib/site-packages/rest_framework/templatetags/rest_framework.py new file mode 100644 index 0000000..db0e9c9 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/templatetags/rest_framework.py @@ -0,0 +1,322 @@ +import re +from collections import OrderedDict + +from django import template +from django.template import loader +from django.urls import NoReverseMatch, reverse +from django.utils.encoding import iri_to_uri +from django.utils.html import escape, format_html, smart_urlquote +from django.utils.safestring import mark_safe + +from rest_framework.compat import apply_markdown, pygments_highlight +from rest_framework.renderers import HTMLFormRenderer +from rest_framework.utils.urls import replace_query_param + +register = template.Library() + +# Regex for adding classes to html snippets +class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])') + + +@register.tag(name='code') +def highlight_code(parser, token): + code = token.split_contents()[-1] + nodelist = parser.parse(('endcode',)) + parser.delete_first_token() + return CodeNode(code, nodelist) + + +class CodeNode(template.Node): + style = 'emacs' + + def __init__(self, lang, code): + self.lang = lang + self.nodelist = code + + def render(self, context): + text = self.nodelist.render(context) + return pygments_highlight(text, self.lang, self.style) + + +@register.filter() +def with_location(fields, location): + return [ + field for field in fields + if field.location == location + ] + + +@register.simple_tag +def form_for_link(link): + import coreschema + properties = OrderedDict([ + (field.name, field.schema or coreschema.String()) + for field in link.fields + ]) + required = [ + field.name + for field in link.fields + if field.required + ] + schema = coreschema.Object(properties=properties, required=required) + return mark_safe(coreschema.render_to_form(schema)) + + +@register.simple_tag +def render_markdown(markdown_text): + if apply_markdown is None: + return markdown_text + return mark_safe(apply_markdown(markdown_text)) + + +@register.simple_tag +def get_pagination_html(pager): + return pager.to_html() + + +@register.simple_tag +def render_form(serializer, template_pack=None): + style = {'template_pack': template_pack} if template_pack else {} + renderer = HTMLFormRenderer() + return renderer.render(serializer.data, None, {'style': style}) + + +@register.simple_tag +def render_field(field, style): + renderer = style.get('renderer', HTMLFormRenderer()) + return renderer.render_field(field, style) + + +@register.simple_tag +def optional_login(request): + """ + Include a login snippet if REST framework's login view is in the URLconf. + """ + try: + login_url = reverse('rest_framework:login') + except NoReverseMatch: + return '' + + snippet = "
  • Log in
  • " + snippet = format_html(snippet, href=login_url, next=escape(request.path)) + + return mark_safe(snippet) + + +@register.simple_tag +def optional_docs_login(request): + """ + Include a login snippet if REST framework's login view is in the URLconf. + """ + try: + login_url = reverse('rest_framework:login') + except NoReverseMatch: + return 'log in' + + snippet = "log in" + snippet = format_html(snippet, href=login_url, next=escape(request.path)) + + return mark_safe(snippet) + + +@register.simple_tag +def optional_logout(request, user): + """ + Include a logout snippet if REST framework's logout view is in the URLconf. + """ + try: + logout_url = reverse('rest_framework:logout') + except NoReverseMatch: + snippet = format_html('', user=escape(user)) + return mark_safe(snippet) + + snippet = """""" + snippet = format_html(snippet, user=escape(user), href=logout_url, next=escape(request.path)) + + return mark_safe(snippet) + + +@register.simple_tag +def add_query_param(request, key, val): + """ + Add a query parameter to the current request url, and return the new url. + """ + iri = request.get_full_path() + uri = iri_to_uri(iri) + return escape(replace_query_param(uri, key, val)) + + +@register.filter +def as_string(value): + if value is None: + return '' + return '%s' % value + + +@register.filter +def as_list_of_strings(value): + return [ + '' if (item is None) else ('%s' % item) + for item in value + ] + + +@register.filter +def add_class(value, css_class): + """ + https://stackoverflow.com/questions/4124220/django-adding-css-classes-when-rendering-form-fields-in-a-template + + Inserts classes into template variables that contain HTML tags, + useful for modifying forms without needing to change the Form objects. + + Usage: + + {{ field.label_tag|add_class:"control-label" }} + + In the case of REST Framework, the filter is used to add Bootstrap-specific + classes to the forms. + """ + html = str(value) + match = class_re.search(html) + if match: + m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class, + css_class, css_class), + match.group(1)) + if not m: + return mark_safe(class_re.sub(match.group(1) + " " + css_class, + html)) + else: + return mark_safe(html.replace('>', ' class="%s">' % css_class, 1)) + return value + + +@register.filter +def format_value(value): + if getattr(value, 'is_hyperlink', False): + name = str(value.obj) + return mark_safe('%s' % (value, escape(name))) + if value is None or isinstance(value, bool): + return mark_safe('%s' % {True: 'true', False: 'false', None: 'null'}[value]) + elif isinstance(value, list): + if any(isinstance(item, (list, dict)) for item in value): + template = loader.get_template('rest_framework/admin/list_value.html') + else: + template = loader.get_template('rest_framework/admin/simple_list_value.html') + context = {'value': value} + return template.render(context) + elif isinstance(value, dict): + template = loader.get_template('rest_framework/admin/dict_value.html') + context = {'value': value} + return template.render(context) + elif isinstance(value, str): + if ( + (value.startswith('http:') or value.startswith('https:')) and not + re.search(r'\s', value) + ): + return mark_safe('{value}'.format(value=escape(value))) + elif '@' in value and not re.search(r'\s', value): + return mark_safe('{value}'.format(value=escape(value))) + elif '\n' in value: + return mark_safe('
    %s
    ' % escape(value)) + return str(value) + + +@register.filter +def items(value): + """ + Simple filter to return the items of the dict. Useful when the dict may + have a key 'items' which is resolved first in Django template dot-notation + lookup. See issue #4931 + Also see: https://stackoverflow.com/questions/15416662/django-template-loop-over-dictionary-items-with-items-as-key + """ + if value is None: + # `{% for k, v in value.items %}` doesn't raise when value is None or + # not in the context, so neither should `{% for k, v in value|items %}` + return [] + return value.items() + + +@register.filter +def data(value): + """ + Simple filter to access `data` attribute of object, + specifically coreapi.Document. + + As per `items` filter above, allows accessing `document.data` when + Document contains Link keyed-at "data". + + See issue #5395 + """ + return value.data + + +@register.filter +def schema_links(section, sec_key=None): + """ + Recursively find every link in a schema, even nested. + """ + NESTED_FORMAT = '%s > %s' # this format is used in docs/js/api.js:normalizeKeys + links = section.links + if section.data: + data = section.data.items() + for sub_section_key, sub_section in data: + new_links = schema_links(sub_section, sec_key=sub_section_key) + links.update(new_links) + + if sec_key is not None: + new_links = OrderedDict() + for link_key, link in links.items(): + new_key = NESTED_FORMAT % (sec_key, link_key) + new_links.update({new_key: link}) + return new_links + + return links + + +@register.filter +def add_nested_class(value): + if isinstance(value, dict): + return 'class=nested' + if isinstance(value, list) and any(isinstance(item, (list, dict)) for item in value): + return 'class=nested' + return '' + + +# Bunch of stuff cloned from urlize +TRAILING_PUNCTUATION = ['.', ',', ':', ';', '.)', '"', "']", "'}", "'"] +WRAPPING_PUNCTUATION = [('(', ')'), ('<', '>'), ('[', ']'), ('<', '>'), + ('"', '"'), ("'", "'")] +word_split_re = re.compile(r'(\s+)') +simple_url_re = re.compile(r'^https?://\[?\w', re.IGNORECASE) +simple_url_2_re = re.compile(r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)$', re.IGNORECASE) +simple_email_re = re.compile(r'^\S+@\S+\.\S+$') + + +def smart_urlquote_wrapper(matched_url): + """ + Simple wrapper for smart_urlquote. ValueError("Invalid IPv6 URL") can + be raised here, see issue #1386 + """ + try: + return smart_urlquote(matched_url) + except ValueError: + return None + + +@register.filter +def break_long_headers(header): + """ + Breaks headers longer than 160 characters (~page length) + when possible (are comma separated) + """ + if len(header) > 160 and ',' in header: + header = mark_safe('
    ' + ',
    '.join(header.split(','))) + return header diff --git a/venv/Lib/site-packages/rest_framework/test.py b/venv/Lib/site-packages/rest_framework/test.py new file mode 100644 index 0000000..0212348 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/test.py @@ -0,0 +1,412 @@ +# Note that we import as `DjangoRequestFactory` and `DjangoClient` in order +# to make it harder for the user to import the wrong thing without realizing. +import io +from importlib import import_module + +import django +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.handlers.wsgi import WSGIHandler +from django.test import override_settings, testcases +from django.test.client import Client as DjangoClient +from django.test.client import ClientHandler +from django.test.client import RequestFactory as DjangoRequestFactory +from django.utils.encoding import force_bytes +from django.utils.http import urlencode + +from rest_framework.compat import coreapi, requests +from rest_framework.settings import api_settings + + +def force_authenticate(request, user=None, token=None): + request._force_auth_user = user + request._force_auth_token = token + + +if requests is not None: + class HeaderDict(requests.packages.urllib3._collections.HTTPHeaderDict): + def get_all(self, key, default): + return self.getheaders(key) + + class MockOriginalResponse: + def __init__(self, headers): + self.msg = HeaderDict(headers) + self.closed = False + + def isclosed(self): + return self.closed + + def close(self): + self.closed = True + + class DjangoTestAdapter(requests.adapters.HTTPAdapter): + """ + A transport adapter for `requests`, that makes requests via the + Django WSGI app, rather than making actual HTTP requests over the network. + """ + def __init__(self): + self.app = WSGIHandler() + self.factory = DjangoRequestFactory() + + def get_environ(self, request): + """ + Given a `requests.PreparedRequest` instance, return a WSGI environ dict. + """ + method = request.method + url = request.url + kwargs = {} + + # Set request content, if any exists. + if request.body is not None: + if hasattr(request.body, 'read'): + kwargs['data'] = request.body.read() + else: + kwargs['data'] = request.body + if 'content-type' in request.headers: + kwargs['content_type'] = request.headers['content-type'] + + # Set request headers. + for key, value in request.headers.items(): + key = key.upper() + if key in ('CONNECTION', 'CONTENT-LENGTH', 'CONTENT-TYPE'): + continue + kwargs['HTTP_%s' % key.replace('-', '_')] = value + + return self.factory.generic(method, url, **kwargs).environ + + def send(self, request, *args, **kwargs): + """ + Make an outgoing request to the Django WSGI application. + """ + raw_kwargs = {} + + def start_response(wsgi_status, wsgi_headers, exc_info=None): + status, _, reason = wsgi_status.partition(' ') + raw_kwargs['status'] = int(status) + raw_kwargs['reason'] = reason + raw_kwargs['headers'] = wsgi_headers + raw_kwargs['version'] = 11 + raw_kwargs['preload_content'] = False + raw_kwargs['original_response'] = MockOriginalResponse(wsgi_headers) + + # Make the outgoing request via WSGI. + environ = self.get_environ(request) + wsgi_response = self.app(environ, start_response) + + # Build the underlying urllib3.HTTPResponse + raw_kwargs['body'] = io.BytesIO(b''.join(wsgi_response)) + raw = requests.packages.urllib3.HTTPResponse(**raw_kwargs) + + # Build the requests.Response + return self.build_response(request, raw) + + def close(self): + pass + + class RequestsClient(requests.Session): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + adapter = DjangoTestAdapter() + self.mount('http://', adapter) + self.mount('https://', adapter) + + def request(self, method, url, *args, **kwargs): + if not url.startswith('http'): + raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url) + return super().request(method, url, *args, **kwargs) + +else: + def RequestsClient(*args, **kwargs): + raise ImproperlyConfigured('requests must be installed in order to use RequestsClient.') + + +if coreapi is not None: + class CoreAPIClient(coreapi.Client): + def __init__(self, *args, **kwargs): + self._session = RequestsClient() + kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)] + super().__init__(*args, **kwargs) + + @property + def session(self): + return self._session + +else: + def CoreAPIClient(*args, **kwargs): + raise ImproperlyConfigured('coreapi must be installed in order to use CoreAPIClient.') + + +class APIRequestFactory(DjangoRequestFactory): + renderer_classes_list = api_settings.TEST_REQUEST_RENDERER_CLASSES + default_format = api_settings.TEST_REQUEST_DEFAULT_FORMAT + + def __init__(self, enforce_csrf_checks=False, **defaults): + self.enforce_csrf_checks = enforce_csrf_checks + self.renderer_classes = {} + for cls in self.renderer_classes_list: + self.renderer_classes[cls.format] = cls + super().__init__(**defaults) + + def _encode_data(self, data, format=None, content_type=None): + """ + Encode the data returning a two tuple of (bytes, content_type) + """ + + if data is None: + return ('', content_type) + + assert format is None or content_type is None, ( + 'You may not set both `format` and `content_type`.' + ) + + if content_type: + # Content type specified explicitly, treat data as a raw bytestring + ret = force_bytes(data, settings.DEFAULT_CHARSET) + + else: + format = format or self.default_format + + assert format in self.renderer_classes, ( + "Invalid format '{}'. Available formats are {}. " + "Set TEST_REQUEST_RENDERER_CLASSES to enable " + "extra request formats.".format( + format, + ', '.join(["'" + fmt + "'" for fmt in self.renderer_classes]) + ) + ) + + # Use format and render the data into a bytestring + renderer = self.renderer_classes[format]() + ret = renderer.render(data) + + # Determine the content-type header from the renderer + content_type = renderer.media_type + if renderer.charset: + content_type = "{}; charset={}".format( + content_type, renderer.charset + ) + + # Coerce text to bytes if required. + if isinstance(ret, str): + ret = ret.encode(renderer.charset) + + return ret, content_type + + def get(self, path, data=None, **extra): + r = { + 'QUERY_STRING': urlencode(data or {}, doseq=True), + } + if not data and '?' in path: + # Fix to support old behavior where you have the arguments in the + # url. See #1461. + query_string = force_bytes(path.split('?')[1]) + query_string = query_string.decode('iso-8859-1') + r['QUERY_STRING'] = query_string + r.update(extra) + return self.generic('GET', path, **r) + + def post(self, path, data=None, format=None, content_type=None, **extra): + data, content_type = self._encode_data(data, format, content_type) + return self.generic('POST', path, data, content_type, **extra) + + def put(self, path, data=None, format=None, content_type=None, **extra): + data, content_type = self._encode_data(data, format, content_type) + return self.generic('PUT', path, data, content_type, **extra) + + def patch(self, path, data=None, format=None, content_type=None, **extra): + data, content_type = self._encode_data(data, format, content_type) + return self.generic('PATCH', path, data, content_type, **extra) + + def delete(self, path, data=None, format=None, content_type=None, **extra): + data, content_type = self._encode_data(data, format, content_type) + return self.generic('DELETE', path, data, content_type, **extra) + + def options(self, path, data=None, format=None, content_type=None, **extra): + data, content_type = self._encode_data(data, format, content_type) + return self.generic('OPTIONS', path, data, content_type, **extra) + + def generic(self, method, path, data='', + content_type='application/octet-stream', secure=False, **extra): + # Include the CONTENT_TYPE, regardless of whether or not data is empty. + if content_type is not None: + extra['CONTENT_TYPE'] = str(content_type) + + return super().generic( + method, path, data, content_type, secure, **extra) + + def request(self, **kwargs): + request = super().request(**kwargs) + request._dont_enforce_csrf_checks = not self.enforce_csrf_checks + return request + + +class ForceAuthClientHandler(ClientHandler): + """ + A patched version of ClientHandler that can enforce authentication + on the outgoing requests. + """ + + def __init__(self, *args, **kwargs): + self._force_user = None + self._force_token = None + super().__init__(*args, **kwargs) + + def get_response(self, request): + # This is the simplest place we can hook into to patch the + # request object. + force_authenticate(request, self._force_user, self._force_token) + return super().get_response(request) + + +class APIClient(APIRequestFactory, DjangoClient): + def __init__(self, enforce_csrf_checks=False, **defaults): + super().__init__(**defaults) + self.handler = ForceAuthClientHandler(enforce_csrf_checks) + self._credentials = {} + + def credentials(self, **kwargs): + """ + Sets headers that will be used on every outgoing request. + """ + self._credentials = kwargs + + def force_authenticate(self, user=None, token=None): + """ + Forcibly authenticates outgoing requests with the given + user and/or token. + """ + self.handler._force_user = user + self.handler._force_token = token + if user is None: + self.logout() # Also clear any possible session info if required + + def request(self, **kwargs): + # Ensure that any credentials set get added to every request. + kwargs.update(self._credentials) + return super().request(**kwargs) + + def get(self, path, data=None, follow=False, **extra): + response = super().get(path, data=data, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def post(self, path, data=None, format=None, content_type=None, + follow=False, **extra): + response = super().post( + path, data=data, format=format, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def put(self, path, data=None, format=None, content_type=None, + follow=False, **extra): + response = super().put( + path, data=data, format=format, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def patch(self, path, data=None, format=None, content_type=None, + follow=False, **extra): + response = super().patch( + path, data=data, format=format, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def delete(self, path, data=None, format=None, content_type=None, + follow=False, **extra): + response = super().delete( + path, data=data, format=format, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def options(self, path, data=None, format=None, content_type=None, + follow=False, **extra): + response = super().options( + path, data=data, format=format, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def logout(self): + self._credentials = {} + + # Also clear any `force_authenticate` + self.handler._force_user = None + self.handler._force_token = None + + if self.session: + super().logout() + + +class APITransactionTestCase(testcases.TransactionTestCase): + client_class = APIClient + + +class APITestCase(testcases.TestCase): + client_class = APIClient + + +class APISimpleTestCase(testcases.SimpleTestCase): + client_class = APIClient + + +class APILiveServerTestCase(testcases.LiveServerTestCase): + client_class = APIClient + + +def cleanup_url_patterns(cls): + if hasattr(cls, '_module_urlpatterns'): + cls._module.urlpatterns = cls._module_urlpatterns + else: + del cls._module.urlpatterns + + +class URLPatternsTestCase(testcases.SimpleTestCase): + """ + Isolate URL patterns on a per-TestCase basis. For example, + + class ATestCase(URLPatternsTestCase): + urlpatterns = [...] + + def test_something(self): + ... + + class AnotherTestCase(URLPatternsTestCase): + urlpatterns = [...] + + def test_something_else(self): + ... + """ + @classmethod + def setUpClass(cls): + # Get the module of the TestCase subclass + cls._module = import_module(cls.__module__) + cls._override = override_settings(ROOT_URLCONF=cls.__module__) + + if hasattr(cls._module, 'urlpatterns'): + cls._module_urlpatterns = cls._module.urlpatterns + + cls._module.urlpatterns = cls.urlpatterns + + cls._override.enable() + + if django.VERSION > (4, 0): + cls.addClassCleanup(cls._override.disable) + cls.addClassCleanup(cleanup_url_patterns, cls) + + super().setUpClass() + + if django.VERSION < (4, 0): + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls._override.disable() + + if hasattr(cls, '_module_urlpatterns'): + cls._module.urlpatterns = cls._module_urlpatterns + else: + del cls._module.urlpatterns diff --git a/venv/Lib/site-packages/rest_framework/throttling.py b/venv/Lib/site-packages/rest_framework/throttling.py new file mode 100644 index 0000000..e262b88 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/throttling.py @@ -0,0 +1,250 @@ +""" +Provides various throttling policies. +""" +import time + +from django.core.cache import cache as default_cache +from django.core.exceptions import ImproperlyConfigured + +from rest_framework.settings import api_settings + + +class BaseThrottle: + """ + Rate throttling of requests. + """ + + def allow_request(self, request, view): + """ + Return `True` if the request should be allowed, `False` otherwise. + """ + raise NotImplementedError('.allow_request() must be overridden') + + def get_ident(self, request): + """ + Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR + if present and number of proxies is > 0. If not use all of + HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR. + """ + xff = request.META.get('HTTP_X_FORWARDED_FOR') + remote_addr = request.META.get('REMOTE_ADDR') + num_proxies = api_settings.NUM_PROXIES + + if num_proxies is not None: + if num_proxies == 0 or xff is None: + return remote_addr + addrs = xff.split(',') + client_addr = addrs[-min(num_proxies, len(addrs))] + return client_addr.strip() + + return ''.join(xff.split()) if xff else remote_addr + + def wait(self): + """ + Optionally, return a recommended number of seconds to wait before + the next request. + """ + return None + + +class SimpleRateThrottle(BaseThrottle): + """ + A simple cache implementation, that only requires `.get_cache_key()` + to be overridden. + + The rate (requests / seconds) is set by a `rate` attribute on the Throttle + class. The attribute is a string of the form 'number_of_requests/period'. + + Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') + + Previous request information used for throttling is stored in the cache. + """ + cache = default_cache + timer = time.time + cache_format = 'throttle_%(scope)s_%(ident)s' + scope = None + THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES + + def __init__(self): + if not getattr(self, 'rate', None): + self.rate = self.get_rate() + self.num_requests, self.duration = self.parse_rate(self.rate) + + def get_cache_key(self, request, view): + """ + Should return a unique cache-key which can be used for throttling. + Must be overridden. + + May return `None` if the request should not be throttled. + """ + raise NotImplementedError('.get_cache_key() must be overridden') + + def get_rate(self): + """ + Determine the string representation of the allowed request rate. + """ + if not getattr(self, 'scope', None): + msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % + self.__class__.__name__) + raise ImproperlyConfigured(msg) + + try: + return self.THROTTLE_RATES[self.scope] + except KeyError: + msg = "No default throttle rate set for '%s' scope" % self.scope + raise ImproperlyConfigured(msg) + + def parse_rate(self, rate): + """ + Given the request rate string, return a two tuple of: + , + """ + if rate is None: + return (None, None) + num, period = rate.split('/') + num_requests = int(num) + duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] + return (num_requests, duration) + + def allow_request(self, request, view): + """ + Implement the check to see if the request should be throttled. + + On success calls `throttle_success`. + On failure calls `throttle_failure`. + """ + if self.rate is None: + return True + + self.key = self.get_cache_key(request, view) + if self.key is None: + return True + + self.history = self.cache.get(self.key, []) + self.now = self.timer() + + # Drop any requests from the history which have now passed the + # throttle duration + while self.history and self.history[-1] <= self.now - self.duration: + self.history.pop() + if len(self.history) >= self.num_requests: + return self.throttle_failure() + return self.throttle_success() + + def throttle_success(self): + """ + Inserts the current request's timestamp along with the key + into the cache. + """ + self.history.insert(0, self.now) + self.cache.set(self.key, self.history, self.duration) + return True + + def throttle_failure(self): + """ + Called when a request to the API has failed due to throttling. + """ + return False + + def wait(self): + """ + Returns the recommended next request time in seconds. + """ + if self.history: + remaining_duration = self.duration - (self.now - self.history[-1]) + else: + remaining_duration = self.duration + + available_requests = self.num_requests - len(self.history) + 1 + if available_requests <= 0: + return None + + return remaining_duration / float(available_requests) + + +class AnonRateThrottle(SimpleRateThrottle): + """ + Limits the rate of API calls that may be made by a anonymous users. + + The IP address of the request will be used as the unique cache key. + """ + scope = 'anon' + + def get_cache_key(self, request, view): + if request.user.is_authenticated: + return None # Only throttle unauthenticated requests. + + return self.cache_format % { + 'scope': self.scope, + 'ident': self.get_ident(request) + } + + +class UserRateThrottle(SimpleRateThrottle): + """ + Limits the rate of API calls that may be made by a given user. + + The user id will be used as a unique cache key if the user is + authenticated. For anonymous requests, the IP address of the request will + be used. + """ + scope = 'user' + + def get_cache_key(self, request, view): + if request.user.is_authenticated: + ident = request.user.pk + else: + ident = self.get_ident(request) + + return self.cache_format % { + 'scope': self.scope, + 'ident': ident + } + + +class ScopedRateThrottle(SimpleRateThrottle): + """ + Limits the rate of API calls by different amounts for various parts of + the API. Any view that has the `throttle_scope` property set will be + throttled. The unique cache key will be generated by concatenating the + user id of the request, and the scope of the view being accessed. + """ + scope_attr = 'throttle_scope' + + def __init__(self): + # Override the usual SimpleRateThrottle, because we can't determine + # the rate until called by the view. + pass + + def allow_request(self, request, view): + # We can only determine the scope once we're called by the view. + self.scope = getattr(view, self.scope_attr, None) + + # If a view does not have a `throttle_scope` always allow the request + if not self.scope: + return True + + # Determine the allowed request rate as we normally would during + # the `__init__` call. + self.rate = self.get_rate() + self.num_requests, self.duration = self.parse_rate(self.rate) + + # We can now proceed as normal. + return super().allow_request(request, view) + + def get_cache_key(self, request, view): + """ + If `view.throttle_scope` is not set, don't apply this throttle. + + Otherwise generate the unique cache key by concatenating the user id + with the '.throttle_scope` property of the view. + """ + if request.user.is_authenticated: + ident = request.user.pk + else: + ident = self.get_ident(request) + + return self.cache_format % { + 'scope': self.scope, + 'ident': ident + } diff --git a/venv/Lib/site-packages/rest_framework/urlpatterns.py b/venv/Lib/site-packages/rest_framework/urlpatterns.py new file mode 100644 index 0000000..bed5708 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/urlpatterns.py @@ -0,0 +1,112 @@ +from django.urls import URLResolver, include, path, re_path, register_converter +from django.urls.resolvers import RoutePattern + +from rest_framework.settings import api_settings + + +def _get_format_path_converter(suffix_kwarg, allowed): + if allowed: + if len(allowed) == 1: + allowed_pattern = allowed[0] + else: + allowed_pattern = '(?:%s)' % '|'.join(allowed) + suffix_pattern = r"\.%s/?" % allowed_pattern + else: + suffix_pattern = r"\.[a-z0-9]+/?" + + class FormatSuffixConverter: + regex = suffix_pattern + + def to_python(self, value): + return value.strip('./') + + def to_url(self, value): + return '.' + value + '/' + + converter_name = 'drf_format_suffix' + if allowed: + converter_name += '_' + '_'.join(allowed) + + return converter_name, FormatSuffixConverter + + +def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None): + ret = [] + for urlpattern in urlpatterns: + if isinstance(urlpattern, URLResolver): + # Set of included URL patterns + regex = urlpattern.pattern.regex.pattern + namespace = urlpattern.namespace + app_name = urlpattern.app_name + kwargs = urlpattern.default_kwargs + # Add in the included patterns, after applying the suffixes + patterns = apply_suffix_patterns(urlpattern.url_patterns, + suffix_pattern, + suffix_required, + suffix_route) + + # if the original pattern was a RoutePattern we need to preserve it + if isinstance(urlpattern.pattern, RoutePattern): + assert path is not None + route = str(urlpattern.pattern) + new_pattern = path(route, include((patterns, app_name), namespace), kwargs) + else: + new_pattern = re_path(regex, include((patterns, app_name), namespace), kwargs) + + ret.append(new_pattern) + else: + # Regular URL pattern + regex = urlpattern.pattern.regex.pattern.rstrip('$').rstrip('/') + suffix_pattern + view = urlpattern.callback + kwargs = urlpattern.default_args + name = urlpattern.name + # Add in both the existing and the new urlpattern + if not suffix_required: + ret.append(urlpattern) + + # if the original pattern was a RoutePattern we need to preserve it + if isinstance(urlpattern.pattern, RoutePattern): + assert path is not None + assert suffix_route is not None + route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route + new_pattern = path(route, view, kwargs, name) + else: + new_pattern = re_path(regex, view, kwargs, name) + + ret.append(new_pattern) + + return ret + + +def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None): + """ + Supplement existing urlpatterns with corresponding patterns that also + include a '.format' suffix. Retains urlpattern ordering. + + urlpatterns: + A list of URL patterns. + + suffix_required: + If `True`, only suffixed URLs will be generated, and non-suffixed + URLs will not be used. Defaults to `False`. + + allowed: + An optional tuple/list of allowed suffixes. eg ['json', 'api'] + Defaults to `None`, which allows any suffix. + """ + suffix_kwarg = api_settings.FORMAT_SUFFIX_KWARG + if allowed: + if len(allowed) == 1: + allowed_pattern = allowed[0] + else: + allowed_pattern = '(%s)' % '|'.join(allowed) + suffix_pattern = r'\.(?P<%s>%s)/?$' % (suffix_kwarg, allowed_pattern) + else: + suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg + + converter_name, suffix_converter = _get_format_path_converter(suffix_kwarg, allowed) + register_converter(suffix_converter, converter_name) + + suffix_route = '<%s:%s>' % (converter_name, suffix_kwarg) + + return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route) diff --git a/venv/Lib/site-packages/rest_framework/urls.py b/venv/Lib/site-packages/rest_framework/urls.py new file mode 100644 index 0000000..0aa3013 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/urls.py @@ -0,0 +1,21 @@ +""" +Login and logout views for the browsable API. + +Add these to your root URLconf if you're using the browsable API and +your API requires authentication: + + urlpatterns = [ + ... + path('auth/', include('rest_framework.urls')) + ] + +You should make sure your authentication settings include `SessionAuthentication`. +""" +from django.contrib.auth import views +from django.urls import path + +app_name = 'rest_framework' +urlpatterns = [ + path('login/', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'), + path('logout/', views.LogoutView.as_view(), name='logout'), +] diff --git a/venv/Lib/site-packages/rest_framework/utils/__init__.py b/venv/Lib/site-packages/rest_framework/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/rest_framework/utils/breadcrumbs.py b/venv/Lib/site-packages/rest_framework/utils/breadcrumbs.py new file mode 100644 index 0000000..54990e9 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/breadcrumbs.py @@ -0,0 +1,53 @@ +from django.urls import get_script_prefix, resolve + + +def get_breadcrumbs(url, request=None): + """ + Given a url returns a list of breadcrumbs, which are each a + tuple of (name, url). + """ + from rest_framework.reverse import preserve_builtin_query_params + from rest_framework.views import APIView + + def breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen): + """ + Add tuples of (name, url) to the breadcrumbs list, + progressively chomping off parts of the url. + """ + try: + (view, unused_args, unused_kwargs) = resolve(url) + except Exception: + pass + else: + # Check if this is a REST framework view, + # and if so add it to the breadcrumbs + cls = getattr(view, 'cls', None) + initkwargs = getattr(view, 'initkwargs', {}) + if cls is not None and issubclass(cls, APIView): + # Don't list the same view twice in a row. + # Probably an optional trailing slash. + if not seen or seen[-1] != view: + c = cls(**initkwargs) + name = c.get_view_name() + insert_url = preserve_builtin_query_params(prefix + url, request) + breadcrumbs_list.insert(0, (name, insert_url)) + seen.append(view) + + if url == '': + # All done + return breadcrumbs_list + + elif url.endswith('/'): + # Drop trailing slash off the end and continue to try to + # resolve more breadcrumbs + url = url.rstrip('/') + return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) + + # Drop trailing non-slash off the end and continue to try to + # resolve more breadcrumbs + url = url[:url.rfind('/') + 1] + return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) + + prefix = get_script_prefix().rstrip('/') + url = url[len(prefix):] + return breadcrumbs_recursive(url, [], prefix, []) diff --git a/venv/Lib/site-packages/rest_framework/utils/encoders.py b/venv/Lib/site-packages/rest_framework/utils/encoders.py new file mode 100644 index 0000000..27293b7 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/encoders.py @@ -0,0 +1,67 @@ +""" +Helper classes for parsers. +""" +import datetime +import decimal +import json # noqa +import uuid + +from django.db.models.query import QuerySet +from django.utils import timezone +from django.utils.encoding import force_str +from django.utils.functional import Promise + +from rest_framework.compat import coreapi + + +class JSONEncoder(json.JSONEncoder): + """ + JSONEncoder subclass that knows how to encode date/time/timedelta, + decimal types, generators and other basic python objects. + """ + def default(self, obj): + # For Date Time string spec, see ECMA 262 + # https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 + if isinstance(obj, Promise): + return force_str(obj) + elif isinstance(obj, datetime.datetime): + representation = obj.isoformat() + if representation.endswith('+00:00'): + representation = representation[:-6] + 'Z' + return representation + elif isinstance(obj, datetime.date): + return obj.isoformat() + elif isinstance(obj, datetime.time): + if timezone and timezone.is_aware(obj): + raise ValueError("JSON can't represent timezone-aware times.") + representation = obj.isoformat() + return representation + elif isinstance(obj, datetime.timedelta): + return str(obj.total_seconds()) + elif isinstance(obj, decimal.Decimal): + # Serializers will coerce decimals to strings by default. + return float(obj) + elif isinstance(obj, uuid.UUID): + return str(obj) + elif isinstance(obj, QuerySet): + return tuple(obj) + elif isinstance(obj, bytes): + # Best-effort for binary blobs. See #4187. + return obj.decode() + elif hasattr(obj, 'tolist'): + # Numpy arrays and array scalars. + return obj.tolist() + elif (coreapi is not None) and isinstance(obj, (coreapi.Document, coreapi.Error)): + raise RuntimeError( + 'Cannot return a coreapi object from a JSON view. ' + 'You should be using a schema renderer instead for this view.' + ) + elif hasattr(obj, '__getitem__'): + cls = (list if isinstance(obj, (list, tuple)) else dict) + try: + return cls(obj) + except Exception: + pass + elif hasattr(obj, '__iter__'): + return tuple(item for item in obj) + return super().default(obj) diff --git a/venv/Lib/site-packages/rest_framework/utils/field_mapping.py b/venv/Lib/site-packages/rest_framework/utils/field_mapping.py new file mode 100644 index 0000000..4f8a4f1 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/field_mapping.py @@ -0,0 +1,302 @@ +""" +Helper functions for mapping model fields to a dictionary of default +keyword arguments that should be used for their equivalent serializer fields. +""" +import inspect + +from django.core import validators +from django.db import models +from django.utils.text import capfirst + +from rest_framework.compat import postgres_fields +from rest_framework.validators import UniqueValidator + +NUMERIC_FIELD_TYPES = ( + models.IntegerField, models.FloatField, models.DecimalField, models.DurationField, +) + + +class ClassLookupDict: + """ + Takes a dictionary with classes as keys. + Lookups against this object will traverses the object's inheritance + hierarchy in method resolution order, and returns the first matching value + from the dictionary or raises a KeyError if nothing matches. + """ + def __init__(self, mapping): + self.mapping = mapping + + def __getitem__(self, key): + if hasattr(key, '_proxy_class'): + # Deal with proxy classes. Ie. BoundField behaves as if it + # is a Field instance when using ClassLookupDict. + base_class = key._proxy_class + else: + base_class = key.__class__ + + for cls in inspect.getmro(base_class): + if cls in self.mapping: + return self.mapping[cls] + raise KeyError('Class %s not found in lookup.' % base_class.__name__) + + def __setitem__(self, key, value): + self.mapping[key] = value + + +def needs_label(model_field, field_name): + """ + Returns `True` if the label based on the model's verbose name + is not equal to the default label it would have based on it's field name. + """ + default_label = field_name.replace('_', ' ').capitalize() + return capfirst(model_field.verbose_name) != default_label + + +def get_detail_view_name(model): + """ + Given a model class, return the view name to use for URL relationships + that refer to instances of the model. + """ + return '%(model_name)s-detail' % { + 'model_name': model._meta.object_name.lower() + } + + +def get_field_kwargs(field_name, model_field): + """ + Creates a default instance of a basic non-relational field. + """ + kwargs = {} + validator_kwarg = list(model_field.validators) + + # The following will only be used by ModelField classes. + # Gets removed for everything else. + kwargs['model_field'] = model_field + + if model_field.verbose_name and needs_label(model_field, field_name): + kwargs['label'] = capfirst(model_field.verbose_name) + + if model_field.help_text: + kwargs['help_text'] = model_field.help_text + + max_digits = getattr(model_field, 'max_digits', None) + if max_digits is not None: + kwargs['max_digits'] = max_digits + + decimal_places = getattr(model_field, 'decimal_places', None) + if decimal_places is not None: + kwargs['decimal_places'] = decimal_places + + if isinstance(model_field, models.SlugField): + kwargs['allow_unicode'] = model_field.allow_unicode + + if isinstance(model_field, models.TextField) and not model_field.choices or \ + (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or \ + (hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)): + kwargs['style'] = {'base_template': 'textarea.html'} + + if isinstance(model_field, models.AutoField) or not model_field.editable: + # If this field is read-only, then return early. + # Further keyword arguments are not valid. + kwargs['read_only'] = True + return kwargs + + if model_field.has_default() or model_field.blank or model_field.null: + kwargs['required'] = False + + if model_field.null: + kwargs['allow_null'] = True + + if model_field.blank and (isinstance(model_field, (models.CharField, models.TextField))): + kwargs['allow_blank'] = True + + if not model_field.blank and (postgres_fields and isinstance(model_field, postgres_fields.ArrayField)): + kwargs['allow_empty'] = False + + if isinstance(model_field, models.FilePathField): + kwargs['path'] = model_field.path + + if model_field.match is not None: + kwargs['match'] = model_field.match + + if model_field.recursive is not False: + kwargs['recursive'] = model_field.recursive + + if model_field.allow_files is not True: + kwargs['allow_files'] = model_field.allow_files + + if model_field.allow_folders is not False: + kwargs['allow_folders'] = model_field.allow_folders + + if model_field.choices: + kwargs['choices'] = model_field.choices + else: + # Ensure that max_value is passed explicitly as a keyword arg, + # rather than as a validator. + max_value = next(( + validator.limit_value for validator in validator_kwarg + if isinstance(validator, validators.MaxValueValidator) + ), None) + if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): + kwargs['max_value'] = max_value + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MaxValueValidator) + ] + + # Ensure that min_value is passed explicitly as a keyword arg, + # rather than as a validator. + min_value = next(( + validator.limit_value for validator in validator_kwarg + if isinstance(validator, validators.MinValueValidator) + ), None) + if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): + kwargs['min_value'] = min_value + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MinValueValidator) + ] + + # URLField does not need to include the URLValidator argument, + # as it is explicitly added in. + if isinstance(model_field, models.URLField): + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.URLValidator) + ] + + # EmailField does not need to include the validate_email argument, + # as it is explicitly added in. + if isinstance(model_field, models.EmailField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_email + ] + + # SlugField do not need to include the 'validate_slug' argument, + if isinstance(model_field, models.SlugField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_slug + ] + + # IPAddressField do not need to include the 'validate_ipv46_address' argument, + if isinstance(model_field, models.GenericIPAddressField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_ipv46_address + ] + # Our decimal validation is handled in the field code, not validator code. + if isinstance(model_field, models.DecimalField): + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.DecimalValidator) + ] + + # Ensure that max_length is passed explicitly as a keyword arg, + # rather than as a validator. + max_length = getattr(model_field, 'max_length', None) + if max_length is not None and (isinstance(model_field, (models.CharField, models.TextField, models.FileField))): + kwargs['max_length'] = max_length + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MaxLengthValidator) + ] + + # Ensure that min_length is passed explicitly as a keyword arg, + # rather than as a validator. + min_length = next(( + validator.limit_value for validator in validator_kwarg + if isinstance(validator, validators.MinLengthValidator) + ), None) + if min_length is not None and isinstance(model_field, models.CharField): + kwargs['min_length'] = min_length + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MinLengthValidator) + ] + + if getattr(model_field, 'unique', False): + unique_error_message = model_field.error_messages.get('unique', None) + if unique_error_message: + unique_error_message = unique_error_message % { + 'model_name': model_field.model._meta.verbose_name, + 'field_label': model_field.verbose_name + } + validator = UniqueValidator( + queryset=model_field.model._default_manager, + message=unique_error_message) + validator_kwarg.append(validator) + + if validator_kwarg: + kwargs['validators'] = validator_kwarg + + return kwargs + + +def get_relation_kwargs(field_name, relation_info): + """ + Creates a default instance of a flat relational field. + """ + model_field, related_model, to_many, to_field, has_through_model, reverse = relation_info + kwargs = { + 'queryset': related_model._default_manager, + 'view_name': get_detail_view_name(related_model) + } + + if to_many: + kwargs['many'] = True + + if to_field: + kwargs['to_field'] = to_field + + limit_choices_to = model_field and model_field.get_limit_choices_to() + if limit_choices_to: + if not isinstance(limit_choices_to, models.Q): + limit_choices_to = models.Q(**limit_choices_to) + kwargs['queryset'] = kwargs['queryset'].filter(limit_choices_to) + + if has_through_model: + kwargs['read_only'] = True + kwargs.pop('queryset', None) + + if model_field: + if model_field.verbose_name and needs_label(model_field, field_name): + kwargs['label'] = capfirst(model_field.verbose_name) + help_text = model_field.help_text + if help_text: + kwargs['help_text'] = help_text + if not model_field.editable: + kwargs['read_only'] = True + kwargs.pop('queryset', None) + if kwargs.get('read_only', False): + # If this field is read-only, then return early. + # No further keyword arguments are valid. + return kwargs + + if model_field.has_default() or model_field.blank or model_field.null: + kwargs['required'] = False + if model_field.null: + kwargs['allow_null'] = True + if model_field.validators: + kwargs['validators'] = model_field.validators + if getattr(model_field, 'unique', False): + validator = UniqueValidator(queryset=model_field.model._default_manager) + kwargs['validators'] = kwargs.get('validators', []) + [validator] + if to_many and not model_field.blank: + kwargs['allow_empty'] = False + + return kwargs + + +def get_nested_relation_kwargs(relation_info): + kwargs = {'read_only': True} + if relation_info.to_many: + kwargs['many'] = True + return kwargs + + +def get_url_kwargs(model_field): + return { + 'view_name': get_detail_view_name(model_field) + } diff --git a/venv/Lib/site-packages/rest_framework/utils/formatting.py b/venv/Lib/site-packages/rest_framework/utils/formatting.py new file mode 100644 index 0000000..c5917fd --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/formatting.py @@ -0,0 +1,93 @@ +""" +Utility functions to return a formatted name and description for a given view. +""" +import re + +from django.utils.encoding import force_str +from django.utils.html import escape +from django.utils.safestring import mark_safe + +from rest_framework.compat import apply_markdown + + +def remove_trailing_string(content, trailing): + """ + Strip trailing component `trailing` from `content` if it exists. + Used when generating names from view classes. + """ + if content.endswith(trailing) and content != trailing: + return content[:-len(trailing)] + return content + + +def dedent(content): + """ + Remove leading indent from a block of text. + Used when generating descriptions from docstrings. + + Note that python's `textwrap.dedent` doesn't quite cut it, + as it fails to dedent multiline docstrings that include + unindented text on the initial line. + """ + content = force_str(content) + lines = [line for line in content.splitlines()[1:] if line.lstrip()] + + # unindent the content if needed + if lines: + whitespace_counts = min([len(line) - len(line.lstrip(' ')) for line in lines]) + tab_counts = min([len(line) - len(line.lstrip('\t')) for line in lines]) + if whitespace_counts: + whitespace_pattern = '^' + (' ' * whitespace_counts) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + elif tab_counts: + whitespace_pattern = '^' + ('\t' * tab_counts) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + return content.strip() + + +def camelcase_to_spaces(content): + """ + Translate 'CamelCaseNames' to 'Camel Case Names'. + Used when generating names from view classes. + """ + camelcase_boundary = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))' + content = re.sub(camelcase_boundary, ' \\1', content).strip() + return ' '.join(content.split('_')).title() + + +def markup_description(description): + """ + Apply HTML markup to the given description. + """ + if apply_markdown: + description = apply_markdown(description) + else: + description = escape(description).replace('\n', '
    ') + description = '

    ' + description + '

    ' + return mark_safe(description) + + +class lazy_format: + """ + Delay formatting until it's actually needed. + + Useful when the format string or one of the arguments is lazy. + + Not using Django's lazy because it is too slow. + """ + __slots__ = ('format_string', 'args', 'kwargs', 'result') + + def __init__(self, format_string, *args, **kwargs): + self.result = None + self.format_string = format_string + self.args = args + self.kwargs = kwargs + + def __str__(self): + if self.result is None: + self.result = self.format_string.format(*self.args, **self.kwargs) + self.format_string, self.args, self.kwargs = None, None, None + return self.result + + def __mod__(self, value): + return str(self) % value diff --git a/venv/Lib/site-packages/rest_framework/utils/html.py b/venv/Lib/site-packages/rest_framework/utils/html.py new file mode 100644 index 0000000..c7ede78 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/html.py @@ -0,0 +1,95 @@ +""" +Helpers for dealing with HTML input. +""" +import re + +from django.utils.datastructures import MultiValueDict + + +def is_html_input(dictionary): + # MultiDict type datastructures are used to represent HTML form input, + # which may have more than one value for each key. + return hasattr(dictionary, 'getlist') + + +def parse_html_list(dictionary, prefix='', default=None): + """ + Used to support list values in HTML forms. + Supports lists of primitives and/or dictionaries. + + * List of primitives. + + { + '[0]': 'abc', + '[1]': 'def', + '[2]': 'hij' + } + --> + [ + 'abc', + 'def', + 'hij' + ] + + * List of dictionaries. + + { + '[0]foo': 'abc', + '[0]bar': 'def', + '[1]foo': 'hij', + '[1]bar': 'klm', + } + --> + [ + {'foo': 'abc', 'bar': 'def'}, + {'foo': 'hij', 'bar': 'klm'} + ] + + :returns a list of objects, or the value specified in ``default`` if the list is empty + """ + ret = {} + regex = re.compile(r'^%s\[([0-9]+)\](.*)$' % re.escape(prefix)) + for field, value in dictionary.items(): + match = regex.match(field) + if not match: + continue + index, key = match.groups() + index = int(index) + if not key: + ret[index] = value + elif isinstance(ret.get(index), dict): + ret[index][key] = value + else: + ret[index] = MultiValueDict({key: [value]}) + + # return the items of the ``ret`` dict, sorted by key, or ``default`` if the dict is empty + return [ret[item] for item in sorted(ret)] if ret else default + + +def parse_html_dict(dictionary, prefix=''): + """ + Used to support dictionary values in HTML forms. + + { + 'profile.username': 'example', + 'profile.email': 'example@example.com', + } + --> + { + 'profile': { + 'username': 'example', + 'email': 'example@example.com' + } + } + """ + ret = MultiValueDict() + regex = re.compile(r'^%s\.(.+)$' % re.escape(prefix)) + for field in dictionary: + match = regex.match(field) + if not match: + continue + key = match.groups()[0] + value = dictionary.getlist(field) + ret.setlist(key, value) + + return ret diff --git a/venv/Lib/site-packages/rest_framework/utils/humanize_datetime.py b/venv/Lib/site-packages/rest_framework/utils/humanize_datetime.py new file mode 100644 index 0000000..48ef895 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/humanize_datetime.py @@ -0,0 +1,47 @@ +""" +Helper functions that convert strftime formats into more readable representations. +""" +from rest_framework import ISO_8601 + + +def datetime_formats(formats): + format = ', '.join(formats).replace( + ISO_8601, + 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]' + ) + return humanize_strptime(format) + + +def date_formats(formats): + format = ', '.join(formats).replace(ISO_8601, 'YYYY-MM-DD') + return humanize_strptime(format) + + +def time_formats(formats): + format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]') + return humanize_strptime(format) + + +def humanize_strptime(format_string): + # Note that we're missing some of the locale specific mappings that + # don't really make sense. + mapping = { + "%Y": "YYYY", + "%y": "YY", + "%m": "MM", + "%b": "[Jan-Dec]", + "%B": "[January-December]", + "%d": "DD", + "%H": "hh", + "%I": "hh", # Requires '%p' to differentiate from '%H'. + "%M": "mm", + "%S": "ss", + "%f": "uuuuuu", + "%a": "[Mon-Sun]", + "%A": "[Monday-Sunday]", + "%p": "[AM|PM]", + "%z": "[+HHMM|-HHMM]" + } + for key, val in mapping.items(): + format_string = format_string.replace(key, val) + return format_string diff --git a/venv/Lib/site-packages/rest_framework/utils/json.py b/venv/Lib/site-packages/rest_framework/utils/json.py new file mode 100644 index 0000000..1c1e69b --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/json.py @@ -0,0 +1,37 @@ +""" +Wrapper for the builtin json module that ensures compliance with the JSON spec. + +REST framework should always import this wrapper module in order to maintain +spec-compliant encoding/decoding. Support for non-standard features should be +handled by users at the renderer and parser layer. +""" +import functools +import json # noqa + + +def strict_constant(o): + raise ValueError('Out of range float values are not JSON compliant: ' + repr(o)) + + +@functools.wraps(json.dump) +def dump(*args, **kwargs): + kwargs.setdefault('allow_nan', False) + return json.dump(*args, **kwargs) + + +@functools.wraps(json.dumps) +def dumps(*args, **kwargs): + kwargs.setdefault('allow_nan', False) + return json.dumps(*args, **kwargs) + + +@functools.wraps(json.load) +def load(*args, **kwargs): + kwargs.setdefault('parse_constant', strict_constant) + return json.load(*args, **kwargs) + + +@functools.wraps(json.loads) +def loads(*args, **kwargs): + kwargs.setdefault('parse_constant', strict_constant) + return json.loads(*args, **kwargs) diff --git a/venv/Lib/site-packages/rest_framework/utils/mediatypes.py b/venv/Lib/site-packages/rest_framework/utils/mediatypes.py new file mode 100644 index 0000000..40bdf26 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/mediatypes.py @@ -0,0 +1,83 @@ +""" +Handling of media types, as found in HTTP Content-Type and Accept headers. + +See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 +""" +from django.http.multipartparser import parse_header + +from rest_framework import HTTP_HEADER_ENCODING + + +def media_type_matches(lhs, rhs): + """ + Returns ``True`` if the media type in the first argument <= the + media type in the second argument. The media types are strings + as described by the HTTP spec. + + Valid media type strings include: + + 'application/json; indent=4' + 'application/json' + 'text/*' + '*/*' + """ + lhs = _MediaType(lhs) + rhs = _MediaType(rhs) + return lhs.match(rhs) + + +def order_by_precedence(media_type_lst): + """ + Returns a list of sets of media type strings, ordered by precedence. + Precedence is determined by how specific a media type is: + + 3. 'type/subtype; param=val' + 2. 'type/subtype' + 1. 'type/*' + 0. '*/*' + """ + ret = [set(), set(), set(), set()] + for media_type in media_type_lst: + precedence = _MediaType(media_type).precedence + ret[3 - precedence].add(media_type) + return [media_types for media_types in ret if media_types] + + +class _MediaType: + def __init__(self, media_type_str): + self.orig = '' if (media_type_str is None) else media_type_str + self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING)) + self.main_type, sep, self.sub_type = self.full_type.partition('/') + + def match(self, other): + """Return true if this MediaType satisfies the given MediaType.""" + for key in self.params: + if key != 'q' and other.params.get(key, None) != self.params.get(key, None): + return False + + if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type: + return False + + if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type: + return False + + return True + + @property + def precedence(self): + """ + Return a precedence level from 0-3 for the media type given how specific it is. + """ + if self.main_type == '*': + return 0 + elif self.sub_type == '*': + return 1 + elif not self.params or list(self.params) == ['q']: + return 2 + return 3 + + def __str__(self): + ret = "%s/%s" % (self.main_type, self.sub_type) + for key, val in self.params.items(): + ret += "; %s=%s" % (key, val.decode('ascii')) + return ret diff --git a/venv/Lib/site-packages/rest_framework/utils/model_meta.py b/venv/Lib/site-packages/rest_framework/utils/model_meta.py new file mode 100644 index 0000000..4cc93b8 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/model_meta.py @@ -0,0 +1,161 @@ +""" +Helper function for returning the field information that is associated +with a model class. This includes returning all the forward and reverse +relationships and their associated metadata. + +Usage: `get_field_info(model)` returns a `FieldInfo` instance. +""" +from collections import OrderedDict, namedtuple + +FieldInfo = namedtuple('FieldResult', [ + 'pk', # Model field instance + 'fields', # Dict of field name -> model field instance + 'forward_relations', # Dict of field name -> RelationInfo + 'reverse_relations', # Dict of field name -> RelationInfo + 'fields_and_pk', # Shortcut for 'pk' + 'fields' + 'relations' # Shortcut for 'forward_relations' + 'reverse_relations' +]) + +RelationInfo = namedtuple('RelationInfo', [ + 'model_field', + 'related_model', + 'to_many', + 'to_field', + 'has_through_model', + 'reverse' +]) + + +def get_field_info(model): + """ + Given a model class, returns a `FieldInfo` instance, which is a + `namedtuple`, containing metadata about the various field types on the model + including information about their relationships. + """ + opts = model._meta.concrete_model._meta + + pk = _get_pk(opts) + fields = _get_fields(opts) + forward_relations = _get_forward_relationships(opts) + reverse_relations = _get_reverse_relationships(opts) + fields_and_pk = _merge_fields_and_pk(pk, fields) + relationships = _merge_relationships(forward_relations, reverse_relations) + + return FieldInfo(pk, fields, forward_relations, reverse_relations, + fields_and_pk, relationships) + + +def _get_pk(opts): + pk = opts.pk + rel = pk.remote_field + + while rel and rel.parent_link: + # If model is a child via multi-table inheritance, use parent's pk. + pk = pk.remote_field.model._meta.pk + rel = pk.remote_field + + return pk + + +def _get_fields(opts): + fields = OrderedDict() + for field in [field for field in opts.fields if field.serialize and not field.remote_field]: + fields[field.name] = field + + return fields + + +def _get_to_field(field): + return getattr(field, 'to_fields', None) and field.to_fields[0] + + +def _get_forward_relationships(opts): + """ + Returns an `OrderedDict` of field names to `RelationInfo`. + """ + forward_relations = OrderedDict() + for field in [field for field in opts.fields if field.serialize and field.remote_field]: + forward_relations[field.name] = RelationInfo( + model_field=field, + related_model=field.remote_field.model, + to_many=False, + to_field=_get_to_field(field), + has_through_model=False, + reverse=False + ) + + # Deal with forward many-to-many relationships. + for field in [field for field in opts.many_to_many if field.serialize]: + forward_relations[field.name] = RelationInfo( + model_field=field, + related_model=field.remote_field.model, + to_many=True, + # manytomany do not have to_fields + to_field=None, + has_through_model=( + not field.remote_field.through._meta.auto_created + ), + reverse=False + ) + + return forward_relations + + +def _get_reverse_relationships(opts): + """ + Returns an `OrderedDict` of field names to `RelationInfo`. + """ + reverse_relations = OrderedDict() + all_related_objects = [r for r in opts.related_objects if not r.field.many_to_many] + for relation in all_related_objects: + accessor_name = relation.get_accessor_name() + reverse_relations[accessor_name] = RelationInfo( + model_field=None, + related_model=relation.related_model, + to_many=relation.field.remote_field.multiple, + to_field=_get_to_field(relation.field), + has_through_model=False, + reverse=True + ) + + # Deal with reverse many-to-many relationships. + all_related_many_to_many_objects = [r for r in opts.related_objects if r.field.many_to_many] + for relation in all_related_many_to_many_objects: + accessor_name = relation.get_accessor_name() + reverse_relations[accessor_name] = RelationInfo( + model_field=None, + related_model=relation.related_model, + to_many=True, + # manytomany do not have to_fields + to_field=None, + has_through_model=( + (getattr(relation.field.remote_field, 'through', None) is not None) and + not relation.field.remote_field.through._meta.auto_created + ), + reverse=True + ) + + return reverse_relations + + +def _merge_fields_and_pk(pk, fields): + fields_and_pk = OrderedDict() + fields_and_pk['pk'] = pk + fields_and_pk[pk.name] = pk + fields_and_pk.update(fields) + + return fields_and_pk + + +def _merge_relationships(forward_relations, reverse_relations): + return OrderedDict( + list(forward_relations.items()) + + list(reverse_relations.items()) + ) + + +def is_abstract_model(model): + """ + Given a model class, returns a boolean True if it is abstract and False if it is not. + """ + return hasattr(model, '_meta') and hasattr(model._meta, 'abstract') and model._meta.abstract diff --git a/venv/Lib/site-packages/rest_framework/utils/representation.py b/venv/Lib/site-packages/rest_framework/utils/representation.py new file mode 100644 index 0000000..6f2efee --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/representation.py @@ -0,0 +1,101 @@ +""" +Helper functions for creating user-friendly representations +of serializer classes and serializer fields. +""" +import re + +from django.db import models +from django.utils.encoding import force_str +from django.utils.functional import Promise + + +def manager_repr(value): + model = value.model + opts = model._meta + names_and_managers = [ + (manager.name, manager) + for manager + in opts.managers + ] + for manager_name, manager_instance in names_and_managers: + if manager_instance == value: + return '%s.%s.all()' % (model._meta.object_name, manager_name) + return repr(value) + + +def smart_repr(value): + if isinstance(value, models.Manager): + return manager_repr(value) + + if isinstance(value, Promise) and value._delegate_text: + value = force_str(value) + + value = repr(value) + + # Representations like u'help text' + # should simply be presented as 'help text' + if value.startswith("u'") and value.endswith("'"): + return value[1:] + + # Representations like + # + # Should be presented as + # + return re.sub(' at 0x[0-9A-Fa-f]{4,32}>', '>', value) + + +def field_repr(field, force_many=False): + kwargs = field._kwargs + if force_many: + kwargs = kwargs.copy() + kwargs['many'] = True + kwargs.pop('child', None) + + arg_string = ', '.join([smart_repr(val) for val in field._args]) + kwarg_string = ', '.join([ + '%s=%s' % (key, smart_repr(val)) + for key, val in sorted(kwargs.items()) + ]) + if arg_string and kwarg_string: + arg_string += ', ' + + if force_many: + class_name = force_many.__class__.__name__ + else: + class_name = field.__class__.__name__ + + return "%s(%s%s)" % (class_name, arg_string, kwarg_string) + + +def serializer_repr(serializer, indent, force_many=None): + ret = field_repr(serializer, force_many) + ':' + indent_str = ' ' * indent + + if force_many: + fields = force_many.fields + else: + fields = serializer.fields + + for field_name, field in fields.items(): + ret += '\n' + indent_str + field_name + ' = ' + if hasattr(field, 'fields'): + ret += serializer_repr(field, indent + 1) + elif hasattr(field, 'child'): + ret += list_repr(field, indent + 1) + elif hasattr(field, 'child_relation'): + ret += field_repr(field.child_relation, force_many=field.child_relation) + else: + ret += field_repr(field) + + if serializer.validators: + ret += '\n' + indent_str + 'class Meta:' + ret += '\n' + indent_str + ' validators = ' + smart_repr(serializer.validators) + + return ret + + +def list_repr(serializer, indent): + child = serializer.child + if hasattr(child, 'fields'): + return serializer_repr(serializer, indent, force_many=child) + return field_repr(serializer) diff --git a/venv/Lib/site-packages/rest_framework/utils/serializer_helpers.py b/venv/Lib/site-packages/rest_framework/utils/serializer_helpers.py new file mode 100644 index 0000000..4cd2ada --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/serializer_helpers.py @@ -0,0 +1,167 @@ +from collections import OrderedDict +from collections.abc import Mapping, MutableMapping + +from django.utils.encoding import force_str + +from rest_framework.utils import json + + +class ReturnDict(OrderedDict): + """ + Return object from `serializer.data` for the `Serializer` class. + Includes a backlink to the serializer instance for renderers + to use if they need richer field information. + """ + + def __init__(self, *args, **kwargs): + self.serializer = kwargs.pop('serializer') + super().__init__(*args, **kwargs) + + def copy(self): + return ReturnDict(self, serializer=self.serializer) + + def __repr__(self): + return dict.__repr__(self) + + def __reduce__(self): + # Pickling these objects will drop the .serializer backlink, + # but preserve the raw data. + return (dict, (dict(self),)) + + +class ReturnList(list): + """ + Return object from `serializer.data` for the `SerializerList` class. + Includes a backlink to the serializer instance for renderers + to use if they need richer field information. + """ + + def __init__(self, *args, **kwargs): + self.serializer = kwargs.pop('serializer') + super().__init__(*args, **kwargs) + + def __repr__(self): + return list.__repr__(self) + + def __reduce__(self): + # Pickling these objects will drop the .serializer backlink, + # but preserve the raw data. + return (list, (list(self),)) + + +class BoundField: + """ + A field object that also includes `.value` and `.error` properties. + Returned when iterating over a serializer instance, + providing an API similar to Django forms and form fields. + """ + + def __init__(self, field, value, errors, prefix=''): + self._field = field + self._prefix = prefix + self.value = value + self.errors = errors + self.name = prefix + self.field_name + + def __getattr__(self, attr_name): + return getattr(self._field, attr_name) + + @property + def _proxy_class(self): + return self._field.__class__ + + def __repr__(self): + return '<%s value=%s errors=%s>' % ( + self.__class__.__name__, self.value, self.errors + ) + + def as_form_field(self): + value = '' if (self.value is None or self.value is False) else self.value + return self.__class__(self._field, value, self.errors, self._prefix) + + +class JSONBoundField(BoundField): + def as_form_field(self): + value = self.value + # When HTML form input is used and the input is not valid + # value will be a JSONString, rather than a JSON primitive. + if not getattr(value, 'is_json_string', False): + try: + value = json.dumps( + self.value, + sort_keys=True, + indent=4, + separators=(',', ': '), + ) + except (TypeError, ValueError): + pass + return self.__class__(self._field, value, self.errors, self._prefix) + + +class NestedBoundField(BoundField): + """ + This `BoundField` additionally implements __iter__ and __getitem__ + in order to support nested bound fields. This class is the type of + `BoundField` that is used for serializer fields. + """ + + def __init__(self, field, value, errors, prefix=''): + if value is None or value == '' or not isinstance(value, Mapping): + value = {} + super().__init__(field, value, errors, prefix) + + def __iter__(self): + for field in self.fields.values(): + yield self[field.field_name] + + def __getitem__(self, key): + field = self.fields[key] + value = self.value.get(key) if self.value else None + error = self.errors.get(key) if isinstance(self.errors, dict) else None + if hasattr(field, 'fields'): + return NestedBoundField(field, value, error, prefix=self.name + '.') + elif getattr(field, '_is_jsonfield', False): + return JSONBoundField(field, value, error, prefix=self.name + '.') + return BoundField(field, value, error, prefix=self.name + '.') + + def as_form_field(self): + values = {} + for key, value in self.value.items(): + if isinstance(value, (list, dict)): + values[key] = value + else: + values[key] = '' if (value is None or value is False) else force_str(value) + return self.__class__(self._field, values, self.errors, self._prefix) + + +class BindingDict(MutableMapping): + """ + This dict-like object is used to store fields on a serializer. + + This ensures that whenever fields are added to the serializer we call + `field.bind()` so that the `field_name` and `parent` attributes + can be set correctly. + """ + + def __init__(self, serializer): + self.serializer = serializer + self.fields = OrderedDict() + + def __setitem__(self, key, field): + self.fields[key] = field + field.bind(field_name=key, parent=self.serializer) + + def __getitem__(self, key): + return self.fields[key] + + def __delitem__(self, key): + del self.fields[key] + + def __iter__(self): + return iter(self.fields) + + def __len__(self): + return len(self.fields) + + def __repr__(self): + return dict.__repr__(self.fields) diff --git a/venv/Lib/site-packages/rest_framework/utils/urls.py b/venv/Lib/site-packages/rest_framework/utils/urls.py new file mode 100644 index 0000000..afb0699 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/utils/urls.py @@ -0,0 +1,27 @@ +from urllib import parse + +from django.utils.encoding import force_str + + +def replace_query_param(url, key, val): + """ + Given a URL and a key/val pair, set or replace an item in the query + parameters of the URL, and return the new URL. + """ + (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url)) + query_dict = parse.parse_qs(query, keep_blank_values=True) + query_dict[force_str(key)] = [force_str(val)] + query = parse.urlencode(sorted(query_dict.items()), doseq=True) + return parse.urlunsplit((scheme, netloc, path, query, fragment)) + + +def remove_query_param(url, key): + """ + Given a URL and a key/val pair, remove an item in the query + parameters of the URL, and return the new URL. + """ + (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url)) + query_dict = parse.parse_qs(query, keep_blank_values=True) + query_dict.pop(key, None) + query = parse.urlencode(sorted(query_dict.items()), doseq=True) + return parse.urlunsplit((scheme, netloc, path, query, fragment)) diff --git a/venv/Lib/site-packages/rest_framework/validators.py b/venv/Lib/site-packages/rest_framework/validators.py new file mode 100644 index 0000000..a5cb75a --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/validators.py @@ -0,0 +1,280 @@ +""" +We perform uniqueness checks explicitly on the serializer class, rather +the using Django's `.full_clean()`. + +This gives us better separation of concerns, allows us to use single-step +object creation, and makes it possible to switch between using the implicit +`ModelSerializer` class and an equivalent explicit `Serializer` class. +""" +from django.db import DataError +from django.utils.translation import gettext_lazy as _ + +from rest_framework.exceptions import ValidationError +from rest_framework.utils.representation import smart_repr + + +# Robust filter and exist implementations. Ensures that queryset.exists() for +# an invalid value returns `False`, rather than raising an error. +# Refs https://github.com/encode/django-rest-framework/issues/3381 +def qs_exists(queryset): + try: + return queryset.exists() + except (TypeError, ValueError, DataError): + return False + + +def qs_filter(queryset, **kwargs): + try: + return queryset.filter(**kwargs) + except (TypeError, ValueError, DataError): + return queryset.none() + + +class UniqueValidator: + """ + Validator that corresponds to `unique=True` on a model field. + + Should be applied to an individual field on the serializer. + """ + message = _('This field must be unique.') + requires_context = True + + def __init__(self, queryset, message=None, lookup='exact'): + self.queryset = queryset + self.message = message or self.message + self.lookup = lookup + + def filter_queryset(self, value, queryset, field_name): + """ + Filter the queryset to all instances matching the given attribute. + """ + filter_kwargs = {'%s__%s' % (field_name, self.lookup): value} + return qs_filter(queryset, **filter_kwargs) + + def exclude_current_instance(self, queryset, instance): + """ + If an instance is being updated, then do not include + that instance itself as a uniqueness conflict. + """ + if instance is not None: + return queryset.exclude(pk=instance.pk) + return queryset + + def __call__(self, value, serializer_field): + # Determine the underlying model field name. This may not be the + # same as the serializer field name if `source=<>` is set. + field_name = serializer_field.source_attrs[-1] + # Determine the existing instance, if this is an update operation. + instance = getattr(serializer_field.parent, 'instance', None) + + queryset = self.queryset + queryset = self.filter_queryset(value, queryset, field_name) + queryset = self.exclude_current_instance(queryset, instance) + if qs_exists(queryset): + raise ValidationError(self.message, code='unique') + + def __repr__(self): + return '<%s(queryset=%s)>' % ( + self.__class__.__name__, + smart_repr(self.queryset) + ) + + +class UniqueTogetherValidator: + """ + Validator that corresponds to `unique_together = (...)` on a model class. + + Should be applied to the serializer class, not to an individual field. + """ + message = _('The fields {field_names} must make a unique set.') + missing_message = _('This field is required.') + requires_context = True + + def __init__(self, queryset, fields, message=None): + self.queryset = queryset + self.fields = fields + self.message = message or self.message + + def enforce_required_fields(self, attrs, serializer): + """ + The `UniqueTogetherValidator` always forces an implied 'required' + state on the fields it applies to. + """ + if serializer.instance is not None: + return + + missing_items = { + field_name: self.missing_message + for field_name in self.fields + if serializer.fields[field_name].source not in attrs + } + if missing_items: + raise ValidationError(missing_items, code='required') + + def filter_queryset(self, attrs, queryset, serializer): + """ + Filter the queryset to all instances matching the given attributes. + """ + # field names => field sources + sources = [ + serializer.fields[field_name].source + for field_name in self.fields + ] + + # If this is an update, then any unprovided field should + # have it's value set based on the existing instance attribute. + if serializer.instance is not None: + for source in sources: + if source not in attrs: + attrs[source] = getattr(serializer.instance, source) + + # Determine the filter keyword arguments and filter the queryset. + filter_kwargs = { + source: attrs[source] + for source in sources + } + return qs_filter(queryset, **filter_kwargs) + + def exclude_current_instance(self, attrs, queryset, instance): + """ + If an instance is being updated, then do not include + that instance itself as a uniqueness conflict. + """ + if instance is not None: + return queryset.exclude(pk=instance.pk) + return queryset + + def __call__(self, attrs, serializer): + self.enforce_required_fields(attrs, serializer) + queryset = self.queryset + queryset = self.filter_queryset(attrs, queryset, serializer) + queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) + + # Ignore validation if any field is None + checked_values = [ + value for field, value in attrs.items() if field in self.fields + ] + if None not in checked_values and qs_exists(queryset): + field_names = ', '.join(self.fields) + message = self.message.format(field_names=field_names) + raise ValidationError(message, code='unique') + + def __repr__(self): + return '<%s(queryset=%s, fields=%s)>' % ( + self.__class__.__name__, + smart_repr(self.queryset), + smart_repr(self.fields) + ) + + +class ProhibitSurrogateCharactersValidator: + message = _('Surrogate characters are not allowed: U+{code_point:X}.') + code = 'surrogate_characters_not_allowed' + + def __call__(self, value): + for surrogate_character in (ch for ch in str(value) + if 0xD800 <= ord(ch) <= 0xDFFF): + message = self.message.format(code_point=ord(surrogate_character)) + raise ValidationError(message, code=self.code) + + +class BaseUniqueForValidator: + message = None + missing_message = _('This field is required.') + requires_context = True + + def __init__(self, queryset, field, date_field, message=None): + self.queryset = queryset + self.field = field + self.date_field = date_field + self.message = message or self.message + + def enforce_required_fields(self, attrs): + """ + The `UniqueForValidator` classes always force an implied + 'required' state on the fields they are applied to. + """ + missing_items = { + field_name: self.missing_message + for field_name in [self.field, self.date_field] + if field_name not in attrs + } + if missing_items: + raise ValidationError(missing_items, code='required') + + def filter_queryset(self, attrs, queryset, field_name, date_field_name): + raise NotImplementedError('`filter_queryset` must be implemented.') + + def exclude_current_instance(self, attrs, queryset, instance): + """ + If an instance is being updated, then do not include + that instance itself as a uniqueness conflict. + """ + if instance is not None: + return queryset.exclude(pk=instance.pk) + return queryset + + def __call__(self, attrs, serializer): + # Determine the underlying model field names. These may not be the + # same as the serializer field names if `source=<>` is set. + field_name = serializer.fields[self.field].source_attrs[-1] + date_field_name = serializer.fields[self.date_field].source_attrs[-1] + + self.enforce_required_fields(attrs) + queryset = self.queryset + queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name) + queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) + if qs_exists(queryset): + message = self.message.format(date_field=self.date_field) + raise ValidationError({ + self.field: message + }, code='unique') + + def __repr__(self): + return '<%s(queryset=%s, field=%s, date_field=%s)>' % ( + self.__class__.__name__, + smart_repr(self.queryset), + smart_repr(self.field), + smart_repr(self.date_field) + ) + + +class UniqueForDateValidator(BaseUniqueForValidator): + message = _('This field must be unique for the "{date_field}" date.') + + def filter_queryset(self, attrs, queryset, field_name, date_field_name): + value = attrs[self.field] + date = attrs[self.date_field] + + filter_kwargs = {} + filter_kwargs[field_name] = value + filter_kwargs['%s__day' % date_field_name] = date.day + filter_kwargs['%s__month' % date_field_name] = date.month + filter_kwargs['%s__year' % date_field_name] = date.year + return qs_filter(queryset, **filter_kwargs) + + +class UniqueForMonthValidator(BaseUniqueForValidator): + message = _('This field must be unique for the "{date_field}" month.') + + def filter_queryset(self, attrs, queryset, field_name, date_field_name): + value = attrs[self.field] + date = attrs[self.date_field] + + filter_kwargs = {} + filter_kwargs[field_name] = value + filter_kwargs['%s__month' % date_field_name] = date.month + return qs_filter(queryset, **filter_kwargs) + + +class UniqueForYearValidator(BaseUniqueForValidator): + message = _('This field must be unique for the "{date_field}" year.') + + def filter_queryset(self, attrs, queryset, field_name, date_field_name): + value = attrs[self.field] + date = attrs[self.date_field] + + filter_kwargs = {} + filter_kwargs[field_name] = value + filter_kwargs['%s__year' % date_field_name] = date.year + return qs_filter(queryset, **filter_kwargs) diff --git a/venv/Lib/site-packages/rest_framework/versioning.py b/venv/Lib/site-packages/rest_framework/versioning.py new file mode 100644 index 0000000..78cfc9d --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/versioning.py @@ -0,0 +1,184 @@ +import re + +from django.utils.translation import gettext_lazy as _ + +from rest_framework import exceptions +from rest_framework.compat import unicode_http_header +from rest_framework.reverse import _reverse +from rest_framework.settings import api_settings +from rest_framework.templatetags.rest_framework import replace_query_param +from rest_framework.utils.mediatypes import _MediaType + + +class BaseVersioning: + default_version = api_settings.DEFAULT_VERSION + allowed_versions = api_settings.ALLOWED_VERSIONS + version_param = api_settings.VERSION_PARAM + + def determine_version(self, request, *args, **kwargs): + msg = '{cls}.determine_version() must be implemented.' + raise NotImplementedError(msg.format( + cls=self.__class__.__name__ + )) + + def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): + return _reverse(viewname, args, kwargs, request, format, **extra) + + def is_allowed_version(self, version): + if not self.allowed_versions: + return True + return ((version is not None and version == self.default_version) or + (version in self.allowed_versions)) + + +class AcceptHeaderVersioning(BaseVersioning): + """ + GET /something/ HTTP/1.1 + Host: example.com + Accept: application/json; version=1.0 + """ + invalid_version_message = _('Invalid version in "Accept" header.') + + def determine_version(self, request, *args, **kwargs): + media_type = _MediaType(request.accepted_media_type) + version = media_type.params.get(self.version_param, self.default_version) + version = unicode_http_header(version) + if not self.is_allowed_version(version): + raise exceptions.NotAcceptable(self.invalid_version_message) + return version + + # We don't need to implement `reverse`, as the versioning is based + # on the `Accept` header, not on the request URL. + + +class URLPathVersioning(BaseVersioning): + """ + To the client this is the same style as `NamespaceVersioning`. + The difference is in the backend - this implementation uses + Django's URL keyword arguments to determine the version. + + An example URL conf for two views that accept two different versions. + + urlpatterns = [ + re_path(r'^(?P[v1|v2]+)/users/$', users_list, name='users-list'), + re_path(r'^(?P[v1|v2]+)/users/(?P[0-9]+)/$', users_detail, name='users-detail') + ] + + GET /1.0/something/ HTTP/1.1 + Host: example.com + Accept: application/json + """ + invalid_version_message = _('Invalid version in URL path.') + + def determine_version(self, request, *args, **kwargs): + version = kwargs.get(self.version_param, self.default_version) + if version is None: + version = self.default_version + + if not self.is_allowed_version(version): + raise exceptions.NotFound(self.invalid_version_message) + return version + + def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): + if request.version is not None: + kwargs = {} if (kwargs is None) else kwargs + kwargs[self.version_param] = request.version + + return super().reverse( + viewname, args, kwargs, request, format, **extra + ) + + +class NamespaceVersioning(BaseVersioning): + """ + To the client this is the same style as `URLPathVersioning`. + The difference is in the backend - this implementation uses + Django's URL namespaces to determine the version. + + An example URL conf that is namespaced into two separate versions + + # users/urls.py + urlpatterns = [ + path('/users/', users_list, name='users-list'), + path('/users//', users_detail, name='users-detail') + ] + + # urls.py + urlpatterns = [ + path('v1/', include('users.urls', namespace='v1')), + path('v2/', include('users.urls', namespace='v2')) + ] + + GET /1.0/something/ HTTP/1.1 + Host: example.com + Accept: application/json + """ + invalid_version_message = _('Invalid version in URL path. Does not match any version namespace.') + + def determine_version(self, request, *args, **kwargs): + resolver_match = getattr(request, 'resolver_match', None) + if resolver_match is None or not resolver_match.namespace: + return self.default_version + + # Allow for possibly nested namespaces. + possible_versions = resolver_match.namespace.split(':') + for version in possible_versions: + if self.is_allowed_version(version): + return version + raise exceptions.NotFound(self.invalid_version_message) + + def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): + if request.version is not None: + viewname = self.get_versioned_viewname(viewname, request) + return super().reverse( + viewname, args, kwargs, request, format, **extra + ) + + def get_versioned_viewname(self, viewname, request): + return request.version + ':' + viewname + + +class HostNameVersioning(BaseVersioning): + """ + GET /something/ HTTP/1.1 + Host: v1.example.com + Accept: application/json + """ + hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$') + invalid_version_message = _('Invalid version in hostname.') + + def determine_version(self, request, *args, **kwargs): + hostname, separator, port = request.get_host().partition(':') + match = self.hostname_regex.match(hostname) + if not match: + return self.default_version + version = match.group(1) + if not self.is_allowed_version(version): + raise exceptions.NotFound(self.invalid_version_message) + return version + + # We don't need to implement `reverse`, as the hostname will already be + # preserved as part of the REST framework `reverse` implementation. + + +class QueryParameterVersioning(BaseVersioning): + """ + GET /something/?version=0.1 HTTP/1.1 + Host: example.com + Accept: application/json + """ + invalid_version_message = _('Invalid version in query parameter.') + + def determine_version(self, request, *args, **kwargs): + version = request.query_params.get(self.version_param, self.default_version) + if not self.is_allowed_version(version): + raise exceptions.NotFound(self.invalid_version_message) + return version + + def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): + url = super().reverse( + viewname, args, kwargs, request, format, **extra + ) + if request.version is not None: + return replace_query_param(url, self.version_param, request.version) + return url diff --git a/venv/Lib/site-packages/rest_framework/views.py b/venv/Lib/site-packages/rest_framework/views.py new file mode 100644 index 0000000..5b06220 --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/views.py @@ -0,0 +1,521 @@ +""" +Provides an APIView class that is the base of all views in REST framework. +""" +from django.conf import settings +from django.core.exceptions import PermissionDenied +from django.db import connections, models +from django.http import Http404 +from django.http.response import HttpResponseBase +from django.utils.cache import cc_delim_re, patch_vary_headers +from django.utils.encoding import smart_str +from django.views.decorators.csrf import csrf_exempt +from django.views.generic import View + +from rest_framework import exceptions, status +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.schemas import DefaultSchema +from rest_framework.settings import api_settings +from rest_framework.utils import formatting + + +def get_view_name(view): + """ + Given a view instance, return a textual name to represent the view. + This name is used in the browsable API, and in OPTIONS responses. + + This function is the default for the `VIEW_NAME_FUNCTION` setting. + """ + # Name may be set by some Views, such as a ViewSet. + name = getattr(view, 'name', None) + if name is not None: + return name + + name = view.__class__.__name__ + name = formatting.remove_trailing_string(name, 'View') + name = formatting.remove_trailing_string(name, 'ViewSet') + name = formatting.camelcase_to_spaces(name) + + # Suffix may be set by some Views, such as a ViewSet. + suffix = getattr(view, 'suffix', None) + if suffix: + name += ' ' + suffix + + return name + + +def get_view_description(view, html=False): + """ + Given a view instance, return a textual description to represent the view. + This name is used in the browsable API, and in OPTIONS responses. + + This function is the default for the `VIEW_DESCRIPTION_FUNCTION` setting. + """ + # Description may be set by some Views, such as a ViewSet. + description = getattr(view, 'description', None) + if description is None: + description = view.__class__.__doc__ or '' + + description = formatting.dedent(smart_str(description)) + if html: + return formatting.markup_description(description) + return description + + +def set_rollback(): + for db in connections.all(): + if db.settings_dict['ATOMIC_REQUESTS'] and db.in_atomic_block: + db.set_rollback(True) + + +def exception_handler(exc, context): + """ + Returns the response that should be used for any given exception. + + By default we handle the REST framework `APIException`, and also + Django's built-in `Http404` and `PermissionDenied` exceptions. + + Any unhandled exceptions may return `None`, which will cause a 500 error + to be raised. + """ + if isinstance(exc, Http404): + exc = exceptions.NotFound() + elif isinstance(exc, PermissionDenied): + exc = exceptions.PermissionDenied() + + if isinstance(exc, exceptions.APIException): + headers = {} + if getattr(exc, 'auth_header', None): + headers['WWW-Authenticate'] = exc.auth_header + if getattr(exc, 'wait', None): + headers['Retry-After'] = '%d' % exc.wait + + if isinstance(exc.detail, (list, dict)): + data = exc.detail + else: + data = {'detail': exc.detail} + + set_rollback() + return Response(data, status=exc.status_code, headers=headers) + + return None + + +class APIView(View): + + # The following policies may be set at either globally, or per-view. + renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + parser_classes = api_settings.DEFAULT_PARSER_CLASSES + authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES + throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES + permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES + content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS + metadata_class = api_settings.DEFAULT_METADATA_CLASS + versioning_class = api_settings.DEFAULT_VERSIONING_CLASS + + # Allow dependency injection of other settings to make testing easier. + settings = api_settings + + schema = DefaultSchema() + + @classmethod + def as_view(cls, **initkwargs): + """ + Store the original class on the view function. + + This allows us to discover information about the view when we do URL + reverse lookups. Used for breadcrumb generation. + """ + if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): + def force_evaluation(): + raise RuntimeError( + 'Do not evaluate the `.queryset` attribute directly, ' + 'as the result will be cached and reused between requests. ' + 'Use `.all()` or call `.get_queryset()` instead.' + ) + cls.queryset._fetch_all = force_evaluation + + view = super().as_view(**initkwargs) + view.cls = cls + view.initkwargs = initkwargs + + # Note: session based authentication is explicitly CSRF validated, + # all other authentication is CSRF exempt. + return csrf_exempt(view) + + @property + def allowed_methods(self): + """ + Wrap Django's private `_allowed_methods` interface in a public property. + """ + return self._allowed_methods() + + @property + def default_response_headers(self): + headers = { + 'Allow': ', '.join(self.allowed_methods), + } + if len(self.renderer_classes) > 1: + headers['Vary'] = 'Accept' + return headers + + def http_method_not_allowed(self, request, *args, **kwargs): + """ + If `request.method` does not correspond to a handler method, + determine what kind of exception to raise. + """ + raise exceptions.MethodNotAllowed(request.method) + + def permission_denied(self, request, message=None, code=None): + """ + If request is not permitted, determine what kind of exception to raise. + """ + if request.authenticators and not request.successful_authenticator: + raise exceptions.NotAuthenticated() + raise exceptions.PermissionDenied(detail=message, code=code) + + def throttled(self, request, wait): + """ + If request is throttled, determine what kind of exception to raise. + """ + raise exceptions.Throttled(wait) + + def get_authenticate_header(self, request): + """ + If a request is unauthenticated, determine the WWW-Authenticate + header to use for 401 responses, if any. + """ + authenticators = self.get_authenticators() + if authenticators: + return authenticators[0].authenticate_header(request) + + def get_parser_context(self, http_request): + """ + Returns a dict that is passed through to Parser.parse(), + as the `parser_context` keyword argument. + """ + # Note: Additionally `request` and `encoding` will also be added + # to the context by the Request object. + return { + 'view': self, + 'args': getattr(self, 'args', ()), + 'kwargs': getattr(self, 'kwargs', {}) + } + + def get_renderer_context(self): + """ + Returns a dict that is passed through to Renderer.render(), + as the `renderer_context` keyword argument. + """ + # Note: Additionally 'response' will also be added to the context, + # by the Response object. + return { + 'view': self, + 'args': getattr(self, 'args', ()), + 'kwargs': getattr(self, 'kwargs', {}), + 'request': getattr(self, 'request', None) + } + + def get_exception_handler_context(self): + """ + Returns a dict that is passed through to EXCEPTION_HANDLER, + as the `context` argument. + """ + return { + 'view': self, + 'args': getattr(self, 'args', ()), + 'kwargs': getattr(self, 'kwargs', {}), + 'request': getattr(self, 'request', None) + } + + def get_view_name(self): + """ + Return the view name, as used in OPTIONS responses and in the + browsable API. + """ + func = self.settings.VIEW_NAME_FUNCTION + return func(self) + + def get_view_description(self, html=False): + """ + Return some descriptive text for the view, as used in OPTIONS responses + and in the browsable API. + """ + func = self.settings.VIEW_DESCRIPTION_FUNCTION + return func(self, html) + + # API policy instantiation methods + + def get_format_suffix(self, **kwargs): + """ + Determine if the request includes a '.json' style format suffix + """ + if self.settings.FORMAT_SUFFIX_KWARG: + return kwargs.get(self.settings.FORMAT_SUFFIX_KWARG) + + def get_renderers(self): + """ + Instantiates and returns the list of renderers that this view can use. + """ + return [renderer() for renderer in self.renderer_classes] + + def get_parsers(self): + """ + Instantiates and returns the list of parsers that this view can use. + """ + return [parser() for parser in self.parser_classes] + + def get_authenticators(self): + """ + Instantiates and returns the list of authenticators that this view can use. + """ + return [auth() for auth in self.authentication_classes] + + def get_permissions(self): + """ + Instantiates and returns the list of permissions that this view requires. + """ + return [permission() for permission in self.permission_classes] + + def get_throttles(self): + """ + Instantiates and returns the list of throttles that this view uses. + """ + return [throttle() for throttle in self.throttle_classes] + + def get_content_negotiator(self): + """ + Instantiate and return the content negotiation class to use. + """ + if not getattr(self, '_negotiator', None): + self._negotiator = self.content_negotiation_class() + return self._negotiator + + def get_exception_handler(self): + """ + Returns the exception handler that this view uses. + """ + return self.settings.EXCEPTION_HANDLER + + # API policy implementation methods + + def perform_content_negotiation(self, request, force=False): + """ + Determine which renderer and media type to use render the response. + """ + renderers = self.get_renderers() + conneg = self.get_content_negotiator() + + try: + return conneg.select_renderer(request, renderers, self.format_kwarg) + except Exception: + if force: + return (renderers[0], renderers[0].media_type) + raise + + def perform_authentication(self, request): + """ + Perform authentication on the incoming request. + + Note that if you override this and simply 'pass', then authentication + will instead be performed lazily, the first time either + `request.user` or `request.auth` is accessed. + """ + request.user + + def check_permissions(self, request): + """ + Check if the request should be permitted. + Raises an appropriate exception if the request is not permitted. + """ + for permission in self.get_permissions(): + if not permission.has_permission(request, self): + self.permission_denied( + request, + message=getattr(permission, 'message', None), + code=getattr(permission, 'code', None) + ) + + def check_object_permissions(self, request, obj): + """ + Check if the request should be permitted for a given object. + Raises an appropriate exception if the request is not permitted. + """ + for permission in self.get_permissions(): + if not permission.has_object_permission(request, self, obj): + self.permission_denied( + request, + message=getattr(permission, 'message', None), + code=getattr(permission, 'code', None) + ) + + def check_throttles(self, request): + """ + Check if request should be throttled. + Raises an appropriate exception if the request is throttled. + """ + throttle_durations = [] + for throttle in self.get_throttles(): + if not throttle.allow_request(request, self): + throttle_durations.append(throttle.wait()) + + if throttle_durations: + # Filter out `None` values which may happen in case of config / rate + # changes, see #1438 + durations = [ + duration for duration in throttle_durations + if duration is not None + ] + + duration = max(durations, default=None) + self.throttled(request, duration) + + def determine_version(self, request, *args, **kwargs): + """ + If versioning is being used, then determine any API version for the + incoming request. Returns a two-tuple of (version, versioning_scheme) + """ + if self.versioning_class is None: + return (None, None) + scheme = self.versioning_class() + return (scheme.determine_version(request, *args, **kwargs), scheme) + + # Dispatch methods + + def initialize_request(self, request, *args, **kwargs): + """ + Returns the initial request object. + """ + parser_context = self.get_parser_context(request) + + return Request( + request, + parsers=self.get_parsers(), + authenticators=self.get_authenticators(), + negotiator=self.get_content_negotiator(), + parser_context=parser_context + ) + + def initial(self, request, *args, **kwargs): + """ + Runs anything that needs to occur prior to calling the method handler. + """ + self.format_kwarg = self.get_format_suffix(**kwargs) + + # Perform content negotiation and store the accepted info on the request + neg = self.perform_content_negotiation(request) + request.accepted_renderer, request.accepted_media_type = neg + + # Determine the API version, if versioning is in use. + version, scheme = self.determine_version(request, *args, **kwargs) + request.version, request.versioning_scheme = version, scheme + + # Ensure that the incoming request is permitted + self.perform_authentication(request) + self.check_permissions(request) + self.check_throttles(request) + + def finalize_response(self, request, response, *args, **kwargs): + """ + Returns the final response object. + """ + # Make the error obvious if a proper response is not returned + assert isinstance(response, HttpResponseBase), ( + 'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` ' + 'to be returned from the view, but received a `%s`' + % type(response) + ) + + if isinstance(response, Response): + if not getattr(request, 'accepted_renderer', None): + neg = self.perform_content_negotiation(request, force=True) + request.accepted_renderer, request.accepted_media_type = neg + + response.accepted_renderer = request.accepted_renderer + response.accepted_media_type = request.accepted_media_type + response.renderer_context = self.get_renderer_context() + + # Add new vary headers to the response instead of overwriting. + vary_headers = self.headers.pop('Vary', None) + if vary_headers is not None: + patch_vary_headers(response, cc_delim_re.split(vary_headers)) + + for key, value in self.headers.items(): + response[key] = value + + return response + + def handle_exception(self, exc): + """ + Handle any exception that occurs, by returning an appropriate response, + or re-raising the error. + """ + if isinstance(exc, (exceptions.NotAuthenticated, + exceptions.AuthenticationFailed)): + # WWW-Authenticate header for 401 responses, else coerce to 403 + auth_header = self.get_authenticate_header(self.request) + + if auth_header: + exc.auth_header = auth_header + else: + exc.status_code = status.HTTP_403_FORBIDDEN + + exception_handler = self.get_exception_handler() + + context = self.get_exception_handler_context() + response = exception_handler(exc, context) + + if response is None: + self.raise_uncaught_exception(exc) + + response.exception = True + return response + + def raise_uncaught_exception(self, exc): + if settings.DEBUG: + request = self.request + renderer_format = getattr(request.accepted_renderer, 'format') + use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') + request.force_plaintext_errors(use_plaintext_traceback) + raise exc + + # Note: Views are made CSRF exempt from within `as_view` as to prevent + # accidental removal of this exemption in cases where `dispatch` needs to + # be overridden. + def dispatch(self, request, *args, **kwargs): + """ + `.dispatch()` is pretty much the same as Django's regular dispatch, + but with extra hooks for startup, finalize, and exception handling. + """ + self.args = args + self.kwargs = kwargs + request = self.initialize_request(request, *args, **kwargs) + self.request = request + self.headers = self.default_response_headers # deprecate? + + try: + self.initial(request, *args, **kwargs) + + # Get the appropriate handler method + if request.method.lower() in self.http_method_names: + handler = getattr(self, request.method.lower(), + self.http_method_not_allowed) + else: + handler = self.http_method_not_allowed + + response = handler(request, *args, **kwargs) + + except Exception as exc: + response = self.handle_exception(exc) + + self.response = self.finalize_response(request, response, *args, **kwargs) + return self.response + + def options(self, request, *args, **kwargs): + """ + Handler method for HTTP 'OPTIONS' request. + """ + if self.metadata_class is None: + return self.http_method_not_allowed(request, *args, **kwargs) + data = self.metadata_class().determine_metadata(request, self) + return Response(data, status=status.HTTP_200_OK) diff --git a/venv/Lib/site-packages/rest_framework/viewsets.py b/venv/Lib/site-packages/rest_framework/viewsets.py new file mode 100644 index 0000000..5a1f8ac --- /dev/null +++ b/venv/Lib/site-packages/rest_framework/viewsets.py @@ -0,0 +1,245 @@ +""" +ViewSets are essentially just a type of class based view, that doesn't provide +any method handlers, such as `get()`, `post()`, etc... but instead has actions, +such as `list()`, `retrieve()`, `create()`, etc... + +Actions are only bound to methods at the point of instantiating the views. + + user_list = UserViewSet.as_view({'get': 'list'}) + user_detail = UserViewSet.as_view({'get': 'retrieve'}) + +Typically, rather than instantiate views from viewsets directly, you'll +register the viewset with a router and let the URL conf be determined +automatically. + + router = DefaultRouter() + router.register(r'users', UserViewSet, 'user') + urlpatterns = router.urls +""" +from collections import OrderedDict +from functools import update_wrapper +from inspect import getmembers + +from django.urls import NoReverseMatch +from django.utils.decorators import classonlymethod +from django.views.decorators.csrf import csrf_exempt + +from rest_framework import generics, mixins, views +from rest_framework.decorators import MethodMapper +from rest_framework.reverse import reverse + + +def _is_extra_action(attr): + return hasattr(attr, 'mapping') and isinstance(attr.mapping, MethodMapper) + + +def _check_attr_name(func, name): + assert func.__name__ == name, ( + 'Expected function (`{func.__name__}`) to match its attribute name ' + '(`{name}`). If using a decorator, ensure the inner function is ' + 'decorated with `functools.wraps`, or that `{func.__name__}.__name__` ' + 'is otherwise set to `{name}`.').format(func=func, name=name) + return func + + +class ViewSetMixin: + """ + This is the magic. + + Overrides `.as_view()` so that it takes an `actions` keyword that performs + the binding of HTTP methods to actions on the Resource. + + For example, to create a concrete view binding the 'GET' and 'POST' methods + to the 'list' and 'create' actions... + + view = MyViewSet.as_view({'get': 'list', 'post': 'create'}) + """ + + @classonlymethod + def as_view(cls, actions=None, **initkwargs): + """ + Because of the way class based views create a closure around the + instantiated view, we need to totally reimplement `.as_view`, + and slightly modify the view function that is created and returned. + """ + # The name and description initkwargs may be explicitly overridden for + # certain route configurations. eg, names of extra actions. + cls.name = None + cls.description = None + + # The suffix initkwarg is reserved for displaying the viewset type. + # This initkwarg should have no effect if the name is provided. + # eg. 'List' or 'Instance'. + cls.suffix = None + + # The detail initkwarg is reserved for introspecting the viewset type. + cls.detail = None + + # Setting a basename allows a view to reverse its action urls. This + # value is provided by the router through the initkwargs. + cls.basename = None + + # actions must not be empty + if not actions: + raise TypeError("The `actions` argument must be provided when " + "calling `.as_view()` on a ViewSet. For example " + "`.as_view({'get': 'list'})`") + + # sanitize keyword arguments + for key in initkwargs: + if key in cls.http_method_names: + raise TypeError("You tried to pass in the %s method name as a " + "keyword argument to %s(). Don't do that." + % (key, cls.__name__)) + if not hasattr(cls, key): + raise TypeError("%s() received an invalid keyword %r" % ( + cls.__name__, key)) + + # name and suffix are mutually exclusive + if 'name' in initkwargs and 'suffix' in initkwargs: + raise TypeError("%s() received both `name` and `suffix`, which are " + "mutually exclusive arguments." % (cls.__name__)) + + def view(request, *args, **kwargs): + self = cls(**initkwargs) + + if 'get' in actions and 'head' not in actions: + actions['head'] = actions['get'] + + # We also store the mapping of request methods to actions, + # so that we can later set the action attribute. + # eg. `self.action = 'list'` on an incoming GET request. + self.action_map = actions + + # Bind methods to actions + # This is the bit that's different to a standard view + for method, action in actions.items(): + handler = getattr(self, action) + setattr(self, method, handler) + + self.request = request + self.args = args + self.kwargs = kwargs + + # And continue as usual + return self.dispatch(request, *args, **kwargs) + + # take name and docstring from class + update_wrapper(view, cls, updated=()) + + # and possible attributes set by decorators + # like csrf_exempt from dispatch + update_wrapper(view, cls.dispatch, assigned=()) + + # We need to set these on the view function, so that breadcrumb + # generation can pick out these bits of information from a + # resolved URL. + view.cls = cls + view.initkwargs = initkwargs + view.actions = actions + return csrf_exempt(view) + + def initialize_request(self, request, *args, **kwargs): + """ + Set the `.action` attribute on the view, depending on the request method. + """ + request = super().initialize_request(request, *args, **kwargs) + method = request.method.lower() + if method == 'options': + # This is a special case as we always provide handling for the + # options method in the base `View` class. + # Unlike the other explicitly defined actions, 'metadata' is implicit. + self.action = 'metadata' + else: + self.action = self.action_map.get(method) + return request + + def reverse_action(self, url_name, *args, **kwargs): + """ + Reverse the action for the given `url_name`. + """ + url_name = '%s-%s' % (self.basename, url_name) + namespace = None + if self.request and self.request.resolver_match: + namespace = self.request.resolver_match.namespace + if namespace: + url_name = namespace + ':' + url_name + kwargs.setdefault('request', self.request) + + return reverse(url_name, *args, **kwargs) + + @classmethod + def get_extra_actions(cls): + """ + Get the methods that are marked as an extra ViewSet `@action`. + """ + return [_check_attr_name(method, name) + for name, method + in getmembers(cls, _is_extra_action)] + + def get_extra_action_url_map(self): + """ + Build a map of {names: urls} for the extra actions. + + This method will noop if `detail` was not provided as a view initkwarg. + """ + action_urls = OrderedDict() + + # exit early if `detail` has not been provided + if self.detail is None: + return action_urls + + # filter for the relevant extra actions + actions = [ + action for action in self.get_extra_actions() + if action.detail == self.detail + ] + + for action in actions: + try: + url_name = '%s-%s' % (self.basename, action.url_name) + url = reverse(url_name, self.args, self.kwargs, request=self.request) + view = self.__class__(**action.kwargs) + action_urls[view.get_view_name()] = url + except NoReverseMatch: + pass # URL requires additional arguments, ignore + + return action_urls + + +class ViewSet(ViewSetMixin, views.APIView): + """ + The base ViewSet class does not provide any actions by default. + """ + pass + + +class GenericViewSet(ViewSetMixin, generics.GenericAPIView): + """ + The GenericViewSet class does not provide any actions by default, + but does include the base set of generic view behavior, such as + the `get_object` and `get_queryset` methods. + """ + pass + + +class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, + mixins.ListModelMixin, + GenericViewSet): + """ + A viewset that provides default `list()` and `retrieve()` actions. + """ + pass + + +class ModelViewSet(mixins.CreateModelMixin, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + mixins.ListModelMixin, + GenericViewSet): + """ + A viewset that provides default `create()`, `retrieve()`, `update()`, + `partial_update()`, `destroy()` and `list()` actions. + """ + pass diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/INSTALLER b/venv/Lib/site-packages/zipp-3.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/LICENSE b/venv/Lib/site-packages/zipp-3.8.0.dist-info/LICENSE new file mode 100644 index 0000000..353924b --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/METADATA b/venv/Lib/site-packages/zipp-3.8.0.dist-info/METADATA new file mode 100644 index 0000000..94e90a0 --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.1 +Name: zipp +Version: 3.8.0 +Summary: Backport of pathlib-compatible object wrapper for zip files +Home-page: https://github.com/jaraco/zipp +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +License: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7 +License-File: LICENSE +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Provides-Extra: testing +Requires-Dist: pytest (>=6) ; extra == 'testing' +Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' +Requires-Dist: pytest-flake8 ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing' +Requires-Dist: jaraco.itertools ; extra == 'testing' +Requires-Dist: func-timeout ; extra == 'testing' +Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/zipp.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/zipp.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/zipp + +.. image:: https://github.com/jaraco/zipp/workflows/tests/badge.svg + :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2022-informational + :target: https://blog.jaraco.com/skeleton + + +A pathlib-compatible Zipfile object wrapper. Official backport of the standard library +`Path object `_. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - zipp + - stdlib + * - 3.5 + - 3.11 + * - 3.3 + - 3.9 + * - 1.0 + - 3.8 + + +Usage +===== + +Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. + + diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/RECORD b/venv/Lib/site-packages/zipp-3.8.0.dist-info/RECORD new file mode 100644 index 0000000..4ec5f9a --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/zipp.cpython-39.pyc,, +zipp-3.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +zipp-3.8.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +zipp-3.8.0.dist-info/METADATA,sha256=6KJo2_gKMNYNz52FNbITlpwbHwa_st0KWpNMFB9OQfE,2719 +zipp-3.8.0.dist-info/RECORD,, +zipp-3.8.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +zipp-3.8.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5 +zipp.py,sha256=5hrx38_-kX0WCpfBk5LSwCIMYraThjaNj5HsriUSB-8,8029 diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/WHEEL b/venv/Lib/site-packages/zipp-3.8.0.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/zipp-3.8.0.dist-info/top_level.txt b/venv/Lib/site-packages/zipp-3.8.0.dist-info/top_level.txt new file mode 100644 index 0000000..e82f676 --- /dev/null +++ b/venv/Lib/site-packages/zipp-3.8.0.dist-info/top_level.txt @@ -0,0 +1 @@ +zipp diff --git a/venv/Lib/site-packages/zipp.py b/venv/Lib/site-packages/zipp.py new file mode 100644 index 0000000..52c82a0 --- /dev/null +++ b/venv/Lib/site-packages/zipp.py @@ -0,0 +1,312 @@ +import io +import posixpath +import zipfile +import itertools +import contextlib +import pathlib + + +__all__ = ['Path'] + + +def _parents(path): + """ + Given a path with elements separated by + posixpath.sep, generate all parents of that path. + + >>> list(_parents('b/d')) + ['b'] + >>> list(_parents('/b/d/')) + ['/b'] + >>> list(_parents('b/d/f/')) + ['b/d', 'b'] + >>> list(_parents('b')) + [] + >>> list(_parents('')) + [] + """ + return itertools.islice(_ancestry(path), 1, None) + + +def _ancestry(path): + """ + Given a path with elements separated by + posixpath.sep, generate all elements of that path + + >>> list(_ancestry('b/d')) + ['b/d', 'b'] + >>> list(_ancestry('/b/d/')) + ['/b/d', '/b'] + >>> list(_ancestry('b/d/f/')) + ['b/d/f', 'b/d', 'b'] + >>> list(_ancestry('b')) + ['b'] + >>> list(_ancestry('')) + [] + """ + path = path.rstrip(posixpath.sep) + while path and path != posixpath.sep: + yield path + path, tail = posixpath.split(path) + + +_dedupe = dict.fromkeys +"""Deduplicate an iterable in original order""" + + +def _difference(minuend, subtrahend): + """ + Return items in minuend not in subtrahend, retaining order + with O(1) lookup. + """ + return itertools.filterfalse(set(subtrahend).__contains__, minuend) + + +class CompleteDirs(zipfile.ZipFile): + """ + A ZipFile subclass that ensures that implied directories + are always included in the namelist. + """ + + @staticmethod + def _implied_dirs(names): + parents = itertools.chain.from_iterable(map(_parents, names)) + as_dirs = (p + posixpath.sep for p in parents) + return _dedupe(_difference(as_dirs, names)) + + def namelist(self): + names = super(CompleteDirs, self).namelist() + return names + list(self._implied_dirs(names)) + + def _name_set(self): + return set(self.namelist()) + + def resolve_dir(self, name): + """ + If the name represents a directory, return that name + as a directory (with the trailing slash). + """ + names = self._name_set() + dirname = name + '/' + dir_match = name not in names and dirname in names + return dirname if dir_match else name + + @classmethod + def make(cls, source): + """ + Given a source (filename or zipfile), return an + appropriate CompleteDirs subclass. + """ + if isinstance(source, CompleteDirs): + return source + + if not isinstance(source, zipfile.ZipFile): + return cls(source) + + # Only allow for FastLookup when supplied zipfile is read-only + if 'r' not in source.mode: + cls = CompleteDirs + + source.__class__ = cls + return source + + +class FastLookup(CompleteDirs): + """ + ZipFile subclass to ensure implicit + dirs exist and are resolved rapidly. + """ + + def namelist(self): + with contextlib.suppress(AttributeError): + return self.__names + self.__names = super(FastLookup, self).namelist() + return self.__names + + def _name_set(self): + with contextlib.suppress(AttributeError): + return self.__lookup + self.__lookup = super(FastLookup, self)._name_set() + return self.__lookup + + +class Path: + """ + A pathlib-compatible interface for zip files. + + Consider a zip file with this structure:: + + . + ├── a.txt + └── b + ├── c.txt + └── d + └── e.txt + + >>> data = io.BytesIO() + >>> zf = zipfile.ZipFile(data, 'w') + >>> zf.writestr('a.txt', 'content of a') + >>> zf.writestr('b/c.txt', 'content of c') + >>> zf.writestr('b/d/e.txt', 'content of e') + >>> zf.filename = 'mem/abcde.zip' + + Path accepts the zipfile object itself or a filename + + >>> root = Path(zf) + + From there, several path operations are available. + + Directory iteration (including the zip file itself): + + >>> a, b = root.iterdir() + >>> a + Path('mem/abcde.zip', 'a.txt') + >>> b + Path('mem/abcde.zip', 'b/') + + name property: + + >>> b.name + 'b' + + join with divide operator: + + >>> c = b / 'c.txt' + >>> c + Path('mem/abcde.zip', 'b/c.txt') + >>> c.name + 'c.txt' + + Read text: + + >>> c.read_text() + 'content of c' + + existence: + + >>> c.exists() + True + >>> (b / 'missing.txt').exists() + False + + Coercion to string: + + >>> import os + >>> str(c).replace(os.sep, posixpath.sep) + 'mem/abcde.zip/b/c.txt' + + At the root, ``name``, ``filename``, and ``parent`` + resolve to the zipfile. Note these attributes are not + valid and will raise a ``ValueError`` if the zipfile + has no filename. + + >>> root.name + 'abcde.zip' + >>> str(root.filename).replace(os.sep, posixpath.sep) + 'mem/abcde.zip' + >>> str(root.parent) + 'mem' + """ + + __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" + + def __init__(self, root, at=""): + """ + Construct a Path from a ZipFile or filename. + + Note: When the source is an existing ZipFile object, + its type (__class__) will be mutated to a + specialized type. If the caller wishes to retain the + original type, the caller should either create a + separate ZipFile object or pass a filename. + """ + self.root = FastLookup.make(root) + self.at = at + + def open(self, mode='r', *args, pwd=None, **kwargs): + """ + Open this entry as text or binary following the semantics + of ``pathlib.Path.open()`` by passing arguments through + to io.TextIOWrapper(). + """ + if self.is_dir(): + raise IsADirectoryError(self) + zip_mode = mode[0] + if not self.exists() and zip_mode == 'r': + raise FileNotFoundError(self) + stream = self.root.open(self.at, zip_mode, pwd=pwd) + if 'b' in mode: + if args or kwargs: + raise ValueError("encoding args invalid for binary operation") + return stream + return io.TextIOWrapper(stream, *args, **kwargs) + + @property + def name(self): + return pathlib.Path(self.at).name or self.filename.name + + @property + def suffix(self): + return pathlib.Path(self.at).suffix or self.filename.suffix + + @property + def suffixes(self): + return pathlib.Path(self.at).suffixes or self.filename.suffixes + + @property + def stem(self): + return pathlib.Path(self.at).stem or self.filename.stem + + @property + def filename(self): + return pathlib.Path(self.root.filename).joinpath(self.at) + + def read_text(self, *args, **kwargs): + with self.open('r', *args, **kwargs) as strm: + return strm.read() + + def read_bytes(self): + with self.open('rb') as strm: + return strm.read() + + def _is_child(self, path): + return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") + + def _next(self, at): + return self.__class__(self.root, at) + + def is_dir(self): + return not self.at or self.at.endswith("/") + + def is_file(self): + return self.exists() and not self.is_dir() + + def exists(self): + return self.at in self.root._name_set() + + def iterdir(self): + if not self.is_dir(): + raise ValueError("Can't listdir a file") + subs = map(self._next, self.root.namelist()) + return filter(self._is_child, subs) + + def __str__(self): + return posixpath.join(self.root.filename, self.at) + + def __repr__(self): + return self.__repr.format(self=self) + + def joinpath(self, *other): + next = posixpath.join(self.at, *other) + return self._next(self.root.resolve_dir(next)) + + __truediv__ = joinpath + + @property + def parent(self): + if not self.at: + return self.filename.parent + parent_at = posixpath.dirname(self.at.rstrip('/')) + if parent_at: + parent_at += '/' + return self._next(parent_at) diff --git a/venv/Scripts/django-admin.exe b/venv/Scripts/django-admin.exe index f74b482..680ded9 100644 Binary files a/venv/Scripts/django-admin.exe and b/venv/Scripts/django-admin.exe differ diff --git a/venv/Scripts/django-admin.py b/venv/Scripts/django-admin.py new file mode 100644 index 0000000..613bba5 --- /dev/null +++ b/venv/Scripts/django-admin.py @@ -0,0 +1,21 @@ +#!C:\Users\PBDELL\Desktop\Django\ITShowPlatform\venv\Scripts\python.exe +# When the django-admin.py deprecation ends, remove this script. +import warnings + +from django.core import management + +try: + from django.utils.deprecation import RemovedInDjango40Warning +except ImportError: + raise ImportError( + 'django-admin.py was deprecated in Django 3.1 and removed in Django ' + '4.0. Please manually remove this script from your virtual environment ' + 'and use django-admin instead.' + ) + +if __name__ == "__main__": + warnings.warn( + 'django-admin.py is deprecated in favor of django-admin.', + RemovedInDjango40Warning, + ) + management.execute_from_command_line() diff --git a/venv/Scripts/markdown_py.exe b/venv/Scripts/markdown_py.exe new file mode 100644 index 0000000..30bfebf Binary files /dev/null and b/venv/Scripts/markdown_py.exe differ